From d17de5971c101324921d5be14f91da82b8858399 Mon Sep 17 00:00:00 2001 From: Andrew Kesterson Date: Tue, 6 Nov 2012 23:30:20 -0500 Subject: [PATCH] Milestone 3.5, tiles and sprites both present on the screen. Also started breaking the code out into libraries for easier editing and maintenance. --- README | 2 +- include/defines.S | 16 ++++ include/graphics.S | 140 +++++++++++++++++++++++++++++ include/math.S | 28 ++++++ nesgame.S | 214 +++++++++++++++++++++++---------------------- 5 files changed, 296 insertions(+), 104 deletions(-) create mode 100644 include/defines.S create mode 100644 include/graphics.S create mode 100644 include/math.S diff --git a/README b/README index f016f25..e8a2854 100644 --- a/README +++ b/README @@ -7,7 +7,7 @@ Milestone list: 1- (DONE) Boot a ROM that does nothing 2- (DONE) Single 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 5- Move sprite on screen with no animation 6- Display a full tile map on the screen diff --git a/include/defines.S b/include/defines.S new file mode 100644 index 0000000..145fee7 --- /dev/null +++ b/include/defines.S @@ -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 \ No newline at end of file diff --git a/include/graphics.S b/include/graphics.S new file mode 100644 index 0000000..47c56b0 --- /dev/null +++ b/include/graphics.S @@ -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 diff --git a/include/math.S b/include/math.S new file mode 100644 index 0000000..852e720 --- /dev/null +++ b/include/math.S @@ -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 diff --git a/nesgame.S b/nesgame.S index c853fd8..17fedb3 100644 --- a/nesgame.S +++ b/nesgame.S @@ -13,7 +13,7 @@ .byte $00,$00,$00,$00 ; Reserved bytes .byte $00,$00,$00,$00 ; Reserved bytes - .include "defines.S" + .require "include/defines.S" .text zp .org $0000 @@ -25,12 +25,20 @@ .space prevReturnAddrHi 1 .org $0010 ;; $0010 - 001F is reserved for global pointers + .space curPaletteLo 1 + .space curPaletteHi 1 .space curSpriteOAMIndexLo 1 .space curSpriteOAMIndexHi 1 + .space curBackgroundLo 1 + .space curBackgroundHi 1 + .space curBackgroundAttrLo 1 + .space curBackgroundAttrHi 1 .space curSpriteDataLo 1 .space curSpriteDataHi 1 .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 playery 1 .space playerx 1 @@ -43,10 +51,20 @@ .space pad1left 1 .space pad1right 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 .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 ;; 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 @@ -113,51 +131,18 @@ _START_vblankwait2: BPL _START_vblankwait2 ;; once we've gotten 1 vblank, ;; cleared mem, and gotten another vblank, ;; the PPU is ready. Wait for it. - -MAIN: + JMP main + + +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 - STA $2006 - LDX #$00 -_MAIN_LoadPaletteLoop: - LDA palette, x ; Loop over each index of the byte array at - STA $2007 ; 'palette', store each one into the accumulator - INX ; and then store the accumulator into the PPU - 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 - + PHA + LDA #palette + PHA + JSR paletteLoad ;; $2000 is the PPU Control register, controlled by various bitflags. ;; ;; 7654 3210 @@ -175,9 +160,6 @@ _MAIN_LoadPaletteLoop: ;; +--------- Generate an NMI at the start of the ;; 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 ;; sets one config option for every bit of the byte @@ -195,10 +177,7 @@ _MAIN_LoadPaletteLoop: ;; |+------- Intensify greens (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 STA playerx STA playery @@ -232,59 +211,36 @@ _MAIN_LoadPaletteLoop: STA curSpriteOAMIndexHi JSR oamInsertMultiSprite ;; --------------------------- +_bgcall: + LDA bgattrsize + PHA + LDA #>backgroundattrs + PHA + LDA #background + PHA + LDA #