Milestone 3.5, tiles and sprites both present on the screen. Also started breaking the code out into libraries for easier editing and maintenance.

This commit is contained in:
2012-11-06 23:30:20 -05:00
parent 38e8607572
commit d17de5971c
5 changed files with 296 additions and 104 deletions

2
README
View File

@@ -7,7 +7,7 @@ Milestone list:
1- (DONE) Boot a ROM that does nothing 1- (DONE) Boot a ROM that does nothing
2- (DONE) Single sprite visible on the screen 2- (DONE) Single sprite visible on the screen
3- (DONE) Complete complex (multi-part) sprite visible on the screen 3- (DONE) Complete complex (multi-part) sprite visible on the screen
3.5 - single background tile visible on the screen 3.5 - (DONE) single background tile visible on the screen
4- Swap palette of sprite in response to gamepad 4- Swap palette of sprite in response to gamepad
5- Move sprite on screen with no animation 5- Move sprite on screen with no animation
6- Display a full tile map on the screen 6- Display a full tile map on the screen

16
include/defines.S Normal file
View File

@@ -0,0 +1,16 @@
.alias curSpriteDataRead curSpriteDataLo
.alias curSpriteDataWrite curSpriteDataHi
.macro storeStackReturn ; storeStackReturn
PLA
STA prevReturnAddrHi
PLA
STA prevReturnAddrLo
.macend
.macro restoreStackReturn
LDA prevReturnAddrLo
PHA
LDA prevReturnAddrhi
PHA
.macend

140
include/graphics.S Normal file
View File

@@ -0,0 +1,140 @@
.require "defines.S"
;; Function : paletteLoad
;;
;; Given the address of a palette in ROM or RAM, and the
;; index of which palette to load (0=3F00, 1=3F10), load a
;; palette into the PPU
;;
;; arguments are on the stack (in push order):
;; - 00 or 10, lyte byte of PPU palette address to load
;; - Low byte of palette address
;; - High byte of palette address
paletteLoad:
.invoke storeStackReturn
PLA
STA curPaletteHi
PLA
STA curPaletteLo
LDA $2002 ; The PPU Memory address at $2006 expects
; the high byte of the palette address first,
; then the low byte, but we can't know
; which one it's expecting right now, so we
; read the PPU status at $2002 to reset the
; high/low latch on $2006.
LDA #$3F ; palettes live at $3F00 and $3F10
STA $2006
PLA
STA $2006
LDX #$00
_loop:
LDA (curPaletteLo), y ; Loop over each index of the byte array at
STA $2007 ; 'palette', store each one into the accumulator
INY ; and then store the accumulator into the PPU
CPY #$20 ; .. compare X to 20 (size of 'palette'), and
BNE _loop ; loop as long as the Zero flag isn't set (NE)
.invoke restoreStackReturn
RTS
;; Function : backgroundLoad
;;
;; Given the address of a set of background tiles, and the
;; address of their attribute data, load the background into the PPU
;;
;; arguments on the stack (in push order):
;; - Length of background attribute data
;; - Hi byte of background attribute data address
;; - Lo byte of background attribute data address
;; - Length of background data
;; - Hi byte of background data address
;; - Lo byte of background data address
backgroundLoad:
.invoke storeStackReturn
PLA
STA curBackgroundLo
PLA
STA curBackgroundHi
PLA
STA curBackgroundLen
PLA
STA curBackgroundAttrLo
PLA
STA curBackgroundAttrHi
PLA
STA curBackgroundAttrLen
LDA $2002 ; reset the PPU hi/low latch
LDA #$20
STA $2006 ; PPU address data is written high then low
LDA #$00
STA $2006
LDY #$00
_loadbgloop:
LDA (curBackgroundLo), y
STA $2007
INY
CPY curBackgroundLen
BNE _loadbgloop
LDA $2002 ; reset the PPU latch again
LDA #$23
STA $2006
LDA #$C0
STA $2006
LDY #$00
_loadattrloop:
LDA (curBackgroundAttrLo), y
STA $2007
INY
CPY curBackgroundAttrLen
BNE _loadattrloop
.invoke restoreStackReturn
RTS
;; Function : oamInsertMultiSprite
;;
;; Given the address of a multisprite, and its length (number of
;; subsprites), load the multisprite into the OAM memory at
;; index 0.
;;
;; Arguments on the stack:
;; - Length of multisprite
;; - High byte of multisprite's address
;; - Low byte of multisprite's address
;;
;; FIXME: Need to keep a list of all multisprites so I can append
;; new ones to the list, and remove dead ones; right now this all
;; presumes $0200 is the root for the multisprite, which will stop
;; being true once I have more than one.
oamInsertMultiSprite:
.invoke storeStackReturn
LDX #$0
LDY #$0
PLA
STA curSpriteLen
PLA
STA curSpriteDataHi
PLA
STA curSpriteDataLo
;; ----
_looptop:
LDA playery ; set Y position
CLC
ADC (curSpriteDataLo), y
STA (curSpriteOAMIndexLo), y
INY
LDA (curSpriteDataLo), y ; set tile number
STA (curSpriteOAMIndexLo), y
INY
LDA (curSpriteDataLo), y
STA (curSpriteOAMIndexLo), y
INY
LDA playerx ; set X position
CLC
ADC (curSpriteDataLo), y
STA (curSpriteOAMIndexLo), y
INY
INX ; increment the sprite counter
CPX curSpriteLen ; any more sprites in the current multisprite?
BNE _looptop
.invoke restoreStackReturn
RTS

28
include/math.S Normal file
View File

@@ -0,0 +1,28 @@
;; Function : divide
;;
;; Divide the value in A by the value in Y
;; The dividend is returned in A
;; The modulus is returned in Y
divide:
.invoke storeStackReturn
sta tempdivident ;Stores divident
sty tempdivisor ;Stores divisor
lda #$00
sta tempdivresult ;Clear result
ldy #$10 ;The loop is for 16-bit result
_divide_loop:
asl tempdivident
rol ;Shift divisor in 1 bit
cmp tempdivisor ;Check if fractional dividend is greater than divisor
bcc _divide_subloop
sbc tempdivisor ;Substract (C is always set)
_divide_subloop:
rol tempdivresult ;Shift result (1 if substation was done, 0 otherwise)
rol tempdivmodulus
dey
bne _divide_loop
lda tempdivresult
ldy tempdivmodulus
.invoke restoreStackReturn
RTS

210
nesgame.S
View File

@@ -13,7 +13,7 @@
.byte $00,$00,$00,$00 ; Reserved bytes .byte $00,$00,$00,$00 ; Reserved bytes
.byte $00,$00,$00,$00 ; Reserved bytes .byte $00,$00,$00,$00 ; Reserved bytes
.include "defines.S" .require "include/defines.S"
.text zp .text zp
.org $0000 .org $0000
@@ -25,12 +25,20 @@
.space prevReturnAddrHi 1 .space prevReturnAddrHi 1
.org $0010 .org $0010
;; $0010 - 001F is reserved for global pointers ;; $0010 - 001F is reserved for global pointers
.space curPaletteLo 1
.space curPaletteHi 1
.space curSpriteOAMIndexLo 1 .space curSpriteOAMIndexLo 1
.space curSpriteOAMIndexHi 1 .space curSpriteOAMIndexHi 1
.space curBackgroundLo 1
.space curBackgroundHi 1
.space curBackgroundAttrLo 1
.space curBackgroundAttrHi 1
.space curSpriteDataLo 1 .space curSpriteDataLo 1
.space curSpriteDataHi 1 .space curSpriteDataHi 1
.org $0020 .org $0020
;; $0020 - 00FF is space for general purpose global variables ;; $0020 - 00FF is space for general purpose global variable
.space curBackgroundLen 1
.space curBackgroundAttrLen 1
.space curSpriteLen 1 .space curSpriteLen 1
.space playery 1 .space playery 1
.space playerx 1 .space playerx 1
@@ -43,10 +51,20 @@
.space pad1left 1 .space pad1left 1
.space pad1right 1 .space pad1right 1
.space pad1areleased 1 .space pad1areleased 1
.space multiSpriteList 16 ; reserve 6 words for the list of multisprites
; currently on screen, each one listed as a
; WORD address
.space tempdivident 1
.space tempdivisor 1
.space tempdivresult 1
.space tempdivmodulus 1
.text .text
.org $C000 ;; PRG bank code starts at 0xC000 .org $C000 ;; PRG bank code starts at 0xC000
.require "include/math.S"
.require "include/graphics.S"
;; START will be called by the NES whenever the system boots ;; START will be called by the NES whenever the system boots
;; or when the reset button is pressed (think of _start in libc ) ;; or when the reset button is pressed (think of _start in libc )
;; but the fact that the NES looks at "START" is only because we ;; but the fact that the NES looks at "START" is only because we
@@ -113,51 +131,18 @@ _START_vblankwait2:
BPL _START_vblankwait2 ;; once we've gotten 1 vblank, BPL _START_vblankwait2 ;; once we've gotten 1 vblank,
;; cleared mem, and gotten another vblank, ;; cleared mem, and gotten another vblank,
;; the PPU is ready. Wait for it. ;; the PPU is ready. Wait for it.
JMP main
MAIN:
main:
;; horray, here is main() ;; horray, here is main()
_MAIN_LoadPalettes:
LDA $2002 ; The PPU Memory address at $2006 expects
; the high byte of the palette address first,
; then the low byte, but we can't know
; which one it's expecting right now, so we
; read the PPU status at $2002 to reset the
; high/low latch on $2006.
LDA #$3F ; we're populating the second palette, at $3F10
STA $2006
LDA #$00 LDA #$00
STA $2006 PHA
LDX #$00 LDA #<palette
_MAIN_LoadPaletteLoop: PHA
LDA palette, x ; Loop over each index of the byte array at LDA #>palette
STA $2007 ; 'palette', store each one into the accumulator PHA
INX ; and then store the accumulator into the PPU JSR paletteLoad
CPX #$20 ; .. compare X to 20 (size of 'palette'), and
BNE _MAIN_LoadPaletteLoop ; loop as long as the Zero flag isn't set (NE)
;; All sprites live between 0200-02FF; there are a max of 64 sprites
;; on screen, and each one has a 4 byte struct describing it.
;; *(sprite + 0) = y position
;; *(sprite + 1) = tile index (0-FF) in the pattern table for pixels
;; *(sprite + 2) = attributes. Color palette, priority, and mirroring.
;; 76543210
;; ||| ||
;; ||| ++- Color Palette of sprite. Choose which set of 4 from
;; ||| the 16 colors to use. You can select sprite colors only
;; ||| in groups of 4 on 4 byte boundaries; so you can select
;; ||| colors 0-3, 4-7, 8-11, and 12-15, but not 2-5, for
;; ||| example. Palette construction and use is an art in
;; ||| itself!
;; |||
;; ||+------ Priority (0: in front of background; 1: behind background)
;; |+------- Flip sprite horizontally
;; +-------- Flip sprite vertically
;; *(sprite + 3) = x position
;; All the sprite OAM data is initialized at the bottom of bank 1
;; at .org $FF00
;; $2000 is the PPU Control register, controlled by various bitflags. ;; $2000 is the PPU Control register, controlled by various bitflags.
;; ;;
;; 7654 3210 ;; 7654 3210
@@ -175,9 +160,6 @@ _MAIN_LoadPaletteLoop:
;; +--------- Generate an NMI at the start of the ;; +--------- Generate an NMI at the start of the
;; vertical blanking interval (0: off; 1: on) ;; vertical blanking interval (0: off; 1: on)
LDA #%10000000 ; enable NMI (so we get a function call every
; vblank), and draw sprites from table 0
STA $2000
;; The PPU mask is set at $2001, the 2nd PPU Control register, and it ;; The PPU mask is set at $2001, the 2nd PPU Control register, and it
;; sets one config option for every bit of the byte ;; sets one config option for every bit of the byte
@@ -195,9 +177,6 @@ _MAIN_LoadPaletteLoop:
;; |+------- Intensify greens (and darken other colors) ;; |+------- Intensify greens (and darken other colors)
;; +-------- Intensify blues (and darken other colors) ;; +-------- Intensify blues (and darken other colors)
LDA #%00010000 ;; turn on sprites, no more background color
STA $2001 ;; Write to PPU Control Register 2
LDX #$0
LDA #$80 LDA #$80
STA playerx STA playerx
@@ -232,59 +211,36 @@ _MAIN_LoadPaletteLoop:
STA curSpriteOAMIndexHi STA curSpriteOAMIndexHi
JSR oamInsertMultiSprite JSR oamInsertMultiSprite
;; --------------------------- ;; ---------------------------
_bgcall:
LDA bgattrsize
PHA
LDA #>backgroundattrs
PHA
LDA #<backgroundattrs
PHA
LDA bgsize
PHA
LDA #>background
PHA
LDA #<background
PHA
JSR backgroundLoad
;; Apparently you do NOT set sprite and tile data while these are turned on, bad things
;; will happen!
LDA #%10010000 ; enable NMI (so we get a function call every
; vblank), and draw sprites from table 0
STA $2000
LDA #%10011110 ;; turn on sprites, turn on background, don't clip on the left
STA $2001 ;; Write to PPU Control Register 2
LDA #$00 ; hey PPU, stop friggin scrolling!
STA $2005
STA $2005
LDX #$0
_MAIN_loop: _MAIN_loop:
JMP _MAIN_loop ;; Loop forever JMP _MAIN_loop ;; Loop forever
;; Function : oamInsertMultiSprite
;;
;; Given the address of a multisprite, and its length (number of
;; subsprites), load the multisprite into the OAM memory at
;; index 0.
;;
;; Arguments on the stack:
;; - Length of multisprite
;; - High byte of multisprite's address
;; - Low byte of multisprite's address
;;
;; FIXME: Need to keep a list of all multisprites so I can append
;; new ones to the list, and remove dead ones; right now this all
;; presumes $0200 is the root for the multisprite, which will stop
;; being true once I have more than one.
oamInsertMultiSprite:
.invoke storeStackReturn
LDX #$0
LDY #$0
PLA
STA curSpriteLen
PLA
STA curSpriteDataHi
PLA
STA curSpriteDataLo
;; ----
_looptop:
LDA playery ; set Y position
CLC
ADC (curSpriteDataLo), y
STA (curSpriteOAMIndexLo), y
INY
LDA (curSpriteDataLo), y ; set tile number
STA (curSpriteOAMIndexLo), y
INY
LDA (curSpriteDataLo), y
STA (curSpriteOAMIndexLo), y
INY
LDA playerx ; set X position
CLC
ADC (curSpriteDataLo), y
STA (curSpriteOAMIndexLo), y
INY
INX ; increment the sprite counter
CPX curSpriteLen ; any more sprites in the current multisprite?
BNE _looptop
.invoke restoreStackReturn
RTS
NMI: NMI:
;; We need to copy all our OAM data to put sprites on screen during ;; We need to copy all our OAM data to put sprites on screen during
;; vblank. $2003 is the PPU OAM address, so we're going to tell it ;; vblank. $2003 is the PPU OAM address, so we're going to tell it
@@ -298,11 +254,63 @@ NMI:
; which takes ~513 cycles. An unrolled ; which takes ~513 cycles. An unrolled
; loop to do the same thing would take ; loop to do the same thing would take
; 3-4 times as long. ; 3-4 times as long.
LDA #%10010000 ; enable NMI (so we get a function call every
; vblank), and draw sprites from table 0
STA $2000
LDA #%00011110 ;; turn on sprites, turn on background, don't clip on the left
STA $2001 ;; Write to PPU Control Register 2
LDA #$00 ; hey PPU, stop friggin scrolling!
STA $2005
STA $2005
RTI ; just return RTI ; just return
palette: palette:
.byte $0F,$31,$32,$33,$0F,$35,$36,$37,$0F,$39,$3A,$3B,$0F,$3D,$3E,$0F .byte $22,$29,$1A,$0F,$22,$36,$17,$0F,$22,$30,$21,$0F,$22,$27,$17,$0F ;;background palette
.byte $0F,$1C,$15,$14,$0F,$02,$38,$3C,$0F,$1C,$15,$14,$0F,$02,$38,$3C .byte $22,$1C,$15,$14,$22,$02,$38,$3C,$22,$1C,$15,$14,$22,$02,$38,$3C ;;sprite palette
bgsize:
.byte $80
bgattrsize:
.byte $08
background:
.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24 ;;row 1
.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24 ;;all sky
.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24 ;;row 2
.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24 ;;all sky
.byte $24,$24,$24,$24,$45,$45,$24,$24,$45,$45,$45,$45,$45,$45,$24,$24 ;;row 3
.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$53,$54,$24,$24 ;;some brick tops
.byte $24,$24,$24,$24,$47,$47,$24,$24,$47,$47,$47,$47,$47,$47,$24,$24 ;;row 4
.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$55,$56,$24,$24 ;;brick bottoms
backgroundattrs:
.byte %00000000,%00010000,%01010000,%00010000
.byte %00000000,%00000000,%00000000,%00110000
.byte $24,$24,$24,$24
.byte $47,$47,$24,$24
.byte $47,$47,$47,$47
.byte $47,$47,$24,$24
.byte $24,$24,$24,$24
.byte $24,$24,$24,$24
.byte $24,$24,$24,$24
.byte $55,$56,$24,$24 ;;brick bottoms
;; All sprites live between 0200-02FF; there are a max of 64 sprites
;; on screen, and each one has a 4 byte struct describing it.
;; *(sprite + 0) = y position
;; *(sprite + 1) = tile index (0-FF) in the pattern table for pixels
;; *(sprite + 2) = attributes. Color palette, priority, and mirroring.
;; 76543210
;; ||| ||
;; ||| ++- Color Palette of sprite. Choose which set of 4 from
;; ||| the 16 colors to use. You can select sprite colors only
;; ||| in groups of 4 on 4 byte boundaries; so you can select
;; ||| colors 0-3, 4-7, 8-11, and 12-15, but not 2-5, for
;; ||| example. Palette construction and use is an art in
;; ||| itself!
;; |||
;; ||+------ Priority (0: in front of background; 1: behind background)
;; |+------- Flip sprite horizontally
;; +-------- Flip sprite vertically
;; *(sprite + 3) = x position
sprMario: sprMario:
.byte $08 ; Total number of subsprites in this metasprite .byte $08 ; Total number of subsprites in this metasprite
sprMarioData: sprMarioData: