The term "sewer"
was also coined by GoldS in his legendary
Sonic 3 and Knuckles Glitches and Oversights series, and refers to the area below the level where the normal layout ends, and insanity ensues.
The reason why this happens is that,
much like before, the developers didn't set the vertical size for levels they thought you would never get to the bottom of, leaving it at the default value of $1000 pixels. But what exactly is down there?
First, you might've noticed I left something out when I talked about loopbacks being shifted up from the actual level:
So what's in that little corner, past the actual level and underneath the loopback? Well, turns out that after the layout for Plane A comes the layout for Plane B, so if you go down there, you can see background chunks in the foreground layer and get a different perspective on the
background tile replacement trick.
If you go below this area, though, you'll find the same pattern repeating over and over. It repeats because it's reading the same row of chunks over and over, and that brings me to the other thing I glossed over in the last post: how does the game "know" where each row is supposed to start?
Sonic Retro wiki gives us the answer. Before the actual chunk data, the layout contains 64 word-sized pointers which don't make much sense inside the ROM, but when the layout is copied to RAM address $8000, they become pointers to the start of each chunk row in RAM, alternating between foreground and background rows:
00 70 00 04 00 18 00 08 80 88 8B 08 80 F8 8B 0C
81 68 8B 10 81 D8 8B 14 82 48 8B 18 82 B8 8B 1C
83 28 8B 20 83 98 8B 24 84 08 00 00 84 78 00 00
84 E8 00 00 85 58 00 00 85 C8 00 00 86 38 00 00
86 A8 00 00 87 18 00 00 87 88 00 00 87 F8 00 00
88 68 00 00 88 D8 00 00 89 48 00 00 89 B8 00 00
8A 28 00 00 8A 98 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 B4 26 00 00 38 38 00 00
Foreground rows Background rows
After a while, the pointers peter out and are all left at zero, which accounts for the repeating chunk rows in the sewers. But what is a zeroed pointer pointing at?
A naïve answer would be to say that since these are RAM pointers, and RAM address $0000 stores chunk definitions, the sewer rows are paradoxically reading chunk definitions as a chunk layout. In reality, though, when the pointers are loaded to an address register, they get sign-extended to 32-bit addresses. Because the pointers are normally $8000-based, they get sign-extended to address $FFFF8000, which maps to 68000 RAM. The zeroed pointers however, are sign-extended to address $00000000, which ends up mapping to the beginning of the ROM!
That means when you're in the sewers, what you're looking at, and possibly even walking and jumping on, is actually the
68000 vector table at the start of the ROM.
Vectors: dc.l $00000000, EntryPoint, ErrorTrap, ErrorTrap ; 0
dc.l ErrorTrap, ErrorTrap, ErrorTrap, ErrorTrap ; 4
dc.l ErrorTrap, ErrorTrap, ErrorTrap, ErrorTrap ; 8
dc.l ErrorTrap, ErrorTrap, ErrorTrap, ErrorTrap ; 12
dc.l ErrorTrap, ErrorTrap, ErrorTrap, ErrorTrap ; 16
dc.l ErrorTrap, ErrorTrap, ErrorTrap, ErrorTrap ; 20
dc.l ErrorTrap, ErrorTrap, ErrorTrap, ErrorTrap ; 24
dc.l H_int_jump, ErrorTrap, V_int_jump, ErrorTrap ; 28
dc.l ErrorTrap, ErrorTrap, ErrorTrap, ErrorTrap ; 32
dc.l ErrorTrap, ErrorTrap, ErrorTrap, ErrorTrap ; 36
dc.l ErrorTrap, ErrorTrap, ErrorTrap, ErrorTrap ; 40
dc.l ErrorTrap, ErrorTrap, ErrorTrap, ErrorTrap ; 44
dc.l ErrorTrap, ErrorTrap, ErrorTrap, ErrorTrap ; 48
dc.l ErrorTrap, ErrorTrap, ErrorTrap, ErrorTrap ; 52
dc.l ErrorTrap, ErrorTrap, ErrorTrap, ErrorTrap ; 56
dc.l ErrorTrap, ErrorTrap, ErrorTrap, ErrorTrap ; 60