Code:
;NES Programming Tutorial
;Level 8 : Multiple Sprites
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Constants
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Variables
L_byte = $0000
H_byte = $0001
bg_X_pos = $0002
bg_Y_pos = $0003
NMI_index = $0004
spt1_Y = $0200
spt1_tile = $0201
spt1_att = $0202
spt1_X = $0203
spt2_Y = $0204
spt2_tile = $0205
spt2_att = $0206
spt2_X = $0207
spt3_Y = $0208
spt3_tile = $0209
spt3_att = $020A
spt3_X = $020B
spt4_Y = $020C
spt4_tile = $020D
spt4_att = $020E
spt4_X = $020F
spt5_Y = $0210
spt5_tile = $0211
spt5_att = $0212
spt5_X = $0213
spt6_Y = $0214
spt6_tile = $0215
spt6_att = $0216
spt6_X = $0217
spt7_Y = $0218
spt7_tile = $0219
spt7_att = $021A
spt7_X = $021B
spt8_Y = $021C
spt8_tile = $021D
spt8_att = $021E
spt8_X = $021F
spt9_Y = $0220
spt9_tile = $0221
spt9_att = $0222
spt9_X = $0223
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;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 #<nam_att
STA L_byte
LDA #>nam_att
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
;Name table + Attribute 2
LDA $2002
LDA #$24
STA $2006
LDA #$00
STA $2006
LDA #<nam_att_2
STA L_byte
LDA #>nam_att_2
STA H_byte
LDX #$00
LDY #$00
nam_loop_2:
LDA ($00), Y
STA $2007
INY
CPY #$00
BNE nam_loop_2
INC H_byte
INX
CPX #$04
BNE nam_loop_2
;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
;Sprites color setup
LDA $2002
LDA #$3F
STA $2006
LDA #$10
STA $2006
LDX #$00
spt_pal_loop:
LDA spt_pal, X
STA $2007
INX
CPX #$10
BNE spt_pal_loop
;Sprites data to ram
LoadSprites:
LDX #$00
LoadSpritesLoop:
LDA sprites, X
STA $0200, X
INX
CPX #$00
BNE LoadSpritesLoop
;Reset Scroll
LDA #$00
STA $2005
LDA #$00
STA $2005
;Turn on NMI and rendering
LDA #%10001000
STA $2000
LDA #%00011010
STA $2001
;Infinite loop
Forever:
;Move background
JSR vblank_wait
INC bg_X_pos
LDA bg_X_pos
STA $2005
LDA #$00
STA $2005
;Give effect to sprites
INC spt6_Y
INC spt7_Y
INC spt8_Y
INC spt9_Y
;Sprites data to OAM
JSR vblank_wait
LDA #$00
STA $2003
LDA #$02
STA $4014
JMP Forever
;---------------------------;
vblank_wait:
LDA NMI_index
not_yet:
CMP NMI_index
BEQ not_yet
RTS
;---------------------------;
NMI:
INC NMI_index
RTI
;---------------------------;
IRQ:
RTI
;---------------------------;
nam_att:
.incbin "mario_bg.nam"
nam_att_2:
.incbin "mario_bg_2.nam"
bg_pal:
.incbin "mario_bg.pal"
spt_pal:
.incbin "mario_spt.pal"
sprites:
.incbin "mario_spt.oam"
;---------------------------;
.pad $FFFA,$FF
;Vectors
.org $FFFA
.dw NMI
.dw RESET
.dw IRQ
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;CHR data $0000 ~ $1FFF (8KB)
.base $0000
.incbin "mario_bg.chr"
.incbin "mario_spt.chr"
.pad $2000,$FF
;Level 8 : Multiple Sprites
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Constants
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Variables
L_byte = $0000
H_byte = $0001
bg_X_pos = $0002
bg_Y_pos = $0003
NMI_index = $0004
spt1_Y = $0200
spt1_tile = $0201
spt1_att = $0202
spt1_X = $0203
spt2_Y = $0204
spt2_tile = $0205
spt2_att = $0206
spt2_X = $0207
spt3_Y = $0208
spt3_tile = $0209
spt3_att = $020A
spt3_X = $020B
spt4_Y = $020C
spt4_tile = $020D
spt4_att = $020E
spt4_X = $020F
spt5_Y = $0210
spt5_tile = $0211
spt5_att = $0212
spt5_X = $0213
spt6_Y = $0214
spt6_tile = $0215
spt6_att = $0216
spt6_X = $0217
spt7_Y = $0218
spt7_tile = $0219
spt7_att = $021A
spt7_X = $021B
spt8_Y = $021C
spt8_tile = $021D
spt8_att = $021E
spt8_X = $021F
spt9_Y = $0220
spt9_tile = $0221
spt9_att = $0222
spt9_X = $0223
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;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 #<nam_att
STA L_byte
LDA #>nam_att
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
;Name table + Attribute 2
LDA $2002
LDA #$24
STA $2006
LDA #$00
STA $2006
LDA #<nam_att_2
STA L_byte
LDA #>nam_att_2
STA H_byte
LDX #$00
LDY #$00
nam_loop_2:
LDA ($00), Y
STA $2007
INY
CPY #$00
BNE nam_loop_2
INC H_byte
INX
CPX #$04
BNE nam_loop_2
;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
;Sprites color setup
LDA $2002
LDA #$3F
STA $2006
LDA #$10
STA $2006
LDX #$00
spt_pal_loop:
LDA spt_pal, X
STA $2007
INX
CPX #$10
BNE spt_pal_loop
;Sprites data to ram
LoadSprites:
LDX #$00
LoadSpritesLoop:
LDA sprites, X
STA $0200, X
INX
CPX #$00
BNE LoadSpritesLoop
;Reset Scroll
LDA #$00
STA $2005
LDA #$00
STA $2005
;Turn on NMI and rendering
LDA #%10001000
STA $2000
LDA #%00011010
STA $2001
;Infinite loop
Forever:
;Move background
JSR vblank_wait
INC bg_X_pos
LDA bg_X_pos
STA $2005
LDA #$00
STA $2005
;Give effect to sprites
INC spt6_Y
INC spt7_Y
INC spt8_Y
INC spt9_Y
;Sprites data to OAM
JSR vblank_wait
LDA #$00
STA $2003
LDA #$02
STA $4014
JMP Forever
;---------------------------;
vblank_wait:
LDA NMI_index
not_yet:
CMP NMI_index
BEQ not_yet
RTS
;---------------------------;
NMI:
INC NMI_index
RTI
;---------------------------;
IRQ:
RTI
;---------------------------;
nam_att:
.incbin "mario_bg.nam"
nam_att_2:
.incbin "mario_bg_2.nam"
bg_pal:
.incbin "mario_bg.pal"
spt_pal:
.incbin "mario_spt.pal"
sprites:
.incbin "mario_spt.oam"
;---------------------------;
.pad $FFFA,$FF
;Vectors
.org $FFFA
.dw NMI
.dw RESET
.dw IRQ
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;CHR data $0000 ~ $1FFF (8KB)
.base $0000
.incbin "mario_bg.chr"
.incbin "mario_spt.chr"
.pad $2000,$FF
Output :
Explanation :
* Transferring 4-byte info of one sprite with $2003 and $2004 is ok, but it is slow for multiple sprites
* There is a faster way to transfer multiple sprites data to OAM : Using $2003 and $4014
* The data of sprites are stored in the RAM from $0200 to $02FF (256 Bytes)
* Extract those data from "Super Mario Bros. (W) [!].nes" and save them as : mario_spt.oam
* Attach it to the source code
Code:
sprites:
.incbin "mario_spt.oam"
.incbin "mario_spt.oam"
* We load the data of sprites into $0200 to $02FF
Code:
;Sprites data to ram
LoadSprites:
LDX #$00
LoadSpritesLoop:
LDA sprites, X
STA $0200, X
INX
CPX #$00
BNE LoadSpritesLoop
LoadSprites:
LDX #$00
LoadSpritesLoop:
LDA sprites, X
STA $0200, X
INX
CPX #$00
BNE LoadSpritesLoop
* We set OAM memory address to $00 with $2003
Code:
LDA #$00
STA $2003
STA $2003
* We write $02 to $2014
Code:
LDA #$02
STA $4014
STA $4014
* We only write the High byte of the RAM address to $2014, the Low byte of the RAM is automatically adjusted to $00 and there is no need to declare it.
* With this method a total of 256 bytes (info of 64 sprites) are transferred from RAM ($0200 ~ $02FF) to OAM memory
* This method is called DMA. Need more info about it? Then read this : PPU OAM
* Inside the game engine loop we can change the info of sprites which are saved on the ram
Code:
;Give effect to sprites
INC spt6_Y
INC spt7_Y
INC spt8_Y
INC spt9_Y
INC spt6_Y
INC spt7_Y
INC spt8_Y
INC spt9_Y
* In this code we increase the Y coordinate of mario sprite, so it seems mario is going down constantly
/////////////////////////////////////////////////////////////////////////////////////////////////
Exercise :
Open mario_spt.oam with HxD and edit mario's data to become upside down.
/////////////////////////////////////////////////////////////////////////////////////////////////
Files :
asm6.exe
Assembler.bat
Game.asm
name.exe
Bg_Editor.bat
alleg42.dll
mario_bg.nam
mario_bg_2.nam
mario_bg.pal
mario_spt.pal
mario_spt.oam
mario_bg.chr
mario_spt.chr
/////////////////////////////////////////////////////////////////////////////////////////////////
Former Level : NES Programming Tutorial : Sprite
Next Level : NES Programming Tutorial : Using Joypad