@@ -27,13 +27,105 @@ contract Emu {
27
27
// Display
28
28
// -------------------------------------------------------------------------
29
29
30
+ /// @notice fontset size
31
+ uint8 constant FONTSET_SIZE = 80 ;
32
+
33
+ /// @notice fontset
34
+ /// @dev Most modern emulators will use that space to store the sprite data for font characters of all the
35
+ /// hexadecimal digits, that is characters of 0-9 and A-F. We could store this data at any fixed position in RAM, but this
36
+ /// space is already defined as empty anyway. Each character is made up of eight rows of five pixels, with each row using
37
+ /// a byte of data, meaning that each letter altogether takes up five bytes of data. The following diagram illustrates how
38
+ /// a character is stored as bytes
39
+ uint8 [FONTSET_SIZE] FONTSET = [
40
+ 0xF0 ,
41
+ 0x90 ,
42
+ 0x90 ,
43
+ 0x90 ,
44
+ 0xF0 , // 0
45
+ 0x20 ,
46
+ 0x60 ,
47
+ 0x20 ,
48
+ 0x20 ,
49
+ 0x70 , // 1
50
+ 0xF0 ,
51
+ 0x10 ,
52
+ 0xF0 ,
53
+ 0x80 ,
54
+ 0xF0 , // 2
55
+ 0xF0 ,
56
+ 0x10 ,
57
+ 0xF0 ,
58
+ 0x10 ,
59
+ 0xF0 , // 3
60
+ 0x90 ,
61
+ 0x90 ,
62
+ 0xF0 ,
63
+ 0x10 ,
64
+ 0x10 , // 4
65
+ 0xF0 ,
66
+ 0x80 ,
67
+ 0xF0 ,
68
+ 0x10 ,
69
+ 0xF0 , // 5
70
+ 0xF0 ,
71
+ 0x80 ,
72
+ 0xF0 ,
73
+ 0x90 ,
74
+ 0xF0 , // 6
75
+ 0xF0 ,
76
+ 0x10 ,
77
+ 0x20 ,
78
+ 0x40 ,
79
+ 0x40 , // 7
80
+ 0xF0 ,
81
+ 0x90 ,
82
+ 0xF0 ,
83
+ 0x90 ,
84
+ 0xF0 , // 8
85
+ 0xF0 ,
86
+ 0x90 ,
87
+ 0xF0 ,
88
+ 0x10 ,
89
+ 0xF0 , // 9
90
+ 0xF0 ,
91
+ 0x90 ,
92
+ 0xF0 ,
93
+ 0x90 ,
94
+ 0x90 , // A
95
+ 0xE0 ,
96
+ 0x90 ,
97
+ 0xE0 ,
98
+ 0x90 ,
99
+ 0xE0 , // B
100
+ 0xF0 ,
101
+ 0x80 ,
102
+ 0x80 ,
103
+ 0x80 ,
104
+ 0xF0 , // C
105
+ 0xE0 ,
106
+ 0x90 ,
107
+ 0x90 ,
108
+ 0x90 ,
109
+ 0xE0 , // D
110
+ 0xF0 ,
111
+ 0x80 ,
112
+ 0xF0 ,
113
+ 0x80 ,
114
+ 0xF0 , // E
115
+ 0xF0 ,
116
+ 0x80 ,
117
+ 0xF0 ,
118
+ 0x80 ,
119
+ 0x80 // F
120
+ ];
121
+
30
122
struct Emulator {
31
123
/// @notice 16-bit program counter
32
124
uint16 pc;
33
125
/// @notice 4KB RAM
34
126
uint8 [RAM_SIZE] ram;
35
- /// @notice A 64x32 monochrome display
36
- bool [SCREEN_WIDTH * SCREEN_HEIGHT ] screen;
127
+ /// @notice A 64x32 monochrome display = 2048 bit = 256 * 8 bits
128
+ uint256 [ 8 ] screen;
37
129
/// @notice Sixteen 8-bit general purpose registers, referred to as V0 thru VF
38
130
uint8 [NUM_REGS] v_reg;
39
131
/// @notice Single 16-bit register used as a pointer for memory access, called the I Register
@@ -63,13 +155,16 @@ contract Emu {
63
155
64
156
constructor () {
65
157
emu.pc = START_ADDR;
158
+ for (uint256 i = 0 ; i < FONTSET_SIZE; i++ ) {
159
+ emu.ram[i] = FONTSET[i];
160
+ }
66
161
}
67
162
68
163
/// @notice Reset the emulator
69
164
function reset () public {
70
165
emu.pc = START_ADDR;
71
- for (uint256 i = 0 ; i < SCREEN_WIDTH * SCREEN_HEIGHT ; i++ ) {
72
- emu.screen[i] = false ;
166
+ for (uint256 i = 0 ; i < 8 ; i++ ) {
167
+ emu.screen[i] = 0 ;
73
168
}
74
169
for (uint256 i = 0 ; i < NUM_REGS; i++ ) {
75
170
emu.v_reg[i] = 0 ;
@@ -84,6 +179,10 @@ contract Emu {
84
179
}
85
180
emu.dt = 0 ;
86
181
emu.st = 0 ;
182
+ // Copy FONTSET
183
+ for (uint256 i = 0 ; i < FONTSET_SIZE; i++ ) {
184
+ emu.ram[i] = FONTSET[i];
185
+ }
87
186
}
88
187
89
188
// -------------------------------------------------------------------------
@@ -159,8 +258,8 @@ contract Emu {
159
258
160
259
// 00E0 - CLS
161
260
if (digit1 == 0x0 && digit2 == 0x0 && digit3 == 0xE && digit4 == 0 ) {
162
- for (uint256 i = 0 ; i < SCREEN_WIDTH * SCREEN_HEIGHT ; i++ ) {
163
- emu.screen[i] = false ;
261
+ for (uint256 i = 0 ; i < 8 ; i++ ) {
262
+ emu.screen[i] = 0 ;
164
263
}
165
264
return ;
166
265
}
@@ -321,17 +420,39 @@ contract Emu {
321
420
for (uint8 row = 0 ; row < height; row++ ) {
322
421
uint8 sprite_byte = emu.ram[emu.i_reg + row];
323
422
for (uint8 col = 0 ; col < 8 ; col++ ) {
423
+ // Get the sprite pixel (bit) at the current column
324
424
uint8 sprite_pixel = (sprite_byte >> (7 - col)) & 0x1 ;
425
+
426
+ // Calculate the screen coordinates, wrapping around if necessary
325
427
uint32 screen_x = uint32 ((x + col) % SCREEN_WIDTH);
326
428
uint32 screen_y = uint32 ((y + row) % SCREEN_HEIGHT);
327
- uint256 index = screen_y * SCREEN_WIDTH + screen_x;
328
429
329
- bool pixel_before = emu.screen[index];
430
+ // Calculate the index in the display buffer
431
+ uint32 pixel_index = screen_y * SCREEN_WIDTH + screen_x; // Range: 0 to 2047
432
+
433
+ // Calculate the display array index and bit position
434
+ uint256 display_index = pixel_index / 256 ; // Index in emu.screen[]
435
+ uint256 bit_position = pixel_index % 256 ; // Bit position within emu.screen[display_index]
436
+
437
+ // Get the current pixel value from the display
438
+ bool pixel_before = ((emu.screen[display_index] >> (255 - bit_position)) & 0x1 ) != 0 ;
439
+
440
+ // Calculate the new pixel value using XOR (as per CHIP-8 drawing behavior)
330
441
bool new_pixel = pixel_before != (sprite_pixel == 1 );
442
+
443
+ // Update the collision flag VF if a pixel is erased
331
444
if (pixel_before && ! new_pixel) {
332
445
emu.v_reg[0xF ] = 1 ;
333
446
}
334
- emu.screen[index] = new_pixel;
447
+
448
+ // Update the display with the new pixel value
449
+ if (new_pixel) {
450
+ // Set the bit to 1
451
+ emu.screen[display_index] |= (1 << (255 - bit_position));
452
+ } else {
453
+ // Set the bit to 0
454
+ emu.screen[display_index] &= ~ (1 << (255 - bit_position));
455
+ }
335
456
}
336
457
}
337
458
return ;
@@ -438,7 +559,7 @@ contract Emu {
438
559
// -------------------------------------------------------------------------
439
560
440
561
/// @notice Get display
441
- function getDisplay () public view returns (bool [SCREEN_WIDTH * SCREEN_HEIGHT ] memory ) {
562
+ function getDisplay () public view returns (uint256 [ 8 ] memory ) {
442
563
return emu.screen;
443
564
}
444
565
@@ -530,16 +651,22 @@ contract Emu {
530
651
}
531
652
532
653
function setScreenPixel (uint256 index , bool value ) public {
533
- require (index < SCREEN_WIDTH * SCREEN_HEIGHT, "Index out of bounds " );
534
- emu.screen[index] = value;
654
+ unchecked {
655
+ require (index < SCREEN_WIDTH * SCREEN_HEIGHT, "Index out of bounds " );
656
+ if (value) {
657
+ // Set the bit to 1
658
+ emu.screen[index >> 8 ] |= 1 << (index & 255 );
659
+ } else {
660
+ // Set the bit to 0
661
+ emu.screen[index >> 8 ] &= ~ (1 << (index & 255 ));
662
+ }
663
+ }
535
664
}
536
665
537
666
function isDisplayCleared () public view returns (bool ) {
538
- for (uint256 i = 0 ; i < SCREEN_WIDTH * SCREEN_HEIGHT; i++ ) {
539
- if (emu.screen[i]) {
540
- return false ;
541
- }
542
- }
543
- return true ;
667
+ return (
668
+ emu.screen[0 ] == 0 && emu.screen[1 ] == 0 && emu.screen[2 ] == 0 && emu.screen[3 ] == 0 && emu.screen[4 ] == 0
669
+ && emu.screen[5 ] == 0 && emu.screen[6 ] == 0 && emu.screen[7 ] == 0
670
+ );
544
671
}
545
672
}
0 commit comments