LIJI32 / SameBoy

Game Boy and Game Boy Color emulator written in C
https://sameboy.github.io/
Other
1.64k stars 207 forks source link

Star Trek 25th Anniversary: Misaligned Border #278

Closed alyosha-tas closed 2 years ago

alyosha-tas commented 4 years ago

The border in the intro text screen is misaligned. You can see it in this video for example, where it's zoomed in and really obvious:

https://www.youtube.com/watch?v=KsthKsr_2Sw&t=32s

You can also see it in this video, where it appears to be played on a gameboy player:

https://www.youtube.com/watch?v=HCmIQ24AMgo&t=62s

However, there are 2 videos where the border is properly aligned, one is played on SGB:

https://www.youtube.com/watch?v=bEArrvHV6wQ&t=181s

The other I'm not sure about:

https://www.youtube.com/watch?v=K-4r30hHwsA

The game uses the Window in a non-standard way. The top few scanlines are part of the window, then it turns off the window and the normal background is used for the text part, then turns the window back on for the bottom of the screen.

If the background is emulated normally, then it is in fact misaligned. If you look at the 2 videos with proper alignment, there is a clear blank vertical line on the left side of the screen. So, for some reason the background is being shifted to the right one pixel for the alignment to work out.

I have confirmed on my own Gameboy Player that the border is mis-aligned. I don't have a SGB or original GB personally to test on.

In Sameboy the border is mis-aligned regardless of which console is selected.

LIJI32 commented 4 years ago

Oh, this one is very interesting! There are some window-related edges cases that, on pre-CGB devices, cause the PPU and LCD (or ICD chip) to become horizontally unsynced for the rest of the line, in various ways. To enhance the fun, on a DMG (and possibly on MGBs too), the way LCD-PPU desyncs behave depends on both LCD revision and CPU revision. I haven't confirmed this is indeed the case here, but it looks like it considering the first two videos are CGB and newer devices, while the 3rd one is an SGB (The last one appears to be emulated). Currently SameBoy only emulates some of the edge cases that trigger this bug, due to the obvious complexity of it.

LIJI32 commented 4 years ago

The black pixels on the left also heavily hint this is the case.

JL2210 commented 4 years ago

I can test this later today on an SGB1

JL2210 commented 4 years ago

Original SGB: IMG_20200801_185440 IMG_20200801_185428 SameBoy latest master in SGB mode: Screenshot 2020-08-01 at 7 07 45 PM Screenshot 2020-08-01 at 7 07 23 PM

nitro2k01 commented 2 years ago

Since I have a logic analyzer setup (even if it's just a Chinese 8 channel Saleae clone) I decided to look into this. Currently the only target I can trace is a SGB.

I've written a simple test ROM that does similar things to Star Trek 25th, but with simpler graphics to make the logic trace easier to read. This is a reference of what the test ROM looks like in BGB (and on my GBA SP as well). If a desync happens, a portion in the middle will be shifted to the right. The pixel pattern is %01000111 Ie, 1 light pixel, 1 dark pixel, 3 light pixels, 3 dark pixels.

bgb00067

Here's the logic trace. It's recorded at 24 MHz. I would have liked to record at 48 MHz for slightly better time resolution, but for some reason this device won't work with PulseView at 48 MHz.

Note that "HBL" is badly named. This is a short sync pulse and doesn't indicate the duration of the HBlank period. Pixels are nominally sampled at the falling edge of CLK (if HBL==0, ignore the random CLK pulse in the middle of the HBL pulse.) or the falling edge of HBL.

Top: trace of the start of a window line. (Ie top and bottom part of the screen.) Bottom: trace of the start of a BG line. (Ie middle part of the screen.)

test_window_line

test_bg_line

As you can see... 1) the HBL pulse is much shorter in the case of BG. 2) There's a whole CLK pulse difference in timing between the two types of line.

I don't see any reason why the LCD revision would matter if the signals coming out of the SoC look like this. However there are a few possibilities, such as glitch pulses that are too short/low in voltage level to be picked up by my LA setup, or that the signals from a different SoC revision look different. More info on the exact revision of SoC and LCD needed, if you have a pre-GBC boy that doesn't desync.

But my theory so far would be that this is a logic error in the SoC that starts the next line too early by mistake if the window was disabled, rather than a timing race condition.

I'm going to expand the functionality of the test ROM so you can adjust various parameters since it's possible that the exact value of various registers matter. But I thought I'd dump what I have so far so I don't delay it forever and never get it done.

Also attached: the test ROM, the raw logic capture at 24 MHz. 1 byte per sample, the signals are as follows:

D0: Reset D1: Pixel clock D2: Reset D3: Pixel data, bit 0 D4: Vblank (1=VBlank) D5: Pixel data, bit 1 D6: Joy D7: HBlank

windesync.zip

desynctest-24M.zip

nitro2k01 commented 2 years ago

Info dump number 2!

Summary of the glitch

The following applies only to pre-GBC hardware. It may reportedly be dependent on the SoC revision and LCD, although from what I've seen so far this seems unlikely.

When the window is disabled after it has been activated once by a WY/WX hit, there's an extra pixel inserted on every subsequent line if a number of conditions are true.

The effect of this glitch is that a pixel that becomes glitched is output as a pixel colored with BG palette entry 0. (That is, the color described by bits 0 and 1 of BGP. The rest of the line is delayed and shifted on pixel to the right.

A special case of this glitch is when WX==0, which causes the window to be shifted depending on the value of SCX. This subset of the glitch is emulated for example in BGB.

It appears that the pixel timing is mostly unaffected by the glitch (see for example test case 4 of the logic capture below) although this is not the case for "low" values of WX. I haven't systemized the timing behavior fully in this session.

In my logic captures, I've not seen this glitch cause the PPU to output more (or less) than 160 pixels on a line. (I set my video conversion script to warn for this.)

How to use the test ROM

I've also written a test ROM to easily test and visualize different scenarios. Source code will be released later. This is a description on how to use it, and interesting things based on some of the functions. The ROM will draw various test patterns that I thought were going to be useful during the development. Use the D pad to navigate the menu. Hold A and press a D pad direction to change a setting. The setting changes by one count per press. No key-repeat, sorry.

Logic captures

Below are gzipped logic captures as raw data, as well as videos of each capture. As before, the files are captured from my SGB at 24 MHz and the data encoding is as follows:

D0: Reset D1: Pixel clock D2: Reset D3: Pixel data, bit 0 D4: Vblank (1=VBlank) D5: Pixel data, bit 1 D6: Joy D7: HBlank

The videos are encoded as FFV1, and so should be lossless.

videos.zip

Case 1: general mucking about

vlcsnap-2022-07-20-03h23m56s967

desync1-general.raw.gz

Case 2: Near the left side of the screen (0<=WX<=8)

vlcsnap-2022-07-20-03h26m53s536

desync2-near-leftside.raw.gz

Case 3: Near the right side of the screen ($A4<=WX<=$A8)

vlcsnap-2022-07-20-03h31m16s652

desync3-near-rightside.raw.gz

Case 4: Timing not affected in the middle of the screen

vlcsnap-2022-07-20-03h33m16s106

desync4-trace

desync4-timing-middleofline.raw.gz

Case 5: Changing SCX while WX==0

vlcsnap-2022-07-20-03h36m36s069

desync5-change-scx-while-wx-is-0.raw.gz

nitro2k01 commented 2 years ago

Forgot the ROM image...

windesync-2022-07-20.zip

nitro2k01 commented 2 years ago

Also worth pointing out that tbsp also discovered the glitch and made some test ROMs for it in 2021.

https://github.com/gbdev/pandocs/issues/376

LIJI32 commented 2 years ago

Thanks a lot for this huge info dump! I should fix this bug for 0.15.2.

nitro2k01 commented 2 years ago

A special case of this glitch is when WX==0, which causes the window to be shifted depending on the value of SCX. This subset of the glitch is emulated for example in BGB.

I had a brainfart when writing that sentence. What I mean by that is that there seems to be a variant of the same issue that happens when WX==0 and the window is enabled as normal. In this case, the window X position is shifted, this is well known. Both when (SCX&7)==6 and (SCX&7)==7 it's shifted the same number of pixels. This indicates a hiccup in the BG pixel pipeline which is similar in nature, and likely has the same underlying mechanism, to the glitch discussed here. This hiccup happens offscreen, but should be visible in the logic capture during the HSync pulse, when dummy pixels of a tile are rendered.

nitro2k01 commented 2 years ago

Unfortunately, we're not done yet...

Validation ROM

I made a validation ROM for this issue and SameBoy doesn't pass as of 1fedb81

The premise of the ROM is that there's a check mark with a line striking through it to make a cross. The check mark is filled with a checkerboard pattern. Sprites are used to erase the strike, however if any line is misaligned the sprites miss by one pixel and end up showing the strike. An easy way to see how this works is by running the ROM in GBC mode with custom palettes enabled.

The first check mark is drawn by modulating the value of SCX and WX every line using an HBlank interrupt. (The check mark is actually stored skewed in VRAM and unskewed as an effect.) The values are generated in pairs to trigger the glitch on every line. The first 24 lines of the check mark test WX values between 0 and 7. This was done mostly to try to hide the repeated glitch pixels off screen, though this didn't work so note that the three glitch pixels are part of the reference.

The rest of the first check mark has a drop shadow on the left side. This is drawn using the glitch pixels.

The second check mark is using combinations of SCX and WX intended to not trigger the glitch. Any reasonably accurate emulator that doesn't attempt to emulate the glitch should match the reference by default. Any glitches here indicate a false positive in the way the desync glitch is implemented. Note that the second check mark does not have a drop shadow on the left side (which would be drawn using glitch pixels) although it does have a shadow the longer line, which is just drawn using normal BG graphics.

ROM download: windesync-validate-2022-07-22.zip

Reference

windesync-validate-2022-07-22-reference-new

This is captured from my SGB, in the same way as described above. My DMG (A/B/C according to which.gb) behaves identically. Updated with corrected reference image.

And the logic capture in case it's useful: windesync-validate-2022-07-22-reference.raw.gz

SameBoy

Screenshot_20220722_020713

This is what SameBoy looks like running the validation ROM.

nitro2k01 commented 2 years ago

Ok, something is wrong with my reference image. Those three glitch pixels should not be 2 pixels wide. I'm investigating why they ended up like that in the image. Assume there may be other errors in the reference image as well.

nitro2k01 commented 2 years ago

I've updated the reference image above. I had been experimenting with triggering on the positive edge instead of the negative edge of the pixel clock and forgot. (D'oh!) This puts into question the pixel by pixel integrity of the earlier images/videos as well. I'll re-export those later. I've added a fixed reference image of the validation ROM above though.

LIJI32 commented 2 years ago

Now it's actually fixed :)

nitro2k01 commented 2 years ago

Still not quite... the glitch line of pixels is still misaligned one pixel to the left. I was lazy and didn't add sprites there, which would have made this failure state more obvious, but here are closeups:

Reference

Screenshot_20220722_190835

SameBoy

Screenshot_20220722_190735

LIJI32 commented 2 years ago

Good catch, fixed now