chip8

CHIP-8 emulator
git clone git://git.christosmarg.xyz/chip8.git
Log | Files | Refs | README | LICENSE

commit 3d1643c9f31a84a19fedfc32e22a66098562eb57
parent ec41dcbf9e85f1ab6c4859fbf3e07f0887e6686f
Author: Christos Margiolis <christos@margiolis.net>
Date:   Mon, 19 Oct 2020 12:11:11 +0300

simplified code using mask macros

Diffstat:
Mchip8.c | 262+++++++++++++++++++++++++++++++++++++++++--------------------------------------
1 file changed, 135 insertions(+), 127 deletions(-)

diff --git a/chip8.c b/chip8.c @@ -1,5 +1,6 @@ /* See LICENSE file for copyright and license details. */ +#include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -16,8 +17,12 @@ typedef unsigned char uint8_t #include <inttypes.h> #endif /* _WIN_32 */ -#define TRUE 1 -#define FALSE 0 +#define VX_MASK(x) ((x & 0x0f00) >> 8) +#define VY_MASK(x) ((x & 0x00f0) >> 4) +#define NN_MASK(x) (x & 0x00ff) +#define NNN_MASK(x) (x & 0x0fff) +#define EXECUTE(pc) do { pc += 2; } while (0) +#define ROM_SIZE_MAX (4096 - 512) struct Chip8 { uint16_t I; @@ -42,12 +47,13 @@ static const uint8_t keymap[16] = { }; static void chip8_init(struct Chip8 *); -static int romload(struct Chip8 *, const char *); +static void romload(struct Chip8 *, const char *); static void emulate(struct Chip8 *); static int decode(struct Chip8 *); static void timers_update(struct Chip8 *); static int evts(struct Chip8 *); static void render(SDL_Renderer *, SDL_Texture *, struct Chip8 *); +static void die(const char *, ...); #define I chip8->I #define opcode chip8->opcode @@ -67,22 +73,22 @@ chip8_init(struct Chip8 *chip8) { int i; uint8_t 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 + 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 }; pc = 0x200; opcode = 0; @@ -99,7 +105,7 @@ chip8_init(struct Chip8 *chip8) ; } -int +void romload(struct Chip8 *chip8, const char *fpath) { FILE *rom; @@ -108,35 +114,24 @@ romload(struct Chip8 *chip8, const char *fpath) int i; char *buf; - if ((rom = fopen(fpath, "rb")) == NULL) { - fprintf(stderr, "Error loading ROM (%s). Exiting. . .\n", fpath); - return FALSE; - } + if ((rom = fopen(fpath, "rb")) == NULL) + die("error loading ROM (%s).", fpath); fseek(rom, 0, SEEK_END); romsize = ftell(rom); rewind(rom); - if ((buf = (char *)malloc(romsize * sizeof(char))) == NULL) { - fputs("Cannot allocate memory. Exiting. . .\n", stderr); - return FALSE; - } - - if ((res = fread(buf, sizeof(char), (size_t)romsize, rom)) != romsize) { - fputs("Error reading ROM. Exiting. . .\n", stderr); - return FALSE; - } - - if ((4096 - 512) > romsize) + if ((buf = malloc(romsize * sizeof(char))) == NULL) + die("cannot allocate memory."); + if ((res = fread(buf, sizeof(char), (size_t)romsize, rom)) != romsize) + die("error reading ROM."); + if (romsize < ROM_SIZE_MAX) for (i = 0; i < romsize; i++) memory[i + 512] = (uint8_t)buf[i]; - else { - fputs("ROM can't fit into memory. Exiting. . .\n", stderr); - return FALSE; - } + else + die("ROM cannot fit into memory."); fclose(rom); free(buf); - return TRUE; } void @@ -144,7 +139,7 @@ emulate(struct Chip8 *chip8) { opcode = memory[pc] << 8 | memory[pc + 1]; // fetch if (decode(chip8)) { - pc += 2; // execute + EXECUTE(pc); // execute timers_update(chip8); } printf("Opcode: %x\tMemory: %x\tI: %x\tSP: %x\tPC: %d\n", @@ -158,101 +153,101 @@ decode(struct Chip8 *chip8) switch (opcode & 0xF000) { case 0x0000: // 00E_ - switch (opcode & 0x00FF) { + switch (NN_MASK(opcode)) { case 0xE0: // 00E0 - Clear screen memset(gfx, 0, 2048 * sizeof(uint8_t)); - drawflag = TRUE; + drawflag = 1; break; case 0xEE: // 00EE - Return from subroutine pc = stack[--sp]; break; default: fprintf(stderr, "Unknown opcode: %x\n", opcode); - return FALSE; + return 0; } break; case 0x1000: // 1NNN - Jump to address NNN - pc = (opcode & 0x0FFF) - 2; + pc = NNN_MASK(opcode) - 2; break; case 0x2000: // 2NNN - Call subroutine at NNN stack[sp++] = pc; - pc = (opcode & 0x0FFF) - 2; + pc = NNN_MASK(opcode) - 2; case 0x3000: // 3NNN - Skip next instruction if VX == NN - if (V[(opcode & 0x0F00) >> 8] == (opcode & 0x00FF)) - pc += 2; + if (V[VX_MASK(opcode)] == NN_MASK(opcode)) + EXECUTE(pc); break; case 0x4000: // 4NNN - Skip next instruction if VX != NN - if (V[(opcode & 0x0F00) >> 8] != (opcode & 0x00FF)) - pc += 2; + if (V[VX_MASK(opcode)] != NN_MASK(opcode)) + EXECUTE(pc); break; case 0x5000: // 5XY0 - Skip next instruction if VX == VY - if (V[(opcode & 0x0F00) >> 8] == V[(opcode & 0x00F0) >> 4]) - pc += 2; + if (V[VX_MASK(opcode)] == V[VY_MASK(opcode)]) + EXECUTE(pc); break; case 0x6000: // 6XNN - Set VX to NN - V[(opcode & 0x0F00) >> 8] = opcode & 0x00FF; + V[VX_MASK(opcode)] = NN_MASK(opcode); break; case 0x7000: // 7XNN - Add NN to VX - V[(opcode & 0x0F00) >> 8] += opcode & 0x00FF; + V[VX_MASK(opcode)] += NN_MASK(opcode); break; case 0x8000: // 8XY_ switch (opcode & 0x000F) { case 0x0000: // 8XY0 - Set VX to VY - V[(opcode & 0x0F00) >> 8] = V[(opcode & 0x00F0) >> 4]; + V[VX_MASK(opcode)] = V[VY_MASK(opcode)]; break; case 0x0001: // 8XY1 - Set VX to (VX OR VY) - V[(opcode & 0x0F00) >> 8] |= V[(opcode & 0x00F0) >> 4]; + V[VX_MASK(opcode)] |= V[VY_MASK(opcode)]; break; case 0x0002: // 8XY2 - Set VX to (VX AND VY) - V[(opcode & 0x0F00) >> 8] &= V[(opcode & 0x00F0) >> 4]; + V[VX_MASK(opcode)] &= V[VY_MASK(opcode)]; break; case 0x0003: // 8XY3 - Set VX to (VX XOR VY) - V[(opcode & 0x0F00) >> 8] ^= V[(opcode & 0x00F0) >> 4]; + V[VX_MASK(opcode)] ^= V[VY_MASK(opcode)]; break; case 0x0004: // 8XY4 - Add VY to VX, VF = 1 if there is a carry - V[(opcode & 0x0F00) >> 8] += V[(opcode & 0x00F0) >> 4]; - V[0xF] = (V[(opcode & 0x00F0) >> 4] > (0xFF - V[(opcode & 0x0F00) >> 8])) ? 1 : 0; + V[VX_MASK(opcode)] += V[VY_MASK(opcode)]; + V[0xF] = (V[VY_MASK(opcode)] > (0xFF - V[VX_MASK(opcode)])) ? 1 : 0; break; case 0x0005: // 8XY5 - Sub VY from VX, VF = 0 if there is a borrow - V[0xF] = (V[(opcode & 0x00F0) >> 4] > V[(opcode & 0x0F00) >> 8]) ? 0 : 1; - V[(opcode & 0x0F00) >> 8] -= V[(opcode & 0x00F0) >> 4]; + V[0xF] = (V[VY_MASK(opcode)] > V[VX_MASK(opcode)]) ? 0 : 1; + V[VX_MASK(opcode)] -= V[VY_MASK(opcode)]; break; case 0x0006: // 8XY6 - Shift VX right by 1. VF = LSB of VX before shift - V[0xF] = V[(opcode & 0x0F00) >> 8] & 0x1; - V[(opcode & 0x0F00) >> 8] >>= 1; + V[0xF] = V[VX_MASK(opcode)] & 0x1; + V[VX_MASK(opcode)] >>= 1; break; case 0x0007: // 8XY7 - Set VX to VY-VX. VF = 0 if there is a borrow - V[0xF] = (V[(opcode & 0x0F00) >> 8] > V[(opcode & 0x00F0) >> 4]) ? 0 : 1; - V[(opcode & 0x0F00) >> 8] = V[(opcode & 0x00F0) >> 4] - V[(opcode & 0x0F00) >> 8]; + V[0xF] = (V[VX_MASK(opcode)] > V[VY_MASK(opcode)]) ? 0 : 1; + V[VX_MASK(opcode)] = V[VY_MASK(opcode)] - V[VX_MASK(opcode)]; break; case 0x000E: // 8XYE - Shift VX left by 1. VF = MSB of VX before shift - V[0xF] = V[(opcode & 0x0F00) >> 8] >> 7; - V[(opcode & 0x0F00) >> 8] <<= 1; + V[0xF] = V[VX_MASK(opcode)] >> 7; + V[VX_MASK(opcode)] <<= 1; break; default: fprintf(stderr, "Unknown opcode: %x\n", opcode); - return FALSE; + return 0; } break; case 0x9000: // 9XY0 - Skip next instruction if VX != VY - if (V[(opcode & 0x0F00) >> 8] != V[(opcode & 0x00F0) >> 4]) - pc += 2; + if (V[VX_MASK(opcode)] != V[VY_MASK(opcode)]) + EXECUTE(pc); break; case 0xA000: // ANNN - Set I to the address NNN - I = opcode & 0x0FFF; + I = NNN_MASK(opcode); break; case 0xB000: // BNNN - Jump to NNN + V0 - pc = ((opcode & 0x0FFF) + V[0]) - 2; + pc = ((NNN_MASK(opcode)) + V[0]) - 2; break; case 0xC000: // CNNN - Set VX to random number masked by NN - V[(opcode & 0x0F00) >> 8] = (rand() % (0xFF + 1)) & (opcode & 0x00FF); + V[VX_MASK(opcode)] = (rand() % (0xFF + 1)) & (NN_MASK(opcode)); break; case 0xD000: { // Draw an 8 pixel sprite at (VX, VY) int yl, xl; uint16_t h = opcode & 0x000F; uint16_t pixel; - uint8_t VX = V[(opcode & 0x0F00) >> 8]; - uint8_t VY = V[(opcode & 0x00F0) >> 4]; + uint8_t VX = V[VX_MASK(opcode)]; + uint8_t VY = V[VY_MASK(opcode)]; V[0xF] = 0; for (yl = 0; yl < h; yl++) { @@ -265,89 +260,89 @@ decode(struct Chip8 *chip8) } } } - drawflag = TRUE; + drawflag = 1; } break; case 0xE000: // EX__ - switch (opcode & 0x00FF) { + switch (NN_MASK(opcode)) { case 0x009E: // EX9E - Skip next instruction if key in VX is pressed - if (keys[V[(opcode & 0x0F00) >> 8]]) - pc += 2; + if (keys[V[VX_MASK(opcode)]]) + EXECUTE(pc); break; case 0x00A1: // EXA1 - Skip next instruction if key in VX isn't pressed - if (!keys[V[(opcode & 0x0F00) >> 8]]) - pc += 2; + if (!keys[V[VX_MASK(opcode)]]) + EXECUTE(pc); break; default: fprintf(stderr, "Unknown opcode: %x\n", opcode); - return FALSE; + return 0; } break; case 0xF000: // FX__ - switch (opcode & 0x00FF) { + switch (NN_MASK(opcode)) { case 0x0007: // FX07 - Set VX to delaytimer - V[(opcode & 0x0F00) >> 8] = delaytimer; + V[VX_MASK(opcode)] = delaytimer; break; case 0x000A: { // FX0A - Wait for key press and then store it in VX - int keypressed = FALSE; + int keypressed = 0; for (i = 0; i < 16; i++) { if (keys[i]) { - V[(opcode & 0x0F00) >> 8] = i; - keypressed = TRUE; + V[VX_MASK(opcode)] = i; + keypressed = 1; } } if (!keypressed) - return FALSE; + return 0; } break; case 0x0015: // FX15 - Set the delaytimer to VX - delaytimer = V[(opcode & 0x0F00) >> 8]; + delaytimer = V[VX_MASK(opcode)]; break; case 0x0018: // FX18 - Set the soundtimer to VX - soundtimer = V[(opcode & 0x0F00) >> 8]; + soundtimer = V[VX_MASK(opcode)]; break; case 0x001E: // FX1E - Add VX to I - V[0xF] = ((I + V[(opcode & 0x0F00) >> 8]) > 0xFFF) ? 1 : 0; - I += V[(opcode & 0x0F00) >> 8]; + V[0xF] = ((I + V[VX_MASK(opcode)]) > 0xFFF) ? 1 : 0; + I += V[VX_MASK(opcode)]; break; case 0x0029: // FX29 - Set I to the location of the sprite for char VX - I = V[(opcode & 0x0F00) >> 8] * 0x5; + I = V[VX_MASK(opcode)] * 0x5; break; case 0x0033: // FX33 - Store bin coded decimal of VX at I, I+1 and I+2 - memory[I] = V[(opcode & 0x0F00) >> 8] / 100; - memory[I+1] = (V[(opcode & 0x0F00) >> 8] / 10) % 10; - memory[I+2] = V[(opcode & 0x0F00) >> 8] % 10; + memory[I] = V[VX_MASK(opcode)] / 100; + memory[I+1] = (V[VX_MASK(opcode)] / 10) % 10; + memory[I+2] = V[VX_MASK(opcode)] % 10; break; case 0x0055: // FX55 - Store V0 to VX in memory starting at I - for (i = 0; i <= ((opcode & 0x0F00) >> 8); i++) + for (i = 0; i <= (VX_MASK(opcode)); i++) memory[I + i] = V[i]; - I += ((opcode & 0x0F00) >> 8) + 1; + I += (VX_MASK(opcode)) + 1; break; case 0x0065: // FX65 - Fill V0 to VX with vals from memory starting at I - for (i = 0; i <= ((opcode & 0x0F00) >> 8); i++) + for (i = 0; i <= (VX_MASK(opcode)); i++) V[i] = memory[I + i]; - I += ((opcode & 0x0F00) >> 8) + 1; + I += (VX_MASK(opcode)) + 1; break; default: fprintf(stderr, "Unknown opcode: %x\n", opcode); - return FALSE; + return 0; } break; default: fputs("Unimplemented opcode\n", stderr); - return FALSE; + return 0; } - return TRUE; + return 1; } void timers_update(struct Chip8 *chip8) { if (delaytimer > 0) - --delaytimer; + delaytimer--; if (soundtimer > 0) - --soundtimer; + soundtimer--; } #undef V @@ -371,17 +366,17 @@ evts(struct Chip8 *chip8) while (SDL_PollEvent(&e)) { if (e.type == SDL_QUIT || e.key.keysym.sym == SDLK_ESCAPE) - return FALSE; + return 0; if (e.type == SDL_KEYDOWN) for (i = 0; i < 16; i++) if (e.key.keysym.sym == keymap[i]) - chip8->keys[i] = TRUE; + chip8->keys[i] = 1; if (e.type == SDL_KEYUP) for (i = 0; i < 16; i++) if (e.key.keysym.sym == keymap[i]) - chip8->keys[i] = FALSE; + chip8->keys[i] = 0; } - return TRUE; + return 1; } void @@ -391,7 +386,7 @@ render(SDL_Renderer *ren, SDL_Texture *tex, struct Chip8 *chip8) uint32_t pixels[2048]; uint8_t pixel; - chip8->drawflag = FALSE; + chip8->drawflag = 0; for (i = 0; i < 2048; i++) { pixel = chip8->gfx[i]; pixels[i] = (0x00FFFFFF * pixel) | 0xFF000000; @@ -400,22 +395,38 @@ render(SDL_Renderer *ren, SDL_Texture *tex, struct Chip8 *chip8) SDL_RenderClear(ren); SDL_RenderCopy(ren, tex, NULL, NULL); SDL_RenderPresent(ren); - } +} + +void +die(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + + if (fmt[0] && fmt[strlen(fmt)-1] == ':') { + fputc(' ', stderr); + perror(NULL); + } else + fputc('\n', stderr); + + exit(EXIT_FAILURE); +} int main(int argc, char **argv) { + struct Chip8 chip8; int w = 1024, h = 512; srand(time(NULL)); - if (argc != 2) { - fprintf(stderr, "Usage: %s [ROM]\n", argv[0]); - return EXIT_FAILURE; - } - if (SDL_Init(SDL_INIT_EVERYTHING) < 0) { - fputs("Cannot initialize SDL. Exiting. . .\n", stderr); - return EXIT_FAILURE; - } + if (argc != 2) + die("usage: %s [ROM]", argv[0]); + if (SDL_Init(SDL_INIT_EVERYTHING) < 0) + die("cannot initalize SDL: %s", SDL_GetError()); + SDL_Window *win = SDL_CreateWindow("CHIP-8 Emulator", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, w, h, SDL_WINDOW_SHOWN); @@ -423,15 +434,12 @@ main(int argc, char **argv) SDL_RenderSetLogicalSize(ren, w, h); SDL_Texture *tex = SDL_CreateTexture(ren, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 64, 32); - if (!win || !ren || !tex) { - fprintf(stderr, "SDL error. Exiting. . .\n%s\n", SDL_GetError()); - return EXIT_FAILURE; - } - struct Chip8 chip8; + if (!win || !ren || !tex) + die("SDL error: %s", SDL_GetError()); + chip8_init(&chip8); - if (!romload(&chip8, argv[1])) - return EXIT_FAILURE; + romload(&chip8, argv[1]); while (evts(&chip8)) { emulate(&chip8);