
Zelda Screen Transitions Are Undefined Behaviour - Kaali
https://gridbugs.org/zelda-screen-transitions-are-undefined-behaviour/
======
yc-kraln
I don't think I would call this undefined behavior... the behavior is defined
by what happens on the hardware when it's executed!

There are many, many classical effects on raster hardware which are
accomplished by changing registers within the horizontal blanking period...
copper bars, mode 7, certain paralax scrolling. When you're on a resource
limited system it becomes an art to get the most out of the platform. Look at
the difference between Mario 64 and Conker's Bad Fur Day... or Genji: Days of
the Blade (PS3) vs Persona 5 (PS3) Even with modern consoles, there is a
marked improvement in the apparent visual quality over the lifetime of the
device, as developers learn how to squeeze more and more out of the platform.

~~~
papln
"Unefined" refers to the spec, not the hardware.

[https://en.wikipedia.org/wiki/Undefined_behavior](https://en.wikipedia.org/wiki/Undefined_behavior)

This case appears to be "undocumented scenario" or "unsupported use-case",
though.

~~~
kazinator
Do we know that the developers didn't confirm this hack with the hardware
people?

It could be "defined by e-mail".

~~~
philipov
You are playing semantics to try to justify it. If it's not in the _official_
spec, it's undefined behavior.

~~~
ohazi
Does anyone actually care? This was embedded software designed to run on
exactly one system, not some libc designed to run on 20 architectures ranging
from 8-bit microcontrollers to VLIW supercomputers.

~~~
stuart78
What I find interesting is looking for creative ways to prioritize the game
experience even when the 'official spec' didn't support it. They could have
given up by accepting that the spec represented the limit of what could be
done, but instead pushed to find a better way.

I hadn't put thought into how important that scroll effect is to the game, but
if there was a clean wipe between scenes it would have been tremendously
distracting. This technique really is essential to the feeling of immersion.

------
daniel5151
I actually had to wrestle with this exact effect while working on wideNES [1].
By saving a screenshot of the screen at each frame alongside with it's
PPUSCROLL value, it's possible to gradually build-up a map of the level as
it's explored. Moreover, on subsequent playthroughs of the same level, it's
possible to sync the map with the on-screen action, effectively enabling a
"widescreen" mode for old NES games (with certain limitations).

Lots of games used funky scrolling mechanics, typically to create status bars,
but of all the different games I tested with, TLOZ was by-far the weirdest,
requiring an entire special case to get working!

I don't have any screenshots of my own, but some japanese website recently
covered wideNES, posting screenshots of it working with the original Legent of
Zelda.[2]

[1]
[http://prilik.com/blog/2018/08/24/wideNES.html](http://prilik.com/blog/2018/08/24/wideNES.html)

[2] [https://emulog.net/fc-nes-emulator-anese-how-to-use-
widenes/](https://emulog.net/fc-nes-emulator-anese-how-to-use-widenes/)

~~~
anon_cow1111
*Note to mobile/metered internet users: first link contains 30MB+ of gif images, click at own risk.

~~~
OJFord
Why isn't there an HTTP request header like 'Accept-Content-Length' to limit
maximum response size?

~~~
tenebrisalietum
Sometimes is really expensive for the server to determine the size of the
response before actually responding, e.g. some database lookups where it may
not be known how many rows are returnable until the lookup is actually done.

Then there are situations where response content length is not known, such as
streaming over HTTP.

Last, if a certain "Accept-Content-Length" became standard, like 8MB,
developers (such as those for the ad industry) would just create javascript
libraries that would download large files in 8MB chunks and sidestep it.

------
jedberg
It feels like programming used to be a much harder job in the past. You not
only had to figure out the program logic, but you had to work within very
tight hardware constraints.

Reading articles like this, or about the Atari and how the code would double
as a sprite in pac-man, or how 3D was rendered in Wolfenstein, makes me think
one had to be much more clever back then.

~~~
cdumler
The catch about developing things back then is that you were working with duct
tape and chicken wire. The 6502 CPU was developed literally by creating a hand
drawn design. Each transistor laid out created additional complexity that
humans had to understand. Therefore, making it as simple as possible by
reducing the gates used to a minimum was important. This also meant that not
all combinations inputs were valid (or event checked).

The 6502 has a few "undocumented" instructions due the fact that if you have
certain on/off input pin set, you're actually crossing gates used for multiple
instructions. These may crash the chip or do non-useful things, but a few do
something useful in vary specific circumstances. The trick to developing on
these old systems is to experiment with the chips to understand what the
system does in various circumstances.

What I liked about that time was that it was possible to truly understand
everything about a system because nothing is hidden from you by software
drivers. The were good times.

~~~
ubermonkey
This reminder of undocumented behavior reminded me of The story of Mel, a Real
Programmer.

See

[http://www.pbm.com/~lindahl/mel.html](http://www.pbm.com/~lindahl/mel.html)

~~~
soulofmischief
I'd never seen that photo before. Mel looks a bit like Brad Pitt from far
away.

~~~
ubermonkey
Until very recently, I assumed the story was mostly apocryphal, but I happened
to look it up about a year ago and learned that the actual Mel had been
identified, and that there was even that picture. Really cool, considering I
first saw the story of Mel in probably 1989.

------
MobiusHorizons
Undefined behavior in C or C++ is possible because the language specification
is built to deal with different hardware architectures, so it's not possible
to build portable code. In the case of something like NES game development,
there is only one hardware target, so the actual observed hardware behavior
can be relied upon when doing things that aren't explicitly documented. In
this case it is possible to know before hand exactly what will happen because
there is only one hardware target. Undefined behavior in a language like C is
unknowable at compile time because its behavior has not been specified by the
language, and it can't be specified by the hardware. Technically I guess you
are correct that it is undefined behavior, but in practice it's pretty
different IMO.

~~~
codebje
I'm not sure it's really true that the NES represents a single hardware
target. There's two families of CPUs (2A03, 2A07) with different clock speeds
depending on whether the device is NTSC or PAL, within each family there's a
half dozen or more revisions, DRAM controller chips changed frequently causing
many interesting variations in how and when the object attribute memory could
be read (or not read, as the case may be).

And that's just the NES devices! The Famicoms were different again, there were
at least two licensed clones, and dozens of unlicensed clones (if you, eg,
wanted your game to sell in Russia, you'd care about being compatible with the
Dendy as well as the genuine NES).

------
Bluecobra
Thanks for posting this... as a non-programmer, I really enjoy reading how the
games I grew up with worked. If you enjoyed reading this, there's a great
Youtube channel called Retro Game Mechanics Explained:

[https://www.youtube.com/channel/UCwRqWnW5ZkVaP_lZF7caZ-g/vid...](https://www.youtube.com/channel/UCwRqWnW5ZkVaP_lZF7caZ-g/videos)

~~~
cableshaft
I didn't know this channel existed and it's very interesting. Thanks for
sharing it.

------
klodolph
There are a few games that do diagonal scrolling. In general it's very
difficult to do well on the NES, and you will likely have to live with some
amount of glitching--unless you have extra name table RAM on the cartridge,
which is fairly rare.

See [http://bootgod.dyndns.org:7777/](http://bootgod.dyndns.org:7777/) for a
database of the hardware inside each cartridge.

~~~
einr
_There are a few games that do diagonal scrolling._

Paperboy comes to mind. It's smooth and looks good without any noticable
glitching, and without using custom chips, too. Not sure how they did it.

~~~
klodolph
Paperboy draws a bunch of black sprites along the left side of the screen to
cover up the glitches. Depending on the game this can be acceptable, but you
only have a budget of 8 sprites per scanline and 64 total sprites, and this
technique can eat up a lot of that budget.

Edit: as sibling comment noted, Paperboy _does_ have custom logic on the
cartridge, a 74HC161 4-bit counter. I think this is just used to switch
between CHR banks.

~~~
coldpie
There's an interesting parallel in Atari 2600 development here. When the
screen is being drawn, the program must update the GPU registers in real-time
with the progress of the electron beam across the screen. If the program
doesn't update these registers quickly enough, graphic glitches will occur.
Most games will poll the game state and prep for updating registers during
each HBLANK period, but often this would take too long for certain scanlines
and you'd end up with short black lines along the left margin. This was a very
common issue, you can see it in lots of screenshots here:
[https://videogamecritic.com/2600tt.htm?e=85998#rev459](https://videogamecritic.com/2600tt.htm?e=85998#rev459)

This wasn't a big problem, because TVs of the time often had big overscan
areas (areas that were rendered, but covered by the TV's physical bezel).
However, it was visible on many TVs, and today it is very visible under
emulation. Activision's programmers found this unacceptable. Rather than
perform the very difficult, or even impossible, task of making their code run
faster than the HBLANK period, they instead chose to render the first cm or so
of each scanline intentionally black! Notice the width of the screen in this
screenshot compared to other nearby games:
[https://videogamecritic.com/2600ff.htm#rev203](https://videogamecritic.com/2600ff.htm#rev203)

More crazy tricks like this are described in the excellent "Racing the Beam"
book by Nick Montfort and Ian Bogost.

~~~
klodolph
Given that the 2600 has the same CPU core and predates the NES/Famicom by six
years, it's not surprising that a lot of the same tricks apply :)

For the NES, depending on the registers in question, it can be easy or hard to
update them during horizontal blank. Changing the scroll registers is easy,
the PPU was designed to make that possible, and gave you a sprite zero hit
test so you could get the timing correct--which is how Super Mario Bros. draws
the HUD at the top of the screen.

With some extra hardware you can make this easier and get some cool effects
like parallax. The NES exposes a NMI line to the 6502 on the cartridge
connector, and you can wire up some logic on the cartridge to signal NMI for
every scanline. Battletoads uses this effectively. Atari 2600 lacks NMI
because the 6507 doesn't have a pin for it.

Certain registers are much more difficult to change during horizontal blank,
but not impossible. For example, palette entries. This requires very precise
timing so it was very rare to see it. Indiana Jones and the Last Crusade
changes the palette entries mid-frame, but only for the title screen.

~~~
toast0
> gave you a sprite zero hit test so you could get the timing correct

This was a workaround to avoid Atari patents. On the 2600 you could write to a
register to halt the CPU until horizontal blanking (commonly written as sta
WSYNC), to get perfectly synchronized to the next scanline. Sprite zero is
more flexible, but the timing isn't as precise.

~~~
klodolph
Given that the NES had NMI and the 2600 didn't, I think that NMI probably
should have been wired to horizontal blank to begin with. I'm sure that there
is some reason why this wasn't done, but I can't understand why.

~~~
jdsully
That would have wasted precious cycles for games that didn’t need it. The ‘n’
in NMI stands for non-maskable.

~~~
bonzini
You can always mask it (enable/disable) at the place that triggers the NMI.

~~~
jdsully
Adding a a gate with jellybean logic was a noticeable expense in the 80s and
would still be today.

------
coldpie
This is a great description of a commonly-used technique for splitting the
screen in NES games that scroll smoothly. It may or may not have been
intended, but this is a common technique for games that have a "status bar".
Super Mario Bros 3 is another obvious example, but even Super Mario Bros uses
it long before then for its top status bar. I first read about it in the
excellent "I Am Error" book by Nathan Altice, but googling around for "nes
sprite zero split" turns up plenty of other articles, too.

~~~
einr
Nitpicking, but SMB3 uses the MMC3 chip which "adds an IRQ timer to allow
split screen scrolling without the sacrifice of sprite 0" (Wikipedia) so it
does not use this technique.

SMB1 actually also does not use the sprite zero split technique because it
never scrolls vertically. Its status bar is just a bunch of fixed background
tiles.

~~~
coldpie
Ah, didn't know that about the MMC3!

Regarding SMB1, I'm quite sure it uses the sprite 0 thing to keep the status
bar stationary while the level scrolls smoothly beneath it by setting the
scroll register only after when the status bar is done drawing. See more
thorough description here:
[https://retrocomputing.stackexchange.com/questions/1898/how-...](https://retrocomputing.stackexchange.com/questions/1898/how-
can-i-create-a-split-scroll-effect-in-an-nes-game)

~~~
papln
The article is confusing. The headline/lede claim is about partial vertical
scrolling. The middle is a long detour to partial horizontal scrolling (well
documented, including SMB1), and then the end goes back to talk about partial
vertical scrolling (Zelda).

------
pubby
The NES designers goofed and made the size of the view window (nametable) 240
pixels tall. This makes vertical scrolling awkward as it throws a non-power-
of-two divisor into the math. The NES doesn't have a division instruction -
only bit shifts, so having to divide by 240 is a real pain!

Also, Y-scrolling wasn't completely figured out until late in the NES's life.
The register writes needed to do so are very strange, and Zelda certainly
doesn't do it correctly!

~~~
simcop2387
I believe that's one of the reasons that games such as Super Mario Bros 3 used
additional hardware in the cartridge to do the y scrolling. The memory mapper
had special support for just y scrolling and scanline counting.

[http://wiki.nesdev.com/w/index.php/MMC3](http://wiki.nesdev.com/w/index.php/MMC3)

~~~
pubby
Oh, you don't need special hardware to do y-scrolling correctly. It's just a
strange set of writes: $2006, $2005, $2005, $2006. MMC3 is for the scanline
counter, which allowed SMB3 to have the score bar on the bottom of the screen.

~~~
kgabis
The way this scanline counter was implemented is quite clever. From nesdev
wiki
([https://wiki.nesdev.com/w/index.php/MMC3](https://wiki.nesdev.com/w/index.php/MMC3)):

 _The counter is based on the following trick: whenever rendering is turned on
in the PPU, it fetches nametable and BG pattern tiles from dots 0-255 and
320-340 of a scanline and fetches sprite patterns from dots 256-319, even if
no sprites are visible. Because of this, if BG uses the left pattern table
($0000), and if sprites always use the right pattern table ($1000), A12 will
remain low during all nametable and BG pattern fetches, and high during all
sprite pattern fetches, causing it to oscillate exactly one time per scanline
and 241 times per frame._

------
and0
Vertical scrolling, and emulating the weird side-effects of the registers
being written to, was the hardest part of recreating the NES using 3D meshes.
It took me a few weekends to get Zelda 2's intro working reliably. I wrote
about it a bit myself (probably got a few details wrong or simplified them)
here:

[http://n3s.io/index.php?title=How_It_Works](http://n3s.io/index.php?title=How_It_Works)

~~~
maaaats
What a cool project!

------
tinus_hn
Weird to have this limitation that you can’t vertically scroll mid-frame, when
it turns out you can if you just circumvent the blockade.

------
baruchthescribe
This reminds me a lot of Mode X which, although a funky 320x240 mode with
square pixels built in to standard VGA, only became popular after Michael
Abrash popularized it in Dr Dobbs. And then there was the utterly gorgeous
mode Q - 256x256 with 256 colors. No muls or shifts - high byte is Y and low
byte is X.

~~~
bloopernova
That "Graphics Programming Black Book" is available online here:
[http://www.drdobbs.com/parallel/graphics-programming-
black-b...](http://www.drdobbs.com/parallel/graphics-programming-black-
book/184404919)

Mode X or Q reminds me of the amazing Mode 7 SNES graphics used to great
smooth effect in F-Zero and many other titles.

------
duxup
It's always interesting how the NES cartridges had their own hardware that
could expand the system's capability. Allowing for simple cartridges for
simple games and more expensive cartridges for more advanced games.

~~~
jordanmorgan10
The same idea extended to the SNES too if I recall, a quick dig up on
Wikipedia:

"The system was designed to accommodate the ongoing development of a variety
of enhancement chips integrated in game cartridges to be competitive into the
next generation."

~~~
duxup
I wonder if we'll ever get back to that sort of thing... I guess with digital
downloads not so much, but i really like the idea.

~~~
toast0
We have some sorts of similar things. EyeToy and Kinect were additional
hardware added to the system for games; not on the same media as the games
though. The difference is expansion hardware today is always going to be
subordinate to the main system --- the economics and data transfer realities
don't work out to put a polygon processing enhancement in a USB add-on; but
the cartridge slot has immense potential and it could also be cheaper to use
the base system with enhancements than to release a whole new enhanced system
(although, it's not clear to me how much the base genesis/megadrive benefits
the 32x)

------
shultays
It is not really undefined behavior when you have single hardware that will
behave in a very well defined way. Old console games have all sort of hacks
that allows them to do stuff that the system is not designed for. Having such
a basic hardware with no security checks allows a lot of potential!

Also isn't vertical split quite common? I would assume this is something the
hardware designera thought of, not a game company figuring it out. They even
put stuff like sprite 0 hit bit for this kind of tricks

------
llao
Warning, 33 megabytes of (great) GIFs.

~~~
chungy
It's probably about time that WebP should get promoted, especially instead of
animated GIF. the libwebp library comes with a gif2webp program to make the
conversion especially easy.

Just doing it now, converting all the animations to WebP makes it 1.6MB. and
it works in all current browsers.

------
EGreg
meta-irony: "in a _manor_ likely that was unintended by its designers"

------
msla
Is anyone else getting a blank white page?

------
penagwin
Thanks for the great visuals!

