Compare commits

...

8 Commits

Author SHA1 Message Date
Matthew Laux d69a3b35f1 slight clean up 2023-10-18 19:38:10 -05:00
Matthew Laux f9f199dad2 generate simple "return 0x1234" and dynamically run
create example program
2023-10-18 19:27:10 -05:00
Matthew Laux d22e2d9cba oops 2023-10-18 18:48:01 -05:00
Matthew Laux aba7a9b847 add gb test program 2023-10-18 18:45:03 -05:00
Matthew Laux a3efa63361 no idea how to write a JIT compiler but this seems like an ok start 2023-10-18 17:52:24 -05:00
Matthew Laux 5ed288eaa9 not really sure what i was working on 2023-10-18 17:14:20 -05:00
Matthew Laux bad415fa23 fix button state for pokemon 2023-05-17 23:50:30 -05:00
Matthew Laux ead7a3e17c fix a bunch of instructions, passes most blargg tests now 2023-05-17 23:41:55 -05:00
10 changed files with 320 additions and 43 deletions

1
.gitignore vendored
View File

@ -14,3 +14,4 @@ lib
imgui.ini
.DS_Store
roms
examples/*.bin

View File

@ -34,8 +34,8 @@ add_executable(gb6
)
target_link_libraries(gb6
"-framework OpenGL"
"-framework CoreFoundation"
"-lGL"
"-ldl"
"${SDL_LIBS}"
)

BIN
cli/save.sav Normal file

Binary file not shown.

15
examples/make.sh Executable file
View File

@ -0,0 +1,15 @@
#!/bin/sh
for src_file in *.asm; do
[ -e "$src_file" ] || continue
obj_file="$(basename "$src_file" .asm).obj"
bin_file="$(basename "$src_file" .asm).bin"
echo "$src_file -> $bin_file"
rgbasm -o $obj_file $src_file
rgblink --nopad -o $bin_file $obj_file
done
rm -f *.obj

23
examples/simple.asm Normal file
View File

@ -0,0 +1,23 @@
section "main", rom0
nop
ld b, $0
ld c, $11
ld d, $22
ld e, $33
ld h, $44
ld l, $55
ld [hl], $66
ld a, $77
ld bc, $0123
ld de, $4567
ld hl, $89ab
ld sp, $cdef
simple_loop:
dec a
jr nz, simple_loop
end:
jr end
db $fd

156
src/compiler68k.c Normal file
View File

@ -0,0 +1,156 @@
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/mman.h> // mprotect
#include <unistd.h> // getpagesize
// A -> D0
// BC -> D1
// DE -> D2
// HL -> A0
// SP -> A7
uint8_t out_code[1024];
uint8_t memory[1024]; // ???
uint32_t out_ptr;
struct basic_block {
// in 68k space?
uint8_t code[256];
size_t length;
};
// need some kind of map from gb address to struct basic_block?
uint8_t test_code[] = {
0x00, // nop
0x06, 0x00, // ld b, $0
0x0e, 0x11, // ld c, $11
0x16, 0x22, // ld d, $22
0x1e, 0x33, // ld e, $33
0x26, 0x44, // ld h, $44
0x2e, 0x55, // ld l, $55
0x36, 0x66, // ld [hl], $66
0x3e, 0x77, // ld a, $77
0x01, 0x23, 0x01, // ld bc, $0123
0x11, 0x67, 0x45, // ld de, $4567
0x21, 0xab, 0x89, // ld hl, $89ab
0x31, 0xef, 0xcd, // ld sp, $cdef
0x3e, 0x0a, // .loop: ld a, 10
0x3d, // dec a
0x20, 0xfd, // jr nz, .loop
0x18, 0xfe, // jr $-1
0xc9 // ret
};
struct basic_block *compile_block(uint16_t src_address, uint8_t *gb_code)
{
uint8_t instruction;
struct basic_block *bblock;
uint32_t dst_ptr = 0;
uint16_t src_ptr = 0;
printf("compile block starting at 0x%04x\n", src_address);
bblock = malloc(sizeof *bblock);
// bblock->code = out_code + start;
while (1) {
instruction = gb_code[src_ptr++];
if (instruction == 0xfd) {
// invalid opcode for testing
break;
}
}
bblock->length = 6;
bblock->code[0] = 0xb8; // mov eax, 1234h
bblock->code[1] = 0x34;
bblock->code[2] = 0x12;
bblock->code[3] = 0x00;
bblock->code[4] = 0x00;
bblock->code[5] = 0xc3; // ret
return bblock;
}
void run_block(struct basic_block *bblock)
{
// calling convention? do i need to do this from asm?
uint16_t jump_target = ((uint16_t (*)()) bblock->code)();
}
// TODO
void block_cache_add(uint16_t src_address, struct basic_block *bblock)
{
// no-op
}
struct basic_block *block_cache_get(uint16_t src_address)
{
return NULL;
}
// 1. compile each block ending in a jump
// 2. turn the jump into a return
// 3. add the compiled code to some kind of cache
// 3. return back to check the cache and maybe compile the next block
void run_all(uint8_t *gb_code)
{
struct basic_block *bblock;
uint16_t jump_target = 0;
int page_size, ret;
while (1) {
bblock = block_cache_get(jump_target);
if (!bblock) {
bblock = compile_block(jump_target, gb_code + jump_target);
if (bblock->length == 0) {
break;
}
block_cache_add(jump_target, bblock);
}
// for testing...
page_size = getpagesize();
ret = mprotect(
(void *) ((uint64_t) bblock & ~(page_size - 1)),
page_size,
PROT_READ | PROT_WRITE | PROT_EXEC
);
if (ret == -1) {
perror("mprotect");
exit(0);
}
jump_target = ((uint16_t (*)()) bblock->code)();
}
}
int main(int argc, char *argv[])
{
FILE *fp;
long len;
uint8_t *data;
if (argc < 2) {
data = test_code;
} else {
fp = fopen(argv[1], "r");
fseek(fp, 0, SEEK_END);
len = ftell(fp);
rewind(fp);
data = malloc(len);
fread(data, 1, len, fp);
fclose(fp);
}
run_all(data);
if (data != test_code) {
free(data);
}
return 0;
}

123
src/cpu.c
View File

@ -73,13 +73,14 @@ static inline void write8(struct cpu *cpu, u16 address, u8 data)
static inline void write16(struct cpu *cpu, u16 address, u16 data)
{
dmg_write(cpu->dmg, address, data);
dmg_write(cpu->dmg, address, data & 0xff);
dmg_write(cpu->dmg, address + 1, data >> 8);
}
static void inc_with_carry(struct cpu *regs, u8 *reg)
{
clear_flag(regs, FLAG_SIGN);
if(*reg == 0xff || *reg == 0x0f)
if((*reg & 0xf) == 0xf)
set_flag(regs, FLAG_HALF_CARRY);
else clear_flag(regs, FLAG_HALF_CARRY);
(*reg)++;
@ -91,7 +92,7 @@ static void inc_with_carry(struct cpu *regs, u8 *reg)
static void dec_with_carry(struct cpu *regs, u8 *reg)
{
set_flag(regs, FLAG_SIGN);
if(*reg == 0x00 || *reg == 0x10)
if((*reg & 0xf) == 0)
set_flag(regs, FLAG_HALF_CARRY);
else clear_flag(regs, FLAG_HALF_CARRY);
(*reg)--;
@ -103,11 +104,20 @@ static void dec_with_carry(struct cpu *regs, u8 *reg)
static u8 rotate_left(struct cpu *regs, u8 reg)
{
int old_carry = flag_isset(regs, FLAG_CARRY);
// copy old leftmost bit to carry flag, clear Z, N, H
regs->f = (reg & 0x80) >> 3;
// rotate
int result = reg << 1 | old_carry;
if (!result) set_flag(regs, FLAG_ZERO);
int result = (u8) ((reg & 0x7f) << 1) | old_carry;
if (!result) {
set_flag(regs, FLAG_ZERO);
} else {
clear_flag(regs, FLAG_ZERO);
}
clear_flag(regs, FLAG_SIGN);
clear_flag(regs, FLAG_HALF_CARRY);
if (reg & 0x80) {
set_flag(regs, FLAG_CARRY);
} else {
clear_flag(regs, FLAG_CARRY);
}
return result;
}
@ -154,7 +164,7 @@ static u8 rrc(struct cpu *cpu, u8 val)
static u8 shift_left(struct cpu *cpu, u8 value)
{
int result = value << 1;
u8 result = value << 1;
if (result == 0) {
set_flag(cpu, FLAG_ZERO);
} else {
@ -172,7 +182,7 @@ static u8 shift_left(struct cpu *cpu, u8 value)
static u8 shift_right(struct cpu *cpu, u8 value)
{
int result = (signed) value >> 1;
u8 result = (signed char) value >> 1;
if (result == 0) {
set_flag(cpu, FLAG_ZERO);
} else {
@ -257,13 +267,9 @@ static void and(struct cpu *cpu, u8 value)
static void add(struct cpu *cpu, u8 value, int with_carry)
{
u8 sum_trunc;
int sum_full = cpu->a + value;
int carry = (with_carry && flag_isset(cpu, FLAG_CARRY)) ? 1 : 0;
if (carry) {
sum_full++;
}
sum_trunc = (u8) sum_full;
int sum_full = cpu->a + value + carry;
u8 sum_trunc = (u8) sum_full;
if (sum_trunc == 0) {
set_flag(cpu, FLAG_ZERO);
} else {
@ -285,25 +291,21 @@ static void add(struct cpu *cpu, u8 value, int with_carry)
static void subtract(struct cpu *cpu, u8 value, int with_carry, int just_compare)
{
u8 sum_trunc;
int sum_full = cpu->a - value;
int carry = (with_carry && flag_isset(cpu, FLAG_CARRY)) ? 1 : 0;
if (carry) {
sum_full--;
}
sum_trunc = (u8) sum_full;
int sum_full = cpu->a - value - carry;
u8 sum_trunc = (u8) sum_full;
if (!sum_trunc) {
set_flag(cpu, FLAG_ZERO);
} else {
clear_flag(cpu, FLAG_ZERO);
}
set_flag(cpu, FLAG_SIGN);
if (value > cpu->a) {
if (sum_full < 0) {
set_flag(cpu, FLAG_CARRY);
} else {
clear_flag(cpu, FLAG_CARRY);
}
if (((cpu->a & 0xf) - (value & 0xf)) & 0x10) {
if (((cpu->a & 0xf) - (value & 0xf) - carry) & 0x10) {
set_flag(cpu, FLAG_HALF_CARRY);
} else {
clear_flag(cpu, FLAG_HALF_CARRY);
@ -336,21 +338,48 @@ static u16 pop(struct cpu *cpu)
static void add16(struct cpu *cpu, u16 src)
{
clear_flag(cpu, FLAG_SIGN);
int total = read_hl(cpu) + src; // promoted to int
int trunc = total & 0xffff;
clear_flag(cpu, FLAG_SIGN);
if (total > 0xffff) {
set_flag(cpu, FLAG_CARRY);
} else {
clear_flag(cpu, FLAG_CARRY);
}
if (((cpu->h & 0xf) + ((src >> 8) & 0xf)) & 0x10) {
if (((read_hl(cpu) & 0xfff) + (src & 0xfff)) & 0x1000) {
// true if carry from bit 11 to bit 12
set_flag(cpu, FLAG_HALF_CARRY);
} else {
clear_flag(cpu, FLAG_HALF_CARRY);
}
write_hl(cpu, total & 0xffff);
write_hl(cpu, trunc);
}
static void add_sp(struct cpu *cpu, u8 value)
{
int total = cpu->sp + (signed char) value;
clear_flag(cpu, FLAG_ZERO);
clear_flag(cpu, FLAG_SIGN);
if (total > 0xffff) {
set_flag(cpu, FLAG_CARRY);
} else {
clear_flag(cpu, FLAG_CARRY);
}
cpu->sp = (u16) total;
}
static void ld_hl_sp(struct cpu *cpu, u8 value)
{
int total = cpu->sp + (signed char) value;
clear_flag(cpu, FLAG_ZERO);
clear_flag(cpu, FLAG_SIGN);
if (total > 0xffff) {
set_flag(cpu, FLAG_CARRY);
} else {
clear_flag(cpu, FLAG_CARRY);
}
write_hl(cpu, total);
}
static u8 read_reg(struct cpu *cpu, int index)
@ -407,6 +436,8 @@ static void extended_insn(struct cpu *cpu, u8 insn)
srl,
};
// rl, sla, sra
#ifdef GB6_DEBUG
printf(" %s\n", instructions[insn + 0x100].format);
#endif
@ -473,6 +504,24 @@ static void daa(struct cpu *cpu)
clear_flag(cpu, FLAG_HALF_CARRY);
}
static void scf(struct cpu *cpu)
{
clear_flag(cpu, FLAG_SIGN);
clear_flag(cpu, FLAG_HALF_CARRY);
set_flag(cpu, FLAG_CARRY);
}
static void ccf(struct cpu *cpu)
{
clear_flag(cpu, FLAG_SIGN);
clear_flag(cpu, FLAG_HALF_CARRY);
if (flag_isset(cpu, FLAG_CARRY)) {
clear_flag(cpu, FLAG_CARRY);
} else {
set_flag(cpu, FLAG_CARRY);
}
}
static u16 handlers[] = { 0x40, 0x48, 0x50, 0x58, 0x60 };
static u16 check_interrupts(struct cpu *cpu)
@ -508,10 +557,15 @@ void cpu_step(struct cpu *cpu)
intr_dest = check_interrupts(cpu);
if (intr_dest) {
push(cpu, cpu->pc);
cpu->halted = 0;
cpu->pc = intr_dest;
return;
}
if (cpu->halted) {
return;
}
u8 opc = dmg_read(cpu->dmg, cpu->pc);
#ifdef GB6_DEBUG
printf("0x%04x %s\n", cpu->pc, instructions[opc].format);
@ -531,6 +585,7 @@ void cpu_step(struct cpu *cpu)
break;
case 0x0f: // RRCA
cpu->a = rrc(cpu, cpu->a);
clear_flag(cpu, FLAG_ZERO);
break;
case 0x10: // STOP
cpu->pc++;
@ -541,6 +596,7 @@ void cpu_step(struct cpu *cpu)
break;
case 0x07: // RLCA
cpu->a = rlc(cpu, cpu->a);
clear_flag(cpu, FLAG_ZERO);
break;
case 0x08: // LD (a16),SP
write16(cpu, read16(cpu, cpu->pc), cpu->sp);
@ -554,16 +610,18 @@ void cpu_step(struct cpu *cpu)
break;
case 0x17: // RLA
cpu->a = rotate_left(cpu, cpu->a);
clear_flag(cpu, FLAG_ZERO);
break;
case 0x1f: // RRA
cpu->a = rotate_right(cpu, cpu->a);
clear_flag(cpu, FLAG_ZERO);
break;
case 0x37: // SCF
set_flag(cpu, FLAG_CARRY);
scf(cpu);
break;
case 0x3f: // CCF
clear_flag(cpu, FLAG_CARRY);
ccf(cpu);
break;
// incs and decs
@ -944,6 +1002,7 @@ void cpu_step(struct cpu *cpu)
break;
case 0x76: // HALT
//cpu->halted = 1;
break;
case 0xc1: // POP BC
@ -993,6 +1052,10 @@ void cpu_step(struct cpu *cpu)
case 0xe5: // PUSH HL
push(cpu, read_hl(cpu));
break;
case 0xe8:
add_sp(cpu, read8(cpu, cpu->pc));
cpu->pc++;
break;
case 0xe9: // JP HL
cpu->pc = read_hl(cpu);
break;
@ -1018,7 +1081,7 @@ void cpu_step(struct cpu *cpu)
push(cpu, read_af(cpu));
break;
case 0xf8: // LD HL, SP+i8
write_hl(cpu, cpu->sp + (signed) read8(cpu, cpu->pc));
ld_hl_sp(cpu, read8(cpu, cpu->pc));
cpu->pc++;
break;
case 0xf9: // LD SP, HL

View File

@ -20,6 +20,8 @@ struct cpu
u32 cycle_count;
u8 interrupt_enable;
u8 halted;
struct dmg *dmg;
// u8 (*mem_read)(void *, u16);
// void (*mem_write)(void *, u16, u8);

View File

@ -40,7 +40,7 @@ void dmg_set_button(struct dmg *dmg, int field, int button, int pressed)
static u8 get_button_state(struct dmg *dmg)
{
u8 ret = 0;
u8 ret = 0xf0;
if (dmg->action_selected) {
ret |= dmg->action_buttons;
}
@ -50,8 +50,6 @@ static u8 get_button_state(struct dmg *dmg)
return ret;
}
static int counter;
u8 dmg_read(void *_dmg, u16 address)
{
struct dmg *dmg = (struct dmg *) _dmg;
@ -82,8 +80,13 @@ u8 dmg_read(void *_dmg, u16 address)
} else if (address == 0xff00) {
return get_button_state(dmg);
} else if (address == REG_TIMER_DIV) {
counter++;
return counter;
return (dmg->timer_div & 0xff00) >> 8;
} else if (address == REG_TIMER_COUNT) {
return dmg->timer_count;
} else if (address == REG_TIMER_MOD) {
return dmg->timer_mod;
} else if (address == REG_TIMER_CONTROL) {
return dmg->timer_control;
} else if (address == 0xff0f) {
return dmg->interrupt_requested;
} else if (address == 0xffff) {
@ -116,6 +119,17 @@ void dmg_write(void *_dmg, u16 address, u8 data)
} else if (address < 0xe000) {
// printf("write ram %04x %02x\n", address, data);
dmg->main_ram[address - 0xc000] = data;
} else if (address == REG_TIMER_DIV) {
dmg->timer_div = 0;
} else if (address == REG_TIMER_COUNT) {
printf("write timer count\n");
dmg->timer_count = data;
} else if (address == REG_TIMER_MOD) {
printf("write timer mod\n");
dmg->timer_mod = data;
} else if (address == REG_TIMER_CONTROL) {
printf("write timer control\n");
dmg->timer_control = data;
} else if (address == 0xFF46) {
u16 src = data << 8;
int k = 0;
@ -248,15 +262,14 @@ static void render_objs(struct dmg *dmg)
static void timer_step(struct dmg *dmg)
{
dmg->timer_div++;
return;
if (!(dmg_read(dmg, REG_TIMER_CONTROL) & TIMER_CONTROL_ENABLED)) {
return;
}
int passed = dmg->cpu->cycle_count - dmg->last_timer_update;
// TODO
if (passed < 10000) {
return;
}
u8 counter = dmg_read(dmg, REG_TIMER_COUNT);
u8 modulo = dmg_read(dmg, REG_TIMER_MOD);
@ -278,7 +291,7 @@ void dmg_step(void *_dmg)
// order of dependencies? i think cpu needs to step first then update
// all other hw
cpu_step(dmg->cpu);
// timer_step(dmg);
timer_step(dmg);
// each line takes 456 cycles
int cycle_diff = dmg->cpu->cycle_count - dmg->last_lcd_update;

View File

@ -36,12 +36,16 @@ struct dmg {
u32 last_lcd_update;
u32 last_timer_update;
int joypad_selected;
int action_selected; // non-0 if A/B/start/select selected, 0 for directions
int action_selected;
u8 interrupt_enabled;
u8 interrupt_requested;
u8 joypad;
u8 action_buttons;
u16 timer_div;
u8 timer_count;
u8 timer_mod;
u8 timer_control;
};
void dmg_new(struct dmg *dmg, struct cpu *cpu, struct rom *rom, struct lcd *lcd);