From 42a9f9cfa4b98aead8f862ea5c570918bd4d2e32 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Fri, 31 Dec 2010 16:43:47 +1100 Subject: [PATCH] Update to release v000r05. byuu says: Fixed all of the previously mentioned problems by myself and Jonas Quinn. Fixed up JOYP and hooked up JOYP interrupts, they work on JOYP writes as well if your selection makes the low four bits change to != 0xF. Added basic sprite emulation, very very very lousy but it works for Tetris. Fixed DAA, fuck that opcode. Fixes blargg's CPU tests 1 and 11 (for some odd reason.) Only test 2 is failing, on the "EI" test. Maybe it relies on STAT interrupts? Did some other stuff. Tetris is now 100% fully playable. But that renderer is an abomination. Soooooo simplistic and missing so many edge cases. But holy shit, a fully playable commercial game in three days. I would have killed to have made that progress when I started on bsnes. --- gameboy/cpu/core/core.cpp | 21 +++++++++++++---- gameboy/cpu/core/core.hpp | 1 + gameboy/cpu/core/disassembler.cpp | 2 +- gameboy/cpu/core/table.cpp | 4 ++-- gameboy/cpu/cpu.cpp | 1 + gameboy/cpu/cpu.hpp | 1 + gameboy/cpu/mmio/mmio.cpp | 38 +++++++++++++++++-------------- gameboy/cpu/mmio/mmio.hpp | 1 + gameboy/gameboy.hpp | 2 +- gameboy/lcd/lcd.cpp | 31 +++++++++++++++++++++++-- gameboy/lcd/lcd.hpp | 4 +--- gameboy/lcd/mmio/mmio.cpp | 34 +++++++++++++-------------- ui/main.cpp | 2 +- 13 files changed, 93 insertions(+), 49 deletions(-) diff --git a/gameboy/cpu/core/core.cpp b/gameboy/cpu/core/core.cpp index b401ec18..317b936b 100755 --- a/gameboy/cpu/core/core.cpp +++ b/gameboy/cpu/core/core.cpp @@ -61,6 +61,10 @@ void CPU::op_ld_ffn_a() { op_write(0xff00 + op_read(r[PC]++), r[A]); } +void CPU::op_ld_a_ffc() { + r[A] = op_read(0xff00 + r[C]); +} + void CPU::op_ld_ffc_a() { op_write(0xff00 + r[C], r[A]); } @@ -101,6 +105,7 @@ void CPU::op_ld_nn_sp() { void CPU::op_ld_sp_hl() { r[SP] = r[HL]; + op_io(); } template void CPU::op_push_rr() { @@ -137,7 +142,7 @@ void CPU::opi_adc_a(uint8 x) { r.f.z = (uint8)rh == 0; r.f.n = 0; r.f.h = rl > 0x0f; - r.f.c = rh > 0x0f; + r.f.c = rh > 0xff; } template void CPU::op_adc_a_r() { opi_adc_a(r[x]); } @@ -214,7 +219,7 @@ void CPU::opi_cp_a(uint8 x) { r.f.z = (uint8)rh == 0; r.f.n = 1; r.f.h = rl > 0x0f; - r.f.c = rh > 0x0f; + r.f.c = rh > 0xff; } template void CPU::op_cp_a_r() { opi_cp_a(r[x]); } @@ -252,10 +257,10 @@ void CPU::op_dec_hl() { } void CPU::op_daa() { - signed a = r[A]; + uint16 a = r[A]; if(r.f.n == 0) { if(r.f.h || (a & 0x0f) > 0x09) a += 0x06; - if(r.f.c || (a & 0xff) > 0x9f) a += 0x60; + if(r.f.c || (a ) > 0x9f) a += 0x60; } else { if(r.f.h) { a -= 0x06; @@ -266,7 +271,7 @@ void CPU::op_daa() { r[A] = a; r.f.z = r[A] == 0; r.f.h = 0; - r.f.c = a & 0x100; + r.f.c |= a & 0x100; } void CPU::op_cpl() { @@ -278,6 +283,7 @@ void CPU::op_cpl() { //16-bit arithmetic commands template void CPU::op_add_hl_rr() { + op_io(); uint32 rb = (r[HL] + r[x]); uint32 rn = (r[HL] & 0xfff) + (r[x] & 0xfff); r[HL] = rb; @@ -287,14 +293,18 @@ template void CPU::op_add_hl_rr() { } template void CPU::op_inc_rr() { + op_io(); r[x]++; } template void CPU::op_dec_rr() { + op_io(); r[x]--; } void CPU::op_add_sp_n() { + op_io(); + op_io(); signed n = (int8)op_read(r[PC]++); r.f.z = 0; r.f.n = 0; @@ -304,6 +314,7 @@ void CPU::op_add_sp_n() { } void CPU::op_ld_hl_sp_n() { + op_io(); signed n = (int8)op_read(r[PC]++); r.f.z = 0; r.f.n = 0; diff --git a/gameboy/cpu/core/core.hpp b/gameboy/cpu/core/core.hpp index 16444966..85ebeb1d 100755 --- a/gameboy/cpu/core/core.hpp +++ b/gameboy/cpu/core/core.hpp @@ -18,6 +18,7 @@ template void op_ld_rr_a(); void op_ld_nn_a(); void op_ld_a_ffn(); void op_ld_ffn_a(); +void op_ld_a_ffc(); void op_ld_ffc_a(); void op_ldi_hl_a(); void op_ldi_a_hl(); diff --git a/gameboy/cpu/core/disassembler.cpp b/gameboy/cpu/core/disassembler.cpp index 9c67f444..dbcee7ee 100755 --- a/gameboy/cpu/core/disassembler.cpp +++ b/gameboy/cpu/core/disassembler.cpp @@ -270,7 +270,7 @@ string CPU::disassemble_opcode(uint16 pc) { case 0xef: return { "rst $0028" }; case 0xf0: return { "ld a,($ff", hex<2>(p0), ")" }; case 0xf1: return { "pop af" }; - case 0xf2: return { "xx" }; + case 0xf2: return { "ld a,($ff00+c)" }; case 0xf3: return { "di" }; case 0xf4: return { "xx" }; case 0xf5: return { "push af" }; diff --git a/gameboy/cpu/core/table.cpp b/gameboy/cpu/core/table.cpp index 2c885591..fa2f1112 100755 --- a/gameboy/cpu/core/table.cpp +++ b/gameboy/cpu/core/table.cpp @@ -127,7 +127,7 @@ void CPU::initialize_opcode_table() { opcode_table[0x7b] = &CPU::op_ld_r_r; opcode_table[0x7c] = &CPU::op_ld_r_r; opcode_table[0x7d] = &CPU::op_ld_r_r; - opcode_table[0x7e] = &CPU::op_ld_r_hl; + opcode_table[0x7e] = &CPU::op_ld_r_hl; opcode_table[0x7f] = &CPU::op_ld_r_r; opcode_table[0x80] = &CPU::op_add_a_r; opcode_table[0x81] = &CPU::op_add_a_r; @@ -243,7 +243,7 @@ void CPU::initialize_opcode_table() { opcode_table[0xef] = &CPU::op_rst_n<0x28>; opcode_table[0xf0] = &CPU::op_ld_a_ffn; opcode_table[0xf1] = &CPU::op_pop_rr; - opcode_table[0xf2] = &CPU::op_xx; + opcode_table[0xf2] = &CPU::op_ld_a_ffc; opcode_table[0xf3] = &CPU::op_di; opcode_table[0xf4] = &CPU::op_xx; opcode_table[0xf5] = &CPU::op_push_rr; diff --git a/gameboy/cpu/cpu.cpp b/gameboy/cpu/cpu.cpp index 5a0bc001..70b4130f 100755 --- a/gameboy/cpu/cpu.cpp +++ b/gameboy/cpu/cpu.cpp @@ -88,6 +88,7 @@ void CPU::reset() { status.p15 = 0; status.p14 = 0; + status.joyp = 0; status.div = 0; diff --git a/gameboy/cpu/cpu.hpp b/gameboy/cpu/cpu.hpp index dc66e90f..28f4d211 100755 --- a/gameboy/cpu/cpu.hpp +++ b/gameboy/cpu/cpu.hpp @@ -13,6 +13,7 @@ struct CPU : Processor, MMIO { //$ff00 JOYP bool p15; bool p14; + uint8 joyp; //$ff04 DIV uint8 div; diff --git a/gameboy/cpu/mmio/mmio.cpp b/gameboy/cpu/mmio/mmio.cpp index 4d6322b0..e18b1e5e 100755 --- a/gameboy/cpu/mmio/mmio.cpp +++ b/gameboy/cpu/mmio/mmio.cpp @@ -1,30 +1,33 @@ #ifdef CPU_CPP +void CPU::mmio_joyp_poll() { + unsigned button = 0, dpad = 0; + + button |= system.interface->input_poll((unsigned)Input::Start) << 3; + button |= system.interface->input_poll((unsigned)Input::Select) << 2; + button |= system.interface->input_poll((unsigned)Input::B) << 1; + button |= system.interface->input_poll((unsigned)Input::A) << 0; + + dpad |= system.interface->input_poll((unsigned)Input::Down) << 3; + dpad |= system.interface->input_poll((unsigned)Input::Up) << 2; + dpad |= system.interface->input_poll((unsigned)Input::Left) << 1; + dpad |= system.interface->input_poll((unsigned)Input::Right) << 0; + + 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; +} + uint8 CPU::mmio_read(uint16 addr) { if(addr >= 0xc000 && addr <= 0xdfff) return wram[addr & 0x1fff]; if(addr >= 0xe000 && addr <= 0xfdff) return wram[addr & 0x1fff]; if(addr >= 0xff80 && addr <= 0xfffe) return hram[addr & 0x7f]; if(addr == 0xff00) { //JOYP - unsigned keys = 0x0f; - - if(status.p15 == 0 && status.p14 == 1) { - keys = !system.interface->input_poll((unsigned)Input::Down) << 3; - keys |= !system.interface->input_poll((unsigned)Input::Up) << 2; - keys |= !system.interface->input_poll((unsigned)Input::Left) << 1; - keys |= !system.interface->input_poll((unsigned)Input::Right) << 0; - } - - if(status.p15 == 1 && status.p14 == 0) { - keys = !system.interface->input_poll((unsigned)Input::Start) << 3; - keys |= !system.interface->input_poll((unsigned)Input::Select) << 2; - keys |= !system.interface->input_poll((unsigned)Input::B) << 1; - keys |= !system.interface->input_poll((unsigned)Input::A) << 0; - } - return (status.p15 << 5) | (status.p14 << 4) - | (keys << 0); + | (status.joyp << 0); } if(addr == 0xff04) { //DIV @@ -71,6 +74,7 @@ void CPU::mmio_write(uint16 addr, uint8 data) { if(addr == 0xff00) { //JOYP status.p15 = data & 0x20; status.p14 = data & 0x10; + mmio_joyp_poll(); return; } diff --git a/gameboy/cpu/mmio/mmio.hpp b/gameboy/cpu/mmio/mmio.hpp index 92b8f614..bc9eb8ba 100755 --- a/gameboy/cpu/mmio/mmio.hpp +++ b/gameboy/cpu/mmio/mmio.hpp @@ -1,2 +1,3 @@ +void mmio_joyp_poll(); uint8 mmio_read(uint16 addr); void mmio_write(uint16 addr, uint8 data); diff --git a/gameboy/gameboy.hpp b/gameboy/gameboy.hpp index f51da8c0..4c131b7f 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.04"; + static const char Version[] = "000.05"; } } diff --git a/gameboy/lcd/lcd.cpp b/gameboy/lcd/lcd.cpp index 11686b29..be9818bc 100755 --- a/gameboy/lcd/lcd.cpp +++ b/gameboy/lcd/lcd.cpp @@ -38,6 +38,7 @@ void LCD::scanline() { void LCD::frame() { system.interface->video_refresh(screen); system.interface->input_poll(); + cpu.mmio_joyp_poll(); status.ly = 0; scheduler.exit(); @@ -68,6 +69,32 @@ void LCD::render() { *output++ = (3 - status.bgp[palette]) * 0x55; } } + + output = screen + status.ly * 160; + for(unsigned s = 0; s < 40; s++) { + unsigned sy = oam[(s << 2) + 0] - 9; + 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; + + unsigned addr = tile * 16 + sy * 2; + + uint8 d0 = vram[addr + 0]; + uint8 d1 = vram[addr + 1]; + unsigned xflip = attribute & 0x20 ? -7 : 0; + + for(unsigned x = 0; x < 8; x++) { + 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; + } + } } void LCD::power() { @@ -113,8 +140,8 @@ void LCD::reset() { for(unsigned n = 0; n < 4; n++) { status.bgp[n] = n; - status.obp0[n] = n; - status.obp1[n] = n; + status.obp[0][n] = n; + status.obp[1][n] = n; } status.wy = 0; diff --git a/gameboy/lcd/lcd.hpp b/gameboy/lcd/lcd.hpp index 70056c8c..d819b3b0 100755 --- a/gameboy/lcd/lcd.hpp +++ b/gameboy/lcd/lcd.hpp @@ -38,10 +38,8 @@ struct LCD : Processor, MMIO { uint8 bgp[4]; //$ff48 OBP0 - uint8 obp0[4]; - //$ff49 OBP1 - uint8 obp1[4]; + uint8 obp[2][4]; //$ff4a WY uint8 wy; diff --git a/gameboy/lcd/mmio/mmio.cpp b/gameboy/lcd/mmio/mmio.cpp index bff31ada..230ba463 100755 --- a/gameboy/lcd/mmio/mmio.cpp +++ b/gameboy/lcd/mmio/mmio.cpp @@ -48,17 +48,17 @@ uint8 LCD::mmio_read(uint16 addr) { } if(addr == 0xff48) { //OBP0 - return (status.obp0[3] << 6) - | (status.obp0[2] << 4) - | (status.obp0[1] << 2) - | (status.obp0[0] << 0); + return (status.obp[0][3] << 6) + | (status.obp[0][2] << 4) + | (status.obp[0][1] << 2) + | (status.obp[0][0] << 0); } if(addr == 0xff49) { //OBP1 - return (status.obp1[3] << 6) - | (status.obp1[2] << 4) - | (status.obp1[1] << 2) - | (status.obp1[0] << 0); + return (status.obp[1][3] << 6) + | (status.obp[1][2] << 4) + | (status.obp[1][1] << 2) + | (status.obp[1][0] << 0); } if(addr == 0xff4a) { //WY @@ -117,7 +117,7 @@ void LCD::mmio_write(uint16 addr, uint8 data) { } if(addr == 0xff46) { //DMA - //TODO + for(unsigned n = 0x00; n <= 0x9f; n++) bus.write(0xfe00 + n, bus.read((data << 8) + n)); return; } @@ -130,18 +130,18 @@ void LCD::mmio_write(uint16 addr, uint8 data) { } if(addr == 0xff48) { //OBP0 - status.obp0[3] = (data >> 6) & 3; - status.obp0[2] = (data >> 4) & 3; - status.obp0[1] = (data >> 2) & 3; - status.obp0[0] = (data >> 0) & 3; + status.obp[0][3] = (data >> 6) & 3; + status.obp[0][2] = (data >> 4) & 3; + status.obp[0][1] = (data >> 2) & 3; + status.obp[0][0] = (data >> 0) & 3; return; } if(addr == 0xff49) { //OBP1 - status.obp1[3] = (data >> 6) & 3; - status.obp1[2] = (data >> 4) & 3; - status.obp1[1] = (data >> 2) & 3; - status.obp1[0] = (data >> 0) & 3; + status.obp[1][3] = (data >> 6) & 3; + status.obp[1][2] = (data >> 4) & 3; + status.obp[1][1] = (data >> 2) & 3; + status.obp[1][0] = (data >> 0) & 3; return; } diff --git a/ui/main.cpp b/ui/main.cpp index 6f65651e..f617896d 100755 --- a/ui/main.cpp +++ b/ui/main.cpp @@ -24,7 +24,7 @@ void Application::main(int argc, char **argv) { video.driver("OpenGL"); video.set(Video::Handle, (uintptr_t)mainWindow.viewport.handle()); - video.set(Video::Synchronize, false); + video.set(Video::Synchronize, true); video.set(Video::Filter, (unsigned)0); video.init();