Initial cpu and instruction parsing

This commit is contained in:
Samuel Walker 2025-01-30 16:27:27 -07:00
parent 945b82a3ca
commit cd588671c4
Signed by: piwalker
GPG Key ID: BE1F84BF85111255
12 changed files with 303 additions and 11 deletions

View File

@ -21,4 +21,7 @@ typedef struct {
bool cart_load(char *cart);
rom_header *get_rom_header();
rom_header *get_rom_header();
u8 cart_read(u16 address);
void cart_write(u16 address, u8 value);

View File

@ -14,4 +14,6 @@ typedef uint64_t u64;
#define BIT_SET(a, n, on) (on ? (a) |= (1 << n) : (a) &= !(1 << n))
#define BETWEEN(a, b, c) ((a >= b) && (a <= c))
void delay(u32 ms);
void delay(u32 ms);
#define NO_IMPL { fprintf(stderr, "NOT YET IMPLEMENTED\n"); exit(-5); }

View File

@ -1,6 +1,34 @@
#pragma once
#include <common.h>
#include <instructions.h>
typedef struct {
u8 a;
u8 f;
u8 b;
u8 c;
u8 d;
u8 e;
u8 h;
u8 l;
u16 pc;
u16 sp;
} cpu_registers;
typedef struct {
cpu_registers regs;
//current fetch...
u16 fetched_data;
u16 mem_dest;
bool dest_is_mem;
u8 cur_opcode;
instruction *cur_inst;
bool halted;
bool stepping;
} cpu_context;
void cpu_init();
bool cpu_step();

View File

@ -9,4 +9,6 @@ typedef struct {
} emu_context;
int emu_run(int, char**);
emu_context *emu_get_context();
emu_context *emu_get_context();
void emu_cycles(int cpu_cycles);

111
include/instructions.h Normal file
View File

@ -0,0 +1,111 @@
#pragma once
#include <common.h>
typedef enum {
AM_R_D16,
AM_R_R,
AM_MR_R,
AM_R,
AM_R_D8,
AM_R_MR,
AM_R_HLI,
AM_R_HLD,
AM_HLI_R,
AM_HLD_R,
AM_R_A8,
AM_A8_R,
AM_HL_SPR,
AM_D16,
AM_D8,
AM_IMP,
AM_D16_R,
AM_MR_D8,
AM_MR,
AM_A16_R,
AM_R_A16
} addr_mode;
typedef enum {
RT_NONE,
RT_A,
RT_F,
RT_B,
RT_C,
RT_D,
RT_E,
RT_H,
RT_L,
RT_AF,
RT_BC,
RT_DE,
RT_HL,
RT_SP,
RT_PC
} reg_type;
typedef enum {
IN_NONE,
IN_NOP,
IN_LD,
IN_INC,
IN_DEC,
IN_RLCA,
IN_ADD,
IN_RRCA,
IN_STOP,
IN_RLA,
IN_JR,
IN_RRA,
IN_DAA,
IN_CPL,
IN_SCF,
IN_CCF,
IN_HALT,
IN_ADC,
IN_SUB,
IN_SBC,
IN_AND,
IN_XOR,
IN_OR,
IN_CP,
IN_POP,
IN_JP,
IN_PUSH,
IN_RET,
IN_CB,
IN_CALL,
IN_RETI,
IN_LDH,
IN_JPHL,
IN_DI,
IN_EI,
IN_RST,
IN_ERR,
//CB instructions...
IN_RCL,
IN_RRC,
IN_RL,
IN_RR,
IN_SLA,
IN_SRA,
IN_SWAP,
IN_SRL,
IN_BIT,
IN_RES,
IN_SET
} in_type;
typedef enum {
CT_NONE, CT_NZ, CT_Z, CT_NC, CT_C
} cond_type;
typedef struct {
in_type type;
addr_mode mode;
reg_type reg_1;
reg_type reg_2;
cond_type cond;
u8 param;
} instruction;
instruction *instruction_by_opcode(u8 opcode);

View File

@ -0,0 +1,32 @@
#include <bus.h>
// 0x0000 - 0x3FFF : ROM Bank 0
// 0x4000 - 0x7FFF : ROM Bank 1 - Switchable
// 0x8000 - 0x97FF : CHR RAM
// 0x9800 - 0x9BFF : BG Map 1
// 0x9C00 - 0x9FFF : BG Map 2
// 0xA000 - 0xBFFF : Cartridge RAM
// 0xC000 - 0xCFFF : RAM Bank 0
// 0xD000 - 0xDFFF : RAM Bank 1-7 - switchable - Color only
// 0xE000 - 0xFDFF : Reserved - Echo RAM
// 0xFE00 - 0xFE9F : Object Attribute Memory
// 0xFEA0 - 0xFEFF : Reserved - Unusable
// 0xFF00 - 0xFF7F : I/O Registers
// 0xFF80 - 0xFFFE : Zero Page
u8 bus_read(u16 address) {
if(address < 0x8000) {
return cart_read(address);
}
NO_IMPL
}
void bus_write(u16 address, u8 value) {
if(address < 0x8000) {
cart_write(address);
return;
}
NO_IMPL
}

View File

@ -173,4 +173,15 @@ bool cart_load(char *cart) {
}
return true;
}
u8 cart_read(u16 address){
//for now ROM ONLY type supported...
return ctx.rom_data[address];
}
void cart_write(u16 address, u8 value){
NO_IMPL
}

View File

@ -1,10 +1,64 @@
#include <cpu.h>
#include <bus.h>
cpu_context ctx = {0};
void cpu_init() {
ctx.regs.pc = 0x100;
}
static void fetch_instruction() {
ctx.cur_opcode = bus_read(ctx.regs.pc++);
ctx.cur_inst = instruction_by_opcode(ctx.cur_opcode);
if(ctx.cur_inst == NULL){
printf("Unknown Instruction! %d\n", ctx.cur_opcode);
exit(-7);
}
}
static void fetch_data() {
ctx.mem_dest = 0;
ctx.dest_is_mem = false;
switch(ctx.cur_inst->mode) {
case AM_IMP: return;
case AM_R:
ctx.fetched_data = cpu_read_reg(ctx.cur_inst->reg_1);
return;
case AM_R_D8:
ctx.fetched_data = bus_read(ctx.regs.pc);
emu_cycles(1);
ctx.regs.pc++;
return;
case AM_D16: {
u16 lo = bus_read(ctx.regs.pc);
emu_cycles(1);
u16 hi = bus_read(ctx.regs.pc+1);
emu_cycles(1);
ctx.fetched_data = lo | (hi << 8);
ctx.regs.pc += 2;
return;
}
default:
printf("Unknown Addressing Mode! %d\n", ctx.cur_inst->mode);
exit(-7);
return;
}
}
static void execute() {
printf("\tNot executing yet...\n");
}
bool cpu_step() {
printf("Cpu not yet implemented.\n");
return false;
if(!ctx.halted) {
u16 pc = ctx.regs.pc;
fetch_instruction();
fetch_data();
printf("Executing Instruction: %02X PC: %04X\n", ctx.cur_opcode, pc);
execute();
}
return true;
}

29
lib/cpu_util.c Normal file
View File

@ -0,0 +1,29 @@
#include <cpu.h>
extern cpu_context ctx;
u16 reverse(u16 n) {
return ((n & 0XFF00) >> 8) | ((n & 0x00FF) << 8);
}
u16 cpu_read_reg(reg_type rt) {
switch(rt) {
case RT_A: return ctx.regs.a;
case RT_F: return ctx.regs.f;
case RT_B: return ctx.regs.b;
case RT_C: return ctx.regs.c;
case RT_D: return ctx.regs.d;
case RT_E: return ctx.regs.e;
case RT_H: return ctx.regs.h;
case RT_L: return ctx.regs.l;
case RT_AF: return reverse(*((u16 *)&ctx.regs.a));
case RT_BC: return reverse(*((u16 *)&ctx.regs.b));
case RT_DE: return reverse(*((u16 *)&ctx.regs.d));
case RT_HL: return reverse(*((u16 *)&ctx.regs.h));
case RT_PC: return ctx.regs.pc;
case RT_SP: return ctx.regs.sp;
default: return 0;
}
}

View File

@ -54,4 +54,8 @@ int emu_run(int argc, char **argv) {
}
return 0;
}
void emu_cycles(int cpu_cycles) {
//TODO
}

22
lib/instructions.c Normal file
View File

@ -0,0 +1,22 @@
#include <instructions.h>
#include <cpu.h>
instruction instructions[0x100] = {
[0x00] = {IN_NOP, AM_IMP},
[0x05] = {IN_DEC, AM_R, RT_B},
[0x0E] = {IN_LD, AM_R_D8, RT_C},
[0xAF] = {IN_XOR, AM_R, RT_A},
[0xC3] = {IN_JP, AM_D16},
};
instruction *instruction_by_opcode(u8 opcode) {
if (instructions[opcode].type == IN_NONE) {
return NULL;
}
return &instructions[opcode];
}

View File

@ -7,11 +7,6 @@
#include <cpu.h>
#include <cart.h>
START_TEST(test_nothing) {
bool b = cpu_step();
ck_assert_uint_eq(b, false);
} END_TEST
START_TEST(test_cart) {
bool b = cart_load("../../roms/cpu_instrs.gb");
ck_assert_uint_eq(b, true);
@ -29,7 +24,6 @@ Suite *stack_suite() {
Suite *s = suite_create("emu");
TCase *tc = tcase_create("core");
tcase_add_test(tc, test_nothing);
tcase_add_test(tc, test_cart);
suite_add_tcase(s, tc);