supercat wrote:Does ensure that all displayed tiles get updated on the same frame?
I can't entirely tell, but it's definitely using the standard NES thing of keeping track of which tiles need to be updated and only updating those tiles. It's on MMC1, and CHR bankswitching is used extensively – possibly to mask tearing – but it's subtle enough I can't tell. No extra RAM.
Here's a longplay: https://www.youtube.com/watch?v=mLQzL8vsNVM
Some interesting graphics changes compared to the C64 and Atari versions; I notice they lost the bonus levels that were 20 meta-tiles wide and designed to fit on a single screen, and also run at a much faster gametick rate than normal levels.
Looking at the video, it appears that is with the original the CHR bank switching is used to cycle among tile sets to animate everything in a fashion asynchronous to gameplay, to do things like make the diamonds sparkle or (in this version) make the rocks back back and forth. The entire nametable is getting redrawn every gametick over the course of several frames. Single-step through the video while watching the left and right edges of the screen during horizontal scrolling, and this effect will be visible.
Since Boulderdash tile-set animation is done asynchronously with regard to gameplay, it doesn't really matter if all of the tiles update at once. Ruby Runner, though, uses tile-set animation to smooth out motion and create "in-between" frames that must be synchronized with nametable updates. Since even a stock NES has two frames worth of tiles in the nametable, I don't think page-flipping should pose any difficulty; I'm curious why you think that's symptomatic of using a "wrong" approach.
My objective is to make Ruby Runner have a play mechanic similar to Boulderdash, but not exactly copying it (you'll notice, for example, that the mosnters in the animated .GIF move straight ahead if they can, while the Boulderdash monsters either follow the left wall or right wall), but with all of the objects animated to move smoothly, and also hopefully without any display glitches like the sides of the Boulderdash screen. Being able to draw all of the nametable tiles in a single frame would be convenient because it would allow the game logic to compute everything that will happen in a game tick if the player were to remain stationary, then draw all of the nametable entries, wait for the frame cycling animations, wait for the frame before the first animation frame of the next game tick, and read the controller for what's should happen on that game tick. The game logic would thus need to synchronize with video only once per game tick, and the game could run smoothly provided only that each gametick's worth of game logic was complete before it was time to show the first frame of the next game tick.
Being limited to updating a quarter of the name table per frame would require either having the game logic for each gametick finish four frames early, or else having a means of starting the name table updates before the game logic is done. The first may or may not adversely affect gameplay; the latter would add complexity.
My guess would be that if I limit boards to about the same size as Boulderdash I could probably get away with the first approach, but if I use an 8K RAM memory expansion so as to allow either boards, adding four extra frames per game tick could be annoying.
[Not being able to move the DMA target] seems like a missed opportunity in the NES design.
Previous times I thought someone had said that the 2C02 can't keep up with OAM DMA's pace, and needed no faster than one byte every 3 CPU cycles. But right now, testing in Visual2C02 seems to imply it works?
I wonder why the speed would be so limited, given that the PPU bus normally runs much so faster than that? Perhaps the NES was originally planned to have the CPU run much faster?
There's a lot of really good stuff in the NES design, but a few missteps with how things fit together. The biggest omission, IMHO, is probably the lack of any on-chip way of requesting an interrupt at a certain line, or at least finding out where the beam is. Ruby Runner on the 2600 didn't have any raster interrupts available to it, but it was able to find out how much time remained before the end of overscan or vblank, run the game processing loop until those times were close to used up, and then go into a polling loop to find the exact ends of those intervals. Having an address which, when read, would report half the number of the current scan line would have been enormously useful, and being able to make the NMI trip at a configurable line would have been even moreso. Starting blanking early and extending the end of it would have made it possible for games that need to perform more updates during vblank to actually do so.
as compared with something like:
Code: Select all
stx $FC ; Set bits 8-15 of address for $7C00-$7CFF region
lda $7C00,y ; uses LSB of address, plus last value accessed at $FC, plus $010000.
Ah, yes. I misunderstood. All clear. Somehow I'd misunderstood you to be talking about blocks of 128 bytes.
So how do you like that idea now that you understand it? Having a larger contiguous regions banked in is useful for running code, or for objects that are going to be accessed via absolute indexed addressing modes, objects that would need to be accessed via indirect indexed addressing modes when using large-bank switching can be accessed more conveniently using absolute indexed addressing mode and page-level switching.