I don't know why he came up with this fix, or why it seemed to help. One possibility is it's related to having a dot-based renderer? Seems unlikely, though. Cydrak suggested that perhaps Snes9X wasn't using a shared latch for all BGnxOFS registers.
But regardless ... this behavior breaks the Pac-Man homebrew game by RTS. The title screen horizontal scrolling becomes extremely shaky, because the game is performing 16-bit writes to both BG3HOFS and BG3VOFS at the same time.
Upon removing the weird logic and treating BGnHOFS writes exactly like BGnVOFS writes, the problem is resolved in Pac-Man ... and there is no observable difference in either the NTSC-J or PAL releases of Theme Park anywhere in the game that I can observe. Definitely no issue on the main map where there used to be the bug.
Note that this errant behavior is pervasive. The bug can be seen in bsnes/higan, Snes9X, SNESGT, no$sns, Kindred, and ZSNES.
Corrected higan code: (do this for all BGnHOFS registers)
Code: Select all
//BG3HOFS
case 0x2111: {
bg3.io.hoffset = data << 8 | latch.bgofs; //correct behavior
//bg3.io.hoffset = data << 8 | (latch.bgofs & ~7) | (bg3.io.hoffset >> 8 & 7); //incorrect behavior
latch.bgofs = data;
return;
}
//BG3VOFS
case 0x2112: {
bg3.io.voffset = data << 8 | latch.bgofs;
latch.bgofs = data;
return;
}
...
anomie's regs.txt:
Code: Select all
210f ww+++- BG2HOFS - BG2 Horizontal Scroll
2110 ww+++- BG2VOFS - BG2 Vertical Scroll
2111 ww+++- BG3HOFS - BG3 Horizontal Scroll
2112 ww+++- BG3VOFS - BG3 Vertical Scroll
2113 ww+++- BG4HOFS - BG4 Horizontal Scroll
2114 ww+++- BG4VOFS - BG4 Vertical Scroll
------xx xxxxxxxx
Note that these are "write twice" registers, first the low byte is
written then the high. Current theory is that writes to the register
work like this:
BGnHOFS = (Current<<8) | (Prev&~7) | ((Reg>>8)&7);
Prev = Current;
or
BGnVOFS = (Current<<8) | Prev;
Prev = Current;
Code: Select all
The registers $210d-$2114 are all write-twice to set the 16-bit value. The way
this works, the last write to any of these registers is stored in a buffer.
When a new byte is written to any register, the current register value, the
previous byte written to any of the 6 registers, and the new byte written
are combined as follows:
For BGnHOFS: (NewByte<<8) | (PrevByte&~7) | ((CurrentValue>>8)&7)
For BGnVOFS: (NewByte<<8) | PrevByte
For the most part, the details don't really matter as most games always write
two bytes to one of these registers. However, some games write only one byte,
or they do other odd things.