Settings

Theme

Zelda Screen Transitions Are Undefined Behaviour

gridbugs.org

366 points by Kaali 6 years ago · 100 comments

Reader

yc-kraln 6 years 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.

  • papln 6 years ago

    "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.

    • klodolph 6 years ago

      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).

      • makomk 6 years ago

        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.

    • kazinator 6 years ago

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

      It could be "defined by e-mail".

      • derefr 6 years ago

        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.

        • kazinator 6 years ago

          It's not "best" enough to actually appear in a Nintendo spec somewhere, unfortunately.

          • derefr 6 years ago

            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.

            • kazinator 6 years ago

              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?

              • derefr 6 years ago

                I think you're confusing "revision" with "generation." Revisions are the difference between e.g. the original SNES, and the 1CHIP (SoC) SNES console, not between the original SNES and the SNES Classic.

                Even though the 1CHIP SNES is a complete ground-up re-layout (has to be, turning a bunch of individual chips into one SoC), every (officially licensed) cartridge made for "the SNES" works on a 1CHIP SNES.

                A hardware revision like this, requires retaining bug-for-bug UB compatibility with officially-sanctioned released game titles, because to do otherwise is to doom the console's support line to endless complaints from people whose games don't work. But hardware revisions are not concerned with keeping UB working the same where no officially-sanctioned released title took advantage of said UB. For everything not constrained by bug-for-bug compat with an existing title, all that's required of the new revision is that it keeps to the spec.

                If you were a third-party in the middle of developing a new title, and were relying on some new UB you found, when suddenly your console mfgr released a new revision, it could turn out that your clever UB hack won't work the same on the new revision. (And this happened; it was why unlicensed titles—your Game Genies, your Aladdin Deck Enhancers, etc.—frequently wouldn't work with later console revisions.)

              • ohithereyou 6 years ago

                The argument that GP seems to be making is that only relying on defined behavior ensures that your game will work on all NES systems made by Nintendo as well as any clone systems that follow the specifications that Nintendo issued for programmers on the NES.

      • philipov 6 years ago

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

        • ohazi 6 years ago

          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 6 years ago

            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.

        • kazinator 6 years ago

          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.

      • lgg 6 years ago

        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.

      • jupiter90000 6 years ago

        Funny to think Zelda was made before email was probably used at these companies :)

  • dllthomas 6 years ago

    > 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.

    • furyofantares 6 years ago

      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.

  • 0xcde4c3db 6 years ago

    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.

  • bluedino 6 years ago

    Super Mario Bros, vs Super Mario 3 (NES) Combat vs Keystone Kapers (Atari 2600)

  • reificator 6 years ago

    I'd love to compare but I don't know what Persona's giant enemy crabs look like.

daniel5151 6 years ago

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

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

  • anon_cow1111 6 years ago

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

    • Avamander 6 years ago

      I got flashbacks of 2000s with this comment

    • OJFord 6 years ago

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

      • tenebrisalietum 6 years ago

        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.

  • soulofmischief 6 years ago

    That was a great write-up. Thanks for sharing.

    You could probably run an async loop which slices up painted frames and compares hashes of the slices to find identical slices to anchor and stitch similar frames together, still maintaining separate layers in case a better match is found later on. Something like that should solve for games like SMB.

jedberg 6 years ago

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 6 years ago

    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.

    • coldpie 6 years ago

      > The 6502 CPU was developed literally by creating a hand drawn design. Each transistor laid out created additional complexity that humans had to understand.

      Want to understand a tiny chunk of that complexity yourself? Check out how the addition operation overflow flag is implemented in silicon on the 6502: http://www.righto.com/2013/01/a-small-part-of-6502-chip-expl...

    • ubermonkey 6 years ago

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

      See

      http://www.pbm.com/~lindahl/mel.html

      • soulofmischief 6 years ago

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

        • ubermonkey 6 years ago

          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.

  • im_down_w_otp 6 years ago

    You still have to be clever like that today depending on what kind of problems you're working on.

    Which was also true back then. Not all programmers back then had to derive arcanely clever tricks to get their job done.

    There's also a part of it that is underappreciated, which is that sometimes the cool effect wasn't necessarily an original design goal, but instead was sometimes something that was stumbled upon one way or another, and then it was having that little trick in one's pocket which informed how the design would be.

    • Qwertystop 6 years ago

      One good example of this: Space Invaders was, AFAIK, the first game to feature escalating difficulty over time (because the aliens speed up the more of them you shoot). This wasn't originally part of the design; the system just couldn't run at the same framerate while managing 30 sprites as it could at 5, and there weren't any enforced timing checks.

    • jdsully 6 years ago

      There were quite a few “professional” software packages written in BASIC at the time. Of course most software needed assembly in the early days.

  • overcast 6 years ago

    Harder to do tasks we take for granted now, sure. But today's games are EXTREMELY complex in comparison. Old games were intentionally made extremely difficult, to counter their simplicity in design. Else you'd be done with the whole game in 15 minutes.

  • justinplouffe 6 years ago

    I’m currently doing a bit of Gameboy Advance programming for fun and I’m actually surprised by how simple most of it is at least for the basics. While some games on the platform do incredibly clever stuff (Super Monkey Ball Jr. being especially impressive), most of the popular titles use the standard tiled sprite rendering mode which is quite easy to understand and fast by default.

    • sgrove 6 years ago

      Wow, I just googled for a youtube video of gameplay [0] and I'm genuinely at a loss at how they pulled off that 3d engine. The sound fidelity is still pretty high given the lack of dedicated sound hardware on the GBA, and somehow they still have time left over for texture-mapped polygons.

      I love reading about how programmers pulled off these tricks on such limited hardware, hopefully there are some notes about it somewhere on the internet.

      [0] https://www.youtube.com/watch?v=K-AZQKTlUMs

  • magicalhippo 6 years ago

    > You not only had to figure out the program logic, but you had to work within very tight hardware constraints.

    Try programming microcontrollers and you can enjoy that feeling today! Get comfy with the relatively powerful ARM Cortex-M variants, which typically sports 10s of kB of RAM. Then if you feel frisky, try your hand at some 8-bit uC's with less than 64 bytes of RAM (spend your bits wisely).

    https://jaycarlson.net/microcontrollers/

  • shareIdeas 6 years ago

    Idk man. Just to make a useful app I need to understand servers, databases, backend frameworks (for security), and front end.

    Maybe it's finding creative solutions, but today there is a lot to know.

    • stopsayingthat 6 years ago

      If by "understand" you mean glue together all of the high-level abstractions of each of those domains, each of which have mountains of tutorials / blog posts / StackOveflow answers, then sure.

      • shareIdeas 6 years ago

        Gluing them is only 1 step.

        You still need to write the logic that each layer needs to be useful.

        • stopsayingthat 6 years ago

          This is true, and I don't mean to trivialize the work of today's programmer.

          However, I suppose somewhat subjectively in reviewing or writing any of this glue or logic layer code using modern web frameworks I've never had the sort of WOW response that I do when I see what the programmers of yore were coming up with.

          • nkohari 6 years ago

            I think the real reason for this might be that clever hacks using current technology are looked on with disdain, whereas yesterday's hacks are seen as impressive ways of making the most of limited hardware and tooling.

            • wutbrodo 6 years ago

              To me, that's a sign of the engineering discipline having matured (as well as average computational needs not keeping pace with computational power). My first job out of college was at Google, and I remember being somewhat disappointed the first time I was told to replace some elegant, dense logic with something more readable. After years of having to read other people's prematurely optimized or unnecessarily compact code in large engineering systems in complex problem domains, I'm more than bought into the idea that readability is one of the primary goals of good code.

              This doesn't preclude compromising readability for hacks when you need to squeeze performance out of some logic (just look at Tensorflow code), but you'd expect a maturing computational environment and engineering culture to reduce the number of clever hacks needed or present.

              • inlined 6 years ago

                That can be the difference in how the software is deployed. At Google, most things are horizontally scaled and machines are cheaper than engineers. When I worked on Windows, it was the opposite: I was told to refactor clean multithreaded code to reduce the thread count by one. The rationale was that Windows scales vertically and every developer wasting a thread is how Windows gets slower over time.

  • red75prime 6 years ago

    On the other hand you had all the hardware under your direct control. And right now I'm trying to find why kernel stashed 17MB of RAM, I really need.

  • syn0byte 6 years ago

    Survivor bias. Plenty of shite code got written for the 6508 but only clever crap gets blog posts 30+ years after the fact.

  • Antonio123123 6 years ago

    It was much easier then to become experienced in programming because it was way less to learn.

  • fred_is_fred 6 years ago

    Not only more constraints but you had to use languages that took more work to do things, like C or Assembly.

MobiusHorizons 6 years ago

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 6 years ago

    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 6 years ago

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...

  • cableshaft 6 years ago

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

klodolph 6 years ago

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/ for a database of the hardware inside each cartridge.

  • einr 6 years ago

    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 6 years ago

      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 6 years ago

        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

        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

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

        • klodolph 6 years ago

          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 6 years ago

            > 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 6 years ago

              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 6 years ago

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

                • bonzini 6 years ago

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

                  • jdsully 6 years ago

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

      • zeta0134 6 years ago

        Lots of other games have custom logic in the cartridge. Super Mario 3 and Kirby's Adventure both have a scanline counter in the cartridge that works by snooping the graphics chip while it reads the sprite and background tiles. Because the access patterns were consistent and well understood, it could keep track of where the console was mid-frame and trigger an interrupt, making it much easier to do scroll splits and fancy raster effects. Go check out the final boss from Kirby's Adventure, or the crazy rotating stage in Butter Building, and remember that the NES is doing that with a single background.

        Some of this may have been undefined behavior, but the MMC3 chipset involved was produced and manufactured by Nintendo. Whatever the original design was, Nintendo appears to have stuck with that same set of capabilities throughout the console's lifetime, and encouraged their developers to take advantage of all the tricks.

      • tenebrisalietum 6 years ago

        The NES PPU has a bit that when set will blank the first 8 pixels of each scanline.

        I think there are games out there that do use sprites to do the same on the other side.

    • eropple 6 years ago

      I read this and was shocked - surely this is incorrect!

      Nope. Paperboy uses a CNROM, which is basically an MMC3. Maybe they're doing some clever edge-replacement stuff on the mirrored table, like SMB3 does with the same hardware? But you can see glitching when SMB3 does it.

      (edit: my sibling post answers it. That's slick.)

  • airstrike 6 years ago

    > See http://bootgod.dyndns.org:7777/ for a database of the hardware inside each cartridge.

    Wow, this is chock-full of content. I wish there was one for the Sega Master System too

    Off-topic, but thought I'd link to this very fun comparison of various games across the two platforms: https://huguesjohnson.com/features/nes-vs-sms/

coldpie 6 years ago

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 6 years ago

    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 6 years ago

      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-...

      • papln 6 years ago

        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 6 years ago

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 6 years ago

    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

    • pubby 6 years ago

      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 6 years ago

        The way this scanline counter was implemented is quite clever. From nesdev wiki (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.

  • bonzini 6 years ago

    You can probably do it without divisions if you use the name table creatively... You can place line 720 of the input at line 192 of the name table (720 modulo 256 is 192) as long as everything above and below it is displayed correctly.

and0 6 years ago

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

tinus_hn 6 years ago

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 6 years ago

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.

duxup 6 years ago

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 6 years ago

    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."

    • whermans 6 years ago

      Going as far as co-processors for 3D rendering[0] - image buying a game today that comes with its own GPU!

      [0] https://en.wikipedia.org/wiki/Super_FX

    • duxup 6 years ago

      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 6 years ago

        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)

      • jordanmorgan10 6 years ago

        Well with xCloud coming up from Microsoft, and Sony partnering with them on cloud gaming - we appear to be going in the opposite direction, for better or worse.

shultays 6 years ago

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 6 years ago

Warning, 33 megabytes of (great) GIFs.

  • chungy 6 years ago

    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 6 years ago

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

msla 6 years ago

Is anyone else getting a blank white page?

penagwin 6 years ago

Thanks for the great visuals!

Keyboard Shortcuts

j
Next item
k
Previous item
o / Enter
Open selected item
?
Show this help
Esc
Close modal / clear selection