mirror of https://github.com/bsnes-emu/bsnes.git
Mostly complete emulation of the OAM bug. Passes oam_bug-2.
This commit is contained in:
parent
9093f22293
commit
4986930511
|
@ -288,7 +288,7 @@ void GB_lcd_off(GB_gameboy_t *gb)
|
||||||
gb->current_line = 0;
|
gb->current_line = 0;
|
||||||
gb->ly_for_comparison = 0;
|
gb->ly_for_comparison = 0;
|
||||||
|
|
||||||
gb->oam_search_index = 0;
|
gb->accessed_oam_row = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void add_object_from_index(GB_gameboy_t *gb, unsigned index)
|
static void add_object_from_index(GB_gameboy_t *gb, unsigned index)
|
||||||
|
@ -478,6 +478,7 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
|
||||||
/* Lines 0 - 143 */
|
/* Lines 0 - 143 */
|
||||||
for (; gb->current_line < LINES; gb->current_line++) {
|
for (; gb->current_line < LINES; gb->current_line++) {
|
||||||
gb->oam_write_blocked = gb->is_cgb;
|
gb->oam_write_blocked = gb->is_cgb;
|
||||||
|
gb->accessed_oam_row = 0;
|
||||||
GB_SLEEP(gb, display, 6, 3);
|
GB_SLEEP(gb, display, 6, 3);
|
||||||
gb->io_registers[GB_IO_LY] = gb->current_line;
|
gb->io_registers[GB_IO_LY] = gb->current_line;
|
||||||
gb->oam_read_blocked = true;
|
gb->oam_read_blocked = true;
|
||||||
|
@ -504,8 +505,15 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
|
||||||
gb->n_visible_objs = 0;
|
gb->n_visible_objs = 0;
|
||||||
|
|
||||||
for (gb->oam_search_index = 0; gb->oam_search_index < 40; gb->oam_search_index++) {
|
for (gb->oam_search_index = 0; gb->oam_search_index < 40; gb->oam_search_index++) {
|
||||||
add_object_from_index(gb, gb->oam_search_index);
|
if (gb->is_cgb) {
|
||||||
|
add_object_from_index(gb, gb->oam_search_index);
|
||||||
|
/* The CGB does not care about the accessed OAM row as there's no OAM bug*/
|
||||||
|
}
|
||||||
GB_SLEEP(gb, display, 8, 2);
|
GB_SLEEP(gb, display, 8, 2);
|
||||||
|
if (!gb->is_cgb) {
|
||||||
|
add_object_from_index(gb, gb->oam_search_index);
|
||||||
|
gb->accessed_oam_row = (gb->oam_search_index & ~1) * 4 + 8;
|
||||||
|
}
|
||||||
if (gb->oam_search_index == 37) {
|
if (gb->oam_search_index == 37) {
|
||||||
gb->vram_read_blocked = !gb->is_cgb;
|
gb->vram_read_blocked = !gb->is_cgb;
|
||||||
gb->vram_write_blocked = false;
|
gb->vram_write_blocked = false;
|
||||||
|
@ -513,6 +521,7 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
|
||||||
GB_STAT_update(gb);
|
GB_STAT_update(gb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
gb->accessed_oam_row = -1;
|
||||||
|
|
||||||
gb->io_registers[GB_IO_STAT] &= ~3;
|
gb->io_registers[GB_IO_STAT] &= ~3;
|
||||||
gb->io_registers[GB_IO_STAT] |= 3;
|
gb->io_registers[GB_IO_STAT] |= 3;
|
||||||
|
|
|
@ -500,6 +500,8 @@ void GB_reset(GB_gameboy_t *gb)
|
||||||
|
|
||||||
/* These are not deterministic, but 00 (CGB) and FF (DMG) are the most common initial values by far */
|
/* These are not deterministic, but 00 (CGB) and FF (DMG) are the most common initial values by far */
|
||||||
gb->io_registers[GB_IO_DMA] = gb->io_registers[GB_IO_OBP0] = gb->io_registers[GB_IO_OBP1] = gb->is_cgb? 0x00 : 0xFF;
|
gb->io_registers[GB_IO_DMA] = gb->io_registers[GB_IO_OBP0] = gb->io_registers[GB_IO_OBP1] = gb->is_cgb? 0x00 : 0xFF;
|
||||||
|
|
||||||
|
gb->accessed_oam_row = -1;
|
||||||
|
|
||||||
gb->magic = (uintptr_t)'SAME';
|
gb->magic = (uintptr_t)'SAME';
|
||||||
}
|
}
|
||||||
|
|
|
@ -427,6 +427,7 @@ struct GB_gameboy_internal_s {
|
||||||
uint8_t n_visible_objs;
|
uint8_t n_visible_objs;
|
||||||
bool fetching_objects;
|
bool fetching_objects;
|
||||||
uint8_t oam_search_index;
|
uint8_t oam_search_index;
|
||||||
|
uint8_t accessed_oam_row;
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Unsaved data. This includes all pointers, as well as everything that shouldn't be on a save state */
|
/* Unsaved data. This includes all pointers, as well as everything that shouldn't be on a save state */
|
||||||
|
|
189
Core/memory.c
189
Core/memory.c
|
@ -29,6 +29,84 @@ static GB_bus_t bus_for_addr(GB_gameboy_t *gb, uint16_t addr)
|
||||||
return GB_BUS_INTERNAL;
|
return GB_BUS_INTERNAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint8_t bitwise_glitch(uint8_t a, uint8_t b, uint8_t c)
|
||||||
|
{
|
||||||
|
return ((a ^ c) & (b ^ c)) ^ c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t bitwise_glitch_read(uint8_t a, uint8_t b, uint8_t c)
|
||||||
|
{
|
||||||
|
return b | (a & c);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t bitwise_glitch_pop(uint8_t a, uint8_t b, uint8_t c, uint8_t d)
|
||||||
|
{
|
||||||
|
return (b & a) | (b & c) | (b & d) | (a & c & d);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GB_trigger_oam_bug(GB_gameboy_t *gb, uint16_t address)
|
||||||
|
{
|
||||||
|
if (gb->is_cgb) return;
|
||||||
|
|
||||||
|
if (address >= 0xFE00 && address < 0xFF00) {
|
||||||
|
if (gb->accessed_oam_row != 0xff && gb->accessed_oam_row >= 8) {
|
||||||
|
gb->oam[gb->accessed_oam_row] = bitwise_glitch(gb->oam[gb->accessed_oam_row],
|
||||||
|
gb->oam[gb->accessed_oam_row - 8],
|
||||||
|
gb->oam[gb->accessed_oam_row - 4]);
|
||||||
|
gb->oam[gb->accessed_oam_row + 1] = bitwise_glitch(gb->oam[gb->accessed_oam_row + 1],
|
||||||
|
gb->oam[gb->accessed_oam_row - 7],
|
||||||
|
gb->oam[gb->accessed_oam_row - 3]);
|
||||||
|
for (unsigned i = 2; i < 8; i++) {
|
||||||
|
gb->oam[gb->accessed_oam_row + i] = gb->oam[gb->accessed_oam_row - 8 + i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GB_trigger_oam_bug_read(GB_gameboy_t *gb, uint16_t address)
|
||||||
|
{
|
||||||
|
if (gb->is_cgb) return;
|
||||||
|
|
||||||
|
if (address >= 0xFE00 && address < 0xFF00) {
|
||||||
|
if (gb->accessed_oam_row != 0xff && gb->accessed_oam_row >= 8) {
|
||||||
|
gb->oam[gb->accessed_oam_row - 8] =
|
||||||
|
gb->oam[gb->accessed_oam_row] = bitwise_glitch_read(gb->oam[gb->accessed_oam_row],
|
||||||
|
gb->oam[gb->accessed_oam_row - 8],
|
||||||
|
gb->oam[gb->accessed_oam_row - 4]);
|
||||||
|
gb->oam[gb->accessed_oam_row - 7] =
|
||||||
|
gb->oam[gb->accessed_oam_row + 1] = bitwise_glitch_read(gb->oam[gb->accessed_oam_row + 1],
|
||||||
|
gb->oam[gb->accessed_oam_row - 7],
|
||||||
|
gb->oam[gb->accessed_oam_row - 3]);
|
||||||
|
for (unsigned i = 2; i < 8; i++) {
|
||||||
|
gb->oam[gb->accessed_oam_row + i] = gb->oam[gb->accessed_oam_row - 8 + i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GB_trigger_oam_bug_read_increase(GB_gameboy_t *gb, uint16_t address)
|
||||||
|
{
|
||||||
|
if (gb->is_cgb) return;
|
||||||
|
|
||||||
|
if (address >= 0xFE00 && address < 0xFF00) {
|
||||||
|
if (gb->accessed_oam_row != 0xff && gb->accessed_oam_row >= 0x20 && gb->accessed_oam_row < 0x98) {
|
||||||
|
gb->oam[gb->accessed_oam_row - 0x8] = bitwise_glitch_pop(gb->oam[gb->accessed_oam_row - 0x10],
|
||||||
|
gb->oam[gb->accessed_oam_row - 0x08],
|
||||||
|
gb->oam[gb->accessed_oam_row ],
|
||||||
|
gb->oam[gb->accessed_oam_row - 0x04]
|
||||||
|
);
|
||||||
|
gb->oam[gb->accessed_oam_row - 0x7] = bitwise_glitch_pop(gb->oam[gb->accessed_oam_row - 0x0f],
|
||||||
|
gb->oam[gb->accessed_oam_row - 0x07],
|
||||||
|
gb->oam[gb->accessed_oam_row + 0x01],
|
||||||
|
gb->oam[gb->accessed_oam_row - 0x03]
|
||||||
|
);
|
||||||
|
for (unsigned i = 0; i < 8; i++) {
|
||||||
|
gb->oam[gb->accessed_oam_row + i] = gb->oam[gb->accessed_oam_row - 0x10 + i] = gb->oam[gb->accessed_oam_row - 0x08 + i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool is_addr_in_dma_use(GB_gameboy_t *gb, uint16_t addr)
|
static bool is_addr_in_dma_use(GB_gameboy_t *gb, uint16_t addr)
|
||||||
{
|
{
|
||||||
if (!gb->dma_steps_left || (gb->dma_cycles < 0 && !gb->is_dma_restarting) || addr >= 0xFE00) return false;
|
if (!gb->dma_steps_left || (gb->dma_cycles < 0 && !gb->is_dma_restarting) || addr >= 0xFE00) return false;
|
||||||
|
@ -118,14 +196,54 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr)
|
||||||
return gb->ram[addr & 0x0FFF];
|
return gb->ram[addr & 0x0FFF];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addr < 0xFEA0) {
|
|
||||||
if (gb->oam_read_blocked || (gb->dma_steps_left && (gb->dma_cycles > 0 || gb->is_dma_restarting))) {
|
|
||||||
return 0xFF;
|
|
||||||
}
|
|
||||||
return gb->oam[addr & 0xFF];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (addr < 0xFF00) {
|
if (addr < 0xFF00) {
|
||||||
|
if (gb->oam_write_blocked) {
|
||||||
|
GB_trigger_oam_bug_read(gb, addr);
|
||||||
|
return 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((gb->dma_steps_left && (gb->dma_cycles > 0 || gb->is_dma_restarting))) {
|
||||||
|
/* Todo: Does reading from OAM during DMA causes the OAM bug? */
|
||||||
|
return 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gb->oam_read_blocked) {
|
||||||
|
if (!gb->is_cgb) {
|
||||||
|
if (addr < 0xFEA0) {
|
||||||
|
if (gb->accessed_oam_row == 0) {
|
||||||
|
gb->oam[(addr & 0xf8)] =
|
||||||
|
gb->oam[0] = bitwise_glitch_read(gb->oam[0],
|
||||||
|
gb->oam[(addr & 0xf8)],
|
||||||
|
gb->oam[(addr & 0xfe)]);
|
||||||
|
gb->oam[(addr & 0xf8) + 1] =
|
||||||
|
gb->oam[1] = bitwise_glitch_read(gb->oam[1],
|
||||||
|
gb->oam[(addr & 0xf8) + 1],
|
||||||
|
gb->oam[(addr & 0xfe) | 1]);
|
||||||
|
for (unsigned i = 2; i < 8; i++) {
|
||||||
|
gb->oam[i] = gb->oam[(addr & 0xf8) + i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (gb->accessed_oam_row == 0xa0) {
|
||||||
|
gb->oam[0x9e] = bitwise_glitch_read(gb->oam[0x9c],
|
||||||
|
gb->oam[0x9e],
|
||||||
|
gb->oam[(addr & 0xf8) | 6]);
|
||||||
|
gb->oam[0x9f] = bitwise_glitch_read(gb->oam[0x9d],
|
||||||
|
gb->oam[0x9f],
|
||||||
|
gb->oam[(addr & 0xf8) | 7]);
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < 8; i++) {
|
||||||
|
gb->oam[(addr & 0xf8) + i] = gb->oam[0x98 + i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addr < 0xFEA0) {
|
||||||
|
return gb->oam[addr & 0xFF];
|
||||||
|
}
|
||||||
|
|
||||||
/* Unusable. CGB results are verified, but DMG results were tested on a SGB2 */
|
/* Unusable. CGB results are verified, but DMG results were tested on a SGB2 */
|
||||||
/* Also, writes to this area are not emulated */
|
/* Also, writes to this area are not emulated */
|
||||||
if ((gb->io_registers[GB_IO_STAT] & 0x3) >= 2) { /* Seems to be disabled in Modes 2 and 3 */
|
if ((gb->io_registers[GB_IO_STAT] & 0x3) >= 2) { /* Seems to be disabled in Modes 2 and 3 */
|
||||||
|
@ -134,6 +252,10 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr)
|
||||||
if (gb->is_cgb) {
|
if (gb->is_cgb) {
|
||||||
return (addr & 0xF0) | ((addr >> 4) & 0xF);
|
return (addr & 0xF0) | ((addr >> 4) & 0xF);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addr < 0xFF00) {
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -398,16 +520,53 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addr < 0xFEA0) {
|
if (addr < 0xFF00) {
|
||||||
if (gb->oam_write_blocked|| (gb->dma_steps_left && (gb->dma_cycles > 0 || gb->is_dma_restarting))) {
|
if (gb->oam_write_blocked) {
|
||||||
|
GB_trigger_oam_bug(gb, addr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
gb->oam[addr & 0xFF] = value;
|
|
||||||
return;
|
if ((gb->dma_steps_left && (gb->dma_cycles > 0 || gb->is_dma_restarting))) {
|
||||||
}
|
/* Todo: Does writing to OAM during DMA causes the OAM bug? */
|
||||||
|
return;
|
||||||
if (addr < 0xFF00) {
|
}
|
||||||
GB_log(gb, "Wrote %02x to %04x (Unused)\n", value, addr);
|
|
||||||
|
if (gb->is_cgb) {
|
||||||
|
if (addr < 0xFEA0) {
|
||||||
|
gb->oam[addr & 0xFF] = value;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addr < 0xFEA0) {
|
||||||
|
if (gb->accessed_oam_row == 0xa0) {
|
||||||
|
for (unsigned i = 0; i < 8; i++) {
|
||||||
|
if ((i & 6) != (addr & 6)) {
|
||||||
|
gb->oam[(addr & 0xf8) + i] = gb->oam[0x98 + i];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
gb->oam[(addr & 0xf8) + i] = bitwise_glitch(gb->oam[(addr & 0xf8) + i], gb->oam[0x9c], gb->oam[0x98 + i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gb->oam[addr & 0xFF] = value;
|
||||||
|
|
||||||
|
if (gb->accessed_oam_row == 0) {
|
||||||
|
gb->oam[0] = bitwise_glitch(gb->oam[0],
|
||||||
|
gb->oam[(addr & 0xf8)],
|
||||||
|
gb->oam[(addr & 0xfe)]);
|
||||||
|
gb->oam[1] = bitwise_glitch(gb->oam[1],
|
||||||
|
gb->oam[(addr & 0xf8) + 1],
|
||||||
|
gb->oam[(addr & 0xfe) | 1]);
|
||||||
|
for (unsigned i = 2; i < 8; i++) {
|
||||||
|
gb->oam[i] = gb->oam[(addr & 0xf8) + i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (gb->accessed_oam_row == 0) {
|
||||||
|
gb->oam[addr & 0x7] = value;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@ void GB_write_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value);
|
||||||
#ifdef GB_INTERNAL
|
#ifdef GB_INTERNAL
|
||||||
void GB_dma_run(GB_gameboy_t *gb);
|
void GB_dma_run(GB_gameboy_t *gb);
|
||||||
void GB_hdma_run(GB_gameboy_t *gb);
|
void GB_hdma_run(GB_gameboy_t *gb);
|
||||||
|
void GB_trigger_oam_bug(GB_gameboy_t *gb, uint16_t address);
|
||||||
|
void GB_trigger_oam_bug_read_increase(GB_gameboy_t *gb, uint16_t address);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* memory_h */
|
#endif /* memory_h */
|
||||||
|
|
|
@ -22,31 +22,6 @@ typedef void GB_opcode_t(GB_gameboy_t *gb, uint8_t opcode);
|
||||||
This is equivalent to running the memory write 1 T-cycle before the memory read.
|
This is equivalent to running the memory write 1 T-cycle before the memory read.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static uint8_t bitwise_glitch(uint8_t a, uint8_t b, uint8_t c)
|
|
||||||
{
|
|
||||||
return ((a ^ c) & (b ^ c)) ^ c;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void trigger_oam_bug(GB_gameboy_t *gb, uint8_t register_id)
|
|
||||||
{
|
|
||||||
if (gb->is_cgb) return;
|
|
||||||
|
|
||||||
if (gb->registers[register_id] >= 0xFE00 && gb->registers[register_id] < 0xFF00) {
|
|
||||||
if (gb->oam_search_index < 38 && gb->oam_search_index > 0) {
|
|
||||||
gb->oam[gb->oam_search_index * 4 + 4] = bitwise_glitch(gb->oam[gb->oam_search_index * 4 + 4],
|
|
||||||
gb->oam[gb->oam_search_index * 4 - 4],
|
|
||||||
gb->oam[gb->oam_search_index * 4]);
|
|
||||||
gb->oam[gb->oam_search_index * 4 + 5] = bitwise_glitch(gb->oam[gb->oam_search_index * 4 + 5],
|
|
||||||
gb->oam[gb->oam_search_index * 4 - 3],
|
|
||||||
gb->oam[gb->oam_search_index * 4 + 1]);
|
|
||||||
for (unsigned i = 2; i < 8; i++) {
|
|
||||||
gb->oam[gb->oam_search_index * 4 + 4 + i] = gb->oam[gb->oam_search_index * 4 - 4 + i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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");
|
||||||
|
@ -115,7 +90,7 @@ static void inc_rr(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
{
|
{
|
||||||
uint8_t register_id = (opcode >> 4) + 1;
|
uint8_t register_id = (opcode >> 4) + 1;
|
||||||
GB_advance_cycles(gb, 4);
|
GB_advance_cycles(gb, 4);
|
||||||
trigger_oam_bug(gb, register_id); /* Todo: test T-cycle timing */
|
GB_trigger_oam_bug(gb, gb->registers[register_id]); /* Todo: test T-cycle timing */
|
||||||
GB_advance_cycles(gb, 4);
|
GB_advance_cycles(gb, 4);
|
||||||
gb->registers[register_id]++;
|
gb->registers[register_id]++;
|
||||||
}
|
}
|
||||||
|
@ -240,7 +215,7 @@ static void dec_rr(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
{
|
{
|
||||||
uint8_t register_id = (opcode >> 4) + 1;
|
uint8_t register_id = (opcode >> 4) + 1;
|
||||||
GB_advance_cycles(gb, 4);
|
GB_advance_cycles(gb, 4);
|
||||||
trigger_oam_bug(gb, register_id); /* Todo: test T-cycle timing */
|
GB_trigger_oam_bug(gb, gb->registers[register_id]); /* Todo: test T-cycle timing */
|
||||||
GB_advance_cycles(gb, 4);
|
GB_advance_cycles(gb, 4);
|
||||||
gb->registers[register_id]--;
|
gb->registers[register_id]--;
|
||||||
}
|
}
|
||||||
|
@ -437,7 +412,6 @@ 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, 3);
|
GB_advance_cycles(gb, 3);
|
||||||
trigger_oam_bug(gb, GB_REGISTER_HL); /* Todo: test T-cycle timing */
|
|
||||||
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, 5);
|
GB_advance_cycles(gb, 5);
|
||||||
}
|
}
|
||||||
|
@ -445,7 +419,6 @@ static void ld_dhli_a(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
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, 3);
|
GB_advance_cycles(gb, 3);
|
||||||
trigger_oam_bug(gb, GB_REGISTER_HL); /* Todo: test T-cycle timing */
|
|
||||||
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, 5);
|
GB_advance_cycles(gb, 5);
|
||||||
}
|
}
|
||||||
|
@ -453,7 +426,7 @@ static void ld_dhld_a(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
static void ld_a_dhli(GB_gameboy_t *gb, uint8_t opcode)
|
static void ld_a_dhli(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
{
|
{
|
||||||
GB_advance_cycles(gb, 4);
|
GB_advance_cycles(gb, 4);
|
||||||
trigger_oam_bug(gb, GB_REGISTER_HL); /* Todo: test T-cycle timing */
|
GB_trigger_oam_bug_read_increase(gb, gb->registers[GB_REGISTER_HL]); /* Todo: test T-cycle timing */
|
||||||
gb->registers[GB_REGISTER_AF] &= 0xFF;
|
gb->registers[GB_REGISTER_AF] &= 0xFF;
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_read_memory(gb, gb->registers[GB_REGISTER_HL]++) << 8;
|
gb->registers[GB_REGISTER_AF] |= GB_read_memory(gb, gb->registers[GB_REGISTER_HL]++) << 8;
|
||||||
GB_advance_cycles(gb, 4);
|
GB_advance_cycles(gb, 4);
|
||||||
|
@ -461,8 +434,9 @@ static void ld_a_dhli(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
|
|
||||||
static void ld_a_dhld(GB_gameboy_t *gb, uint8_t opcode)
|
static void ld_a_dhld(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
{
|
{
|
||||||
|
GB_trigger_oam_bug(gb, gb->registers[GB_REGISTER_SP]); /* Todo: test T-cycle timing */
|
||||||
GB_advance_cycles(gb, 4);
|
GB_advance_cycles(gb, 4);
|
||||||
trigger_oam_bug(gb, GB_REGISTER_HL); /* Todo: test T-cycle timing */
|
GB_trigger_oam_bug_read_increase(gb, gb->registers[GB_REGISTER_HL]); /* Todo: test T-cycle timing */
|
||||||
gb->registers[GB_REGISTER_AF] &= 0xFF;
|
gb->registers[GB_REGISTER_AF] &= 0xFF;
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_read_memory(gb, gb->registers[GB_REGISTER_HL]--) << 8;
|
gb->registers[GB_REGISTER_AF] |= GB_read_memory(gb, gb->registers[GB_REGISTER_HL]--) << 8;
|
||||||
GB_advance_cycles(gb, 4);
|
GB_advance_cycles(gb, 4);
|
||||||
|
@ -748,15 +722,14 @@ static void halt(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
|
|
||||||
static void ret_cc(GB_gameboy_t *gb, uint8_t opcode)
|
static void ret_cc(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
{
|
{
|
||||||
/* Todo: Verify timing */
|
|
||||||
if (condition_code(gb, opcode)) {
|
if (condition_code(gb, opcode)) {
|
||||||
GB_debugger_ret_hook(gb);
|
GB_debugger_ret_hook(gb);
|
||||||
GB_advance_cycles(gb, 8);
|
GB_advance_cycles(gb, 8);
|
||||||
gb->pc = GB_read_memory(gb, gb->registers[GB_REGISTER_SP]);
|
GB_trigger_oam_bug_read_increase(gb, gb->registers[GB_REGISTER_SP]); /* Todo: test T-cycle timing */
|
||||||
|
gb->pc = GB_read_memory(gb, gb->registers[GB_REGISTER_SP]++);
|
||||||
GB_advance_cycles(gb, 4);
|
GB_advance_cycles(gb, 4);
|
||||||
gb->pc |= GB_read_memory(gb, gb->registers[GB_REGISTER_SP] + 1) << 8;
|
gb->pc |= GB_read_memory(gb, gb->registers[GB_REGISTER_SP]++) << 8;
|
||||||
GB_advance_cycles(gb, 8);
|
GB_advance_cycles(gb, 8);
|
||||||
gb->registers[GB_REGISTER_SP] += 2;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
GB_advance_cycles(gb, 8);
|
GB_advance_cycles(gb, 8);
|
||||||
|
@ -768,10 +741,9 @@ static void pop_rr(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
uint8_t register_id;
|
uint8_t register_id;
|
||||||
GB_advance_cycles(gb, 4);
|
GB_advance_cycles(gb, 4);
|
||||||
register_id = ((opcode >> 4) + 1) & 3;
|
register_id = ((opcode >> 4) + 1) & 3;
|
||||||
trigger_oam_bug(gb, GB_REGISTER_SP); /* Todo: test T-cycle timing */
|
GB_trigger_oam_bug_read_increase(gb, gb->registers[GB_REGISTER_SP]); /* Todo: test T-cycle timing */
|
||||||
gb->registers[register_id] = GB_read_memory(gb, gb->registers[GB_REGISTER_SP]++);
|
gb->registers[register_id] = GB_read_memory(gb, gb->registers[GB_REGISTER_SP]++);
|
||||||
GB_advance_cycles(gb, 4);
|
GB_advance_cycles(gb, 4);
|
||||||
trigger_oam_bug(gb, GB_REGISTER_SP); /* Todo: test T-cycle timing */
|
|
||||||
gb->registers[register_id] |= GB_read_memory(gb, gb->registers[GB_REGISTER_SP]++) << 8;
|
gb->registers[register_id] |= GB_read_memory(gb, gb->registers[GB_REGISTER_SP]++) << 8;
|
||||||
GB_advance_cycles(gb, 4);
|
GB_advance_cycles(gb, 4);
|
||||||
gb->registers[GB_REGISTER_AF] &= 0xFFF0; // Make sure we don't set impossible flags on F! See Blargg's PUSH AF test.
|
gb->registers[GB_REGISTER_AF] &= 0xFFF0; // Make sure we don't set impossible flags on F! See Blargg's PUSH AF test.
|
||||||
|
@ -808,14 +780,15 @@ static void call_cc_a16(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
uint16_t call_addr = gb->pc - 1;
|
uint16_t call_addr = gb->pc - 1;
|
||||||
if (condition_code(gb, opcode)) {
|
if (condition_code(gb, opcode)) {
|
||||||
GB_advance_cycles(gb, 4);
|
GB_advance_cycles(gb, 4);
|
||||||
gb->registers[GB_REGISTER_SP] -= 2;
|
|
||||||
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, 7);
|
GB_advance_cycles(gb, 3);
|
||||||
GB_write_memory(gb, gb->registers[GB_REGISTER_SP] + 1, (gb->pc) >> 8);
|
GB_trigger_oam_bug(gb, gb->registers[GB_REGISTER_SP]); /* Todo: test T-cycle timing */
|
||||||
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) >> 8);
|
||||||
|
GB_advance_cycles(gb, 4);
|
||||||
|
GB_write_memory(gb, --gb->registers[GB_REGISTER_SP], (gb->pc) & 0xFF);
|
||||||
GB_advance_cycles(gb, 5);
|
GB_advance_cycles(gb, 5);
|
||||||
gb->pc = addr;
|
gb->pc = addr;
|
||||||
|
|
||||||
|
@ -830,12 +803,12 @@ 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, 7);
|
GB_advance_cycles(gb, 3);
|
||||||
|
GB_trigger_oam_bug(gb, gb->registers[GB_REGISTER_SP]); /* Todo: test T-cycle timing */
|
||||||
|
GB_advance_cycles(gb, 4);
|
||||||
register_id = ((opcode >> 4) + 1) & 3;
|
register_id = ((opcode >> 4) + 1) & 3;
|
||||||
trigger_oam_bug(gb, GB_REGISTER_SP); /* Todo: test T-cycle timing */
|
|
||||||
GB_write_memory(gb, --gb->registers[GB_REGISTER_SP], (gb->registers[register_id]) >> 8);
|
GB_write_memory(gb, --gb->registers[GB_REGISTER_SP], (gb->registers[register_id]) >> 8);
|
||||||
GB_advance_cycles(gb, 4);
|
GB_advance_cycles(gb, 4);
|
||||||
trigger_oam_bug(gb, GB_REGISTER_SP); /* Todo: test T-cycle timing */
|
|
||||||
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, 5);
|
GB_advance_cycles(gb, 5);
|
||||||
}
|
}
|
||||||
|
@ -982,11 +955,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, 7);
|
GB_advance_cycles(gb, 3);
|
||||||
gb->registers[GB_REGISTER_SP] -= 2;
|
GB_trigger_oam_bug(gb, gb->registers[GB_REGISTER_SP]); /* Todo: test T-cycle timing */
|
||||||
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) >> 8);
|
||||||
|
GB_advance_cycles(gb, 4);
|
||||||
|
GB_write_memory(gb, --gb->registers[GB_REGISTER_SP], (gb->pc) & 0xFF);
|
||||||
GB_advance_cycles(gb, 5);
|
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);
|
||||||
|
@ -996,11 +970,11 @@ static void ret(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
{
|
{
|
||||||
GB_debugger_ret_hook(gb);
|
GB_debugger_ret_hook(gb);
|
||||||
GB_advance_cycles(gb, 4);
|
GB_advance_cycles(gb, 4);
|
||||||
gb->pc = GB_read_memory(gb, gb->registers[GB_REGISTER_SP]);
|
GB_trigger_oam_bug_read_increase(gb, gb->registers[GB_REGISTER_SP]); /* Todo: test T-cycle timing */
|
||||||
|
gb->pc = GB_read_memory(gb, gb->registers[GB_REGISTER_SP]++);
|
||||||
GB_advance_cycles(gb, 4);
|
GB_advance_cycles(gb, 4);
|
||||||
gb->pc |= GB_read_memory(gb, gb->registers[GB_REGISTER_SP] + 1) << 8;
|
gb->pc |= GB_read_memory(gb, gb->registers[GB_REGISTER_SP]++) << 8;
|
||||||
GB_advance_cycles(gb, 8);
|
GB_advance_cycles(gb, 8);
|
||||||
gb->registers[GB_REGISTER_SP] += 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void reti(GB_gameboy_t *gb, uint8_t opcode)
|
static void reti(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
|
@ -1013,14 +987,15 @@ static void call_a16(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, 4);
|
GB_advance_cycles(gb, 4);
|
||||||
gb->registers[GB_REGISTER_SP] -= 2;
|
|
||||||
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, 7);
|
GB_advance_cycles(gb, 3);
|
||||||
GB_write_memory(gb, gb->registers[GB_REGISTER_SP] + 1, (gb->pc) >> 8);
|
GB_trigger_oam_bug(gb, gb->registers[GB_REGISTER_SP]); /* Todo: test T-cycle timing */
|
||||||
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) >> 8);
|
||||||
|
GB_advance_cycles(gb, 4);
|
||||||
|
GB_write_memory(gb, --gb->registers[GB_REGISTER_SP], (gb->pc) & 0xFF);
|
||||||
GB_advance_cycles(gb, 5);
|
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);
|
||||||
|
@ -1419,16 +1394,19 @@ void GB_cpu_run(GB_gameboy_t *gb)
|
||||||
if (gb->halted && !effecitve_ime && interrupt_queue) {
|
if (gb->halted && !effecitve_ime && interrupt_queue) {
|
||||||
gb->halted = false;
|
gb->halted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Call interrupt */
|
/* Call interrupt */
|
||||||
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, 11);
|
|
||||||
gb->registers[GB_REGISTER_SP] -= 2;
|
GB_advance_cycles(gb, 7);
|
||||||
GB_write_memory(gb, gb->registers[GB_REGISTER_SP] + 1, (gb->pc) >> 8);
|
GB_trigger_oam_bug(gb, gb->registers[GB_REGISTER_SP]); /* Todo: test T-cycle timing */
|
||||||
|
GB_advance_cycles(gb, 4);
|
||||||
|
GB_write_memory(gb, --gb->registers[GB_REGISTER_SP], (gb->pc) >> 8);
|
||||||
interrupt_queue = gb->interrupt_enable;
|
interrupt_queue = gb->interrupt_enable;
|
||||||
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);
|
||||||
interrupt_queue &= (gb->io_registers[GB_IO_IF]) & 0x1F;
|
interrupt_queue &= (gb->io_registers[GB_IO_IF]) & 0x1F;
|
||||||
|
|
||||||
GB_advance_cycles(gb, 5);
|
GB_advance_cycles(gb, 5);
|
||||||
|
|
Loading…
Reference in New Issue