first commit

This commit is contained in:
raxracks 2024-01-27 12:20:21 +13:00
commit 01cccf387d
14 changed files with 327 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
/emulator
/hello
/inspector

8
Makefile Normal file
View file

@ -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

206
emulator.c Normal file
View file

@ -0,0 +1,206 @@
#include <stdio.h>
#include <slibs/slibs.h>
#include <assert.h>
#include <stdint.h>
#include <cpu.h>
#include <raylib.h>
#include <math.h>
#include <time.h>
#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;
}

2
hello.asm Normal file
View file

@ -0,0 +1,2 @@
ld V0, 5
add V0, 10

13
include/cpu.h Normal file
View file

@ -0,0 +1,13 @@
#include <stdint.h>
#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

1
include/slibs Submodule

@ -0,0 +1 @@
Subproject commit 63edb9343b6787883a667b69ece86bde005ab311

16
inspector.c Normal file
View file

@ -0,0 +1,16 @@
#include <stdio.h>
#include <slibs/slibs.h>
#include <assert.h>
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;
}

BIN
roms/breakout Normal file

Binary file not shown.

BIN
roms/maze Normal file

Binary file not shown.

BIN
roms/particle Normal file

Binary file not shown.

BIN
roms/pong Normal file

Binary file not shown.

BIN
roms/test Normal file

Binary file not shown.

78
roms/test.asm Normal file
View file

@ -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,

BIN
roms/zeropong Normal file

Binary file not shown.