From ea8d2991233b1eb41159eaaa955d736f5fa55752 Mon Sep 17 00:00:00 2001 From: ekeeke Date: Sun, 9 Oct 2022 10:13:24 +0200 Subject: [PATCH] [Core/CD] improved GFX processing accuracy to halt it while Word RAM is allocated to Main CPU in 2M mode --- HISTORY.txt | 1 + core/cd_hw/gfx.c | 213 ++++++++++++++++++++++++----------------------- core/cd_hw/scd.c | 14 ++++ core/mem68k.c | 44 ++++++++-- core/mem68k.h | 2 +- 5 files changed, 160 insertions(+), 114 deletions(-) diff --git a/HISTORY.txt b/HISTORY.txt index 6cfe9bf..3546529 100644 --- a/HISTORY.txt +++ b/HISTORY.txt @@ -29,6 +29,7 @@ Genesis Plus GX 1.7.5 (xx/xx/xxxx) (Eke-Eke) * improved CDD "play" command accuracy (fixes "Snatcher" Act 2 starting cutscene) * improved CDD status report accuracy (fixes track looping with Mode 1 patched games using MSU-MD driver) * improved Word-RAM byte access accuracy (verified on schematics) +* improved GFX processing accuracy to halt it while Word RAM is allocated to Main CPU in 2M mode * disabled 68k and Z80 access to PRG-RAM when SUB-CPU is running (fixes "Dungeon Explorer") * disabled CD hardware reset on Soft-Reset (verified on real hardware) * fixed potential load issues with non-zero backup RAM cart diff --git a/core/cd_hw/gfx.c b/core/cd_hw/gfx.c index a5e4952..16023e2 100644 --- a/core/cd_hw/gfx.c +++ b/core/cd_hw/gfx.c @@ -586,132 +586,137 @@ INLINE void gfx_render(uint32 bufferIndex, uint32 width) void gfx_start(unsigned int base, int cycles) { - /* make sure 2M mode is enabled */ - if (!(scd.regs[0x02>>1].byte.l & 0x04)) + uint32 mask; + + /* trace vector pointer */ + gfx.tracePtr = (uint16 *)(scd.word_ram_2M + ((base << 2) & 0x3fff8)); + + /* stamps & stamp map size */ + switch ((scd.regs[0x58>>1].byte.l >> 1) & 0x03) { - uint32 mask; - - /* trace vector pointer */ - gfx.tracePtr = (uint16 *)(scd.word_ram_2M + ((base << 2) & 0x3fff8)); + case 0: + gfx.dotMask = 0x07ffff; /* 256x256 dots/map */ + gfx.stampShift = 11 + 4; /* 16x16 dots/stamps */ + gfx.mapShift = 4; /* 16x16 stamps/map */ + mask = 0x3fe00; /* 512 bytes/table */ + break; - /* stamps & stamp map size */ - switch ((scd.regs[0x58>>1].byte.l >> 1) & 0x03) - { - case 0: - gfx.dotMask = 0x07ffff; /* 256x256 dots/map */ - gfx.stampShift = 11 + 4; /* 16x16 dots/stamps */ - gfx.mapShift = 4; /* 16x16 stamps/map */ - mask = 0x3fe00; /* 512 bytes/table */ - break; + case 1: + gfx.dotMask = 0x07ffff; /* 256x256 dots/map */ + gfx.stampShift = 11 + 5; /* 32x32 dots/stamps */ + gfx.mapShift = 3; /* 8x8 stamps/map */ + mask = 0x3ff80; /* 128 bytes/table */ + break; - case 1: - gfx.dotMask = 0x07ffff; /* 256x256 dots/map */ - gfx.stampShift = 11 + 5; /* 32x32 dots/stamps */ - gfx.mapShift = 3; /* 8x8 stamps/map */ - mask = 0x3ff80; /* 128 bytes/table */ - break; + case 2: + gfx.dotMask = 0x7fffff; /* 4096*4096 dots/map */ + gfx.stampShift = 11 + 4; /* 16x16 dots/stamps */ + gfx.mapShift = 8; /* 256x256 stamps/map */ + mask = 0x20000; /* 131072 bytes/table */ + break; - case 2: - gfx.dotMask = 0x7fffff; /* 4096*4096 dots/map */ - gfx.stampShift = 11 + 4; /* 16x16 dots/stamps */ - gfx.mapShift = 8; /* 256x256 stamps/map */ - mask = 0x20000; /* 131072 bytes/table */ - break; - - case 3: - gfx.dotMask = 0x7fffff; /* 4096*4096 dots/map */ - gfx.stampShift = 11 + 5; /* 32x32 dots/stamps */ - gfx.mapShift = 7; /* 128x128 stamps/map */ - mask = 0x38000; /* 32768 bytes/table */ - break; - } - - /* stamp map table base address */ - gfx.mapPtr = (uint16 *)(scd.word_ram_2M + ((scd.regs[0x5a>>1].w << 2) & mask)); - - /* image buffer column offset (64 pixels/cell, minus 7 pixels to restart at cell beginning) */ - gfx.bufferOffset = (((scd.regs[0x5c>>1].byte.l & 0x1f) + 1) << 6) - 7; - - /* image buffer start index in dot units (2 pixels/byte) */ - gfx.bufferStart = (scd.regs[0x5e>>1].w << 3) & 0x7ffc0; - - /* add image buffer horizontal dot offset */ - gfx.bufferStart += (scd.regs[0x60>>1].byte.l & 0x3f); - - /* reset GFX chip cycle counter */ - gfx.cycles = cycles; - - /* update GFX chip timings (see AC3:Thunderhawk / Thunderstrike) */ - gfx.cyclesPerLine = 4 * 5 * scd.regs[0x62>>1].w; - - /* start graphics operation */ - scd.regs[0x58>>1].byte.h = 0x80; + case 3: + gfx.dotMask = 0x7fffff; /* 4096*4096 dots/map */ + gfx.stampShift = 11 + 5; /* 32x32 dots/stamps */ + gfx.mapShift = 7; /* 128x128 stamps/map */ + mask = 0x38000; /* 32768 bytes/table */ + break; } + + /* stamp map table base address */ + gfx.mapPtr = (uint16 *)(scd.word_ram_2M + ((scd.regs[0x5a>>1].w << 2) & mask)); + + /* image buffer column offset (64 pixels/cell, minus 7 pixels to restart at cell beginning) */ + gfx.bufferOffset = (((scd.regs[0x5c>>1].byte.l & 0x1f) + 1) << 6) - 7; + + /* image buffer start index in dot units (2 pixels/byte) */ + gfx.bufferStart = (scd.regs[0x5e>>1].w << 3) & 0x7ffc0; + + /* add image buffer horizontal dot offset */ + gfx.bufferStart += (scd.regs[0x60>>1].byte.l & 0x3f); + + /* reset GFX chip cycle counter */ + gfx.cycles = cycles; + + /* update GFX chip timings (see AC3:Thunderhawk / Thunderstrike) */ + gfx.cyclesPerLine = 4 * 5 * scd.regs[0x62>>1].w; + + /* start graphics operation */ + scd.regs[0x58>>1].byte.h = 0x80; } void gfx_update(int cycles) { - /* synchronize GFX chip with SUB-CPU */ - cycles -= gfx.cycles; - - /* make sure SUB-CPU is ahead */ - if (cycles > 0) + /* make sure Word-RAM is assigned to SUB-CPU in 2M mode */ + if ((scd.regs[0x02>>1].byte.l & 0x05) != 0x01) { - /* number of lines to process */ - unsigned int lines = (cycles + gfx.cyclesPerLine - 1) / gfx.cyclesPerLine; + /* synchronize GFX processing with SUB-CPU */ + cycles -= gfx.cycles; - /* check against remaining lines */ - if (lines < scd.regs[0x64>>1].byte.l) + /* make sure SUB-CPU is ahead */ + if (cycles > 0) { - /* update Vdot remaining size */ - scd.regs[0x64>>1].byte.l -= lines; + /* number of lines to process */ + unsigned int lines = (cycles + gfx.cyclesPerLine - 1) / gfx.cyclesPerLine; - /* increment cycle counter */ - gfx.cycles += lines * gfx.cyclesPerLine; - } - else - { - /* process remaining lines */ - lines = scd.regs[0x64>>1].byte.l; - - /* clear Vdot remaining size */ - scd.regs[0x64>>1].byte.l = 0; - - /* end of graphics operation */ - scd.regs[0x58>>1].byte.h = 0; - - /* SUB-CPU idle on register $58 polling ? */ - if (s68k.stopped & (1<<0x08)) + /* check against remaining lines */ + if (lines < scd.regs[0x64>>1].byte.l) { - /* sync SUB-CPU with GFX chip */ - s68k.cycles = scd.cycles; + /* update Vdot remaining size */ + scd.regs[0x64>>1].byte.l -= lines; - /* restart SUB-CPU */ - s68k.stopped = 0; + /* increment cycle counter */ + gfx.cycles += lines * gfx.cyclesPerLine; + } + else + { + /* process remaining lines */ + lines = scd.regs[0x64>>1].byte.l; + + /* clear Vdot remaining size */ + scd.regs[0x64>>1].byte.l = 0; + + /* 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; + + /* restart SUB-CPU */ + s68k.stopped = 0; #ifdef LOG_SCD - error("s68k started from %d cycles\n", s68k.cycles); + error("s68k started from %d cycles\n", s68k.cycles); #endif + } + + /* level 1 interrupt enabled ? */ + if (scd.regs[0x32>>1].byte.l & 0x02) + { + /* trigger level 1 interrupt */ + scd.pending |= (1 << 1); + + /* update IRQ level */ + s68k_update_irq((scd.pending & scd.regs[0x32>>1].byte.l) >> 1); + } } - /* level 1 interrupt enabled ? */ - if (scd.regs[0x32>>1].byte.l & 0x02) + /* render lines */ + while (lines--) { - /* trigger level 1 interrupt */ - scd.pending |= (1 << 1); + /* process dots to image buffer */ + gfx_render(gfx.bufferStart, scd.regs[0x62>>1].w); - /* update IRQ level */ - s68k_update_irq((scd.pending & scd.regs[0x32>>1].byte.l) >> 1); + /* increment image buffer start index for next line (8 pixels/line) */ + gfx.bufferStart += 8; } } - - /* render lines */ - while (lines--) - { - /* process dots to image buffer */ - gfx_render(gfx.bufferStart, scd.regs[0x62>>1].w); - - /* increment image buffer start index for next line (8 pixels/line) */ - gfx.bufferStart += 8; - } + } + else + { + /* GFX processing is halted */ + gfx.cycles = cycles; } } diff --git a/core/cd_hw/scd.c b/core/cd_hw/scd.c index ed02a5e..bbc6bff 100644 --- a/core/cd_hw/scd.c +++ b/core/cd_hw/scd.c @@ -932,6 +932,13 @@ static void scd_write_byte(unsigned int address, unsigned int data) /* RET bit set in 2M mode */ if (data & 0x01) { + /* check if graphics operation is running */ + if (scd.regs[0x58>>1].byte.h & 0x80) + { + /* synchronize GFX processing with SUB-CPU */ + gfx_update(s68k.cycles); + } + /* Word-RAM is returned to MAIN-CPU */ scd.dmna = 0; @@ -1196,6 +1203,13 @@ static void scd_write_word(unsigned int address, unsigned int data) /* RET bit set in 2M mode */ if (data & 0x01) { + /* check if graphics operation is running */ + if (scd.regs[0x58>>1].byte.h & 0x80) + { + /* synchronize GFX processing with SUB-CPU */ + gfx_update(s68k.cycles); + } + /* Word-RAM is returned to MAIN-CPU */ scd.dmna = 0; diff --git a/core/mem68k.c b/core/mem68k.c index 23f0d67..4908aed 100644 --- a/core/mem68k.c +++ b/core/mem68k.c @@ -3,7 +3,7 @@ * Main 68k bus handlers * * Copyright (C) 1998-2003 Charles Mac Donald (original code) - * Copyright (C) 2007-2019 Eke-Eke (Genesis Plus GX) + * Copyright (C) 2007-2022 Eke-Eke (Genesis Plus GX) * * Redistribution and use of this code or any derivative works are permitted * provided that the following conditions are met: @@ -777,7 +777,7 @@ void ctrl_io_write_byte(unsigned int address, unsigned int data) } else { - /* writing 0 to DMNA in 1M mode actually set DMNA bit */ + /* writing 0 to DMNA in 1M mode actually sets DMNA bit */ data |= 0x02; /* update BK0-1 & DMNA bits */ @@ -787,19 +787,32 @@ void ctrl_io_write_byte(unsigned int address, unsigned int data) } else { - /* writing 0 in 2M mode does nothing */ + /* writing 0 to DMNA in 2M mode does nothing */ if (data & 0x02) { /* Word-RAM is assigned to SUB-CPU */ scd.dmna = 1; - /* clear RET bit */ + /* clear RET bit and update BK0-1 & DMNA bits */ scd.regs[0x03>>1].byte.l = (scd.regs[0x03>>1].byte.l & ~0xc3) | (data & 0xc2); + + /* check if graphics operation is running */ + 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) */ + if (gfx.cycles < cycles) + { + gfx.cycles = cycles; + } + } return; } } - /* update BK0-1 bits */ + /* update BK0-1 bits only */ scd.regs[0x03>>1].byte.l = (scd.regs[0x02>>1].byte.l & ~0xc0) | (data & 0xc0); return; } @@ -1014,7 +1027,7 @@ void ctrl_io_write_word(unsigned int address, unsigned int data) } else { - /* writing 0 to DMNA in 1M mode actually set DMNA bit */ + /* writing 0 to DMNA in 1M mode actually sets DMNA bit */ data |= 0x02; /* update WP0-7, BK0-1 & DMNA bits */ @@ -1024,19 +1037,32 @@ void ctrl_io_write_word(unsigned int address, unsigned int data) } else { - /* writing 0 in 2M mode does nothing */ + /* writing 0 to DMNA in 2M mode does nothing */ if (data & 0x02) { /* Word-RAM is assigned to SUB-CPU */ scd.dmna = 1; - /* clear RET bit */ + /* clear RET bit and update WP0-7 & BK0-1 bits */ scd.regs[0x02>>1].w = (scd.regs[0x02>>1].w & ~0xffc3) | (data & 0xffc2); + + /* check if graphics operation is running */ + 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) */ + if (gfx.cycles < cycles) + { + gfx.cycles = cycles; + } + } return; } } - /* update WP0-7 & BK0-1 bits */ + /* update WP0-7 & BK0-1 bits only */ scd.regs[0x02>>1].w = (scd.regs[0x02>>1].w & ~0xffc0) | (data & 0xffc0); return; } diff --git a/core/mem68k.h b/core/mem68k.h index 2d325e9..da37bef 100644 --- a/core/mem68k.h +++ b/core/mem68k.h @@ -3,7 +3,7 @@ * Main 68k bus handlers * * Copyright (C) 1998-2003 Charles Mac Donald (original code) - * Copyright (C) 2007-2019 Eke-Eke (Genesis Plus GX) + * Copyright (C) 2007-2022 Eke-Eke (Genesis Plus GX) * * Redistribution and use of this code or any derivative works are permitted * provided that the following conditions are met: