Showing posts with label Atari 2600. Show all posts
Showing posts with label Atari 2600. Show all posts

Thursday, May 1, 2014

Maze 3D postmortem

While there was not enough interest in Coffee Quest 2600 to warrant developing it, I really wanted to see if my rendering technique really would work on the 2600 with the limitations the system has. This game is the result and proves that CQ2600 could be done. I would love to develop a proper 2600 RPG though it is doubtful this will happen. Still, here is the postmortem for my 2600 3D maze game.

What Went Right

Stella was the code name for the 2600 console and is also then name of an open source 2600 emulator. If you ever decide to develop a home-brew 2600 game, this is an awesome emulator to use during development. The built in debugger is simply wonderful to use. Not only does it have your break-points and stepping through code capabilities, but you also see where the cathode ray is when stepping through the code. As some of the hardest programming/debugging involves the timing of the cathode ray, this is a wonderful feature. Other handy features include actually seeing what the TIA registers hold and smart disassembly.  The disassembler appears to track exactly which parts of the ROM are used for instructions and what parts are used for play-field and sprite data showing graphical representation of the data. This debugger made getting the game working much easier!

Mixed Blessings

The game required reworking the display that was used in my Coffee Quest 2600 game. While a lot of the logic used in that game was usable, there was a fairly large amount of code that needed to be reworked. The decision to have a top bar with a compass in the centre was a direct result of playing Coffee Quest 2600 as I found that it was way too easy to get disoriented while playing the game. The compass at least makes it possible to know which direction you are heading in. I was also going to have coordinates displayed on the bar, but the amount of time and effort to add this was too high so this feature was left out of the game.

What Went Wrong

"Racing the Beam" is a phrase used to describe programming the 2600. This is sort of correct as the entire program revolves around the timing of the display. However, especially when you are doing more complex things such as asymmetrical play-fields, you are not trying to keep ahead of the beam but need to wait for the beam to get ahead of you. With asymmetrical play-fields, you set up the play-field registers for the first half of the display but can only set up the registers for the second half of the display only after the beam has drawn the register you are changing otherwise what is being drawn is incorrect. This results in ugly code where some of the logic that should be done at the end of the block has to be done in the middle of the block. This is so that the beam can draw the play-register that needs to be changed.

This was a very interesting challenge as such tight constraints in memory and timing are rarely issues with modern day programs. With so little time to get things done, seemingly simple things such as shifting mask registers takes too long and has to be replaced with small look-up tables. Yet look-up tables take up precious memory so it is an interesting balancing act.

Thursday, April 3, 2014

Hello World 2600

For comparison purposes, this week I am showing an assembly language version of Hello World, but created for the Atari 2600. This is using DASM as the assembler due to it's popularity with the 2600 home-brew crowd. This assembler supports a number of different chips so we start off with a declaration of the processor type. This is followed by setting up the constants that we use. In this case, the constants are the names of the TIA registers which are mapped to memory. While it may seem strange that many of these ports are mapped to precious zero page addresses, the 2600 only has 128 bytes of RAM so all RAM is in zero page anyway.

processor 6502

; Set up TIA registers as constants

VSYNC  = $00
VBLANK = $01
WSYNC  = $02
COLUPF = $08
COLUBK = $09
PF0    = $0D
PF1    = $0E
PF2    = $0F
INTIM  = $284
TIM64T = $296

As with the NES, things start out with basic housekeeping. Memory and TIA registers are zeroed out. The colours for the background and playfield are then set.

org $F000
Start
; Typical starting houskeeping
SEI ; Disable  Interrupts
CLD ; Clear BCD mode.
LDX #$FF ; Set ...
TXS ; ... stack pointer

; lets take advantage of X to wipe memory and TIA registers
LDA #0
ClearMemory
STA 0,X
DEX
BNE ClearMemory

; set up background and playfield colors
LDA #$CE ; A light greenish color
STA COLUBK
LDA #$60 ; Purple!
STA COLUPF

Next we start the main loop. This is where things get really different. The 2600 does not have any type of graphics memory. Instead, the display is drawn by the program as the television is actually drawing the display. This means that the program has to manage the television display. The first thing for doing that is to perform a Vertical sync which synchronizes the television signal with the frame that we are sending. This is done by telling the TIA chip we are syncing then waiting for 3 scanlines to be drawn. The WSYNC register will halt the processor until the horizontal blank (when the TV's Cathode ray is turned off and moved to the left for the next scanline) starts. This is followed by a 37 scan line  blank period before we start drawing the display. By setting a timer, we can do some work while we wait. The timer is set for 2752 cycles before it goes off. We have nothing to do, so we will waste this time.

MainLoop
; Vertical sync
; bit D1 of VSYNC needs to be set to turn on vsync
LDA #2
STA VSYNC
; now wait for 3 scanlines
STA WSYNC
STA WSYNC
STA WSYNC

; Vertical Blank
; set timer so we know when vertical blank nearly over
LDA  #43 ;load 43 (decimal) in the accumulator
STA  TIM64T ;and store that in the timer
; end VSync
LDA #0 ; Zero out bit 2 of VSYNC
STA  VSYNC ; to indicate sync time is over

; some game logic can go here while vertical blank happening
; as long as less than 2752 cycles

WaitForEndOfVBlank
LDA INTIM ; load remaining time
BNE WaitForEndOfVBlank ; wait till timer done

TAX ; set x to 0 for holding current scanline
TAY ; set y to 0 for offset data
; end vblank period
STA WSYNC
STA VBLANK  

Now we are ready to start drawing the message. The 2600 does not have any type of text capability so we are going to create the message using playfield graphics. This is only 40 blocks per scan line so rather rough looking but it is what we have. The thing is, the playfield registers only support 20 blocks with the other half of the display either copied or mirrored. In order to have an asymmetrical display like we need, the playfield registers need to be changed in the middle of drawing the line. Each line consists of 22 2/3 cycles in the horizontal blank period followed by 53 1/3 cycles of actual drawing time for a total of 76 cycles per scan line. These are 6502 cycles, as the TIA uses something it calls color-clocks which happen at 3 times the rate of processor cycles. This means that we need to set up the playfield registers by loading in our data from a data segment at the end of the program.  Then we do other stuff until the beam is far enough along. As I am repeating the playfield data for 16 lines per data set, the checking if time to move to next line of data is done in the middle of the scan to let the beam catch up with us. Finally, we set up the other half of the display then finish our end-of-scan-line logic.

ScanLoop
; fill left playfield data from table
LDA PlayfieldData,Y
STA PF0
LDA PlayfieldData+1,Y
STA PF1
LDA PlayfieldData+2,Y
STA PF2

; end of scanline logic done here so beam is far enough to reload 
; playfields. We are simply checking if on a line evenly divisible by
; 16 since when 16 (32,48,...) the lower bits will be zeros.
INX
TXA
AND #15
PHA

; replace playfield data with right side data from table
LDA PlayfieldData+3,Y
STA PF0
LDA PlayfieldData+4,Y
STA PF1
LDA PlayfieldData+5,Y
STA PF2

; durring mid line we calculated if time for next set of playfield bytes
; and pushed it onto stack. Pull results and check
PLA
BNE EndOfLine
; if time for new playfield data, increment y index by 6
TYA
CLC
ADC #6
TAY

EndOfLine
STA WSYNC
; are we finished rendering?
CPX #191
BNE ScanLoop

Once we have finished drawing the display, it is time for the over scan. This lasts 30 scan lines. Again, a timer is set up. Other things can be done while the timer is running, but we don't have anything to do so this will be wasted as well. Obviously, in a game this is where some of the game logic would be handled, and all the buttons handled. Did I mention that the buttons on the console, including the reset button, are the responsibility of the program? Thankfully we really don't need to worry about that with this program.

; Overscan
LDA #2 ; Set D1 bit for the VBLANK...
STA VBLANK ; Make TIA output invisible for the overscan, 
; set timer for overscan
LDA #35
STA TIM64T

; could put more game logic here if we had any
; 2240 cycles set on timer

WaitForOverscan
LDA INTIM ; load remaining time
BNE WaitForOverscan ; wait till timer done
STA WSYNC

JMP  MainLoop      ; Loop forver!

And now we are done. Now the playfield data. Notice that the playfield registers are not logically set up. PF0 is only half a byte, with bits 4 through 7 used drawn in that order (backwards from a human perspective). PF1 is written from bits 7 to 0 so is logical from a human perspective. PF2 is written from bits 0 to 7 so again is backwards from a human perspective. This strangeness was most likely done to make the TIA chip easier and cheaper to produce. Still, it is simply a matter of making sure the data is in the correct order, so here is the display data.

; Game Data
PlayfieldData ; pf0-4..7  pf1-7..0  pf2 0..7   pf0-4..7   pf1-7..0    pf2-0..7
.byte 000000, 000000, 000000, 000000, 000000, 000000 
.byte %01000000, %01011110, 100001, %10000000, %10000000, 000000 
.byte %01000000, %01010000, 100001, %01000000, %01000000, 000000 
.byte %11000000, %11011100, 100001, %01000000, %01000000, 000000 
.byte %01000000, %01010000, 100001, %01000000, %01000000, 000000 
.byte %01000000, %01011110, %11101111, %10010000, %10000000, 000000 
.byte 000000, 000000, 000000, 000000, 000000, 000000 
.byte 000000, 000010, %01100100, %11100000, 100001, 100011 
.byte 000000, 000010, %10010100, 100000, %10100001, 100100 
.byte 000000, 000010, %10010101, %11100000, 100001, 100100 
.byte 000000, 000011, %10010110, 100000, %10100001, 000100 
.byte 000000, 000010, %01100100, 100000, %10111101, 100011 
.byte 000000, 000000, 000000, 000000, 000000, 000000 

; Set pointers hardware uses to find start of program
org $FFFC
.word Start
.word Start

And we are finished. Clearly the NES is a nicer system but it is a lot newer. Considering what is involved in creating a 2600 game, you have to be impressed with many of the games that were created for the platform.

Friday, March 28, 2014

Coffee Quest 2600 postmortem

The Mini-Ludum Dare for this month had the theme of Demakes so I decided to create a version of Coffee Quest as it would be had it been created or ported to the Atari 2600. The link is http://blazinggames.com/gamejams/2014/MiniLD50/ but  it is going to be my April game. That means it is time for a postmortem.

What went right

For rendering the 3D view, the program emulates the play field graphics of the 2600 by having a function that takes the pf0, pf1, and pf2 registers along with 3 alternate values for the right side of the screen. While writing the rasterizer as if I was really coding for the 2600 took a lot longer than I like, it ensured that the resulting game was actually something that could run on the machine.  For me, the whole purpose of this challenge was to create a game that could actually run on real hardware. With a bit of work, it would be quite possible to create a full-fledged RPG along the lines of Coffee Quest IV on the 2600. Sure, the graphics would not be that great and a pretty big cartridge would have to be used but it is doable.

Mixed blessings

While the rendering is done the way it would have been coded for the 2600, the map is not. The map is 32x32 bytes which is far too large. If this was coded properly, the map would have been broken into a wall bitmap (which would have only taken 128 bytes of ROM) with an additional 16 bytes to hold the coordinates of the objects. The time taken getting the renderer working made this impractical so I went with a traditional tile map approach so the game could be finished over the weekend. As I know my reduced memory approach will work, this is not that huge of a deal as the game is clearly still possible.

What went wrong

I simply did not have the time necessary to create this game for the 2600 so had to roughly emulate the limitations of the system.  Having researched the 2600 before starting this project, I found the limitations of the platform really intriguing so was sad that doing this project for real hardware (or at least a properly emulated version of real hardware) was not practical. The problem is justifying the amount of time that would be required to create this game on a 2600 emulator. Even considering that modern tools allow for much greater efficiency in creating the game, I suspect that it would take at least a solid month of work to finish this game. With no real way of recouping the value of my time, this project is sadly not worth doing. If enough people were interest (or if someone was willing to sponsor the game) then I might reconsider this project. So, if anybody wants to see this project running on a 2600 emulator, email me at spelchan at blazinggames dot com and let me know.

Next week, partly as a continuation of this topic and partly to demonstrate how different machines can be when it comes to assembly language, I will be going over the Hello World program that I wrote for the Atari 2600. Just quickly going over what is required to do that simple program will give you much more respect for those poor 2600 programmers.