This video is part 6 in a series about Super Nintendo Entertainment System features.
In this episode we'll take a slight detour and look at the mechanics of the old Cathode
Ray Tube monitor; specifically, the scanning electron beam.
Understanding how it works will make it more clear what lag frames are and how and why
lag occurs in the first place.
The most common television set consoles like the Super NES were played on were Cathode
Ray Tube monitors, or CRTs for short.
These types of monitors displayed an image by focusing a cathode ray, or electron beam,
at a phosphorescent screen, causing it to light up at a point.
The beam can be bent by a magnetic field, changing the point on the screen that lights
up.
By moving the beam really fast back and forth, an entire image can be displayed.
The electron beam causes the phosphor layer in the screen to light up for a very short
amount of time, but since it is so bright, the persistence of vision effect of our eyes
causes everything to kind of blur together and appear as a single static image.
The pattern the beam traces out on the screen looks something like this.
There are actually way more scanlines than what's shown here, but if all of them were
shown it would be quite a mess.
The beam scans across horizontally, forming a scanline, then it moves back across to the
left and completes another scanline below, and so on.
Once the bottom of the screen is reached, the beam moves all the way back up to the
top left corner, and begins the entire process again.
This cycle is contained within one frame.
With all of this zig-zagging across the screen, many portions of the screen are swept across
more than once.
In order to ensure that each position is scanned only once, the electron beam is stopped when
repositioning for the next frame or scanline.
Turning off the electron beam is usually referred to as 'blanking'.
When it is disabled in between scanlines, it's referred to as 'horizontal blanking'
or H-blank, since the beam has to reposition itself horizontally.
And when it's disabled in between frames, this is called 'vertical blanking' or V-blank,
since the beam now also has to reposition itself vertically.
The beam can also be turned off at any point for whatever reason, and this is called 'forced
blanking' or F-blank, since we are forcing the beam to be off when it would otherwise
be on.
These blanking periods are important to know from a programming standpoint, since certain
SNES hardware registers can only be modified during a blanking period.
Before moving on, now would be a good time to get a sense of scale for the timing behind
all of this.
In the NTSC standard, the Super NES runs at about 60.1 frames per second, but how fast
does the beam sweep across the screen?
The answer is very fast.
One frame is about 16.64 ms long, so we're already looking at a scale of micro seconds.
The V-blank period is about 2.41 ms, which takes up about 14.5% of the frame.
Split up the remaining time among the number of visible scanlines--each scanline takes
up 63.51 μs, or 0.38% of the entire frame.
That value includes the H-blank time, which is about 24.7% of the scanline, or just 15.69
μs, leaving 47.82 μs for the actual visible part of the line.
If the visible part of the scanline consists of 256 dots, this means the beam sweeps across
256 dots in 47.8 μs, or 5.35 million dots/s.
If we slow time down so that one frame takes up 10 seconds, the beam still moves too fast
to really see what is going on.
Here is what the last few lines look like in ultra slow motion.
There are normally a few lines traced out above and below the display area before the
beam actually starts to move back to the top.
The exact position of the beam when it is blanking isn't really well defined, and differs
between manufactures.
You can really appreciate the speed of the scanning beam when you see just how long the
vertical blanking period is relatively speaking.
To make this scanning pattern a little more clean and standardized, we're going to make
a few changes to it.
It is important to know exactly where the scanning beam is in its scanning cycle if
we need to have some code run at a precise time.
The Super Nintendo actually has a couple of hardware registers that provide an approximate
position of the beam when read.
The issue though is understanding the scale it uses.
The vertical counter provides which scanline is currently being scanned, while the horizontal
counter tells how long this scanline has been scanning for.
So it doesn't actually provide an (x,y) coordinate position, but it does give enough information
for our needs.
We just need to convert these (h,v) counter coordinates to some sort of (x,y) coordinate.
To do this, we are going to create a sort of virtual scanning beam pattern, that isn't
possible in real life, but it will make things easier to understand and visualize.
Instead of horizontal blanking and sweeping the beam all the way back to the left, we
are going to suppose the beam can instantly teleport back to the left side when it needs
to.
This way we can visualize the H-blank period as a small extended scan to the right of the
scanline, instead of this messy diagonal line across the screen.
Similarly, we can reimagine the V-blank period as successive ghost scanlines past the end
of the screen, and the beam will teleport to the top when it needs to.
Now V-blank can be represented as a block of 38 scanlines beneath the main screen.
If you have a keen eye you might notice this lines up perfectly with the NTSC progressive
standard of 262 scanlines per field.
Now, in order for things to make sense later, we should offset everything so that the very
top left corner corresponds to the (h,v) counter coordinates (0,0), since, surprise surprise,
the origin is not the top left corner of the screen.
The first visible scanline is actually scanline number 1 in a zero-based index, so we need
to shift everything by one line.
And the horizontal counter is cleared in the middle of H-blank, not exactly at the end,
so we need to shift everything a bit to the side to compensate.
This gives us a useful visualization of the scanning beam in relation to the vertical
and horizontal counters the SNES provides.
Using this system, we can assume the beam moves at a constant speed at all times (which
in reality it doesn't).
The display screen starts at scanline 1 and ends at scanline 224, while the maximum vertical
value is 261; and each scanline starts at horizontal position 22 and ends at 277, while
the maximum horizontal value is 339.
So for example, if we read the H/V counters to be (214,33), we know the beam is on the
33rd visible scanline, about three fourths of the way across the screen.
Now lets incorporate the speed at which the processor can execute instructions into the
mix.
At some point in time, the processor will begin executing an instruction, and the scanning
beam will be at some point on the screen.
By the time that instruction is finished executing and the next begins, the scanning beam will
have moved a small amount.
This amount depends on the instruction, since not all of them take the same amount of time
to process, but the ones shown here are of average length.
One frame's worth of execution can be visualized by mapping all of the instructions to the
screen according to the (h,v) counters at the time of execution.
In this image, each instruction is colored according to its mnemonic; each one is a different
color.
You can see there is some sort of organization and patterns here instead of it looking somewhat
random.
The first thing to point out is this vertical strip here where all the instructions seem
to take longer to execute--the processor is paused for a very short time every scanline
while the dynamic RAM is being refreshed.
Otherwise all the data stored inside would decay away and be lost forever.
Secondly, the bottom part of the visible screen is full of a short pattern that repeats over
and over again.
This will be referred to as 'spinning,' where the processor is waiting for something to
happen like an interrupt or another external signal.
In this case, the main code for this frame has been completed and it is waiting for the
next V-blank period to begin before continuing execution.
The third thing, which is not particularly easy to see without seeing exactly what the
instructions are, is the V-blank routine.
You can kind of tell by the very long instructions--these are DMA transfers which will be discussed
in a later video.
They can only occur during a blanking period, which is why you don't see any while the screen
is being drawn.
To understand why lag, or a slowdown of the gameplay, happens, we need to understand the
flow of control of these major routines and when they occur.
Execution of code during main gameplay is a cycle that repeats itself once per frame.
What exactly happens each frame is up to the developers, but we'll look at a general case
with nothing fancy going on.
We'll start at the beginning of V-blank.
At the beginning of the vertical blanking period, a Non-Maskable Interrupt will be fired
to the processor.
It will completely stop what it is working on by saving its current program counter and
jumping immediately to the location in ROM that was specified to be the NMI & V-Blank
routine.
It is during this period that the controllers will be polled, VRAM, OAM, CGRAM, can be accessed,
and hardware registers can be modified.
Since most of this can't happen outside of a blanking period, it is sort of a rush to
hurry and update everything that needs to be updated before V-blank finishes.
As soon as the V-blank routine is complete, the main game code can then be executed.
This includes anything that doesn't really need to access the previously mentioned memories,
like physics calculations and checking against user input.
When this iteration of the main game loop for this frame is complete, there is not much
else to do except wait for the next NMI signalling the start of the next V-blank period.
So the processor can just be put into a short spinning loop that continuously checks if
that routine has been completed yet.
When the NMI fires again, execution will be forced out of the loop to complete the V-blank
routine again.
And when execution is returned back to the loop, the condition can be met and it will
break out of the loop, and the cycle continues.
We'll analyze two routines, the V-blank routine, and the main code routine which is basically
everything that isn't the V-blank routine in this case.
Both of them are limited in execution time; if they take too long to wrap up, the cycle
will fall apart and something will have to give.
First let's look at what happens when the V-blank routine takes too long.
As a safety percaution the first thing that is done during this routine is to enable forced
blanking.
And then on the way out, forced blanking is disabled right before returning to normal
execution.
Normally this is still during the vertical blanking period, but if the routine takes
too long to finish, that is, there is too much code to run, the V-blank routine will
finish after vertical blanking has ended.
Because of this, there is a period at which the scanning beam is trying to draw the image
to the screen, but it is still in forced-blank, so the top of the screen turns black.
This anomoly only sticks around for a single frame, but if this type of lag occurs a lot
at once, this black bit will tend to flicker up and down.
Since this type of lag is due to the V-blank routine running too long, which is responsible
for updating visuals, it can happen more often during portions of a game where there is a
lot of graphical stuff going on at once.
Either that, or there is some code in the V-blank routine that doesn't necessary need
to be executed during V-blank, and is eating up some of that precious time.
In this example, the extra load of updating the background tilemap to add this hole in
the ground was the culprit.
The other, more common and infamous type of lag is the variety that happens when the main
game code runs for too long, and causes the gameplay to slow down to half-speed.
Another thing that happens at the very start of the V-blank routine is a check to see if
the main game code for the previous frame has completed and the processor is now spinning.
If it is, everything is good to go and the rest of the V-blank routine is ran as expected.
But if it isn't, then the V-blank routine just returns immediately to let the main code
finish.
This means that nothing on screen will be updated on that frame, causing a duplicate
picture.
The NMI to signal V-blank starting is only fired exactly once per frame, so even if the
main code finishes up during V-blank, it will have to wait almost an entire frame for the
next one, spinning the whole time.
Since this type of lag is due to the main game code running too long, which is responsible
for updating gameplay variables, it can happen more often during portions of a game where
there is a lot of calculation-heavy stuff going on at once.
These lag frames are not limited to just one frame either--if there is a huge amount of
code to execute in just one instance of the game loop, that one frame will continue to
be drawn over and over until that code is completed.
This can happen when a game's save data is being saved or loaded, or during a loading
zone transition between two rooms.
The amount of data that is being handled is quite large, and it can't all be done in a
single frame of execution.
These two routines can be graphically represented on top of an active execution of the game
by showing the regions of the screen that are drawn while that routine is being executed.
The red region represents the V-blank routine, the blue region represents the main game code,
and yellow represents spinning.
This is the representation of the first example, when the V-Blank routine cuts into the display
area.
And here is the second example, where the main game loop isn't finished when NMI occurs.
This representation can also be applied to real-time execution, showing how the execution
time of each routine varies over time.
You can see that as the blue region approaches the bottom of the screen, and eventually touches
it, the game begins to slow down to make up for the lost time.
In order to change a hardware register while the screen is being drawn, forced-blanking
must be enabled for a short time.
The register can be updated, then F-blank can be disabled right after.
The downside to this is that there will be a black streak across the screen where this
update occured.
This can be hidden with something black on the screen, like a divider as in Super Mario
Kart, but sometimes this isn't viable.
It can be attempted to time this update during horizontal blanking instead, but the H-blank
period is so short that small artifacts on either side of the screen may still appear.
The Super NES had a feature called Horizontal Direct Memory Access, or HDMA, in addition
to ordinary DMA that allowed for this sort of thing to be done easily, without having
to do any timing calculations manually.
Everything about DMA and HDMA will be explained in the next video in this series.
Thank you for watching!
No comments:
Post a Comment