2020-08-03 17:34:41 +02:00
|
|
|
#include "SDL2/SDL.h"
|
2020-08-03 18:01:43 +02:00
|
|
|
#include <SDL2/SDL_mixer.h>
|
2020-08-03 15:02:03 +02:00
|
|
|
#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
|
2020-08-03 16:57:18 +02:00
|
|
|
#define WAIT 200
|
2020-08-03 15:02:03 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
typedef uint8_t byte;
|
|
|
|
typedef uint16_t word;
|
|
|
|
|
|
|
|
byte dt = 0;
|
|
|
|
byte gfx[64][32];
|
2020-08-03 16:57:18 +02:00
|
|
|
word i = 0;
|
|
|
|
byte key[16];
|
|
|
|
byte memory[4096];
|
2020-08-03 15:02:03 +02:00
|
|
|
byte oldgfx[64][32];
|
|
|
|
word op = 0;
|
2020-08-03 16:57:18 +02:00
|
|
|
word pc = 0x200;
|
|
|
|
word sp = 0;
|
|
|
|
byte st = 0;
|
|
|
|
word stack[16];
|
|
|
|
byte v[16];
|
2020-08-03 15:02:03 +02:00
|
|
|
|
|
|
|
SDL_Event e;
|
2020-08-03 17:34:41 +02:00
|
|
|
SDL_Surface* s;
|
|
|
|
SDL_Window* w;
|
2020-08-03 15:02:03 +02:00
|
|
|
|
2020-08-03 18:01:43 +02:00
|
|
|
Mix_Chunk* buzzer;
|
|
|
|
|
2020-08-03 17:34:41 +02:00
|
|
|
Uint32 fg;
|
|
|
|
Uint32 bg;
|
2020-08-03 15:02:03 +02:00
|
|
|
|
|
|
|
void
|
|
|
|
init_chip8(void)
|
|
|
|
{
|
|
|
|
srand(time(NULL));
|
|
|
|
|
2020-08-03 15:34:33 +02:00
|
|
|
memset(v, 0, 16 * sizeof(byte));
|
|
|
|
memset(stack, 0, 16 * sizeof(word));
|
|
|
|
memset(key, 0, 16 * sizeof(byte));
|
|
|
|
memset(gfx, 0, 32 * 64 * sizeof(byte));
|
|
|
|
memset(oldgfx, 0, 32 * 64 * sizeof(byte));
|
2020-08-03 15:02:03 +02:00
|
|
|
|
|
|
|
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
|
|
|
|
};
|
|
|
|
|
2020-08-03 15:34:33 +02:00
|
|
|
memcpy(memory, fontset, 80 * sizeof(byte));
|
|
|
|
memset(memory + 80, 0, 4016);
|
2020-08-03 15:02:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
cleanup(void)
|
|
|
|
{
|
2020-08-03 18:01:43 +02:00
|
|
|
Mix_FreeChunk(buzzer);
|
|
|
|
Mix_Quit();
|
2020-08-03 15:02:03 +02:00
|
|
|
SDL_FreeSurface(s);
|
2020-08-03 17:34:41 +02:00
|
|
|
SDL_DestroyWindow(w);
|
2020-08-03 15:02:03 +02:00
|
|
|
SDL_Quit();
|
|
|
|
}
|
|
|
|
|
|
|
|
uint_fast8_t
|
|
|
|
init_SDL(const char* path)
|
|
|
|
{
|
2020-08-03 18:01:43 +02:00
|
|
|
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)) {
|
2020-08-03 15:02:03 +02:00
|
|
|
fputs("ERROR: Failed to initialize SDL\n", stderr);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-08-03 17:34:41 +02:00
|
|
|
char caption[7 + strlen(path)];
|
|
|
|
sprintf(caption, "CHIP-8 - %s", path);
|
2020-08-03 18:01:43 +02:00
|
|
|
w = SDL_CreateWindow(caption,
|
|
|
|
SDL_WINDOWPOS_UNDEFINED,
|
|
|
|
SDL_WINDOWPOS_UNDEFINED,
|
|
|
|
64 * SCALE,
|
|
|
|
32 * SCALE,
|
|
|
|
SDL_WINDOW_SHOWN);
|
2020-08-03 17:34:41 +02:00
|
|
|
if (!w) {
|
|
|
|
fputs("ERROR: Failed to create a window\n", stderr);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
s = SDL_GetWindowSurface(w);
|
2020-08-03 15:02:03 +02:00
|
|
|
if (!s) {
|
|
|
|
fputs("ERROR: Failed to create SDL surface\n", stderr);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-08-03 18:01:43 +02:00
|
|
|
if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 1, 2048)) {
|
|
|
|
fputs("ERROR: Failed to open audio\n", stderr);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
buzzer = Mix_LoadWAV("resources/buzzer.wav");
|
|
|
|
if (!buzzer) {
|
|
|
|
fputs("ERROR: Failed to load sound effects\n", stderr);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-08-03 15:02:03 +02:00
|
|
|
if (atexit(cleanup)) {
|
|
|
|
fputs("ERROR: Failed to set exit function\n", stderr);
|
|
|
|
SDL_FreeSurface(s);
|
|
|
|
SDL_Quit();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2020-08-03 17:34:41 +02:00
|
|
|
SDL_UpdateWindowSurface(w);
|
2020-08-03 15:02:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline uint_fast8_t
|
|
|
|
op_handler(void)
|
|
|
|
{
|
|
|
|
switch (op) {
|
|
|
|
case 0x00E0:
|
2020-08-03 17:34:41 +02:00
|
|
|
memset(gfx, 0, 32 * 64 * sizeof(byte));
|
2020-08-03 15:02:03 +02:00
|
|
|
|
|
|
|
SDL_Rect r;
|
|
|
|
r.h = 32 * SCALE;
|
|
|
|
r.w = 64 * SCALE;
|
|
|
|
r.x = 0;
|
|
|
|
r.y = 0;
|
|
|
|
SDL_FillRect(s, &r, bg);
|
|
|
|
|
2020-08-03 17:34:41 +02:00
|
|
|
SDL_UpdateWindowSurface(w);
|
|
|
|
|
2020-08-03 15:02:03 +02:00
|
|
|
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:
|
2020-08-03 16:36:19 +02:00
|
|
|
memcpy(memory + i, v, ((X) + 1) * sizeof(byte));
|
|
|
|
i += (X) + 1;
|
2020-08-03 15:02:03 +02:00
|
|
|
pc += 2;
|
|
|
|
return 1;
|
|
|
|
case 0xF065:
|
2020-08-03 16:36:19 +02:00
|
|
|
memcpy(v, memory + i, ((X) + 1) * sizeof(byte));
|
|
|
|
i += (X) + 1;
|
2020-08-03 15:02:03 +02:00
|
|
|
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:
|
2020-08-03 15:34:33 +02:00
|
|
|
memcpy(oldgfx, gfx, 32 * 64 * sizeof(byte));
|
2020-08-03 15:02:03 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2020-08-03 18:01:43 +02:00
|
|
|
if (st) {
|
|
|
|
Mix_PlayChannel(-1, buzzer, 0);
|
2020-08-03 15:02:03 +02:00
|
|
|
--st;
|
2020-08-03 18:01:43 +02:00
|
|
|
}
|
2020-08-03 15:02:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|