Sync with non-wide

This commit is contained in:
ds22x 2024-03-22 22:57:44 +01:00
parent d5531ca58d
commit 19e9f0f095
30 changed files with 1140 additions and 548 deletions

View File

@ -19,8 +19,15 @@ Genesis Plus GX 1.7.5 (xx/xx/xxxx) (Eke-Eke)
* added CDC & GFX register polling detection / synchronization
* added configurable CD-DA and PCM outputs mixing volume
* added setting to enable/disable CD access time simulation
* added emulation of Word-RAM access limitations in 2M mode (fixes graphical issues in Marko's Magic Football)
* added limited support for LC89513K extended registers when Wondermega M2, X'Eye, CDX or Multi-Mega BIOS is detected (fixes Krikzz's mcd-verificator CDC REGS tests)
* improved Timer interrupt timings and CDD interrupt accuracy (fixes audio stutters during Popful Mail FMV)
* improved CDC emulation (fixes random freezes during Jeopardy & ESPN Sunday Night NFL intro)
* improved accuracy of Main-CPU & Sub-CPU access to CDC registers (verified on real hardware, cf. Krikzz's mcd-verificator)
* improved accuracy of CDC data transfer to Main-CPU & Sub-CPU (verified on real hardware, cf. Krikzz's mcd-verificator)
* improved accuracy of CDC DMA processing (verified on real hardware, cf. Krikzz's mcd-verificator)
* improved accuracy of CDC decoder processing (verified on real hardware, cf. Krikzz's mcd-verificator)
* improved accuracy of CDC interrupt processing (verified on real hardware, cf. Krikzz's mcd-verificator)
* improved emulation of mirrored memory areas
* improved savestate format
* improved Sub-CPU synchronization with Main-CPU (fixes "Soul Star")
@ -43,11 +50,13 @@ Genesis Plus GX 1.7.5 (xx/xx/xxxx) (Eke-Eke)
* fixed PRG-RAM access from MAIN-CPU side on system reset
* fixed state loading bug when SUB-CPU interrupt is pending
* fixed H-INT vector handling when using Mode 1
* fixed H-INT vector upper word value
* fixed BOOT ROM level 4 interrupt vector upper word value (verified on real hardware, cf. Krikzz's mcd-verificator)
* fixed access to "write-only" communication flags (verified on real hardware by Notaz)
* fixed access to Sub-CPU "read-only" communication registers (fixes Round 5 Boss freeze in Streets of Rage / Sega Classics Arcade Collection)
* fixed byte access to word-only registers (verified on real hardware, cf. Krikzz's mcd-verificator)
* fixed byte access to memory mode, timer and font color registers at even address (verified on real hardware, cf. Krikzz's mcd-verificator)
* fixed byte access to font data registers
* fixed memory mode register bits masking when read from MAIN-CPU and SUB-CPU (verified on real hardware, cf. Krikzz's mcd-verificator)
* fixed pending level 1 interrupts when GFX interrupt is disabled (fixes random freezes when exiting "Batman Returns" option menu)
* fixed CDD seek command again (Final Fight CD freeze with model 2 BIOS)
* fixed CDD status reported during seek/access time (fixes sound effect synchronization issue in Bari Arm)
@ -55,6 +64,7 @@ Genesis Plus GX 1.7.5 (xx/xx/xxxx) (Eke-Eke)
* fixed word access to CDD control register (fixes spurious audio track playback on startup with Mode 1 patched games using MSU-MD driver)
* fixed CD communication registers state on peripheral reset (fixes SUB-CPU side initialization in MSU-MD sample demo and some Mode 1 patched games using MSU-MD driver)
* fixed 32x32 pixels stamp index masking during GFX operation (fixes graphics rotation/scaling effects in "Chuck Rock II - Son of Chuck")
* fixed CDC DMA to PCM RAM when transfer length is not word-aligned
* optimized Sub-CPU / Main-CPU synchronization
[Core/MD]
@ -64,12 +74,16 @@ 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
* added SRAM support for games larger than 8MB
* improved console region auto-detection for a few PAL-only games (The Smurfs Travel the World & Williams Arcade's Greatest Hits)
* improved invalid SRAM header info detection (fixes crashes with some unlicensed hacks/translations)
* improved I2C EEPROM boards emulation accuracy
* improved SVP memory handlers accuracy (fixes Virtua Racing debug mode)
* improved accuracy of 68k access to Z80 bus delays
* fixed Realtec mapper behavior on soft-reset and with TMSS hardware
* fixed Game Genie / Pro Action Replay lock-on support when Mega CD hardware is enabled
* fixed Game Genie / Pro Action Replay lock-on support with games larger than 8MB
@ -109,7 +123,8 @@ Genesis Plus GX 1.7.5 (xx/xx/xxxx) (Eke-Eke)
[Core/CPU]
---------------
* added Z80 wait-states on 68k bus access (fixes Remute Red Eyes demo)
* improved 68k auto-vectored interrupts acknowledge cycle timing accuracy (Bubsy background color corruption during cutscenes)
* improved 68k auto-vectored interrupts acknowledge cycle timing accuracy (fixes Bubsy background color corruption during cutscenes & Pacman 2 - New Adventures crashes during Pac-Jr minigame levels transitions)
* improved 68k MOVEM instruction accuracy (implements extra read cycle from memory as verified on real hardware)
* fixed 68k undocumented behaviors for ABCD/SBCD/NBCD instructions (thanks to Flamewing for his test ROM)
* fixed 68k timing of BTST Dn,#Imm instruction (verified by Flamewing in original microcode)
* fixed 68k timings of ANDI.L #Imm,Dn, ADDQ.W #Imm,An and TAS instructions (cf. Yacht.txt)
@ -132,6 +147,7 @@ Genesis Plus GX 1.7.5 (xx/xx/xxxx) (Eke-Eke)
* improved 4 Way-Play emulation (fixes multitap detection in CD games)
* increased Sega Mouse latency (fixes mouse support in Star Blade)
* fixed TeamPlayer emulation (fixes multitap detection in Gauntlet 4)
* improved Japanese Paddle emulation (fixes Paddle support on control port #2)
[Core/VDP]
---------------
@ -143,9 +159,11 @@ Genesis Plus GX 1.7.5 (xx/xx/xxxx) (Eke-Eke)
* improved Mode 5 sprites parsing accuracy (verified on real hardware)
* improved Mode 5 sprites rendering timings (fixes "Overdrive" demo)
* improved FIFO timings accuracy (fixes "Overdrive" Demo)
* improved FIFO emulation (fixes potential lockup when switching between H32 & H40 mode during active display)
* improved H-Counter accuracy in H32 mode
* improved VDP status timing accuracy
* improved HBLANK flag timing accuracy (verified on real hardware by Nemesis)
* improved VINT timing accuracy in H32 mode (verified on real hardware by Nemesis)
* improved DMA timing accuracy during blanking (verified on real hardware by Mask of Destiny)
* improved accuracy of Master System color palette brightness range (verified against real hardware)
* fixed misaligned buffer writes in Mode 4 when -DALIGN_LONG option is used
@ -154,9 +172,9 @@ Genesis Plus GX 1.7.5 (xx/xx/xxxx) (Eke-Eke)
* fixed Mode 1 rendering (TMS99xx "text" mode)
* fixed Master System II extended video modes sprite parsing (fixes Mega Man 2 demo)
* fixed Game Gear display rendering regression when left/right borders were disabled
* fixed 68k cycles delay on invalid VRAM writes (fixes "Microcosm" intro loop)
* fixed address/code potential corruption by one-instruction execution delay after HV interrupts activation (fixes Pugsy's Pyramids stage boss)
* optimized tile caching
* reverted FIFO access timings hack when using invalid write code value
[Core/Sound]
---------------

View File

@ -3,7 +3,7 @@ Unless otherwise explicitly stated, all code in Genesis Plus GX is released
under the following license:
Copyright (c) 1998-2003 Charles MacDonald
Copyright (c) 2007-2017 Eke-Eke
Copyright (c) 2007-2023 Eke-Eke
Some portions copyright Nicola Salmoria and the MAME team
All rights reserved.
@ -37,6 +37,26 @@ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------------------------
Nuked OPN2 core is distributed under the following license:
/*
* Copyright (C) 2017-2022 Alexey Khokholov (Nuke.YKT)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------------

View File

@ -772,9 +772,14 @@ ifneq (,$(findstring msvc,$(platform)))
CFLAGS += -MT
CXXFLAGS += -MT
endif
ifeq ($(platform), emscripten)
CFLAGS += -O3 -DNDEBUG
CXXFLAGS += -O3 -DNDEBUG
else
CFLAGS += -O2 -DNDEBUG
CXXFLAGS += -O2 -DNDEBUG
endif
endif
ifeq ($(SHARED_LIBVORBIS),)
TREMOR_SRC_DIR := $(CORE_DIR)/core/tremor

View File

@ -2,7 +2,7 @@
* Genesis Plus
* Backup RAM support
*
* Copyright (C) 2007-2021 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:
@ -99,16 +99,25 @@ void sram_init(void)
sram.end = 0x203fff;
}
/* fixe games indicating internal RAM as volatile external RAM (Feng Kuang Tao Hua Yuan) */
/* fixes games indicating internal RAM as volatile external RAM (Feng Kuang Tao Hua Yuan) */
else if (sram.start == 0xff0000)
{
/* backup RAM should be disabled */
sram.on = 0;
}
/* fixe other bad header informations */
/* fixes games with invalid SRAM start address */
else if (sram.start >= 0x800000)
{
/* forces 64KB static RAM mapped to $200000-$20ffff (default) */
sram.start = 0x200000;
sram.end = 0x20ffff;
}
/* fixes games with invalid SRAM end address */
else if ((sram.start > sram.end) || ((sram.end - sram.start) >= 0x10000))
{
/* forces 64KB static RAM max */
sram.end = sram.start + 0xffff;
}
}

View File

@ -2,7 +2,7 @@
* Genesis Plus
* Backup RAM support
*
* Copyright (C) 2007-2021 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:

View File

@ -2,7 +2,7 @@
* Genesis Plus
* CD data controller (LC8951x compatible)
*
* Copyright (C) 2012-2023 Eke-Eke (Genesis Plus GX)
* Copyright (C) 2012-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:
@ -62,8 +62,11 @@
/* STAT3 register bitmask */
#define BIT_VALST 0x80
/* TODO: figure exact DMA transfer rate */
#define DMA_BYTES_PER_LINE 512
/* DMA transfer rate */
/* min. 4 x SUB-CPU cycles (i.e 16 x SCD cycles) per byte (cf https://github.com/MiSTer-devel/MegaCD_MiSTer/tree/master/docs/mcd%20logs) */
/* additional delays caused by SUB-CPU access & periodic refresh (all RAM), GPU operation (Word-RAM only) or PCM playback (PCM RAM only) */
/* are not emulated */
#define DMA_CYCLES_PER_BYTE 16
void cdc_init(void)
{
@ -105,11 +108,14 @@ void cdc_reset(void)
cdc.head[1][2] = 0x00;
cdc.head[1][3] = 0x00;
/* reset CDC cycle counter */
cdc.cycles = 0;
/* reset CDC DMA & decoder cycle counters */
cdc.cycles[0] = cdc.cycles[1] = 0;
/* DMA transfer disabled */
cdc.dma_w = 0;
/* disable CDC DMA */
cdc.dma_w = cdc.halted_dma_w = 0;
/* reset CDC IRQ state */
cdc.irq = 0;
/* clear any pending IRQ */
if (scd.pending & (1 << 5))
@ -147,12 +153,19 @@ int cdc_context_save(uint8 *state)
{
tmp8 = 5;
}
else if (cdc.halted_dma_w == prg_ram_dma_w)
{
tmp8 = 6;
}
else if (cdc.halted_dma_w == word_ram_2M_dma_w)
{
tmp8 = 7;
}
else
{
tmp8 = 0;
}
save_param(&cdc.ifstat, sizeof(cdc.ifstat));
save_param(&cdc.ifctrl, sizeof(cdc.ifctrl));
save_param(&cdc.dbc, sizeof(cdc.dbc));
@ -163,7 +176,6 @@ int cdc_context_save(uint8 *state)
save_param(&cdc.head, sizeof(cdc.head));
save_param(&cdc.stat, sizeof(cdc.stat));
save_param(&cdc.cycles, sizeof(cdc.cycles));
save_param(&cdc.dma_w, sizeof(cdc.dma_w));
save_param(&cdc.ram, sizeof(cdc.ram));
save_param(&tmp8, 1);
@ -185,7 +197,6 @@ int cdc_context_load(uint8 *state)
load_param(&cdc.head, sizeof(cdc.head));
load_param(&cdc.stat, sizeof(cdc.stat));
load_param(&cdc.cycles, sizeof(cdc.cycles));
load_param(&cdc.dma_w, sizeof(cdc.dma_w));
load_param(&cdc.ram, sizeof(cdc.ram));
load_param(&tmp8, 1);
@ -194,35 +205,203 @@ int cdc_context_load(uint8 *state)
{
case 1:
cdc.dma_w = pcm_ram_dma_w;
cdc.halted_dma_w = 0;
break;
case 2:
cdc.dma_w = prg_ram_dma_w;
cdc.halted_dma_w = 0;
break;
case 3:
cdc.dma_w = word_ram_0_dma_w;
cdc.halted_dma_w = 0;
break;
case 4:
cdc.dma_w = word_ram_1_dma_w;
cdc.halted_dma_w = 0;
break;
case 5:
cdc.dma_w = word_ram_2M_dma_w;
cdc.halted_dma_w = 0;
break;
case 6:
cdc.dma_w = 0;
cdc.halted_dma_w = prg_ram_dma_w;
break;
case 7:
cdc.dma_w = 0;
cdc.halted_dma_w = word_ram_2M_dma_w;
break;
default:
cdc.dma_w = 0;
cdc.halted_dma_w = 0;
break;
}
cdc.irq = ~cdc.ifstat & cdc.ifctrl & (BIT_DTEIEN | BIT_DECIEN);
return bufferptr;
}
void cdc_dma_update(void)
void cdc_dma_init(void)
{
/* no effect if data transfer is not started */
if (cdc.ifstat & BIT_DTEN)
return;
/* disable DMA by default */
cdc.dma_w = cdc.halted_dma_w = 0;
/* check data transfer destination */
switch (scd.regs[0x04>>1].byte.h & 0x07)
{
case 2: /* MAIN-CPU host read */
case 3: /* SUB-CPU host read */
{
/* read 16-bit word from CDC RAM buffer (big-endian format) into gate-array register $08 */
/* Note: on real-hardware, 16-bit word is not immediately available, cf. https://github.com/MiSTer-devel/MegaCD_MiSTer/blob/master/docs/mcd%20logs/dma_sub_read.jpg for transfer timings */
scd.regs[0x08>>1].w = READ_WORD(cdc.ram, cdc.dac.w & 0x3ffe);
#ifdef LOG_CDC
error("CDC host read 0x%04x -> 0x%04x (dbc=0x%x) (%X)\n", cdc.dac.w, scd.regs[0x08>>1].w, cdc.dbc.w, s68k.pc);
#endif
/* set DSR bit (gate-array register $04) */
scd.regs[0x04>>1].byte.h |= 0x40;
/* increment data address counter */
cdc.dac.w += 2;
/* decrement data byte counter */
cdc.dbc.w -= 2;
/* end of transfer ? */
if ((int16)cdc.dbc.w < 0)
{
/* reset data byte counter (DBCH bits 4-7 should also be set to 1) */
cdc.dbc.w = 0xffff;
/* clear !DTEN and !DTBSY */
cdc.ifstat |= (BIT_DTBSY | BIT_DTEN);
/* pending Data Transfer End interrupt */
cdc.ifstat &= ~BIT_DTEI;
/* Data Transfer End interrupt enabled ? */
if (cdc.ifctrl & BIT_DTEIEN)
{
/* check end of CDC decoder active period */
if ((cdc.irq & BIT_DECI) && (cdc.cycles[0] > cdc.cycles[1]))
{
/* clear pending decoder interrupt */
cdc.ifstat |= BIT_DECI;
/* update CDC IRQ state */
cdc.irq &= ~BIT_DECI;
}
/* level 5 interrupt triggered only on CDC /INT falling edge with interrupt enabled on gate-array side */
if (!cdc.irq && (scd.regs[0x32>>1].byte.l & 0x20))
{
/* pending level 5 interrupt */
scd.pending |= (1 << 5);
/* update IRQ level */
s68k_update_irq((scd.pending & scd.regs[0x32>>1].byte.l) >> 1);
}
/* update CDC IRQ state */
cdc.irq |= BIT_DTEI;
}
/* set EDT bit (gate-array register $04) */
scd.regs[0x04>>1].byte.h |= 0x80;
}
break;
}
case 4: /* PCM RAM DMA */
{
cdc.dma_w = pcm_ram_dma_w;
break;
}
case 5: /* PRG-RAM DMA */
{
/* check if MAIN-CPU has access to PRG-RAM */
if (scd.regs[0x00].byte.l & 0x02)
{
/* halt DMA to PRG-RAM */
cdc.halted_dma_w = prg_ram_dma_w;
}
else
{
/* enable DMA to PRG-RAM */
cdc.dma_w = prg_ram_dma_w;
}
break;
}
case 7: /* Word-RAM DMA */
{
/* check memory mode */
if (scd.regs[0x02 >> 1].byte.l & 0x04)
{
/* 1M mode */
if (scd.regs[0x02 >> 1].byte.l & 0x01)
{
/* Word-RAM bank 0 is assigned to SUB-CPU */
cdc.dma_w = word_ram_0_dma_w;
}
else
{
/* Word-RAM bank 1 is assigned to SUB-CPU */
cdc.dma_w = word_ram_1_dma_w;
}
}
else
{
/* check if MAIN-CPU has access to 2M Word-RAM */
if (scd.regs[0x02 >> 1].byte.l & 0x01)
{
/* halt DMA to 2M Word-RAM */
cdc.halted_dma_w = word_ram_2M_dma_w;
}
else
{
/* enable DMA to 2M Word-RAM */
cdc.dma_w = word_ram_2M_dma_w;
}
}
break;
}
default: /* invalid */
{
#ifdef LOG_CDC
error("invalid CDC transfer destination (%d)\n", scd.regs[0x04>>1].byte.h & 0x07);
#endif
break;
}
}
}
void cdc_dma_update(unsigned int cycles)
{
/* max number of bytes that can be transfered */
int dma_bytes = (cycles - cdc.cycles[0] + DMA_CYCLES_PER_BYTE - 1) / DMA_CYCLES_PER_BYTE;
/* always process blocks of 8 bytes */
dma_bytes = (dma_bytes / 8) * 8;
/* end of DMA transfer ? */
if (cdc.dbc.w < DMA_BYTES_PER_LINE)
if (cdc.dbc.w < dma_bytes)
{
/* transfer remaining bytes using DMA */
cdc.dma_w(cdc.dbc.w + 1);
/* update DMA cycle counter */
cdc.cycles[0] += (cdc.dbc.w + 1) * DMA_CYCLES_PER_BYTE;
/* reset data byte counter (DBCH bits 4-7 should also be set to 1) */
cdc.dbc.w = 0xffff;
@ -235,15 +414,28 @@ void cdc_dma_update(void)
/* Data Transfer End interrupt enabled ? */
if (cdc.ifctrl & BIT_DTEIEN)
{
/* pending level 5 interrupt */
scd.pending |= (1 << 5);
/* level 5 interrupt enabled ? */
if (scd.regs[0x32>>1].byte.l & 0x20)
/* check end of CDC decoder active period */
if ((cdc.irq & BIT_DECI) && (cdc.cycles[0] > cdc.cycles[1]))
{
/* clear pending decoder interrupt */
cdc.ifstat |= BIT_DECI;
/* update CDC IRQ state */
cdc.irq &= ~BIT_DECI;
}
/* level 5 interrupt triggered only on CDC /INT falling edge with interrupt enabled on gate-array side*/
if (!cdc.irq && (scd.regs[0x32>>1].byte.l & 0x20))
{
/* pending level 5 interrupt */
scd.pending |= (1 << 5);
/* update IRQ level */
s68k_update_irq((scd.pending & scd.regs[0x32>>1].byte.l) >> 1);
}
/* update CDC IRQ state */
cdc.irq |= BIT_DTEI;
}
/* clear DSR bit & set EDT bit (CD register $04) */
@ -252,8 +444,11 @@ void cdc_dma_update(void)
/* SUB-CPU idle on register $04 polling ? */
if (s68k.stopped & (1<<0x04))
{
/* sync SUB-CPU with CDC */
s68k.cycles = scd.cycles;
/* sync SUB-CPU with CDC DMA (only if not already ahead) */
if (s68k.cycles < cdc.cycles[0])
{
s68k.cycles = cdc.cycles[0];
}
/* restart SUB-CPU */
s68k.stopped = 0;
@ -262,16 +457,19 @@ void cdc_dma_update(void)
#endif
}
/* disable DMA transfer */
cdc.dma_w = 0;
/* disable DMA */
cdc.dma_w = cdc.halted_dma_w = 0;
}
else
else if (dma_bytes > 0)
{
/* transfer limited amount of bytes using DMA */
cdc.dma_w(DMA_BYTES_PER_LINE);
cdc.dma_w(dma_bytes);
/* decrement data byte counter */
cdc.dbc.w -= DMA_BYTES_PER_LINE;
cdc.dbc.w -= dma_bytes;
/* update DMA cycle counter */
cdc.cycles[0] += dma_bytes * DMA_CYCLES_PER_BYTE;
}
}
@ -289,18 +487,25 @@ void cdc_decoder_update(uint32 header)
/* pending decoder interrupt */
cdc.ifstat &= ~BIT_DECI;
/* update CDC decoder end cycle (value adjusted for MCD-verificator CDC FLAGS Tests #40 & #41) */
cdc.cycles[1] = s68k.cycles + 269000;
/* decoder interrupt enabled ? */
if (cdc.ifctrl & BIT_DECIEN)
{
/* pending level 5 interrupt */
scd.pending |= (1 << 5);
/* level 5 interrupt enabled ? */
if (scd.regs[0x32>>1].byte.l & 0x20)
/* level 5 interrupt triggered only on CDC /INT falling edge with interrupt enabled on gate-array side */
/* note: only check DTEI as DECI is cleared automatically between decoder interrupt triggering */
if (!(cdc.irq & BIT_DTEI) && (scd.regs[0x32>>1].byte.l & 0x20))
{
/* pending level 5 interrupt */
scd.pending |= (1 << 5);
/* update IRQ level */
s68k_update_irq((scd.pending & scd.regs[0x32>>1].byte.l) >> 1);
}
/* update CDC IRQ state */
cdc.irq |= BIT_DECI;
}
/* buffer RAM write enabled ? */
@ -375,25 +580,28 @@ void cdc_reg_w(unsigned char data)
{
case 0x01: /* IFCTRL */
{
/* pending interrupts ? */
if (((data & BIT_DTEIEN) && !(cdc.ifstat & BIT_DTEI)) ||
((data & BIT_DECIEN) && !(cdc.ifstat & BIT_DECI)))
/* previous CDC IRQ state */
uint8 prev_irq = cdc.irq;
/* check end of CDC decoder active period */
if (s68k.cycles > cdc.cycles[1])
{
/* clear pending decoder interrupt */
cdc.ifstat |= BIT_DECI;
/* update previous CDC IRQ state */
prev_irq &= ~BIT_DECI;
}
/* update CDC IRQ state according to DTEIEN and DECIEN bits */
cdc.irq = ~cdc.ifstat & data & (BIT_DTEIEN | BIT_DECIEN);
/* level 5 interrupt is triggered on CDC /INT falling edge if interrupt enabled on gate-array side */
if (cdc.irq && !prev_irq && (scd.regs[0x32>>1].byte.l & 0x20))
{
/* pending level 5 interrupt */
scd.pending |= (1 << 5);
/* level 5 interrupt enabled ? */
if (scd.regs[0x32>>1].byte.l & 0x20)
{
/* update IRQ level */
s68k_update_irq((scd.pending & scd.regs[0x32>>1].byte.l) >> 1);
}
}
else if (scd.pending & (1 << 5))
{
/* clear pending level 5 interrupts */
scd.pending &= ~(1 << 5);
/* update IRQ level */
s68k_update_irq((scd.pending & scd.regs[0x32>>1].byte.l) >> 1);
}
@ -403,6 +611,9 @@ void cdc_reg_w(unsigned char data)
{
/* clear !DTBSY and !DTEN */
cdc.ifstat |= (BIT_DTBSY | BIT_DTEN);
/* disable DMA */
cdc.dma_w = cdc.halted_dma_w = 0;
}
cdc.ifctrl = data;
@ -436,110 +647,11 @@ void cdc_reg_w(unsigned char data)
/* clear EDT & DSR bits (gate-array register $04) */
scd.regs[0x04>>1].byte.h &= 0x07;
/* setup data transfer destination */
switch (scd.regs[0x04>>1].byte.h & 0x07)
{
case 2: /* MAIN-CPU host read */
case 3: /* SUB-CPU host read */
{
/* read 16-bit word from CDC RAM buffer (big-endian format) into gate-array register $08 */
/* Note: on real-hardware, 16-bit word is not immediately available, cf. https://github.com/MiSTer-devel/MegaCD_MiSTer/blob/master/docs/mcd%20logs/dma_sub_read.jpg for transfer timings */
scd.regs[0x08>>1].w = READ_WORD(cdc.ram, cdc.dac.w & 0x3ffe);
#ifdef LOG_CDC
error("CDC host read 0x%04x -> 0x%04x (dbc=0x%x) (%X)\n", cdc.dac.w, scd.regs[0x08>>1].w, cdc.dbc.w, s68k.pc);
#endif
/* initialize data transfer destination */
cdc_dma_init();
/* set DSR bit (gate-array register $04) */
scd.regs[0x04>>1].byte.h |= 0x40;
/* increment data address counter */
cdc.dac.w += 2;
/* decrement data byte counter */
cdc.dbc.w -= 2;
/* end of transfer ? */
if ((int16)cdc.dbc.w < 0)
{
/* reset data byte counter (DBCH bits 4-7 should also be set to 1) */
cdc.dbc.w = 0xffff;
/* clear !DTEN and !DTBSY */
cdc.ifstat |= (BIT_DTBSY | BIT_DTEN);
/* pending Data Transfer End interrupt */
cdc.ifstat &= ~BIT_DTEI;
/* Data Transfer End interrupt enabled ? */
if (cdc.ifctrl & BIT_DTEIEN)
{
/* pending level 5 interrupt */
scd.pending |= (1 << 5);
/* level 5 interrupt enabled ? */
if (scd.regs[0x32>>1].byte.l & 0x20)
{
/* update IRQ level */
s68k_update_irq((scd.pending & scd.regs[0x32>>1].byte.l) >> 1);
}
}
/* set EDT bit (gate-array register $04) */
scd.regs[0x04>>1].byte.h |= 0x80;
}
break;
}
case 4: /* PCM RAM DMA */
{
cdc.dma_w = pcm_ram_dma_w;
break;
}
case 5: /* PRG-RAM DMA */
{
cdc.dma_w = prg_ram_dma_w;
break;
}
case 7: /* WORD-RAM DMA */
{
/* check memory mode */
if (scd.regs[0x02 >> 1].byte.l & 0x04)
{
/* 1M mode */
if (scd.regs[0x02 >> 1].byte.l & 0x01)
{
/* Word-RAM bank 0 is assigned to SUB-CPU */
cdc.dma_w = word_ram_0_dma_w;
}
else
{
/* Word-RAM bank 1 is assigned to SUB-CPU */
cdc.dma_w = word_ram_1_dma_w;
}
}
else
{
/* 2M mode */
if (scd.regs[0x02 >> 1].byte.l & 0x02)
{
/* only process DMA if Word-RAM is assigned to SUB-CPU */
cdc.dma_w = word_ram_2M_dma_w;
}
}
break;
}
default: /* invalid */
{
#ifdef LOG_CDC
error("invalid CDC transfer destination (%d)\n", scd.regs[0x04>>1].byte.h & 0x07);
#endif
break;
}
}
/* initialize DMA cycle counter */
cdc.cycles[0] = s68k.cycles;
}
break;
@ -550,17 +662,8 @@ void cdc_reg_w(unsigned char data)
/* clear pending data transfer end interrupt */
cdc.ifstat |= BIT_DTEI;
#if 0
/* no pending decoder interrupt ? */
if ((cdc.ifstat | BIT_DECI) || !(cdc.ifctrl & BIT_DECIEN))
{
/* clear pending level 5 interrupt */
scd.pending &= ~(1 << 5);
/* update IRQ level */
s68k_update_irq((scd.pending & scd.regs[0x32>>1].byte.l) >> 1);
}
#endif
/* update CDC IRQ state */
cdc.irq &= ~BIT_DTEI;
break;
}
@ -577,6 +680,16 @@ void cdc_reg_w(unsigned char data)
/* set CRCOK bit only if decoding is enabled */
cdc.stat[0] = data & BIT_DECEN;
/* decoding disabled ? */
if (!(data & BIT_DECEN))
{
/* clear pending decoder interrupt */
cdc.ifstat |= BIT_DECI;
/* update CDC IRQ state */
cdc.irq &= ~BIT_DECI;
}
/* update STAT2 register */
if (data & BIT_AUTORQ)
{
@ -623,7 +736,7 @@ void cdc_reg_w(unsigned char data)
cdc_reset();
break;
default: /* unemulated registers*/
default: /* unemulated registers */
break;
}
@ -642,6 +755,16 @@ unsigned char cdc_reg_r(void)
{
case 0x01: /* IFSTAT */
{
/* check end of CDC decoder active period */
if (s68k.cycles > cdc.cycles[1])
{
/* clear pending decoder interrupt */
cdc.ifstat |= BIT_DECI;
/* update CDC IRQ state */
cdc.irq &= ~BIT_DECI;
}
data = cdc.ifstat;
break;
}
@ -734,18 +857,8 @@ unsigned char cdc_reg_r(void)
/* clear pending decoder interrupt */
cdc.ifstat |= BIT_DECI;
#if 0
/* no pending data transfer end interrupt */
if ((cdc.ifstat | BIT_DTEI) || !(cdc.ifctrl & BIT_DTEIEN))
{
/* clear pending level 5 interrupt */
scd.pending &= ~(1 << 5);
/* update IRQ level */
s68k_update_irq((scd.pending & scd.regs[0x32>>1].byte.l) >> 1);
}
#endif
/* update CDC IRQ state */
cdc.irq &= ~BIT_DECI;
break;
}
@ -757,7 +870,7 @@ unsigned char cdc_reg_r(void)
}
#ifdef LOG_CDC
error("CDC register %d read 0x%02X (%X)\n", scd.regs[0x04>>1].byte.l, data, s68k.pc);
error("CDC register %d read 0x%02X (%X)\n", scd.regs[0x04>>1].byte.l, data, s68k.pc);
#endif
/* increment address register (except when register #0 is selected) */
@ -769,13 +882,13 @@ unsigned char cdc_reg_r(void)
return data;
}
unsigned short cdc_host_r(void)
unsigned short cdc_host_r(uint8 cpu_access)
{
/* read CDC buffered data (gate-array register $08) */
uint16 data = scd.regs[0x08>>1].w;
/* check if host data transfer is enabled */
if (scd.regs[0x04>>1].byte.h & 0x40)
/* check if host data transfer is started for selected CPU */
if ((scd.regs[0x04>>1].byte.h & 0x47) == cpu_access)
{
/* check if EDT bit (gate-array register $04) is set (host data transfer is finished) */
if (scd.regs[0x04>>1].byte.h & 0x80)
@ -813,15 +926,28 @@ unsigned short cdc_host_r(void)
/* Data Transfer End interrupt enabled ? */
if (cdc.ifctrl & BIT_DTEIEN)
{
/* pending level 5 interrupt */
scd.pending |= (1 << 5);
/* level 5 interrupt enabled ? */
if (scd.regs[0x32>>1].byte.l & 0x20)
/* check end of CDC decoder active period */
if ((cdc.irq & BIT_DECI) && (cdc.cycles[0] > cdc.cycles[1]))
{
/* clear pending decoder interrupt */
cdc.ifstat |= BIT_DECI;
/* update CDC IRQ state */
cdc.irq &= ~BIT_DECI;
}
/* level 5 interrupt triggered only on CDC /INT falling edge with interrupt enabled on gate-array side */
if (!cdc.irq && (scd.regs[0x32>>1].byte.l & 0x20))
{
/* pending level 5 interrupt */
scd.pending |= (1 << 5);
/* update IRQ level */
s68k_update_irq((scd.pending & scd.regs[0x32>>1].byte.l) >> 1);
}
/* update CDC IRQ state */
cdc.irq |= BIT_DTEI;
}
/* set EDT bit (gate-array register $04) */

View File

@ -2,7 +2,7 @@
* Genesis Plus
* CD data controller (LC8951x compatible)
*
* Copyright (C) 2012-2023 Eke-Eke (Genesis Plus GX)
* Copyright (C) 2012-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:
@ -40,6 +40,9 @@
#define cdc scd.cdc_hw
#define CDC_MAIN_CPU_ACCESS 0x42
#define CDC_SUB_CPU_ACCESS 0x43
/* CDC hardware */
typedef struct
{
@ -52,10 +55,12 @@ typedef struct
uint8 ctrl[2];
uint8 head[2][4];
uint8 stat[4];
int cycles;
void (*dma_w)(unsigned int length); /* DMA transfer callback */
int cycles[2];
void (*dma_w)(unsigned int length); /* active DMA callback */
void (*halted_dma_w)(unsigned int length); /* halted DMA callback */
uint8 ram[0x4000 + 2352]; /* 16K external RAM (with one block overhead to handle buffer overrun) */
uint8 ar_mask;
uint8 irq; /* invert of CDC /INT output */
} cdc_t;
/* Function prototypes */
@ -63,10 +68,11 @@ extern void cdc_init(void);
extern void cdc_reset(void);
extern int cdc_context_save(uint8 *state);
extern int cdc_context_load(uint8 *state);
extern void cdc_dma_update(void);
extern void cdc_dma_init(void);
extern void cdc_dma_update(unsigned int cycles);
extern void cdc_decoder_update(uint32 header);
extern void cdc_reg_w(unsigned char data);
extern unsigned char cdc_reg_r(void);
extern unsigned short cdc_host_r(void);
extern unsigned short cdc_host_r(uint8 cpu_access);
#endif

View File

@ -2,7 +2,7 @@
* Genesis Plus
* CD drive processor & CD-DA fader
*
* Copyright (C) 2012-2023 Eke-Eke (Genesis Plus GX)
* Copyright (C) 2012-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:
@ -1855,9 +1855,14 @@ void cdd_update(void)
scd.regs[0x36>>1].byte.h = 0x01;
}
}
else
{
/* CDC decoder is still running while disc is not being read (fixes MCD-verificator CDC Flags Test #30) */
cdc_decoder_update(0);
}
/* scanning disc */
else if (cdd.status == CD_SCAN)
if (cdd.status == CD_SCAN)
{
/* current track index */
int index = cdd.index;

View File

@ -2,7 +2,7 @@
* Genesis Plus
* CD drive processor & CD-DA fader
*
* Copyright (C) 2012-2023 Eke-Eke (Genesis Plus GX)
* Copyright (C) 2012-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:

View File

@ -2,7 +2,7 @@
* Genesis Plus
* CD graphics processor
*
* Copyright (C) 2012-2023 Eke-Eke (Genesis Plus GX)
* Copyright (C) 2012-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:
@ -683,7 +683,7 @@ void gfx_update(int cycles)
/* update Vdot remaining size */
scd.regs[0x64>>1].byte.l -= lines;
/* increment cycle counter */
/* update cycle counter */
gfx.cycles += lines * gfx.cyclesPerLine;
}
else
@ -694,14 +694,20 @@ void gfx_update(int cycles)
/* clear Vdot remaining size */
scd.regs[0x64>>1].byte.l = 0;
/* update cycle counter */
gfx.cycles += lines * gfx.cyclesPerLine;
/* end of graphics operation */
scd.regs[0x58>>1].byte.h = 0;
/* SUB-CPU idle on register $58 polling ? */
if (s68k.stopped & (1<<0x08))
{
/* sync SUB-CPU with GFX chip */
s68k.cycles = scd.cycles;
/* sync SUB-CPU with GFX chip (only if not already ahead) */
if (s68k.cycles < gfx.cycles)
{
s68k.cycles = gfx.cycles;
}
/* restart SUB-CPU */
s68k.stopped = 0;

View File

@ -2,7 +2,7 @@
* Genesis Plus
* CD graphics processor
*
* Copyright (C) 2012-2023 Eke-Eke (Genesis Plus GX)
* Copyright (C) 2012-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:

View File

@ -2,7 +2,7 @@
* Genesis Plus
* Mega CD / Sega CD hardware
*
* Copyright (C) 2012-2023 Eke-Eke (Genesis Plus GX)
* Copyright (C) 2012-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:
@ -84,7 +84,7 @@ static void s68k_lockup_w_8 (unsigned int address, unsigned int data)
#ifdef LOGERROR
error ("[SUB 68k] Lockup write8 %08X = %02X (%08X)\n", address, data, s68k.pc);
#endif
s68k_pulse_wait();
s68k_pulse_wait(address, 1);
}
static void s68k_lockup_w_16 (unsigned int address, unsigned int data)
@ -92,7 +92,7 @@ static void s68k_lockup_w_16 (unsigned int address, unsigned int data)
#ifdef LOGERROR
error ("[SUB 68k] Lockup write16 %08X = %04X (%08X)\n", address, data, s68k.pc);
#endif
s68k_pulse_wait();
s68k_pulse_wait(address, 1);
}
static unsigned int s68k_lockup_r_8 (unsigned int address)
@ -100,7 +100,7 @@ static unsigned int s68k_lockup_r_8 (unsigned int address)
#ifdef LOGERROR
error ("[SUB 68k] Lockup read8 %08X.b (%08X)\n", address, s68k.pc);
#endif
s68k_pulse_wait();
s68k_pulse_wait(address, 0);
return 0xff;
}
@ -109,7 +109,7 @@ static unsigned int s68k_lockup_r_16 (unsigned int address)
#ifdef LOGERROR
error ("[SUB 68k] Lockup read16 %08X.w (%08X)\n", address, s68k.pc);
#endif
s68k_pulse_wait();
s68k_pulse_wait(address, 0);
return 0xffff;
}
@ -135,12 +135,6 @@ void prg_ram_dma_w(unsigned int length)
/* update DMA source address */
cdc.dac.w += (words << 1);
/* check PRG-RAM write protected area */
if (dst_index < (scd.regs[0x02>>1].byte.h << 9))
{
return;
}
/* DMA transfer */
while (words--)
{
@ -682,7 +676,7 @@ static unsigned int scd_read_word(unsigned int address)
/* CDC host data (word access only ?) */
if (address == 0x08)
{
return cdc_host_r();
return cdc_host_r(CDC_SUB_CPU_ACCESS);
}
/* LED & RESET status */
@ -810,6 +804,8 @@ INLINE void word_ram_switch(uint8 mode)
}
}
static void scd_write_word(unsigned int address, unsigned int data);
static void scd_write_byte(unsigned int address, unsigned int data)
{
/* PCM area (8K) mirrored into $xF0000-$xF7FFF */
@ -1005,6 +1001,17 @@ static void scd_write_byte(unsigned int address, unsigned int data)
gfx_update(s68k.cycles);
}
/* check if CDC DMA to 2M Word-RAM is running */
if (cdc.dma_w == word_ram_2M_dma_w)
{
/* synchronize CDC DMA with SUB-CPU */
cdc_dma_update(s68k.cycles);
/* halt CDC DMA to 2M Word-RAM (if still running) */
cdc.halted_dma_w = cdc.dma_w;
cdc.dma_w = 0;
}
/* Word-RAM is returned to MAIN-CPU */
scd.dmna = 0;
@ -1040,6 +1047,24 @@ static void scd_write_byte(unsigned int address, unsigned int data)
return;
}
case 0x04: /* CDC mode */
{
scd.regs[0x04 >> 1].byte.h = data & 0x07;
/* synchronize CDC DMA (if running) with SUB-CPU */
if (cdc.dma_w)
{
cdc_dma_update(s68k.cycles);
}
/* reinitialize CDC data transfer destination (verified on real hardware, cf. Krikzz's mcd-verificator) */
cdc_dma_init();
/* reset CDC DMA address (verified on real hardware, cf. Krikzz's mcd-verificator) */
scd.regs[0x0a >> 1].w = 0;
return;
}
case 0x05: /* CDC register address */
{
scd.regs[0x04 >> 1].byte.l = data & cdc.ar_mask;
@ -1102,29 +1127,40 @@ static void scd_write_byte(unsigned int address, unsigned int data)
default:
{
uint16 reg_16 = address & 0x1fe;
/* SUB-CPU communication words */
if ((address & 0x1f0) == 0x20)
if ((reg_16 >= 0x20) && (reg_16 <= 0x2f))
{
s68k_poll_sync(1 << ((address - 0x10) & 0x1f));
}
/* MAIN-CPU communication words */
else if ((address & 0x1f0) == 0x10)
else if ((reg_16 >= 0x10) && (reg_16 <= 0x1f))
{
/* read-only (Sega Classic Arcade Collection) */
return;
}
/* word-only registers */
else if (((reg_16 >= 0x08) && (reg_16 <= 0x0d)) ||
((reg_16 >= 0x34) && (reg_16 <= 0x35)) ||
((reg_16 >= 0x5a) && (reg_16 <= 0x67)))
{
scd_write_word(address, (data << 8) | (data & 0xff));
return;
}
/* default registers */
if (address & 1)
{
/* register LSB */
scd.regs[(address >> 1) & 0xff].byte.l = data;
scd.regs[reg_16 >> 1].byte.l = data;
return;
}
/* register MSB */
scd.regs[(address >> 1) & 0xff].byte.h = data;
scd.regs[reg_16 >> 1].byte.h = data;
return;
}
}
@ -1314,6 +1350,17 @@ static void scd_write_word(unsigned int address, unsigned int data)
gfx_update(s68k.cycles);
}
/* check if CDC DMA to 2M Word-RAM is running */
if (cdc.dma_w == word_ram_2M_dma_w)
{
/* synchronize CDC DMA with SUB-CPU */
cdc_dma_update(s68k.cycles);
/* halt CDC DMA to 2M Word-RAM (if still running) */
cdc.halted_dma_w = cdc.dma_w;
cdc.dma_w = 0;
}
/* Word-RAM is returned to MAIN-CPU */
scd.dmna = 0;
@ -1352,6 +1399,18 @@ static void scd_write_word(unsigned int address, unsigned int data)
case 0x04: /* CDC mode & register address */
{
scd.regs[0x04 >> 1].w = data & (0x0700 | cdc.ar_mask);
/* synchronize CDC DMA (if running) with SUB-CPU */
if (cdc.dma_w)
{
cdc_dma_update(s68k.cycles);
}
/* reinitialize CDC data transfer destination (verified on real hardware, cf. Krikzz's mcd-verificator) */
cdc_dma_init();
/* reset CDC DMA address (verified on real hardware, cf. Krikzz's mcd-verificator) */
scd.regs[0x0a >> 1].w = 0;
return;
}
@ -1361,7 +1420,14 @@ static void scd_write_word(unsigned int address, unsigned int data)
return;
}
case 0x0c: /* Stopwatch (word access only) */
case 0x08: /* CDC host data */
{
/* CDC data is also read (although unused) on write access (verified on real hardware, cf. Krikzz's mcd-verificator) */
cdc_host_r(CDC_SUB_CPU_ACCESS);
return;
}
case 0x0c: /* Stopwatch */
{
/* synchronize the counter with SUB-CPU */
int ticks = (s68k.cycles - scd.stopwatch) / TIMERS_SCYCLES_RATIO;
@ -1838,16 +1904,6 @@ void scd_update(unsigned int cycles)
int s68k_run_cycles;
int s68k_end_cycles = scd.cycles + SCYCLES_PER_LINE;
/* update CDC DMA transfer */
if (cdc.dma_w)
{
/* check if Word-RAM is returned to SUB-CPU in 2M mode */
if ((cdc.dma_w != word_ram_2M_dma_w) || scd.dmna)
{
cdc_dma_update();
}
}
/* run both CPU in sync until end of line */
do
{
@ -1923,10 +1979,15 @@ void scd_update(unsigned int cycles)
}
while ((m68k.cycles < cycles) || (s68k.cycles < s68k_end_cycles));
/* GFX processing */
/* update CDC DMA processing (if running) */
if (cdc.dma_w)
{
cdc_dma_update(scd.cycles);
}
/* update GFX processing (if started) */
if (scd.regs[0x58>>1].byte.h & 0x80)
{
/* update graphics operation if running */
gfx_update(scd.cycles);
}
}
@ -1940,9 +2001,11 @@ void scd_end_frame(unsigned int cycles)
/* adjust Stopwatch counter for next frame (can be negative) */
scd.stopwatch += (ticks * TIMERS_SCYCLES_RATIO) - cycles;
/* adjust SUB-CPU & GPU cycle counters for next frame */
s68k.cycles -= cycles;
gfx.cycles -= cycles;
/* adjust SUB-CPU, GPU and CDC cycle counters for next frame */
s68k.cycles -= cycles;
gfx.cycles -= cycles;
cdc.cycles[0] -= cycles;
cdc.cycles[1] -= cycles;
/* reset CPU registers polling */
m68k.poll.cycle = 0;
@ -2300,24 +2363,18 @@ int scd_68k_irq_ack(int level)
error("INT ack level %d (%X)\n", level, s68k.pc);
#endif
#if 0
/* level 5 interrupt is normally acknowledged by CDC */
if (level != 5)
#endif
/* clear pending interrupt flag */
scd.pending &= ~(1 << level);
/* level 2 interrupt acknowledge */
if (level == 2)
{
/* clear pending interrupt flag */
scd.pending &= ~(1 << level);
/* level 2 interrupt acknowledge */
if (level == 2)
{
/* clear IFL2 flag */
scd.regs[0x00].byte.h &= ~0x01;
}
/* update IRQ level */
s68k_update_irq((scd.pending & scd.regs[0x32>>1].byte.l) >> 1);
/* clear IFL2 flag */
scd.regs[0x00].byte.h &= ~0x01;
}
/* update IRQ level */
s68k_update_irq((scd.pending & scd.regs[0x32>>1].byte.l) >> 1);
return M68K_INT_ACK_AUTOVECTOR;
}

View File

@ -2,7 +2,7 @@
* Genesis Plus
* Mega-CD / Sega CD hardware
*
* Copyright (C) 2012-2023 Eke-Eke (Genesis Plus GX)
* Copyright (C) 2012-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:

View File

@ -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 */

View File

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

View File

@ -238,12 +238,14 @@ typedef struct
cpu_idle_t poll; /* polling detection */
uint cycles; /* current master cycle count */
sint cycles; /* current master cycle count */
sint refresh_cycles; /* external bus refresh cycle */
uint cycle_end; /* aimed master cycle count for current execution frame */
uint dar[16]; /* Data and Address Registers */
uint pc; /* Program Counter */
uint prev_pc; /* Previous Program Counter */
uint prev_ar[8]; /* Previous Address Registers */
uint sp[5]; /* User and Interrupt Stack Pointers */
uint ir; /* Instruction Register */
uint t1_flag; /* Trace 1 */
@ -383,10 +385,8 @@ extern void m68k_clear_halt(void);
extern void s68k_pulse_halt(void);
extern void s68k_clear_halt(void);
/* Put the CPU in waiting state as if DTACK pin is not asserted during bus access */
extern void m68k_pulse_wait(void);
extern void m68k_clear_wait(void);
extern void s68k_pulse_wait(void);
/* Put the CPU in waiting state until DTACK pin is asserted during bus access */
extern void s68k_pulse_wait(unsigned int address, unsigned int write_access);
extern void s68k_clear_wait(void);
/* Peek at the internals of a CPU context. This can either be a context

View File

@ -296,12 +296,16 @@ void m68k_run(unsigned int cycles)
cpu_hook(HOOK_M68K_E, 0, REG_PC, 0);
#endif
/* Save current instruction PC */
m68k.prev_pc = REG_PC;
/* Decode next instruction */
REG_IR = m68ki_read_imm_16();
/* 68K bus access refresh delay (Mega Drive / Genesis specific) */
if (m68k.cycles >= (m68k.refresh_cycles + (128*7)))
{
m68k.refresh_cycles = (m68k.cycles / (128*7)) * (128*7);
m68k.cycles += (2*7);
}
/* Execute instruction */
m68ki_instruction_jump_table[REG_IR]();
USE_CYCLES(CYC_INSTRUCTION[REG_IR]);
@ -385,6 +389,9 @@ void m68k_pulse_reset(void)
#endif
USE_CYCLES(CYC_EXCEPTION[EXCEPTION_RESET]);
/* Synchronize 68k bus refresh mechanism (Mega Drive / Genesis specific) */
m68k.refresh_cycles = (m68k.cycles / (128*7)) * (128*7);
}
void m68k_pulse_halt(void)
@ -399,24 +406,6 @@ void m68k_clear_halt(void)
CPU_STOPPED &= ~STOP_LEVEL_HALT;
}
void m68k_pulse_wait(void)
{
/* Hold the DTACK line on the CPU */
CPU_STOPPED |= STOP_LEVEL_WAIT;
/* End CPU execution */
m68k.cycles = m68k.cycle_end - m68k_cycles();
/* Rollback current instruction (memory access will be executed once /DTACK is asserted) */
m68k.pc = m68k.prev_pc;
}
void m68k_clear_wait(void)
{
/* Assert the DTACK line on the CPU */
CPU_STOPPED &= ~STOP_LEVEL_WAIT;
}
/* ======================================================================== */
/* ============================== END OF FILE ============================= */
/* ======================================================================== */

View File

@ -616,13 +616,13 @@ static const uint16 m68ki_exception_cycle_table[256] =
4*MUL, /* 22: RESERVED */
4*MUL, /* 23: RESERVED */
44*MUL, /* 24: Spurious Interrupt */
54*MUL, /* 25: Level 1 Interrupt Autovector */
54*MUL, /* 26: Level 2 Interrupt Autovector */
54*MUL, /* 27: Level 3 Interrupt Autovector */
54*MUL, /* 28: Level 4 Interrupt Autovector */
54*MUL, /* 29: Level 5 Interrupt Autovector */
54*MUL, /* 30: Level 6 Interrupt Autovector */
54*MUL, /* 31: Level 7 Interrupt Autovector */
44*MUL, /* 25: Level 1 Interrupt Autovector */
44*MUL, /* 26: Level 2 Interrupt Autovector */
44*MUL, /* 27: Level 3 Interrupt Autovector */
44*MUL, /* 28: Level 4 Interrupt Autovector */
44*MUL, /* 29: Level 5 Interrupt Autovector */
44*MUL, /* 30: Level 6 Interrupt Autovector */
44*MUL, /* 31: Level 7 Interrupt Autovector */
34*MUL, /* 32: TRAP #0 -- ASG: chanaged from 38 */
34*MUL, /* 33: TRAP #1 */
34*MUL, /* 34: TRAP #2 */
@ -1374,6 +1374,11 @@ INLINE void m68ki_exception_address_error(void)
}
#endif
/* See MC68000 User Manual appendix B for autovectors interrupts processing time */
/* 44 cycles + N wait-state cycles where N depends on CPU clock alignement with internal E clock (corresponding to CPU clock / 10) when interrupt ack cycle starts */
/* N minimal/maximal values are 6..15 cycles according to manual but real hardware measure apparently indicate 5..14 cycles (cf https://gendev.spritesmind.net/forum/viewtopic.php?f=2&t=2202&p=27485) */
static uint m68ki_cycle_interrupts[10] = {50*MUL, 59*MUL, 58*MUL, 57*MUL, 56*MUL, 55*MUL, 54*MUL, 53*MUL, 52*MUL, 51*MUL};
/* Service an interrupt request and start exception processing */
INLINE void m68ki_exception_interrupt(uint int_level)
{
@ -1384,7 +1389,7 @@ INLINE void m68ki_exception_interrupt(uint int_level)
#endif /* M68K_EMULATE_ADDRESS_ERROR */
/* Turn off the stopped state */
CPU_STOPPED &= STOP_LEVEL_HALT;
CPU_STOPPED &= (STOP_LEVEL_HALT | STOP_LEVEL_WAIT);
/* If we are halted, don't do anything */
if(CPU_STOPPED)
@ -1416,7 +1421,7 @@ INLINE void m68ki_exception_interrupt(uint int_level)
m68ki_jump(new_pc);
/* Update cycle count now */
USE_CYCLES(CYC_EXCEPTION[vector]);
USE_CYCLES(m68ki_cycle_interrupts[(m68ki_cpu.cycles / MUL) % 10]);
}
/* ASG: Check for interrupts */

View File

@ -8,7 +8,7 @@ INLINE void UseDivuCycles(uint32 dst, uint32 src)
int i;
/* minimum cycle time */
uint mcycles = 38 * MUL;
uint mcycles = 76 * MUL;
/* 16-bit divisor */
src <<= 16;
@ -27,27 +27,33 @@ INLINE void UseDivuCycles(uint32 dst, uint32 src)
{
/* shift dividend and add two cycles */
dst <<= 1;
mcycles += (2 * MUL);
mcycles += (4 * MUL);
if (dst >= src)
{
/* apply divisor and remove one cycle */
dst -= src;
mcycles -= 1 * MUL;
mcycles -= 2 * MUL;
}
}
}
USE_CYCLES(mcycles << 1);
USE_CYCLES(mcycles);
/* one 68K bus refresh cycle should be skipped if instruction processing time is longer than refresh period (128 CPU cycles on Mega Drive / Genesis) */
if (mcycles >= (128*MUL))
{
m68ki_cpu.refresh_cycles += (128*MUL);
}
}
INLINE void UseDivsCycles(sint32 dst, sint16 src)
{
/* minimum cycle time */
uint mcycles = 6 * MUL;
uint mcycles = 12 * MUL;
/* negative dividend */
if (dst < 0) mcycles += 1 * MUL;
if (dst < 0) mcycles += 2 * MUL;
if ((abs(dst) >> 16) < abs(src))
{
@ -57,30 +63,36 @@ INLINE void UseDivsCycles(sint32 dst, sint16 src)
uint32 quotient = abs(dst) / abs(src);
/* add default cycle time */
mcycles += (55 * MUL);
mcycles += (110 * MUL);
/* positive divisor */
if (src >= 0)
{
/* check dividend sign */
if (dst >= 0) mcycles -= 1 * MUL;
else mcycles += 1 * MUL;
if (dst >= 0) mcycles -= 2 * MUL;
else mcycles += 2 * MUL;
}
/* check higher 15-bits of quotient */
for (i=0; i<15; i++)
{
quotient >>= 1;
if (!(quotient & 1)) mcycles += 1 * MUL;
if (!(quotient & 1)) mcycles += 2 * MUL;
}
}
else
{
/* absolute overflow */
mcycles += (2 * MUL);
mcycles += (4 * MUL);
}
USE_CYCLES(mcycles << 1);
USE_CYCLES(mcycles);
/* one 68K bus refresh cycle should be skipped if instruction processing time is longer than refresh period (128 CPU cycles on Mega Drive / Genesis) */
if (mcycles >= (128*MUL))
{
m68ki_cpu.refresh_cycles += (128*MUL);
}
}
INLINE void UseMuluCycles(uint16 src)
@ -1868,6 +1880,9 @@ static void m68k_op_addq_8_d(void)
FLAG_Z = MASK_OUT_ABOVE_8(res);
*r_dst = MASK_OUT_BELOW_8(*r_dst) | FLAG_Z;
/* reset idle loop detection (fixes cases where instruction is used in tight counter incrementing loop) */
m68ki_cpu.poll.detected = 0;
}
@ -2028,6 +2043,9 @@ static void m68k_op_addq_16_d(void)
FLAG_Z = MASK_OUT_ABOVE_16(res);
*r_dst = MASK_OUT_BELOW_16(*r_dst) | FLAG_Z;
/* reset idle loop detection (fixes cases where instruction is used in tight counter incrementing loop) */
m68ki_cpu.poll.detected = 0;
}
@ -2164,6 +2182,9 @@ static void m68k_op_addq_32_d(void)
FLAG_Z = MASK_OUT_ABOVE_32(res);
*r_dst = FLAG_Z;
/* reset idle loop detection (fixes cases where instruction is used in tight counter incrementing loop) */
m68ki_cpu.poll.detected = 0;
}
@ -15222,6 +15243,9 @@ static void m68k_op_movem_16_er_pi(void)
}
AY = ea;
/* MOVEM extra read cycle (can have side effect if target hardware is impacted by read access) */
m68ki_read_16(ea);
USE_CYCLES(count * CYC_MOVEM_W);
}
@ -15241,6 +15265,9 @@ static void m68k_op_movem_16_er_pcdi(void)
count++;
}
/* MOVEM extra read cycle (can have side effect if target hardware is impacted by read access) */
m68ki_read_16(ea);
USE_CYCLES(count * CYC_MOVEM_W);
}
@ -15260,6 +15287,9 @@ static void m68k_op_movem_16_er_pcix(void)
count++;
}
/* MOVEM extra read cycle (can have side effect if extra address is not mapped or mapped to hardware impacted by read access) */
m68ki_read_16(ea);
USE_CYCLES(count * CYC_MOVEM_W);
}
@ -15279,6 +15309,9 @@ static void m68k_op_movem_16_er_ai(void)
count++;
}
/* MOVEM extra read cycle (can have side effect if extra address is not mapped or mapped to hardware impacted by read access) */
m68ki_read_16(ea);
USE_CYCLES(count * CYC_MOVEM_W);
}
@ -15298,6 +15331,9 @@ static void m68k_op_movem_16_er_di(void)
count++;
}
/* MOVEM extra read cycle (can have side effect if extra address is not mapped or mapped to hardware impacted by read access) */
m68ki_read_16(ea);
USE_CYCLES(count * CYC_MOVEM_W);
}
@ -15317,6 +15353,9 @@ static void m68k_op_movem_16_er_ix(void)
count++;
}
/* MOVEM extra read cycle (can have side effect if extra address is not mapped or mapped to hardware impacted by read access) */
m68ki_read_16(ea);
USE_CYCLES(count * CYC_MOVEM_W);
}
@ -15336,6 +15375,9 @@ static void m68k_op_movem_16_er_aw(void)
count++;
}
/* MOVEM extra read cycle (can have side effect if extra address is not mapped or mapped to hardware impacted by read access) */
m68ki_read_16(ea);
USE_CYCLES(count * CYC_MOVEM_W);
}
@ -15355,6 +15397,9 @@ static void m68k_op_movem_16_er_al(void)
count++;
}
/* MOVEM extra read cycle (can have side effect if extra address is not mapped or mapped to hardware impacted by read access) */
m68ki_read_16(ea);
USE_CYCLES(count * CYC_MOVEM_W);
}
@ -15375,6 +15420,9 @@ static void m68k_op_movem_32_er_pi(void)
}
AY = ea;
/* MOVEM extra read cycle (can have side effect if extra address is not mapped or mapped to hardware impacted by read access) */
m68ki_read_16(ea);
USE_CYCLES(count * CYC_MOVEM_L);
}
@ -15394,6 +15442,9 @@ static void m68k_op_movem_32_er_pcdi(void)
count++;
}
/* MOVEM extra read cycle (can have side effect if extra address is not mapped or mapped to hardware impacted by read access) */
m68ki_read_16(ea);
USE_CYCLES(count * CYC_MOVEM_L);
}
@ -15413,6 +15464,9 @@ static void m68k_op_movem_32_er_pcix(void)
count++;
}
/* MOVEM extra read cycle (can have side effect if extra address is not mapped or mapped to hardware impacted by read access) */
m68ki_read_16(ea);
USE_CYCLES(count * CYC_MOVEM_L);
}
@ -15432,6 +15486,9 @@ static void m68k_op_movem_32_er_ai(void)
count++;
}
/* MOVEM extra read cycle (can have side effect if extra address is not mapped or mapped to hardware impacted by read access) */
m68ki_read_16(ea);
USE_CYCLES(count * CYC_MOVEM_L);
}
@ -15451,6 +15508,9 @@ static void m68k_op_movem_32_er_di(void)
count++;
}
/* MOVEM extra read cycle (can have side effect if extra address is not mapped or mapped to hardware impacted by read access) */
m68ki_read_16(ea);
USE_CYCLES(count * CYC_MOVEM_L);
}
@ -15470,6 +15530,9 @@ static void m68k_op_movem_32_er_ix(void)
count++;
}
/* MOVEM extra read cycle (can have side effect if extra address is not mapped or mapped to hardware impacted by read access) */
m68ki_read_16(ea);
USE_CYCLES(count * CYC_MOVEM_L);
}
@ -15489,6 +15552,9 @@ static void m68k_op_movem_32_er_aw(void)
count++;
}
/* MOVEM extra read cycle (can have side effect if extra address is not mapped or mapped to hardware impacted by read access) */
m68ki_read_16(ea);
USE_CYCLES(count * CYC_MOVEM_L);
}
@ -15508,6 +15574,9 @@ static void m68k_op_movem_32_er_al(void)
count++;
}
/* MOVEM extra read cycle (can have side effect if extra address is not mapped or mapped to hardware impacted by read access) */
m68ki_read_16(ea);
USE_CYCLES(count * CYC_MOVEM_L);
}
@ -18635,6 +18704,7 @@ static void m68k_op_reset(void)
{
m68ki_output_reset() /* auto-disable (see m68kcpu.h) */
USE_CYCLES(CYC_RESET);
m68ki_cpu.refresh_cycles += (128*MUL); /* skip one 68K bus refresh cycle as instruction processing time is longer than refresh period (128 CPU cycles on Mega Drive / Genesis) */
return;
}
m68ki_exception_privilege_violation();

View File

@ -357,22 +357,178 @@ void s68k_clear_halt(void)
CPU_STOPPED &= ~STOP_LEVEL_HALT;
}
void s68k_pulse_wait(void)
void s68k_pulse_wait(unsigned int address, unsigned int write_access)
{
/* Hold the DTACK line on the CPU */
CPU_STOPPED |= STOP_LEVEL_WAIT;
/* Check CPU is not already waiting for /DTACK */
if (!(CPU_STOPPED & STOP_LEVEL_WAIT))
{
/* Hold the DTACK line on the CPU */
CPU_STOPPED |= STOP_LEVEL_WAIT;
/* End CPU execution */
s68k.cycles = s68k.cycle_end - s68k_cycles();
/* End CPU execution */
s68k.cycles = s68k.cycle_end - s68k_cycles();
/* Rollback current instruction (memory access will be executed once /DTACK is asserted) */
s68k.pc = s68k.prev_pc;
/* Save CPU address registers */
s68k.prev_ar[0] = s68k.dar[8+0];
s68k.prev_ar[1] = s68k.dar[8+1];
s68k.prev_ar[2] = s68k.dar[8+2];
s68k.prev_ar[3] = s68k.dar[8+3];
s68k.prev_ar[4] = s68k.dar[8+4];
s68k.prev_ar[5] = s68k.dar[8+5];
s68k.prev_ar[6] = s68k.dar[8+6];
s68k.prev_ar[7] = s68k.dar[8+7];
/* Detect address register(s) pre-decrement/post-increment done by MOVE/MOVEA instruction */
if ((s68k.ir >= 0x1000) && (s68k.ir < 0x4000))
{
/* MOVE/MOVEA instructions operand sizes */
static const int mov_instr_sizes[4] = {0, 1, 4, 2};
if ((s68k.ir & 0x38) == 0x18)
{
/* revert source address register post-increment */
s68k.prev_ar[s68k.ir&0x07] -= mov_instr_sizes[(s68k.ir>>12)&0x03];
}
else if ((s68k.ir & 0x38) == 0x20)
{
/* revert source address register pre-decrement */
s68k.prev_ar[s68k.ir&0x07] += mov_instr_sizes[(s68k.ir>>12)&0x03];
}
/* only check destination address register post-increment/pre-decrement in case of halting on write access */
if (write_access)
{
if ((s68k.ir & 0x01c0) == 0x00c0)
{
/* revert destination address register post-increment */
s68k.prev_ar[(s68k.ir>>9)&0x07] -= mov_instr_sizes[(s68k.ir>>12)&0x03];
}
else if ((s68k.ir & 0x01c0) == 0x0100)
{
/* revert destination address register pre-decrement */
s68k.prev_ar[(s68k.ir>>9)&0x07] += mov_instr_sizes[(s68k.ir>>12)&0x03];
}
}
}
else
{
/* Other instructions operand sizes */
static const int def_instr_sizes[4] = {1, 2, 4, 2};
/* Detect address register(s) pre-decrement done by ABCD/SBCD instruction */
if ((s68k.ir & 0xb1f8) == 0x8108)
{
/* revert source address register pre-decrement (byte operands only) */
s68k.prev_ar[s68k.ir&0x07] += 1;
/* only revert destination address register pre-decrement in case of halting on destination address access (byte operands only) */
if (address == s68k.prev_ar[(s68k.ir>>9)&0x07])
{
s68k.prev_ar[(s68k.ir>>9)&0x07] += 1;
}
}
/* Detect address register(s) pre-decrement done by ADDX/SUBX instruction */
else if (((s68k.ir & 0xb1f8) == 0x9108) || ((s68k.ir & 0xb1f8) == 0x9148) || ((s68k.ir & 0xb1f8) == 0x9188))
{
/* revert source address register pre-decrement */
s68k.prev_ar[s68k.ir&0x07] += def_instr_sizes[(s68k.ir>>6)&0x03];
/* only revert destination address register pre-decrement in case of halting on destination address access */
if (address == s68k.prev_ar[(s68k.ir>>9)&0x07])
{
s68k.prev_ar[(s68k.ir>>9)&0x07] += def_instr_sizes[(s68k.ir>>6)&0x03];
}
}
/* Detect address register(s) post-increment done by CMPM instruction */
else if ((s68k.ir & 0xf138) == 0xb108)
{
/* revert source address register post-increment */
s68k.prev_ar[s68k.ir&0x07] -= def_instr_sizes[(s68k.ir>>6)&0x03];
/* only revert destination address register post-increment in case of halting on destination address access */
if (address == s68k.prev_ar[(s68k.ir>>9)&0x07])
{
s68k.prev_ar[(s68k.ir>>9)&0x07] -= def_instr_sizes[(s68k.ir>>6)&0x03];
}
}
/* Detect address register post-increment or pre-increment done by other instruction */
else if (((s68k.ir & 0x38) == 0x18) || ((s68k.ir & 0x38) == 0x20))
{
int size;
/* autodetect MOVEM instruction (no address register modification needed as post-increment/pre-decrement is done after memory access) */
if ((s68k.ir & 0xfb80) == 0x4880)
{
size = 0;
}
/* autodetect instruction with fixed byte operand (and not covered by generic size field value) */
else if (((s68k.ir & 0xf100) == 0x0100) || /* BTST, BCHG, BCLR, BSET (dynamic) */
((s68k.ir & 0xff00) == 0x0800) || /* BTST, BCHG, BCLR, BSET (static) */
((s68k.ir & 0xffc0) == 0x4ac0) || /* TAS */
((s68k.ir & 0xf0c0) == 0x50c0)) /* Scc */
{
size = 1;
}
/* autodetect instruction with fixed word operand (and not covered by generic size field value) */
else if ((s68k.ir & 0xf1c0) == 0x4180) /* CHK */
{
size = 2;
}
/* autodetect instruction with either word or long operand (not covered by generic size field value) */
else if (((s68k.ir & 0xb0c0) == 0x90c0) || /* SUBA, ADDA*/
((s68k.ir & 0xf0c0) == 0xb0c0)) /* CMPA */
{
size = (s68k.ir & 0x100) ? 4 : 2;
}
/* default operand size */
else
{
size = def_instr_sizes[(s68k.ir>>6)&0x03];
}
if (s68k.ir & 0x08)
{
/* revert source address register post-increment */
s68k.prev_ar[s68k.ir&0x07] -= size;
}
else
{
/* revert source address register pre-decrement */
s68k.prev_ar[s68k.ir&0x07] += size;
}
}
}
}
}
void s68k_clear_wait(void)
{
/* Assert the DTACK line on the CPU */
CPU_STOPPED &= ~STOP_LEVEL_WAIT;
/* check CPU is waiting for DTACK */
if (CPU_STOPPED & STOP_LEVEL_WAIT)
{
/* Assert the DTACK line on the CPU */
CPU_STOPPED &= ~STOP_LEVEL_WAIT;
/* Rollback to previously held instruction */
s68k.pc = s68k.prev_pc;
/* Restore CPU address registers */
s68k.dar[8+0] = s68k.prev_ar[0];
s68k.dar[8+1] = s68k.prev_ar[1];
s68k.dar[8+2] = s68k.prev_ar[2];
s68k.dar[8+3] = s68k.prev_ar[3];
s68k.dar[8+4] = s68k.prev_ar[4];
s68k.dar[8+5] = s68k.prev_ar[5];
s68k.dar[8+6] = s68k.prev_ar[6];
s68k.dar[8+7] = s68k.prev_ar[7];
}
}
/* ======================================================================== */

View File

@ -3,7 +3,7 @@
* Main 68k bus handlers
*
* Copyright (C) 1998-2003 Charles Mac Donald (original code)
* Copyright (C) 2007-2023 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:
@ -88,7 +88,8 @@ void m68k_lockup_w_8 (unsigned int address, unsigned int data)
#endif
if (!config.force_dtack)
{
m68k_pulse_wait();
m68k_pulse_halt();
m68k.cycles = m68k.cycle_end;
}
}
@ -99,7 +100,8 @@ void m68k_lockup_w_16 (unsigned int address, unsigned int data)
#endif
if (!config.force_dtack)
{
m68k_pulse_wait();
m68k_pulse_halt();
m68k.cycles = m68k.cycle_end;
}
}
@ -108,11 +110,12 @@ unsigned int m68k_lockup_r_8 (unsigned int address)
#ifdef LOGERROR
error ("Lockup %08X.b (%08X)\n", address, m68k_get_reg(M68K_REG_PC));
#endif
address = m68k.pc | (address & 1);
if (!config.force_dtack)
{
m68k_pulse_wait();
m68k_pulse_halt();
m68k.cycles = m68k.cycle_end;
}
address = m68k.pc | (address & 1);
return READ_BYTE(m68k.memory_map[((address)>>16)&0xff].base, (address) & 0xffff);
}
@ -121,11 +124,12 @@ unsigned int m68k_lockup_r_16 (unsigned int address)
#ifdef LOGERROR
error ("Lockup %08X.w (%08X)\n", address, m68k_get_reg(M68K_REG_PC));
#endif
address = m68k.pc;
if (!config.force_dtack)
{
m68k_pulse_wait();
m68k_pulse_halt();
m68k.cycles = m68k.cycle_end;
}
address = m68k.pc;
return *(uint16 *)(m68k.memory_map[((address)>>16)&0xff].base + ((address) & 0xffff));
}
@ -136,6 +140,9 @@ unsigned int m68k_lockup_r_16 (unsigned int address)
unsigned int z80_read_byte(unsigned int address)
{
/* Z80 bus access latency */
m68k.cycles += 1 * 7;
switch ((address >> 13) & 3)
{
case 2: /* YM2612 */
@ -168,6 +175,9 @@ unsigned int z80_read_word(unsigned int address)
void z80_write_byte(unsigned int address, unsigned int data)
{
/* Z80 bus access latency (fixes Pacman 2: New Adventures sound engine crashes & Puyo Puyo 2 crash when exiting option menu) */
m68k.cycles += 1 * 7;
switch ((address >> 13) & 3)
{
case 2: /* YM2612 */
@ -203,7 +213,6 @@ void z80_write_byte(unsigned int address, unsigned int data)
default: /* ZRAM */
{
zram[address & 0x1FFF] = data;
m68k.cycles += 2 * 7; /* ZRAM access latency (fixes Pacman 2: New Adventures & Puyo Puyo 2) */
return;
}
}
@ -533,7 +542,7 @@ unsigned int ctrl_io_read_word(unsigned int address)
{
s68k_sync();
}
return cdc_host_r();
return cdc_host_r(CDC_MAIN_CPU_ACCESS);
}
/* H-INT vector (word access only ?) */
@ -752,6 +761,17 @@ void ctrl_io_write_byte(unsigned int address, unsigned int data)
m68k.memory_map[base].write16 = m68k.memory_map[base+1].write16 = NULL;
zbank_memory_map[base].read = zbank_memory_map[base+1].read = NULL;
zbank_memory_map[base].write = zbank_memory_map[base+1].write = NULL;
/* check if CDC DMA to PRG-RAM is running */
if (cdc.dma_w == prg_ram_dma_w)
{
/* synchronize CDC DMA with MAIN-CPU */
cdc_dma_update((m68k.cycles * SCYCLES_PER_LINE) / MCYCLES_PER_LINE);
/* halt CDC DMA to PRG-RAM (if still running) */
cdc.halted_dma_w = cdc.dma_w;
cdc.dma_w = 0;
}
}
else
{
@ -761,6 +781,23 @@ void ctrl_io_write_byte(unsigned int address, unsigned int data)
m68k.memory_map[base].write16 = m68k.memory_map[base+1].write16 = m68k_unused_16_w;
zbank_memory_map[base].read = zbank_memory_map[base+1].read = zbank_unused_r;
zbank_memory_map[base].write = zbank_memory_map[base+1].write = zbank_unused_w;
/* check if CDC DMA to PRG-RAM is halted */
if (cdc.halted_dma_w == prg_ram_dma_w)
{
/* relative SUB-CPU cycle counter */
unsigned int cycles = (m68k.cycles * SCYCLES_PER_LINE) / MCYCLES_PER_LINE;
/* enable CDC DMA to PRG-RAM */
cdc.dma_w = prg_ram_dma_w;
cdc.halted_dma_w = 0;
/* synchronize CDC DMA with MAIN-CPU (only if not already ahead) */
if (cdc.cycles[0] < cycles)
{
cdc.cycles[0] = cycles;
}
}
}
}
@ -836,7 +873,7 @@ void ctrl_io_write_byte(unsigned int address, unsigned int data)
/* check if SUB-CPU is waiting for Word-RAM access */
if (s68k.stopped & 0x04)
{
/* sync SUB-CPU with MAIN-CPU */
/* synchronize SUB-CPU with MAIN-CPU */
s68k.cycles = (m68k.cycles * SCYCLES_PER_LINE) / MCYCLES_PER_LINE;
/* restart SUB-CPU */
@ -846,18 +883,36 @@ void ctrl_io_write_byte(unsigned int address, unsigned int data)
#endif
}
/* check if graphics operation is running */
/* check if graphics operation is started */
if (scd.regs[0x58>>1].byte.h & 0x80)
{
/* relative SUB-CPU cycle counter */
unsigned int cycles = (m68k.cycles * SCYCLES_PER_LINE) / MCYCLES_PER_LINE;
/* synchronize GFX processing with SUB-CPU (only if not already ahead) */
/* synchronize GFX processing with MAIN-CPU (only if not already ahead) */
if (gfx.cycles < cycles)
{
gfx.cycles = cycles;
}
}
/* check if CDC DMA to 2M Word-RAM is halted */
if (cdc.halted_dma_w == word_ram_2M_dma_w)
{
/* relative SUB-CPU cycle counter */
unsigned int cycles = (m68k.cycles * SCYCLES_PER_LINE) / MCYCLES_PER_LINE;
/* enable CDC DMA to 2M Word-RAM */
cdc.dma_w = word_ram_2M_dma_w;
cdc.halted_dma_w = 0;
/* synchronize CDC DMA with MAIN-CPU (only if not already ahead) */
if (cdc.cycles[0] < cycles)
{
cdc.cycles[0] = cycles;
}
}
return;
}
}
@ -1035,6 +1090,17 @@ void ctrl_io_write_word(unsigned int address, unsigned int data)
m68k.memory_map[base].write16 = m68k.memory_map[base+1].write16 = NULL;
zbank_memory_map[base].read = zbank_memory_map[base+1].read = NULL;
zbank_memory_map[base].write = zbank_memory_map[base+1].write = NULL;
/* check if CDC DMA to PRG-RAM is running */
if (cdc.dma_w == prg_ram_dma_w)
{
/* synchronize CDC DMA with MAIN-CPU */
cdc_dma_update((m68k.cycles * SCYCLES_PER_LINE) / MCYCLES_PER_LINE);
/* halt CDC DMA to PRG-RAM (if still running) */
cdc.halted_dma_w = cdc.dma_w;
cdc.dma_w = 0;
}
}
else
{
@ -1044,6 +1110,23 @@ void ctrl_io_write_word(unsigned int address, unsigned int data)
m68k.memory_map[base].write16 = m68k.memory_map[base+1].write16 = m68k_unused_16_w;
zbank_memory_map[base].read = zbank_memory_map[base+1].read = zbank_unused_r;
zbank_memory_map[base].write = zbank_memory_map[base+1].write = zbank_unused_w;
/* check if CDC DMA to PRG-RAM is halted */
if (cdc.halted_dma_w == prg_ram_dma_w)
{
/* relative SUB-CPU cycle counter */
unsigned int cycles = (m68k.cycles * SCYCLES_PER_LINE) / MCYCLES_PER_LINE;
/* enable CDC DMA to PRG-RAM */
cdc.dma_w = prg_ram_dma_w;
cdc.halted_dma_w = 0;
/* synchronize CDC DMA with MAIN-CPU (only if not already ahead) */
if (cdc.cycles[0] < cycles)
{
cdc.cycles[0] = cycles;
}
}
}
}
@ -1129,7 +1212,7 @@ void ctrl_io_write_word(unsigned int address, unsigned int data)
/* check if SUB-CPU is waiting for Word-RAM access */
if (s68k.stopped & 0x04)
{
/* sync SUB-CPU with MAIN-CPU */
/* synchronize SUB-CPU with MAIN-CPU */
s68k.cycles = (m68k.cycles * SCYCLES_PER_LINE) / MCYCLES_PER_LINE;
/* restart SUB-CPU */
@ -1139,18 +1222,35 @@ void ctrl_io_write_word(unsigned int address, unsigned int data)
#endif
}
/* check if graphics operation is running */
/* check if graphics operation is started */
if (scd.regs[0x58>>1].byte.h & 0x80)
{
/* relative SUB-CPU cycle counter */
unsigned int cycles = (m68k.cycles * SCYCLES_PER_LINE) / MCYCLES_PER_LINE;
/* synchronize GFX processing with SUB-CPU (only if not already ahead) */
/* synchronize GFX processing with MAIN-CPU (only if not already ahead) */
if (gfx.cycles < cycles)
{
gfx.cycles = cycles;
}
}
/* check if CDC DMA to 2M Word-RAM is halted */
if (cdc.halted_dma_w == word_ram_2M_dma_w)
{
/* relative SUB-CPU cycle counter */
unsigned int cycles = (m68k.cycles * SCYCLES_PER_LINE) / MCYCLES_PER_LINE;
/* enable CDC DMA to 2M Word-RAM */
cdc.dma_w = word_ram_2M_dma_w;
cdc.halted_dma_w = 0;
/* synchronize CDC DMA with MAIN-CPU (only if not already ahead) */
if (cdc.cycles[0] < cycles)
{
cdc.cycles[0] = cycles;
}
}
return;
}
}
@ -1166,6 +1266,13 @@ void ctrl_io_write_word(unsigned int address, unsigned int data)
return;
}
case 0x08: /* CDC host data */
{
/* CDC data is also read (although unused) on write access (verified on real hardware, cf. Krikzz's mcd-verificator) */
cdc_host_r(CDC_MAIN_CPU_ACCESS);
return;
}
case 0x0e: /* CPU communication flags */
{
m68k_poll_sync(1<<0x0e);

View File

@ -3,7 +3,7 @@
* Main 68k bus handlers
*
* Copyright (C) 1998-2003 Charles Mac Donald (original code)
* Copyright (C) 2007-2023 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:

View File

@ -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)
{

View File

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

View File

@ -5,7 +5,7 @@
* Support for 16-bit & 8-bit hardware modes
*
* Copyright (C) 1998-2003 Charles Mac Donald (original code)
* Copyright (C) 2007-2021 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:
@ -352,8 +352,10 @@ void system_frame_gen(int do_skip)
mcycles_vdp = 0;
/* reset VDP FIFO */
fifo_write_cnt = 0;
fifo_slots = 0;
fifo_cycles[0] = 0;
fifo_cycles[1] = 0;
fifo_cycles[2] = 0;
fifo_cycles[3] = 0;
/* check if display setings have changed during previous frame */
if (bitmap.viewport.changed & 2)
@ -439,8 +441,8 @@ void system_frame_gen(int do_skip)
/* clear DMA Busy, FIFO FULL & field flags */
status &= 0xFEED;
/* set VBLANK & FIFO EMPTY flags */
status |= 0x0208;
/* set VBLANK flag */
status |= 0x08;
/* check interlaced modes */
if (interlaced)
@ -481,14 +483,14 @@ void system_frame_gen(int do_skip)
v_counter = bitmap.viewport.h;
/* delay between VBLANK flag & Vertical Interrupt (Dracula, OutRunners, VR Troopers) */
m68k_run(788);
m68k_run(vint_cycle);
if (zstate == 1)
{
z80_run(788);
z80_run(vint_cycle);
}
/* set VINT flag */
status |= 0x80;
status |= 0x80;
/* Vertical Interrupt */
vint_pending = 0x20;
@ -678,8 +680,10 @@ void system_frame_gen(int do_skip)
/* adjust timings for next frame */
input_end_frame(mcycles_vdp);
m68k.refresh_cycles -= mcycles_vdp;
m68k.cycles -= mcycles_vdp;
Z80.cycles -= mcycles_vdp;
dma_endCycles = 0;
}
void system_frame_scd(int do_skip)
@ -692,8 +696,10 @@ void system_frame_scd(int do_skip)
scd.cycles = 0;
/* reset VDP FIFO */
fifo_write_cnt = 0;
fifo_slots = 0;
fifo_cycles[0] = 0;
fifo_cycles[1] = 0;
fifo_cycles[2] = 0;
fifo_cycles[3] = 0;
/* check if display setings have changed during previous frame */
if (bitmap.viewport.changed & 2)
@ -779,8 +785,8 @@ void system_frame_scd(int do_skip)
/* clear DMA Busy, FIFO FULL & field flags */
status &= 0xFEED;
/* set VBLANK & FIFO EMPTY flags */
status |= 0x0208;
/* set VBLANK flag */
status |= 0x08;
/* check interlaced modes */
if (interlaced)
@ -821,14 +827,14 @@ void system_frame_scd(int do_skip)
v_counter = bitmap.viewport.h;
/* delay between VBLANK flag & Vertical Interrupt (Dracula, OutRunners, VR Troopers) */
m68k_run(788);
m68k_run(vint_cycle);
if (zstate == 1)
{
z80_run(788);
z80_run(vint_cycle);
}
/* set VINT flag */
status |= 0x80;
status |= 0x80;
/* Vertical Interrupt */
vint_pending = 0x20;
@ -1003,8 +1009,10 @@ void system_frame_scd(int do_skip)
/* adjust timings for next frame */
scd_end_frame(scd.cycles);
input_end_frame(mcycles_vdp);
m68k.refresh_cycles -= mcycles_vdp;
m68k.cycles -= mcycles_vdp;
Z80.cycles -= mcycles_vdp;
dma_endCycles = 0;
}
void system_frame_sms(int do_skip)
@ -1016,8 +1024,10 @@ void system_frame_sms(int do_skip)
mcycles_vdp = 0;
/* reset VDP FIFO */
fifo_write_cnt = 0;
fifo_slots = 0;
fifo_cycles[0] = 0;
fifo_cycles[1] = 0;
fifo_cycles[2] = 0;
fifo_cycles[3] = 0;
/* check if display settings has changed during previous frame */
if (bitmap.viewport.changed & 2)

View File

@ -5,7 +5,7 @@
* Support for 16-bit & 8-bit hardware modes
*
* 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:
@ -66,8 +66,8 @@
#define MCYCLES_PER_LINE 3420
/* Horizontal timing offsets when running in Z80 mode */
#define SMS_CYCLE_OFFSET 530
#define PBC_CYCLE_OFFSET 560
#define SMS_CYCLE_OFFSET 530
#define PBC_CYCLE_OFFSET 560
typedef struct
{
@ -98,7 +98,6 @@ typedef struct
int16 cd_last_save[2]; /* For saving and restoring the sound buffer */
} t_snd;
/* Global variables */
extern t_bitmap bitmap;
extern t_snd snd;
@ -122,4 +121,3 @@ extern void system_frame_scd(int do_skip);
extern void system_frame_sms(int do_skip);
#endif /* _SYSTEM_H_ */

View File

@ -5,7 +5,7 @@
* Support for SG-1000 (TMS99xx & 315-5066), Master System (315-5124 & 315-5246), Game Gear & Mega Drive VDP
*
* Copyright (C) 1998-2003 Charles Mac Donald (original code)
* Copyright (C) 2007-2023 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:
@ -58,6 +58,10 @@ static void vdp_set_all_vram(const uint8 *src);
bg_name_dirty[name] |= (1 << ((addr >> 2) & 7)); \
}
/* VINT timings */
#define VINT_H32_MCYCLE (770)
#define VINT_H40_MCYCLE (788)
/* HBLANK flag timings */
#define HBLANK_H32_START_MCYCLE (280)
#define HBLANK_H32_END_MCYCLE (860)
@ -65,15 +69,17 @@ static void vdp_set_all_vram(const uint8 *src);
#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 */
@ -98,9 +104,9 @@ uint16 v_counter; /* Vertical counter */
uint16 vc_max; /* Vertical counter overflow value */
uint16 lines_per_frame; /* PAL: 313 lines, NTSC: 262 lines */
uint16 max_sprite_pixels; /* Max. sprites pixels per line (parsing & rendering) */
int32 fifo_write_cnt; /* VDP FIFO write count */
uint32 fifo_slots; /* VDP FIFO access slot count */
uint32 fifo_cycles[4]; /* VDP FIFO read-out cycles */
uint32 hvc_latch; /* latched HV counter */
uint32 vint_cycle; /* VINT occurence cycle */
const uint8 *hctab; /* pointer to H Counter table */
/* Function pointers */
@ -122,7 +128,6 @@ static void vdp_z80_data_w_ms(unsigned int data);
static void vdp_z80_data_w_gg(unsigned int data);
static void vdp_z80_data_w_sg(unsigned int data);
static void vdp_bus_w(unsigned int data);
static void vdp_fifo_update(unsigned int cycles);
static void vdp_reg_w(unsigned int r, unsigned int d, unsigned int cycles);
static void vdp_dma_68k_ext(unsigned int length);
static void vdp_dma_68k_ram(unsigned int length);
@ -136,23 +141,20 @@ 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 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 */
@ -171,16 +173,20 @@ static const uint16 vc_table[4][2] =
};
/* FIFO access slots timings */
static const int fifo_timing_h32[16+4] =
static const int fifo_timing_h32[] =
{
230, 510, 810, 970, 1130, 1450, 1610, 1770, 2090, 2250, 2410, 2730, 2890, 3050, 3350, 3370,
MCYCLES_PER_LINE + 230, MCYCLES_PER_LINE + 510, MCYCLES_PER_LINE + 810, MCYCLES_PER_LINE + 970,
MCYCLES_PER_LINE + 1130, MCYCLES_PER_LINE + 1450, MCYCLES_PER_LINE + 1610, MCYCLES_PER_LINE + 1770,
MCYCLES_PER_LINE + 2090, MCYCLES_PER_LINE + 2250, MCYCLES_PER_LINE + 2410, MCYCLES_PER_LINE + 2730
};
static const int fifo_timing_h40[18+4] =
static const int fifo_timing_h40[] =
{
352, 820, 948, 1076, 1332, 1460, 1588, 1844, 1972, 2100, 2356, 2484, 2612, 2868, 2996, 3124, 3364, 3380,
MCYCLES_PER_LINE + 352, MCYCLES_PER_LINE + 820, MCYCLES_PER_LINE + 948, MCYCLES_PER_LINE + 1076,
MCYCLES_PER_LINE + 1332, MCYCLES_PER_LINE + 1460, MCYCLES_PER_LINE + 1588, MCYCLES_PER_LINE + 1844,
MCYCLES_PER_LINE + 1972, MCYCLES_PER_LINE + 2100, MCYCLES_PER_LINE + 2356, MCYCLES_PER_LINE + 2484
};
/* DMA Timings (number of access slots per line) */
@ -279,9 +285,6 @@ void vdp_reset(void)
odd_frame = 0;
im2_flag = 0;
interlaced = 0;
fifo_write_cnt = 0;
fifo_cycles = 0;
fifo_slots = 0;
fifo_idx = 0;
cached_write = -1;
fifo_byte_access = 1;
@ -312,10 +315,10 @@ void vdp_reset(void)
/* default Window clipping */
window_clip(0,0);
/* reset VDP status (FIFO empty flag is set) */
/* reset VDP status */
if (system_hw & SYSTEM_MD)
{
status = vdp_pal | 0x200;
status = vdp_pal;
}
else
{
@ -341,6 +344,9 @@ void vdp_reset(void)
/* default FIFO access slots timings */
fifo_timing = (int *)fifo_timing_h32;
/* default VINT timing */
vint_cycle = VINT_H32_MCYCLE;
/* default HBLANK flag timings */
hblank_start_cycle = HBLANK_H32_START_MCYCLE;
hblank_end_cycle = HBLANK_H32_END_MCYCLE;
@ -618,7 +624,7 @@ int vdp_context_load(uint8 *state)
void vdp_dma_update(unsigned int cycles)
{
unsigned int dma_cycles, dma_bytes;
int dma_cycles, dma_bytes;
/* DMA transfer rate (bytes per line)
@ -647,8 +653,21 @@ void vdp_dma_update(unsigned int cycles)
/* Adjust for 68k bus DMA to VRAM (one word = 2 access) or DMA Copy (one read + one write = 2 access) */
rate = rate >> (dma_type & 1);
/* Adjust for 68k bus DMA to CRAM or VSRAM when display is off (one additional access slot is lost for each refresh slot) */
if (dma_type == 0)
{
if (rate == 166) rate = 161; /* 5 refresh slots per line in H32 mode when display is off */
else if (rate == 204) rate = 198; /* 6 refresh slots per line in H40 mode when display is off */
}
/* Remaining DMA cycles */
/* Adjust DMA start cycle for DMA fill to take in account intial data port write */
else if (dmafill)
{
cycles += (2 * (MCYCLES_PER_LINE / rate));
}
/* Available DMA cycles */
if (status & 8)
{
/* Process DMA until the end of VBLANK */
@ -663,14 +682,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 */
@ -678,29 +697,39 @@ 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 */
if (dma_bytes)
if (dma_bytes > 0)
{
/* Update DMA length */
dma_length -= dma_bytes;
@ -712,8 +741,11 @@ void vdp_dma_update(unsigned int cycles)
if (!dma_length)
{
/* DMA source address registers are incremented during DMA (even DMA Fill) */
if (config.vdp_fix_dma_boundary_bug) {
reg[21] = end & 0xff;
/*
reg[22] = end >> 8;
* NOTICE: VDP has a hardware bug where DMA transfer source address is not incremented properly,
* causing the transfer to wrap around 128kb memory boundaries.
*
@ -745,6 +777,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;
}
}
}
@ -887,18 +922,12 @@ void vdp_68k_ctrl_w(unsigned int data)
/*
FIFO emulation (Chaos Engine/Soldier of Fortune, Double Clutch, Sol Deace)
--------------------------------------------------------------------------
Each VRAM access is byte wide, so one VRAM write (word) need two slot access.
Each VRAM access is byte wide, so one VRAM write (word) need two access slots.
NOTE: Invalid code 0x02 (register write) should not behave the same as VRAM
access, i.e data is ignored and only one access slot is used for each word,
BUT a few games ("Clue", "Microcosm") which accidentally corrupt code value
will have issues when emulating FIFO timings. They likely work fine on real
hardware because of periodical 68k wait-states which have been observed and
would naturaly add some delay between writes. Until those wait-states are
accurately measured and emulated, delay is forced when invalid code value
is being used.
NOTE: Invalid codes 0x00, 0x08 and 0x09 behaves the same as VRAM access (0x01) i.e,
although no data is written, two access slots are required to empty the FIFO entry.
*/
fifo_byte_access = ((code & 0x0F) <= 0x02);
fifo_byte_access = (code & 0x06) ? 0 : 1;
}
/* Mega Drive VDP control port specific (MS compatibility mode) */
@ -1219,12 +1248,6 @@ unsigned int vdp_68k_ctrl_r(unsigned int cycles)
/* Cycle-accurate VDP status read (adjust CPU time with current instruction execution time) */
cycles += m68k_cycles();
/* Update FIFO status flags if not empty */
if (fifo_write_cnt)
{
vdp_fifo_update(cycles);
}
/* Check if DMA Busy flag is set */
if (status & 2)
{
@ -1245,6 +1268,20 @@ unsigned int vdp_68k_ctrl_r(unsigned int cycles)
/* Clear SOVR & SCOL flags */
status &= 0xFF9F;
/* Check if FIFO last entry read-out cycle has been reached */
if (cycles >= fifo_cycles[(fifo_idx + 3) & 3])
{
/* FIFO is empty */
temp |= 0x200;
}
/* Check if FIFO oldest entry read-out cycle is not yet reached */
else if (cycles < fifo_cycles[fifo_idx])
{
/* FIFO is full */
temp |= 0x100;
}
/* VBLANK flag is set when display is disabled */
if (!(reg[1] & 0x40))
{
@ -1254,9 +1291,9 @@ unsigned int vdp_68k_ctrl_r(unsigned int cycles)
/* Adjust cycle count relatively to start of line */
cycles -= mcycles_vdp;
/* Cycle-accurate VINT flag (Ex-Mutants, Tyrant / Mega-Lo-Mania, Marvel Land) */
/* Cycle-accurate VINT flag (Ex-Mutants, Tyrant / Mega-Lo-Mania, Marvel Land, Pacman 2 - New Adventures / Pac-Jr minigame) */
/* this allows VINT flag to be read just before vertical interrupt is being triggered */
if ((v_counter == bitmap.viewport.h) && (cycles >= 788))
if ((v_counter == bitmap.viewport.h) && (cycles >= vint_cycle))
{
/* check Z80 interrupt state to assure VINT has not already been triggered (and flag cleared) */
if (Z80.irq_state != ASSERT_LINE)
@ -2019,13 +2056,6 @@ static void vdp_reg_w(unsigned int r, unsigned int d, unsigned int cycles)
/* Active display width */
if (r & 0x01)
{
/* FIFO access slots timings depend on active width */
if (fifo_slots)
{
/* Synchronize VDP FIFO */
vdp_fifo_update(cycles);
}
if (d & 0x01)
{
/* Update display-dependant registers */
@ -2046,9 +2076,12 @@ static void vdp_reg_w(unsigned int r, unsigned int d, unsigned int cycles)
/* FIFO access slots timings */
fifo_timing = (int *)fifo_timing_h40;
/* VINT timing */
vint_cycle = VINT_H40_MCYCLE;
/* HBLANK flag timings */
hblank_start_cycle = HBLANK_H32_START_MCYCLE;
hblank_end_cycle = HBLANK_H32_END_MCYCLE;
hblank_start_cycle = HBLANK_H40_START_MCYCLE;
hblank_end_cycle = HBLANK_H40_END_MCYCLE;
}
else
{
@ -2070,9 +2103,12 @@ static void vdp_reg_w(unsigned int r, unsigned int d, unsigned int cycles)
/* FIFO access slots timings */
fifo_timing = (int *)fifo_timing_h32;
/* VINT timing */
vint_cycle = VINT_H32_MCYCLE;
/* HBLANK flag timings */
hblank_start_cycle = HBLANK_H40_START_MCYCLE;
hblank_end_cycle = HBLANK_H40_END_MCYCLE;
hblank_start_cycle = HBLANK_H32_START_MCYCLE;
hblank_end_cycle = HBLANK_H32_END_MCYCLE;
}
/* Active screen width modified during VBLANK will be applied on upcoming frame */
@ -2136,68 +2172,6 @@ static void vdp_reg_w(unsigned int r, unsigned int d, unsigned int cycles)
}
}
/*--------------------------------------------------------------------------*/
/* FIFO emulation (Mega Drive VDP specific) */
/* ---------------------------------------- */
/* */
/* CPU access to VRAM, CRAM & VSRAM is limited during active display: */
/* H32 mode -> 16 access per line */
/* H40 mode -> 18 access per line */
/* */
/* with fixed access slots timings detailled below. */
/* */
/* Each VRAM access is byte wide, so one VRAM write (word) need two slots. */
/* */
/*--------------------------------------------------------------------------*/
static void vdp_fifo_update(unsigned int cycles)
{
int fifo_read_cnt, line_slots = 0;
/* number of access slots up to current line */
int total_slots = dma_timing[0][reg[12] & 1] * ((v_counter + 1) % lines_per_frame);
/* number of access slots within current line */
cycles -= mcycles_vdp;
while (fifo_timing[line_slots] <= cycles)
{
line_slots++;
}
/* number of processed FIFO entries since last access (byte access needs two slots to process one FIFO word) */
fifo_read_cnt = (total_slots + line_slots - fifo_slots) >> fifo_byte_access;
if (fifo_read_cnt > 0)
{
/* process FIFO entries */
fifo_write_cnt -= fifo_read_cnt;
/* Clear FIFO full flag */
status &= 0xFEFF;
if (fifo_write_cnt <= 0)
{
/* No more FIFO entries */
fifo_write_cnt = 0;
/* Set FIFO empty flag */
status |= 0x200;
/* Reinitialize FIFO access slot counter */
fifo_slots = total_slots + line_slots;
}
else
{
/* Update FIFO access slot counter */
fifo_slots += (fifo_read_cnt << fifo_byte_access);
}
}
/* next FIFO update cycle */
fifo_cycles = mcycles_vdp + fifo_timing[fifo_slots - total_slots + fifo_byte_access];
}
/*--------------------------------------------------------------------------*/
/* Internal 16-bit data bus access function (Mode 5 only) */
/*--------------------------------------------------------------------------*/
@ -2333,8 +2307,6 @@ static void vdp_bus_w(unsigned int data)
default:
{
/* add some delay until 68k periodical wait-states are accurately emulated ("Clue", "Microcosm") */
m68k.cycles += 2;
#ifdef LOGERROR
error("[%d(%d)][%d(%d)] Invalid (%d) 0x%x write -> 0x%x (%x)\n", v_counter, (v_counter + (m68k.cycles - mcycles_vdp)/MCYCLES_PER_LINE)%lines_per_frame, m68k.cycles, m68k.cycles%MCYCLES_PER_LINE, code, addr, data, m68k_get_reg(M68K_REG_PC));
#endif
@ -2359,29 +2331,31 @@ static void vdp_68k_data_w_m4(unsigned int data)
/* Restricted VDP writes during active display */
if (!(status & 8) && (reg[1] & 0x40))
{
/* Update VDP FIFO */
vdp_fifo_update(m68k.cycles);
int slot = 0;
/* Clear FIFO empty flag */
status &= 0xFDFF;
/* Cycle-accurate VDP data port access */
int cycles = m68k.cycles;
/* up to 4 words can be stored */
if (fifo_write_cnt < 4)
/* Check against last FIFO entry read-out cycle */
if (cycles < fifo_cycles[(fifo_idx + 3) & 3])
{
/* Increment FIFO counter */
fifo_write_cnt++;
/* Check against oldest FIFO entry read-out cycle */
if (cycles < fifo_cycles[fifo_idx])
{
/* FIFO is full, 68k waits until oldest FIFO entry is processed (Chaos Engine / Soldiers of Fortune, Double Clutch, Titan Overdrive Demo) */
m68k.cycles = (((fifo_cycles[fifo_idx] + 6) / 7) * 7);
}
/* Set FIFO full flag if 4 words are stored */
status |= ((fifo_write_cnt & 4) << 6);
/* FIFO is not empty, next FIFO entry will be processed after last FIFO entry */
cycles = fifo_cycles[(fifo_idx + 3) & 3];
}
else
{
/* CPU is halted until next FIFO entry processing */
m68k.cycles = fifo_cycles;
/* Update FIFO access slot counter */
fifo_slots += (fifo_byte_access + 1);
}
/* Determine next FIFO entry processing slot */
cycles -= mcycles_vdp;
while (cycles >= fifo_timing[slot]) slot++;
/* Update last FIFO entry read-out cycle */
fifo_cycles[fifo_idx] = mcycles_vdp + fifo_timing[slot + fifo_byte_access];
}
/* Check destination code */
@ -2451,40 +2425,39 @@ static void vdp_68k_data_w_m5(unsigned int data)
/* Restricted VDP writes during active display */
if (!(status & 8) && (reg[1] & 0x40))
{
/* Update VDP FIFO */
vdp_fifo_update(m68k.cycles);
int slot = 0;
/* Clear FIFO empty flag */
status &= 0xFDFF;
/* Cycle-accurate VDP data port access */
int cycles = m68k.cycles;
/* up to 4 words can be stored */
if (fifo_write_cnt < 4)
/* Check against last FIFO entry read-out cycle */
if (cycles < fifo_cycles[(fifo_idx + 3) & 3])
{
/* Increment FIFO counter */
fifo_write_cnt++;
/* Check against oldest FIFO entry read-out cycle */
if (cycles < fifo_cycles[fifo_idx])
{
/* FIFO is full, 68k waits until oldest FIFO entry is processed (Chaos Engine / Soldiers of Fortune, Double Clutch, Titan Overdrive Demo) */
m68k.cycles = (((fifo_cycles[fifo_idx] + 6) / 7) * 7);
}
/* Set FIFO full flag if 4 words are stored */
status |= ((fifo_write_cnt & 4) << 6);
/* FIFO is not empty, next FIFO entry will be processed after last FIFO entry */
cycles = fifo_cycles[(fifo_idx + 3) & 3];
}
else
{
/* CPU is halted until next FIFO entry processing (Chaos Engine / Soldiers of Fortune, Double Clutch, Titan Overdrive Demo) */
m68k.cycles = fifo_cycles;
/* Update FIFO access slot counter */
fifo_slots += (fifo_byte_access + 1);
}
/* Determine next FIFO entry processing slot */
cycles -= mcycles_vdp;
while (cycles >= fifo_timing[slot]) slot++;
/* Update last FIFO entry read-out cycle */
fifo_cycles[fifo_idx] = mcycles_vdp + fifo_timing[slot + fifo_byte_access];
}
/* Write data */
vdp_bus_w(data);
/* Check if DMA Fill is pending */
if (dmafill)
{
/* Clear DMA Fill pending flag */
dmafill = 0;
/* DMA length */
dma_length = (reg[20] << 8) | reg[19];
@ -2496,6 +2469,9 @@ static void vdp_68k_data_w_m5(unsigned int data)
/* Trigger DMA */
vdp_dma_update(m68k.cycles);
/* Clear DMA Fill pending flag */
dmafill = 0;
}
}
@ -3077,6 +3053,7 @@ static void vdp_dma_68k_ext(unsigned int length)
source += 2;
if (!config.vdp_fix_dma_boundary_bug) {
source = (reg[23] << 17) | (source & 0x1FFFF);
/* 128k DMA window */
source = (reg[23] << 17) | (source & 0x1FFFF);
}
@ -3219,7 +3196,7 @@ static void vdp_dma_fill(unsigned int length)
{
int name;
/* Get source data from last written FIFO entry */
/* Get source data from last written FIFO entry */
uint8 data = fifo[(fifo_idx+3)&3] >> 8;
do

View File

@ -5,7 +5,7 @@
* Support for SG-1000 (TMS99xx & 315-5066), Master System (315-5124 & 315-5246), Game Gear & Mega Drive VDP
*
* Copyright (C) 1998-2003 Charles Mac Donald (original code)
* Copyright (C) 2007-2023 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:
@ -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;
@ -76,9 +78,9 @@ extern uint16 vc_max;
extern uint16 vscroll;
extern uint16 lines_per_frame;
extern uint16 max_sprite_pixels;
extern int32 fifo_write_cnt;
extern uint32 fifo_slots;
extern uint32 fifo_cycles[4];
extern uint32 hvc_latch;
extern uint32 vint_cycle;
extern const uint8 *hctab;
/* Function pointers */

View File

@ -865,7 +865,7 @@ void fill_pathname_resolve_relative(char *out_path,
* Makes sure not to get two consecutive slashes
* between directory and path.
**/
void fill_pathname_join(char *out_path,
size_t fill_pathname_join(char *out_path,
const char *dir, const char *path, size_t size)
{
if (out_path != dir)
@ -874,7 +874,7 @@ void fill_pathname_join(char *out_path,
if (*out_path)
fill_pathname_slash(out_path, size);
strlcat(out_path, path, size);
return strlcat(out_path, path, size);
}
void fill_pathname_join_special_ext(char *out_path,

View File

@ -368,7 +368,7 @@ void fill_pathname_resolve_relative(char *out_path, const char *in_refpath,
* Makes sure not to get two consecutive slashes
* between directory and path.
**/
void fill_pathname_join(char *out_path, const char *dir,
size_t fill_pathname_join(char *out_path, const char *dir,
const char *path, size_t size);
void fill_pathname_join_special_ext(char *out_path,