Hacker News new | ask | show | jobs
by yc-kraln 2531 days ago
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.

5 comments

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

https://en.wikipedia.org/wiki/Undefined_behavior

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

This definitely does not appear to be an “unsupported use-case”. This technique is used in many first-party launch titles. As far as I can tell, this is the reason that sprite zero tests exist in the first place.

If you want an example of something that’s really an unsupported use case, consider mid-frame palette changes—in order to do this, you actually have to disable and re-enable rasterization during the horizontal blanking interval. It works, but it is difficult to get right and the PPU is clearly not designed with this in mind.

Short of having an actual spec in hand, it would appear that mid-frame scroll register updates were entirely normal and an intended way to use the device, based on the evidence (no other reason for sprite zero check, lots of first-party titles using this feature, other mid-frame updates are much more difficult).

The sprite zero check is useful for changing the horizontal scroll position mid-frame, which is something the hardware seems to be designed to support - it helpfully copies the value written to the scroll register to the internal horizontal scroll position at the end of each scanline. It does not seem to be designed to make the same thing possible for vertical scrolling. The programmed register value for vertical scroll is read once at the start of each frame.
Do we know that the developers didn't confirm this hack with the hardware people?

It could be "defined by e-mail".

The best definition of "undefined behavior" is: "if some other company created a clone of this console by following the spec to the letter, but otherwise making all their own decisions... would this game run?"

Of course, if you don't have cloners immediately nipping at your heels, you might end up releasing a smash hit that takes advantage of UB in a way that forces you to define the behavior as "it works however it needs to work to keep that game working", because eventually you yourself (the original console manufacturer) will tape out later revisions of the CPU, and you'll want to make sure they can run that game, given that it's "officially licensed" by you.

If you had the right static analysis tools in play, though, you might have wanted to run them over the game, notice the use of UB, and thus fail the game at the QA stage, before things need to escalate to that point.

It's not "best" enough to actually appear in a Nintendo spec somewhere, unfortunately.
I meant "best" as in "most practical for a third-party dev when deciding what behaviors to take advantage of." Defined behaviors are guaranteed to work in both 1. clone consoles that conform to the spec, and 2. future revisions of the official hardware. Undefined behaviors aren't.
Future revisions of the hardware can throw everything out the window regardless of what is documented for the current hardware.

What NES cartridge from 1986 can you plug into current Nintendo boxes?

You are playing semantics to try to justify it. If it's not in the official spec, it's undefined behavior.
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.
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.

I don't agree with the suggestion that there is no difference (other than word semantics) between relying on empirical observations and relying on a privately communicated piece of spec.

The one playing semantics is you, since your apparent concern is the definition of the term "undefined behavior" and whether something falls under that definition.

My point is about how well an engineering decision is justified, not what term applies to it according to some document.

We have no idea if it was even undefined in the first place, since the PPU documentation is not public.

You are correct thought, often guarantees about behavior can be made after the fact. Generally what happens these days is someone determines they need or want to use a certain undefined behavior. The hardware people then go, look at the RTL for the chip and confirm it behaves in a certain way, and then they UPDATE the documentation to explicitly document the newly guaranteed behavior.

Conversely, often something that is documented to work just doesn't. Especially in the case of console developers who tend to be using early steppings of custom silicon. In that case sometimes when you go to the HW team they fix it in the next stepping, sometimes they document it as errata and move on.

Funny to think Zelda was made before email was probably used at these companies :)
> developers learn how to squeeze more and more out of the platform

That's a part of it, to be sure, but another dynamic is that developers have to squeeze more out of the system. Dropping old techniques on new hardware will probably give you a game that looks "better" than what's already out. Once everyone has done that, to look "better" you need something new other than hardware.

That’s another part of it, to be sure, but yet another dynamic is that it being a mostly fixed system, rather than a wide array of target hardware, is what makes it feasible to implement these tricks and accumulate knowledge in the first place. It all kind of goes hand-in-hand — the fixed nature of the hardware makes it so you have to find these tricks in order to compete with games that play on newer hardware, and also makes it so that it’s possible to do so. I find it fascinating. But I suppose the market would have just found different stabilizing points were this not the case.
Agreed.
Also, the specific phrase "undefined behavior" is typically invoked in a context related to the C programming language. In the C standard and some standards related to it, undefined behavior is just one point on a spectrum of (un)definedness:

- implementation-defined: Not defined by the standard, but implementations must choose a consistent behavior and document it (e.g. what's shifted in when right-shifting signed integer types)

- unspecified: Not defined by the standard; implementations must choose some way of addressing the situation, but need not document it (e.g. the order in which function arguments are evaluated)

- undefined: Entirely outside the scope of the standard; implementations may assume that such situations never occur, and need not have any sensible or consistent behavior (e.g. dereferencing NULL)

In that taxonomy, I think this is much closer to "unspecified" than "undefined". The latter is usually used in scarier contexts like random memory corruption or crashes, not consistent behaviors that rely on deliberate implementation choices as we see here.

Super Mario Bros, vs Super Mario 3 (NES) Combat vs Keystone Kapers (Atari 2600)
I'd love to compare but I don't know what Persona's giant enemy crabs look like.