implement simple ops
This commit is contained in:
228
chip8.c
228
chip8.c
@@ -1,9 +1,22 @@
|
|||||||
// http://devernay.free.fr/hacks/chip8/C8TECH10.HTM
|
// http://devernay.free.fr/hacks/chip8/C8TECH10.HTM
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define READ_INS() \
|
||||||
|
uint8_t low = c->memory[c->pc++]; \
|
||||||
|
uint8_t high = c->memory[c->pc++]; \
|
||||||
|
uint16_t ins = (low << 8) | high; \
|
||||||
|
uint16_t nnn = ins & 0x0FFF; \
|
||||||
|
uint8_t n = ins & 0x000F; \
|
||||||
|
uint8_t x = (ins >> 8) & 0x0F; \
|
||||||
|
uint8_t y = (ins >> 4) & 0x0F; \
|
||||||
|
uint8_t kk = ins & 0x00FF
|
||||||
|
|
||||||
|
#define BAD_INS() \
|
||||||
|
fprintf(stderr, "unrecognized instruction\n"); \
|
||||||
|
exit(1)
|
||||||
|
|
||||||
typedef struct CHIP8 {
|
typedef struct CHIP8 {
|
||||||
uint8_t *memory;
|
uint8_t *memory;
|
||||||
uint16_t pc;
|
uint16_t pc;
|
||||||
@@ -42,17 +55,9 @@ CHIP8 chip8_create() {
|
|||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
void chip8_disassemble(CHIP8 *c, size_t size) {
|
void chip8_disassemble(CHIP8 *c, size_t ins_count) {
|
||||||
for (size_t i = 0; i < size; i++) {
|
for (size_t i = 0; i < ins_count; i++) {
|
||||||
uint8_t low = c->memory[c->pc++];
|
READ_INS();
|
||||||
uint8_t high = c->memory[c->pc++];
|
|
||||||
uint16_t ins = (low << 8) | high;
|
|
||||||
|
|
||||||
uint16_t nnn = ins & 0x0FFF;
|
|
||||||
uint8_t n = ins & 0x000F;
|
|
||||||
uint8_t x = (ins >> 8) & 0x0F;
|
|
||||||
uint8_t y = (ins >> 4) & 0x0F;
|
|
||||||
uint8_t kk = ins & 0x00FF;
|
|
||||||
|
|
||||||
switch ((ins >> 12) & 0xF) {
|
switch ((ins >> 12) & 0xF) {
|
||||||
case 0x0: {
|
case 0x0: {
|
||||||
@@ -64,29 +69,29 @@ void chip8_disassemble(CHIP8 *c, size_t size) {
|
|||||||
puts("RET");
|
puts("RET");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
printf("SYS %hd\n", nnn);
|
printf("SYS %u\n", nnn);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case 0x1: {
|
case 0x1: {
|
||||||
printf("JP %hd\n", nnn);
|
printf("JP %u\n", nnn);
|
||||||
} break;
|
} break;
|
||||||
case 0x2: {
|
case 0x2: {
|
||||||
printf("CALL %hd\n", nnn);
|
printf("CALL %u\n", nnn);
|
||||||
} break;
|
} break;
|
||||||
case 0x3: {
|
case 0x3: {
|
||||||
printf("SE V%x, %hhd\n", x, kk);
|
printf("SE V%x, %u\n", x, kk);
|
||||||
} break;
|
} break;
|
||||||
case 0x4: {
|
case 0x4: {
|
||||||
printf("SNE V%x, %hhd\n", x, kk);
|
printf("SNE V%x, %u\n", x, kk);
|
||||||
} break;
|
} break;
|
||||||
case 0x5: {
|
case 0x5: {
|
||||||
printf("SE V%x, V%x\n", x, y);
|
printf("SE V%x, V%x\n", x, y);
|
||||||
} break;
|
} break;
|
||||||
case 0x6: {
|
case 0x6: {
|
||||||
printf("LD V%x, %hhd\n", x, kk);
|
printf("LD V%x, %u\n", x, kk);
|
||||||
} break;
|
} break;
|
||||||
case 0x7: {
|
case 0x7: {
|
||||||
printf("ADD V%x, %hhd\n", x, kk);
|
printf("ADD V%x, %u\n", x, kk);
|
||||||
} break;
|
} break;
|
||||||
case 0x8: {
|
case 0x8: {
|
||||||
switch (n) {
|
switch (n) {
|
||||||
@@ -118,24 +123,23 @@ void chip8_disassemble(CHIP8 *c, size_t size) {
|
|||||||
printf("SHL V%x\n", x);
|
printf("SHL V%x\n", x);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "unrecognized instruction\n");
|
BAD_INS();
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case 0x9: {
|
case 0x9: {
|
||||||
printf("SNE V%x, V%x\n", x, y);
|
printf("SNE V%x, V%x\n", x, y);
|
||||||
} break;
|
} break;
|
||||||
case 0xA: {
|
case 0xA: {
|
||||||
printf("LD I, %hd\n", nnn);
|
printf("LD I, %u\n", nnn);
|
||||||
} break;
|
} break;
|
||||||
case 0xB: {
|
case 0xB: {
|
||||||
printf("JP V0, %hd\n", nnn);
|
printf("JP V0, %u\n", nnn);
|
||||||
} break;
|
} break;
|
||||||
case 0xC: {
|
case 0xC: {
|
||||||
printf("RND V%x, %hd\n", x, kk);
|
printf("RND V%x, %u\n", x, kk);
|
||||||
} break;
|
} break;
|
||||||
case 0xD: {
|
case 0xD: {
|
||||||
printf("DRW V%x, V%x, %hhd\n", x, y, n);
|
printf("DRW V%x, V%x, %u\n", x, y, n);
|
||||||
} break;
|
} break;
|
||||||
case 0xE: {
|
case 0xE: {
|
||||||
switch (kk) {
|
switch (kk) {
|
||||||
@@ -146,8 +150,7 @@ void chip8_disassemble(CHIP8 *c, size_t size) {
|
|||||||
printf("SKNP V%x\n", x);
|
printf("SKNP V%x\n", x);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "unrecognized instruction\n");
|
BAD_INS();
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case 0xF: {
|
case 0xF: {
|
||||||
@@ -180,28 +183,185 @@ void chip8_disassemble(CHIP8 *c, size_t size) {
|
|||||||
printf("LD V%x, [I]\n", x);
|
printf("LD V%x, [I]\n", x);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "unrecognized instruction\n");
|
BAD_INS();
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "unrecognized instruction\n");
|
BAD_INS();
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void chip8_execute(CHIP8 *c) {
|
||||||
|
while (1) {
|
||||||
|
READ_INS();
|
||||||
|
|
||||||
|
switch ((ins >> 12) & 0xF) {
|
||||||
|
case 0x0: {
|
||||||
|
switch (nnn) {
|
||||||
|
case 0x0E0:
|
||||||
|
puts("TODO: CLS");
|
||||||
|
break;
|
||||||
|
case 0x0EE:
|
||||||
|
c->pc = c->stack[c->sp];
|
||||||
|
c->sp--;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
c->pc = nnn;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case 0x1: {
|
||||||
|
c->pc = nnn;
|
||||||
|
} break;
|
||||||
|
case 0x2: {
|
||||||
|
c->sp++;
|
||||||
|
c->stack[c->sp] = c->pc;
|
||||||
|
c->pc = nnn;
|
||||||
|
} break;
|
||||||
|
case 0x3: {
|
||||||
|
if (c->reg[x] == kk) {
|
||||||
|
c->pc += 2;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case 0x4: {
|
||||||
|
if (c->reg[x] != kk) {
|
||||||
|
c->pc += 2;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case 0x5: {
|
||||||
|
if (c->reg[x] == c->reg[y]) {
|
||||||
|
c->pc += 2;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case 0x6: {
|
||||||
|
c->reg[x] = kk;
|
||||||
|
} break;
|
||||||
|
case 0x7: {
|
||||||
|
c->reg[x] += kk;
|
||||||
|
} break;
|
||||||
|
case 0x8: {
|
||||||
|
switch (n) {
|
||||||
|
case 0x0:
|
||||||
|
c->reg[x] = c->reg[y];
|
||||||
|
break;
|
||||||
|
case 0x1:
|
||||||
|
c->reg[x] |= c->reg[y];
|
||||||
|
break;
|
||||||
|
case 0x2:
|
||||||
|
c->reg[x] &= c->reg[y];
|
||||||
|
break;
|
||||||
|
case 0x3:
|
||||||
|
c->reg[x] ^= c->reg[y];
|
||||||
|
break;
|
||||||
|
case 0x4:
|
||||||
|
printf("TODO: ADD V%x, V%x\n", x, y);
|
||||||
|
break;
|
||||||
|
case 0x5:
|
||||||
|
printf("TODO: SUB V%x, V%x\n", x, y);
|
||||||
|
break;
|
||||||
|
case 0x6:
|
||||||
|
printf("TODO: SHR V%x\n", x);
|
||||||
|
break;
|
||||||
|
case 0x7:
|
||||||
|
printf("TODO: SUBN V%x, V%x\n", x, y);
|
||||||
|
break;
|
||||||
|
case 0xE:
|
||||||
|
printf("TODO: SHL V%x\n", x);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
BAD_INS();
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case 0x9: {
|
||||||
|
if (c->reg[x] != c->reg[y]) {
|
||||||
|
c->pc += 2;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case 0xA: {
|
||||||
|
c->I = nnn;
|
||||||
|
} break;
|
||||||
|
case 0xB: {
|
||||||
|
c->pc = nnn + c->reg[0];
|
||||||
|
} break;
|
||||||
|
case 0xC: {
|
||||||
|
c->reg[x] = (rand() % 256) & kk;
|
||||||
|
} break;
|
||||||
|
case 0xD: {
|
||||||
|
printf("TODO: DRW V%x, V%x, %u\n", x, y, n);
|
||||||
|
} break;
|
||||||
|
case 0xE: {
|
||||||
|
switch (kk) {
|
||||||
|
case 0x9E:
|
||||||
|
printf("TODO: SKP V%x\n", x);
|
||||||
|
break;
|
||||||
|
case 0xA1:
|
||||||
|
printf("TODO: SKNP V%x\n", x);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
BAD_INS();
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case 0xF: {
|
||||||
|
switch (kk) {
|
||||||
|
case 0x07:
|
||||||
|
c->reg[x] = c->delay_timer;
|
||||||
|
break;
|
||||||
|
case 0x0A:
|
||||||
|
printf("TODO: LD V%x, K\n", x);
|
||||||
|
break;
|
||||||
|
case 0x15:
|
||||||
|
c->delay_timer = c->reg[x];
|
||||||
|
break;
|
||||||
|
case 0x18:
|
||||||
|
c->sound_timer = c->reg[x];
|
||||||
|
break;
|
||||||
|
case 0x1E:
|
||||||
|
c->I += c->reg[x];
|
||||||
|
break;
|
||||||
|
case 0x29:
|
||||||
|
printf("TODO: LD F, V%x\n", x);
|
||||||
|
break;
|
||||||
|
case 0x33:
|
||||||
|
c->memory[c->I] = c->reg[x] / 100;
|
||||||
|
c->memory[c->I + 1] = (c->reg[x] / 10) % 10;
|
||||||
|
c->memory[c->I + 2] = c->reg[x] % 10;
|
||||||
|
break;
|
||||||
|
case 0x55:
|
||||||
|
printf("TODO: LD [I], V%x\n", x);
|
||||||
|
break;
|
||||||
|
case 0x65:
|
||||||
|
printf("TODO: LD V%x, [I]\n", x);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
BAD_INS();
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
default:
|
||||||
|
BAD_INS();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void chip8_free(CHIP8 c) { free(c.memory); }
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
if (argc < 2) {
|
||||||
|
fprintf(stderr, "Usage: %s <path>\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
CHIP8 c = chip8_create();
|
CHIP8 c = chip8_create();
|
||||||
|
|
||||||
uint8_t buffer[100000];
|
uint8_t buffer[4000];
|
||||||
FILE *f = fopen(argv[1], "rb");
|
FILE *f = fopen(argv[1], "rb");
|
||||||
size_t n = fread(buffer, 1, 100000, f);
|
size_t n = fread(buffer, 1, 4000, f);
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
for (size_t i = 0; i < n; i++) {
|
for (size_t i = 0; i < n; i++) {
|
||||||
c.memory[0x200 + i] = buffer[i];
|
c.memory[0x200 + i] = buffer[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
chip8_disassemble(&c, n);
|
chip8_execute(&c);
|
||||||
|
|
||||||
|
chip8_free(c);
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user