commanderx16 / x16-emulator

Emulator for the Commander X16 8-bit computer
384 stars 60 forks source link

YM2151 busy flag and timing #144

Open Frank-Buss opened 5 years ago

Frank-Buss commented 5 years ago

The current AUDIO_SAMPLES buffer of size 4096 results in timing issues and lost notes, because the registers are only evaluated in chunks after each 180 ms, but typical players update them with VSync with 60 Hz. Changing the buffer size to 256 improves the behavior, but it could result in audible gaps on slow PCs or on Windows (because SDL uses the standard Windows audio interface, and not ASIO, and depending on the load of the operating system and other factors).

Also currently the busy flag is commented. The effect is that an audio playback might work in the emulator, but not on the real hardware.

To fix both issues, the "advance" function in the ym2151.c file should be coupled to the CPU cycle and execution. This would require some synchronization with the audio playback frequency, and would need more work, if the emulator doesn't run at 100% on slow computers. I think the SID implementation of VICE have solved these problems, and someone could take a look at it.

Frank-Buss commented 5 years ago

Idea how to fix both problems: add a FIFO in ym2151.c for the samples. Then add a new "tick" function, which is called in sync with the 6502 CPU clock. Needs not to be called at full speed, 1 MHz or slower should work, too. In this function, call YM_advance_eg() with audio rate, so for 1 MHz tick clock and 22,050 Hz samplerate, once every 45.35 ticks on average (can be done with a fixed point arithmetic counter). All samples are stored in a FIFO. In case the FIFO gets too full, don't call YM_advance_eg and don't store more samples, in order to catch up, if the audio playback is a bit slower than calculated. "too full" could mean, if it has more samples than the audio buffer size, maybe plus the size it needs for one VSync.

Now the YM_stream_update can read the FIFO. On FIFO underflow, generate more samples with YM_advance_eg, in case the CPU was too slow, if it doesn't run at 100% on slow computers.

To implement the busy flag: Set a counter on register write. Then in the tick function, decrement the counter each tick, unless it is 0 and then reset the busy flag. If the tick function is called at (virtually) 1 MHz, and the chip is running at 4 MHz, the counter needs to be set to 16, to set the busy bit for the required 64 cycles. YM_write_reg should ignore writes, if the busy bit is set.

mattuna15 commented 4 years ago

Adding this link here so if someone picks it up they will have a starting point. https://github.com/chernandezba/zesarux/blob/master/src/audiosdl2.c