From b7edca10c2e679c161f976ad6c1f1ee2a78b4b61 Mon Sep 17 00:00:00 2001 From: Franck STAUFFER Date: Mon, 3 Aug 2020 15:02:03 +0200 Subject: [PATCH] Initial commit --- Makefile | 28 +++ README.md | 1 + src/main.c | 597 +++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/TEST | Bin 0 -> 478 bytes 4 files changed, 626 insertions(+) create mode 100644 Makefile create mode 100644 README.md create mode 100644 src/main.c create mode 100644 tests/TEST diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..91b5d86 --- /dev/null +++ b/Makefile @@ -0,0 +1,28 @@ +CPPFLAGS = -D_DEFAULT_SOURCE -D_FOTIFY_SOURCE=2 +CFLAGS = $(CPPFLAGS) -std=c99 -pedantic -Wdefault -Wextra -march=native -mtune=native -O2 +LDFLAGS = -Wl,-z,relro,-z,now,-O2 -lSDL + +default: bin obj bin/chip8 + +bin: + mkdir bin + +obj: + mkdir obj + +clean: + rm -rf bin obj + +bin/chip8: obj/main.o + $(CC) -o $@ $^ $(LDFLAGS) + +obj/%.o: src/%.c + $(CC) $(CFLAGS) -c -o $@ $^ + +format: + clang-format -i -style="{BasedOnStyle: mozilla, IndentWidth: 4}" src/main.c + +.PHONY: test +test: default + @./bin/chip8 tests/TEST + diff --git a/README.md b/README.md new file mode 100644 index 0000000..720ad5c --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# CHIP-8 diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..7d96d27 --- /dev/null +++ b/src/main.c @@ -0,0 +1,597 @@ +#include "SDL/SDL.h" +#include +#include +#include +#include +#include +#include +#include + +#define X (op & 0x0F00) >> 8 +#define Y (op & 0x00F0) >> 4 +#define N op & 0x000F +#define NN op & 0x00FF +#define NNN op & 0x0FFF + +#ifndef SCALE +#define SCALE 20 +#endif + +#ifndef WAIT +#define WAIT 100 +#endif + +typedef uint8_t byte; +typedef uint16_t word; + +byte memory[4096]; +byte v[16]; +word i = 0; +word pc = 0x200; +word sp = 0; +word stack[16]; +byte dt = 0; +byte st = 0; +byte key[16]; +byte gfx[64][32]; +byte oldgfx[64][32]; +word op = 0; + +SDL_Surface* s; +SDL_Event e; + +Uint8 fg; +Uint8 bg; + +Uint8* wav_buf; + +void +init_chip8(void) +{ + srand(time(NULL)); + + for (register uint_fast8_t t = 0; t < 16; ++t) { + v[t] = 0; + stack[t] = 0; + key[t] = 0; + } + + for (register uint_fast8_t x = 0; x < 64; ++x) + for (register uint_fast8_t y = 0; y < 32; ++y) + gfx[x][y] = 0; + + const byte fontset[80] = { + 0xF0, 0x90, 0x90, 0x90, 0xF0, // 0 + 0x20, 0x60, 0x20, 0x20, 0x70, // 1 + 0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2 + 0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3 + 0x90, 0x90, 0xF0, 0x10, 0x10, // 4 + 0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5 + 0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6 + 0xF0, 0x10, 0x20, 0x40, 0x40, // 7 + 0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8 + 0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9 + 0xF0, 0x90, 0xF0, 0x90, 0x90, // A + 0xE0, 0x90, 0xE0, 0x90, 0xE0, // B + 0xF0, 0x80, 0x80, 0x80, 0xF0, // C + 0xE0, 0x90, 0x90, 0x90, 0xE0, // D + 0xF0, 0x80, 0xF0, 0x80, 0xF0, // E + 0xF0, 0x80, 0xF0, 0x80, 0x80 // F + }; + + for (register uint_fast8_t t = 0; t < 80; ++t) + memory[t] = fontset[t]; + + for (register uint_fast16_t t = 80; t < 4096; ++t) + memory[t] = 0; +} + +void +cleanup(void) +{ + SDL_FreeSurface(s); + SDL_Quit(); +} + +uint_fast8_t +init_SDL(const char* path) +{ + if (SDL_Init(SDL_INIT_VIDEO)) { + fputs("ERROR: Failed to initialize SDL\n", stderr); + return 0; + } + + s = SDL_SetVideoMode(64 * SCALE, 32 * SCALE, 8, SDL_HWSURFACE); + if (!s) { + fputs("ERROR: Failed to create SDL surface\n", stderr); + return 0; + } + + if (atexit(cleanup)) { + fputs("ERROR: Failed to set exit function\n", stderr); + SDL_FreeSurface(s); + SDL_Quit(); + return 0; + } + + char caption[7 + strlen(path)]; + sprintf(caption, "CHIP-8 - %s", path); + SDL_WM_SetCaption(caption, NULL); + + return 1; +} + +long +get_file_size(FILE* f) +{ + if (fseek(f, 0, SEEK_END)) { + fputs("ERROR: Failed to get file size\n", stderr); + return 0; + } + + const long s = ftell(f); + if (s == -1) { + fputs("ERROR: Failed to get file size\n", stderr); + return 0; + } + + rewind(f); + + return s; +} + +uint_fast8_t +load_rom(const char* path) +{ + FILE* f = fopen(path, "rb"); + if (!f) { + fputs("ERROR: Failed to open rom file\n", stderr); + return 0; + } + + const long s = get_file_size(f); + if (!s) { + if (fclose(f)) + fputs("ERROR: Failed to close rom file\n", stderr); + return 0; + } + + if (s > 3072) { + fputs("ERROR: Rom file too big\n", stderr); + if (fclose(f)) + fputs("ERROR: Failed to close rom file\n", stderr); + return 0; + } + + fread(memory + 0x200, sizeof(byte), s, f); + if (ferror(f)) { + fputs("ERROR: Failed to read rom file\n", stderr); + if (fclose(f)) + fputs("ERROR: Failed to close rom file\n", stderr); + return 0; + } + + if (fclose(f)) { + fputs("ERROR: Failed to close rom file\n", stderr); + return 0; + } + + fprintf(stderr, "INFO: %ldB loaded from %s/%s\n", s, getenv("PWD"), path); + + fputs("Loaded ROM:\n", stderr); + uint_fast8_t c = 0; + for (long t = 0; t < s; t += 2) { + fprintf( + stderr, "%04X ", (memory[t + 0x200] << 8) + memory[t + 0x200 + 1]); + if (c++ == 7) { + putchar('\n'); + c = 0; + } + } + putchar('\n'); + + return 1; +} + +static inline void +event_handler(void) +{ + if (e.type == SDL_KEYDOWN) { + switch (e.key.keysym.sym) { + case SDLK_0: + key[0x0] = 1; + break; + case SDLK_1: + key[0x1] = 1; + break; + case SDLK_2: + key[0x2] = 1; + break; + case SDLK_3: + key[0x3] = 1; + break; + case SDLK_4: + key[0x4] = 1; + break; + case SDLK_5: + key[0x5] = 1; + break; + case SDLK_6: + key[0x6] = 1; + break; + case SDLK_7: + key[0x7] = 1; + break; + case SDLK_8: + key[0x8] = 1; + break; + case SDLK_9: + key[0x9] = 1; + break; + case SDLK_q: + key[0xA] = 1; + break; + case SDLK_w: + key[0xB] = 1; + break; + case SDLK_e: + key[0xC] = 1; + break; + case SDLK_a: + key[0xD] = 1; + break; + case SDLK_s: + key[0xE] = 1; + break; + case SDLK_d: + key[0xF] = 1; + break; + case SDLK_ESCAPE: + exit(0); + default: + fputs("WARNING: Unhandled input detected\n", stderr); + } + } else if (e.type == SDL_KEYUP) { + switch (e.key.keysym.sym) { + case SDLK_0: + key[0x0] = 0; + break; + case SDLK_1: + key[0x1] = 0; + break; + case SDLK_2: + key[0x2] = 0; + break; + case SDLK_3: + key[0x3] = 0; + break; + case SDLK_4: + key[0x4] = 0; + break; + case SDLK_5: + key[0x5] = 0; + break; + case SDLK_6: + key[0x6] = 0; + break; + case SDLK_7: + key[0x7] = 0; + break; + case SDLK_8: + key[0x8] = 0; + break; + case SDLK_9: + key[0x9] = 0; + break; + case SDLK_q: + key[0xA] = 0; + break; + case SDLK_w: + key[0xB] = 0; + break; + case SDLK_e: + key[0xC] = 0; + break; + case SDLK_a: + key[0xD] = 0; + break; + case SDLK_s: + key[0xE] = 0; + break; + case SDLK_d: + key[0xF] = 0; + break; + default: + break; + } + } else if (e.type == SDL_QUIT) { + exit(0); + } +} + +static inline void +render(void) +{ + SDL_Rect r; + r.h = SCALE; + r.w = SCALE; + + for (register uint_fast8_t x = 0; x < 64; x++) + for (register uint_fast8_t y = 0; y < 32; y++) + if (gfx[x][y] ^ oldgfx[x][y]) { + r.x = x * SCALE; + r.y = y * SCALE; + SDL_FillRect(s, &r, (gfx[x][y]) ? fg : bg); + } + + SDL_Flip(s); +} + +static inline uint_fast8_t +op_handler(void) +{ + switch (op) { + case 0x00E0: + for (register uint_fast8_t x = 0; x < 64; ++x) + for (register uint_fast8_t y = 0; y < 32; ++y) + gfx[x][y] = 0; + + SDL_Rect r; + r.h = 32 * SCALE; + r.w = 64 * SCALE; + r.x = 0; + r.y = 0; + SDL_FillRect(s, &r, bg); + + pc += 2; + return 1; + case 0x00EE: + pc = stack[--sp]; + pc += 2; + return 1; + default: + switch (op & 0xF0FF) { + case 0xE09E: + if (key[v[X]]) { + pc += 2; + key[v[X]] = 0; + } + pc += 2; + return 1; + case 0xE0A1: + if (!key[v[X]]) + pc += 2; + pc += 2; + return 1; + case 0xF007: + v[X] = dt; + pc += 2; + return 1; + case 0xF00A: + for (register uint_fast8_t t = 0; t < 16; ++t) + if (key[t]) { + v[X] = t; + key[t] = 0; + pc += 2; + return 1; + } + return 1; + case 0xF015: + dt = v[X]; + pc += 2; + return 1; + case 0xF018: + st = v[X]; + pc += 2; + return 1; + case 0xF01E: + i += v[X]; + pc += 2; + return 1; + case 0xF029: + i = v[X] * 5; + pc += 2; + return 1; + case 0xF033: + memory[i] = v[X] / 100; + memory[i + 1] = (v[X] % 100) / 10; + memory[i + 2] = (v[X] % 100) % 10; + pc += 2; + return 1; + case 0xF055: + for (register uint_fast8_t t = 0; t < X; ++t) + memory[i + t] = v[t]; + pc += 2; + return 1; + case 0xF065: + for (register uint_fast8_t t = 0; t < X; ++t) + v[t] = memory[i + t]; + pc += 2; + return 1; + default: + switch (op & 0xF00F) { + case 0x5000: + if (v[X] == v[Y]) + pc += 2; + pc += 2; + return 1; + case 0x8000: + v[X] = v[Y]; + pc += 2; + return 1; + case 0x8001: + v[X] |= v[Y]; + pc += 2; + return 1; + case 0x8002: + v[X] &= v[Y]; + pc += 2; + return 1; + case 0x8003: + v[X] ^= v[Y]; + pc += 2; + return 1; + case 0x8004: + v[0xF] = (v[X] > 0xFF - v[Y]); + v[X] += v[Y]; + pc += 2; + return 1; + case 0x8005: + v[0xF] = (v[X] >= v[Y]); + v[X] -= v[Y]; + pc += 2; + return 1; + case 0x8006: + v[0xF] = v[X] & 0x0001; + v[X] >>= 1; + pc += 2; + return 1; + case 0x8007: + v[0xF] = (v[X] <= v[Y]); + v[X] = v[Y] - v[X]; + pc += 2; + return 1; + case 0x800E: + v[0xF] = v[X] >> 7; + v[X] <<= 1; + pc += 2; + return 1; + case 0x9000: + if (v[X] != v[Y]) + pc += 2; + pc += 2; + return 1; + default: + switch (op & 0xF000) { + case 0x1000: + pc = NNN; + return 1; + case 0x2000: + stack[sp++] = pc; + pc = NNN; + return 1; + case 0x3000: + if (v[X] == (NN)) + pc += 2; + pc += 2; + return 1; + case 0x4000: + if (v[X] != (NN)) + pc += 2; + pc += 2; + return 1; + case 0x6000: + v[X] = NN; + pc += 2; + return 1; + case 0x7000: + v[X] += NN; + pc += 2; + return 1; + case 0xA000: + i = NNN; + pc += 2; + return 1; + case 0xB000: + pc = v[0] + (NNN); + return 1; + case 0xC000: + v[X] = (rand() % 255) & NN; + pc += 2; + return 1; + case 0xD000: + for (register uint_fast8_t x = 0; x < 64; + ++x) + for (register uint_fast8_t y = 0; + y < 32; + ++y) + oldgfx[x][y] = gfx[x][y]; + + v[0xF] = 0; + + for (register uint_fast8_t x = 0; x < (N); + ++x) + for (register uint_fast8_t y = 0; y < 8; + ++y) + if (memory[i + x] & (0x80 >> y)) { + if (gfx[v[X] + y][v[Y] + x]) + v[0xF] = 1; + gfx[v[X] + y][v[Y] + x] ^= 1; + } + render(); + pc += 2; + return 1; + default: + fprintf(stderr, + "ERROR: Unhandled opcode: %04X\n", + op); + sleep(3); + return 0; + } + } + } + } +} + +void +timers() +{ + if (dt) + --dt; + + if (st) + --st; +} + +int +main(int argc, char** argv) +{ + if (argc < 2) { + fputs("USAGE: chip8 ", stderr); + return 1; + } + + if (access(argv[1], F_OK)) { + fputs("ERROR: Provided ROM file does not exist\n", stderr); + return 1; + } + + if (access(argv[1], R_OK)) { + fputs("ERROR: No read permission to provided ROM file\n", stderr); + return 1; + } + + init_chip8(); + + if (!load_rom(argv[1])) + return 1; + + if (!init_SDL(argv[1])) + return 1; + + fg = SDL_MapRGB(s->format, 255, 255, 255); + bg = SDL_MapRGB(s->format, 0, 0, 0); + + struct sigaction sa; + memset(&sa, 0, sizeof(struct sigaction)); + sa.sa_handler = timers; + if (sigaction(SIGALRM, &sa, 0)) { + fputs("ERROR: Failed to set SIGALRM handler", stderr); + return 1; + } + + ualarm(16667, 16667); + +loop: + op = (memory[pc] << 8) + memory[pc + 1]; + + while (SDL_PollEvent(&e)) + event_handler(); + + if (!op_handler()) + exit(1); + + usleep(WAIT); + + goto loop; +} diff --git a/tests/TEST b/tests/TEST new file mode 100644 index 0000000000000000000000000000000000000000..f540f69ecfc06e6c7b30881234469cc10847bff9 GIT binary patch literal 478 zcmYk&ze~eF6bJC8;uTt`rKMFO)T4t#DiW)7aF9c=aT1sA@kf%j0Y~YWsiDx-J-lv3 z1R;Mw{sLz$9o|@rh&s6H-K7zijmckgkn@idt{U=U!$7$S`jO^N|hYBI{u zLvEBJ7rsFj@J-|)dXX2fAb|8VOudV>H0vnc)n3+BEX}S%S^y zPBf8sb5KpBZAvM?b|SYwtvFSN-Nf$VlnRYlYR8@R?q-j=3S_x7MK{?=O%i)k6%A5AWWClvaIsC#&di4 cz5iuFT33MVtpI6c