Changing the timings of memory writes so they’re not effectively one T-cycle late. This screws up APU’s cycle accuracy for now.

This commit is contained in:
Lior Halphon 2018-03-05 21:17:37 +02:00
parent 88a11b891f
commit 544ca2be4c
4 changed files with 66 additions and 36 deletions

View File

@ -557,6 +557,7 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
GB_STATE(gb, display, 20); GB_STATE(gb, display, 20);
GB_STATE(gb, display, 21); GB_STATE(gb, display, 21);
GB_STATE(gb, display, 22); GB_STATE(gb, display, 22);
GB_STATE(gb, display, 23);
} }
if (!(gb->io_registers[GB_IO_LCDC] & 0x80)) { if (!(gb->io_registers[GB_IO_LCDC] & 0x80)) {
@ -567,6 +568,8 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
return; return;
} }
GB_SLEEP(gb, display, 23, 1);
/* Handle the very first line 0 */ /* Handle the very first line 0 */
gb->current_line = 0; gb->current_line = 0;
gb->ly_for_comparison = 0; gb->ly_for_comparison = 0;

View File

@ -496,8 +496,8 @@ void GB_reset(GB_gameboy_t *gb)
} }
gb->io_registers[GB_IO_OBP0] = gb->io_registers[GB_IO_OBP1] = 0xFF; gb->io_registers[GB_IO_OBP0] = gb->io_registers[GB_IO_OBP1] = 0xFF;
} }
/* The serial interrupt always occur on the 0xF8th cycle of every 0x100 cycle since boot. */ /* The serial interrupt always occur on the 0xF7th cycle of every 0x100 cycle since boot. */
gb->serial_cycles = 0x100 - 0xF8; gb->serial_cycles = 0x100-0xF7;
gb->io_registers[GB_IO_SC] = 0x7E; gb->io_registers[GB_IO_SC] = 0x7E;
gb->magic = (uintptr_t)'SAME'; gb->magic = (uintptr_t)'SAME';
} }

View File

@ -178,7 +178,15 @@ void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles)
gb->debugger_ticks += cycles; gb->debugger_ticks += cycles;
cycles <<= !gb->cgb_double_speed; if (!gb->cgb_double_speed) {
cycles <<= 1;
if ((cycles & 6) == 2) {
cycles--;
}
else if ((cycles & 6) == 6) {
cycles++;
}
}
// Not affected by speed boost // Not affected by speed boost
gb->hdma_cycles += cycles; gb->hdma_cycles += cycles;

View File

@ -5,6 +5,24 @@
typedef void GB_opcode_t(GB_gameboy_t *gb, uint8_t opcode); typedef void GB_opcode_t(GB_gameboy_t *gb, uint8_t opcode);
/*
About memroy timings:
Each M-cycle consists of 4 T-cycles. Every time the CPU accesses the memory it happens on the 1st T-cycle of an
M-cycle. During that cycle, other things may happen, such the PPU drawing to the screen. Since we can't really run
things in parallel, we run non-CPU "activities" serially using advnace_cycles(...). This is normally not a problem,
unless two entities (e.g. both the CPU and the PPU) read the same register at the same time (e.g. BGP). Since memory
accesses happen for an enitre T-cycle, if someone reads a value while someone else changes it during in the same
T-cycle, the read will return the new value. To correctly emulate this, a memory access T-cycle looks like this:
- Perform memory write (If needed)
- Run everything else
- Perform memory read (If needed)
This is equivalent to running the memory write 1 T-cycle before the memory read.
*/
static void ill(GB_gameboy_t *gb, uint8_t opcode) static void ill(GB_gameboy_t *gb, uint8_t opcode)
{ {
GB_log(gb, "Illegal Opcode. Halting.\n"); GB_log(gb, "Illegal Opcode. Halting.\n");
@ -63,10 +81,10 @@ static void ld_rr_d16(GB_gameboy_t *gb, uint8_t opcode)
static void ld_drr_a(GB_gameboy_t *gb, uint8_t opcode) static void ld_drr_a(GB_gameboy_t *gb, uint8_t opcode)
{ {
uint8_t register_id; uint8_t register_id;
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 3);
register_id = (opcode >> 4) + 1; register_id = (opcode >> 4) + 1;
GB_write_memory(gb, gb->registers[register_id], gb->registers[GB_REGISTER_AF] >> 8); GB_write_memory(gb, gb->registers[register_id], gb->registers[GB_REGISTER_AF] >> 8);
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 5);
} }
static void inc_rr(GB_gameboy_t *gb, uint8_t opcode) static void inc_rr(GB_gameboy_t *gb, uint8_t opcode)
@ -155,11 +173,11 @@ static void ld_da16_sp(GB_gameboy_t *gb, uint8_t opcode)
addr = GB_read_memory(gb, gb->pc++); addr = GB_read_memory(gb, gb->pc++);
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 4);
addr |= GB_read_memory(gb, gb->pc++) << 8; addr |= GB_read_memory(gb, gb->pc++) << 8;
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 3);
GB_write_memory(gb, addr, gb->registers[GB_REGISTER_SP] & 0xFF); GB_write_memory(gb, addr, gb->registers[GB_REGISTER_SP] & 0xFF);
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 4);
GB_write_memory(gb, addr+1, gb->registers[GB_REGISTER_SP] >> 8); GB_write_memory(gb, addr+1, gb->registers[GB_REGISTER_SP] >> 8);
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 5);
} }
static void add_hl_rr(GB_gameboy_t *gb, uint8_t opcode) static void add_hl_rr(GB_gameboy_t *gb, uint8_t opcode)
@ -392,16 +410,16 @@ static void ccf(GB_gameboy_t *gb, uint8_t opcode)
static void ld_dhli_a(GB_gameboy_t *gb, uint8_t opcode) static void ld_dhli_a(GB_gameboy_t *gb, uint8_t opcode)
{ {
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 3);
GB_write_memory(gb, gb->registers[GB_REGISTER_HL]++, gb->registers[GB_REGISTER_AF] >> 8); GB_write_memory(gb, gb->registers[GB_REGISTER_HL]++, gb->registers[GB_REGISTER_AF] >> 8);
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 5);
} }
static void ld_dhld_a(GB_gameboy_t *gb, uint8_t opcode) static void ld_dhld_a(GB_gameboy_t *gb, uint8_t opcode)
{ {
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 3);
GB_write_memory(gb, gb->registers[GB_REGISTER_HL]--, gb->registers[GB_REGISTER_AF] >> 8); GB_write_memory(gb, gb->registers[GB_REGISTER_HL]--, gb->registers[GB_REGISTER_AF] >> 8);
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 5);
} }
static void ld_a_dhli(GB_gameboy_t *gb, uint8_t opcode) static void ld_a_dhli(GB_gameboy_t *gb, uint8_t opcode)
@ -425,9 +443,9 @@ static void inc_dhl(GB_gameboy_t *gb, uint8_t opcode)
uint8_t value; uint8_t value;
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 4);
value = GB_read_memory(gb, gb->registers[GB_REGISTER_HL]) + 1; value = GB_read_memory(gb, gb->registers[GB_REGISTER_HL]) + 1;
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 3);
GB_write_memory(gb, gb->registers[GB_REGISTER_HL], value); GB_write_memory(gb, gb->registers[GB_REGISTER_HL], value);
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 5);
gb->registers[GB_REGISTER_AF] &= ~(GB_SUBSTRACT_FLAG | GB_ZERO_FLAG | GB_HALF_CARRY_FLAG); gb->registers[GB_REGISTER_AF] &= ~(GB_SUBSTRACT_FLAG | GB_ZERO_FLAG | GB_HALF_CARRY_FLAG);
if ((value & 0x0F) == 0) { if ((value & 0x0F) == 0) {
@ -444,9 +462,9 @@ static void dec_dhl(GB_gameboy_t *gb, uint8_t opcode)
uint8_t value; uint8_t value;
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 4);
value = GB_read_memory(gb, gb->registers[GB_REGISTER_HL]) - 1; value = GB_read_memory(gb, gb->registers[GB_REGISTER_HL]) - 1;
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 3);
GB_write_memory(gb, gb->registers[GB_REGISTER_HL], value); GB_write_memory(gb, gb->registers[GB_REGISTER_HL], value);
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 5);
gb->registers[GB_REGISTER_AF] &= ~( GB_ZERO_FLAG | GB_HALF_CARRY_FLAG); gb->registers[GB_REGISTER_AF] &= ~( GB_ZERO_FLAG | GB_HALF_CARRY_FLAG);
gb->registers[GB_REGISTER_AF] |= GB_SUBSTRACT_FLAG; gb->registers[GB_REGISTER_AF] |= GB_SUBSTRACT_FLAG;
@ -463,9 +481,9 @@ static void ld_dhl_d8(GB_gameboy_t *gb, uint8_t opcode)
{ {
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 4);
uint8_t data = GB_read_memory(gb, gb->pc++); uint8_t data = GB_read_memory(gb, gb->pc++);
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 3);
GB_write_memory(gb, gb->registers[GB_REGISTER_HL], data); GB_write_memory(gb, gb->registers[GB_REGISTER_HL], data);
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 5);
} }
uint8_t get_src_value(GB_gameboy_t *gb, uint8_t opcode) uint8_t get_src_value(GB_gameboy_t *gb, uint8_t opcode)
@ -501,8 +519,9 @@ static void set_src_value(GB_gameboy_t *gb, uint8_t opcode, uint8_t value)
gb->registers[GB_REGISTER_AF] |= value << 8; gb->registers[GB_REGISTER_AF] |= value << 8;
} }
else { else {
GB_advance_cycles(gb, 3);
GB_write_memory(gb, gb->registers[GB_REGISTER_HL], value); GB_write_memory(gb, gb->registers[GB_REGISTER_HL], value);
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 1);
} }
} }
else { else {
@ -540,9 +559,9 @@ GB_advance_cycles(gb, 4);\
#define LD_DHL_Y(y) \ #define LD_DHL_Y(y) \
static void ld_##dhl##_##y(GB_gameboy_t *gb, uint8_t opcode) \ static void ld_##dhl##_##y(GB_gameboy_t *gb, uint8_t opcode) \
{ \ { \
GB_advance_cycles(gb, 4); \ GB_advance_cycles(gb, 3); \
GB_write_memory(gb, gb->registers[GB_REGISTER_HL], gb->y); \ GB_write_memory(gb, gb->registers[GB_REGISTER_HL], gb->y); \
GB_advance_cycles(gb, 4);\ GB_advance_cycles(gb, 5);\
} }
LD_X_Y(b,c) LD_X_Y(b,d) LD_X_Y(b,e) LD_X_Y(b,h) LD_X_Y(b,l) LD_X_DHL(b) LD_X_Y(b,a) LD_X_Y(b,c) LD_X_Y(b,d) LD_X_Y(b,e) LD_X_Y(b,h) LD_X_Y(b,l) LD_X_DHL(b) LD_X_Y(b,a)
@ -762,11 +781,11 @@ static void call_cc_a16(GB_gameboy_t *gb, uint8_t opcode)
uint16_t addr = GB_read_memory(gb, gb->pc++); uint16_t addr = GB_read_memory(gb, gb->pc++);
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 4);
addr |= (GB_read_memory(gb, gb->pc++) << 8); addr |= (GB_read_memory(gb, gb->pc++) << 8);
GB_advance_cycles(gb, 8); GB_advance_cycles(gb, 7);
GB_write_memory(gb, gb->registers[GB_REGISTER_SP] + 1, (gb->pc) >> 8); GB_write_memory(gb, gb->registers[GB_REGISTER_SP] + 1, (gb->pc) >> 8);
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 4);
GB_write_memory(gb, gb->registers[GB_REGISTER_SP], (gb->pc) & 0xFF); GB_write_memory(gb, gb->registers[GB_REGISTER_SP], (gb->pc) & 0xFF);
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 5);
gb->pc = addr; gb->pc = addr;
GB_debugger_call_hook(gb, call_addr); GB_debugger_call_hook(gb, call_addr);
@ -780,13 +799,13 @@ static void call_cc_a16(GB_gameboy_t *gb, uint8_t opcode)
static void push_rr(GB_gameboy_t *gb, uint8_t opcode) static void push_rr(GB_gameboy_t *gb, uint8_t opcode)
{ {
uint8_t register_id; uint8_t register_id;
GB_advance_cycles(gb, 8); GB_advance_cycles(gb, 7);
register_id = ((opcode >> 4) + 1) & 3; register_id = ((opcode >> 4) + 1) & 3;
gb->registers[GB_REGISTER_SP] -= 2; gb->registers[GB_REGISTER_SP] -= 2;
GB_write_memory(gb, gb->registers[GB_REGISTER_SP] + 1, (gb->registers[register_id]) >> 8); GB_write_memory(gb, gb->registers[GB_REGISTER_SP] + 1, (gb->registers[register_id]) >> 8);
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 4);
GB_write_memory(gb, gb->registers[GB_REGISTER_SP], (gb->registers[register_id]) & 0xFF); GB_write_memory(gb, gb->registers[GB_REGISTER_SP], (gb->registers[register_id]) & 0xFF);
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 5);
} }
static void add_a_d8(GB_gameboy_t *gb, uint8_t opcode) static void add_a_d8(GB_gameboy_t *gb, uint8_t opcode)
@ -931,12 +950,12 @@ static void cp_a_d8(GB_gameboy_t *gb, uint8_t opcode)
static void rst(GB_gameboy_t *gb, uint8_t opcode) static void rst(GB_gameboy_t *gb, uint8_t opcode)
{ {
uint16_t call_addr = gb->pc - 1; uint16_t call_addr = gb->pc - 1;
GB_advance_cycles(gb, 8); GB_advance_cycles(gb, 7);
gb->registers[GB_REGISTER_SP] -= 2; gb->registers[GB_REGISTER_SP] -= 2;
GB_write_memory(gb, gb->registers[GB_REGISTER_SP] + 1, (gb->pc) >> 8); GB_write_memory(gb, gb->registers[GB_REGISTER_SP] + 1, (gb->pc) >> 8);
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 4);
GB_write_memory(gb, gb->registers[GB_REGISTER_SP], (gb->pc) & 0xFF); GB_write_memory(gb, gb->registers[GB_REGISTER_SP], (gb->pc) & 0xFF);
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 5);
gb->pc = opcode ^ 0xC7; gb->pc = opcode ^ 0xC7;
GB_debugger_call_hook(gb, call_addr); GB_debugger_call_hook(gb, call_addr);
} }
@ -966,11 +985,11 @@ static void call_a16(GB_gameboy_t *gb, uint8_t opcode)
uint16_t addr = GB_read_memory(gb, gb->pc++); uint16_t addr = GB_read_memory(gb, gb->pc++);
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 4);
addr |= (GB_read_memory(gb, gb->pc++) << 8); addr |= (GB_read_memory(gb, gb->pc++) << 8);
GB_advance_cycles(gb, 8); GB_advance_cycles(gb, 7);
GB_write_memory(gb, gb->registers[GB_REGISTER_SP] + 1, (gb->pc) >> 8); GB_write_memory(gb, gb->registers[GB_REGISTER_SP] + 1, (gb->pc) >> 8);
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 4);
GB_write_memory(gb, gb->registers[GB_REGISTER_SP], (gb->pc) & 0xFF); GB_write_memory(gb, gb->registers[GB_REGISTER_SP], (gb->pc) & 0xFF);
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 5);
gb->pc = addr; gb->pc = addr;
GB_debugger_call_hook(gb, call_addr); GB_debugger_call_hook(gb, call_addr);
} }
@ -979,9 +998,9 @@ static void ld_da8_a(GB_gameboy_t *gb, uint8_t opcode)
{ {
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 4);
uint8_t temp = GB_read_memory(gb, gb->pc++); uint8_t temp = GB_read_memory(gb, gb->pc++);
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 3);
GB_write_memory(gb, 0xFF00 + temp, gb->registers[GB_REGISTER_AF] >> 8); GB_write_memory(gb, 0xFF00 + temp, gb->registers[GB_REGISTER_AF] >> 8);
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 5);
} }
static void ld_a_da8(GB_gameboy_t *gb, uint8_t opcode) static void ld_a_da8(GB_gameboy_t *gb, uint8_t opcode)
@ -996,9 +1015,9 @@ static void ld_a_da8(GB_gameboy_t *gb, uint8_t opcode)
static void ld_dc_a(GB_gameboy_t *gb, uint8_t opcode) static void ld_dc_a(GB_gameboy_t *gb, uint8_t opcode)
{ {
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 3);
GB_write_memory(gb, 0xFF00 + (gb->registers[GB_REGISTER_BC] & 0xFF), gb->registers[GB_REGISTER_AF] >> 8); GB_write_memory(gb, 0xFF00 + (gb->registers[GB_REGISTER_BC] & 0xFF), gb->registers[GB_REGISTER_AF] >> 8);
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 5);
} }
static void ld_a_dc(GB_gameboy_t *gb, uint8_t opcode) static void ld_a_dc(GB_gameboy_t *gb, uint8_t opcode)
@ -1043,9 +1062,9 @@ static void ld_da16_a(GB_gameboy_t *gb, uint8_t opcode)
addr = GB_read_memory(gb, gb->pc++); addr = GB_read_memory(gb, gb->pc++);
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 4);
addr |= GB_read_memory(gb, gb->pc++) << 8; addr |= GB_read_memory(gb, gb->pc++) << 8;
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 3);
GB_write_memory(gb, addr, gb->registers[GB_REGISTER_AF] >> 8); GB_write_memory(gb, addr, gb->registers[GB_REGISTER_AF] >> 8);
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 5);
} }
static void ld_a_da16(GB_gameboy_t *gb, uint8_t opcode) static void ld_a_da16(GB_gameboy_t *gb, uint8_t opcode)
@ -1372,7 +1391,7 @@ void GB_cpu_run(GB_gameboy_t *gb)
else if (effecitve_ime && interrupt_queue) { else if (effecitve_ime && interrupt_queue) {
gb->halted = false; gb->halted = false;
uint16_t call_addr = gb->pc - 1; uint16_t call_addr = gb->pc - 1;
GB_advance_cycles(gb, 12); GB_advance_cycles(gb, 11);
gb->registers[GB_REGISTER_SP] -= 2; gb->registers[GB_REGISTER_SP] -= 2;
GB_write_memory(gb, gb->registers[GB_REGISTER_SP] + 1, (gb->pc) >> 8); GB_write_memory(gb, gb->registers[GB_REGISTER_SP] + 1, (gb->pc) >> 8);
interrupt_queue = gb->interrupt_enable; interrupt_queue = gb->interrupt_enable;
@ -1380,7 +1399,7 @@ void GB_cpu_run(GB_gameboy_t *gb)
GB_write_memory(gb, gb->registers[GB_REGISTER_SP], (gb->pc) & 0xFF); GB_write_memory(gb, gb->registers[GB_REGISTER_SP], (gb->pc) & 0xFF);
interrupt_queue &= (gb->io_registers[GB_IO_IF]) & 0x1F; interrupt_queue &= (gb->io_registers[GB_IO_IF]) & 0x1F;
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 5);
if (interrupt_queue) { if (interrupt_queue) {
uint8_t interrupt_bit = 0; uint8_t interrupt_bit = 0;
while (!(interrupt_queue & 1)) { while (!(interrupt_queue & 1)) {