emulator timing fixes, VDP DMA fixes, improved DAC audio

This commit is contained in:
kub 2020-01-14 23:00:44 +01:00
parent b9bc876c9c
commit 43e1401008
10 changed files with 118 additions and 98 deletions

View file

@ -288,6 +288,8 @@ Cz80_Exec_End:
#if CZ80_ENCRYPTED_ROM #if CZ80_ENCRYPTED_ROM
CPU->OPBase = OPBase; CPU->OPBase = OPBase;
#endif #endif
if (CPU->HaltState)
CPU->ICount = 0;
cycles -= CPU->ICount; cycles -= CPU->ICount;
#if !CZ80_EMULATE_R_EXACTLY #if !CZ80_EMULATE_R_EXACTLY
zR = (zR + (cycles >> 2)) & 0x7f; zR = (zR + (cycles >> 2)) & 0x7f;

View file

@ -687,13 +687,14 @@ OP_CCF:
OP(0x76): // HALT OP(0x76): // HALT
OP_HALT: OP_HALT:
CPU->HaltState = 1; CPU->HaltState = 1;
CPU->ICount = 0; // CPU->ICount = 0;
goto Cz80_Check_Interrupt; goto Cz80_Check_Interrupt;
OP(0xf3): // DI OP(0xf3): // DI
OP_DI: OP_DI:
zIFF = 0; zIFF = 0;
RET(4) USE_CYCLES(4)
goto Cz80_Exec_nocheck;
OP(0xfb): // EI OP(0xfb): // EI
OP_EI: OP_EI:
@ -712,8 +713,6 @@ OP_EI:
if (CPU->IRQState) if (CPU->IRQState)
{ {
afterEI = 1; afterEI = 1;
CPU->ExtraCycles += 1 - CPU->ICount;
CPU->ICount = 1;
} }
} }
else zIFF2 = (1 << 2); else zIFF2 = (1 << 2);

View file

@ -269,7 +269,8 @@ void p32x_schedule_hint(SH2 *sh2, unsigned int m68k_cycles)
return; // nobody cares return; // nobody cares
// note: when Pico.m.scanline is 224, SH2s might // note: when Pico.m.scanline is 224, SH2s might
// still be at scanline 93 (or so) // 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; return;
after = (Pico32x.sh2_regs[4 / 2] + 1) * 488; after = (Pico32x.sh2_regs[4 / 2] + 1) * 488;

View file

@ -369,42 +369,32 @@ void PDebugDumpMem(void)
void PDebugZ80Frame(void) void PDebugZ80Frame(void)
{ {
int lines, line_sample; int lines;
if (PicoIn.AHW & PAHW_SMS) if (PicoIn.AHW & PAHW_SMS)
return; return;
if (Pico.m.pal) { if (Pico.m.pal)
lines = 313; lines = 313;
line_sample = 68; else
} else {
lines = 262; lines = 262;
line_sample = 93;
}
z80_resetCycles(); z80_resetCycles();
PsndStartFrame(); 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)) { if (/*Pico.m.z80Run &&*/ !Pico.m.z80_reset && (PicoIn.opt&POPT_EN_Z80)) {
PicoSyncZ80(Pico.t.m68c_cnt + 224 * 488); PicoSyncZ80(Pico.t.m68c_cnt + 224 * 488);
z80_int(); z80_int();
} }
if (PicoIn.sndOut)
PsndGetSamples(224);
// sync z80 // sync z80
if (/*Pico.m.z80Run &&*/ !Pico.m.z80_reset && (PicoIn.opt&POPT_EN_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 Pico.t.m68c_cnt += Pico.m.pal ? 151809 : 127671; // cycles adjusted for converter
PicoSyncZ80(Pico.t.m68c_cnt); PicoSyncZ80(Pico.t.m68c_cnt);
} }
if (PicoIn.sndOut && ym2612.dacen && Pico.snd.dac_line < lines)
PsndDoDAC(lines - 1); if (PicoIn.sndOut)
PsndDoPSG(lines - 1); PsndGetSamples(lines);
timers_cycle(); timers_cycle();
Pico.t.m68c_aim = Pico.t.m68c_cnt; Pico.t.m68c_aim = Pico.t.m68c_cnt;

View file

@ -943,11 +943,11 @@ static int ym2612_write_local(u32 a, u32 d, int is_from_z80)
a &= 3; a &= 3;
if (a == 1 && ym2612.OPN.ST.address == 0x2a) /* DAC data */ if (a == 1 && ym2612.OPN.ST.address == 0x2a) /* DAC data */
{ {
int scanline = get_scanline(is_from_z80); int cycles = is_from_z80 ? z80_cyclesDone() : z80_cycles_from_68k();
//elprintf(EL_STATUS, "%03i -> %03i dac w %08x z80 %i", Pico.snd.dac_line, scanline, d, is_from_z80); //elprintf(EL_STATUS, "%03i dac w %08x z80 %i", cycles, d, is_from_z80);
ym2612.dacout = ((int)d - 0x80) << 6; ym2612.dacout = ((int)d - 0x80) << 6;
if (ym2612.dacen) if (ym2612.dacen)
PsndDoDAC(scanline); PsndDoDAC(cycles);
return 0; return 0;
} }
@ -1029,13 +1029,9 @@ static int ym2612_write_local(u32 a, u32 d, int is_from_z80)
return 0; return 0;
} }
case 0x2b: { /* DAC Sel (YM2612) */ case 0x2b: { /* DAC Sel (YM2612) */
int scanline = get_scanline(is_from_z80); ym2612.dacen = d & 0x80;
if (ym2612.dacen != (d & 0x80)) {
ym2612.dacen = d & 0x80;
Pico.snd.dac_line = scanline;
}
#ifdef __GP2X__ #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 #endif
return 0; return 0;
} }
@ -1059,8 +1055,7 @@ static int ym2612_write_local(u32 a, u32 d, int is_from_z80)
break; break;
} }
int scanline = get_scanline(is_from_z80); PsndDoFM(get_scanline(is_from_z80));
PsndDoFM(scanline);
#ifdef __GP2X__ #ifdef __GP2X__
if (PicoIn.opt & POPT_EXT_FM) if (PicoIn.opt & POPT_EXT_FM)
return YM2612Write_940(a, d, get_scanline(is_from_z80)); return YM2612Write_940(a, d, get_scanline(is_from_z80));

View file

@ -224,40 +224,45 @@ void PicoLoopPrepare(void)
// this table is wrong and should be removed // this table is wrong and should be removed
// keeping it for now to compensate wrong timing elswhere, mainly for Outrunners // keeping it for now to compensate wrong timing elswhere, mainly for Outrunners
static const int dma_timings[] = { static const int dma_timings[] = { // Q16
83, 166, 83, 83, // vblank: 32cell: dma2vram dma2[vs|c]ram vram_fill vram_copy // dma2vram dma2[vs|c]ram vram_fill vram_copy
102, 204, 102, 102, // vblank: 40cell: // VRAM has half the width of VSRAM/CRAM, thus half the performance
8, 16, 8, 8, // active: 32cell: ( 83<<16)/488, (166<<16)/488, (165<<16)/488, ( 83<<16)/488, // vblank 32cell
17, 18, 9, 9 // ... (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[] = { static const int dma_bsycles[] = { // Q16
(488<<8)/83, (488<<8)/166, (488<<8)/83, (488<<8)/83, (488<<16)/83, (488<<16)/166, (488<<16)/165, (488<<16)/83,
(488<<8)/102, (488<<8)/204, (488<<8)/102, (488<<8)/102, (488<<16)/102, (488<<16)/204, (488<<16)/203, (488<<16)/102,
(488<<8)/8, (488<<8)/16, (488<<8)/8, (488<<8)/8, (488<<16)/8, (488<<16)/16, (488<<16)/15, (488<<16)/8,
(488<<8)/9, (488<<8)/18, (488<<8)/9, (488<<8)/9 (488<<16)/9, (488<<16)/18, (488<<16)/17, (488<<16)/9
}; };
// grossly inaccurate.. FIXME FIXXXMEE // 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 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 xfers = Pico.m.dma_xfers;
int dma_op1; 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 if(!(dma_op&2)) dma_op = (Pico.video.type==1) ? 0 : 1; // setting dma_timings offset here according to Gens
dma_op1 = dma_op; dma_op1 = dma_op;
if(Pico.video.reg[12] & 1) dma_op |= 4; // 40 cell mode? 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? 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) if(xfers <= xfers_can)
{ {
Pico.video.status &= ~SR_DMA; Pico.video.status &= ~SR_DMA;
if (!(dma_op & 2)) 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; Pico.m.dma_xfers = 0;
} else { } else {
if(!(dma_op&2)) burn = 488; if(!(dma_op&2)) burn = cycles;
Pico.m.dma_xfers -= xfers_can; Pico.m.dma_xfers -= xfers_can;
} }

View file

@ -22,25 +22,29 @@
#endif #endif
// sync m68k to Pico.t.m68c_aim // sync m68k to Pico.t.m68c_aim
static void SekExecM68k(int cyc_do)
{
Pico.t.m68c_cnt += cyc_do;
#if defined(EMU_C68K)
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;
#elif defined(EMU_F68K)
Pico.t.m68c_cnt += fm68k_emulate(&PicoCpuFM68k, cyc_do, 0) - cyc_do;
#endif
}
static void SekSyncM68k(void) static void SekSyncM68k(void)
{ {
int cyc_do; int cyc_do;
pprof_start(m68k); pprof_start(m68k);
pevt_log_m68k_o(EVT_RUN_START); pevt_log_m68k_o(EVT_RUN_START);
while ((cyc_do = Pico.t.m68c_aim - Pico.t.m68c_cnt) > 0) { while ((cyc_do = Pico.t.m68c_aim - Pico.t.m68c_cnt) > 0)
Pico.t.m68c_cnt += cyc_do; SekExecM68k(cyc_do);
#if defined(EMU_C68K)
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;
#elif defined(EMU_F68K)
Pico.t.m68c_cnt += fm68k_emulate(&PicoCpuFM68k, cyc_do, 0) - cyc_do;
#endif
}
SekCyclesLeft = 0; 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 pv->lwrite_cnt += vdp_slots - Pico.m.dma_xfers * 2; // wrong *2
if (pv->lwrite_cnt > vdp_slots) 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) else if (pv->lwrite_cnt < 0)
pv->lwrite_cnt = 0; pv->lwrite_cnt = 0;
if (Pico.m.dma_xfers) 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)) if (unlikely(Pico.m.dma_xfers))
SekCyclesBurn(CheckDMA()); SekCyclesBurn(CheckDMA(cycles));
} }
static int PicoFrameHints(void) static int PicoFrameHints(void)
@ -151,7 +155,7 @@ static int PicoFrameHints(void)
// Run scanline: // Run scanline:
Pico.t.m68c_line_start = Pico.t.m68c_aim; 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); CPUS_RUN(CYCLES_M68K_LINE);
if (PicoLineHook) PicoLineHook(); 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 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) // also delay between last H-int and V-int (Golden Axe 3)
Pico.t.m68c_line_start = Pico.t.m68c_aim; 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); CPUS_RUN(CYCLES_M68K_VINT_LAG);
pv->status |= SR_F; pv->status |= SR_F;
pv->pending_ints |= 0x20; pv->pending_ints |= 0x20;
if (pv->reg[1] & 0x20) { if (pv->reg[1] & 0x20) {
Pico.t.m68c_aim = Pico.t.m68c_cnt + 11; // HACK SekExecM68k(11); // HACK
SekSyncM68k();
elprintf(EL_INTS, "vint: @ %06x [%u]", SekPc, SekCyclesDone()); elprintf(EL_INTS, "vint: @ %06x [%u]", SekPc, SekCyclesDone());
SekInterrupt(6); SekInterrupt(6);
} }
cycles = SekCyclesDone(); cycles = Pico.t.m68c_aim;
if (Pico.m.z80Run && !Pico.m.z80_reset && (PicoIn.opt&POPT_EN_Z80)) { if (Pico.m.z80Run && !Pico.m.z80_reset && (PicoIn.opt&POPT_EN_Z80)) {
PicoSyncZ80(cycles); PicoSyncZ80(cycles);
elprintf(EL_INTS, "zint"); elprintf(EL_INTS, "zint");
@ -221,6 +224,7 @@ static int PicoFrameHints(void)
#endif #endif
// Run scanline: // Run scanline:
do_timing_hacks_vb(CYCLES_M68K_LINE - CYCLES_M68K_VINT_LAG);
CPUS_RUN(CYCLES_M68K_LINE - CYCLES_M68K_VINT_LAG); CPUS_RUN(CYCLES_M68K_LINE - CYCLES_M68K_VINT_LAG);
if (PicoLineHook) PicoLineHook(); if (PicoLineHook) PicoLineHook();
@ -256,7 +260,7 @@ static int PicoFrameHints(void)
// Run scanline: // Run scanline:
Pico.t.m68c_line_start = Pico.t.m68c_aim; 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); CPUS_RUN(CYCLES_M68K_LINE);
if (PicoLineHook) PicoLineHook(); if (PicoLineHook) PicoLineHook();
@ -267,7 +271,7 @@ static int PicoFrameHints(void)
unsigned int l = PicoIn.overclockM68k * lines / 100; unsigned int l = PicoIn.overclockM68k * lines / 100;
while (l-- > 0) { while (l-- > 0) {
Pico.t.m68c_cnt -= CYCLES_M68K_LINE; Pico.t.m68c_cnt -= CYCLES_M68K_LINE;
do_timing_hacks_vb(); do_timing_hacks_vb(CYCLES_M68K_LINE);
SekSyncM68k(); SekSyncM68k();
} }
} }
@ -293,20 +297,16 @@ static int PicoFrameHints(void)
// Run scanline: // Run scanline:
Pico.t.m68c_line_start = Pico.t.m68c_aim; 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); CPUS_RUN(CYCLES_M68K_LINE);
if (PicoLineHook) PicoLineHook(); if (PicoLineHook) PicoLineHook();
pevt_log_m68k_o(EVT_NEXT_LINE); pevt_log_m68k_o(EVT_NEXT_LINE);
// sync cpus // sync cpus
cycles = SekCyclesDone(); cycles = Pico.t.m68c_aim;
if (Pico.m.z80Run && !Pico.m.z80_reset && (PicoIn.opt&POPT_EN_Z80)) if (Pico.m.z80Run && !Pico.m.z80_reset && (PicoIn.opt&POPT_EN_Z80))
PicoSyncZ80(cycles); 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 #ifdef PICO_CD
if (PicoIn.AHW & PAHW_MCD) if (PicoIn.AHW & PAHW_MCD)

View file

@ -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_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_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_subCLeft(c) CZ80.ICount -= c
#define z80_pc() Cz80_Get_Reg(&CZ80, CZ80_PC) #define z80_pc() Cz80_Get_Reg(&CZ80, CZ80_PC)
@ -431,7 +431,9 @@ struct PicoSound
short len_use; // adjusted short len_use; // adjusted
int len_e_add; // for non-int samples/frame int len_e_add; // for non-int samples/frame
int len_e_cnt; 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; short psg_line;
unsigned int fm_mult; // samples per line in Q16 unsigned int fm_mult; // samples per line in Q16
unsigned int fm_pos; // last FM position in Q16 unsigned int fm_pos; // last FM position in Q16
@ -738,7 +740,7 @@ extern struct Pico Pico;
extern struct PicoMem PicoMem; extern struct PicoMem PicoMem;
extern void (*PicoResetHook)(void); extern void (*PicoResetHook)(void);
extern void (*PicoLineHook)(void); extern void (*PicoLineHook)(void);
PICO_INTERNAL int CheckDMA(void); PICO_INTERNAL int CheckDMA(int cycles);
PICO_INTERNAL void PicoDetectRegion(void); PICO_INTERNAL void PicoDetectRegion(void);
PICO_INTERNAL void PicoSyncZ80(unsigned int m68k_cycles_done); 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 // sound/sound.c
PICO_INTERNAL void PsndReset(void); PICO_INTERNAL void PsndReset(void);
PICO_INTERNAL void PsndStartFrame(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 PsndDoPSG(int line_to);
PICO_INTERNAL void PsndDoFM(int line_to); PICO_INTERNAL void PsndDoFM(int line_to);
PICO_INTERNAL void PsndClear(void); PICO_INTERNAL void PsndClear(void);

View file

@ -89,6 +89,8 @@ void PsndRerate(int preserve_state)
// samples per line (Q16) // samples per line (Q16)
Pico.snd.fm_mult = 65536LL * PicoIn.sndRate / (target_fps*target_lines); 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 // recalculate dac info
dac_recalculate(); dac_recalculate();
@ -117,34 +119,46 @@ PICO_INTERNAL void PsndStartFrame(void)
Pico.snd.len_use++; Pico.snd.len_use++;
} }
Pico.snd.dac_line = Pico.snd.psg_line = 0; Pico.snd.psg_line = 0;
Pico.snd.fm_pos = 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 dout = ym2612.dacout;
int line_from = Pico.snd.dac_line;
pos = dac_info[line_from]; // number of samples to fill in buffer (Q20)
pos1 = dac_info[line_to + 1]; len = (cyc_to * Pico.snd.dac_mult) - Pico.snd.dac_pos;
len = pos1 - 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) if (len <= 0)
return; return;
Pico.snd.dac_line = line_to + 1;
if (!PicoIn.sndOut) if (!PicoIn.sndOut)
return; 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) { if (PicoIn.opt & POPT_EN_STEREO) {
short *d = PicoIn.sndOut + pos*2; 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 { } else {
short *d = PicoIn.sndOut + pos; 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) PICO_INTERNAL void PsndDoPSG(int line_to)
@ -258,6 +272,8 @@ PICO_INTERNAL void PsndClear(void)
} }
if (!(PicoIn.opt & POPT_EN_FM)) if (!(PicoIn.opt & POPT_EN_FM))
memset32(PsndBuffer, 0, PicoIn.opt & POPT_EN_STEREO ? len*2 : len); 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 *buf32;
int stereo = (PicoIn.opt & 8) >> 3; int stereo = (PicoIn.opt & 8) >> 3;
int fmlen = ((Pico.snd.fm_pos+0x8000) >> 16) - offset; int fmlen = ((Pico.snd.fm_pos+0x8000) >> 16) - offset;
int daclen = ((Pico.snd.dac_pos+0x80000) >> 20) - offset;
offset <<= stereo; offset <<= stereo;
buf32 = PsndBuffer+offset; buf32 = PsndBuffer+offset;
@ -277,6 +294,15 @@ static int PsndRender(int offset, int length)
return 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 // Add in parts of the FM buffer not yet done
if (length-fmlen > 0) { if (length-fmlen > 0) {
int *fmbuf = buf32 + (fmlen << stereo); int *fmbuf = buf32 + (fmlen << stereo);
@ -317,8 +343,8 @@ PICO_INTERNAL void PsndGetSamples(int y)
{ {
static int curr_pos = 0; static int curr_pos = 0;
if (ym2612.dacen && Pico.snd.dac_line < y) if (ym2612.dacen)
PsndDoDAC(y - 1); PsndDoDAC(cycles_68k_to_z80(Pico.t.m68c_aim - Pico.t.m68c_frame_start));
PsndDoPSG(y - 1); PsndDoPSG(y - 1);
curr_pos = PsndRender(0, Pico.snd.len_use); 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)); PicoIn.writeSound(curr_pos * ((PicoIn.opt & POPT_EN_STEREO) ? 4 : 2));
// clear sound buffer // clear sound buffer
PsndClear(); PsndClear();
Pico.snd.dac_line = y;
} }
PICO_INTERNAL void PsndGetSamplesMS(int y) PICO_INTERNAL void PsndGetSamplesMS(int y)

View file

@ -97,7 +97,7 @@ static void DmaSlow(int len, unsigned int source)
Pico.m.dma_xfers = len; Pico.m.dma_xfers = len;
if (Pico.m.dma_xfers < len) // lame 16bit var if (Pico.m.dma_xfers < len) // lame 16bit var
Pico.m.dma_xfers = ~0; Pico.m.dma_xfers = ~0;
SekCyclesBurnRun(CheckDMA()); SekCyclesBurnRun(CheckDMA(488 - (SekCyclesDone()-Pico.t.m68c_line_start)));
if ((source & 0xe00000) == 0xe00000) { // Ram if ((source & 0xe00000) == 0xe00000) { // Ram
base = (u16 *)PicoMem.ram; base = (u16 *)PicoMem.ram;
@ -344,7 +344,8 @@ static NOINLINE void CommandChange(void)
static void DrawSync(int blank_on) 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) { !PicoIn.skipFrame && Pico.est.DrawScanline <= Pico.m.scanline) {
//elprintf(EL_ANOMALY, "sync"); //elprintf(EL_ANOMALY, "sync");
PicoDrawSync(Pico.m.scanline, blank_on); 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 case 0x00: // Data port 0 or 2
// try avoiding the sync.. // 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->pending &&
((pvid->command & 0xc00000f0) == 0x40000010 && PicoMem.vsram[pvid->addr>>1] == d)) ((pvid->command & 0xc00000f0) == 0x40000010 && PicoMem.vsram[pvid->addr>>1] == d))
) )