From 724db457da104aa9298bdeb861e540e0e2ab42fd Mon Sep 17 00:00:00 2001 From: kub Date: Sat, 13 Jan 2024 12:07:13 +0100 Subject: [PATCH] core, handle background color DMA (aka fantom bitmap) --- pico/draw.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++- pico/pico.c | 1 + pico/pico_int.h | 1 + pico/videoport.c | 10 ++++++ 4 files changed, 97 insertions(+), 1 deletion(-) diff --git a/pico/draw.c b/pico/draw.c index 66b4fca5..40903e74 100644 --- a/pico/draw.c +++ b/pico/draw.c @@ -1599,7 +1599,59 @@ void BackFill(int bgc, int sh, struct PicoEState *est) // -------------------------------------------- -void PicoDoHighPal555_8bit(int sh, int line, struct PicoEState *est) +static u16 *BgcDMAbase; +static u32 BgcDMAsrc, BgcDMAmask; +static int BgcDMAlen, BgcDMAoffs; + +#ifndef _ASM_DRAW_C +static +#endif +// handle DMA to background color +int BgcDMA(u16 *pd, int len, struct PicoEState *est) +{ + int xl = (len == 320 ? 38 : 33); // DMA slots during HSYNC + int upscale = (est->rendstatus & PDRAW_SOFTSCALE) && len < 320; + + if (BgcDMAlen > 0) { + // BG color DMA under way. TODO for now handles the line as all background. + int i, l = len; + u16 *q = upscale ? DefOutBuff : pd; + u16 t; + + if ((est->rendstatus & PDRAW_BORDER_32) && !upscale) + q += (320-len) / 2; + + BgcDMAlen -= (l>>1)+xl; + if (BgcDMAlen < 0) + // partial line + l += 2*BgcDMAlen; + + for (i = 0; i < l; i += 2) { + // TODO use ps to overwrite only real bg pixels + t = BgcDMAbase[BgcDMAsrc++ & BgcDMAmask]; + q[i] = q[i+1] = PXCONV(t); + } + BgcDMAsrc += xl; // HSYNC DMA + + t = est->HighPal[Pico.video.reg[7] & 0x3f]; + while (i < len) q[i++] = t; // fill partial line with BG + + if (upscale) { + switch (PicoIn.filter) { + case 3: h_upscale_bl4_4_5(pd, 320, q, 256, len, f_nop); break; + case 2: h_upscale_bl2_4_5(pd, 320, q, 256, len, f_nop); break; + case 1: h_upscale_snn_4_5(pd, 320, q, 256, len, f_nop); break; + default: h_upscale_nn_4_5(pd, 320, q, 256, len, f_nop); break; + } + } + return 1; + } + return 0; +} + +// -------------------------------------------- + +static void PicoDoHighPal555_8bit(int sh, int line, struct PicoEState *est) { unsigned int *spal, *dpal; unsigned int cnt = (sh ? 1 : est->SonicPalCount+1); @@ -1695,6 +1747,9 @@ void FinalizeLine555(int sh, int line, struct PicoEState *est) else if ((PicoIn.AHW & PAHW_SMS) && (est->Pico->video.reg[0] & 0x20)) len -= 8, ps += 8; + if (BgcDMA(pd, len, est)) + return; + if ((est->rendstatus & PDRAW_SOFTSCALE) && len < 320) { if (len >= 240 && len <= 256) { pd += (256-len)>>1; @@ -2111,6 +2166,35 @@ void PicoDrawRefreshSprites(void) } } +void PicoDrawBgcDMA(u16 *base, u32 source, u32 mask, int dlen, int sl) +{ + struct PicoEState *est = &Pico.est; + int len = (est->Pico->video.reg[12]&1) ? 320 : 256; + int xl = (est->Pico->video.reg[12]&1) ? 38 : 33; // DMA slots during HSYNC + + BgcDMAbase = base; + BgcDMAsrc = source; + BgcDMAmask = mask; + BgcDMAlen = dlen; + BgcDMAoffs = 0; + + // handle slot offset in 1st line + if (sl-12 > 0) // active display output only starts at slot 12 + BgcDMAoffs = 2*(sl-12); + else if (sl < 0) { // DMA starts before active display + BgcDMAsrc += 2*-sl; + BgcDMAlen -= 2*-sl; + } + + // skip 1st line if it had been drawn already + if (Pico.est.DrawScanline > Pico.m.scanline) { + len -= BgcDMAoffs; + BgcDMAsrc += (len>>1)+xl; + BgcDMAlen -= (len>>1)+xl; + BgcDMAoffs = 0; + } +} + // also works for fast renderer void PicoDrawUpdateHighPal(void) { diff --git a/pico/pico.c b/pico/pico.c index ced4091e..ad57b6ec 100644 --- a/pico/pico.c +++ b/pico/pico.c @@ -190,6 +190,7 @@ int PicoReset(void) // create an empty "dma" to cause 68k exec start at random frame location Pico.t.m68c_line_start = Pico.t.m68c_aim; + PicoDrawBgcDMA(NULL, 0, 0, 0, 0); PicoVideoFIFOWrite(rand() & 0x1fff, 0, 0, PVS_CPURD); SekFinishIdleDet(); diff --git a/pico/pico_int.h b/pico/pico_int.h index e74a4ac2..0d336822 100644 --- a/pico/pico_int.h +++ b/pico/pico_int.h @@ -704,6 +704,7 @@ int CM_compareRun(int cyc, int is_sub); void PicoDrawInit(void); PICO_INTERNAL void PicoFrameStart(void); void PicoDrawRefreshSprites(void); +void PicoDrawBgcDMA(u16 *base, u32 source, u32 mask, int len, int sl); void PicoDrawSync(int to, int blank_last_line, int limit_sprites); void BackFill(int reg7, int sh, struct PicoEState *est); void FinalizeLine555(int sh, int line, struct PicoEState *est); diff --git a/pico/videoport.c b/pico/videoport.c index bd744a3b..fe8e5ebd 100644 --- a/pico/videoport.c +++ b/pico/videoport.c @@ -522,6 +522,7 @@ static void DmaSlow(int len, u32 source) u32 a = Pico.video.addr | (Pico.video.addr_u << 16), e; u16 *r, *base = NULL; u32 mask = 0x1ffff; + int lc = SekCyclesDone()-Pico.t.m68c_line_start; elprintf(EL_VDPDMA, "DmaSlow[%i] %06x->%04x len %i inc=%i blank %i [%u] @ %06x", Pico.video.type, source, a, len, inc, (Pico.video.status&SR_VB)||!(Pico.video.reg[1]&0x40), @@ -603,6 +604,15 @@ static void DmaSlow(int len, u32 source) case 3: // cram Pico.m.dirtyPal = 1; r = PicoMem.cram; + if (inc == 0 && (Pico.video.reg[7] & 0x3f) == ((a/2) & 0x3f)) { // bg color DMA + PicoVideoSync(1); + int sl = VdpFIFO.fifo_hcounts[lc/clkdiv]; + if (sl > VdpFIFO.fifo_hcounts[0]-5) // hint delay is 5 slots + sl = (s8)sl; + // TODO this is needed to cover timing inaccuracies + if (sl <= 12) sl = -2; + PicoDrawBgcDMA(base, source, mask, len, sl); + } for (; len; len--) { r[(a / 2) & 0x3f] = base[source++ & mask] & 0xeee; -- 2.39.2