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.
This commit is contained in:
Tim Allen 2010-12-31 16:43:47 +11:00
parent 2330ed6e8c
commit 42a9f9cfa4
13 changed files with 93 additions and 49 deletions

View File

@ -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<unsigned x> 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<unsigned x> 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<unsigned x> 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<unsigned x> 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<unsigned x> void CPU::op_add_hl_rr() {
}
template<unsigned x> void CPU::op_inc_rr() {
op_io();
r[x]++;
}
template<unsigned x> 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;

View File

@ -18,6 +18,7 @@ template<unsigned x> 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();

View File

@ -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" };

View File

@ -127,7 +127,7 @@ void CPU::initialize_opcode_table() {
opcode_table[0x7b] = &CPU::op_ld_r_r<A, E>;
opcode_table[0x7c] = &CPU::op_ld_r_r<A, H>;
opcode_table[0x7d] = &CPU::op_ld_r_r<A, L>;
opcode_table[0x7e] = &CPU::op_ld_r_hl<E>;
opcode_table[0x7e] = &CPU::op_ld_r_hl<A>;
opcode_table[0x7f] = &CPU::op_ld_r_r<A, A>;
opcode_table[0x80] = &CPU::op_add_a_r<B>;
opcode_table[0x81] = &CPU::op_add_a_r<C>;
@ -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<AF>;
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<AF>;

View File

@ -88,6 +88,7 @@ void CPU::reset() {
status.p15 = 0;
status.p14 = 0;
status.joyp = 0;
status.div = 0;

View File

@ -13,6 +13,7 @@ struct CPU : Processor, MMIO {
//$ff00 JOYP
bool p15;
bool p14;
uint8 joyp;
//$ff04 DIV
uint8 div;

View File

@ -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;
}

View File

@ -1,2 +1,3 @@
void mmio_joyp_poll();
uint8 mmio_read(uint16 addr);
void mmio_write(uint16 addr, uint8 data);

View File

@ -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";
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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();