From ebd6a528118eab29ae1e06831389f81560c2ae44 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Sun, 2 Jan 2011 15:46:54 +1100 Subject: [PATCH] Update to release v000r06. byuu says: Added MBC1 emulation, although battery RAM doesn't save or load to disk yet. Made up a fake MBC0 which is really just saying 'no MBC', for consistent handling of all MBCs. Added bumpers to stop ROM/RAM out of bounds accesses. Added STAT interrupts for LY coincidence, Vblank and Hblank (not for OAM access yet, I don't know the timings.) Fixed timer interrupt [Jonas Quinn] Made all interrupts call a CPU function instead of just setting a flag for better control (to allow below addition.) Added HALT and STOP emulation, the latter permanently locks the Game Boy for now. The former breaks on interrupts. Rewrote all the rendering code to suck 50% less, though it's still absolutely miserable and scanline-based. Added pixel-level horizontal scrolling to BGs. Fixed OBJ rendering error that was making them render upside down (I was flipping to compensate before.) Added OBJ 8x16 mode. Added OBJ priority support. Added window (but it's broken to all hell on Mega Man II.) --- gameboy/cartridge/cartridge.cpp | 55 ++++++++++- gameboy/cartridge/cartridge.hpp | 23 ++++- gameboy/cartridge/mbc0/mbc0.cpp | 17 ++++ gameboy/cartridge/mbc0/mbc0.hpp | 6 ++ gameboy/cartridge/mbc1/mbc1.cpp | 62 ++++++++++++ gameboy/cartridge/mbc1/mbc1.hpp | 14 +++ gameboy/cartridge/mmio/mmio.cpp | 10 -- gameboy/cartridge/mmio/mmio.hpp | 2 - gameboy/cpu/core/core.cpp | 6 +- gameboy/cpu/cpu.cpp | 14 +++ gameboy/cpu/cpu.hpp | 12 +++ gameboy/cpu/mmio/mmio.cpp | 2 +- gameboy/cpu/timing/timing.cpp | 16 +-- gameboy/gameboy.hpp | 2 +- gameboy/lcd/lcd.cpp | 168 +++++++++++++++++++++++--------- gameboy/lcd/lcd.hpp | 12 ++- gameboy/lcd/mmio/mmio.cpp | 13 ++- ui/general/main-window.cpp | 2 +- 18 files changed, 354 insertions(+), 82 deletions(-) create mode 100755 gameboy/cartridge/mbc0/mbc0.cpp create mode 100755 gameboy/cartridge/mbc0/mbc0.hpp create mode 100755 gameboy/cartridge/mbc1/mbc1.cpp create mode 100755 gameboy/cartridge/mbc1/mbc1.hpp delete mode 100755 gameboy/cartridge/mmio/mmio.cpp delete mode 100755 gameboy/cartridge/mmio/mmio.hpp diff --git a/gameboy/cartridge/cartridge.cpp b/gameboy/cartridge/cartridge.cpp index 611b35f5..53d3d66a 100755 --- a/gameboy/cartridge/cartridge.cpp +++ b/gameboy/cartridge/cartridge.cpp @@ -3,7 +3,8 @@ #define CARTRIDGE_CPP namespace GameBoy { -#include "mmio/mmio.cpp" +#include "mbc0/mbc0.cpp" +#include "mbc1/mbc1.cpp" Cartridge cartridge; void Cartridge::load(uint8_t *data, unsigned size) { @@ -18,7 +19,18 @@ void Cartridge::load(uint8_t *data, unsigned size) { info.cgbflag = romdata[0x0143]; info.sgbflag = romdata[0x0146]; - info.type = romdata[0x0147]; + + info.mapper = Mapper::Unknown; + info.ram = false; + info.battery = false; + info.rtc = false; + + switch(romdata[0x0147]) { default: + case 0x00: info.mapper = Mapper::MBC0; break; + case 0x01: info.mapper = Mapper::MBC1; break; + case 0x02: info.mapper = Mapper::MBC1; info.ram = true; break; + case 0x03: info.mapper = Mapper::MBC1; info.ram = true; info.battery = true; break; + } switch(romdata[0x0148]) { default: case 0x00: info.romsize = 2 * 16 * 1024; break; @@ -42,6 +54,8 @@ void Cartridge::load(uint8_t *data, unsigned size) { case 0x03: info.ramsize = 32 * 1024; break; } + ramdata = new uint8_t[ramsize = info.ramsize](); + loaded = true; } @@ -53,13 +67,48 @@ void Cartridge::unload() { loaded = false; } +uint8 Cartridge::rom_read(unsigned addr) { + if(addr >= romsize) addr %= romsize; + return romdata[addr]; +} + +void Cartridge::rom_write(unsigned addr, uint8 data) { + if(addr >= romsize) addr %= romsize; + romdata[addr] = data; +} + +uint8 Cartridge::ram_read(unsigned addr) { + if(ramsize == 0) return 0x00; + if(addr >= ramsize) addr %= ramsize; + return ramdata[addr]; +} + +void Cartridge::ram_write(unsigned addr, uint8 data) { + if(ramsize == 0) return; + if(addr >= ramsize) addr %= ramsize; + ramdata[addr] = data; +} + void Cartridge::power() { - for(unsigned n = 0x0000; n <= 0x7fff; n++) bus.mmio[n] = this; + mbc0.power(); + mbc1.power(); + + MMIO *mapper = 0; + switch(info.mapper) { + case Mapper::MBC0: mapper = &mbc0; break; + case Mapper::MBC1: mapper = &mbc1; break; + } + if(mapper) { + for(unsigned n = 0x0000; n <= 0x7fff; n++) bus.mmio[n] = mapper; + for(unsigned n = 0xa000; n <= 0xbfff; n++) bus.mmio[n] = mapper; + } reset(); } void Cartridge::reset() { + mbc1.reset(); + mbc1.reset(); } Cartridge::Cartridge() { diff --git a/gameboy/cartridge/cartridge.hpp b/gameboy/cartridge/cartridge.hpp index 4868fc56..03e4a1f2 100755 --- a/gameboy/cartridge/cartridge.hpp +++ b/gameboy/cartridge/cartridge.hpp @@ -1,11 +1,23 @@ -struct Cartridge : MMIO, property { - #include "mmio/mmio.hpp" +struct Cartridge : property { + #include "mbc0/mbc0.hpp" + #include "mbc1/mbc1.hpp" + + enum Mapper : unsigned { + MBC0, + MBC1, + Unknown, + }; struct Information { string name; uint8 cgbflag; uint8 sgbflag; - uint8 type; + + Mapper mapper; + bool ram; + bool battery; + bool rtc; + unsigned romsize; unsigned ramsize; } info; @@ -21,6 +33,11 @@ struct Cartridge : MMIO, property { void load(uint8_t *data, unsigned size); void unload(); + uint8 rom_read(unsigned addr); + void rom_write(unsigned addr, uint8 data); + uint8 ram_read(unsigned addr); + void ram_write(unsigned addr, uint8 data); + void power(); void reset(); diff --git a/gameboy/cartridge/mbc0/mbc0.cpp b/gameboy/cartridge/mbc0/mbc0.cpp new file mode 100755 index 00000000..85a8a505 --- /dev/null +++ b/gameboy/cartridge/mbc0/mbc0.cpp @@ -0,0 +1,17 @@ +#ifdef CARTRIDGE_CPP + +uint8 Cartridge::MBC0::mmio_read(uint16 addr) { + if(addr >= 0x0000 && addr <= 0x7fff) return cartridge.rom_read(addr); + return 0x00; +} + +void Cartridge::MBC0::mmio_write(uint16 addr, uint8 data) { +} + +void Cartridge::MBC0::power() { +} + +void Cartridge::MBC0::reset() { +} + +#endif diff --git a/gameboy/cartridge/mbc0/mbc0.hpp b/gameboy/cartridge/mbc0/mbc0.hpp new file mode 100755 index 00000000..fef9f9a9 --- /dev/null +++ b/gameboy/cartridge/mbc0/mbc0.hpp @@ -0,0 +1,6 @@ +struct MBC0 : MMIO { + uint8 mmio_read(uint16 addr); + void mmio_write(uint16 addr, uint8 data); + void power(); + void reset(); +} mbc0; diff --git a/gameboy/cartridge/mbc1/mbc1.cpp b/gameboy/cartridge/mbc1/mbc1.cpp new file mode 100755 index 00000000..fedb6b79 --- /dev/null +++ b/gameboy/cartridge/mbc1/mbc1.cpp @@ -0,0 +1,62 @@ +#ifdef CARTRIDGE_CPP + +uint8 Cartridge::MBC1::mmio_read(uint16 addr) { + if(addr >= 0x0000 && addr <= 0x3fff) { + return cartridge.rom_read(addr); + } + + if(addr >= 0x4000 && addr <= 0x7fff) { + return cartridge.rom_read(rom_bank | (addr & 0x3fff)); + } + + if(addr >= 0xa000 && addr <= 0xbfff) { + if(ram_enable) return cartridge.ram_read(ram_bank | (addr & 0x1fff)); + return 0x00; + } +} + +void Cartridge::MBC1::mmio_write(uint16 addr, uint8 data) { + if(addr >= 0x0000 && addr <= 0x1fff) { + ram_enable = (data & 0x0f) == 0x0a; + } + + if(addr >= 0x2000 && addr <= 0x3fff) { + rom_select = data & 0x1f; + if(rom_select == 0) rom_select = 1; + } + + if(addr >= 0x4000 && addr <= 0x5fff) { + ram_select = data & 0x03; + } + + if(addr >= 0x6000 && addr <= 0x7fff) { + mode_select = data & 0x01; + } + + if(mode_select == 0) { + rom_bank = (ram_select << 19) | (rom_select << 14); + ram_bank = 0x00; + } else { + rom_bank = (rom_select << 14); + ram_bank = (ram_select << 13); + } + + if(addr >= 0xa000 && addr <= 0xbfff) { + if(ram_enable) cartridge.ram_write(ram_bank | (addr & 0x1fff), data); + } +} + +void Cartridge::MBC1::power() { +} + +void Cartridge::MBC1::reset() { + ram_enable = false; + rom_select = 0x01; + ram_select = 0x00; + mode_select = 0; + + rom_bank = 0x4000; + ram_bank = 0x0000; +} + +#endif diff --git a/gameboy/cartridge/mbc1/mbc1.hpp b/gameboy/cartridge/mbc1/mbc1.hpp new file mode 100755 index 00000000..516acde8 --- /dev/null +++ b/gameboy/cartridge/mbc1/mbc1.hpp @@ -0,0 +1,14 @@ +struct MBC1 : MMIO { + bool ram_enable; //0000-1fff + uint8 rom_select; //2000-3fff + uint8 ram_select; //4000-5fff + bool mode_select; //6000-7fff + + unsigned rom_bank; + unsigned ram_bank; + + uint8 mmio_read(uint16 addr); + void mmio_write(uint16 addr, uint8 data); + void power(); + void reset(); +} mbc1; diff --git a/gameboy/cartridge/mmio/mmio.cpp b/gameboy/cartridge/mmio/mmio.cpp deleted file mode 100755 index 749b5895..00000000 --- a/gameboy/cartridge/mmio/mmio.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#ifdef CARTRIDGE_CPP - -uint8 Cartridge::mmio_read(uint16 addr) { - if(addr >= 0x0000 && addr <= 0x7fff) return romdata[addr]; -} - -void Cartridge::mmio_write(uint16 addr, uint8 data) { -} - -#endif diff --git a/gameboy/cartridge/mmio/mmio.hpp b/gameboy/cartridge/mmio/mmio.hpp deleted file mode 100755 index 92b8f614..00000000 --- a/gameboy/cartridge/mmio/mmio.hpp +++ /dev/null @@ -1,2 +0,0 @@ -uint8 mmio_read(uint16 addr); -void mmio_write(uint16 addr, uint8 data); diff --git a/gameboy/cpu/core/core.cpp b/gameboy/cpu/core/core.cpp index 317b936b..e1e226e2 100755 --- a/gameboy/cpu/core/core.cpp +++ b/gameboy/cpu/core/core.cpp @@ -566,11 +566,13 @@ void CPU::op_nop() { } void CPU::op_halt() { - //TODO + status.halt = true; + while(status.halt == true) op_io(); } void CPU::op_stop() { - //TODO + status.stop = true; + while(status.stop == true) op_io(); } void CPU::op_di() { diff --git a/gameboy/cpu/cpu.cpp b/gameboy/cpu/cpu.cpp index 70b4130f..f01523e4 100755 --- a/gameboy/cpu/cpu.cpp +++ b/gameboy/cpu/cpu.cpp @@ -21,6 +21,17 @@ void CPU::main() { } } +void CPU::interrupt_raise(CPU::Interrupt id) { + switch(id) { + case Interrupt::Vblank: status.interrupt_request_vblank = 1; break; + case Interrupt::Stat : status.interrupt_request_stat = 1; break; + case Interrupt::Timer : status.interrupt_request_timer = 1; break; + case Interrupt::Serial: status.interrupt_request_serial = 1; break; + case Interrupt::Joypad: status.interrupt_request_joypad = 1; break; + } + status.halt = false; +} + void CPU::interrupt_test() { if(status.ime) { if(status.interrupt_request_vblank && status.interrupt_enable_vblank) { @@ -80,6 +91,9 @@ void CPU::reset() { r[DE] = 0x0000; r[HL] = 0x0000; + status.halt = false; + status.stop = false; + status.ime = 0; status.timer0 = 0; status.timer1 = 0; diff --git a/gameboy/cpu/cpu.hpp b/gameboy/cpu/cpu.hpp index 28f4d211..7d410b35 100755 --- a/gameboy/cpu/cpu.hpp +++ b/gameboy/cpu/cpu.hpp @@ -3,7 +3,18 @@ struct CPU : Processor, MMIO { #include "mmio/mmio.hpp" #include "timing/timing.hpp" + enum class Interrupt : unsigned { + Vblank, + Stat, + Timer, + Serial, + Joypad, + }; + struct Status { + bool halt; + bool stop; + bool ime; unsigned timer0; unsigned timer1; @@ -48,6 +59,7 @@ struct CPU : Processor, MMIO { static void Main(); void main(); + void interrupt_raise(Interrupt id); void interrupt_test(); void interrupt_exec(uint16 pc); void power(); diff --git a/gameboy/cpu/mmio/mmio.cpp b/gameboy/cpu/mmio/mmio.cpp index e18b1e5e..610474b9 100755 --- a/gameboy/cpu/mmio/mmio.cpp +++ b/gameboy/cpu/mmio/mmio.cpp @@ -16,7 +16,7 @@ void CPU::mmio_joyp_poll() { status.joyp = 0x0f; if(status.p15 == 0) status.joyp &= button ^ 0x0f; if(status.p14 == 0) status.joyp &= dpad ^ 0x0f; - if(status.joyp != 0x0f) status.interrupt_request_joypad = 1; + if(status.joyp != 0x0f) interrupt_raise(Interrupt::Joypad); } uint8 CPU::mmio_read(uint16 addr) { diff --git a/gameboy/cpu/timing/timing.cpp b/gameboy/cpu/timing/timing.cpp index 0d96e62e..b62c5995 100755 --- a/gameboy/cpu/timing/timing.cpp +++ b/gameboy/cpu/timing/timing.cpp @@ -21,10 +21,10 @@ void CPU::add_clocks(unsigned clocks) { } void CPU::timer_stage0() { //262144hz - if(status.timer_clock == 1) { + if(status.timer_enable && status.timer_clock == 1) { if(++status.tima == 0) { status.tima = status.tma; - status.interrupt_request_timer = 1; + interrupt_raise(Interrupt::Timer); } } @@ -33,10 +33,10 @@ void CPU::timer_stage0() { //262144hz } void CPU::timer_stage1() { // 65536hz - if(status.timer_clock == 2) { + if(status.timer_enable && status.timer_clock == 2) { if(++status.tima == 0) { status.tima = status.tma; - status.interrupt_request_timer = 1; + interrupt_raise(Interrupt::Timer); } } @@ -45,10 +45,10 @@ void CPU::timer_stage1() { // 65536hz } void CPU::timer_stage2() { // 16384hz - if(status.timer_clock == 3) { + if(status.timer_enable && status.timer_clock == 3) { if(++status.tima == 0) { status.tima = status.tma; - status.interrupt_request_timer = 1; + interrupt_raise(Interrupt::Timer); } } @@ -59,10 +59,10 @@ void CPU::timer_stage2() { // 16384hz } void CPU::timer_stage3() { // 4096hz - if(status.timer_clock == 0) { + if(status.timer_enable && status.timer_clock == 0) { if(++status.tima == 0) { status.tima = status.tma; - status.interrupt_request_timer = 1; + interrupt_raise(Interrupt::Timer); } } diff --git a/gameboy/gameboy.hpp b/gameboy/gameboy.hpp index 4c131b7f..13c62158 100755 --- a/gameboy/gameboy.hpp +++ b/gameboy/gameboy.hpp @@ -5,7 +5,7 @@ namespace GameBoy { namespace Info { static const char Name[] = "bgameboy"; - static const char Version[] = "000.05"; + static const char Version[] = "000.06"; } } diff --git a/gameboy/lcd/lcd.cpp b/gameboy/lcd/lcd.cpp index be9818bc..abcf26ad 100755 --- a/gameboy/lcd/lcd.cpp +++ b/gameboy/lcd/lcd.cpp @@ -13,6 +13,10 @@ void LCD::Main() { void LCD::main() { while(true) { add_clocks(4); + + if(status.lx == 320) { + if(status.interrupt_hblank) cpu.interrupt_raise(CPU::Interrupt::Stat); + } } } @@ -28,8 +32,14 @@ void LCD::scanline() { status.lx -= 456; status.ly++; - if(status.ly == 144) cpu.status.interrupt_request_vblank = 1; -//print("Vblank - ", cpu.status.ime, " - ", cpu.status.interrupt_enable_vblank, "\n"); } + if(status.interrupt_lyc == true) { + if(status.ly == status.lyc) cpu.interrupt_raise(CPU::Interrupt::Stat); + } + + if(status.ly == 144) { + cpu.interrupt_raise(CPU::Interrupt::Vblank); + if(status.interrupt_vblank) cpu.interrupt_raise(CPU::Interrupt::Stat); + } if(status.ly == 154) frame(); if(status.ly < 144) render(); @@ -45,54 +55,130 @@ void LCD::frame() { } void LCD::render() { - uint8_t *output = screen + status.ly * 160; - uint8 y = status.ly + status.scy; - uint16 tmaddr = (status.bg_tilemap_select == 0 ? 0x1800 : 0x1c00); - tmaddr += (y >> 3) * 32; - tmaddr += (status.scx >> 3); - - for(unsigned t = 0; t < 20; t++) { - unsigned tdaddr; - if(status.bg_tiledata_select == 0) { - tdaddr = 0x1000 + (int8)vram[tmaddr + t] * 16; - } else { - tdaddr = 0x0000 + vram[tmaddr + t] * 16; - } - tdaddr += (status.ly & 7) * 2; - - uint8 d0 = vram[tdaddr + 0]; - uint8 d1 = vram[tdaddr + 1]; - - for(unsigned x = 0; x < 8; x++) { - uint8 palette = ((d0 & 0x80) >> 7) + ((d1 & 0x80) >> 6); - d0 <<= 1, d1 <<= 1; - *output++ = (3 - status.bgp[palette]) * 0x55; - } + for(unsigned n = 0; n < 160; n++) { + line[n].source = Line::Source::None; + line[n].output = 0; } - output = screen + status.ly * 160; + if(status.display_enable == true) { + if(status.bg_enable == true) render_bg(); + if(status.obj_enable == true) render_obj(); + if(status.window_display_enable == true) render_window(); + } + + uint8_t *output = screen + status.ly * 160; + for(unsigned n = 0; n < 160; n++) { + output[n] = (3 - line[n].output) * 0x55; + } +} + +void LCD::render_bg() { + unsigned iy = (status.ly + status.scy) & 255; + unsigned ix = status.scx; + + unsigned tmaddr = (status.bg_tilemap_select == 0 ? 0x1800 : 0x1c00); + + unsigned tx = (ix - 7) & 7; + uint8 d0 = 0, d1 = 0; + for(signed ox = -7; ox < 160; ox++) { + if(tx == 0) { + unsigned tile = (((iy >> 3) * 32) + (ix >> 3)) & 0x3fff; + unsigned tdaddr; + if(status.bg_tiledata_select == 0) { + tdaddr = 0x1000 + (int8)vram[tmaddr + tile] * 16; + } else { + tdaddr = 0x0000 + vram[tmaddr + tile] * 16; + } + tdaddr += (iy & 7) * 2; + + d0 = vram[tdaddr + 0]; + d1 = vram[tdaddr + 1]; + } + + uint8 palette = ((d0 & 0x80) >> 7) + ((d1 & 0x80) >> 6); + d0 <<= 1, d1 <<= 1; + + if(ox >= 0) { + line[ox].source = Line::Source::BG; + line[ox].output = status.bgp[palette]; + } + + ix = (ix + 1) & 255; + tx = (tx + 1) & 7; + } +} + +void LCD::render_window() { + if(status.wy > status.ly) return; + unsigned iy = (status.ly + status.wy) & 255; + unsigned ix = (status.wx - 7) & 255; + + unsigned tmaddr = (status.window_tilemap_select == 0 ? 0x1800 : 0x1c00); + + unsigned tx = (ix - 7) & 7; + uint8 d0 = 0, d1 = 0; + for(signed ox = -7; ox < 160; ox++) { + if(tx == 0) { + unsigned tile = (((iy >> 3) * 32) + (ix >> 3)) & 0x3fff; + unsigned tdaddr; + if(status.bg_tiledata_select == 0) { + tdaddr = 0x1000 + (int8)vram[tmaddr + tile] * 16; + } else { + tdaddr = 0x0000 + vram[tmaddr + tile] * 16; + } + tdaddr += (iy & 7) * 2; + + d0 = vram[tdaddr + 0]; + d1 = vram[tdaddr + 1]; + } + + uint8 palette = ((d0 & 0x80) >> 7) + ((d1 & 0x80) >> 6); + d0 <<= 1, d1 <<= 1; + + if(ox >= 7) { + line[ox].source = Line::Source::Window; + line[ox].output = status.bgp[palette]; + } + + ix = (ix + 1) & 255; + tx = (tx + 1) & 7; + } +} + +void LCD::render_obj() { + unsigned obj_size = (status.obj_size == 0 ? 8 : 16); + for(unsigned s = 0; s < 40; s++) { - unsigned sy = oam[(s << 2) + 0] - 9; - unsigned sx = oam[(s << 2) + 1] - 8; + unsigned sy = oam[(s << 2) + 0] - 16; + unsigned sx = oam[(s << 2) + 1] - 8; unsigned tile = oam[(s << 2) + 2]; unsigned attribute = oam[(s << 2) + 3]; - sy -= status.ly; - if(sy >= 8) continue; - if(attribute & 0x40||1) sy ^= 7; + sy = status.ly - sy; + if(sy >= obj_size) continue; + if(attribute & 0x40) sy ^= (obj_size - 1); - unsigned addr = tile * 16 + sy * 2; + unsigned tdaddr = tile * 16 + sy * 2; - uint8 d0 = vram[addr + 0]; - uint8 d1 = vram[addr + 1]; - unsigned xflip = attribute & 0x20 ? -7 : 0; + uint8 d0 = vram[tdaddr + 0]; + uint8 d1 = vram[tdaddr + 1]; + unsigned xflip = attribute & 0x20 ? 7 : 0; - for(unsigned x = 0; x < 8; x++) { + for(unsigned tx = 0; tx < 8; tx++) { uint8 palette = ((d0 & 0x80) >> 7) + ((d1 & 0x80) >> 6); d0 <<= 1, d1 <<= 1; if(palette == 0) continue; + palette = status.obp[(bool)(attribute & 0x10)][palette]; - output[sx + (x ^ xflip)] = (3 - palette) * 0x55; + unsigned ox = sx + (tx ^ xflip); + + if(ox <= 159) { + if((attribute & 0x80) == 1) { + if(line[ox].source == Line::Source::BG && line[ox].output > 0) continue; + } + line[ox].source = Line::Source::OBJ; + line[ox].output = palette; + } } } } @@ -121,21 +207,16 @@ void LCD::reset() { status.bg_tilemap_select = 0; status.obj_size = 0; status.obj_enable = 0; - status.bg_display = 0; + status.bg_enable = 0; status.interrupt_lyc = 0; status.interrupt_oam = 0; status.interrupt_vblank = 0; status.interrupt_hblank = 0; - status.coincidence = 0; - status.mode = 0; status.scy = 0; - status.scx = 0; - status.ly = 0; - status.lyc = 0; for(unsigned n = 0; n < 4; n++) { @@ -145,7 +226,6 @@ void LCD::reset() { } status.wy = 0; - status.wx = 0; } diff --git a/gameboy/lcd/lcd.hpp b/gameboy/lcd/lcd.hpp index d819b3b0..cd3dcc12 100755 --- a/gameboy/lcd/lcd.hpp +++ b/gameboy/lcd/lcd.hpp @@ -12,15 +12,13 @@ struct LCD : Processor, MMIO { bool bg_tilemap_select; bool obj_size; bool obj_enable; - bool bg_display; + bool bg_enable; //$ff41 STAT bool interrupt_lyc; bool interrupt_oam; bool interrupt_vblank; bool interrupt_hblank; - bool coincidence; - unsigned mode; //$ff42 SCY uint8 scy; @@ -52,12 +50,20 @@ struct LCD : Processor, MMIO { uint8 vram[8192]; uint8 oam[160]; + struct Line { + enum class Source : unsigned { None, BG, OBJ, Window } source; + uint8 output; + } line[160]; + static void Main(); void main(); void add_clocks(unsigned clocks); void scanline(); void frame(); void render(); + void render_bg(); + void render_window(); + void render_obj(); void power(); void reset(); diff --git a/gameboy/lcd/mmio/mmio.cpp b/gameboy/lcd/mmio/mmio.cpp index 230ba463..aafef096 100755 --- a/gameboy/lcd/mmio/mmio.cpp +++ b/gameboy/lcd/mmio/mmio.cpp @@ -12,16 +12,21 @@ uint8 LCD::mmio_read(uint16 addr) { | (status.bg_tilemap_select << 3) | (status.obj_size << 2) | (status.obj_enable << 1) - | (status.bg_display << 0); + | (status.bg_enable << 0); } if(addr == 0xff41) { //STAT + unsigned mode; + if(status.ly >= 144) mode = 1; //Vblank + else if(status.lx >= 320) mode = 0; //Hblank + else mode = 3; //LCD transfer + return (status.interrupt_lyc << 6) | (status.interrupt_oam << 5) | (status.interrupt_vblank << 4) | (status.interrupt_hblank << 3) - | (status.coincidence << 2) - | (status.mode << 0); + | ((status.ly == status.lyc) << 2) + | (mode << 0); } if(addr == 0xff42) { //SCY @@ -84,7 +89,7 @@ void LCD::mmio_write(uint16 addr, uint8 data) { status.bg_tilemap_select = data & 0x08; status.obj_size = data & 0x04; status.obj_enable = data & 0x02; - status.bg_display = data & 0x01; + status.bg_enable = data & 0x01; return; } diff --git a/ui/general/main-window.cpp b/ui/general/main-window.cpp index 7aa7541e..9aa81741 100755 --- a/ui/general/main-window.cpp +++ b/ui/general/main-window.cpp @@ -33,7 +33,7 @@ void MainWindow::create() { }; systemLoadCartridge.onTick = []() { - string filename = OS::fileOpen(mainWindow, "Game Boy cartridges\t*.gb", "/media/sdb1/root/gameboy_images/"); + string filename = OS::fileOpen(mainWindow, "Game Boy cartridges\t*.gb,*.gbc", "/media/sdb1/root/gameboy_images/"); if(filename != "") utility.loadCartridge(filename); };