NES Programming Tutorial : Background

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
NES Programming Tutorial : Background
by on (#190905)
NES graphic :

* NES uses a very complex method to compress and store graphic data, why? Maybe because memory was expensive back then

* There are two kinds of graphic on screen : 1) Background 2) Sprites

* Anything that moves on the screen is a sprite (for example Mario), other fixed graphics are background

* Background and sprites are made from small tiles

* Tiles are stored inside of CHR (an EPROM memory inside of the cartridge)

* Each tile is 8x8 pixel

Image

* To see and edit those tiles you can use YY-CHR.NET or Tile Layer Pro

Image

* Need more info? then read this : PPU pattern tables

* Background and Sprites needs color, right? But you know NES hardware is not very powerful, it can only produce maximum of 64 different colors! Also it is not possible to use all of those 64 colors at the same time!

* You can choose and apply only 13 colors to background, and 13 other colors for sprites.

* Fortunately with some programming methods you can overcome these limitations, well to some extent! For example you can choose different colors for each stage of the game!

* For more info about colors read this : PPU palettes

* Yeah I know, NES has a lot of limitations, it is really disappointing, then again if you are a NES enthusiast then you have to live with its limitations

/////////////////////////////////////////////////////////////////////////////////////////////////

Extracting graphic data from a NES game :

* Run Super Mario Bros. (W) [!].nes with FCEUX emulator

* Start the game and then press Pause on the keyboard

* Go to Debug > PPU Viewer. On the left you can see the tiles for sprites, on the right there are tiles of background

Image

* There are some colors on the button. The first row is the colors of background. The second row is the colors of sprites.

* Go to Debug > Name Table Viewer. You can see the background picture here.

Image

* Go to Debug > Hex Editor > View > PPU Memory

Image

* Do not confuse PPU memory address with CPU memory address, they are completely separated from each other

* PPU has its own address space starting from $0000 to $3FFF

* From $0000 to $0FFF (4KB) is data of Left pattern table (sprites tiles)

* From $1000 to $1FFF (4KB) is data of right pattern table (background tiles)

* From $2000 to $2400 (1KB) is data of first name table (background graphic)

* From $3F00 to $3F0F (16bytes) is data of background colors

* From $3F10 to $3F1F (16Bytes) is data of sprites colors

* Copy those values and by using HxD save them separately : mario_spt.chr, mario_bg.chr, mario_bg.nam, mario_bg.pal, mario_spt.pal,

/////////////////////////////////////////////////////////////////////////////////////////////////

Code:
;NES Programming Tutorial
;Level 5 : Background
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Constants
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Variables
L_byte         = $0000
H_byte         = $0001
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;iNES header data (16bytes)
;32KB PRG + 8KB CHR + NROM-256 + Vertical Mirroring
  .db $4E,$45,$53,$1A,$02,$01,$01,$00
  .db $00,$00,$00,$00,$00,$00,$00,$00
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;PRG codes $8000 ~ $FFFF (32KB)
  .base $8000

RESET:
   SEI
   CLD

;Turn off NMI and rendering
   LDA #%00000000
   STA $2000
   LDA #%00000000
   STA $2001

;PPU warm up
   LDA $2002
vBlank_wait1:
   BIT $2002
   BPL vBlank_wait1
vBlank_wait2:
   BIT $2002
   BPL vBlank_wait2

;Clear RAM
   LDA #$00
   LDX #$00
clear_loop:
   STA $0000, X
   STA $0100, X
   STA $0200, X
   STA $0300, X
   STA $0400, X
   STA $0500, X
   STA $0600, X
   STA $0700, X
   INX
   CPX #$00
   BNE clear_loop

;Name table + Attribute
   LDA $2002
   LDA #$20
   STA $2006
   LDA #$00
   STA $2006
   LDA #<bg_nam
   STA L_byte
   LDA #>bg_nam
   STA H_byte
   LDX #$00
   LDY #$00
nam_loop:
   LDA ($00), Y
   STA $2007
   INY
   CPY #$00
   BNE nam_loop
   INC H_byte
   INX
   CPX #$04
   BNE nam_loop

;Background color setup
   LDA $2002
   LDA #$3F
   STA $2006
   LDA #$00
   STA $2006
   LDX #$00
bg_pal_loop:
   LDA bg_pal, X
   STA $2007
   INX
   CPX #$10
   BNE bg_pal_loop

;Reset Scroll
   LDA #$00
   STA $2005
   LDA #$00
   STA $2005
   
;Turn on NMI and rendering
   LDA #%00000000
   STA $2000
   LDA #%00001010
   STA $2001

;Infinite loop
Forever:
   JMP Forever
;---------------------------;
NMI:
   RTI
;---------------------------;
IRQ:
   RTI
;---------------------------;
bg_nam:
  .incbin "mario_bg.nam"

bg_pal:
  .incbin "mario_bg.pal"
;---------------------------; 
  .pad $FFFA,$FF   
;Vectors
  .org $FFFA
  .dw NMI
  .dw RESET
  .dw IRQ
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;CHR data $0000 ~ $1FFF (8KB)
  .base $0000
  .incbin "mario_bg.chr"
  .pad $2000,$FF


Output :

Image


Explanation :

Code:
L_byte         = $0000


* With this code we simply assign a name to an address. Remembering the names is easier than remembering some bunch of address numbers, right?

Code:
   LDA #%00001010
   STA $2001


* We want to show the background so we have to set 1st and 3rd bits

* To make graphic on screen you have to write graphic data to PPU memory

* But you can't write directly to PPU memory, you have to use PPU ports : $2006 and $2007

* By using $2006 you declare the address of PPU memory then by using $2007 you write the desired value to that address

* PPU Memory addresses are 16bit starting from $0000 ~ $3FFF (4KB)

Code:
   LDA #$20
   STA $2006
   LDA #$00
   STA $2006


* So you need to write twice to $2006 to declare it's address, the first write declares high byte of address, the second write declares the low byte of address

* Each time you write a value to $2007, the PPU address is automatically adjusted to the next address, so you don't need to declare the PPU address with $2006 for sequential PPU memory addresses

Code:
bg_nam:
  .incbin "mario_bg.nam"


* We can add any data file by using .incbin to the source code file

Code:
   LDA #<bg_nam


* bg_nam is a label right? So it has 16bit address. By using < we take its low byte of address and then store it into A

* We assigned L_byte to $0000 already

Code:
   STA L_byte


* Also we take the high byte of the address of bg_nam and then store it into $0001

Code:
   LDA #>bg_nam
   STA H_byte


* LDY is similar to LDA. Y is another register like A

* From $0000 to $00FF is ram right? but it is kind of special, because we can show those address with this : $00 to $FF, we call those address Zero Page

* LDA ($00), Y : Let's imagine : $0000 = #$00 / $0001 = #$90 / Y = #$44. Then : $9000 is made. Then $9000 + #$44 = $9044. Then the value inside of $9044 is loaded into A

* INY stands for INcrement Y, For example if the value of Y is #$44 then it will be #$45

* CPY stands for ComPare Y register. It compares the value of X with its following value

* INC H_byte, increase the value inside of $0001 by one

* The codes of ;Name table + Attribute actually transfers a total of 1KB (= 400 bytes in hex) data of background to PPU memory ($2000 ~ $2400)

* The codes of ;Background color setup actually transfers a total of 16 bytes (= 10 bytes in hex) data of background color to PPU memory ($3F00 ~ $3F0F)

* $2005 is another PPU port which is used to move the background

* Need more info about PPU ports? then read this : PPU registers

* Need to learn about 6502 opcodes? then read this : Assembly In One Step

* Need a reference for assembly 6502 opcodes? then check this : NMOS 6502 Opcodes

/////////////////////////////////////////////////////////////////////////////////////////////////

Editing a background :

* You can edit the exiting backgrounds

* Download 8name which is a background editor

* Make a new text file

* Write this inside of it :

Code:
name mario_bg -f


* Save it with this name : Bg_Editor.bat

* Make sure these files are in the same folder :

Code:
name.exe
Bg_Editor.bat
alleg42.dll
mario_bg.nam
mario_bg.pal
mario_bg.chr


* Run Bg_Editor.bat to start editing the background :

Image

/////////////////////////////////////////////////////////////////////////////////////////////////

NES Screen Tool :

* You can edit your extracted graphic files with NES Screen Tool

* Use "Patterns > Open CHR" to load "mario_bg.chr"

* Use "Nametable > Open nametable or map" to load "mario_bg.nam"

* Use "Palettes > Open palettes" to load "mario_bg.pal"

Image

* After editing use "All > Save" to save the files

/////////////////////////////////////////////////////////////////////////////////////////////////

Exercise :

Extract graphic data from other games and use them instead of mario_bg.nam, mario_bg.pal, mario_bg.chr.

/////////////////////////////////////////////////////////////////////////////////////////////////

Files :
asm6.exe
Assembler.bat
Game.asm
name.exe
Bg_Editor.bat
alleg42.dll
mario_bg.nam
mario_bg.pal
mario_bg.chr

/////////////////////////////////////////////////////////////////////////////////////////////////

Former Level : NES Programming Tutorial : Init Code
Next Level : NES Programming Tutorial : Move Background