From: kub Date: Tue, 14 Jan 2020 22:00:44 +0000 (+0100) Subject: emulator timing fixes, VDP DMA fixes, improved DAC audio X-Git-Tag: v2.00~799 X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=43e1401008bd6f981cdb45365e96674ef3ab2a10;p=picodrive.git emulator timing fixes, VDP DMA fixes, improved DAC audio --- diff --git a/cpu/cz80/cz80.c b/cpu/cz80/cz80.c index 0326b0b8..6b9afcde 100644 --- a/cpu/cz80/cz80.c +++ b/cpu/cz80/cz80.c @@ -288,6 +288,8 @@ Cz80_Exec_End: #if CZ80_ENCRYPTED_ROM CPU->OPBase = OPBase; #endif + if (CPU->HaltState) + CPU->ICount = 0; cycles -= CPU->ICount; #if !CZ80_EMULATE_R_EXACTLY zR = (zR + (cycles >> 2)) & 0x7f; diff --git a/cpu/cz80/cz80_op.c b/cpu/cz80/cz80_op.c index f84f8e75..5d623caf 100644 --- a/cpu/cz80/cz80_op.c +++ b/cpu/cz80/cz80_op.c @@ -687,13 +687,14 @@ OP_CCF: OP(0x76): // HALT OP_HALT: CPU->HaltState = 1; - CPU->ICount = 0; +// CPU->ICount = 0; goto Cz80_Check_Interrupt; OP(0xf3): // DI OP_DI: zIFF = 0; - RET(4) + USE_CYCLES(4) + goto Cz80_Exec_nocheck; OP(0xfb): // EI OP_EI: @@ -712,8 +713,6 @@ OP_EI: if (CPU->IRQState) { afterEI = 1; - CPU->ExtraCycles += 1 - CPU->ICount; - CPU->ICount = 1; } } else zIFF2 = (1 << 2); diff --git a/pico/32x/32x.c b/pico/32x/32x.c index aa45ba7b..0f0cc4f5 100644 --- a/pico/32x/32x.c +++ b/pico/32x/32x.c @@ -269,7 +269,8 @@ void p32x_schedule_hint(SH2 *sh2, unsigned int m68k_cycles) return; // nobody cares // note: when Pico.m.scanline is 224, SH2s might // still be at scanline 93 (or so) - if (!(Pico32x.sh2_regs[0] & 0x80) && Pico.m.scanline > 224) + if (!(Pico32x.sh2_regs[0] & 0x80) && + Pico.m.scanline > (Pico.video.reg[1] & 0x08 ? 240 : 224)) return; after = (Pico32x.sh2_regs[4 / 2] + 1) * 488; diff --git a/pico/debug.c b/pico/debug.c index 50cbaf38..e617d908 100644 --- a/pico/debug.c +++ b/pico/debug.c @@ -369,42 +369,32 @@ void PDebugDumpMem(void) void PDebugZ80Frame(void) { - int lines, line_sample; + int lines; if (PicoIn.AHW & PAHW_SMS) return; - if (Pico.m.pal) { + if (Pico.m.pal) lines = 313; - line_sample = 68; - } else { + else lines = 262; - line_sample = 93; - } z80_resetCycles(); PsndStartFrame(); - if (/*Pico.m.z80Run &&*/ !Pico.m.z80_reset && (PicoIn.opt&POPT_EN_Z80)) - PicoSyncZ80(Pico.t.m68c_cnt + line_sample * 488); - if (PicoIn.sndOut) - PsndGetSamples(line_sample); - if (/*Pico.m.z80Run &&*/ !Pico.m.z80_reset && (PicoIn.opt&POPT_EN_Z80)) { PicoSyncZ80(Pico.t.m68c_cnt + 224 * 488); z80_int(); } - if (PicoIn.sndOut) - PsndGetSamples(224); // sync z80 if (/*Pico.m.z80Run &&*/ !Pico.m.z80_reset && (PicoIn.opt&POPT_EN_Z80)) { Pico.t.m68c_cnt += Pico.m.pal ? 151809 : 127671; // cycles adjusted for converter PicoSyncZ80(Pico.t.m68c_cnt); } - if (PicoIn.sndOut && ym2612.dacen && Pico.snd.dac_line < lines) - PsndDoDAC(lines - 1); - PsndDoPSG(lines - 1); + + if (PicoIn.sndOut) + PsndGetSamples(lines); timers_cycle(); Pico.t.m68c_aim = Pico.t.m68c_cnt; diff --git a/pico/memory.c b/pico/memory.c index 9fe3a085..1d9b9135 100644 --- a/pico/memory.c +++ b/pico/memory.c @@ -943,11 +943,11 @@ static int ym2612_write_local(u32 a, u32 d, int is_from_z80) a &= 3; if (a == 1 && ym2612.OPN.ST.address == 0x2a) /* DAC data */ { - int scanline = get_scanline(is_from_z80); - //elprintf(EL_STATUS, "%03i -> %03i dac w %08x z80 %i", Pico.snd.dac_line, scanline, d, is_from_z80); + int cycles = is_from_z80 ? z80_cyclesDone() : z80_cycles_from_68k(); + //elprintf(EL_STATUS, "%03i dac w %08x z80 %i", cycles, d, is_from_z80); ym2612.dacout = ((int)d - 0x80) << 6; if (ym2612.dacen) - PsndDoDAC(scanline); + PsndDoDAC(cycles); return 0; } @@ -1029,13 +1029,9 @@ static int ym2612_write_local(u32 a, u32 d, int is_from_z80) return 0; } case 0x2b: { /* DAC Sel (YM2612) */ - int scanline = get_scanline(is_from_z80); - if (ym2612.dacen != (d & 0x80)) { - ym2612.dacen = d & 0x80; - Pico.snd.dac_line = scanline; - } + ym2612.dacen = d & 0x80; #ifdef __GP2X__ - if (PicoIn.opt & POPT_EXT_FM) YM2612Write_940(a, d, scanline); + if (PicoIn.opt & POPT_EXT_FM) YM2612Write_940(a, d, get_scanline(is_from_z80)); #endif return 0; } @@ -1059,8 +1055,7 @@ static int ym2612_write_local(u32 a, u32 d, int is_from_z80) break; } - int scanline = get_scanline(is_from_z80); - PsndDoFM(scanline); + PsndDoFM(get_scanline(is_from_z80)); #ifdef __GP2X__ if (PicoIn.opt & POPT_EXT_FM) return YM2612Write_940(a, d, get_scanline(is_from_z80)); diff --git a/pico/pico.c b/pico/pico.c index f6b43cd6..2a16a0e2 100644 --- a/pico/pico.c +++ b/pico/pico.c @@ -224,40 +224,45 @@ void PicoLoopPrepare(void) // this table is wrong and should be removed // keeping it for now to compensate wrong timing elswhere, mainly for Outrunners -static const int dma_timings[] = { - 83, 166, 83, 83, // vblank: 32cell: dma2vram dma2[vs|c]ram vram_fill vram_copy - 102, 204, 102, 102, // vblank: 40cell: - 8, 16, 8, 8, // active: 32cell: - 17, 18, 9, 9 // ... +static const int dma_timings[] = { // Q16 + // dma2vram dma2[vs|c]ram vram_fill vram_copy + // VRAM has half the width of VSRAM/CRAM, thus half the performance + ( 83<<16)/488, (166<<16)/488, (165<<16)/488, ( 83<<16)/488, // vblank 32cell + (102<<16)/488, (204<<16)/488, (203<<16)/488, (102<<16)/488, // vblank 40cell + ( 8<<16)/488, ( 16<<16)/488, ( 15<<16)/488, ( 8<<16)/488, // active 32cell + ( 9<<16)/488, ( 18<<16)/488, ( 17<<16)/488, ( 9<<16)/488 // active 40cell }; -static const int dma_bsycles[] = { - (488<<8)/83, (488<<8)/166, (488<<8)/83, (488<<8)/83, - (488<<8)/102, (488<<8)/204, (488<<8)/102, (488<<8)/102, - (488<<8)/8, (488<<8)/16, (488<<8)/8, (488<<8)/8, - (488<<8)/9, (488<<8)/18, (488<<8)/9, (488<<8)/9 +static const int dma_bsycles[] = { // Q16 + (488<<16)/83, (488<<16)/166, (488<<16)/165, (488<<16)/83, + (488<<16)/102, (488<<16)/204, (488<<16)/203, (488<<16)/102, + (488<<16)/8, (488<<16)/16, (488<<16)/15, (488<<16)/8, + (488<<16)/9, (488<<16)/18, (488<<16)/17, (488<<16)/9 }; // grossly inaccurate.. FIXME FIXXXMEE -PICO_INTERNAL int CheckDMA(void) +PICO_INTERNAL int CheckDMA(int cycles) { int burn = 0, xfers_can, dma_op = Pico.video.reg[0x17]>>6; // see gens for 00 and 01 modes int xfers = Pico.m.dma_xfers; int dma_op1; + // safety pin + if (cycles <= 0) return 0; + if(!(dma_op&2)) dma_op = (Pico.video.type==1) ? 0 : 1; // setting dma_timings offset here according to Gens dma_op1 = dma_op; if(Pico.video.reg[12] & 1) dma_op |= 4; // 40 cell mode? if(!(Pico.video.status&8)&&(Pico.video.reg[1]&0x40)) dma_op|=8; // active display? - xfers_can = dma_timings[dma_op]; + xfers_can = (dma_timings[dma_op] * cycles + 0xff) >> 16; if(xfers <= xfers_can) { Pico.video.status &= ~SR_DMA; if (!(dma_op & 2)) - burn = xfers * dma_bsycles[dma_op] >> 8; // have to be approximate because can't afford division.. + burn = xfers * dma_bsycles[dma_op] >> 16; Pico.m.dma_xfers = 0; } else { - if(!(dma_op&2)) burn = 488; + if(!(dma_op&2)) burn = cycles; Pico.m.dma_xfers -= xfers_can; } diff --git a/pico/pico_cmn.c b/pico/pico_cmn.c index 5fa0b16f..8c22c977 100644 --- a/pico/pico_cmn.c +++ b/pico/pico_cmn.c @@ -22,25 +22,29 @@ #endif // sync m68k to Pico.t.m68c_aim -static void SekSyncM68k(void) +static void SekExecM68k(int cyc_do) { - int cyc_do; - pprof_start(m68k); - pevt_log_m68k_o(EVT_RUN_START); - - while ((cyc_do = Pico.t.m68c_aim - Pico.t.m68c_cnt) > 0) { - Pico.t.m68c_cnt += cyc_do; + Pico.t.m68c_cnt += cyc_do; #if defined(EMU_C68K) - PicoCpuCM68k.cycles = cyc_do; - CycloneRun(&PicoCpuCM68k); - Pico.t.m68c_cnt -= PicoCpuCM68k.cycles; + PicoCpuCM68k.cycles = cyc_do; + CycloneRun(&PicoCpuCM68k); + Pico.t.m68c_cnt -= PicoCpuCM68k.cycles; #elif defined(EMU_M68K) - Pico.t.m68c_cnt += m68k_execute(cyc_do) - cyc_do; + Pico.t.m68c_cnt += m68k_execute(cyc_do) - cyc_do; #elif defined(EMU_F68K) - Pico.t.m68c_cnt += fm68k_emulate(&PicoCpuFM68k, cyc_do, 0) - cyc_do; + Pico.t.m68c_cnt += fm68k_emulate(&PicoCpuFM68k, cyc_do, 0) - cyc_do; #endif - } +} + +static void SekSyncM68k(void) +{ + int cyc_do; + pprof_start(m68k); + pevt_log_m68k_o(EVT_RUN_START); + + while ((cyc_do = Pico.t.m68c_aim - Pico.t.m68c_cnt) > 0) + SekExecM68k(cyc_do); SekCyclesLeft = 0; @@ -68,7 +72,7 @@ static void do_hint(struct PicoVideo *pv) } } -static void do_timing_hacks_as(struct PicoVideo *pv, int vdp_slots) +static void do_timing_hacks_as(struct PicoVideo *pv, int vdp_slots, int cycles) { pv->lwrite_cnt += vdp_slots - Pico.m.dma_xfers * 2; // wrong *2 if (pv->lwrite_cnt > vdp_slots) @@ -76,13 +80,13 @@ static void do_timing_hacks_as(struct PicoVideo *pv, int vdp_slots) else if (pv->lwrite_cnt < 0) pv->lwrite_cnt = 0; if (Pico.m.dma_xfers) - SekCyclesBurn(CheckDMA()); + SekCyclesBurn(CheckDMA(cycles)); } -static void do_timing_hacks_vb(void) +static void do_timing_hacks_vb(int cycles) { if (unlikely(Pico.m.dma_xfers)) - SekCyclesBurn(CheckDMA()); + SekCyclesBurn(CheckDMA(cycles)); } static int PicoFrameHints(void) @@ -151,7 +155,7 @@ static int PicoFrameHints(void) // Run scanline: Pico.t.m68c_line_start = Pico.t.m68c_aim; - do_timing_hacks_as(pv, vdp_slots); + do_timing_hacks_as(pv, vdp_slots, CYCLES_M68K_LINE); CPUS_RUN(CYCLES_M68K_LINE); if (PicoLineHook) PicoLineHook(); @@ -192,19 +196,18 @@ static int PicoFrameHints(void) // also delay between F bit (bit 7) is set in SR and IRQ happens (Ex-Mutants) // also delay between last H-int and V-int (Golden Axe 3) Pico.t.m68c_line_start = Pico.t.m68c_aim; - do_timing_hacks_vb(); + do_timing_hacks_vb(CYCLES_M68K_VINT_LAG); CPUS_RUN(CYCLES_M68K_VINT_LAG); pv->status |= SR_F; pv->pending_ints |= 0x20; if (pv->reg[1] & 0x20) { - Pico.t.m68c_aim = Pico.t.m68c_cnt + 11; // HACK - SekSyncM68k(); + SekExecM68k(11); // HACK elprintf(EL_INTS, "vint: @ %06x [%u]", SekPc, SekCyclesDone()); SekInterrupt(6); } - cycles = SekCyclesDone(); + cycles = Pico.t.m68c_aim; if (Pico.m.z80Run && !Pico.m.z80_reset && (PicoIn.opt&POPT_EN_Z80)) { PicoSyncZ80(cycles); elprintf(EL_INTS, "zint"); @@ -221,6 +224,7 @@ static int PicoFrameHints(void) #endif // Run scanline: + do_timing_hacks_vb(CYCLES_M68K_LINE - CYCLES_M68K_VINT_LAG); CPUS_RUN(CYCLES_M68K_LINE - CYCLES_M68K_VINT_LAG); if (PicoLineHook) PicoLineHook(); @@ -256,7 +260,7 @@ static int PicoFrameHints(void) // Run scanline: Pico.t.m68c_line_start = Pico.t.m68c_aim; - do_timing_hacks_vb(); + do_timing_hacks_vb(CYCLES_M68K_LINE); CPUS_RUN(CYCLES_M68K_LINE); if (PicoLineHook) PicoLineHook(); @@ -267,7 +271,7 @@ static int PicoFrameHints(void) unsigned int l = PicoIn.overclockM68k * lines / 100; while (l-- > 0) { Pico.t.m68c_cnt -= CYCLES_M68K_LINE; - do_timing_hacks_vb(); + do_timing_hacks_vb(CYCLES_M68K_LINE); SekSyncM68k(); } } @@ -293,20 +297,16 @@ static int PicoFrameHints(void) // Run scanline: Pico.t.m68c_line_start = Pico.t.m68c_aim; - do_timing_hacks_as(pv, vdp_slots); + do_timing_hacks_as(pv, vdp_slots, CYCLES_M68K_LINE); CPUS_RUN(CYCLES_M68K_LINE); if (PicoLineHook) PicoLineHook(); pevt_log_m68k_o(EVT_NEXT_LINE); // sync cpus - cycles = SekCyclesDone(); + cycles = Pico.t.m68c_aim; if (Pico.m.z80Run && !Pico.m.z80_reset && (PicoIn.opt&POPT_EN_Z80)) PicoSyncZ80(cycles); - if (PicoIn.sndOut && ym2612.dacen && Pico.snd.dac_line < lines) - PsndDoDAC(lines - 1); - if (PicoIn.sndOut && Pico.snd.psg_line < lines) - PsndDoPSG(lines - 1); #ifdef PICO_CD if (PicoIn.AHW & PAHW_MCD) diff --git a/pico/pico_int.h b/pico/pico_int.h index d3da72ce..58d3da88 100644 --- a/pico/pico_int.h +++ b/pico/pico_int.h @@ -193,7 +193,7 @@ extern struct DrZ80 drZ80; #define z80_int_assert(a) Cz80_Set_IRQ(&CZ80, 0, (a) ? ASSERT_LINE : CLEAR_LINE) #define z80_nmi() Cz80_Set_IRQ(&CZ80, IRQ_LINE_NMI, 0) -#define z80_cyclesLeft (CZ80.ICount - CZ80.ExtraCycles) +#define z80_cyclesLeft CZ80.ICount #define z80_subCLeft(c) CZ80.ICount -= c #define z80_pc() Cz80_Get_Reg(&CZ80, CZ80_PC) @@ -431,7 +431,9 @@ struct PicoSound short len_use; // adjusted int len_e_add; // for non-int samples/frame int len_e_cnt; - short dac_line; + int dac_val, dac_val2; // last DAC sample + unsigned int dac_mult; // z80 clocks per line in Q16 + unsigned int dac_pos; // last DAC position in Q16 short psg_line; unsigned int fm_mult; // samples per line in Q16 unsigned int fm_pos; // last FM position in Q16 @@ -738,7 +740,7 @@ extern struct Pico Pico; extern struct PicoMem PicoMem; extern void (*PicoResetHook)(void); extern void (*PicoLineHook)(void); -PICO_INTERNAL int CheckDMA(void); +PICO_INTERNAL int CheckDMA(int cycles); PICO_INTERNAL void PicoDetectRegion(void); PICO_INTERNAL void PicoSyncZ80(unsigned int m68k_cycles_done); @@ -872,7 +874,7 @@ PICO_INTERNAL_ASM void wram_1M_to_2M(unsigned char *m); // sound/sound.c PICO_INTERNAL void PsndReset(void); PICO_INTERNAL void PsndStartFrame(void); -PICO_INTERNAL void PsndDoDAC(int line_to); +PICO_INTERNAL void PsndDoDAC(int cycle_to); PICO_INTERNAL void PsndDoPSG(int line_to); PICO_INTERNAL void PsndDoFM(int line_to); PICO_INTERNAL void PsndClear(void); diff --git a/pico/sound/sound.c b/pico/sound/sound.c index 74fb6fcd..155aa452 100644 --- a/pico/sound/sound.c +++ b/pico/sound/sound.c @@ -89,6 +89,8 @@ void PsndRerate(int preserve_state) // samples per line (Q16) Pico.snd.fm_mult = 65536LL * PicoIn.sndRate / (target_fps*target_lines); + // samples per z80 clock (Q20) + Pico.snd.dac_mult = 16 * Pico.snd.fm_mult * 15/7 / 488; // recalculate dac info dac_recalculate(); @@ -117,34 +119,46 @@ PICO_INTERNAL void PsndStartFrame(void) Pico.snd.len_use++; } - Pico.snd.dac_line = Pico.snd.psg_line = 0; - Pico.snd.fm_pos = 0; + Pico.snd.psg_line = 0; } -PICO_INTERNAL void PsndDoDAC(int line_to) +PICO_INTERNAL void PsndDoDAC(int cyc_to) { - int pos, pos1, len; + int pos, len; int dout = ym2612.dacout; - int line_from = Pico.snd.dac_line; - pos = dac_info[line_from]; - pos1 = dac_info[line_to + 1]; - len = pos1 - pos; + // number of samples to fill in buffer (Q20) + len = (cyc_to * Pico.snd.dac_mult) - Pico.snd.dac_pos; + + // update position and calculate buffer offset and length + pos = (Pico.snd.dac_pos+0x80000) >> 20; + Pico.snd.dac_pos += len; + len = ((Pico.snd.dac_pos+0x80000) >> 20) - pos; + + // avoid loss of the 1st sample of a new block (Q rounding issues) + if (pos+len == 0) + len = 1, Pico.snd.dac_pos += 0x80000; if (len <= 0) return; - Pico.snd.dac_line = line_to + 1; - if (!PicoIn.sndOut) return; + // fill buffer, applying a rather weak order 1 bessel IIR on the way + // y[n] = (x[n] + x[n-1])*(1/2) (3dB cutoff at 11025 Hz, no gain) + // 1 sample delay for correct IIR filtering over audio frame boundaries if (PicoIn.opt & POPT_EN_STEREO) { short *d = PicoIn.sndOut + pos*2; - for (; len > 0; len--, d+=2) *d += dout; + // left channel only, mixed ro right channel in mixing phase + *d++ += Pico.snd.dac_val2; d++; + while (--len) *d++ += Pico.snd.dac_val, d++; } else { short *d = PicoIn.sndOut + pos; - for (; len > 0; len--, d++) *d += dout; + *d++ += Pico.snd.dac_val2; + while (--len) *d++ += Pico.snd.dac_val; } + Pico.snd.dac_val2 = (Pico.snd.dac_val + dout) >> 1; + Pico.snd.dac_val = dout; } PICO_INTERNAL void PsndDoPSG(int line_to) @@ -258,6 +272,8 @@ PICO_INTERNAL void PsndClear(void) } if (!(PicoIn.opt & POPT_EN_FM)) memset32(PsndBuffer, 0, PicoIn.opt & POPT_EN_STEREO ? len*2 : len); + // drop pos remainder to avoid rounding errors (not entirely correct though) + Pico.snd.dac_pos = Pico.snd.fm_pos = 0; } @@ -266,6 +282,7 @@ static int PsndRender(int offset, int length) int *buf32; int stereo = (PicoIn.opt & 8) >> 3; int fmlen = ((Pico.snd.fm_pos+0x8000) >> 16) - offset; + int daclen = ((Pico.snd.dac_pos+0x80000) >> 20) - offset; offset <<= stereo; buf32 = PsndBuffer+offset; @@ -277,6 +294,15 @@ static int PsndRender(int offset, int length) return length; } + // Fill up DAC output in case of missing samples (Q16 rounding errors) + if (length-daclen > 0) { + short *dacbuf = PicoIn.sndOut + (daclen << stereo); + for (; length-daclen > 0; daclen++) { + *dacbuf++ += Pico.snd.dac_val; + if (stereo) dacbuf++; + } + } + // Add in parts of the FM buffer not yet done if (length-fmlen > 0) { int *fmbuf = buf32 + (fmlen << stereo); @@ -317,8 +343,8 @@ PICO_INTERNAL void PsndGetSamples(int y) { static int curr_pos = 0; - if (ym2612.dacen && Pico.snd.dac_line < y) - PsndDoDAC(y - 1); + if (ym2612.dacen) + PsndDoDAC(cycles_68k_to_z80(Pico.t.m68c_aim - Pico.t.m68c_frame_start)); PsndDoPSG(y - 1); curr_pos = PsndRender(0, Pico.snd.len_use); @@ -327,7 +353,6 @@ PICO_INTERNAL void PsndGetSamples(int y) PicoIn.writeSound(curr_pos * ((PicoIn.opt & POPT_EN_STEREO) ? 4 : 2)); // clear sound buffer PsndClear(); - Pico.snd.dac_line = y; } PICO_INTERNAL void PsndGetSamplesMS(int y) diff --git a/pico/videoport.c b/pico/videoport.c index d18c2cf9..d196ee4f 100644 --- a/pico/videoport.c +++ b/pico/videoport.c @@ -97,7 +97,7 @@ static void DmaSlow(int len, unsigned int source) Pico.m.dma_xfers = len; if (Pico.m.dma_xfers < len) // lame 16bit var Pico.m.dma_xfers = ~0; - SekCyclesBurnRun(CheckDMA()); + SekCyclesBurnRun(CheckDMA(488 - (SekCyclesDone()-Pico.t.m68c_line_start))); if ((source & 0xe00000) == 0xe00000) { // Ram base = (u16 *)PicoMem.ram; @@ -344,7 +344,8 @@ static NOINLINE void CommandChange(void) static void DrawSync(int blank_on) { - if (Pico.m.scanline < 224 && !(PicoIn.opt & POPT_ALT_RENDERER) && + int lines = Pico.video.reg[1]&0x08 ? 240 : 224; + if (Pico.m.scanline < lines && !(PicoIn.opt & POPT_ALT_RENDERER) && !PicoIn.skipFrame && Pico.est.DrawScanline <= Pico.m.scanline) { //elprintf(EL_ANOMALY, "sync"); PicoDrawSync(Pico.m.scanline, blank_on); @@ -363,7 +364,7 @@ PICO_INTERNAL_ASM void PicoVideoWrite(unsigned int a,unsigned short d) { case 0x00: // Data port 0 or 2 // try avoiding the sync.. - if (Pico.m.scanline < 224 && (pvid->reg[1]&0x40) && + if (Pico.m.scanline < (pvid->reg[1]&0x08 ? 240 : 224) && (pvid->reg[1]&0x40) && !(!pvid->pending && ((pvid->command & 0xc00000f0) == 0x40000010 && PicoMem.vsram[pvid->addr>>1] == d)) )