#if CZ80_ENCRYPTED_ROM\r
CPU->OPBase = OPBase;\r
#endif\r
+ if (CPU->HaltState)\r
+ CPU->ICount = 0;\r
cycles -= CPU->ICount;\r
#if !CZ80_EMULATE_R_EXACTLY\r
zR = (zR + (cycles >> 2)) & 0x7f;\r
OP(0x76): // HALT\r
OP_HALT:\r
CPU->HaltState = 1;\r
- CPU->ICount = 0;\r
+// CPU->ICount = 0;\r
goto Cz80_Check_Interrupt;\r
\r
OP(0xf3): // DI\r
OP_DI:\r
zIFF = 0;\r
- RET(4)\r
+ USE_CYCLES(4)\r
+ goto Cz80_Exec_nocheck;\r
\r
OP(0xfb): // EI\r
OP_EI:\r
if (CPU->IRQState)\r
{\r
afterEI = 1;\r
- CPU->ExtraCycles += 1 - CPU->ICount;\r
- CPU->ICount = 1;\r
}\r
}\r
else zIFF2 = (1 << 2);\r
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;
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;
a &= 3;\r
if (a == 1 && ym2612.OPN.ST.address == 0x2a) /* DAC data */\r
{\r
- int scanline = get_scanline(is_from_z80);\r
- //elprintf(EL_STATUS, "%03i -> %03i dac w %08x z80 %i", Pico.snd.dac_line, scanline, d, is_from_z80);\r
+ int cycles = is_from_z80 ? z80_cyclesDone() : z80_cycles_from_68k();\r
+ //elprintf(EL_STATUS, "%03i dac w %08x z80 %i", cycles, d, is_from_z80);\r
ym2612.dacout = ((int)d - 0x80) << 6;\r
if (ym2612.dacen)\r
- PsndDoDAC(scanline);\r
+ PsndDoDAC(cycles);\r
return 0;\r
}\r
\r
return 0;\r
}\r
case 0x2b: { /* DAC Sel (YM2612) */\r
- int scanline = get_scanline(is_from_z80);\r
- if (ym2612.dacen != (d & 0x80)) {\r
- ym2612.dacen = d & 0x80;\r
- Pico.snd.dac_line = scanline;\r
- }\r
+ ym2612.dacen = d & 0x80;\r
#ifdef __GP2X__\r
- if (PicoIn.opt & POPT_EXT_FM) YM2612Write_940(a, d, scanline);\r
+ if (PicoIn.opt & POPT_EXT_FM) YM2612Write_940(a, d, get_scanline(is_from_z80));\r
#endif\r
return 0;\r
}\r
break;\r
}\r
\r
- int scanline = get_scanline(is_from_z80);\r
- PsndDoFM(scanline);\r
+ PsndDoFM(get_scanline(is_from_z80));\r
#ifdef __GP2X__\r
if (PicoIn.opt & POPT_EXT_FM)\r
return YM2612Write_940(a, d, get_scanline(is_from_z80));\r
\r
// this table is wrong and should be removed\r
// keeping it for now to compensate wrong timing elswhere, mainly for Outrunners\r
-static const int dma_timings[] = {\r
- 83, 166, 83, 83, // vblank: 32cell: dma2vram dma2[vs|c]ram vram_fill vram_copy\r
- 102, 204, 102, 102, // vblank: 40cell:\r
- 8, 16, 8, 8, // active: 32cell:\r
- 17, 18, 9, 9 // ...\r
+static const int dma_timings[] = { // Q16\r
+ // dma2vram dma2[vs|c]ram vram_fill vram_copy\r
+ // VRAM has half the width of VSRAM/CRAM, thus half the performance\r
+ ( 83<<16)/488, (166<<16)/488, (165<<16)/488, ( 83<<16)/488, // vblank 32cell\r
+ (102<<16)/488, (204<<16)/488, (203<<16)/488, (102<<16)/488, // vblank 40cell\r
+ ( 8<<16)/488, ( 16<<16)/488, ( 15<<16)/488, ( 8<<16)/488, // active 32cell\r
+ ( 9<<16)/488, ( 18<<16)/488, ( 17<<16)/488, ( 9<<16)/488 // active 40cell\r
};\r
\r
-static const int dma_bsycles[] = {\r
- (488<<8)/83, (488<<8)/166, (488<<8)/83, (488<<8)/83,\r
- (488<<8)/102, (488<<8)/204, (488<<8)/102, (488<<8)/102,\r
- (488<<8)/8, (488<<8)/16, (488<<8)/8, (488<<8)/8,\r
- (488<<8)/9, (488<<8)/18, (488<<8)/9, (488<<8)/9\r
+static const int dma_bsycles[] = { // Q16\r
+ (488<<16)/83, (488<<16)/166, (488<<16)/165, (488<<16)/83,\r
+ (488<<16)/102, (488<<16)/204, (488<<16)/203, (488<<16)/102,\r
+ (488<<16)/8, (488<<16)/16, (488<<16)/15, (488<<16)/8,\r
+ (488<<16)/9, (488<<16)/18, (488<<16)/17, (488<<16)/9\r
};\r
\r
// grossly inaccurate.. FIXME FIXXXMEE\r
-PICO_INTERNAL int CheckDMA(void)\r
+PICO_INTERNAL int CheckDMA(int cycles)\r
{\r
int burn = 0, xfers_can, dma_op = Pico.video.reg[0x17]>>6; // see gens for 00 and 01 modes\r
int xfers = Pico.m.dma_xfers;\r
int dma_op1;\r
\r
+ // safety pin\r
+ if (cycles <= 0) return 0;\r
+\r
if(!(dma_op&2)) dma_op = (Pico.video.type==1) ? 0 : 1; // setting dma_timings offset here according to Gens\r
dma_op1 = dma_op;\r
if(Pico.video.reg[12] & 1) dma_op |= 4; // 40 cell mode?\r
if(!(Pico.video.status&8)&&(Pico.video.reg[1]&0x40)) dma_op|=8; // active display?\r
- xfers_can = dma_timings[dma_op];\r
+ xfers_can = (dma_timings[dma_op] * cycles + 0xff) >> 16;\r
if(xfers <= xfers_can)\r
{\r
Pico.video.status &= ~SR_DMA;\r
if (!(dma_op & 2))\r
- burn = xfers * dma_bsycles[dma_op] >> 8; // have to be approximate because can't afford division..\r
+ burn = xfers * dma_bsycles[dma_op] >> 16;\r
Pico.m.dma_xfers = 0;\r
} else {\r
- if(!(dma_op&2)) burn = 488;\r
+ if(!(dma_op&2)) burn = cycles;\r
Pico.m.dma_xfers -= xfers_can;\r
}\r
\r
#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;
}
}
-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)
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)
// 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();
// 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");
#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();
// 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();
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();
}
}
// 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)
#define z80_int_assert(a) Cz80_Set_IRQ(&CZ80, 0, (a) ? ASSERT_LINE : CLEAR_LINE)\r
#define z80_nmi() Cz80_Set_IRQ(&CZ80, IRQ_LINE_NMI, 0)\r
\r
-#define z80_cyclesLeft (CZ80.ICount - CZ80.ExtraCycles)\r
+#define z80_cyclesLeft CZ80.ICount\r
#define z80_subCLeft(c) CZ80.ICount -= c\r
#define z80_pc() Cz80_Get_Reg(&CZ80, CZ80_PC)\r
\r
short len_use; // adjusted\r
int len_e_add; // for non-int samples/frame\r
int len_e_cnt;\r
- short dac_line;\r
+ int dac_val, dac_val2; // last DAC sample\r
+ unsigned int dac_mult; // z80 clocks per line in Q16\r
+ unsigned int dac_pos; // last DAC position in Q16\r
short psg_line;\r
unsigned int fm_mult; // samples per line in Q16\r
unsigned int fm_pos; // last FM position in Q16\r
extern struct PicoMem PicoMem;\r
extern void (*PicoResetHook)(void);\r
extern void (*PicoLineHook)(void);\r
-PICO_INTERNAL int CheckDMA(void);\r
+PICO_INTERNAL int CheckDMA(int cycles);\r
PICO_INTERNAL void PicoDetectRegion(void);\r
PICO_INTERNAL void PicoSyncZ80(unsigned int m68k_cycles_done);\r
\r
// sound/sound.c\r
PICO_INTERNAL void PsndReset(void);\r
PICO_INTERNAL void PsndStartFrame(void);\r
-PICO_INTERNAL void PsndDoDAC(int line_to);\r
+PICO_INTERNAL void PsndDoDAC(int cycle_to);\r
PICO_INTERNAL void PsndDoPSG(int line_to);\r
PICO_INTERNAL void PsndDoFM(int line_to);\r
PICO_INTERNAL void PsndClear(void);\r
\r
// samples per line (Q16)\r
Pico.snd.fm_mult = 65536LL * PicoIn.sndRate / (target_fps*target_lines);\r
+ // samples per z80 clock (Q20)\r
+ Pico.snd.dac_mult = 16 * Pico.snd.fm_mult * 15/7 / 488;\r
\r
// recalculate dac info\r
dac_recalculate();\r
Pico.snd.len_use++;\r
}\r
\r
- Pico.snd.dac_line = Pico.snd.psg_line = 0;\r
- Pico.snd.fm_pos = 0;\r
+ Pico.snd.psg_line = 0;\r
}\r
\r
-PICO_INTERNAL void PsndDoDAC(int line_to)\r
+PICO_INTERNAL void PsndDoDAC(int cyc_to)\r
{\r
- int pos, pos1, len;\r
+ int pos, len;\r
int dout = ym2612.dacout;\r
- int line_from = Pico.snd.dac_line;\r
\r
- pos = dac_info[line_from];\r
- pos1 = dac_info[line_to + 1];\r
- len = pos1 - pos;\r
+ // number of samples to fill in buffer (Q20)\r
+ len = (cyc_to * Pico.snd.dac_mult) - Pico.snd.dac_pos;\r
+\r
+ // update position and calculate buffer offset and length\r
+ pos = (Pico.snd.dac_pos+0x80000) >> 20;\r
+ Pico.snd.dac_pos += len;\r
+ len = ((Pico.snd.dac_pos+0x80000) >> 20) - pos;\r
+\r
+ // avoid loss of the 1st sample of a new block (Q rounding issues)\r
+ if (pos+len == 0)\r
+ len = 1, Pico.snd.dac_pos += 0x80000;\r
if (len <= 0)\r
return;\r
\r
- Pico.snd.dac_line = line_to + 1;\r
-\r
if (!PicoIn.sndOut)\r
return;\r
\r
+ // fill buffer, applying a rather weak order 1 bessel IIR on the way\r
+ // y[n] = (x[n] + x[n-1])*(1/2) (3dB cutoff at 11025 Hz, no gain)\r
+ // 1 sample delay for correct IIR filtering over audio frame boundaries\r
if (PicoIn.opt & POPT_EN_STEREO) {\r
short *d = PicoIn.sndOut + pos*2;\r
- for (; len > 0; len--, d+=2) *d += dout;\r
+ // left channel only, mixed ro right channel in mixing phase\r
+ *d++ += Pico.snd.dac_val2; d++;\r
+ while (--len) *d++ += Pico.snd.dac_val, d++;\r
} else {\r
short *d = PicoIn.sndOut + pos;\r
- for (; len > 0; len--, d++) *d += dout;\r
+ *d++ += Pico.snd.dac_val2;\r
+ while (--len) *d++ += Pico.snd.dac_val;\r
}\r
+ Pico.snd.dac_val2 = (Pico.snd.dac_val + dout) >> 1;\r
+ Pico.snd.dac_val = dout;\r
}\r
\r
PICO_INTERNAL void PsndDoPSG(int line_to)\r
}\r
if (!(PicoIn.opt & POPT_EN_FM))\r
memset32(PsndBuffer, 0, PicoIn.opt & POPT_EN_STEREO ? len*2 : len);\r
+ // drop pos remainder to avoid rounding errors (not entirely correct though)\r
+ Pico.snd.dac_pos = Pico.snd.fm_pos = 0;\r
}\r
\r
\r
int *buf32;\r
int stereo = (PicoIn.opt & 8) >> 3;\r
int fmlen = ((Pico.snd.fm_pos+0x8000) >> 16) - offset;\r
+ int daclen = ((Pico.snd.dac_pos+0x80000) >> 20) - offset;\r
\r
offset <<= stereo;\r
buf32 = PsndBuffer+offset;\r
return length;\r
}\r
\r
+ // Fill up DAC output in case of missing samples (Q16 rounding errors)\r
+ if (length-daclen > 0) {\r
+ short *dacbuf = PicoIn.sndOut + (daclen << stereo);\r
+ for (; length-daclen > 0; daclen++) {\r
+ *dacbuf++ += Pico.snd.dac_val;\r
+ if (stereo) dacbuf++;\r
+ }\r
+ }\r
+\r
// Add in parts of the FM buffer not yet done\r
if (length-fmlen > 0) {\r
int *fmbuf = buf32 + (fmlen << stereo);\r
{\r
static int curr_pos = 0;\r
\r
- if (ym2612.dacen && Pico.snd.dac_line < y)\r
- PsndDoDAC(y - 1);\r
+ if (ym2612.dacen)\r
+ PsndDoDAC(cycles_68k_to_z80(Pico.t.m68c_aim - Pico.t.m68c_frame_start));\r
PsndDoPSG(y - 1);\r
\r
curr_pos = PsndRender(0, Pico.snd.len_use);\r
PicoIn.writeSound(curr_pos * ((PicoIn.opt & POPT_EN_STEREO) ? 4 : 2));\r
// clear sound buffer\r
PsndClear();\r
- Pico.snd.dac_line = y;\r
}\r
\r
PICO_INTERNAL void PsndGetSamplesMS(int y)\r
Pico.m.dma_xfers = len;\r
if (Pico.m.dma_xfers < len) // lame 16bit var\r
Pico.m.dma_xfers = ~0;\r
- SekCyclesBurnRun(CheckDMA());\r
+ SekCyclesBurnRun(CheckDMA(488 - (SekCyclesDone()-Pico.t.m68c_line_start)));\r
\r
if ((source & 0xe00000) == 0xe00000) { // Ram\r
base = (u16 *)PicoMem.ram;\r
\r
static void DrawSync(int blank_on)\r
{\r
- if (Pico.m.scanline < 224 && !(PicoIn.opt & POPT_ALT_RENDERER) &&\r
+ int lines = Pico.video.reg[1]&0x08 ? 240 : 224;\r
+ if (Pico.m.scanline < lines && !(PicoIn.opt & POPT_ALT_RENDERER) &&\r
!PicoIn.skipFrame && Pico.est.DrawScanline <= Pico.m.scanline) {\r
//elprintf(EL_ANOMALY, "sync");\r
PicoDrawSync(Pico.m.scanline, blank_on);\r
{\r
case 0x00: // Data port 0 or 2\r
// try avoiding the sync..\r
- if (Pico.m.scanline < 224 && (pvid->reg[1]&0x40) &&\r
+ if (Pico.m.scanline < (pvid->reg[1]&0x08 ? 240 : 224) && (pvid->reg[1]&0x40) &&\r
!(!pvid->pending &&\r
((pvid->command & 0xc00000f0) == 0x40000010 && PicoMem.vsram[pvid->addr>>1] == d))\r
)\r