From 01cccf387dfbf69e15f75e0ac90332baf4b2212e Mon Sep 17 00:00:00 2001 From: raxracks Date: Sat, 27 Jan 2024 12:20:21 +1300 Subject: [PATCH] first commit --- .gitignore | 3 + Makefile | 8 ++ emulator.c | 206 ++++++++++++++++++++++++++++++++++++++++++++++++++ hello.asm | 2 + include/cpu.h | 13 ++++ include/slibs | 1 + inspector.c | 16 ++++ roms/breakout | Bin 0 -> 232 bytes roms/maze | Bin 0 -> 38 bytes roms/particle | Bin 0 -> 353 bytes roms/pong | Bin 0 -> 246 bytes roms/test | Bin 0 -> 56 bytes roms/test.asm | 78 +++++++++++++++++++ roms/zeropong | Bin 0 -> 184 bytes 14 files changed, 327 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 emulator.c create mode 100644 hello.asm create mode 100644 include/cpu.h create mode 160000 include/slibs create mode 100644 inspector.c create mode 100644 roms/breakout create mode 100644 roms/maze create mode 100644 roms/particle create mode 100644 roms/pong create mode 100644 roms/test create mode 100644 roms/test.asm create mode 100644 roms/zeropong diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dbb7603 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/emulator +/hello +/inspector \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b28ae1c --- /dev/null +++ b/Makefile @@ -0,0 +1,8 @@ +emulator: emulator.c + gcc emulator.c -o emulator -Iinclude -g -lraylib -lGL -lm -lpthread -ldl -lrt -lX11 +inspector: inspector.c + gcc inspector.c -o inspector -Iinclude -g +hello: hello.asm + c8asm hello.asm -o hello +run: hello emulator + ./emulator hello diff --git a/emulator.c b/emulator.c new file mode 100644 index 0000000..1aaf2c9 --- /dev/null +++ b/emulator.c @@ -0,0 +1,206 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define __DEBUG + +#ifndef __DEBUG +#define printf +#endif + +CPU cpu = { 0 }; +uint8_t display[64 * 32]; + +int main(int argc, char** argv) { + assert(argc > 1); + + srand(time(0)); + + sl_string code = { 0 }; + sl_read_file(argv[1], &code); + memcpy(cpu.mem + 512, code.data, code.size); + + InitWindow(1280, 640, "Chip8"); + SetTargetFPS(60); + + for(cpu.pc = 512; cpu.pc < 512 + code.size; cpu.pc += 2) { + uint8_t current = cpu.mem[cpu.pc] & 0xFF; + uint8_t a = (current >> 4) & 0xF; + uint8_t b = current & 0xF; + + uint8_t next = cpu.mem[cpu.pc + 1] & 0xFF; + uint8_t c = (next >> 4) & 0xF; + uint8_t d = next & 0xF; + + uint16_t address = b << 8 | c << 4 | d; + + switch(a) { + case 0x0: + switch(d) { + case 0x0: + printf("CLS\n"); + BeginDrawing(); + ClearBackground(BLACK); + EndDrawing(); + break; + case 0xE: + printf("RET\n"); + break; + } + break; + case 0x1: + cpu.pc = address - 2; + printf("JP 0x%X\n", address); + break; + case 0x3: + if(cpu.regs[b] == next) + cpu.pc += 2; + printf("SE V%X, 0x%X\n", b, next); + break; + case 0x4: + if(cpu.regs[b] != next) + cpu.pc += 2; + printf("SNE V%X, 0x%X\n", b, next); + break; + case 0x5: + if(cpu.regs[b] == cpu.regs[c]) + cpu.pc += 2; + break; + case 0x6: + cpu.regs[b] = next; + printf("LD V%X, 0x%X\n", b, next); + break; + case 0x7: + cpu.regs[b] += next; + printf("ADD V%X, 0x%X\n", b, next); + break; + case 0x8: + switch(d) { + case 0x0: + cpu.regs[b] = cpu.regs[c]; + printf("LD V%X, V%X\n", b, c); + break; + case 0x1: + cpu.regs[b] |= cpu.regs[c]; + printf("OR V%X, V%X\n", b, c); + break; + case 0x2: + cpu.regs[b] &= cpu.regs[c]; + printf("AND V%X, V%X"); + break; + case 0x3: + cpu.regs[b] ^= cpu.regs[c]; + break; + case 0x4: + cpu.regs[b] += cpu.regs[c]; + printf("ADD V%X, V%X\n", b, c); + break; + case 0x5: + cpu.regs[b] -= cpu.regs[c]; + printf("SUB V%X, V%X\n", b, c); + break; + case 0x6: + cpu.regs[0xF] = cpu.regs[b] & 1; + cpu.regs[b] >>= 1; + break; + case 0x7: + cpu.regs[b] = cpu.regs[c] - cpu.regs[b]; + break; + case 0xE: + cpu.regs[0xF] = (cpu.regs[b] & 0xFF != 0) ? 1 : 0; + cpu.regs[b] <<= 1; + break; + } + break; + case 0x9: + if(cpu.regs[b] != cpu.regs[c]) + cpu.pc += 2; + break; + case 0xA: + cpu.I = address; + printf("LD I, 0x%016X\n", address); + break; + case 0xB: + cpu.pc = cpu.regs[0] + address; + case 0xC: + cpu.regs[b] = rand() & next; + printf("RND V%X, 0x%X (out: 0x%08X)\n", b, next, cpu.regs[b]); + break; + case 0xD: + int xPos = cpu.regs[b]; + int yPos = cpu.regs[c]; + int height = d; + + BeginDrawing(); + for(int y = 0; y < height; y++) { + uint8_t line = cpu.mem[cpu.I + y]; + for(int x = 0; x < 8; x++) { + uint8_t pixel = line & (0x80 >> x); + if(pixel != 0) { + int totalX = xPos + x; + int totalY = yPos + y; + + totalX = totalX % 64; + totalY = totalY % 32; + + int index = (totalY * 64) + totalX; + + if(display[index] == 1) + cpu.regs[0xF] = 1; + + display[index] ^= 1; + + DrawRectangle((xPos + x) * 20, (yPos + y) * 20, 20, 20, display[index] == 0 ? BLACK : WHITE); + } + } + } + EndDrawing(); + + printf("DRW V%X, V%X, %d\n", b, c, height); + + break; + case 0xF: + switch(next) { + case 0x1E: + cpu.I += cpu.regs[b]; + printf("ADD I, V%X\n", b); + break; + case 0x29: + // TODO: unimplemented + break; + case 0x33: + // TODO: unimplemented + break; + case 0x55: + int offset = 0; + for(int i = 0; i <= b; i++) { + cpu.mem[cpu.I + offset++] = cpu.regs[i]; + } + break; + case 0x65: + offset = 0; + for(int i = 0; i <= b; i++) { + cpu.regs[i] = cpu.mem[cpu.I + offset++]; + } + printf("RSTR V%X\n", b); + break; + } + break; + } + } + + printf("\nRegisters:\n"); + for(int i = 0; i < sl_array_len(cpu.regs); i++) { + printf("V%X:\t0b%08b\t\t0x%08X\n", i, cpu.regs[i], cpu.regs[i]); + } + printf("I:\t0b%016b\t0x%016X\n", cpu.I, cpu.I); + + CloseWindow(); + + return 0; +} diff --git a/hello.asm b/hello.asm new file mode 100644 index 0000000..e5c2666 --- /dev/null +++ b/hello.asm @@ -0,0 +1,2 @@ +ld V0, 5 +add V0, 10 diff --git a/include/cpu.h b/include/cpu.h new file mode 100644 index 0000000..36ab1fd --- /dev/null +++ b/include/cpu.h @@ -0,0 +1,13 @@ +#include + +#ifndef __CPU_H__ +#define __CPU_H__ + +typedef struct CPU { + uint16_t pc; + uint8_t mem[4096]; + uint8_t regs[16]; + uint16_t I; +} CPU; + +#endif diff --git a/include/slibs b/include/slibs new file mode 160000 index 0000000..63edb93 --- /dev/null +++ b/include/slibs @@ -0,0 +1 @@ +Subproject commit 63edb9343b6787883a667b69ece86bde005ab311 diff --git a/inspector.c b/inspector.c new file mode 100644 index 0000000..ce63ba8 --- /dev/null +++ b/inspector.c @@ -0,0 +1,16 @@ +#include +#include +#include + +int main(int argc, char** argv) { + assert(argc > 1); + + sl_string code = { .size = 0x200 }; + sl_read_file(argv[1], &code); + + for(int i = 0x200; i < code.size; i += 2) { + printf("L%03X: %02X%02X\n", i, code.data[i] & 0xFF, code.data[i + 1] & 0xFF); + } + + return 0; +} diff --git a/roms/breakout b/roms/breakout new file mode 100644 index 0000000000000000000000000000000000000000..70b50dbc2399a49032ea11711160eec4754bd994 GIT binary patch literal 232 zcmZ2d)Yz{*n%e#d+|MJ*?B+~`Kh9egQ{TYR3I5DI!I@zcE zcV-Ye=PWO@1f;9Ur8(r1p*^FJle3)Aj4Y;K5@n2wP6>5fGK83Rl+lzi6)2m<{7V9) zzWBe88Hg6rSakM+;Gz%Tj6bC&OQcAD(7fzg%=!^begcw0Cq6X%|G(&7vS^aPWz|KG VlVvZf3axtAb*`uDUB{V@cL2dMVebF{ literal 0 HcmV?d00001 diff --git a/roms/maze b/roms/maze new file mode 100644 index 0000000000000000000000000000000000000000..0dca98194909931a4c217eca56340d0b693542d8 GIT binary patch literal 38 tcmYdbNMu-~bcoT2agp2wkpdP22O*XOhC&uY1tAt8nFa?10R@K!0RXgI2xI^N literal 0 HcmV?d00001 diff --git a/roms/particle b/roms/particle new file mode 100644 index 0000000000000000000000000000000000000000..2df976558b145545accfb5199f978892c78c0f7d GIT binary patch literal 353 zcmZ3in81+8ki>C8^pjkJ3UOo_YdTXFgV2s_h8%`PH-5`~PE|Um6eE-bBCAVk2;mts6*CG= zQJMqR5h~=fnBkY)htwv9D#jG~E~f;AM7ajOM!Cbx#XlI{f$WBejY9*YX&YuB0(%FeM~>5dFYzz#t@bT)dnk^Z%j`*9svj5*QyW ztp1n40wkFd;4%tW-tkfRsQ$^-*V-rKOfC+<;QURlbMGB*kM#EDFh7^WH2A3=@NC*kl MH7?z|cV=T90HE3tP5=M^ literal 0 HcmV?d00001 diff --git a/roms/test.asm b/roms/test.asm new file mode 100644 index 0000000..9434160 --- /dev/null +++ b/roms/test.asm @@ -0,0 +1,78 @@ +; Companion Cube Sample program. Disregard its advice. + +; Assemble the program to a CHIP-8 binary like so: +; $ ./c8asm -o GAMES/CUBE8.ch8 examples/cube.asm +; Once you've assembled it, you can run it in the interpreter like so: +; $ ./chip8 -f FFB6F8 -b B2B2B2 GAMES/CUBE.ch8 + +; Define some more human-friendly names for the registers +define boxx V0 ; Sprite X,Y position +define boxy V1 +define oldx V2 ; Previous sprite X,Y position +define oldy V3 +define dirx V4 ; Sprite direction +define diry V5 +define tmp VE + +; Clear the screen +CLS + +SYS 1 ; SYS instructions are treated as no-ops + +; Load the variables' initial values +LD boxx, 1 +LD dirx, 1 +LD boxy, 10 +LD I, sprite1 +DRW boxx, boxy, 8 +LD tmp, 1 + +; The main loop +loop: + ; Store the current position +LD oldx, boxx +LD oldy, boxy + + ; If the X direction is 0, go to sub1... +SE dirx, 0 +JP sub1 + + ; ...otherwise add 1 to the box' position +ADD boxx, 1 + + ; If you reached the right edge of the screen, change direction + SNE boxx, 56 +LD dirx, 1 +jp draw1 +sub1: + ; subtract 1 from the box' position +SUB boxx, tmp + + ; If you reached the left edge of the screen, change direction +SNE boxx, 0 +LD dirx, 0 + +; Draw the box +draw1: + ; Load the address of the sprite's graphics into register I +LD I, sprite1 + ; Erase the sprite at the old position +DRW oldx, oldy, 8 + ; Draw the sprite at the new position +DRW boxx, boxy, 8 + + ; Return to the start of the loop + JP loop + +; Binary data of the sprite. +; 1s represent pixels to be drawn, 0s are blank pixels. +sprite1: +db %01111110, + %10000001, + %10100101, + %10111101, + %10111101, + %10011001, + %10000001, + %01111110, + diff --git a/roms/zeropong b/roms/zeropong new file mode 100644 index 0000000000000000000000000000000000000000..3715699205369c16ebd1ac48f61ab048f070b9ce GIT binary patch literal 184 zcmWeCN?`sVx@bcJ*MoUNV%OU$7_YYpDJ5_Nc}hUu|LbiD96+|g&1F@LH