diff --git a/cli/compile.sh b/cli/compile.sh index 4d9d243..5bc6d9c 100755 --- a/cli/compile.sh +++ b/cli/compile.sh @@ -1,6 +1,6 @@ #!/bin/sh mkdir -p build cd build -cmake .. +cmake -DCMAKE_BUILD_TYPE=Debug .. make cd .. diff --git a/cli/emulator.c b/cli/emulator.c index d608f2b..929ee2b 100644 --- a/cli/emulator.c +++ b/cli/emulator.c @@ -3,13 +3,16 @@ #include "dmg.h" #include "cpu.h" #include "rom.h" +#include "lcd.h" int main(int argc, char *argv[]) { struct cpu cpu; struct rom rom; struct dmg dmg; - int executed = 0; + struct lcd lcd; + + int executed; if (argc < 2) { printf("no rom specified\n"); @@ -24,12 +27,12 @@ int main(int argc, char *argv[]) // this might be too much abstraction but it'll let me // test the cpu, rom, and dmg independently and use the cpu // for other non-GB stuff - dmg_new(&dmg, &cpu, &rom); + dmg_new(&dmg, &cpu, &rom, &lcd); cpu_bind_mem_model(&cpu, &dmg, dmg_read, dmg_write); cpu.pc = 0; - for (; executed < 100000; executed++) { + for (executed = 0; executed < 100000; executed++) { cpu_step(&cpu); } diff --git a/src/dmg.c b/src/dmg.c index f6468e6..02e45ed 100644 --- a/src/dmg.c +++ b/src/dmg.c @@ -2,14 +2,16 @@ #include "cpu.h" #include "rom.h" +#include "lcd.h" #include "dmg.h" #include "types.h" #include "bootstrap.h" -void dmg_new(struct dmg *dmg, struct cpu *cpu, struct rom *rom) +void dmg_new(struct dmg *dmg, struct cpu *cpu, struct rom *rom, struct lcd *lcd) { dmg->cpu = cpu; dmg->rom = rom; + dmg->lcd = lcd; } u8 dmg_read(void *_dmg, u16 address) @@ -30,10 +32,13 @@ u8 dmg_read(void *_dmg, u16 address) return 0; } else if (address < 0xe000) { return dmg->main_ram[address - 0xc000]; + } else if (lcd_is_valid_addr(address)) { + return lcd_read(dmg->lcd, address); } else if (address >= 0xff80 && address <= 0xfffe) { return dmg->zero_page[address - 0xff80]; } else { // not sure about any of this yet + fprintf(stderr, "don't know how to read 0x%04x\n", address); return 0; } } @@ -54,9 +59,22 @@ void dmg_write(void *_dmg, u16 address, u8 data) // TODO switchable ram bank } else if (address < 0xe000) { dmg->main_ram[address - 0xc000] = data; + } else if (lcd_is_valid_addr(address)) { + lcd_write(dmg->lcd, address, data); } else if (address >= 0xff80 && address <= 0xfffe) { dmg->zero_page[address - 0xff80] = data; } else { // not sure about any of this yet } } + +void dmg_step(void *_dmg) +{ + struct dmg *dmg = (struct dmg *) _dmg; + + // order of dependencies? i think cpu needs to step first then update + // all other hw + cpu_step(dmg->cpu); + + lcd_step(dmg->lcd); +} \ No newline at end of file diff --git a/src/dmg.h b/src/dmg.h index da5a765..fc071f3 100644 --- a/src/dmg.h +++ b/src/dmg.h @@ -13,9 +13,12 @@ struct dmg { u8 zero_page[0x80]; }; -void dmg_new(struct dmg *dmg, struct cpu *cpu, struct rom *rom); +void dmg_new(struct dmg *dmg, struct cpu *cpu, struct rom *rom, struct lcd *lcd); +// why did i make these void * u8 dmg_read(void *dmg, u16 address); void dmg_write(void *dmg, u16 address, u8 data); +void dmg_step(void *dmg); + #endif diff --git a/src/lcd.c b/src/lcd.c index f0774ec..0acaf2d 100644 --- a/src/lcd.c +++ b/src/lcd.c @@ -3,6 +3,35 @@ #include "types.h" #include "lcd.h" +static void set_bit(struct lcd *lcd, u16 addr, u8 bit) +{ + lcd_write(lcd, addr, lcd_read(lcd, addr) | (1 << bit)); +} + +static void clear_bit(struct lcd *lcd, u16 addr, u8 bit) +{ + lcd_write(lcd, addr, lcd_read(lcd, addr) & ~(1 << bit)); +} + +u8 lcd_is_valid_addr(u16 addr) +{ + return addr >= REG_LCD_BASE && addr <= REG_LCD_LAST; +} + +u8 lcd_read(struct lcd *lcd, u16 addr) +{ + return lcd->regs[addr - REG_LCD_BASE]; +} + +void lcd_write(struct lcd *lcd, u16 addr, u8 value) +{ + if (addr == REG_LY) { + // writing to this register always resets it + value = 0; + } + lcd->regs[addr - REG_LCD_BASE] = value; +} + void lcd_put_pixel(struct lcd *lcd, u8 x, u8 y, u8 value) { if (x >= LCD_WIDTH || y >= LCD_HEIGHT) { @@ -10,4 +39,18 @@ void lcd_put_pixel(struct lcd *lcd, u8 x, u8 y, u8 value) return; } lcd->pixels[y * LCD_WIDTH + x] = value; +} + +void lcd_step(struct lcd *lcd) +{ + // update LYC + if (lcd_read(lcd, REG_LY) == lcd_read(lcd, REG_LYC)) { + set_bit(lcd, REG_STAT, STAT_FLAG_MATCH); + } else { + clear_bit(lcd, REG_STAT, STAT_FLAG_MATCH); + } + + // step to next scanline 0-153 + u8 next_scanline = (lcd_read(lcd, REG_LY) + 1) % 154; + lcd_write(lcd, REG_LY, next_scanline); } \ No newline at end of file diff --git a/src/lcd.h b/src/lcd.h index c0a228e..ccb2326 100644 --- a/src/lcd.h +++ b/src/lcd.h @@ -6,10 +6,37 @@ #define LCD_WIDTH 160 #define LCD_HEIGHT 144 +#define REG_LCD_BASE 0xff40 + +#define REG_LCDC 0xff40 +#define REG_STAT 0xff41 +#define REG_SCY 0xff42 +#define REG_SCX 0xff43 +#define REG_LY 0xff44 +#define REG_LYC 0xff45 +#define REG_DMA 0xff46 +#define REG_BGP 0xff47 +#define REG_OBP0 0xff48 +#define REG_OBP1 0xff49 +#define REG_WY 0xff4a +#define REG_WX 0xff4b + +#define REG_LCD_LAST REG_WX + +#define STAT_FLAG_MATCH 2 + struct lcd { + u8 regs[0x0c]; u8 *pixels; }; +u8 lcd_is_valid_addr(u16 addr); +u8 lcd_read(struct lcd *lcd, u16 addr); +void lcd_write(struct lcd *lcd, u16 addr, u8 value); + void lcd_put_pixel(struct lcd *lcd, u8 x, u8 y, u8 value); +// i feel like i'm going to need to call this every cycle and update regs +void lcd_step(struct lcd *lcd); + #endif \ No newline at end of file