mirror of
https://github.com/libretro/Genesis-Plus-GX-Wide.git
synced 2024-11-26 18:10:21 +00:00
Sync with non-wide
This commit is contained in:
parent
d5531ca58d
commit
19e9f0f095
24
HISTORY.txt
24
HISTORY.txt
@ -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]
|
||||
---------------
|
||||
|
22
LICENSE.txt
22
LICENSE.txt
@ -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
|
||||
|
||||
----------------------------------------------------------------------------------------
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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:
|
||||
|
486
core/cd_hw/cdc.c
486
core/cd_hw/cdc.c
@ -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) */
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
153
core/cd_hw/scd.c
153
core/cd_hw/scd.c
@ -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;
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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 */
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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 ============================= */
|
||||
/* ======================================================================== */
|
||||
|
@ -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 */
|
||||
|
@ -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();
|
||||
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
||||
/* ======================================================================== */
|
||||
|
137
core/mem68k.c
137
core/mem68k.c
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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_ */
|
||||
|
||||
|
347
core/vdp_ctrl.c
347
core/vdp_ctrl.c
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user