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.

Thursday, March 20, 2014

Hello World NES

I normally don't like code dumps as to me they seem like a way to bloat the size of a book or article. In future installments of this NES series, I will link to a separate zipped source files and just cover the chunks of code that are relevant. For a hello world program, I figured it would be best to have all the code. Granted, a 6502 assembly language Hello World for the NES has a bit more code than your typical Hello World program.

I am using NESASM for my assembler. Other options are possible in which case the code would be slightly different for setting up the ROM file header and banking information. The first thing when creating a ROM is the header file for the ROM. iNES is pretty much the standard format for NES emulators. The assembler needs a bit of information to create this file so the start of the program is assembler bookkeeping.

  .inesprg 1   ; 1x 16KB PRG code
  .ineschr 1   ; 1x  8KB CHR data
  .inesmap 0   ; mapper 0 = NROM, no bank swapping
  .inesmir 0   ; horizontal background mirroring

With the assembler given what it needs to output the ROM file, we are ready to start. The thing about early consoles is that they do not have any operating system. This means that when you start a NES program, the console is in an unknown state so the first thing that has to be done is make sure you disable things that you may not want going and set up things like the system stack. One important thing to remember is that the version of the 6502 that the NES has does not support the full instruction set. Sadly Binary Coded Decimal is not available so one of the first thing that you should do is make sure decimal mode is disabled otherwise who knows what could happen. This is followed by disabling the APU and the PPU's rendering.

  .bank 0
  .org $C000 

RESET:
  SEI          ; disable IRQs
  CLD          ; disable decimal mode
  LDX #$40
  STX $4017    ; disable APU frame IRQ
  LDX #$FF
  TXS          ; Set up stack
  INX          ; now X = 0
  STX $2000    ; disable NMI
  STX $2001    ; disable rendering
  STX $4010    ; disable DMC IRQs

For some reason, two vertical blanks need to happen before you can be sure that the PPU is working. Some additional housekeeping can be done between the vertical blanks but as we aren't doing much in this program there really is no need to do anything. Once the PPU is ready, we set up the palette. This is done by telling the PPU (through the memory addresses that are mapped to it) that we want to write to PPU memory. PPU memory is separate from the memory the 6502 has access to which has it's advantages, but this means that setting up any graphics means setting up the PPU memory address to write to then writing repeatedly to the PPU port mapped to $2007. The PPU conveniently increments the address to write for you.

  jsr WaitForVBlank ; First wait for vblank to make sure PPU ready
; can do a bit of additional initialization here if necessary
  jsr WaitForVBlank ; Second vblank. PPU now ready
  
LoadPalettes:
  LDA $2002             ; read PPU status to reset the high/low latch
  LDA #$3F
  STA $2006             ; write the high byte of $3F00 address
  LDA #$00
  STA $2006             ; write the low byte of $3F00 address
  LDX #$00              ; start out at 0
LoadPalettesLoop:
  LDA palette, x        ; load data from address (palette + the value in x)
  STA $2007             ; write to PPU
  INX                   ; X = X + 1
  CPX #$20              ; Compare X to hex $10, decimal 16 - copying 16 bytes = 4 sprites
  BNE LoadPalettesLoop  ; Branch to LoadPalettesLoop if compare was Not Equal to zero
                        ; if compare was equal to 32, keep going down

Now comes time for for the actual printing of Hello World. Note that the contents of the screen are undefined, and even if 0 would be incorrect so we also have to fill the remainder of the screen with our space character.\ This is done as with the palette by simply setting the screen memory address and sending all the characters to the PPU through port $2007.

HelloWorld:
  LDA #$00
  STA $2001    ; disable rendering

; first 8 lines blank
  LDX #0
  LDA $2002             ; read PPU status to reset the high/low latch
  LDA #$20
  STA $2006             ; write the high byte of screen address
  STX $2006             ; write the low byte of screen address
  ; Accumulator alread has $20 (space character) so no need to set it
HelloWorld_topBlank:
  STA $2007 ; write character to screen
  INX
  BNE HelloWorld_topBlank
HelloWorld_printHello:
  LDA hello_string,X
  STA $2007
  INX
  CPX #$10
  BNE HelloWorld_printHello
  
  ; prepair 2-level loop to fill remainder of screen
  LDX #$50 ; this is $50 due to $10(16) printed characters and
  ; $40 (64) attribute bytes 
  LDY #3 ; loop through 256 (-80 first pass) 3 times
  LDA #$20 ; printing spaces
HelloWorld_bottomBlank:
  STA $2007
  INX
  BNE HelloWorld_bottomBlank
  DEY
  BNE HelloWorld_bottomBlank

Of course, we also need to set the attribute table so the colors of the characters would be correct. This could have been avoided by setting all the palettes to the same 4 colors but as this table is conveniently located right after the screen memory we simply need to write 0s to the remaining 64 bytes.
 
  ; finally, set up attribute table
  LDX #$40
  LDA #0
HelloWorld_attributeTable:
  STA $2007
  DEX
  BNE HelloWorld_attributeTable

Nothing appears yet as we disabled the PPU earlier so now make sure the screen scroll position is set correctly and enable the background again then we do nothing. The NOP instruction isn't actually needed, but it is my favorite 6502 instruction so had to put it in here.
  
  ; set scrolling position and screen enable screen
  STA $2005
  STA $2005
  LDA #001000   ; enable background
  STA $2001

MainGameLoop:
  NOP ; Right now we do nothing in the main loop.
  JMP MainGameLoop

; ---------------------------
; Functions  
WaitForVBlank:
  BIT $2002
  BPL WaitForVBlank
  RTS

The data that we use for the program has to go somewhere. While this could easily fit with the program on bank 0, I like to at least make use of bank 1 since we need to set up the jump vectors anyway. Jump vectors are addresses at the end of the cartridge that get used when the cartridge is reset.

; ---------------------------
; Game Data

  .bank 1
  .org $E000
palette:
  .db $0F,$19,$2B,$39, $0F,$17,$16,$06, $0F,$39,$3A,$3B, $0F,$3D,$3E,$0F
  .db $0F,$14,$27,$39, $0F,$27,$06,$2D, $0F,$0A,$29,$25, $0F,$02,$38,$3C

hello_string:
  .db "  Hello World!!!", $00
  
; jump Vectors
  .org $FFFA     ;first of the three jump vectors starts here
  .dw 0          ; Jump for NMIs if enabled;
  .dw RESET      ; jump for when RESET or first turned on
  .dw 0          ;external interrupt IRQ is not used
  
; ---------------------------
; Character Tables (tilesets)
  .bank 2
  .org $0000
  .incbin "tileset.chr"   ;includes 8KB graphics file from SMB1

And that is all there is to it. Not really that difficult but there is a lot more work than the equivalent C program. Next (after a postmortem and possibly other article) we will take a bit more detailed look at the PPU ports then start playing with sprites.


Friday, March 14, 2014

The NES Screen

Now that we have an ASCII character set, we are now ready to display something. The display memory is broken into two parts called the NAME table and the ATTRIBUTE table. They are paired together and the NES supports up to four of them. The NAME table is a 32x30 grid of bytes that store the indexes of which characters to display.  The palette used to display the character is determined by the ATTRIBUTE table. This is where things get really confusing.

There are only 64 bytes of attribute memory yet 32 x 30 = 960.  there are only four palettes so only two bits per tile are needed. Even then, there is only enough memory for a quarter of the display. The obvious solution to this problem is to group the tiles into 2x2 blocks that share the same palette. This is what is done, but instead of storing the colour information consecutively, the PPU groups 2x2 blocks into another 2x2 block as shown in the figure below.



Up to four screens can be set up, but the NES only has enough memory built in for two screens. Additional RAM can be added if four way scrolling is required, but many games only need horizontal or vertical scrolling. If addition NAME/ATTRIBUTE table memory is not present, the missing memory is mirrored copies of existing memory based on the scrolling mode that is set.

The easiest way of thinking about the four screens is to think of them as a 2x2 grid of screens. You then set the pixel position of the display which determines which parts of the four screens are displayed.

Now that we have an overview of what is required to display a piece of text, we are ready to create a hello world program which is what we will be doing next.

Saturday, March 8, 2014

The ASCII Connection

It is important to remember that the characters that make up a character set are whatever you want them to be. If you are using a memory manager, you may have multiple character sets and be able to swap them as needed. That means that there are no rules as to where letters appear in the character set or even if there are any alpha-numeric characters at all. If the game you are creating doesn't have any text in it, then there is no requirement to have any letters in the set. Even if your game has text, it may not be necessary to have the entire set of letters or you may even incorporate the words as part of other images.

All this freedom is really nice to have, but one thing to remember is that the assembler you are using to convert the 6502 assembly language code into machine language does have a format it uses. While this is not directly relevant to the resulting code that is produced, this fact can be taken advantage of to reduce the amount of work that you will need to do. You see, any strings that you use get stored as ASCII values. If you are not using ASCII then instead of using strings such as "Hello World" in your code, you will need to have a table of numbers with the numbers being the character indexes you used for the letters you want to display. This is simple but time-consuming work that is easy to avoid.

ASCII, which stands for American Standard Code for Information Interchange, is a rather old standard that was created in the early 60's to allow for a standardized way of transmitting information between computers. It is a 7-bit standard because 6-bits was insufficient for all the characters needed and 8-bits would require an extra bit be transmitted for every character which was deemed too costly. Remember that next time you send a multi-megabyte image to someone!

The first 32 characters (indexes 0 through 31) are control codes so these characters can be used for any graphics you want as they don't have a visual representation. 32 is the space character, 48 through 57 are numbers 65 through 90 are upper case letters, 97 through 122 are lower case letters and between these are various punctuation marks. This layout may seem largely random but if you convert the indexes to hexadecimal then you will notice that displayable characters start at 20, numbers at 30, letters at 41 and 61.

I am sure that if one looked, they would be able to find a NES ASCII Character set, but making your own set reduces the chance for any legal claim. Here is an image of my character set that I will be using as the starting point for the games that I will be developing. It is not the greatest character set, but is good enough for my needs. Any characters that are not needed can easily be replaced with other graphics, though I suspect that this will not be necessary for my projects.



Now that we have characters that can be displayed, we are ready to take a look at how to actually display things on the screen.