Code:
;NES Programming Tutorial
;Level 4 : Init Code
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Constants
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Variables
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;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
;Turn on NMI and rendering
LDA #%00000000
STA $2000
LDA #%10000000
STA $2001
;Infinite loop
Forever:
JMP Forever
;---------------------------;
NMI:
RTI
;---------------------------;
IRQ:
RTI
;---------------------------;
.pad $FFFA,$FF
;Vectors
.org $FFFA
.dw NMI
.dw RESET
.dw IRQ
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;CHR data $0000 ~ $1FFF (8KB)
.base $0000
;graphic data
.pad $2000,$FF
;Level 4 : Init Code
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Constants
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Variables
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;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
;Turn on NMI and rendering
LDA #%00000000
STA $2000
LDA #%10000000
STA $2001
;Infinite loop
Forever:
JMP Forever
;---------------------------;
NMI:
RTI
;---------------------------;
IRQ:
RTI
;---------------------------;
.pad $FFFA,$FF
;Vectors
.org $FFFA
.dw NMI
.dw RESET
.dw IRQ
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;CHR data $0000 ~ $1FFF (8KB)
.base $0000
;graphic data
.pad $2000,$FF
Output :
Explanation :
* SEI stands for SEt the Interrupt-disable bit, we use it to disable IRQ interrupt.
* CLD stands for CLear Decimal, NES cpu doesn't have Decimal mode at all but we use CLD to make sure it is disabled!
* $ is a prefix for address in hexadecimal system starting from $0000 to $FFFF
* #$ is a prefix for immediate value in hexadecimal system starting from #$00 to #$FF
* #% is a prefix for immediate value in binary system starting from #%00000000 to #$11111111
* The rightmost bit is called bit 0 or lsb (least significant bit)
* The leftmost bit is called bit 7 or msb (most significant bit)
* So the name of the bits are like this : bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
* LDA stands for LoaD Accumulator. Accumulator is a register of CPU, it is like a variable. It can hold max 8bit values starting from #$00 to #$FF
* STA stands for STore Accumulator. We can write value of Accumulator into an address
* PPU stands for Picture Processing Unit
* Some special addresses are called Port. CPU use Ports to control other parts like PPU, Joypads, etc
* Most of the Ports are bit wise. It means making their bit 1 or 0 have a special effect and meaning.
* $2000 and $2001 are PPU ports.
Code:
LDA #%00000000
STA $2000
STA $2000
* With this code we Load the value #%00000000 ( = #$00) into A and then we write A to $2000. Setting the 7th bit to 0 stops NMI Interrupt from happening.
Code:
LDA #%00000000
STA $2001
* We set the 3rd and 4th bits to 0 to hid the graphics. At start up there is nothing to show, right?
* $2002 is another PPU port. We can only read its value. It gives report about PPU. If 7th bit is 1 then it shows that vBlank is happening. Reading this port clears 7th bit and will stay 0 until the start of a new vBlank
* To find out the meaning of other bits of $2000, $2001, $2002 read this : PPU registers
* There are different kinds of Flags : Carry, Zero, Interrupt, Decimal, Overflow, Sign
* Executing an opcodes can set or clear these flags
* These flags together are called Processor Status
* Need more info about flags? then read this : CPU status flag behavior
* After each operation there is a result value. The 7th bit of the result is copied to SIGN FLAG. 0 shows positive result and 1 shows negative result
* BIT checks the 7th bit of $2002. If it is 0 vBlank is not happening. If it is 1 then vBlank is happening. SIGN FLAG gets the value
* BPL stands for Branch on PLus (positive sign). If SIGN FLAG is set to 0 then a jump will happen to its following label. It will jump until the SIGN FLAG is set to 1.
* ;PPU warm up : This loop runs until a new vBlank starts. We have to wait at least 2 vBlanks before using PPU. It needs time to become stabilized.
* LDX is similar to LDA. X is another register like A
* STA $0000, X : it is similar to this : A --> ($0000 + X). For example if the value of X is #$44, then the value of A will go into $0044.
* INX stands for INcrement X. For example if the value of X is #$44 then it will be #$45
* X can hold up to 8bit values, let's say X is #$FF, after INX it becomes #$00
* CPX stands for ComPare X register. It compares the value of X with its following value
* BNE stands for Branch on Not Equal. If compared values are not equal to each other then it will jump to its following label
* From $0000 to $07FF is 2KB RAM. We can store values like score, life, etc here to use later. At start up we have to clear the whole RAM
Code:
LDA #%10000000
STA $2001
STA $2001
* With this code we set the 7th bit of $2001 to 1, and by doing so the whole screen becomes blue!
* Here is a more correct and optimized init code
/////////////////////////////////////////////////////////////////////////////////////////////////
How to use Debugger :
* Sometimes the game doesn't work as we expect, maybe because of a logical mistake in the source code.
* So how to find it? We need to use a tool called Debugger. We can run the game step by step and check the codes to find the error.
* In the above code the RESET label is under $8000, it means the game will run from that address. So let's use debugger from that address and find out what is going on!
* Run the game with FCEUX emulator
* Go to Debug > Debugger
* Click Add
* Type 8000 in the address box, click on Execute option, click ok
* This is called a breakpoint and it means when CPU want to read or write or execute a special address then stop the game and use debugger to run the game step by step.
* Go to NES > Reset
* Game stops running and debugger windows comes up
* On the first line it shows the instruction which is going to be executed
Code:
00:8000:78 SEI
* Click on step into to run the instructions one by one
/////////////////////////////////////////////////////////////////////////////////////////////////
How to disassemble :
* An assembler (ASM6) converts the source code of assembly language to object code of machine language : Game.asm --> Game.nes
* A dissembler does the opposite : Game.nes --> Game.asm
* Disassembling is not accurate all the times because of some difficulties, for example it is not easy to distinguish between opcodes and data.
* The best disassembler that I found so far is : NESrevPlus
* It is easy to use : load your game.nes into it then save game.asm :
* Compare the original source code with disassembled one.
* Wow today was awesome, wasn't it?
/////////////////////////////////////////////////////////////////////////////////////////////////
Exercise :
Change the background color to Red.
/////////////////////////////////////////////////////////////////////////////////////////////////
Files :
asm6.exe
Assembler.bat
Game.asm
/////////////////////////////////////////////////////////////////////////////////////////////////
Former Level : NES Programming Tutorial : Interrupts
Next Level : NES Programming Tutorial : Background