mirror of
https://github.com/raxracks/chip8.git
synced 2024-07-07 21:31:18 +12:00
first commit
This commit is contained in:
commit
01cccf387d
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
/emulator
|
||||
/hello
|
||||
/inspector
|
8
Makefile
Normal file
8
Makefile
Normal 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
206
emulator.c
Normal 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;
|
||||
}
|
13
include/cpu.h
Normal file
13
include/cpu.h
Normal 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
1
include/slibs
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 63edb9343b6787883a667b69ece86bde005ab311
|
16
inspector.c
Normal file
16
inspector.c
Normal 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
BIN
roms/breakout
Normal file
Binary file not shown.
BIN
roms/particle
Normal file
BIN
roms/particle
Normal file
Binary file not shown.
78
roms/test.asm
Normal file
78
roms/test.asm
Normal 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
BIN
roms/zeropong
Normal file
Binary file not shown.
Loading…
Reference in a new issue