diff --git a/HISTORY.txt b/HISTORY.txt index 3ff047c..63bd127 100644 --- a/HISTORY.txt +++ b/HISTORY.txt @@ -74,6 +74,7 @@ Genesis Plus GX 1.7.5 (xx/xx/xxxx) (Eke-Eke) * added support for some new unlicensed games with copy protection (Thunderbolt II, Tom Clown, Chaoji Puke / Super Poker, Rock Heaven, Rock World) * added support for Everdrive extended SSF mapper * added support for MegaSD CD hardware overlay (MD+ hacks) and extended SSF2 / ROM write mappers +* added emulation of Z80 halt when accessing 68k bus during DMA from 68k bus * added basic emulation of 68k bus access refresh delays (fixes Super Airwolf graphical glitch during intro & some Krikzz's mcd-verificator timing tests) * added (very basic) emulation of Flashkit MD hardware * added emulation of Micro Machines USA on-board TMSS bypass logic hardware diff --git a/builds/genesis_plus_gx_libretro.dll b/builds/genesis_plus_gx_libretro.dll index f49787c..9da20de 100644 Binary files a/builds/genesis_plus_gx_libretro.dll and b/builds/genesis_plus_gx_libretro.dll differ diff --git a/builds/genplus_cube.dol b/builds/genplus_cube.dol index 23e4458..6b410fd 100644 Binary files a/builds/genplus_cube.dol and b/builds/genplus_cube.dol differ diff --git a/builds/genplus_wii.dol b/builds/genplus_wii.dol index 9a52846..d3d8af5 100644 Binary files a/builds/genplus_wii.dol and b/builds/genplus_wii.dol differ diff --git a/core/genesis.c b/core/genesis.c index 447c56a..b964c20 100644 --- a/core/genesis.c +++ b/core/genesis.c @@ -5,7 +5,7 @@ * Support for SG-1000, Mark-III, Master System, Game Gear, Mega Drive & Mega CD hardware * * Copyright (C) 1998-2003 Charles Mac Donald (original code) - * Copyright (C) 2007-2022 Eke-Eke (Genesis Plus GX) + * Copyright (C) 2007-2024 Eke-Eke (Genesis Plus GX) * * Redistribution and use of this code or any derivative works are permitted * provided that the following conditions are met: @@ -50,7 +50,7 @@ uint8 boot_rom[0x800]; /* Genesis BOOT ROM */ uint8 work_ram[0x10000]; /* 68K RAM */ uint8 zram[0x2000]; /* Z80 RAM */ uint32 zbank; /* Z80 bank window address */ -uint8 zstate; /* Z80 bus state (d0 = /RESET, d1 = BUSACK) */ +uint8 zstate; /* Z80 bus state (d0 = /RESET, d1 = BUSREQ, d2 = WAIT) */ uint8 pico_current; /* PICO current page */ static uint8 tmss[4]; /* TMSS security register */ diff --git a/core/genesis.h b/core/genesis.h index 04e0052..8d90757 100644 --- a/core/genesis.h +++ b/core/genesis.h @@ -5,7 +5,7 @@ * Support for SG-1000, Mark-III, Master System, Game Gear, Mega Drive & Mega CD hardware * * Copyright (C) 1998-2003 Charles Mac Donald (original code) - * Copyright (C) 2007-2022 Eke-Eke (Genesis Plus GX) + * Copyright (C) 2007-2024 Eke-Eke (Genesis Plus GX) * * Redistribution and use of this code or any derivative works are permitted * provided that the following conditions are met: diff --git a/core/memz80.c b/core/memz80.c index bfe562b..521403e 100644 --- a/core/memz80.c +++ b/core/memz80.c @@ -5,7 +5,7 @@ * Support for SG-1000, Mark-III, Master System, Game Gear & Mega Drive ports access * * Copyright (C) 1998-2003 Charles Mac Donald (original code) - * Copyright (C) 2007-2022 Eke-Eke (Genesis Plus GX) + * Copyright (C) 2007-2024 Eke-Eke (Genesis Plus GX) * * Redistribution and use of this code or any derivative works are permitted * provided that the following conditions are met: @@ -92,6 +92,26 @@ INLINE unsigned char z80_lockup_r(unsigned int address) /* Z80 Memory handlers (Genesis mode) */ /*--------------------------------------------------------------------------*/ +static void z80_request_68k_bus_access(void) +{ + /* check if 68k bus is accessed by VDP DMA */ + if ((Z80.cycles < dma_endCycles) && (dma_type < 2)) + { + /* force Z80 to wait until end of DMA */ + Z80.cycles = dma_endCycles; + + /* check if DMA is not finished at the end of current timeframe */ + if (dma_length) + { + /* indicate Z80 will still be waiting for 68k bus at the end of current DMA timeframe */ + zstate |= 4; + } + } + + /* average Z80 wait-states when accessing 68k area */ + Z80.cycles += 3 * 15; +} + unsigned char z80_memory_r(unsigned int address) { switch((address >> 13) & 7) @@ -111,8 +131,10 @@ unsigned char z80_memory_r(unsigned int address) { if ((address >> 8) == 0x7F) { - /* average Z80 wait-states when accessing 68k area */ - Z80.cycles += 3 * 15; + /* request access to 68k bus */ + z80_request_68k_bus_access(); + + /* read from $C00000-$C0FFFF area */ return (*zbank_memory_map[0xc0].read)(address); } return z80_unused_r(address); @@ -120,9 +142,10 @@ unsigned char z80_memory_r(unsigned int address) default: /* $8000-$FFFF: 68k bank (32K) */ { - /* average Z80 wait-states when accessing 68k area */ - Z80.cycles += 3 * 15; + /* request access to 68k bus */ + z80_request_68k_bus_access(); + /* read from 68k banked area */ address = zbank | (address & 0x7FFF); if (zbank_memory_map[address >> 16].read) { @@ -163,8 +186,10 @@ void z80_memory_w(unsigned int address, unsigned char data) case 0x7F: /* $7F00-$7FFF: VDP */ { - /* average Z80 wait-states when accessing 68k area */ - Z80.cycles += 3 * 15; + /* request access to 68k bus */ + z80_request_68k_bus_access(); + + /* write to $C00000-$C0FFFF area */ (*zbank_memory_map[0xc0].write)(address, data); return; } @@ -179,9 +204,10 @@ void z80_memory_w(unsigned int address, unsigned char data) default: /* $8000-$FFFF: 68k bank (32K) */ { - /* average Z80 wait-states when accessing 68k area */ - Z80.cycles += 3 * 15; + /* request access to 68k bus */ + z80_request_68k_bus_access(); + /* write to 68k banked area */ address = zbank | (address & 0x7FFF); if (zbank_memory_map[address >> 16].write) { diff --git a/core/memz80.h b/core/memz80.h index b34c47b..2ec19b0 100644 --- a/core/memz80.h +++ b/core/memz80.h @@ -5,7 +5,7 @@ * Support for SG-1000, Mark-III, Master System, Game Gear & Mega Drive ports access * * Copyright (C) 1998-2003 Charles Mac Donald (original code) - * Copyright (C) 2007-2022 Eke-Eke (Genesis Plus GX) + * Copyright (C) 2007-2024 Eke-Eke (Genesis Plus GX) * * Redistribution and use of this code or any derivative works are permitted * provided that the following conditions are met: diff --git a/core/system.c b/core/system.c index 20843aa..669f22d 100644 --- a/core/system.c +++ b/core/system.c @@ -662,6 +662,7 @@ void system_frame_gen(int do_skip) m68k.refresh_cycles -= mcycles_vdp; m68k.cycles -= mcycles_vdp; Z80.cycles -= mcycles_vdp; + dma_endCycles = 0; } void system_frame_scd(int do_skip) @@ -987,6 +988,7 @@ void system_frame_scd(int do_skip) m68k.refresh_cycles -= mcycles_vdp; m68k.cycles -= mcycles_vdp; Z80.cycles -= mcycles_vdp; + dma_endCycles = 0; } void system_frame_sms(int do_skip) diff --git a/core/vdp_ctrl.c b/core/vdp_ctrl.c index c3a7573..04b8b22 100644 --- a/core/vdp_ctrl.c +++ b/core/vdp_ctrl.c @@ -63,15 +63,17 @@ #define HBLANK_H40_END_MCYCLE (872) /* VDP context */ -uint8 ALIGNED_(4) sat[0x400]; /* Internal copy of sprite attribute table */ -uint8 ALIGNED_(4) vram[0x10000]; /* Video RAM (64K x 8-bit) */ -uint8 ALIGNED_(4) cram[0x80]; /* On-chip color RAM (64 x 9-bit) */ -uint8 ALIGNED_(4) vsram[0x80]; /* On-chip vertical scroll RAM (40 x 11-bit) */ -uint8 reg[0x20]; /* Internal VDP registers (23 x 8-bit) */ -uint8 hint_pending; /* 0= Line interrupt is pending */ -uint8 vint_pending; /* 1= Frame interrupt is pending */ -uint16 status; /* VDP status flags */ -uint32 dma_length; /* DMA remaining length */ +uint8 ALIGNED_(4) sat[0x400]; /* Internal copy of sprite attribute table */ +uint8 ALIGNED_(4) vram[0x10000]; /* Video RAM (64K x 8-bit) */ +uint8 ALIGNED_(4) cram[0x80]; /* On-chip color RAM (64 x 9-bit) */ +uint8 ALIGNED_(4) vsram[0x80]; /* On-chip vertical scroll RAM (40 x 11-bit) */ +uint8 reg[0x20]; /* Internal VDP registers (23 x 8-bit) */ +uint8 hint_pending; /* 0= Line interrupt is pending */ +uint8 vint_pending; /* 1= Frame interrupt is pending */ +uint16 status; /* VDP status flags */ +uint32 dma_length; /* DMA remaining length */ +uint32 dma_endCycles; /* DMA end cycle */ +uint8 dma_type; /* DMA mode */ /* Global variables */ uint16 ntab; /* Name table A base address */ @@ -135,23 +137,21 @@ static const uint8 shift_table[] = { 6, 7, 0, 8 }; static const uint8 col_mask_table[] = { 0x0F, 0x1F, 0x0F, 0x3F }; static const uint16 row_mask_table[] = { 0x0FF, 0x1FF, 0x2FF, 0x3FF }; -static uint8 border; /* Border color index */ -static uint8 pending; /* Pending write flag */ -static uint8 code; /* Code register */ -static uint8 dma_type; /* DMA mode */ -static uint16 addr; /* Address register */ -static uint16 addr_latch; /* Latched A15, A14 of address */ -static uint16 sat_base_mask; /* Base bits of SAT */ -static uint16 sat_addr_mask; /* Index bits of SAT */ -static uint16 dma_src; /* DMA source address */ -static uint32 dma_endCycles; /* 68k cycles to DMA end */ -static int dmafill; /* DMA Fill pending flag */ -static int cached_write; /* 2nd part of 32-bit CTRL port write (Genesis mode) or LSB of CRAM data (Game Gear mode) */ -static uint16 fifo[4]; /* FIFO ring-buffer */ -static int fifo_idx; /* FIFO write index */ -static int fifo_byte_access; /* FIFO byte access flag */ -static uint32 fifo_cycles; /* FIFO next access cycle */ -static int *fifo_timing; /* FIFO slots timing table */ +static uint8 border; /* Border color index */ +static uint8 pending; /* Pending write flag */ +static uint8 code; /* Code register */ +static uint16 addr; /* Address register */ +static uint16 addr_latch; /* Latched A15, A14 of address */ +static uint16 sat_base_mask; /* Base bits of SAT */ +static uint16 sat_addr_mask; /* Index bits of SAT */ +static uint16 dma_src; /* DMA source address */ +static int dmafill; /* DMA Fill pending flag */ +static int cached_write; /* 2nd part of 32-bit CTRL port write (Genesis mode) or LSB of CRAM data (Game Gear mode) */ +static uint16 fifo[4]; /* FIFO ring-buffer */ +static int fifo_idx; /* FIFO write index */ +static int fifo_byte_access; /* FIFO byte access flag */ +static uint32 fifo_cycles; /* FIFO next access cycle */ +static int *fifo_timing; /* FIFO slots timing table */ static int hblank_start_cycle; /* HBLANK flag set cycle */ static int hblank_end_cycle; /* HBLANK flag clear cycle */ @@ -630,7 +630,7 @@ void vdp_dma_update(unsigned int cycles) else if (rate == 204) rate = 198; /* 6 refresh slots per line in H40 mode when display is off */ } - /* Remaining DMA cycles */ + /* Available DMA cycles */ if (status & 8) { /* Process DMA until the end of VBLANK */ @@ -645,14 +645,14 @@ void vdp_dma_update(unsigned int cycles) dma_cycles = (mcycles_vdp + MCYCLES_PER_LINE) - cycles; } - /* Remaining DMA bytes for that line */ + /* Max number of DMA bytes to be processed */ dma_bytes = (dma_cycles * rate) / MCYCLES_PER_LINE; #ifdef LOGVDP error("[%d(%d)][%d(%d)] DMA type %d (%d access/line)(%d cycles left)-> %d access (%d remaining) (%x)\n", v_counter, (v_counter + (cycles - mcycles_vdp)/MCYCLES_PER_LINE)%lines_per_frame, cycles, cycles%MCYCLES_PER_LINE,dma_type, rate, dma_cycles, dma_bytes, dma_length, m68k_get_reg(M68K_REG_PC)); #endif - /* Check if DMA can be finished before the end of current line */ + /* Check if DMA can be finished within current timeframe */ if (dma_length < dma_bytes) { /* Adjust remaining DMA bytes */ @@ -660,25 +660,35 @@ void vdp_dma_update(unsigned int cycles) dma_cycles = (dma_bytes * MCYCLES_PER_LINE) / rate; } - /* Update DMA timings */ + /* Set DMA end cycle */ + dma_endCycles = cycles + dma_cycles; +#ifdef LOGVDP + error("-->DMA ends at %d cycles\n", dma_endCycles); +#endif + + /* Check if 68k bus is accessed by DMA */ if (dma_type < 2) { - /* 68K is frozen during DMA from 68k bus */ - m68k.cycles = cycles + dma_cycles; + /* 68K is waiting during DMA from 68k bus */ + m68k.cycles = dma_endCycles; #ifdef LOGVDP - error("-->CPU frozen for %d cycles\n", dma_cycles); + error("-->68K CPU waiting for %d cycles\n", dma_cycles); #endif + + /* Check if Z80 is waiting for 68k bus */ + if (zstate & 4) + { + /* force Z80 to wait until end of DMA timeframe */ + Z80.cycles = dma_endCycles; +#ifdef LOGVDP + error("-->Z80 CPU waiting for %d cycles\n", dma_cycles); +#endif + } } else { - /* Set DMA Busy flag */ + /* Set DMA Busy flag only when 68K can read it */ status |= 0x02; - - /* 68K is still running, set DMA end cycle */ - dma_endCycles = cycles + dma_cycles; -#ifdef LOGVDP - error("-->DMA ends in %d cycles\n", dma_cycles); -#endif } /* Process DMA */ @@ -707,6 +717,9 @@ void vdp_dma_update(unsigned int cycles) vdp_68k_ctrl_w(cached_write); cached_write = -1; } + + /* indicate Z80 is not waiting for 68k bus at the end of DMA timeframe */ + zstate &= ~4; } } } diff --git a/core/vdp_ctrl.h b/core/vdp_ctrl.h index 6f7be19..7f49c89 100644 --- a/core/vdp_ctrl.h +++ b/core/vdp_ctrl.h @@ -52,6 +52,8 @@ extern uint8 hint_pending; extern uint8 vint_pending; extern uint16 status; extern uint32 dma_length; +extern uint32 dma_endCycles; +extern uint8 dma_type; /* Global variables */ extern uint16 ntab;