One of the trickier things to manage on a NES (without additional hardware on the cartridge) was maintaining a static information panel on the bottom of the display - for the score and so forth - while the portion of the display devoted to gameplay scrolled freely around both vertically and horizontally.
The trick we used at Zippo (Wizard and Warriors II and 3, Solar Jetman etc.) a trick given to us by Rare, was to change the scroll registers and I think the character look up location at the moment the screen refresh cycle reached the appropriate point on the display. These registers would then need to be reset during the vertical blanking interval. So all in all either 100 or 120 times a second depending on the TV system.
Since we couldn't afford to keep the CPU hanging around doing nothing while we waited for the cathode ray tube gun to hit the right point on the screen the trick was a two parter.
First you would get the sound chip - such as it was - to play an inaudible sample of a determined length which would then trigger a CPU interrupt at more or less the right time, within say two or three scanlines of the position of our static panel. You would then position a spare sprite on top of a visible pixel at precisely the right point on the screen so that when it flipped its hardware collision detection bit this was precisely the right time to switch the scroll registers etc.
These were the kinds of shenanigans that made programming the NES intricate and time consuming, and also in later years I suspect made the job of emulator writers something of a misery.
> also in later years I suspect made the job of emulator writers something of a misery
Most certainly. These tricks force developers to emulate systems down to individual cycles in order to get the timing right because getting them wrong will result in visual glitches or worse.
Byuu, the author of bsnes, wrote some very detailed articles about this as well. I can't seem to find them anymore though. His domain has also been excluded from the internet archive for some reason.
> His domain has also been excluded from the internet archive for some reason.
Because Byuu, who later went by "Near", tried to scrub as much personal information from the internet in the hopes of staying ahead of constant doxing attacks by an awful internet community called KiwiFarms. It didn't work. In the end, Near/Byuu committed suicide months ago to escape the constant harassment.
The free speech Alamo. It often leads to funny results, like lesbians finding it to be the only place they could complain about some psycho in Canada targeting women operating small businesses - Youtube and Twitter banned several people over it before the Canadian media started covering it. Sometimes it leads to not so funny results, because that is the cost of agency - its kinda crazy that even has to be pointed out... but we seem to be at that point.
Holy shit, I didn't know. I'm sorry, I'm shocked and just don't know what to say. Thanks byuu for all your work. Hope you find peace. You will be remembered.
> was to change the scroll registers and I think the character look up location at the moment the screen refresh cycle reached the appropriate point on the display. These registers would then need to be reset during the vertical blanking interval. So all in all either 100 or 120 times a second depending on the TV system.
That's a cool way to do it on the NES. While the NES did not have a raster interrupt, requiring such tricks (though you mentioned the later cartridges with the extra chip that added one), the Game Boy did have a raster interrupt, and the SNES essentially went all in on it. Switching the mode mid-frame became a very common method then.
By the way, PAL/NTSC is 50/60Hz, but a frame consists of two fields (odd/even lines), so 25 or 30 frames per second for PAL/NTSC respectively. So I guess this means you probably did the trick 50/60 times per second? (Unless you used more than two configs per frame maybe.)
NTSC draws each frame as two fields; first the 240 odd rows, then the 240 even rows, for a total of 480 interlaced rows, or "480i".
However, older video game consoles (such as the NES and SNES) typically output a malformed NTSC signal, designed to trick the TV into drawing just the odd lines, over and over again, sometimes referred to as "240p". As a result, it's effectively 60 independent frames per second, not 30 frames of two fields.
Yes. Even if that weren't the case, the NES would have needed to separately buffer the whole frame (or worse) if you did not want to repeat the scroll register dance per field, which is where I went wrong.
Ah, duh, you're completely right. I somehow hallucinated another buffer between PPU-generated frames and video output. Which would have been rather expensive at the time...
Also I only just learned that because the NES does not actually output the half scanlines that make interlacing work, both fields are drawn on top of each other, effectively making it 50/60 actual frames per second anyway, instead of interlaced fields! (https://wiki.nesdev.com/w/index.php/NTSC_video)
Ah yes, in retrospect I think the NES was refreshing at 25/30 times a second, so double that for the number of scroll register updates we needed to make.
When I hear stories like these, I always think that nobody is doing stuff like this these days; but in retrospect, I think it's just that I've never asked or seen it discussed.. so..
What are you guys doing these days that has this same hacker-ethos applied to it?
It's semi-common on PS4 to abuse the audio coprocessor to do things other than audio, just to eek out that little bit of extra processing power. After all, it's programmable, so why not? In fact Sony specifically made the PS5's audio processor non programmable to make sure developers don't do this and actually focus on its strengths and things that it can do very well with 3D audio and such, rather than make it crunch physics or whatever.
Also I'm aware of at least one game that uses an internal executable restart routine to reboot and reload data on PS4 and X1 since it's just easier than dynamically unloading all engine data and loading it again following an in-game update. While not forbidden by either 1st party, it's certainly somewhat unorthodox.
In general, I think that even with next(current) gen there is still a lot of the good old school hacker mentality going on, it's just that you can't hear about it because it's all NDAd. Wait 10-20 years and those stories will start to come out.
> Also I'm aware of at least one game that uses an internal executable restart routine to reboot and reload data on PS4 and X1 since it's just easier than dynamically unloading all engine data and loading it again following an in-game update. While not forbidden by either 1st party, it's certainly somewhat unorthodox.
Microsoft put out a video soon after purchasing Bethesda where someone from that team mentioned doing a similar trick in the Xbox port of Morrowind. If the game was approaching the relatively tight 64MB RAM limit of the platform it could soft reboot the console during a loading screen and then reload to the current point.
Very cool! Sounds a lot like all the tricks you had to employ for the Atari 2600. If someone is interested in this, I can highly recommend the book "Racing The Beam". It's hard to imagine these days how complicated even the simplest things were back then.
15 years back I did some hobbyist programming for the GameBoy. Drawing a status bar there was comparatively easy. The hardware allowed you to set some registers to define a "window region", overlaying the main background map.
No hblank interrupts? Game boy has them to make this sort of thing relatively easy. Hblank interrupts is also how "mode7" style pseudo 3d for a Mario kart style game can be made (on GBA or SNES).
We abused hblank to death in Infinity for Game Boy Color (unpublished in 2001, later dumped on GitHub), for things like parallax scrolling, tilted overworld map, screen transitions, and scrolling inner text.
I looked for that, found it on github and then looked at the build instructions, followed the link provided to GBDK and saw my credit as one of the contributors. Totally forgot about that... :)
Are you participating in the re-launch of development for Infinity? (great to see it happening)
If so, what approach are you taking for the toolchain? Keeping the existing version that had been used/modified? Updating to newer tools (and code changes that may go along with that)?
Everything's up to Incube8, although I'll be advising. In general, my recommendation is to consider better tooling for managing assets and running builds but be very careful with the code (e.g. don't change compiler versions, don't refactor anything).
Thanks for the reply. It'll be interesting to follow the progress.
Can definitely see wanting to keep the compiler and code in a known state. Though modern SDCC generates code that's probably 50% faster in some cases and with 90% less bugs than versions from the early 2000s.
Lots of great Game Boy releases have been coming out.
Atari had a patent on a cpu instruction to wait for hblank, I suspect Nintendo didn't put anything similar in the hardware as a result. But it was needed, so interrupt generation ended up in cartridges... And since they didn't get sued, put in the next gen hardware.
The hardware was so limited and expensive that rather trickery paid off.
For instance, on Sinclair Spectrum the memory refresh and the keyboard matrix scanning were intimately coupled; tape I/O and screen border color circuits were also somehow coupled, which was easy to see with every tape operation.
I know, I mean the thought process when designing a program in such constraints where any aspect of a system might be a useful resource and you had almost no other choice (kinda like abrash rotated texture trick on pentium) .. it's so at odds with current processes where everything is split and isolated as much as possible.
That’s what the “hardware collision” bit is about. You place sprite 0 at a certain location on screen, and you can find out when that sprite gets drawn (with some certain other restrictions / assumptions). IIRC, the collision bit gets set when a pixel from the sprite is drawn overlapping a background pixel. This is how early games like Super Mario Bros. drew the status bar, but Super Mario Bros. could just spin the CPU waiting for this.
There are two other mechanism I know off the top of my head.
One is to put some circuitry on the cartridge, and then arrange the usage of tiles such that all the background tiles are in one bank and all the foreground tiles are in another. If you do this right, you get an address line which cycles high and low once each scanline–and you can put a circuit on the cartridge which counts the scanlines, triggering an interrupt. This was only available if you could put that circuitry (usually, a “mapper”) on the cartridge.
The last method is to count cycles.
I would note that among other differences, this is much, much easier on a Game Boy. The Game Boy has a register you can read which tells you which row you are on. Not the only thing that’s easier on a Game Boy—there’s also a hblank interrupt, and the tilemap is larger than the screen. Anyone interested in NES programming but unsure about how much they like dealing with obscure technical problems may want to try Game Boy programming first to get a taste for it.
Yes: but it's messy. You spin in a loop with a BIT instruction polling the PPU_STATUS register and a BVC instruction pointing backwards waiting for bit 6 to be set. Besides the fact that you're stuck doing this, the main problem is synchronization, this little loop takes 7 CPU cycles (with each CPU cycle taking 3 PPU cycles, or 3 "pixels" of distance) leading to an 21 "pixel" variance. When dealing with the very short horizontal blanking period between scanlines, it leaves very little time to do anything meaningful with the PPU in that window.
If you've ever noticed some "weird flickering" near status bars in NES games, this is probably the culprit: Few very skilled developers were able to pull it off cleanly.
The TV syncs to the clock in the input signal that the console generates. The console knows what line the TV was on because it's the line that the console is currently outputting. Analog TVs were very simple devices where the signal coming in via the wire or antenna was basically pumped directly to the CRT to manipulate the beam. While at the user's end it looked like a raster, it's all analog tricks.
Imagine a text file pushed out theough a serial port, all the data is just dumped on to the wire and the other end worries about interpreting carriage returns and line feeds. You just imagined how old line printers worked!
When the TV wasn't able to figure out the sync signal you'd get rolling [1] or tearing [2] where the picture was being displayed the best the TV could make out.
They mean that they need to fiddle with those specific video registers twice a frame, which is 100 or 120 times. Vblank timing isn't too hard, the base system has an interrupt for it; scanline timing is hard though, you either need to carefully time your loop (and redo it for 50hz systems), or poll on things, or use extra hardware built into the cartridge (mappers).
I don't think there was an IRQ for sound, but it might have made sense to poll the audio unit rather than the graphics unit for some reason.
I wrote an NES game as my senior project for college. It was a singular experience as the vast majority of my development experience had been web technologies.
I had the advantage of full-featured emulators, breakpoints, examining the code as it was running, and a very searchable community online. I have sometimes pondered on the challenges of writing 6502 assembly without any of these things. It’s really interesting to see some of what that was.
It certainly is so much easier to get documentation or read forum posts and ask questions today. Back then, we did have fairly decent dev systems though, could do breakpoints and examine code as it was running. It was also cross-development so you could edit and download from your computer onto the dev system attached to the console. To be fair, some people had much more primitive systems, I feel lucky since others could only test by burning EPROMs.
Could you qualify one thing? One that might help is code back then was almost always single threaded and thus easy to reason about. I mean, even today concurrent code is difficult to deal with.
For NES there are many development environments now... Probably dozens. The main modern advantage is emulators. I do enjoy folks who take a different approach like this dev who is using Lisp to make a NES game.
http://www.dustmop.io/blog/2019/09/10/what-remains-technical...
I do it in C (good enough for most purposes, although a little slow and bloated - most people do it straigh in ASM with extensions).
I use Ubuntu, good old Sublime Text with C extension, FCEUX (Windows version on wine) and YY_CHR (for working with graphics). To compile, check out the awesome CC65 suite (it has a compiler, assembler, linker... the whole thing).
I used an emulator called NinTaco, a 6502 assembler, and VS Code. My game was very simple and completed in about 3 months (I didn’t have any sound and a very limited game)
I think the crucial piece is the assembler and documentation about the start up and loop processes.
What's the reason for using ASM? Were there no good compilers for higher level languages (like C) available for the platform, or were the developers simply accustomed to using ASM, or was the platform limitations such that developers simply had to use ASM?
6502 is just not that viable for C-like languages. Mostly because of its tiny hardcoded stack, so for C you need to either emulate the stack in software (slow) or figure out workarounds (mostly involving precious zero page.)
Not to mention that there’s only three registers (A, X, and Y (and P, technically)). And each one has a different purpose; There were no “general purpose registers”
NES games were still pretty simple, so I can see how simple tools were more or less adequate.
I'm far more impressed that complex SNES games were also developed purely in assembly, like A Link to the Past (1991) or Super Metroid (1994). This is an era where PC game developers would mostly be using C.
You may be interested in this series on SNES hardware features and how they were exploited. The videos have very informative visual representations of memory/io/etc. (the other videos on the channel are also good)
On the other hand, there were pretty complex games on the vastly more simple NES. Less than on the SNES, they required elaborate tricks. (I guess that however mostly required more from the actual game code, and sometimes some extra hardware, than from the tools.)
The trick we used at Zippo (Wizard and Warriors II and 3, Solar Jetman etc.) a trick given to us by Rare, was to change the scroll registers and I think the character look up location at the moment the screen refresh cycle reached the appropriate point on the display. These registers would then need to be reset during the vertical blanking interval. So all in all either 100 or 120 times a second depending on the TV system.
Since we couldn't afford to keep the CPU hanging around doing nothing while we waited for the cathode ray tube gun to hit the right point on the screen the trick was a two parter.
First you would get the sound chip - such as it was - to play an inaudible sample of a determined length which would then trigger a CPU interrupt at more or less the right time, within say two or three scanlines of the position of our static panel. You would then position a spare sprite on top of a visible pixel at precisely the right point on the screen so that when it flipped its hardware collision detection bit this was precisely the right time to switch the scroll registers etc.
These were the kinds of shenanigans that made programming the NES intricate and time consuming, and also in later years I suspect made the job of emulator writers something of a misery.