Improving Framebuffer Emulation
You and I both know uoYabause still has many graphical glitches. Some of these are caused by inaccurate framebuffer emulation.
How does the framebuffer work?
Sega Saturn draws graphics using three individual chips: the CPU, VDP1 (Video Display Processor 1), and VDP2 (Video Display Processor 2). The CPU sends images and draw commands to VDP1, which begins drawing to the “draw framebuffer.” Meanwhile, VDP2 periodically reads a separate “read framebuffer” that creates a composite image of the sprite and background layers to display on a television. Once VDP1 finishes drawing, it swaps these buffers and informs the CPU that drawing is finished. The CPU then prepares the next draw commands.
It sounds simple, but there are four major problems:
1. When are buffers swapped?
VDP1 swaps buffers after the CPU writes 0x03 to the FBCR (Framebuffer Change Register), but it seems there is a delay. Since the timing is not accurate, sometimes the framebuffer image and background images are misaligned when composited.
2. When does VDP1 start drawing?
VDP1 starts drawing when the CPU writes 0x02 to the PTMR (Plot Trigger Mode Register). But since the timing is not accurate, bad images, like the one below, are sometimes drawn.
3. When should the framebuffer be cleared?.
After the swap but prior to drawing, the framebuffer needs cleared. In the current version of the emulator, every time drawing begins, the framebuffer is forced to be cleared. This operation causes flicker. Sometimes nothing is drawn at all!
4. How and when should the CPU be informed that drawing is finished?
Since the timing is incorrect, the next command from the CPU is delayed. As a result, the frame rate drops in certain games like Sonic Jam.
I’ve been investigating these problems for a couple days by analyzing code from various games. Finally, I’ve found answers!
-
The buffers should be swapped at the first horizontal-blank-out interrupt, not the vertical-blank-out interrupt.
-
VDP1 should start drawing immediately after buffers are swapped.
-
The framebuffer should be cleared at the next vertical-blank-in interrupt, after 0x01 is written to the VBE (V-Blank Erase/Write Enable) register.
-
How and when the CPU should be informed that drawing is finished depends on the size of the command. For example, with Dracula X, check the EDSR (End Status Register) at the 60th horizontal-blank interrupt.
I’ve entirely rewritten framebuffer emulation to implement these changes. You can check out the results by watching the YouTube video below. Once beta testing is complete, version 0.4.0 of uoYabause will be released.
These improvements represent significant progress towards more accurate Sega Saturn emulation. Yet, it’s still not enough. My journey continues...
proofread by @Saturn Memories. Thank you!