From 0e2e188e1e93e293d11663df07cbb89582442704 Mon Sep 17 00:00:00 2001 From: kub Date: Fri, 30 Jun 2023 19:12:53 +0000 Subject: [PATCH] sound, improve ym2612 timers implementation --- pico/memory.c | 71 ++++++++++++++++++++++++++++++------------------- pico/pico_int.h | 9 +++---- 2 files changed, 47 insertions(+), 33 deletions(-) diff --git a/pico/memory.c b/pico/memory.c index a9398b8e..2f373cd6 100644 --- a/pico/memory.c +++ b/pico/memory.c @@ -1041,7 +1041,7 @@ static int get_scanline(int is_from_z80) if (is_from_z80) { // ugh... compute by dividing cycles since frame start by cycles per line // need some fractional resolution here, else there may be an extra line - int cycles_line = cycles_68k_to_z80(488 << 8); // cycles per line, as Q8 + int cycles_line = cycles_68k_to_z80(488 << 8)+1; // cycles per line, as Q8 int cycles_z80 = (z80_cyclesLeft<0 ? Pico.t.z80c_aim:z80_cyclesDone())<<8; int cycles = cycles_line * Pico.t.z80_scanline; // approximation by multiplying with inverse @@ -1063,45 +1063,65 @@ static int get_scanline(int is_from_z80) return Pico.m.scanline; } +#define ym2612_update_status(xcycles) \ + if (xcycles >= Pico.t.timer_a_next_oflow) \ + ym2612.OPN.ST.status |= (ym2612.OPN.ST.mode >> 2) & 1; \ + if (xcycles >= Pico.t.timer_b_next_oflow) \ + ym2612.OPN.ST.status |= (ym2612.OPN.ST.mode >> 2) & 2 + /* probably should not be in this file, but it's near related code here */ void ym2612_sync_timers(int z80_cycles, int mode_old, int mode_new) { int xcycles = z80_cycles << 8; - /* check for overflows */ - if ((mode_old & 4) && xcycles >= Pico.t.timer_a_next_oflow) - ym2612.OPN.ST.status |= 1; - - if ((mode_old & 8) && xcycles >= Pico.t.timer_b_next_oflow) - ym2612.OPN.ST.status |= 2; + // update timer status + ym2612_update_status(xcycles); - /* update timer a */ + // update timer a if (mode_old & 1) - while (xcycles > Pico.t.timer_a_next_oflow) + while (xcycles >= Pico.t.timer_a_next_oflow) Pico.t.timer_a_next_oflow += Pico.t.timer_a_step; - if ((mode_old ^ mode_new) & 1) // turning on/off + // turning on/off + if ((mode_old ^ mode_new) & 1) { if (mode_old & 1) Pico.t.timer_a_next_oflow = TIMER_NO_OFLOW; - else - Pico.t.timer_a_next_oflow = xcycles + Pico.t.timer_a_step; + else { + /* The internal tick of the YM2612 takes 144 clock cycles (with clock + * being OSC/7), or 67.2 z80 cycles. Timers are run once each tick. + * Starting a timer takes place at the next tick, so xcycles needs to be + * rounded up to that: t = next tick# = (xcycles / TICK_ZCYCLES) + 1 + */ + unsigned t = ((xcycles * (((1<<20)/TIMER_A_TICK_ZCYCLES)+1))>>20) + 1; + Pico.t.timer_a_next_oflow = t*TIMER_A_TICK_ZCYCLES + Pico.t.timer_a_step; + } } + if (mode_new & 1) elprintf(EL_YMTIMER, "timer a upd to %i @ %i", Pico.t.timer_a_next_oflow>>8, z80_cycles); - /* update timer b */ + // update timer b if (mode_old & 2) - while (xcycles > Pico.t.timer_b_next_oflow) + while (xcycles >= Pico.t.timer_b_next_oflow) Pico.t.timer_b_next_oflow += Pico.t.timer_b_step; + // turning on/off if ((mode_old ^ mode_new) & 2) { if (mode_old & 2) Pico.t.timer_b_next_oflow = TIMER_NO_OFLOW; - else - Pico.t.timer_b_next_oflow = xcycles + Pico.t.timer_b_step; + else { + /* timer b has a divider of 16 which runs in its own counter. It is not + * reset by loading timer b. The first run of timer b after loading is + * therefore shorter by up to 15 ticks. + */ + unsigned t = ((xcycles * (((1<<20)/TIMER_A_TICK_ZCYCLES)+1))>>20) + 1; + int step = Pico.t.timer_b_step - TIMER_A_TICK_ZCYCLES*(t&15); + Pico.t.timer_b_next_oflow = t*TIMER_A_TICK_ZCYCLES + step; + } } + if (mode_new & 2) elprintf(EL_YMTIMER, "timer b upd to %i @ %i", Pico.t.timer_b_next_oflow>>8, z80_cycles); } @@ -1130,6 +1150,7 @@ static int ym2612_write_local(u32 a, u32 d, int is_from_z80) switch (addr) { + // NB, OD2 A/V sync HACK: lower timer step by 1/4 z80 cycle (=64 in Q8) case 0x24: // timer A High 8 case 0x25: { // timer A Low 2 int TAnew = (addr == 0x24) ? ((ym2612.OPN.ST.TA & 0x03)|(((int)d)<<2)) @@ -1142,7 +1163,7 @@ static int ym2612_write_local(u32 a, u32 d, int is_from_z80) ym2612.OPN.ST.TA = TAnew; //ym2612.OPN.ST.TAC = (1024-TAnew)*18; //ym2612.OPN.ST.TAT = 0; - Pico.t.timer_a_step = TIMER_A_TICK_ZCYCLES * (1024 - TAnew); + Pico.t.timer_a_step = TIMER_A_TICK_ZCYCLES * (1024 - TAnew) - 64; elprintf(EL_YMTIMER, "timer a set to %i, %i", 1024 - TAnew, Pico.t.timer_a_next_oflow>>8); } return 0; @@ -1155,7 +1176,7 @@ static int ym2612_write_local(u32 a, u32 d, int is_from_z80) ym2612.OPN.ST.TB = d; //ym2612.OPN.ST.TBC = (256-d) * 288; //ym2612.OPN.ST.TBT = 0; - Pico.t.timer_b_step = TIMER_B_TICK_ZCYCLES * (256 - d); // 262800 + Pico.t.timer_b_step = TIMER_B_TICK_ZCYCLES * (256 - d) - 64; elprintf(EL_YMTIMER, "timer b set to %i, %i", 256 - d, Pico.t.timer_b_next_oflow>>8); } return 0; @@ -1163,11 +1184,11 @@ static int ym2612_write_local(u32 a, u32 d, int is_from_z80) int old_mode = ym2612.OPN.ST.mode; int cycles = is_from_z80 ? z80_cyclesDone() : z80_cycles_from_68k(); - ym2612.OPN.ST.mode = d; - elprintf(EL_YMTIMER, "st mode %02x", d); ym2612_sync_timers(cycles, old_mode, d); + ym2612.OPN.ST.mode = d; + /* reset Timer a flag */ if (d & 0x10) ym2612.OPN.ST.status &= ~1; @@ -1213,17 +1234,11 @@ static int ym2612_write_local(u32 a, u32 d, int is_from_z80) } -#define ym2612_read_local() \ - if (xcycles >= Pico.t.timer_a_next_oflow) \ - ym2612.OPN.ST.status |= (ym2612.OPN.ST.mode >> 2) & 1; \ - if (xcycles >= Pico.t.timer_b_next_oflow) \ - ym2612.OPN.ST.status |= (ym2612.OPN.ST.mode >> 2) & 2 - static u32 ym2612_read_local_z80(void) { int xcycles = z80_cyclesDone() << 8; - ym2612_read_local(); + ym2612_update_status(xcycles); elprintf(EL_YMTIMER, "timer z80 read %i, sched %i, %i @ %i|%i", ym2612.OPN.ST.status, Pico.t.timer_a_next_oflow >> 8, @@ -1235,7 +1250,7 @@ static u32 ym2612_read_local_68k(void) { int xcycles = z80_cycles_from_68k() << 8; - ym2612_read_local(); + ym2612_update_status(xcycles); elprintf(EL_YMTIMER, "timer 68k read %i, sched %i, %i @ %i|%i", ym2612.OPN.ST.status, Pico.t.timer_a_next_oflow >> 8, diff --git a/pico/pico_int.h b/pico/pico_int.h index d8b7160b..4944bb7d 100644 --- a/pico/pico_int.h +++ b/pico/pico_int.h @@ -873,16 +873,15 @@ void ym2612_unpack_state(void); #define TIMER_NO_OFLOW 0x70000000 -// NB ~0.2% timers speed up (1/8(A), 2(B) z80 cycles), HACK for A/V sync in OD2 // tA = 72 * (1024 - TA) / M, with M = mclock/2 -#define TIMER_A_TICK_ZCYCLES (cycles_68k_to_z80(256LL* 72*2)-32) // Q8 +#define TIMER_A_TICK_ZCYCLES cycles_68k_to_z80(256LL* 72*2) // Q8 // tB = 16*72 * ( 256 - TB) / M -#define TIMER_B_TICK_ZCYCLES (cycles_68k_to_z80(256LL*16*72*2)-32*16) // Q8 +#define TIMER_B_TICK_ZCYCLES cycles_68k_to_z80(256LL*16*72*2) // Q8 #define timers_cycle(ticks) \ - if (Pico.t.timer_a_next_oflow > 0 && Pico.t.timer_a_next_oflow < TIMER_NO_OFLOW) \ + if (Pico.t.timer_a_next_oflow < TIMER_NO_OFLOW) \ Pico.t.timer_a_next_oflow -= ticks << 8; \ - if (Pico.t.timer_b_next_oflow > 0 && Pico.t.timer_b_next_oflow < TIMER_NO_OFLOW) \ + if (Pico.t.timer_b_next_oflow < TIMER_NO_OFLOW) \ Pico.t.timer_b_next_oflow -= ticks << 8; \ ym2612_sync_timers(0, ym2612.OPN.ST.mode, ym2612.OPN.ST.mode); -- 2.39.5