Long ago, anomie observed a horizontal scrolling bug in Theme Park. The fix he came up with is pasted below in code blocks, but essentially it was to only update the top eight bits with the new byte on BGnHOFS writes, and for the low eight bits ... the top five came from the previous register write, and the bottom three from the previous register value.
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)
Again, I don't have my scanline renderer anymore. I can't say if reverting this will break Theme Park for other emulators again or not, but ... the behavior is almost surely wrong. I don't know when or how Theme Park started working correctly in higan without this hack.
...
anomie's regs.txt:
Thanks to BMF54123 for bringing this bug to my attention.
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:
//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;
}
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;
}
Again, I don't have my scanline renderer anymore. I can't say if reverting this will break Theme Park for other emulators again or not, but ... the behavior is almost surely wrong. I don't know when or how Theme Park started working correctly in higan without this hack.
...
anomie's regs.txt:
Code:
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;
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:
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.
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.
Thanks to BMF54123 for bringing this bug to my attention.