skip navigation

Analysing the Atari 2600 Code

Description

In this episode we look at the code from the previous episode, stepping through it line by line. We'll talk about some of the 6507 op-codes and Atari Television Interface Adaptor registers. We'll also touch on the difference between colour palettes for NTSC, PAL, and SECAM CRT's. A small change to the example code highlights the colour capabilities of the TIA.

Released:
March 22, 2021

Original Link:
https://youtube.com/watch/nKhgqCp8wNk

Transcription

Your support is greatly appreciated! https://www.patreon.com/8blit

In the last video we talked about setting up a development environment to enable us to write, compile and execute some code using the dasm assembler and the Stella Atari emulator.

We used a short piece of code which simply turns the screen blue, and In this video we’ll examine that code to find out how it works. But before we do that, we’ll have to talk about how the Atari 2600 executes it’s code, and how it draws to the television screen.

You see, back when the atari 2600 came out, televisions used a Cathode Ray Tube (commonly known as a CRT). This would use a ray of negative particles fired from an electrode, and directed on to glass covered in phosphor. The negative particles would excite the phosphor causing it to glow. Using magnetic fields, the ray can be deflected to land on any point on the glass. Done quickly, this forms the images we see on the screen.

The first commercial CRT television was released in the 1930’s, and while other technologies have all but replaced the CRT, they are still very much in use today.

The CRT’s electron beam starts drawing at the top left of the screen, and draws a horizontal line across to the right. It then moves the ray back to the right and down one to draw another line. It repeats this action moving all the way down the screen to the bottom. It will then reset it’s position back to the starting point and begin drawing the next frame.

On NTSC CRT screens used primarily in North America, this means the electron beam draws 525 lines per frame, at about 30 frames per second. In many European countries, PAL screens are the standard which uses 625 lines per frame, at 25 frames per second. Another standard called SECAM is used in Russia, China, Pakistan, France, and a few other countries is similar to PAL, but the way it processes colour makes it incompatible. There are versions of Atari 2600 consoles for each of these standards. However, as we’ll see shortly, the TIA chip in the SECAM version has a dramatically limited colour palette.

Due to the way the electron beam works on a CRT, there are several periods of time where it cannot draw on the screen. During these time periods you are free to do calculations in your game, or setup the location of your game objects Vertical Sync is the time period it takes to signal the CRT to start a new frame. On NTSC CRT’s this lasts for 3 scan lines. Vertical Blank is the time it takes to physically turn off the beam while it is being repositioned back to the start of the frame. This lasts for 37 scan lines. Horizontal Blank is the time it takes to physically turn off the beam and reposition it to the start of the next scan line. This lasts for 28 clock cycles and happens on each of the 192 scan line used for drawing the screen. Overscan is another period when the beam is turned off when it’s not drawing on the screen.

CRT’s are not all made the same, so in some cases you may wish to draw a few more or a few less scanlines. For NTSC, the recommended number of scan lines to use for drawing the screen is 192, while PAL and SECAM recommended number 228. For the most part, you can bend these numbers a little as long as the total scan lines per frame are equal to 262 for NTSC and 312 for PAL and SECAM. However, the farther you stray from the recommended values the less compatible you will be with different brands and models of televisions.

So now we know how a television’s CRT works, and the recommended timing of the sections of each frame, but how do we deal with this in our program? Let’s take a look at some of the commands we’ll be sending to the Atari’s Television Interface Adapter to handle drawing to the screen.

The vcs.h file included with the dasm assembler includes pre-defined labels for common register addresses on the Television Interface Adapter. The first is VSYNC or Vertical Sync. By assigning a value to this register, you’re signalling the television to blank the bean and position it at the top of the screen to start a new frame. This signal must be present for at least 2 scan lines. You can then write 0 to the VSYNC register to turn it off.

Next is VBLANK or vertical blank. By setting the VBLANK register to 0, you’re telling the television to turn off the beam during its vertical sync. This takes 37 scan lines before the image will draw to the screen.

WSYNC stands for “Wait for Sync”. The TIA keeps track of it’s horizontal position while it’s drawing a scan line, and will automatically move to the beginning of the next line when it reaches 76 machine cycles. Since it may be difficult, if not impossible to determine exactly when this would occur in your code, you can write 1 to the WSYNC register and the TIA will halt execution of the 6507 processor until the start of a new scan line. The last register we’ll talk about here is COLUBK or Colour-Lum background. By setting a value to this register the TIA will draw that colour behind all objects.

In this chart we can see the Atari 2600 colour palette for both NTSC and PAL, along with the hexadecimal value associated with each colour.
Notice that NTSC has 16 available colours, while PAL is limited to 13.

Both however have 8 luminance values for each colour.

While the mapping between NTSC and PAL colours do not match up, with careful consideration you could design your game with to work on both with no changes. If you wanted to make use of the full colour palettes in each format you would need to distribute two version of your game as there is no way programmatically to determine which standard your Atari 2600 is using.

This gets a lot more difficult if you also want to support SECAM as there are only 8 colours and no luminance values. You can still play a NTSC or PAL game on a SECAM system, but the luminance values would be mapped to these 8 colours.

Now that we know how the registers on the Television Interface Adapter work, it’s time to talk about the registers on the 6507 processor, and various operations available to use for our logic and calculations. As we did before, for now we’ll limit these to only the items that were used in our example program.

Starting with the A or Accumulator register and the X and Y index registers. Each holds a single byte and most operations you will use work with the contents of these registers.

The P or Processor Status register is set with certain flags based on the results of the last operation you executed. LDX, and LDA both load a value into the corresponding x or a register. STA will store the value currently stored in the a register into another location. INX will increase the value currently stored in the x register by 1. CPX stands for Compare X register, and depending on the value stored in the x register and what you are comparing it to, it will set the Negative, Zero, or Carry flags of the P register BNE stands for Branch Not Equal and will transfer execution to another location based on the Zero flag of the P register. JMP stands for Jump. This tells the processor to transfer program execution to the supplied address or label.

There are a couple more registers on the 6507, and a lot more operations available in the instruction set. We’ll leave those for a later video because write now we just want to focus on, what exactly is our example code doing? So let’s get into that now.

In the first few lines we’re just telling the assembler what type of processor we want this to run on, and to include the VCS and MACRO files as part of your program. As mentioned previously, these files contain predefined labels to the TIA registers and a few helpful routines.

Here we’re defining a label named BLUE, along with a value. In this case we chose the hexadecimal value 9A.

If we refer to our colour pallets, we can see that 9A on the NTSC palette is a soft blue colour. Luckily, the same value on the PAL palette is very similar. Had we chosen 5A, the colour on an NTSC television would be pink, and a very different green on PAL televisions.

Going back to our code. The next two lines are pseudo operations or assembler directives. They are not code that will be turned into processor instructions. SEG is defining the start of the code segment which will be included in the binary ROM file when compiled, and with ORG being the address of the start of RAM.

Here we define another label, but instead of assigning a value to it, it can be used as the destination of a jump point later on in the code. We’ll get back to this Reset label in a bit.

The Atari 2600 starts up in an uninitialized state, so to start off with a clean slate we need to zero out all of RAM and the TIA registers. First we assign X and A to 0, define a label called ‘Clear’ to use as a loop point, and then store the value in A which is 0, to the memory address stored in X, we then increase the value in X by 1 then check if X is currently 0 and if not transfer execution back to the Clear label.

Now you may be asking yourself how does X go back to 0 if we keep increasing the value by 1. That’s because the 8 bit registers wrap-around from 128 to 0 when you increment it past it’s maximum value. Since the Increase X operation sets the processor status flags in the P register, we can use Branch if not equal to monitor the Zero flag in the P register.

This is where we’re going to set the colour we want as our background. We first load the value stored in the label name BLUE into the A registers, then we store that value from the A register in to the COLUBK register on the TIA

Now we’re going to start the first frame, so we create a label so we can jump back to it for each frame.

The frame starts by first turning off VBLANK by loading 0 in the A register, and then storing A into the VBLANK register. Now we want to turn on VSYNC by loading 2 into the A register , and then storing A into the VSYNC register. Since VSYNC requires 3 scan lines, we simpleng initiating 3 WSYNC’s. As we covered earlier, writing a value into WSYNC will cause the TIA to halt the 6507 processor until the beginning of the next scan line, at which point it will release the 6507 to continue executing starting at the next op code. After three full scan lines, it’s time to turn off VSYNC, which is done by loading 0 into the A register and then storing it in VSYNC register.

After VSYNC, we have 37 scanlines of vertical blank. In normal circumstances, we would be coding our game logic during VSYNC, VBLANK, and HORIZONTAL BLANK periods, but since this is a basic demo and we’re only setting the background colour, we only really need to generate then necessary timings. So once again we’re just going to set up a loop calling WSYNC for 37 scanlines.

We do this by loading 0 into the X register. We’ll use this as our loop counter. and create a label called Vertical Blank as the beginning of our loop point. Then it’s time to store a value into WSYNC which will halt the processor until the start of the next scanline. We increase the X register by 1, and the Compare the value in the x register with the value 37. If they are equal then the Zero flag on the P register will be set and the Branch if Not Equal op-code will continue on to the next op-code. If the values are not equal then execution will start again back at the VerticalBlank label to begin another loop.

Now it’s time for the Television Interface Adaptor to draw the screen, which will take 192 scan lines. Since we already set the COLUBK register previously, we don’t need to do anything else except wait for the whole 192 scan lines. Just as we did with the VERTICAL BLANK period, we’re just going to create a loop to generate 192 WSYNC’s.

After the screen is drawn we’ll turn VBLANK back on to stop the beam. This time we’re loading a binary value which will set bit 6 and bit 1 of the VBLANK register. Bit 1 will cause the TELEVISION INTERFACE ADAPTOR to display black, and bit 6 causes the TIA to latch the joystick buttons. Which we will get into more detail in a later video.

And finally we’re in the Overstan period which lasts for 30 scan lines. Once again, since our code doesn’t need to do anything, we’re setting up a loop to initiate 30 WSYNC, and then jump all the way back up near the top where we defined the StartOfFrame label to begin the frame loop all over again. This will keep repeating itself until indefinitely, or until you turn off the machine.

Here we’re defining the locations of code that will handle a little housekeeping. The bottom three are Interrupt Vectors. You can think of interrupts as switches that can go on, and the processor is constantly monitoring them, and an Interrupt Vector is a pointer to an area of code to execute when those switches are latched. On the 6507, these Interrupt Vectors must start at address FFFA in the binary rom file.

NMI stands for Non-maskable interrupt, and typically occurs during a hardware error.

IRQ stands for Interrupt Request and is used by hardware to tell the processor to stop and run it’s own code.

Both of these cannot be triggered by hardware on the Atari 2600’s 6507 chip because their pin’s on the processor have been removed as a cost saving measure. However, the Atari 7800 runs on a 6502, which does have the necessary pins and is backwards compatible with the Atari 2600 games. RESET is the only one you need, and it must be at this location in the rom file. You see, this actually the very start of our program. When the 6507 starts up, it first executes the code located at this location. Here we have the Reset Vector pointing to the RESET label, which we defined all the way near the beginning of your code. So the processor essentially jumps to this known address, to find out where the start of our code begins.

Well that’s pretty much everything that’s going on in the code. Really it’s mostly nothing because we’re just setting a background colour and then wasting cycles until the VSYNC. Now we can’t just take out all those wasted cycles because it’s actually essential to the Television Interface Adaptors timing as we’re tied to the refresh rate of a CRT television. The CRT is going to draw the screen on it’s own schedule and we just have to cram in our game logic in the folds.

Now I have a slight modification of the code for you to better illustrate the scan lines.

Here is the exact same code we looked at for the blue background, but I’ve added a single operation. Instead of defining the label BLUE, assigning it a colour, and then pushing that in the COLUBK register, we’re just going to push the current scanline index into the register. This will show all 128 NTSC colours on the screen at once, one per scanline. Of course, there will only be 104 for PAL, and 8 for SECAM.

So this is the end of the video, and we’ve looked at a lot of information on how the 6507 processor works, a few of the capabilities of the Television Interface Adaptor, or TIA for short, and we’ve gone through our sample code and made a slight modification. I hope you found this interesting and informative. If so, please like and subscribe to follow along. The code presented will always be available on our GitHub which is linked below.

What is your experience with programming? Have you done it before, recently started, or are you a professional? Have you programmed in Assembly before, or built a game? Let me know in the comments below.

Thanks for watching, and following along on our journey. Bye for now.

Back to top of page