Code:
;NES Programming Tutorial
;Level 7 : Sprite
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Constants
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Variables
L_byte = $0000
H_byte = $0001
bg_X_pos = $0002
bg_Y_pos = $0003
NMI_index = $0004
sprite_Y = $0200
sprite_tile = $0201
sprite_att = $0202
sprite_X = $0203
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;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 #$04
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 sprite
INC sprite_Y
;Sprites data to OAM
JSR vblank_wait
LDA #$00
STA $2003
LDA sprite_Y
STA $2004
LDA sprite_tile
STA $2004
LDA sprite_att
STA $2004
LDA sprite_X
STA $2004
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:
.db $80,$65,%00000000,$80
;---------------------------;
.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 7 : Sprite
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Constants
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Variables
L_byte = $0000
H_byte = $0001
bg_X_pos = $0002
bg_Y_pos = $0003
NMI_index = $0004
sprite_Y = $0200
sprite_tile = $0201
sprite_att = $0202
sprite_X = $0203
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;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 #$04
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 sprite
INC sprite_Y
;Sprites data to OAM
JSR vblank_wait
LDA #$00
STA $2003
LDA sprite_Y
STA $2004
LDA sprite_tile
STA $2004
LDA sprite_att
STA $2004
LDA sprite_X
STA $2004
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:
.db $80,$65,%00000000,$80
;---------------------------;
.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 :
* Enable NMI, use left pattern table for background, use right pattern table for sprites
Code:
LDA #%10001000
STA $2000
STA $2000
* Show background, show sprites
Code:
LDA #%00011010
STA $2001
STA $2001
* The data of sprites tiles is stored in the PPU memory from $0000 to $0FFF (4KB)
* The data of sprites color is stored in the PPU memory from $3F10 to $3F1F (16Bytes)
* Extract those data from "Super Mario Bros. (W) [!].nes" and save them as : mario_spt.chr, mario_spt.pal
* Attach them to the source code
Code:
spt_pal:
.incbin "mario_spt.pal"
.incbin "mario_spt.pal"
Code:
.incbin "mario_spt.chr"
* The codes of ;Sprites color setup actually transfers a total of 16 bytes (= 10 bytes in hex) data of sprites color to PPU memory ($3F10 ~ $3F1F)
* Each tile of sprite need 4 byte-info to be able to appear on the screen
* Y coordinate of the sprite on the screen
* One byte for tile number from sprites pattern table
* One byte for the attribute of the sprite, this byte is bit wise
* X coordinate of the sprite on the screen
Code:
sprites:
.db $80,$65,%00000000,$80
.db $80,$65,%00000000,$80
* With this code we take those 4 bytes and save them into ram
Code:
;Sprites data to ram
LoadSprites:
LDX #$00
LoadSpritesLoop:
LDA sprites, X
STA $0200, X
INX
CPX #$04
BNE LoadSpritesLoop
LoadSprites:
LDX #$00
LoadSpritesLoop:
LDA sprites, X
STA $0200, X
INX
CPX #$04
BNE LoadSpritesLoop
* OAM is a special memory for holding sprites info, it has its own memory space starting from $00 to $FF (256bytes)
* To show a sprite on the screen you have to write its 4byte-info to OAM memory
* Every sprite needs 4 bytes of info, right? So OAM can hold up to 64 sprites at the same time
* We can't write directly to OAM memory, we have to use ports : $2003 and $2004
* With $2003 we declare what address of OAM want to write, then we use $2004 to write the desired value to that address
* Each time we write a value to $2004, the OAM address is automatically adjusted to the next address, so we don't need to declare the OAM address again
Code:
;Give effect to sprite
INC sprite_Y
;Sprites data to OAM
JSR vblank_wait
LDA #$00
STA $2003
LDA sprite_Y
STA $2004
LDA sprite_tile
STA $2004
LDA sprite_att
STA $2004
LDA sprite_X
STA $2004
INC sprite_Y
;Sprites data to OAM
JSR vblank_wait
LDA #$00
STA $2003
LDA sprite_Y
STA $2004
LDA sprite_tile
STA $2004
LDA sprite_att
STA $2004
LDA sprite_X
STA $2004
* In the game engine we increase the Y coordinate of the sprite and it seems that sprite is falling
* Need more info about sprites and OAM memory? then read this : PPU OAM
/////////////////////////////////////////////////////////////////////////////////////////////////
Exercise :
Make another sprite on the screen.
/////////////////////////////////////////////////////////////////////////////////////////////////
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_bg.chr
mario_spt.chr
/////////////////////////////////////////////////////////////////////////////////////////////////
Former Level : NES Programming Tutorial : Move Background
Next Level : NES Programming Tutorial : Multiple Sprites