DaveTCode / GBADotnet

A C#/net core GBA emulator
MIT License
20 stars 1 forks source link

Spyro shows possibly wrong priorities in game #41

Closed DaveTCode closed 2 years ago

DaveTCode commented 2 years ago

image

Looks like a priority issue to me but could also be tile rendering issues

DaveTCode commented 2 years ago

This is "in game" and is running mode 0 with all 4 backgrounds enabled as well as objects but no windows.

BG0 has priority 0 with tile map base 31 BG1 has priority 1 with tile map base 30 BG2 has priority 2 with tile map base 29 BG3 has priority 3 with tile map base 28

BG0 should be just tile 512 which is all palette 0 so shouldn't be appearing. BG1 contains the sprite looking things that appear in the above screenshot and the rest is black tiles BG2 should be the background

mgba looks like this image

DaveTCode commented 2 years ago

Best guess is that one of BG0/1 is being placed above BG3 despite it containing tiles which are supposed to be Backdrop

DaveTCode commented 2 years ago

It seems like the scanline combine routine is finding no backgrounds which are both enabled and have non-zero value for the palette.

DaveTCode commented 2 years ago

Ah interesting, this is actually because the backgrounds (and presumably objects) are being disabled for some weird reason.

Printing out the values of dispcnt after dispcnt + 1 is written to gives me the following values cycling whilst that screen is supposed to be up.

0x00005340=0b0101_0011_0100_0000
0x00001F40=0b0001_1111_0100_0000
0x00004240=0b0100_0010_0100_0000
0x00004F40=0b0100_1111_0100_0000

First 8 bits mean mode 0, GBA mode, Frame 0, HBlank OAM accessible, 1D OAM mapping, forced blank off. They're consistent.

The next 5 are for enabling bg and obj and they cycle through various options. Looking at what line these writes happen shows:

So if the write to 4240 didn't happen then this would work I think. 1F40 is the write which doesn't enable window which I think makes more sense for this game.

The real question then is whether a write is happening at the wrong time, the wrong value is being written or no write should be happening at all.

DaveTCode commented 2 years ago

Started looking into where each of the DISPCNT set register calls come from: 0x1f40 - 0x080114B8 (instruction 7050) whilst in Thumb/IRQ 0x4240 - 0x08012826 (instruction 7050) whilst in Thumb/System 0x4F40 - 0x08006864 (instruction 7050) whilst in Thumb/IRQ 0x5340 - 0x080056B8 (instruction 7050) whilst in Thumb/IRQ

7050 is STRB r0, [r1,#1] and r1 in all cases is DISPCNT with r0 correctly being the high byte of DISPCNT. So no instruction issues (as expected, this has all been well tested already)

A disassembly of spyro shows the following at the suspect call:

08012818  0000E006  b         0x8012828
0801281A  00002280  mov       r2,0x80
0801281C  000004D2  lsl       r2,r2,0x13
0801281E  00007851  ldrb      r1,[r2,0x1]
08012820  00002021  mov       r0,0x21
08012822  00004240  neg       r0,r0
08012824  00004008  and       r0,r1
08012826  00007050  strb      r0,[r2,0x1]

Clearly the most sequential instructions that could be used here are from 0801281A as the previous is an unconditional branch. These instructions seem fairly straightforward. Load R2 with 0x80, left shift that value by 0x13 to get 0x0400_0000 (DISPCNT), load DISPCNT + 1 into r1, Fill r0 with 0-0x21 which is 0xFFFFFFDF then AND with the high byte of DISPCNT to put the result in r0 and write it back to DISPCNT + 1.

A worked example of that with DISPCNT containing 0x1f40 has r1 containing 0x1F, r0 containing 0xFFFF_FFDF and then r0 getting masked to stay as 0x1F.

 r0:00000000   r1:00000000   r2:FFFFFFFF   r3:03006D04
 r4:030010BC   r5:00000000   r6:030010BC   r7:030010BC
 r8:00000000   r9:00000000  r10:00000000  r11:00000000
r12:00000273  r13:03007ED0  r14:08011E13  r15:0801281E
cpsr: 6000003F -ZC---- Thm System
spsr: 6000003F -ZC---- Thm System
Cycle: 735866353
0801281A: 2280   Move/compare/add/sub #imm

 r0:00000000   r1:00000000   r2:00000080   r3:03006D04
 r4:030010BC   r5:00000000   r6:030010BC   r7:030010BC
 r8:00000000   r9:00000000  r10:00000000  r11:00000000
r12:00000273  r13:03007ED0  r14:08011E13  r15:08012820
cpsr: 2000003F --C---- Thm System
spsr: 2000003F --C---- Thm System
Cycle: 735866354
0801281C: 04D2   MOV Shifted reg

 r0:00000000   r1:00000000   r2:04000000   r3:03006D04
 r4:030010BC   r5:00000000   r6:030010BC   r7:030010BC
 r8:00000000   r9:00000000  r10:00000000  r11:00000000
r12:00000273  r13:03007ED0  r14:08011E13  r15:08012822
cpsr: 0000003F ------- Thm System
spsr: 0000003F ------- Thm System
Cycle: 735866355
0801281E: 7851   LDR #imm/STR #imm

 r0:00000000   r1:00000042   r2:04000000   r3:03006D04
 r4:030010BC   r5:00000000   r6:030010BC   r7:030010BC
 r8:00000000   r9:00000000  r10:00000000  r11:00000000
r12:00000273  r13:03007ED0  r14:08011E13  r15:08012824
cpsr: 0000003F ------- Thm System
spsr: 0000003F ------- Thm System
Cycle: 735866358
08012820: 2021   Move/compare/add/sub #imm

 r0:00000021   r1:00000042   r2:04000000   r3:03006D04
 r4:030010BC   r5:00000000   r6:030010BC   r7:030010BC
 r8:00000000   r9:00000000  r10:00000000  r11:00000000
r12:00000273  r13:03007ED0  r14:08011E13  r15:08012826
cpsr: 0000003F ------- Thm System
spsr: 0000003F ------- Thm System
Cycle: 735866359
08012822: 4240   NEG R0, R0

 r0:FFFFFFDF   r1:00000042   r2:04000000   r3:03006D04
 r4:030010BC   r5:00000000   r6:030010BC   r7:030010BC
 r8:00000000   r9:00000000  r10:00000000  r11:00000000
r12:00000273  r13:03007ED0  r14:08011E13  r15:08012828
cpsr: 8000003F N------ Thm System
spsr: 8000003F N------ Thm System
Cycle: 735866360
08012824: 4008   AND R0, R1

 r0:00000042   r1:00000042   r2:04000000   r3:03006D04
 r4:030010BC   r5:00000000   r6:030010BC   r7:030010BC
 r8:00000000   r9:00000000  r10:00000000  r11:00000000
r12:00000273  r13:03007ED0  r14:08011E13  r15:0801282A
cpsr: 0000003F ------- Thm System
spsr: 0000003F ------- Thm System
Cycle: 735866361
08012826: 7050   LDR #imm/STR #imm

Contains a dump of those lines being executed in my emulator with my half arsed disassembly.

Spoiler. 42 is not the answer. The LDRB is returning the wrong value from DISPCNT. What could that possibly be???

DaveTCode commented 2 years ago

Turns out that my ReadRegisterByte function for the PPU forgot to align the address for the half word read so reads of DISPCNT+1 ended up getting the byte in DISPCNT+2.

3 hours debugging for a 14 character fix. Emulation development in a nutshell.