Browse Source

emulator timing fixes, VDP DMA fixes, improved DAC audio

kub 4 years ago
parent
commit
43e1401008
10 changed files with 118 additions and 98 deletions
  1. 2 0
      cpu/cz80/cz80.c
  2. 3 4
      cpu/cz80/cz80_op.c
  3. 2 1
      pico/32x/32x.c
  4. 6 16
      pico/debug.c
  5. 6 11
      pico/memory.c
  6. 19 14
      pico/pico.c
  7. 30 30
      pico/pico_cmn.c
  8. 6 4
      pico/pico_int.h
  9. 40 15
      pico/sound/sound.c
  10. 4 3
      pico/videoport.c

+ 2 - 0
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;

+ 3 - 4
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);

+ 2 - 1
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;

+ 6 - 16
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;

+ 6 - 11
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));

+ 19 - 14
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;
   }
 

+ 30 - 30
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)

+ 6 - 4
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);

+ 40 - 15
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)

+ 4 - 3
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))
        )