Well I found the issue. Nothing too crazy although really not sure how best to fix it.
Basically it writes to the registers twice in a frame.
First one happens after the real3d status bit flips.
Second time happens during v-blank or when IRQ2 is fired.
Looks like this
swap buffers
Tile generator registers 2
tile gen 64 = 80008000 <-- first write turns on line scrolling
Tile generator registers 2
tile gen 68 = 80008000
Tile generator registers 33
tile gen 60 = 80000000
Tile generator registers 33
tile gen 64 = 80000000 <-- second write turns it off
Tile generator registers 33
tile gen 68 = 80000000
Tile generator registers 33
tile gen 6C = 80000000
Tile generator registers 33
tile gen 10 = 00000002
Tile generator registers 35
tile gen 10 = 00000004
Tile generator registers 35
tile gen 10 = 00000008
WritePolygonRAM 38
WriteHighCullingRAM 46
flush 46
Tile generator registers 46
tile gen 0C = 00000003
swap buffers
The numbers at the end indicate at what % of the frame they were written at. So 33 is 33% of the frame time. Also irq2 fires at that point ..
Anyway we use the number that is written during v-blank, since that is the last value written. The first value is essentially written at 66% of the time after v-blank, so really in the middle of the frame somewhere. The gfx are at the bottom so changes probably show up mid frame.
Maybe the only way to emulate that is to draw a line of the tilegen for every time slice, instead of just at the end of the frame. Not sure I care to emulate at this level but I guess it would fix this particular issue.