Simple SNES shoot-'em-up game.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

382 lines
7.4 KiB

.INCLUDE "header.asm"
.INCLUDE "InitSNES.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
.ORG 0
.SECTION "MainCode"
; Memory layout:
; 00-0F: scratch space for functions.
; 10-11: controller state of joypad #1.
; 12-13: controller state of joypad #2.
; 14-17: 32-bit counter of vblanks.
; 20-21: (x, y) coordinates of player.
; 22-24: RGB color values to use for background color, from [0-31].
Start:
InitializeSNES
jsr LoadPaletteAndTileData
; Turn on the screen.
; Format: x000bbbb
; x: 0 = screen on, 1 = screen off, bbbb: Brightness ($0-$F)
lda #%00001111
sta INIDISP
; Enable NMI interrupt & joypad.
; n-vh---j n: NMI interrupt enable v: vertical counter enable
; h: horizontal counter enable j: joypad enable
lda #%10000001
sta NMITIMEN
; Store zeroes to the controller status registers.
; TODO(mcmillen): is this needed? I think the system will overwrite these
; automatically.
stz JOY1H
stz JOY1L
; Write something recognizable into our scratch space.
jsr FillScratch
; Start the background color as a dark blue.
lda #16
sta $24
MainLoop:
wai ; Wait for interrupt.
jmp 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:
jsr VBlankCounter ; DEBUG
jsr JoypadHandler
jsr SetBackgroundColor
jsr SetPlayerPosition
rti
VBlankCounter:
; Increment a counter of how many VBlanks we've done.
inc $14
lda $14
cmp #$00
bne VBlankCounterDone
inc $15
lda $15
cmp #$00
bne VBlankCounterDone
inc $16
lda $16
cmp #$00
bne VBlankCounterDone
inc $17
VBlankCounterDone:
rts
JoypadHandler:
jsr JoypadDebug ; DEBUG
JoypadUp:
lda JOY1H
and #$08 ; Up
cmp #$08
bne JoypadDown ; Button not pressed.
lda $21
cmp #0
beq JoypadDown ; Value saturated.
dec $21
dec $21
JoypadDown:
lda JOY1H
and #$04
cmp #$04
bne JoypadLeft ; Button not pressed.
lda $21
cmp #(224 - 16)
beq JoypadLeft ; Value saturated.
inc $21
inc $21
JoypadLeft:
lda JOY1H
and #$02 ; Left
cmp #$02
bne JoypadRight ; Button not pressed.
lda $20
cmp #0
beq JoypadRight ; Value saturated.
dec $20
dec $20
JoypadRight:
lda JOY1H
and #$01
cmp #$01 ; Right
bne JoypadB ; Button not pressed.
lda $20
cmp #(256 - 16)
beq JoypadB ; Value saturated.
inc $20
inc $20
JoypadB:
lda JOY1H
and #$80 ; B
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.
lda $22
cmp #31
beq JoypadY ; Value saturated.
inc $22
JoypadY:
lda JOY1H
and #$40 ; Y
cmp #$40
bne JoypadX ; Button not pressed.
lda $23
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.
inc $24
; TODO(mcmillen): have Start and Select do something too.
JoypadDone:
rts
JoypadDebug:
; Load joypad registers into RAM for easier inspection.
lda JOY1L
sta $10
lda JOY1H
sta $11
lda JOY2L
sta $12
lda JOY2H
sta $13
rts
SetBackgroundColor:
; $22 $23 $24 are (R, G, B), each ranging from [0-31].
; 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.
lda $23 ; Green.
.rept 5
asl
.endr
ora $22 ; Red.
sta CGDATA
; Compute the high-order byte and store it in CGDATA.
lda $24 ; Blue.
.rept 2
asl
.endr
sta $00
lda $23 ; Green.
.rept 3
lsr
.endr
ora $00
sta CGDATA
rts
SetPlayerPosition:
; 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
FillScratch:
lda #$42 ; B
ldx #0
FillScratchLoop:
sta $00,X
inx
cpx #$10
bne FillScratchLoop
rts
.ENDS
.BANK 1 SLOT 0
.ORG 0
.SECTION "TileData"
.INCLUDE "tiles.asm"
.ENDS