Add a "ship" and let the player move around.

This includes some tile / palette data in a separate file and some code
for DMA'ing that data into VRAM. This is used to render a "ship" (white
circle) that the player can move around with the D-Pad.

Added a whole bunch of named registers (& documentation for them) to
registers.asm.
This commit is contained in:
Colin McMillen 2015-05-23 14:00:43 -04:00
parent 1925869fb6
commit 2da42f1eb1
5 changed files with 385 additions and 44 deletions

View File

@ -2,7 +2,7 @@
;- Standard SNES initialization routine, originally by Neviksti ;- Standard SNES initialization routine, originally by Neviksti
;------------------------------------------------------------------------ ;------------------------------------------------------------------------
.MACRO InitSNES .MACRO InitializeSNES
sei ;disable interrupts sei ;disable interrupts
clc ;switch to native mode clc ;switch to native mode
xce xce

View File

@ -12,7 +12,7 @@
.SNESHEADER .SNESHEADER
ID "SNES" ID "SNES"
NAME "PEW PEW " ; Program title. Should be 21 bytes long; NAME "PEW! PEW! " ; Program title. Should be 21 bytes long;
; "123456789012345678901" ; use spaces for unused bytes of the name. ; "123456789012345678901" ; use spaces for unused bytes of the name.
SLOWROM SLOWROM

View File

@ -2,6 +2,34 @@
.INCLUDE "InitSNES.asm" .INCLUDE "InitSNES.asm"
.INCLUDE "registers.asm" .INCLUDE "registers.asm"
; The JSR and RTS instructions add a total of 12 cycles of overhead. For
; short, commonly-used functions, it makes sense to declare them as macros,
; which get inlined by the assembler at the point of use. This saves on
; CPU cycles, at the cost of code size.
.MACRO ConvertXCoordinate
; Data in: world x-coordinate, in A register.
; Data out: SNES scroll data, in C (the 16-bit A register).
rep #%00100000 ; 16-bit A
eor #$FFFF ; Flip bits
ina
sep #%00100000 ; 8-bit A
.ENDM
.MACRO ConvertYCoordinate
; Data in: world y-coordinate, in A register.
; Data out: SNES scroll data, in C (the 16-bit A register).
rep #%00100000 ; 16-bit A
eor #$FFFF ; Flip bits
sep #%00100000 ; 8-bit A
.ENDM
.BANK 0 SLOT 0 .BANK 0 SLOT 0
.ORG 0 .ORG 0
.SECTION "MainCode" .SECTION "MainCode"
@ -9,13 +37,17 @@
; Memory layout: ; Memory layout:
; 00-0F: scratch space for functions. ; 00-0F: scratch space for functions.
; 10-13: controller state. ; 10-11: controller state of joypad #1.
; 12-13: controller state of joypad #2.
; 14-17: 32-bit counter of vblanks. ; 14-17: 32-bit counter of vblanks.
; 20-22: RGB color values to use for background color, from [0-31]. ; 20-21: (x, y) coordinates of player.
; 22-24: RGB color values to use for background color, from [0-31].
Start: Start:
InitSNES ; Initialize SNES. InitializeSNES
jsr LoadPaletteAndTileData
; Turn on the screen. ; Turn on the screen.
; Format: x000bbbb ; Format: x000bbbb
@ -26,7 +58,7 @@ Start:
; Enable NMI interrupt & joypad. ; Enable NMI interrupt & joypad.
; n-vh---j n: NMI interrupt enable v: vertical counter enable ; n-vh---j n: NMI interrupt enable v: vertical counter enable
; h: horizontal counter enable j: joypad enable ; h: horizontal counter enable j: joypad enable
lda #$81 lda #%10000001
sta NMITIMEN sta NMITIMEN
; Store zeroes to the controller status registers. ; Store zeroes to the controller status registers.
@ -38,6 +70,10 @@ Start:
; Write something recognizable into our scratch space. ; Write something recognizable into our scratch space.
jsr FillScratch jsr FillScratch
; Start the background color as a dark blue.
lda #16
sta $24
MainLoop: MainLoop:
@ -46,11 +82,77 @@ MainLoop:
LoadPaletteAndTileData:
; 16-bit X/Y registers. Used for DMA source address & transfer size, both of
; which want 16-bit values.
; TODO(mcmillen): change back to 8-bit when we're done?
rep #%00010000
; 8-bit A/B registers. Used for DMA source bank & destination address.
sep #%00100000
; We only need one palette entry, so we just initialize it manually.
; We could also do this with a DMA transfer (like we do with the tile data
; below), but it seems overkill for just one entry :)
lda #34 ; Set the 34th palette entry.
sta CGADDR
lda.l PaletteData
sta CGDATA
lda.l PaletteData + 1
sta CGDATA
; DMA 0 source address & bank.
ldx #TileData
stx DMA0SRC
lda #:TileData
sta DMA0SRCBANK
; DMA 0 transfer size.
ldy #(15 * 16 *2) ; Also see the helpful "480 bytes" comment in tiles.asm.
sty DMA0SIZE
; DMA 0 control register.
; Transfer type 001 = 2 addresses, LH.
lda #%00000001
sta DMA0CTRL
; DMA 0 destination.
lda #$18 ; Upper-byte is assumed to be $21, so this is $2118 & $2119.
sta DMA0DST
; $2116 sets the word address for accessing VRAM.
ldy #$0000
sty VMADDR
; Enable DMA channel 0.
lda #%00000001
sta DMAENABLE
; VRAM writing mode. Increments the address every time we write to $2119.
lda #%10000000
sta VMAIN
; Set word address for accessing VRAM to $6000.
ldx #$6000 ; BG 2 starts here.
stx VMADDR
ldx #$000A ; Stick one tile into BG2.
stx VMDATA
; Set up the screen. 16x16 tiles for BG2, 8x8 tiles elsewhere, mode 0.
lda #%00100000
sta BGMODE
; $2108 is the BG2 VRAM location register.
; This tells it that the BG2 data starts at $6000.
lda #%01100000
sta BG2SC
stz BG12NBA
; Main screen: enable BG2.
lda #%00000010
sta MSENABLE
rts
VBlankHandler: VBlankHandler:
jsr VBlankCounter ; DEBUG jsr VBlankCounter ; DEBUG
jsr JoypadHandler jsr JoypadHandler
jsr SetBackgroundColor jsr SetBackgroundColor
; jsr FillScratch ; DEBUG jsr SetPlayerPosition
rti rti
@ -76,72 +178,113 @@ VBlankCounterDone:
JoypadHandler: JoypadHandler:
; $4218: Joypad #1 status [JOY1L]
; Format: AXLR0000
; $4219: Joypad #1 status [JOY1H]
; Format: BYsSudlr (s=select, S=start, udlr = joypad)
jsr JoypadDebug ; DEBUG jsr JoypadDebug ; DEBUG
; TODO(mcmillen): read joypad from local memory instead of registers?
JoypadUp: JoypadUp:
lda JOY1H lda JOY1H
and #$08 ; Up and #$08 ; Up
cmp #$08 cmp #$08
bne JoypadDown ; Button not pressed. bne JoypadDown ; Button not pressed.
lda $20 lda $21
cmp #31 cmp #0
beq JoypadDown ; Value saturated. beq JoypadDown ; Value saturated.
inc $20 dec $21
dec $21
JoypadDown: JoypadDown:
lda JOY1H lda JOY1H
and #$04 and #$04
cmp #$04 cmp #$04
bne JoypadLeft ; Button not pressed. bne JoypadLeft ; Button not pressed.
lda $20 lda $21
cmp #0 cmp #(224 - 16)
beq JoypadLeft ; Value saturated. beq JoypadLeft ; Value saturated.
dec $20 inc $21
inc $21
JoypadLeft: JoypadLeft:
lda JOY1H lda JOY1H
and #$02 ; Left and #$02 ; Left
cmp #$02 cmp #$02
bne JoypadRight ; Button not pressed. bne JoypadRight ; Button not pressed.
lda $22 lda $20
cmp #0 cmp #0
beq JoypadRight ; Value saturated. beq JoypadRight ; Value saturated.
dec $22 dec $20
dec $20
JoypadRight: JoypadRight:
lda JOY1H lda JOY1H
and #$01 and #$01
cmp #$01 ; Right cmp #$01 ; Right
bne JoypadB ; Button not pressed. bne JoypadB ; Button not pressed.
lda $22 lda $20
cmp #31 cmp #(256 - 16)
beq JoypadB ; Value saturated. beq JoypadB ; Value saturated.
inc $22 inc $20
inc $20
JoypadB: JoypadB:
lda JOY1H lda JOY1H
and #$80 ; B and #$80 ; B
cmp #$80 cmp #$80
bne JoypadA ; Button not pressed.
lda $22
cmp #0
beq JoypadA ; Value saturated.
dec $22
JoypadA:
lda JOY1L
and #$80 ; A
cmp #$80
bne JoypadY ; Button not pressed. bne JoypadY ; Button not pressed.
lda $21 lda $22
cmp #31 cmp #31
beq JoypadY ; Value saturated. beq JoypadY ; Value saturated.
inc $21 inc $22
JoypadY: JoypadY:
lda JOY1H lda JOY1H
and #$40 ; Y and #$40 ; Y
cmp #$40 cmp #$40
bne JoypadDone ; Button not pressed. bne JoypadX ; Button not pressed.
lda $21 lda $23
cmp #0 cmp #0
beq JoypadX ; Value saturated.
dec $23
JoypadX:
lda JOY1L
and #$40 ; X
cmp #$40
bne JoypadL ; Button not pressed.
lda $23
cmp #31
beq JoypadL ; Value saturated.
inc $23
JoypadL:
lda JOY1L
and #$20 ; L
cmp #$20
bne JoypadR ; Button not pressed.
lda $24
cmp #0
beq JoypadR ; Value saturated.
dec $24
JoypadR:
lda JOY1L
and #$10 ; R
cmp #$10
bne JoypadDone ; Button not pressed.
lda $24
cmp #31
beq JoypadDone ; Value saturated. beq JoypadDone ; Value saturated.
dec $21 inc $24
; TODO(mcmillen): have Start and Select do something too.
JoypadDone: JoypadDone:
rts rts
@ -163,34 +306,57 @@ JoypadDebug:
SetBackgroundColor: SetBackgroundColor:
; $20 $21 $22 are (R, G, B), each ranging from [0-31]. ; $22 $23 $24 are (R, G, B), each ranging from [0-31].
; The palette color format is 15-bit: [0bbbbbgg][gggrrrrr] ; The palette color format is 15-bit: [0bbbbbgg][gggrrrrr]
; Set the background color.
; Entry 0 corresponds to the SNES background color.
stz CGADDR
; Compute and the low-order byte and store it in CGDATA. ; Compute and the low-order byte and store it in CGDATA.
lda $21 ; Green. lda $23 ; Green.
.rept 5 .rept 5
asl asl
.endr .endr
ora $20 ; Red. ora $22 ; Red.
sta CGDATA sta CGDATA
; Compute the high-order byte and store it in CGDATA. ; Compute the high-order byte and store it in CGDATA.
lda $22 ; Blue. lda $24 ; Blue.
.rept 2 .rept 2
asl asl
.endr .endr
sta $00 sta $00
lda $21 ; Green. lda $23 ; Green.
.rept 3 .rept 3
lsr lsr
.endr .endr
ora $00 ora $00
sta CGDATA sta CGDATA
rts
; Set the background color.
; $2121 is the color palette selection register [CGADD].
; Entry 0 corresponds to the SNES background color. SetPlayerPosition:
stz CGADD ; Make sure the high byte of A is zeroed out.
lda #$00
xba
; Get player x and convert it to a scroll offset.
lda $0020
ConvertXCoordinate
sta BG2HOFS
xba
sta BG2HOFS
; Make sure the high byte of A is zeroed out.
lda #$00
xba
; Get player y and convert it to a scroll offset.
lda $0021
ConvertYCoordinate
sta BG2VOFS
xba
sta BG2VOFS
rts rts
@ -206,3 +372,11 @@ FillScratchLoop:
rts rts
.ENDS .ENDS
.BANK 1 SLOT 0
.ORG 0
.SECTION "TileData"
.INCLUDE "tiles.asm"
.ENDS

View File

@ -5,17 +5,98 @@
; the same sense as PC, A, X, Y, and so on. Despite that, I call them ; the same sense as PC, A, X, Y, and so on. Despite that, I call them
; "registers" too, since that's what everyone else calls them. ; "registers" too, since that's what everyone else calls them.
; ;
; Where possible, I have named these register definitions in the same way that ; I've often named these register definitions in the same way that they're
; they're named in Yoshi's venerable snes.txt document. ; named in Yoshi's venerable snes.txt document. In some cases (where the
; mnemonic is too obscure) I've invented a different name. In particular,
; I've changed "ADD" to "ADDR" to reduce possible confusion between "addresses"
; and "addition". The original name from Yoshi's doc is still listed in
; brackets, like [CGADD], for easy cross-referencing.
;
; I've also heavily borrowed from Yoshi's descriptions of what these registers
; do, though in many cases I've clarified / simplified the descriptions based
; on my own understanding, or simply reformatted them a bit.
; $2100: Screen display register [INIDISP] ; $2100: Screen display initialization [INIDISP]
; Format: x000bbbb ; Format: x000bbbb
; x: 0 = screen on, 1 = screen off, bbbb: Brightness ($0-$F) ; x: 0 = screen on, 1 = screen off, bbbb: Brightness ($0-$F)
.define INIDISP $2100 .define INIDISP $2100
; $2105: Screen mode register [BGMODE]
; abcdefff a: BG4 tile size (0=8x8, 1=16x16).
; b: BG3 tile size (0=8x8, 1=16x16).
; c: BG2 tile size (0=8x8, 1=16x16).
; d: BG1 tile size (0=8x8, 1=16x16).
; e: Highest priority for BG3 in MODE 1.
; f: MODE definition.
.define BGMODE $2105
; $2107-210A: BG1-4 VRAM location registers [BGxSC]
; xxxxxxab x: Base address
; ab: SC size
.define BG1SC $2107
.define BG2SC $2108
.define BG3SC $2109
.define BG4SC $210A
; $210B: BG1 & BG2 VRAM location register [BG12NBA]
; $210C: BG3 & BG4 VRAM location register [BG34NBA]
; aaaabbbb a: Base address for BG2 (or BG4).
; b: Base address for BG1 (or BG3).
.define BG12NBA $210B
.define BG34NBA $210C
; BG1 horizontal scroll offset. [BG1HOFS]
; BG1 vertical scroll offset. [BG1VOFS]
; ... and similar registers for BG2-4.
; Write to all of these twice, as they want 2 bytes of data.
; mmmmmaaa aaaaaaaa a: Horizontal offset.
; m: Only set with MODE 7.
.define BG1HOFS $210D
.define BG1VOFS $210E
.define BG2HOFS $210F
.define BG2VOFS $2110
.define BG3HOFS $2111
.define BG3VOFS $2112
.define BG4HOFS $2113
.define BG4VOFS $2114
; $2115: Video port control [VMAIN]
; i000abcd i: 0 = Address increment after writing to $2118 or reading
; from $2139.
; 1 = Address increment after writing to $2119 or reading
; from $213A.
; ab: Full graphic (see table below).
; cd: SC increment (see table below).
;
; abcd Result
; 0100 Increment by 8 for 32 times (2-bit formation).
; 1000 Increment by 8 for 64 times (4-bit formation).
; 1100 Increment by 8 for 128 times (8-bit formation).
; 0000 Address increments 1x1.
; 0001 Address increments 32x32.
; 0010 Address increments 64x64.
; 0011 Address increments 128x128.
.define VMAIN $2115
; $2116-$2117: Video port address. 2 bytes. [VMADDL/VMADDH]
.define VMADDR $2116
; $2118-$2119: Video port data. 2 bytes. [VMDATAL/VMDATAH]
; According to bit 7 of VMAIN, the data can be stored as:
; Bit 7
; 0 Write to $2118 only. Lower 8-bits written then
; address is increased.
; 0 Write to $2119 then $2118. Address increased when both
; are written to (in order).
; 1 Write to $2119 only. Upper 8-bits written, then
; address is increased.
; 1 Write to $2118 then $2119. Address increased when both
; are written to (in order).
.define VMDATA $2118
; $2121: Color palette selection register [CGADD] ; $2121: Color palette selection register [CGADD]
; Entry 0 corresponds to the SNES background color. ; Entry 0 corresponds to the SNES background color.
.define CGADD $2121 .define CGADDR $2121
; $2122: Color data register [CGDATA] ; $2122: Color data register [CGDATA]
; The palette color format is 15-bit: [0bbbbbgg][gggrrrrr]. ; The palette color format is 15-bit: [0bbbbbgg][gggrrrrr].
@ -24,11 +105,23 @@
; (containing blue and green). ; (containing blue and green).
.define CGDATA $2122 .define CGDATA $2122
; $212C: Main screen designation [TM]
; 000abcde a: OBJ/OAM disable/enable.
; b: Disable/enable BG4.
; c: Disable/enable BG3.
; d: Disable/enable BG2.
; e: Disable/enable BG1.
.define MSENABLE $212C
; $4200: Counter enable [NMITIMEN] ; $4200: Counter enable [NMITIMEN]
; n-vh---j n: NMI interrupt enable v: vertical counter enable ; n-vh---j n: NMI interrupt enable v: vertical counter enable
; h: horizontal counter enable j: joypad enable ; h: horizontal counter enable j: joypad enable
.define NMITIMEN $4200 .define NMITIMEN $4200
; $420B: DMA enable [MDMAEN]
; Each bit that's set enables one channel: 76543210
.define DMAENABLE $420B
; $4218: Joypad #1 status [JOY1L] ; $4218: Joypad #1 status [JOY1L]
; Format: AXLR0000 ; Format: AXLR0000
.define JOY1L $4218 .define JOY1L $4218
@ -44,3 +137,38 @@
; $421B: Joypad #2 status [JOY2H] ; $421B: Joypad #2 status [JOY2H]
; Format: BYsSudlr (s=select, S=start, udlr = joypad) ; Format: BYsSudlr (s=select, S=start, udlr = joypad)
.define JOY2H $421B .define JOY2H $421B
; $43x0: DMA control for channel x. [DMAPX]
; vh0cbaaa v: 0 = CPU memory -> PPU.
; 1 = PPU -> CPU memory.
; h: For HDMA only:
; 0 = Absolute addressing.
; 1 = Indirect addressing.
; c: 0 = Auto address inc/decrement.
; 1 = Fixed address (for VRAM, etc.).
; b: 0 = Automatic increment.
; 1 = Automatic decrement.
; a: Transfer type:
; 000 = 1 address write twice: LH.
; 001 = 2 addresses: LH.
; 010 = 1 address write once.
; 011 = 2 addresses write twice: LLHH
; 100 = 4 addresses: LHLH
.define DMA0CTRL $4300
; $43x1: DMA destination for channel x. [BBADX]
; The upper byte is assumed to be $21, so the possible destinations are
; $2100-$21FF.
.define DMA0DST $4301
; $43x2-$43x3: DMA source address for channel x. 2 bytes. [AITXL/AITXH]
.define DMA0SRC $4302
; $43x4: DMA source bank for channel x [AIBX]
.define DMA0SRCBANK $4304
; $43x5: DMA transfer size & HDMA address. 2 bytes. [DASXL/DASXH]
; When using DMA, $43x5 defines the # of bytes to be transferred via DMA
; itself. When using HDMA, $43x5 defines the data address ($43x5 = low byte,
; $43x6 = hi byte).
.define DMA0SIZE $4305

39
tiles.asm Normal file
View File

@ -0,0 +1,39 @@
; Created with eKid's pcx2snes converter ;
TileData:
.db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
.db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
.db $03, $03, $03, $03, $03, $03, $03, $03, $03, $03, $03, $03, $03, $03, $03, $03
.db $C0, $C0, $C0, $C0, $C0, $C0, $C0, $C0, $C0, $C0, $C0, $C0, $C0, $C0, $C0, $C0
.db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $FF, $FF, $FF, $FF
.db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $FF, $FF, $FF, $FF
.db $03, $03, $03, $03, $03, $03, $03, $03, $03, $03, $03, $03, $FF, $FF, $FF, $FF
.db $C0, $C0, $C0, $C0, $C0, $C0, $C0, $C0, $C0, $C0, $C0, $C0, $FF, $FF, $FF, $FF
.db $00, $C0, $00, $E0, $00, $70, $00, $38, $00, $1C, $00, $0E, $00, $07, $00, $03
.db $00, $03, $00, $07, $00, $0E, $00, $1C, $00, $38, $00, $70, $00, $E0, $00, $C0
.db $00, $07, $00, $0F, $00, $18, $00, $30, $00, $60, $00, $C0, $00, $C0, $00, $C0
.db $00, $E0, $00, $F0, $00, $18, $00, $0C, $00, $06, $00, $03, $00, $03, $00, $03
.db $FC, $00, $F8, $00, $F0, $00, $E0, $00, $C0, $00, $80, $00, $00, $00, $00, $00
.db $3F, $00, $1F, $00, $0F, $00, $07, $00, $03, $00, $01, $00, $00, $00, $00, $00
.db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
.db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
.db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
.db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
.db $03, $03, $03, $03, $03, $03, $03, $03, $03, $03, $03, $03, $03, $03, $03, $03
.db $C0, $C0, $C0, $C0, $C0, $C0, $C0, $C0, $C0, $C0, $C0, $C0, $C0, $C0, $C0, $C0
.db $FF, $FF, $FF, $FF, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
.db $FF, $FF, $FF, $FF, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
.db $FF, $FF, $FF, $FF, $03, $03, $03, $03, $03, $03, $03, $03, $03, $03, $03, $03
.db $FF, $FF, $FF, $FF, $C0, $C0, $C0, $C0, $C0, $C0, $C0, $C0, $C0, $C0, $C0, $C0
.db $00, $03, $00, $07, $00, $0E, $00, $1C, $00, $38, $00, $70, $00, $E0, $00, $C0
.db $00, $C0, $00, $E0, $00, $70, $00, $38, $00, $1C, $00, $0E, $00, $07, $00, $03
.db $00, $C0, $00, $C0, $00, $C0, $00, $60, $00, $30, $00, $18, $00, $0F, $00, $07
.db $00, $03, $00, $03, $00, $03, $00, $06, $00, $0C, $00, $18, $00, $F0, $00, $E0
.db $00, $00, $00, $00, $80, $00, $C0, $00, $E0, $00, $F0, $00, $F8, $00, $FC, $00
.db $00, $00, $00, $00, $01, $00, $03, $00, $07, $00, $0F, $00, $1F, $00, $3F, $00
PaletteData:
.db $FF, $7F
; 30 tiles (2 spaces)
; 480 bytes