diff --git a/libHawk/GBHawk/GBHawk/Core.h b/libHawk/GBHawk/GBHawk/Core.h index 1cf7a97dad..f59f12f8eb 100644 --- a/libHawk/GBHawk/GBHawk/Core.h +++ b/libHawk/GBHawk/GBHawk/Core.h @@ -76,24 +76,26 @@ namespace GBHawk MemMap.serialport_pntr = &serialport; cpu.mem_ctrl = &MemMap; - MemMap.ppu_pntr->FlagI = &cpu.FlagI; - MemMap.ppu_pntr->in_vblank = &MemMap.in_vblank; - MemMap.ppu_pntr->cpu_LY = &cpu.LY; - MemMap.ppu_pntr->REG_FFFF = &MemMap.REG_FFFF; - MemMap.ppu_pntr->REG_FF0F = &MemMap.REG_FF0F; - MemMap.ppu_pntr->_scanlineCallbackLine = &MemMap._scanlineCallbackLine; - MemMap.ppu_pntr->OAM = &MemMap.OAM[0]; - MemMap.ppu_pntr->VRAM = &MemMap.VRAM[0]; - MemMap.ppu_pntr->VRAM_Bank = &MemMap.VRAM_Bank; - MemMap.ppu_pntr->cpu_halted = &cpu.halted; - MemMap.ppu_pntr->_vidbuffer = &MemMap.vidbuffer[0]; - MemMap.ppu_pntr->color_palette = &MemMap.color_palette[0]; - MemMap.ppu_pntr->HDMA_transfer = &MemMap.HDMA_transfer; - MemMap.ppu_pntr->GBC_compat = &MemMap.GBC_compat; + ppu->FlagI = &cpu.FlagI; + ppu->in_vblank = &MemMap.in_vblank; + ppu->vblank_rise = &MemMap.vblank_rise; + ppu->cpu_LY = &cpu.LY; + ppu->REG_FFFF = &MemMap.REG_FFFF; + ppu->REG_FF0F = &MemMap.REG_FF0F; + ppu->_scanlineCallbackLine = &MemMap._scanlineCallbackLine; + ppu->OAM = &MemMap.OAM[0]; + ppu->VRAM = &MemMap.VRAM[0]; + ppu->VRAM_Bank = &MemMap.VRAM_Bank; + ppu->cpu_halted = &cpu.halted; + ppu->_vidbuffer = &MemMap.vidbuffer[0]; + ppu->color_palette = &MemMap.color_palette[0]; + ppu->HDMA_transfer = &MemMap.HDMA_transfer; + ppu->GBC_compat = &MemMap.GBC_compat; timer.FlagI = &cpu.FlagI; timer.REG_FFFF = &MemMap.REG_FFFF; timer.REG_FF0F = &MemMap.REG_FF0F; + timer.CPU_cycle_pntr = &cpu.TotalExecutedCycles; serialport.GBC_compat = &MemMap.GBC_compat; serialport.FlagI = &cpu.FlagI; @@ -146,6 +148,9 @@ namespace GBHawk temp_check = 5; } */ + MemMap.new_controller_1 = new_controller_1; + MemMap.new_accx = new_accx; + MemMap.new_accy = new_accy; temp_check = 70224; @@ -160,57 +165,30 @@ namespace GBHawk { // These things all tick twice as fast in GBC double speed mode if (ppu->DMA_start && !cpu.halted) { ppu->DMA_tick(); } - timer.tick_1(); serialport.serial_transfer_tick(); cpu.ExecuteOne(&MemMap.REG_FF0F, &MemMap.REG_FFFF); - timer.tick_2(); + timer.tick(); if (MemMap.double_speed) { if (ppu->DMA_start && !cpu.halted) { ppu->DMA_tick(); } - timer.tick_1(); serialport.serial_transfer_tick(); cpu.ExecuteOne(&MemMap.REG_FF0F, &MemMap.REG_FFFF); - timer.tick_2(); + timer.tick(); } } else { - timer.tick_1(); - timer.tick_2(); + timer.tick(); cpu.TotalExecutedCycles++; if (MemMap.double_speed) { - timer.tick_1(); - timer.tick_2(); + timer.tick(); cpu.TotalExecutedCycles++; } } - if (MemMap.in_vblank && !MemMap.in_vblank_old) - { - MemMap.lagged = false; - - // update the controller state on VBlank - MemMap.controller_state = new_controller_1; - MemMap.Acc_X_state = new_accx; - MemMap.Acc_Y_state = new_accy; - - // check if controller state caused interrupt - do_controller_check(); - - // send the image on VBlank - SendVideoBuffer(); - - if ((ppu->scanlineCallback) && (MemMap._scanlineCallbackLine == -1)) - { - ppu->scanlineCallback(); - } - } - MemMap.REG_FF0F_OLD = MemMap.REG_FF0F; - - MemMap.in_vblank_old = MemMap.in_vblank; } // turn off the screen so the image doesnt persist @@ -238,29 +216,25 @@ namespace GBHawk // These things all tick twice as fast in GBC double speed mode // Note that DMA is halted when the CPU is halted if (ppu->DMA_start && !cpu.halted) { ppu->DMA_tick(); } - timer.tick_1(); serialport.serial_transfer_tick(); cpu.ExecuteOne(&MemMap.REG_FF0F, &MemMap.REG_FFFF); - timer.tick_2(); + timer.tick(); if (MemMap.double_speed) { if (ppu->DMA_start && !cpu.halted) { ppu->DMA_tick(); } - timer.tick_1(); serialport.serial_transfer_tick(); cpu.ExecuteOne(&MemMap.REG_FF0F, &MemMap.REG_FFFF); - timer.tick_2(); + timer.tick(); } } else { - timer.tick_1(); - timer.tick_2(); + timer.tick(); cpu.TotalExecutedCycles++; if (MemMap.double_speed) { - timer.tick_1(); - timer.tick_2(); + timer.tick(); cpu.TotalExecutedCycles++; } } @@ -274,69 +248,6 @@ namespace GBHawk MemMap.REG_FF0F_OLD = MemMap.REG_FF0F; } - void do_controller_check() - { - // check if new input changed the input register and triggered IRQ - uint8_t contr_prev = MemMap.input_register; - - MemMap.input_register &= 0xF0; - if ((MemMap.input_register & 0x30) == 0x20) - { - MemMap.input_register |= (uint8_t)(MemMap.controller_state & 0xF); - } - else if ((MemMap.input_register & 0x30) == 0x10) - { - MemMap.input_register |= (uint8_t)((MemMap.controller_state & 0xF0) >> 4); - } - else if ((MemMap.input_register & 0x30) == 0x00) - { - // if both polls are set, then a bit is zero if either or both pins are zero - uint8_t temp = (uint8_t)((MemMap.controller_state & 0xF) & ((MemMap.controller_state & 0xF0) >> 4)); - MemMap.input_register |= temp; - } - else - { - MemMap.input_register |= 0xF; - } - - // check for interrupts - if (((contr_prev & 8) > 0) && ((MemMap.input_register & 8) == 0) || - ((contr_prev & 4) > 0) && ((MemMap.input_register & 4) == 0) || - ((contr_prev & 2) > 0) && ((MemMap.input_register & 2) == 0) || - ((contr_prev & 1) > 0) && ((MemMap.input_register & 1) == 0)) - { - if ((MemMap.REG_FFFF & 0x10) > 0) { cpu.FlagI = true; } - MemMap.REG_FF0F |= 0x10; - } - } - - void SendVideoBuffer() - { - if (MemMap.GBC_compat) - { - if (!ppu->blank_frame) - { - for (int j = 0; j < (160 * 144); j++) { MemMap.frame_buffer[j] = MemMap.vidbuffer[j]; } - } - - ppu->blank_frame = false; - } - else - { - if (ppu->blank_frame) - { - for (int i = 0; i < (160 * 144); i++) - { - MemMap.vidbuffer[i] = (int)MemMap.color_palette[0]; - } - } - - for (int j = 0; j < (160 * 144); j++) { MemMap.frame_buffer[j] = MemMap.vidbuffer[j]; } - - ppu->blank_frame = false; - } - } - void GetVideo(uint32_t* dest) { uint32_t* src = MemMap.frame_buffer; diff --git a/libHawk/GBHawk/GBHawk/Memory.cpp b/libHawk/GBHawk/GBHawk/Memory.cpp index 6fb4366e39..597609604e 100644 --- a/libHawk/GBHawk/GBHawk/Memory.cpp +++ b/libHawk/GBHawk/GBHawk/Memory.cpp @@ -926,4 +926,74 @@ namespace GBHawk color_palette_BG[i] = color_palette[(ppu_pntr->BGP >> (i * 2)) & 3]; } } + + void MemoryManager::do_controller_check() + { + lagged = false; + + // update the controller state on VBlank + controller_state = new_controller_1; + Acc_X_state = new_accx; + Acc_Y_state = new_accy; + + // check if new input changed the input register and triggered IRQ + uint8_t contr_prev = input_register; + + input_register &= 0xF0; + if ((input_register & 0x30) == 0x20) + { + input_register |= (uint8_t)(controller_state & 0xF); + } + else if ((input_register & 0x30) == 0x10) + { + input_register |= (uint8_t)((controller_state & 0xF0) >> 4); + } + else if ((input_register & 0x30) == 0x00) + { + // if both polls are set, then a bit is zero if either or both pins are zero + uint8_t temp = (uint8_t)((controller_state & 0xF) & ((controller_state & 0xF0) >> 4)); + input_register |= temp; + } + else + { + input_register |= 0xF; + } + + // check for interrupts + if (((contr_prev & 8) > 0) && ((input_register & 8) == 0) || + ((contr_prev & 4) > 0) && ((input_register & 4) == 0) || + ((contr_prev & 2) > 0) && ((input_register & 2) == 0) || + ((contr_prev & 1) > 0) && ((input_register & 1) == 0)) + { + if ((REG_FFFF & 0x10) > 0) { cpu_pntr->FlagI = true; } + REG_FF0F |= 0x10; + } + } + + void MemoryManager::SendVideoBuffer() + { + if (GBC_compat) + { + if (!ppu_pntr->blank_frame) + { + for (int j = 0; j < (160 * 144); j++) { frame_buffer[j] = vidbuffer[j]; } + } + + ppu_pntr->blank_frame = false; + } + else + { + if (ppu_pntr->blank_frame) + { + for (int i = 0; i < (160 * 144); i++) + { + vidbuffer[i] = color_palette[0]; + } + } + + for (int j = 0; j < (160 * 144); j++) { frame_buffer[j] = vidbuffer[j]; } + + ppu_pntr->blank_frame = false; + } + } } \ No newline at end of file diff --git a/libHawk/GBHawk/GBHawk/Memory.h b/libHawk/GBHawk/GBHawk/Memory.h index f3f58458fb..6d051eace1 100644 --- a/libHawk/GBHawk/GBHawk/Memory.h +++ b/libHawk/GBHawk/GBHawk/Memory.h @@ -29,6 +29,8 @@ namespace GBHawk uint8_t Read_Registers(uint32_t addr); void Write_Registers(uint32_t addr, uint8_t value); void compute_palettes(); + void do_controller_check(); + void SendVideoBuffer(); #pragma region Declarations @@ -47,6 +49,11 @@ namespace GBHawk uint32_t ROM_Mapper; uint32_t Cart_RAM_Length; + // passed in on frame advace, not stated + uint8_t new_controller_1; + uint32_t new_accx; + uint32_t new_accy; + // State bool lagged; bool is_GBC; diff --git a/libHawk/GBHawk/GBHawk/PPU.cpp b/libHawk/GBHawk/GBHawk/PPU.cpp index 0d18d7dc1d..236fb0b859 100644 --- a/libHawk/GBHawk/GBHawk/PPU.cpp +++ b/libHawk/GBHawk/GBHawk/PPU.cpp @@ -13,4 +13,20 @@ namespace GBHawk { return mem_ctrl->ReadMemory(addr); } + + void PPU::vblank_process() + { + in_vblank[0] = true; + vblank_rise[0] = true; + + if (scanlineCallback && (_scanlineCallbackLine[0] == -1)) + { + scanlineCallback(); + } + + mem_ctrl->do_controller_check(); + + // send the image on VBlank + mem_ctrl->SendVideoBuffer(); + } } \ No newline at end of file diff --git a/libHawk/GBHawk/GBHawk/PPU.h b/libHawk/GBHawk/GBHawk/PPU.h index 205808c2db..b40147c1aa 100644 --- a/libHawk/GBHawk/GBHawk/PPU.h +++ b/libHawk/GBHawk/GBHawk/PPU.h @@ -23,6 +23,7 @@ namespace GBHawk // pointers not stated bool* FlagI = nullptr; bool* in_vblank = nullptr; + bool* vblank_rise = nullptr; bool* cpu_halted = nullptr; bool* HDMA_transfer = nullptr; bool* GBC_compat = nullptr; @@ -198,6 +199,8 @@ namespace GBHawk virtual void reorder_and_assemble_sprites() { } + void vblank_process(); + uint8_t BG_PAL_read() { if (VRAM_access_read) @@ -850,7 +853,7 @@ namespace GBHawk if (LY == 144) { - in_vblank[0] = true; + vblank_process(); } } @@ -1787,7 +1790,7 @@ namespace GBHawk { if (OAM_scan_index < 40) { - uint32_t temp = DMA_OAM_access ? OAM[OAM_scan_index * 4] : (uint32_t)0xFF; + int32_t temp = DMA_OAM_access ? OAM[OAM_scan_index * 4] : (int32_t)0xFF; // (sprite Y - 16) equals LY, we have a sprite if ((temp - 16) <= LY && ((temp - 16) + 8 + (((LCDC & 0x4) > 0) ? 8 : 0)) > LY) @@ -2220,7 +2223,7 @@ namespace GBHawk if (LY == 144) { - in_vblank[0] = true; + vblank_process(); } } @@ -3221,7 +3224,7 @@ namespace GBHawk { if (OAM_scan_index < 40) { - uint32_t temp = DMA_OAM_access ? OAM[OAM_scan_index * 4] : (uint32_t)0xFF; + int32_t temp = DMA_OAM_access ? OAM[OAM_scan_index * 4] : (int32_t)0xFF; // (sprite Y - 16) equals LY, we have a sprite if ((temp - 16) <= LY && ((temp - 16) + 8 + (((LCDC & 0x4) > 0) ? 8 : 0)) > LY) @@ -3680,7 +3683,7 @@ namespace GBHawk if (LY == 144) { - in_vblank[0] = true; + vblank_process(); } } @@ -4738,7 +4741,7 @@ namespace GBHawk { if (OAM_scan_index < 40) { - uint32_t temp = DMA_OAM_access ? OAM[OAM_scan_index * 4] : (uint32_t)0xFF; + int32_t temp = DMA_OAM_access ? OAM[OAM_scan_index * 4] : (int32_t)0xFF; // (sprite Y - 16) equals LY, we have a sprite if ((temp - 16) <= LY && ((temp - 16) + 8 + (((LCDC & 0x4) > 0) ? 8 : 0)) > LY) diff --git a/libHawk/GBHawk/GBHawk/Timer.h b/libHawk/GBHawk/GBHawk/Timer.h index 713ef03db1..752ced9a85 100644 --- a/libHawk/GBHawk/GBHawk/Timer.h +++ b/libHawk/GBHawk/GBHawk/Timer.h @@ -19,20 +19,21 @@ namespace GBHawk bool* FlagI = nullptr; uint8_t* REG_FFFF = nullptr; uint8_t* REG_FF0F = nullptr; + uint64_t* CPU_cycle_pntr = nullptr; - bool old_state; - bool state; - bool reload_block; - bool TMA_coincidence; + bool old_state = false; + bool state = false; + bool reload_block = false; + + uint8_t timer_reload = 0; + uint8_t timer = 0; + uint8_t timer_old = 0; + uint8_t timer_control = 0; + uint8_t pending_reload = 0; - uint8_t timer_reload; - uint8_t timer; - uint8_t timer_old; - uint8_t timer_control; - uint8_t pending_reload; - uint8_t write_ignore; - - uint32_t divider_reg; + uint32_t divider_reg = 0; + + uint64_t next_free_cycle = 0; uint8_t ReadReg(uint32_t addr) { @@ -60,7 +61,7 @@ namespace GBHawk // TIMA (Timer Counter) case 0xFF05: - if (write_ignore == 0) + if (CPU_cycle_pntr[0] >= next_free_cycle) { timer_old = timer; timer = value; @@ -71,7 +72,7 @@ namespace GBHawk // TMA (Timer Modulo) case 0xFF06: timer_reload = value; - if (TMA_coincidence) + if (CPU_cycle_pntr[0] < next_free_cycle) { timer = timer_reload; timer_old = timer; @@ -85,35 +86,7 @@ namespace GBHawk } } - void tick_1() - { - if (write_ignore > 0) - { - write_ignore--; - if (write_ignore == 0) - { - TMA_coincidence = false; - } - } - - if (pending_reload > 0) - { - pending_reload--; - if (pending_reload == 0 && !reload_block) - { - timer = timer_reload; - timer_old = timer; - write_ignore = 4; - TMA_coincidence = true; - - // set interrupts - if ((REG_FFFF[0] & 0x4) > 0) { FlagI[0] = true; } - REG_FF0F[0] |= 0x04; - } - } - } - - void tick_2() + void tick() { divider_reg++; @@ -162,6 +135,22 @@ namespace GBHawk } old_state = state; + + if (pending_reload > 0) + { + pending_reload--; + if (pending_reload == 0 && !reload_block) + { + timer = timer_reload; + timer_old = timer; + + next_free_cycle = 4 + CPU_cycle_pntr[0]; + + // set interrupts + if ((REG_FFFF[0] & 0x4) > 0) { FlagI[0] = true; } + REG_FF0F[0] |= 0x04; + } + } } void Reset() @@ -172,11 +161,10 @@ namespace GBHawk timer_old = 0; timer_control = 0xF8; pending_reload = 0; - write_ignore = 0; old_state = false; state = false; reload_block = false; - TMA_coincidence = false; + next_free_cycle = 0; } #pragma region State Save / Load @@ -187,18 +175,21 @@ namespace GBHawk *saver = (uint8_t)(old_state ? 1 : 0); saver++; *saver = (uint8_t)(state ? 1 : 0); saver++; *saver = (uint8_t)(reload_block ? 1 : 0); saver++; - *saver = (uint8_t)(TMA_coincidence ? 1 : 0); saver++; *saver = timer_reload; saver++; *saver = timer; saver++; *saver = timer_old; saver++; *saver = timer_control; saver++; *saver = pending_reload; saver++; - *saver = write_ignore; saver++; *saver = (uint8_t)(divider_reg & 0xFF); saver++; *saver = (uint8_t)((divider_reg >> 8) & 0xFF); saver++; *saver = (uint8_t)((divider_reg >> 16) & 0xFF); saver++; *saver = (uint8_t)((divider_reg >> 24) & 0xFF); saver++; + *saver = (uint8_t)(next_free_cycle & 0xFF); saver++; *saver = (uint8_t)((next_free_cycle >> 8) & 0xFF); saver++; + *saver = (uint8_t)((next_free_cycle >> 16) & 0xFF); saver++; *saver = (uint8_t)((next_free_cycle >> 24) & 0xFF); saver++; + *saver = (uint8_t)((next_free_cycle >> 32) & 0xFF); saver++; *saver = (uint8_t)((next_free_cycle >> 40) & 0xFF); saver++; + *saver = (uint8_t)((next_free_cycle >> 48) & 0xFF); saver++; *saver = (uint8_t)((next_free_cycle >> 56) & 0xFF); saver++; + return saver; } @@ -207,18 +198,21 @@ namespace GBHawk old_state = *loader == 1; loader++; state = *loader == 1; loader++; reload_block = *loader == 1; loader++; - TMA_coincidence = *loader == 1; loader++; timer_reload = *loader; loader++; timer = *loader; loader++; timer_old = *loader; loader++; timer_control = *loader; loader++; pending_reload = *loader; loader++; - write_ignore = *loader; loader++; divider_reg = *loader; loader++; divider_reg |= (*loader << 8); loader++; divider_reg |= (*loader << 16); loader++; divider_reg |= (*loader << 24); loader++; + next_free_cycle = *loader; loader++; next_free_cycle |= ((uint64_t)*loader << 8); loader++; + next_free_cycle |= ((uint64_t)*loader << 16); loader++; next_free_cycle |= ((uint64_t)*loader << 24); loader++; + next_free_cycle |= ((uint64_t)*loader << 32); loader++; next_free_cycle |= ((uint64_t)*loader << 40); loader++; + next_free_cycle |= ((uint64_t)*loader << 48); loader++; next_free_cycle |= ((uint64_t)*loader << 56); loader++; + return loader; }