pewpew/pewpew.asm

568 lines
12 KiB
NASM
Raw Normal View History

2015-05-22 18:08:57 +00:00
.INCLUDE "header.asm"
2015-05-23 19:42:47 +00:00
.INCLUDE "init.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"
2015-05-22 16:49:40 +00:00
; Memory layout:
2015-05-24 14:43:25 +00:00
; 0000-000F: scratch space for functions.
; 0010-0011: controller state of joypad #1.
; 0012-0013: controller state of joypad #2.
; 0014-0017: 32-bit counter of vblanks.
; 0020-0021: (x, y) coordinates of player.
; 0022-0024: RGB color values to use for background color, from [0-31].
;
; Sprite table buffers -- copied each frame to OAM during VBlank, using DMA.
; 0100-02FF: table 1 (4 bytes each: x/y coord, tile #, flip/priority/palette)
; 0300-031F: table 2 (2 bits each: high x-coord bit, size)
2015-05-22 16:49:40 +00:00
Start:
InitializeSNES
jsr LoadPaletteAndTileData
2015-05-24 14:43:25 +00:00
jsr InitializeSpriteTables
2015-05-24 18:44:26 +00:00
; Set screen mode: 16x16 tiles for backgrounds, mode 0.
lda #%11110000
sta SCREENMODE
2015-05-24 14:43:25 +00:00
; Set sprite size to 16x16 (small) and 32x32 (large).
lda #%01100000
sta OAMSIZE
; Main screen: enable sprites & BG2.
lda #%00010010
2015-05-24 14:43:25 +00:00
sta MSENABLE
2015-05-22 16:49:40 +00:00
; Turn on the screen.
; Format: x000bbbb
; x: 0 = screen on, 1 = screen off, bbbb: Brightness ($0-$F)
lda #%00001111
sta INIDISP
2015-05-22 16:49:40 +00:00
; 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
2015-05-22 16:49:40 +00:00
; Store zeroes to the controller status registers.
; TODO(mcmillen): is this needed? I think the system will overwrite these
; automatically.
stz JOY1H
stz JOY1L
2015-05-22 16:49:40 +00:00
; Write something recognizable into our scratch space.
jsr FillScratch
2015-05-24 18:44:26 +00:00
; Start the background color as a dark blue.
lda #4
sta $24
2015-05-23 22:00:49 +00:00
; Player's initial starting location.
lda #(256 / 4)
sta $20
lda #((224 - 8) / 2)
sta $21
2015-05-22 16:49:40 +00:00
MainLoop:
wai ; Wait for interrupt.
jmp MainLoop
2015-05-22 16:49:40 +00:00
LoadPaletteAndTileData:
; For more details on DMA, see:
; http://wiki.superfamicom.org/snes/show/Grog%27s+Guide+to+DMA+and+HDMA+on+the+SNES
; http://wiki.superfamicom.org/snes/show/Making+a+Small+Game+-+Tic-Tac-Toe
;
; A lot of the graphics-related registers are explained in Qwertie's doc:
; http://emu-docs.org/Super%20NES/General/snesdoc.html
; ... but be careful, because there are some errors in this doc.
;
; bazz's tutorial (available from http://wiki.superfamicom.org/snes/) is
; quite helpful with palette / sprites / DMA, especially starting at
; http://wiki.superfamicom.org/snes/show/Working+with+VRAM+-+Loading+the+Palette
; 16-bit X/Y registers. Used for DMA source address & transfer size, both of
; which want 16-bit values.
2015-05-24 14:43:25 +00:00
rep #%00010000
; 8-bit A/B registers. Used for DMA source bank & destination address.
sep #%00100000
2015-05-23 21:17:39 +00:00
; Initialize the palette memory in a loop.
; We could also do this with a DMA transfer (like we do with the tile data
2015-05-23 21:17:39 +00:00
; below), but it seems overkill for just a few bytes. :)
; TODO(mcmillen): do it with a DMA transfer.
; First, sprite palette data:
2015-05-23 21:17:39 +00:00
ldx #0
2015-05-24 14:43:25 +00:00
lda #128 ; Palette entries for sprites start at 128.
sta CGADDR
2015-05-23 21:17:39 +00:00
-
2015-05-24 14:43:25 +00:00
lda.l SpritePalette, X
sta CGDATA
2015-05-23 21:17:39 +00:00
inx
2015-05-24 14:43:25 +00:00
cpx #32 ; 32 bytes of palette data.
2015-05-23 21:17:39 +00:00
bne -
; Now, BG2 palette data:
ldx #0
lda #32 ; Palette entries for BG2 start at 32.
sta CGADDR
-
lda.l TilePalette, X
sta CGDATA
inx
cpx #8 ; 8 bytes of palette data.
bne -
; TODO(mcmillen): make the "DMA stuff into VRAM" a macro or function.
; Set VMADDR to where we want the DMA to start. We'll store sprite data
; at the beginning of VRAM.
ldx #$0000
stx VMADDR
; DMA 0 source address & bank.
2015-05-24 14:43:25 +00:00
ldx #SpriteData
stx DMA0SRC
2015-05-24 14:43:25 +00:00
lda #:SpriteData
sta DMA0SRCBANK
; DMA 0 transfer size.
2015-05-24 14:43:25 +00:00
; See the helpful comment in sprites.asm to find the size of the tile data.
ldx #576
stx DMA0SIZE
; DMA 0 control register.
; Transfer type 001 = 2 addresses, LH.
lda #%00000001
sta DMA0CTRL
; DMA 0 destination.
lda #$18 ; The upper byte is assumed to be $21, so this is $2118 & $2119.
sta DMA0DST
; Enable DMA channel 0.
lda #%00000001
sta DMAENABLE
; Store background tile data at byte $2000 of VRAM.
; (VMADDR is a word address, so multiply by 2 to get the byte address.)
ldx #$1000
2015-05-24 14:43:25 +00:00
stx VMADDR
; DMA 0 source address & bank.
ldx #TileData
stx DMA0SRC
lda #:TileData
sta DMA0SRCBANK
; DMA 0 transfer size.
; See the helpful comment in tiles.asm to find the size of the tile data.
ldx #384
stx DMA0SIZE
; DMA 0 control register.
; Transfer type 001 = 2 addresses, LH.
lda #%00000001
sta DMA0CTRL
; DMA 0 destination.
lda #$18 ; The upper byte is assumed to be $21, so this is $2118 & $2119.
sta DMA0DST
; Enable DMA channel 0.
lda #%00000001
sta DMAENABLE
2015-05-24 18:44:26 +00:00
; Tell the system that the BG2 tilemap starts at $4000.
lda #%00100000
sta BG2TILEMAP
; ... and that the background tile data for BG1 & BG2 starts at $2000.
lda #%00010001
sta BG12NBA
; Set up the BG2 tilemap.
; VRAM write mode: increments the address every time we write a word.
lda #%10000000
sta VMAIN
; Set word address for accessing VRAM.
ldx #$2000 ; BG 2 tilemap starts here. (Byte address $4000.)
stx VMADDR
; Now write entries into the tile map.
2015-05-24 18:44:26 +00:00
; We have only a couple tiles, but we set the invert horizontal/vertical
; bits so that we get more variation. We also do 7 tiles per loop -- since
; the tile map is 32 entries wide, this means that successive rows end up
; looking different.
ldy #0
-
2015-05-24 18:44:26 +00:00
ldx #$0002
stx VMDATA
iny
ldx #$0004
stx VMDATA
iny
ldx #$4002
stx VMDATA
iny
ldx #$8002
stx VMDATA
iny
ldx #$8004
stx VMDATA
iny
2015-05-24 18:44:26 +00:00
ldx #$A002
stx VMDATA
iny
ldx #$A004
stx VMDATA
iny
; The tile map is 32x32 (1024 entries).
; This is the next multiple of 7 above that.
cpy #1029
bne -
2015-05-24 14:43:25 +00:00
rts
InitializeSpriteTables:
; This page is a good reference on SNES sprite formats:
; http://wiki.superfamicom.org/snes/show/SNES+Sprites
; It uses the same approach we're using, in which we keep a buffer of the
; sprite tables in RAM, and DMA the sprite tables to the system's OAM
; during VBlank.
rep #%00110000 ; 16-bit A/X/Y.
ldx #$0000
; Fill sprite table 1. 4 bytes per sprite, laid out as follows:
; Byte 1: xxxxxxxx x: X coordinate
; Byte 2: yyyyyyyy y: Y coordinate
; Byte 3: cccccccc c: Starting tile #
; Byte 4: vhoopppc v: vertical flip h: horizontal flip o: priority bits
; p: palette #
lda #$01
-
sta $0100, X ; We keep our sprite table at $0100 and DMA it to OAM later.
inx
inx
inx
inx
cpx #$0200
bne -
; Fill sprite table 2. 2 bits per sprite, like so:
; bits 0,2,4,6 - High bit of the sprite's x-coordinate.
2015-05-24 14:43:25 +00:00
; bits 1,3,5,7 - Toggle Sprite size: 0 - small size 1 - large size
; Setting all the high bits keeps the sprites offscreen.
2015-05-24 14:43:25 +00:00
lda #%0101010101010101
-
sta $0100, X
inx
inx
cpx #$0220
bne -
2015-05-24 14:43:25 +00:00
sep #%00100000 ; 8-bit A.
rts
2015-05-22 16:49:40 +00:00
VBlankHandler:
jsr VBlankCounter ; DEBUG
jsr JoypadHandler
jsr SetBackgroundColor
jsr UpdateGraphics
2015-05-24 14:43:25 +00:00
jsr DMASpriteTables
2015-05-22 16:49:40 +00:00
rti
2015-05-22 16:49:40 +00:00
VBlankCounter:
; Increment a counter of how many VBlanks we've done.
2015-05-22 16:49:40 +00:00
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
2015-05-22 16:49:40 +00:00
JoypadHandler:
jsr JoypadDebug ; DEBUG
JoypadUp:
lda JOY1H
2015-05-22 16:49:40 +00:00
and #$08 ; Up
cmp #$08
bne JoypadDown ; Button not pressed.
lda $21
cmp #0
2015-05-22 16:49:40 +00:00
beq JoypadDown ; Value saturated.
dec $21
dec $21
2015-05-22 16:49:40 +00:00
JoypadDown:
lda JOY1H
and #$04
2015-05-22 16:49:40 +00:00
cmp #$04
bne JoypadLeft ; Button not pressed.
lda $21
cmp #(224 - 16)
2015-05-22 16:49:40 +00:00
beq JoypadLeft ; Value saturated.
inc $21
inc $21
2015-05-22 16:49:40 +00:00
JoypadLeft:
lda JOY1H
2015-05-22 16:49:40 +00:00
and #$02 ; Left
cmp #$02
bne JoypadRight ; Button not pressed.
lda $20
2015-05-22 16:49:40 +00:00
cmp #0
beq JoypadRight ; Value saturated.
dec $20
dec $20
2015-05-22 16:49:40 +00:00
JoypadRight:
lda JOY1H
2015-05-22 16:49:40 +00:00
and #$01
cmp #$01 ; Right
bne JoypadB ; Button not pressed.
lda $20
cmp #(256 - 16)
2015-05-22 16:49:40 +00:00
beq JoypadB ; Value saturated.
inc $20
inc $20
2015-05-22 16:49:40 +00:00
JoypadB:
lda JOY1H
2015-05-22 16:49:40 +00:00
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
2015-05-22 16:49:40 +00:00
bne JoypadY ; Button not pressed.
lda $22
2015-05-22 16:49:40 +00:00
cmp #31
beq JoypadY ; Value saturated.
inc $22
2015-05-22 16:49:40 +00:00
JoypadY:
lda JOY1H
2015-05-22 16:49:40 +00:00
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
2015-05-22 16:49:40 +00:00
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.
2015-05-22 16:49:40 +00:00
JoypadDone:
rts
2015-05-22 16:49:40 +00:00
JoypadDebug:
; Load joypad registers into RAM for easier inspection.
lda JOY1L
2015-05-22 16:49:40 +00:00
sta $10
lda JOY1H
2015-05-22 16:49:40 +00:00
sta $11
lda JOY2L
2015-05-22 16:49:40 +00:00
sta $12
lda JOY2H
2015-05-22 16:49:40 +00:00
sta $13
rts
2015-05-22 16:49:40 +00:00
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
2015-05-22 16:49:40 +00:00
sta $00
lda $23 ; Green.
.rept 3
lsr
.endr
2015-05-22 16:49:40 +00:00
ora $00
sta CGDATA
rts
UpdateGraphics:
2015-05-24 14:43:25 +00:00
; Copy player coords into sprite table.
lda $0020
2015-05-24 14:43:25 +00:00
sta $0100
lda $0021
2015-05-24 14:43:25 +00:00
sta $0101
; Set priority bits so that the sprite is drawn in front.
lda #%00110000
sta $0103
2015-05-24 14:43:25 +00:00
; Clear x-MSB so that the sprite is displayed.
lda $0300
and #%11111110
sta $0300
2015-05-24 18:44:26 +00:00
; Make the background scroll. Horizontal over time; vertical depending on
; player's y-coordinate.
lda $14
sta BG2HOFS
lda $15
sta BG2HOFS
2015-05-24 18:44:26 +00:00
lda $21
.rept 3
lsr
.endr
sta BG2VOFS
stz BG2VOFS
2015-05-24 14:43:25 +00:00
rts
DMASpriteTables:
rep #%00010000 ; 16-bit X/Y.
sep #%00100000 ; 8-bit A.
; Store at the base OAM address.
ldx #$0000
stx OAMADDR
; Default DMA control; destination $2104 (OAM data register).
stz DMA0CTRL
lda #$04
sta DMA0DST
; Our sprites start at $0100 in bank 0 and are #$220 bytes long.
ldx #$0100
stx DMA0SRC
stz DMA0SRCBANK
ldx #$0220
stx DMA0SIZE
; Kick off the DMA transfer.
lda #%00000001
sta DMAENABLE
2015-05-22 16:49:40 +00:00
rts
2015-05-22 16:49:40 +00:00
FillScratch:
2015-05-24 14:43:25 +00:00
lda #$42 ; ASCII "B"
2015-05-22 16:49:40 +00:00
ldx #0
2015-05-24 14:43:25 +00:00
-
sta $00, X
2015-05-22 16:49:40 +00:00
inx
cpx #$10
2015-05-24 14:43:25 +00:00
bne -
2015-05-22 16:49:40 +00:00
rts
2015-05-24 14:43:25 +00:00
2015-05-22 16:49:40 +00:00
.ENDS
.BANK 1 SLOT 0
.ORG 0
2015-05-24 14:43:25 +00:00
.SECTION "SpriteData"
.INCLUDE "sprites.asm"
.ENDS
.BANK 2 SLOT 0
.ORG 0
.SECTION "TileData"
.INCLUDE "tiles.asm"
.ENDS