Initial commit

This commit is contained in:
Franck STAUFFER 2020-08-03 15:02:03 +02:00
commit b7edca10c2
Signed by: franck.stauffer
GPG Key ID: AAF5A94045CEC261
4 changed files with 626 additions and 0 deletions

28
Makefile Normal file
View File

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

1
README.md Normal file
View File

@ -0,0 +1 @@
# CHIP-8

597
src/main.c Normal file
View File

@ -0,0 +1,597 @@
#include "SDL/SDL.h"
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#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 <FILE>", 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;
}

BIN
tests/TEST Normal file

Binary file not shown.