8

a CHIP-8 emulator
Log | Files | Refs | README

commit 9d18655339b743b72bd82f8d93c905e66dbac4bd
parent 9fe1ca723978ede2fb6785443ffa3a5a08dab310
Author: Naveen Narayanan <zerous@nocebo.space>
Date:   Sun, 21 Jul 2024 19:04:52 +0200

Use SDL

Diffstat:
M8.c | 257+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
MMakefile | 20++++++++++++++++++--
Aav.c | 151++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aav.h | 15+++++++++++++++
4 files changed, 330 insertions(+), 113 deletions(-)

diff --git a/8.c b/8.c @@ -1,6 +1,7 @@ #include <err.h> #include <fcntl.h> #include <limits.h> +#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -9,22 +10,26 @@ #include <sys/types.h> #include <sys/stat.h> +#include "av.h" + #define BUFSZ 256 +#define START 0x200 -unsigned short opcode; -unsigned char mem[4096]; -unsigned char V[16]; -unsigned short I; -unsigned short pc; -unsigned char gfx[64 * 32]; -unsigned char delaytmr; -unsigned char soundtmr; -unsigned short stack[16]; -unsigned short* sp; -unsigned char keys[16]; -unsigned char buf[BUFSZ]; +uint16_t opcode; +uint8_t *mem; +uint8_t V[16]; +uint16_t I; +uint16_t pc; +uint8_t scene[32*64]; +uint8_t delaytmr; +uint8_t soundtmr; +uint16_t stack[16]; +uint16_t *sp = stack; +uint8_t keys[16]; +uint8_t buf[BUFSZ]; +uint8_t bit[8]; -unsigned char fnt[80] = +uint8_t fnt[80] = { 0xF0, 0x90, 0x90, 0x90, 0xF0, //0 0x20, 0x60, 0x20, 0x20, 0x70, //1 @@ -45,111 +50,107 @@ unsigned char fnt[80] = }; static void -init() +init(int fd, size_t sz) { - pc = 0x200; - opcode = 0; - I = 0; - sp = stack; - - for (int i = 0; i < 80; ++i) - mem[i] = fnt[i]; - - for (int i = 0; i < BUFSZ; ++i) - mem[i + 512] = buf[i]; -} + size_t r, t; -static void -clrscr() -{ -} - -static short -sprite() -{ - /* return address of sprite */ - return 0; -} + t = sz; + mem = malloc(4096); + if (mem == NULL) + err(1, "malloc failed"); + memset(mem, 0, 4096); -static void -draw() -{ -} - -static int -key() -{ - /* return pressed key */ - return 0; + pc = START; + memcpy(mem, fnt, 80); + while ((r = read(fd, mem + pc, sz)) != -1) { + sz -= r; + if (!sz) + break; + } + if (r == -1) + err(1, "read failed"); } -static int -waitkey() +void +predraw(unsigned short n, unsigned short x, unsigned y) { - /* block until keypress and return it */ - return 0; -} + uint16_t px; -static void -bcd() -{ + V[0xF] = 0; + for (int yy = 0; yy < n; ++yy) { + px = mem[I + yy]; + for (int xx = 0; xx < 8; ++xx) { + if ((px & (0x80 >> xx)) != 0) { + if (scene[(V[x] + xx + ((V[y] + yy) * 64))] == 1) + V[0xF] = 1; + scene[V[x] + xx + ((V[y] + yy) * 64)] ^= 1; + } + } + } + draw(scene); } static void -execute() +execute(void) { - int x, y, c; + unsigned short x, y, c, n; switch (opcode & 0xF000) { case 0x0000: - switch (opcode) { - case 0x00E0: - clrscr(); + switch (opcode & 0x0FFF) { + case 0xe0: + //fprintf(stderr,"[OK] 0x%X: 00E0\n", opcode); + memset(scene, 0, 2048); pc += 2; - case 0x00EE: - if (sp > stack) { - pc = *sp; - --sp; - } return; - default: - fprintf(stderr, "machine code routine\n"); - pc += 2; + case 0xee: + //fprintf(stderr,"[OK] 0x%X: 00EE\n", opcode); + pc = *sp--; + pc += 2; return; } case 0x1000: + //fprintf(stderr,"[OK] 0x%X: 1NNN\n", opcode); pc = opcode & 0x0FFF; return; case 0x2000: - *sp = pc; - ++sp; + //fprintf(stderr,"[OK] 0x%X: 2NNN\n", opcode); + *++sp = pc; pc = opcode & 0x0FFF; return; case 0x3000: + //fprintf(stderr,"[OK] 0x%X: 3XNN\n", opcode); x = (opcode & 0x0F00) >> 8; c = opcode & 0x00FF; if (V[x] == c) - pc += 4; + pc += 2; + pc += 2; return; case 0x4000: + //fprintf(stderr,"[OK] 0x%X: 4XNN\n", opcode); x = (opcode & 0x0F00) >> 8; c = opcode & 0x00FF; if (V[x] != c) - pc += 4; + pc += 2; + pc += 2; return; case 0x5000: + //fprintf(stderr,"[OK] 0x%X: 5XY0\n", opcode); x = (opcode & 0x0F00) >> 8; y = (opcode & 0x00F0) >> 4; if (V[x] == V[y]) - pc += 4; + pc += 2; + pc += 2; return; case 0x6000: + //fprintf(stderr,"[OK] 0x%X: 6XNN\n", opcode); x = (opcode & 0x0F00) >> 8; c = opcode & 0x00FF; V[x] = c; pc += 2; return; case 0x7000: + //fprintf(stderr,"[OK] 0x%X: 7XNN\n", opcode); x = (opcode & 0x0F00) >> 8; c = opcode & 0x00FF; V[x] += c; @@ -161,71 +162,88 @@ execute() pc += 2; switch(opcode & 0x000F) { case 0x0000: + //fprintf(stderr,"[OK] 0x%X: 8XY0\n", opcode); V[x] = V[y]; return; case 0x0001: + //fprintf(stderr,"[OK] 0x%X: 8XY1\n", opcode); V[x] |= V[y]; return; case 0x0002: + //fprintf(stderr,"[OK] 0x%X: 8XY2\n", opcode); V[x] &= V[y]; return; case 0x0003: + //fprintf(stderr,"[OK] 0x%X: 8XY3\n", opcode); V[x] ^= V[y]; return; case 0x0004: + //fprintf(stderr,"[OK] 0x%X: 8XY4\n", opcode); V[0xF] = 0; if (V[y] > CHAR_MAX - V[x]) V[0xF] = 1; V[x] += V[y]; return; case 0x0005: + //fprintf(stderr,"[OK] 0x%X: 8XY5\n", opcode); V[0xF] = 0; - if (V[y] > V[x]) + if (V[y] < V[x]) V[0xF] = 1; V[x] -= V[y]; return; case 0x0006: + //fprintf(stderr,"[OK] 0x%X: 8XY6\n", opcode); V[0xF] = V[x] & 0x01; V[x] >>= 1; return; case 0x0007: - V[0xF] = 1; - if (V[x] > V[y]) - V[0xF] = 0; + //fprintf(stderr,"[OK] 0x%X: 8XY7\n", opcode); + V[0xF] = 0; + if (V[x] < V[y]) + V[0xF] = 1; V[x] = V[y] - V[x]; return; case 0x000E: - V[0xF] = V[x] & 0x80; + //fprintf(stderr,"[OK] 0x%X: 8XYE\n", opcode); + V[0xF] = (V[x] >> 7) & 0x01; V[x] <<= 1; return; default: goto err; } case 0x9000: + //fprintf(stderr,"[OK] 0x%X: 9XY0\n", opcode); + //fprintf(stderr, "0x9000\n"); x = (opcode & 0x0F00) >> 8; y = (opcode & 0x00F0) >> 4; if (V[x] != V[y]) - pc += 4; + pc += 2; pc += 2; return; case 0xA000: + //fprintf(stderr,"[OK] 0x%X: ANNN\n", opcode); I = opcode & 0x0FFF; pc += 2; return; case 0xB000: + //fprintf(stderr,"[OK] 0x%X: BNNN\n", opcode); + //fprintf(stderr, "0xB000\n"); pc = V[0] + (opcode & 0x0FFF); return; case 0xC000: + //fprintf(stderr,"[OK] 0x%X: CXNN\n", opcode); x = (opcode & 0x0F00) >> 8; c = (opcode & 0x00FF); V[x] = (rand() % 256) & c; + //V[x] = 128 & c; pc += 2; return; case 0xD000: + //fprintf(stderr,"[OK] 0x%X: DXYN\n", opcode); + n = (opcode & 0x00F); x = (opcode & 0x0F00) >> 8; y = (opcode & 0x00F0) >> 4; - c = opcode & 0x000F; - draw(V[x], V[y], c); + predraw(n, x, y); pc += 2; return; case 0xE000: @@ -233,12 +251,14 @@ execute() pc += 2; switch (opcode & 0x00FF) { case 0x009E: - if (key() == V[x]) - pc += 4; + //fprintf(stderr,"[OK] 0x%X: EX9E\n", opcode); + if (key[V[x]]) + pc += 2; return; case 0x00A1: - if (key() != V[x]) - pc += 4; + //fprintf(stderr,"[OK] 0x%X: EXA1\n", opcode); + if (!key[V[x]]) + pc += 2; return; default: goto err; @@ -248,33 +268,47 @@ execute() pc += 2; switch (opcode & 0x00FF) { case 0x0007: + //fprintf(stderr,"[OK] 0x%X: FX07\n", opcode); V[x] = delaytmr; return; case 0x000A: - V[x] = waitkey(); + //fprintf(stderr,"[OK] 0x%X: FX0A\n", opcode); + for (int i = 0; i < 16; ++i) + if (key[i]) { + V[x] = i; + return; + } + pc -= 2; return; case 0x0015: + //fprintf(stderr,"[OK] 0x%X: FX15\n", opcode); delaytmr = V[x]; return; case 0x0018: + //fprintf(stderr,"[OK] 0x%X: FX18\n", opcode); soundtmr = V[x]; return; case 0x001E: + //fprintf(stderr,"[OK] 0x%X: FX1E\n", opcode); I += V[x]; return; case 0x0029: - I = sprite(V[x]); + //fprintf(stderr,"[OK] 0x%X: FX29\n", opcode); + I = V[x] * 5; return; case 0x0033: - bcd(x); + //fprintf(stderr,"[OK] 0x%X: FX33\n", opcode); + mem[I] = (V[x] % 1000) / 100; + mem[I + 1] = (V[x] % 100) / 10; + mem[I + 2] = V[x] % 10; return; case 0x0055: - for (int i = 0; i < 16; ++i) - mem[I + 1 + i] = V[i]; /* mem[I] shouldn't be modified */ + //fprintf(stderr,"[OK] 0x%X: FX55\n", opcode); + memcpy(mem + I, V, x+1); return; case 0x0065: - for (int i = 0; i < 16; ++i) - V[i] = mem[I + 1 + i]; /* mem[I] shouldn't be modified */ + //fprintf(stderr,"[OK] 0x%X: FX65\n", opcode); + memcpy(V, mem + I, x+1); return; default: goto err; @@ -287,16 +321,25 @@ err: } static void +die(void) +{ + sdlquit(); + free(mem); + exit(0); +} + +static void cycle() { - opcode = mem[pc] << 8 | mem[pc+1]; + static int n; + + opcode = mem[pc] << 8 | mem[pc + 1]; + //fprintf(stderr, "opc: 0x%hx\n", opcode); execute(); - if (delaytmr > 0) - --delaytmr; - if (soundtmr > 0) { - printf("BEEP\n"); - --soundtmr; - } + handleev(); + if (quit) + die(); + usleep(1500); } static void @@ -310,28 +353,20 @@ int main(int argc, char **argv) { struct stat st; - int fd, n, r; + int fd; if (argc < 2) usage(); if (stat(argv[1], &st) != 0) err(1, "stat failed"); - if (st.st_size > BUFSZ) { - fprintf(stderr, "file too big\n"); - return 1; - } if ((fd = open(argv[1], O_RDONLY)) == -1) err(1, "open failed"); - r = 0; - while (r < st.st_size) { - if ((n = read(fd, buf, st.st_size-r)) == -1) - err(1, "read failed"); - r += n; - } - - init(); + sdlinit(); + init(fd, st.st_size); + for (;;) + cycle(); return 0; } diff --git a/Makefile b/Makefile @@ -1,8 +1,24 @@ +include config.mk + BIN = 8 -all: ${BIN} +HEADER = av.h + +COMMONOBJ = \ + 8.o \ + av.o \ + +all: $(BIN) .PHONY: clean clean: - -rm -f ${BIN} + -rm -f ${BIN} av 8.o av.o + +.SUFFIXES: .c .o + +.c.o: + $(CC) $(CPPFLAGS) $(CFLAGS) -c $< + +8: $(COMMONOBJ) $(HEADER) + $(CC) $(CFLAGS) -o $@ $(COMMONOBJ) $(LDFLAGS) $(LDLIBS) diff --git a/av.c b/av.c @@ -0,0 +1,151 @@ +#include <SDL.h> +#include <SDL_mixer.h> +#include <err.h> +#include <stdint.h> +#include <stdio.h> +#include <unistd.h> + +#include "av.h" + +extern uint8_t delaytmr; +extern uint8_t soundtmr; + +int key[16]; +int quit; + +static int WIDTH = 64 * 8; +static int HEIGHT = 32 * 8; + +static SDL_Window *W; +static SDL_Renderer *R; +static Mix_Music *M; +static SDL_TimerID T; + +void +handleev(void) +{ + SDL_Event e; + const Uint8 *kstate; + + quit = 0; + if (SDL_PollEvent(&e)) { + kstate = SDL_GetKeyboardState(NULL); + switch (e.type) { + case SDL_QUIT: + quit = 1; + break; + default: + if (kstate[SDL_SCANCODE_ESCAPE]) + quit = 1; + key[0] = kstate[SDL_SCANCODE_1]; + key[1] = kstate[SDL_SCANCODE_2]; + key[2] = kstate[SDL_SCANCODE_3]; + key[3] = kstate[SDL_SCANCODE_4]; + key[4] = kstate[SDL_SCANCODE_Q]; + key[5] = kstate[SDL_SCANCODE_W]; + key[6] = kstate[SDL_SCANCODE_E]; + key[7] = kstate[SDL_SCANCODE_R]; + key[8] = kstate[SDL_SCANCODE_A]; + key[9] = kstate[SDL_SCANCODE_S]; + key[10] = kstate[SDL_SCANCODE_D]; + key[11] = kstate[SDL_SCANCODE_F]; + key[12] = kstate[SDL_SCANCODE_Z]; + key[13] = kstate[SDL_SCANCODE_X]; + key[14] = kstate[SDL_SCANCODE_C]; + key[15] = kstate[SDL_SCANCODE_V]; + break; + } + } +} + +unsigned int +timer(unsigned int interval, void *) +{ + if (delaytmr > 0) + delaytmr--; + if (soundtmr > 0) { + soundtmr--; + beep(); + } + return interval; +} + +void +sdlinit(void) +{ + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER) < 0) + errx(1, "SDL_Init failed: %s\n", SDL_GetError()); + W = SDL_CreateWindow("SDL Tutorial", SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, WIDTH, HEIGHT, + SDL_WINDOW_SHOWN); + if (W == NULL) + errx(1, "SDL_CreateWindow failed: %s\n", SDL_GetError()); + R = SDL_CreateRenderer(W, -1, SDL_RENDERER_ACCELERATED); + if (R == NULL) + errx(1, "SDL_CreateRenderer failed: %s\n", SDL_GetError()); + if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) < 0) + errx(1, "Mix_OpenAudio failed: %s\n", Mix_GetError()); + M = Mix_LoadMUS("assets/beep.wav"); + if (M == NULL) + errx(1, "Mix_LoadMUS failed: %s\n", Mix_GetError()); + T = SDL_AddTimer(16, timer, NULL); + if (T == 0) + errx(1, "Mix_LoadMUS failed: %s\n", Mix_GetError()); +} + +int +makesound(void *) +{ + unsigned int tick; + + tick = SDL_GetTicks(); + while (1) { + if (Mix_PlayingMusic()) { + if ((SDL_GetTicks() - tick) > 4) { + Mix_HaltMusic(); + return 0; + } + } else { + Mix_PlayMusic(M, -1); + } + } +} + +void +beep(void) +{ + SDL_CreateThread(makesound, "BEEP", NULL); +} + +int +draw(unsigned char *scene) +{ + SDL_Rect r; + + r.w = 8; + r.h = 8; + SDL_SetRenderDrawColor(R, 0, 0, 0, 255); + SDL_RenderClear(R); + SDL_SetRenderDrawColor(R, 255, 255, 255, 255); + + for (int y = 0; y < 32; ++y) + for (int x = 0; x < 64; ++x) + if (scene[x + (y * 64)]) { + r.x = x * 8; + r.y = y * 8; + SDL_RenderFillRect(R, &r); + } + + SDL_RenderPresent(R); + return 0; +} + +void +sdlquit(void) +{ + SDL_DestroyRenderer(R); + SDL_DestroyWindow(W); + Mix_FreeMusic(M); + SDL_RemoveTimer(T); + SDL_Quit(); +} diff --git a/av.h b/av.h @@ -0,0 +1,15 @@ +#ifndef AV_H +#define AV_H + +//Include stdint.h prior to this header + +extern int key[16]; +extern int quit; + +int draw(uint8_t *); +void sdlinit(void); +void handleev(void); +void sdlquit(void); +void beep(void); + +#endif