Chapter 9- October 5th, 2013
One Byte Scrolling
We have now arrived at a point where we activate yet another important feature of the game, the one byte hardware scroll. We're going to get a bit more technical and intense in this chapter as I attempt to explain the basic operation of how the GIME chip's horizontal scrolling works and how we go beyond the 2 byte hardware scroll limitation to implement a 1 byte hardware scroll that provides a smoother scroll at a slower scroll rate.
Although I won't be divulging into any code specifics, I will go into some of the GIME chip's operation with regards to the horizontal scrolling. I'll try not to confuse but if I do, just nod your head as if you totally understand. That's always worked for me!
How does horizontal scrolling work?
We've proven that split screen hardware horizontal scrolling is possible by changing the horizontal offset register at different horizontal scanlines of the screen. Next is one byte hardware scrolling but before we do, a quick tutorial on how the GIME chip's hardware scrolling works.
In the top diagram on the right, the red area indicates a normal 160 byte wide graphics screen (320x225 pixel resolution in this case). When horizontal virtually enabled mode is activated within the GIME chip, this screen area expands to 256 bytes as shown by the green area, but it still only displays a 160 byte screen. The difference now is that we must add 256 bytes to a memory location instead of 160 in order to wrap around to the byte displayed immediately below.
It is important to note that although memory continues to be mapped in the normal way, the way it displays on screen is different in a very important way.
Normally, after the GIME displays the byte on the rightmost edge of the screen, it retraces back to the leftmost edge and continues generating the next scanline down with the byte that follows the last byte previously displayed. This repeats for the entire screen.
When horizontal scrolling is enabled, the GIME chip displays the video memory a bit differently by retracing back to the leftmost edge and down to the next scanline but it skips the next 96 bytes to return to the byte directly below the leftmost edge on the 160 byte displayed page. This is assumuming you are using a 160 byte wide display mode as I am here. It changes depending on the video mode with the number of bytes skipped being 256 minus the mode byte width.
The horizontal offset register within the GIME determines where the leftmost edge starts and will display 160 bytes from there. A zero value in this register sets the screen to the leftmost edge of the 256 byte boundary as shown in the top diagram. Increasing this value shifts the start position to the right relative to the 256 byte position as shown in the middle diagram.
What happens when a value is input that pushes the right edge of the 160 byte screen beyond the 256 byte boundary? This is where it gets interesting.
The bottom image on the right shows this. Our horizontally offset image now starts in from the leftmost edge (the 2nd red area on the right). The displayed image passes the rightmost edge and wraps around, backtracking on the same row to continue from the leftmost edge of the 256 byte boundary till all 160 bytes of the screen are displayed. It then skips 96 bytes as before.
This is important because it allows the display to continue scrolling to the right. It will of course eventually repeat the same image but this is exactly what we want.
The process to creating an endless but varying scroll is that we draw the new incoming material just outside the incoming edge. If we draw the entire vertical incoming edge, this also effectively erases the old material that rolls off from the outgoing edge and would otherwise eventually be repeated and redisplayed when the horizontal offset register loops back. We can scroll forever by providing new display material and scrolling by merely incrementing the horizontal offset register.
The two byte scroll limitation
The horizontal scroll register uses bits 0 to 6 (7 bits) to represent the offset with bit 7 as the enable control for the horizontal virtually enabled mode. This bit must be kept ON meaning that the vales 128 to 255 represent the scroll positions. Let's ignore bit 7 (let's assume it is masked to zero) and simply talk about it by referring to the 7 bit values as 0 to 127.
Immediately you can see the limitation. We have a 256 byte virtually enabled screen yet we can only choose 0 to 127 positions (128 in total) within that area meaning that the hardware scroll jumps 2 bytes at each interval. 2 bytes account for 4 pixels at a time. If we have a scroll rate of 30 frames per second (60Hz fields) then this 2 byte scroll looks very smooth and fast. For this game though, I need a slower rate but if I lower the scroll rate, the 2 byte movement becomes very apparent with a jerkiness of the graphics movement.
I need to reduce the byte scroll interval to 1 byte movement which will provide a slower scroll rate but retain that glass smooth scroll but unfortunately re-engineering the GIME chip is not an option!
The 2 byte scrolling is an inherant operation of the GIME and may have been a technical design limitation imposed by the GIME fetching video data from memory 2 bytes at a time. Whatever the reason, I needed to find a way to use only one byte at a time.
Double buffer extension
I had always planned to use two screens for the display so as to implement video double buffering. This technique allows me to draw all the graphics for a frame offline while displaying the previous completed frame. This avoids the user from seeing the screen redraw operations. The screen is then switched to display the new completed frame and the process loops alternating between the two screens. The end result is a display devoid of flickering objects and looks very clean indeed.
I can extend the double buffering to create a 1 byte scroll effect by drawing each frame offset by 1 byte. With everything syncronized correctly, the display would move 1 byte at a time even though the hardware was still shifting 2 bytes.
Refer to the diagrams below right as I describe the technique. The squares highlighted in yellow represent the last 2 rightmost edge bytes of a displayed page. The squares following to the right (outside the yellow highlight) represent the bytes off-screen which are part of the 96 bytes the GIME display hardware skips. This is just a single scanline example but in reality would repeat for the entire right edge of the screen.
The solid squares represent 1 byte (2 pixels) with the pixels represented in each byte by their color. Let's assume that the entire screen is filled with black pixels. We are going to scroll 3 colored pixels sequentially in from the right edge and scroll them to the left. Screen 1 and Screen 2 are the screens we are going to use and will alternate between them.
A byte (black) is written to the byte immediately outside of the displayed highlighted yellow area. This byte represents the last byte written from the previous operation. Ignore this for now, just refer to it as the old byte.
Next, a 2nd byte (red) is written immediately after the last byte. Now, let's refer to this red square as the new old byte.
Now we switch to Screen 2. We do not change the horizontal offset at this stage. The offset on Screen 2 remains the same as Screen 1.
We repeat the operation from step 1 using the old byte (red) as the first byte written immediately after the yellow highlighted region and the next byte (green) is introduced immediately following as was done in step 1. This byte now becomes the new old byte.
Switch back to Screen 1 but this time increment the horizontal offset by 1. As we described earlier, the GIME will increment 2 bytes and this brings in the black/red pair of bytes written in step 1 into view. Note that the yellow highlighted area as been shifted to the right 2 bytes to show this. Note that the red byte has just entered the frame.
Now, we repeat the operation of writing bytes as done in steps 1 and 2 with the old byte (green) and a new byte (blue) being written after it. This byte now becomes the new old byte.
Switching again to Screen 2, we repeat the byte writing with the blue byte acting as the old byte followed by a black byte which then becomes the new old byte. Note that the red byte looks to have shifted to the left by one position and the green byte has entered the frame.
Remember, the parts within the visible display are only the parts highlighted by yellow. The user does not see the screen itself moving, only the contents. Visualize these 4 steps as constantly repeating and you will see that the colored bytes are moving to the left 1 byte at a time. It can be a bit of a brain twist to see it clearly so the best way to truly prove the theory is to actually code it and see it for real.
The proof is in the pudding!
After a few days, I had added the dual screen code and implemented a simple test for the 1 byte scroll. This was just a diagonal falling line from the top of the scroll area in my game screen falling down to the bottom only to repeat back at the top. With each iteration, the screen was scrolled to the left creating the diagonal fall of the line.
The image to the left is the fruits of my labours. A smooth scrolling diagonal strip moving to the left 1 byte at a time using the power of the GIME. All I needed to provide was the new incoming data.
Well, the image doesn't look too exciting unless you could actually see it moving but take my word for it, the movement was as smooth as glass.
But what's with the red parts in the top border?
This is a very important technique used to gauge how much time the code is consuming from the beginning of the vertical screen refresh to the end of my game code cycle. This is the start point of syncronisation for the entire game. The trick is to wait for the falling edge of the Vsync screen interrupt or the moment the screen draw has finished drawing one frame of display.
It generates an interrupt and then continues to retrace back to the top of the screen to begin drawing the next frame. This is done 30 frames per second or 60 fields per second (see NOTE 1 at end of chapter). What I am doing here is syncing with this interrupt, pausing the game execution till it occurs then immediately set the screen border color to red. I then execute all the game code and when it is finished, I reset the border to black and go back to waiting for the next Vsync interrupt. This gives me a graphical representation of how much time has been consumed by my game code and more importantly, how much time I have left before the next Vsync interrupt.
As you can see, it is high on the screen but remember, all I am doing so far is the scroll as well as the frequent interruption from the interrupt driven sound routine. As the game is developed this red region will get lower. The ultimate goal is to have the entire game processed before the red region reaches the bottom. A difficult task but achieving this would mean a game that runs at 30 frames per second. The reality will most probably end up that I go over this mark and have to fall back to 15 frames per second.
Never-the-less, I will strive for this magic number. I may need to simplify some of my planned graphics in order to achieve the required speed and efficiency but... that's life in the game development world.
Coming up next
In the next installment, I should have something more visually interesting to show you. I will be updating this scroll test code by implementing actual ground terrain with smooth scrolling from right to left, one byte at a time.
NOTE 1: An old-school CRT video image is composed of 30 frames of 60 interlaced fields. The CoCo doesn't do true interlaced video because otherwise we would have a vertical resolution that is double what we currently have (up to 450 lines). Can anyone tell me how the GIME is actually creating it's video? Does it just pad a blank field between each active field?
NOTE 2: I dug up my spare CoCo3 with a '96 issue GIME chip to test my code. My current development CoCo3 has a '87 issue GIME and I was aware that these versions differed with their handling of horizontal scrolling. Lo and behold, it didn't work right. The split screen didn't work within the game yet the earlier test code I did does work. I think it's a difference in counting my scanlines so I will endevour to fix this later as I get closer to the end. The screen still scrolls but the score and status display also scroll. Basically a full screen scroll is happening with the '86 GIME with the current code. Interestingly, the VCC emulator does the same thing which tells me that it's emulation was modelled on this '86 GIME.
Copyright 2013 by Nickolas Marentes