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.

567 lines
12 KiB

9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
  1. .INCLUDE "header.asm"
  2. .INCLUDE "init.asm"
  3. .INCLUDE "registers.asm"
  4. ; The JSR and RTS instructions add a total of 12 cycles of overhead. For
  5. ; short, commonly-used functions, it makes sense to declare them as macros,
  6. ; which get inlined by the assembler at the point of use. This saves on
  7. ; CPU cycles, at the cost of code size.
  8. .MACRO ConvertXCoordinate
  9. ; Data in: world x-coordinate, in A register.
  10. ; Data out: SNES scroll data, in C (the 16-bit A register).
  11. rep #%00100000 ; 16-bit A
  12. eor #$FFFF ; Flip bits
  13. ina
  14. sep #%00100000 ; 8-bit A
  15. .ENDM
  16. .MACRO ConvertYCoordinate
  17. ; Data in: world y-coordinate, in A register.
  18. ; Data out: SNES scroll data, in C (the 16-bit A register).
  19. rep #%00100000 ; 16-bit A
  20. eor #$FFFF ; Flip bits
  21. sep #%00100000 ; 8-bit A
  22. .ENDM
  23. .BANK 0 SLOT 0
  24. .ORG 0
  25. .SECTION "MainCode"
  26. ; Memory layout:
  27. ; 0000-000F: scratch space for functions.
  28. ; 0010-0011: controller state of joypad #1.
  29. ; 0012-0013: controller state of joypad #2.
  30. ; 0014-0017: 32-bit counter of vblanks.
  31. ; 0020-0021: (x, y) coordinates of player.
  32. ; 0022-0024: RGB color values to use for background color, from [0-31].
  33. ;
  34. ; Sprite table buffers -- copied each frame to OAM during VBlank, using DMA.
  35. ; 0100-02FF: table 1 (4 bytes each: x/y coord, tile #, flip/priority/palette)
  36. ; 0300-031F: table 2 (2 bits each: high x-coord bit, size)
  37. Start:
  38. InitializeSNES
  39. jsr LoadPaletteAndTileData
  40. jsr InitializeSpriteTables
  41. ; Set screen mode: 16x16 tiles for backgrounds, mode 0.
  42. lda #%11110000
  43. sta SCREENMODE
  44. ; Set sprite size to 16x16 (small) and 32x32 (large).
  45. lda #%01100000
  46. sta OAMSIZE
  47. ; Main screen: enable sprites & BG2.
  48. lda #%00010010
  49. sta MSENABLE
  50. ; Turn on the screen.
  51. ; Format: x000bbbb
  52. ; x: 0 = screen on, 1 = screen off, bbbb: Brightness ($0-$F)
  53. lda #%00001111
  54. sta INIDISP
  55. ; Enable NMI interrupt & joypad.
  56. ; n-vh---j n: NMI interrupt enable v: vertical counter enable
  57. ; h: horizontal counter enable j: joypad enable
  58. lda #%10000001
  59. sta NMITIMEN
  60. ; Store zeroes to the controller status registers.
  61. ; TODO(mcmillen): is this needed? I think the system will overwrite these
  62. ; automatically.
  63. stz JOY1H
  64. stz JOY1L
  65. ; Write something recognizable into our scratch space.
  66. jsr FillScratch
  67. ; Start the background color as a dark blue.
  68. lda #4
  69. sta $24
  70. ; Player's initial starting location.
  71. lda #(256 / 4)
  72. sta $20
  73. lda #((224 - 8) / 2)
  74. sta $21
  75. MainLoop:
  76. wai ; Wait for interrupt.
  77. jmp MainLoop
  78. LoadPaletteAndTileData:
  79. ; For more details on DMA, see:
  80. ; http://wiki.superfamicom.org/snes/show/Grog%27s+Guide+to+DMA+and+HDMA+on+the+SNES
  81. ; http://wiki.superfamicom.org/snes/show/Making+a+Small+Game+-+Tic-Tac-Toe
  82. ;
  83. ; A lot of the graphics-related registers are explained in Qwertie's doc:
  84. ; http://emu-docs.org/Super%20NES/General/snesdoc.html
  85. ; ... but be careful, because there are some errors in this doc.
  86. ;
  87. ; bazz's tutorial (available from http://wiki.superfamicom.org/snes/) is
  88. ; quite helpful with palette / sprites / DMA, especially starting at
  89. ; http://wiki.superfamicom.org/snes/show/Working+with+VRAM+-+Loading+the+Palette
  90. ; 16-bit X/Y registers. Used for DMA source address & transfer size, both of
  91. ; which want 16-bit values.
  92. rep #%00010000
  93. ; 8-bit A/B registers. Used for DMA source bank & destination address.
  94. sep #%00100000
  95. ; Initialize the palette memory in a loop.
  96. ; We could also do this with a DMA transfer (like we do with the tile data
  97. ; below), but it seems overkill for just a few bytes. :)
  98. ; TODO(mcmillen): do it with a DMA transfer.
  99. ; First, sprite palette data:
  100. ldx #0
  101. lda #128 ; Palette entries for sprites start at 128.
  102. sta CGADDR
  103. -
  104. lda.l SpritePalette, X
  105. sta CGDATA
  106. inx
  107. cpx #32 ; 32 bytes of palette data.
  108. bne -
  109. ; Now, BG2 palette data:
  110. ldx #0
  111. lda #32 ; Palette entries for BG2 start at 32.
  112. sta CGADDR
  113. -
  114. lda.l TilePalette, X
  115. sta CGDATA
  116. inx
  117. cpx #8 ; 8 bytes of palette data.
  118. bne -
  119. ; TODO(mcmillen): make the "DMA stuff into VRAM" a macro or function.
  120. ; Set VMADDR to where we want the DMA to start. We'll store sprite data
  121. ; at the beginning of VRAM.
  122. ldx #$0000
  123. stx VMADDR
  124. ; DMA 0 source address & bank.
  125. ldx #SpriteData
  126. stx DMA0SRC
  127. lda #:SpriteData
  128. sta DMA0SRCBANK
  129. ; DMA 0 transfer size.
  130. ; See the helpful comment in sprites.asm to find the size of the tile data.
  131. ldx #576
  132. stx DMA0SIZE
  133. ; DMA 0 control register.
  134. ; Transfer type 001 = 2 addresses, LH.
  135. lda #%00000001
  136. sta DMA0CTRL
  137. ; DMA 0 destination.
  138. lda #$18 ; The upper byte is assumed to be $21, so this is $2118 & $2119.
  139. sta DMA0DST
  140. ; Enable DMA channel 0.
  141. lda #%00000001
  142. sta DMAENABLE
  143. ; Store background tile data at byte $2000 of VRAM.
  144. ; (VMADDR is a word address, so multiply by 2 to get the byte address.)
  145. ldx #$1000
  146. stx VMADDR
  147. ; DMA 0 source address & bank.
  148. ldx #TileData
  149. stx DMA0SRC
  150. lda #:TileData
  151. sta DMA0SRCBANK
  152. ; DMA 0 transfer size.
  153. ; See the helpful comment in tiles.asm to find the size of the tile data.
  154. ldx #384
  155. stx DMA0SIZE
  156. ; DMA 0 control register.
  157. ; Transfer type 001 = 2 addresses, LH.
  158. lda #%00000001
  159. sta DMA0CTRL
  160. ; DMA 0 destination.
  161. lda #$18 ; The upper byte is assumed to be $21, so this is $2118 & $2119.
  162. sta DMA0DST
  163. ; Enable DMA channel 0.
  164. lda #%00000001
  165. sta DMAENABLE
  166. ; Tell the system that the BG2 tilemap starts at $4000.
  167. lda #%00100000
  168. sta BG2TILEMAP
  169. ; ... and that the background tile data for BG1 & BG2 starts at $2000.
  170. lda #%00010001
  171. sta BG12NBA
  172. ; Set up the BG2 tilemap.
  173. ; VRAM write mode: increments the address every time we write a word.
  174. lda #%10000000
  175. sta VMAIN
  176. ; Set word address for accessing VRAM.
  177. ldx #$2000 ; BG 2 tilemap starts here. (Byte address $4000.)
  178. stx VMADDR
  179. ; Now write entries into the tile map.
  180. ; We have only a couple tiles, but we set the invert horizontal/vertical
  181. ; bits so that we get more variation. We also do 7 tiles per loop -- since
  182. ; the tile map is 32 entries wide, this means that successive rows end up
  183. ; looking different.
  184. ldy #0
  185. -
  186. ldx #$0002
  187. stx VMDATA
  188. iny
  189. ldx #$0004
  190. stx VMDATA
  191. iny
  192. ldx #$4002
  193. stx VMDATA
  194. iny
  195. ldx #$8002
  196. stx VMDATA
  197. iny
  198. ldx #$8004
  199. stx VMDATA
  200. iny
  201. ldx #$A002
  202. stx VMDATA
  203. iny
  204. ldx #$A004
  205. stx VMDATA
  206. iny
  207. ; The tile map is 32x32 (1024 entries).
  208. ; This is the next multiple of 7 above that.
  209. cpy #1029
  210. bne -
  211. rts
  212. InitializeSpriteTables:
  213. ; This page is a good reference on SNES sprite formats:
  214. ; http://wiki.superfamicom.org/snes/show/SNES+Sprites
  215. ; It uses the same approach we're using, in which we keep a buffer of the
  216. ; sprite tables in RAM, and DMA the sprite tables to the system's OAM
  217. ; during VBlank.
  218. rep #%00110000 ; 16-bit A/X/Y.
  219. ldx #$0000
  220. ; Fill sprite table 1. 4 bytes per sprite, laid out as follows:
  221. ; Byte 1: xxxxxxxx x: X coordinate
  222. ; Byte 2: yyyyyyyy y: Y coordinate
  223. ; Byte 3: cccccccc c: Starting tile #
  224. ; Byte 4: vhoopppc v: vertical flip h: horizontal flip o: priority bits
  225. ; p: palette #
  226. lda #$01
  227. -
  228. sta $0100, X ; We keep our sprite table at $0100 and DMA it to OAM later.
  229. inx
  230. inx
  231. inx
  232. inx
  233. cpx #$0200
  234. bne -
  235. ; Fill sprite table 2. 2 bits per sprite, like so:
  236. ; bits 0,2,4,6 - High bit of the sprite's x-coordinate.
  237. ; bits 1,3,5,7 - Toggle Sprite size: 0 - small size 1 - large size
  238. ; Setting all the high bits keeps the sprites offscreen.
  239. lda #%0101010101010101
  240. -
  241. sta $0100, X
  242. inx
  243. inx
  244. cpx #$0220
  245. bne -
  246. sep #%00100000 ; 8-bit A.
  247. rts
  248. VBlankHandler:
  249. jsr VBlankCounter ; DEBUG
  250. jsr JoypadHandler
  251. jsr SetBackgroundColor
  252. jsr UpdateGraphics
  253. jsr DMASpriteTables
  254. rti
  255. VBlankCounter:
  256. ; Increment a counter of how many VBlanks we've done.
  257. inc $14
  258. lda $14
  259. cmp #$00
  260. bne VBlankCounterDone
  261. inc $15
  262. lda $15
  263. cmp #$00
  264. bne VBlankCounterDone
  265. inc $16
  266. lda $16
  267. cmp #$00
  268. bne VBlankCounterDone
  269. inc $17
  270. VBlankCounterDone:
  271. rts
  272. JoypadHandler:
  273. jsr JoypadDebug ; DEBUG
  274. JoypadUp:
  275. lda JOY1H
  276. and #$08 ; Up
  277. cmp #$08
  278. bne JoypadDown ; Button not pressed.
  279. lda $21
  280. cmp #0
  281. beq JoypadDown ; Value saturated.
  282. dec $21
  283. dec $21
  284. JoypadDown:
  285. lda JOY1H
  286. and #$04
  287. cmp #$04
  288. bne JoypadLeft ; Button not pressed.
  289. lda $21
  290. cmp #(224 - 16)
  291. beq JoypadLeft ; Value saturated.
  292. inc $21
  293. inc $21
  294. JoypadLeft:
  295. lda JOY1H
  296. and #$02 ; Left
  297. cmp #$02
  298. bne JoypadRight ; Button not pressed.
  299. lda $20
  300. cmp #0
  301. beq JoypadRight ; Value saturated.
  302. dec $20
  303. dec $20
  304. JoypadRight:
  305. lda JOY1H
  306. and #$01
  307. cmp #$01 ; Right
  308. bne JoypadB ; Button not pressed.
  309. lda $20
  310. cmp #(256 - 16)
  311. beq JoypadB ; Value saturated.
  312. inc $20
  313. inc $20
  314. JoypadB:
  315. lda JOY1H
  316. and #$80 ; B
  317. cmp #$80
  318. bne JoypadA ; Button not pressed.
  319. lda $22
  320. cmp #0
  321. beq JoypadA ; Value saturated.
  322. dec $22
  323. JoypadA:
  324. lda JOY1L
  325. and #$80 ; A
  326. cmp #$80
  327. bne JoypadY ; Button not pressed.
  328. lda $22
  329. cmp #31
  330. beq JoypadY ; Value saturated.
  331. inc $22
  332. JoypadY:
  333. lda JOY1H
  334. and #$40 ; Y
  335. cmp #$40
  336. bne JoypadX ; Button not pressed.
  337. lda $23
  338. cmp #0
  339. beq JoypadX ; Value saturated.
  340. dec $23
  341. JoypadX:
  342. lda JOY1L
  343. and #$40 ; X
  344. cmp #$40
  345. bne JoypadL ; Button not pressed.
  346. lda $23
  347. cmp #31
  348. beq JoypadL ; Value saturated.
  349. inc $23
  350. JoypadL:
  351. lda JOY1L
  352. and #$20 ; L
  353. cmp #$20
  354. bne JoypadR ; Button not pressed.
  355. lda $24
  356. cmp #0
  357. beq JoypadR ; Value saturated.
  358. dec $24
  359. JoypadR:
  360. lda JOY1L
  361. and #$10 ; R
  362. cmp #$10
  363. bne JoypadDone ; Button not pressed.
  364. lda $24
  365. cmp #31
  366. beq JoypadDone ; Value saturated.
  367. inc $24
  368. ; TODO(mcmillen): have Start and Select do something too.
  369. JoypadDone:
  370. rts
  371. JoypadDebug:
  372. ; Load joypad registers into RAM for easier inspection.
  373. lda JOY1L
  374. sta $10
  375. lda JOY1H
  376. sta $11
  377. lda JOY2L
  378. sta $12
  379. lda JOY2H
  380. sta $13
  381. rts
  382. SetBackgroundColor:
  383. ; $22 $23 $24 are (R, G, B), each ranging from [0-31].
  384. ; The palette color format is 15-bit: [0bbbbbgg][gggrrrrr]
  385. ; Set the background color.
  386. ; Entry 0 corresponds to the SNES background color.
  387. stz CGADDR
  388. ; Compute and the low-order byte and store it in CGDATA.
  389. lda $23 ; Green.
  390. .rept 5
  391. asl
  392. .endr
  393. ora $22 ; Red.
  394. sta CGDATA
  395. ; Compute the high-order byte and store it in CGDATA.
  396. lda $24 ; Blue.
  397. .rept 2
  398. asl
  399. .endr
  400. sta $00
  401. lda $23 ; Green.
  402. .rept 3
  403. lsr
  404. .endr
  405. ora $00
  406. sta CGDATA
  407. rts
  408. UpdateGraphics:
  409. ; Copy player coords into sprite table.
  410. lda $0020
  411. sta $0100
  412. lda $0021
  413. sta $0101
  414. ; Set priority bits so that the sprite is drawn in front.
  415. lda #%00110000
  416. sta $0103
  417. ; Clear x-MSB so that the sprite is displayed.
  418. lda $0300
  419. and #%11111110
  420. sta $0300
  421. ; Make the background scroll. Horizontal over time; vertical depending on
  422. ; player's y-coordinate.
  423. lda $14
  424. sta BG2HOFS
  425. lda $15
  426. sta BG2HOFS
  427. lda $21
  428. .rept 3
  429. lsr
  430. .endr
  431. sta BG2VOFS
  432. stz BG2VOFS
  433. rts
  434. DMASpriteTables:
  435. rep #%00010000 ; 16-bit X/Y.
  436. sep #%00100000 ; 8-bit A.
  437. ; Store at the base OAM address.
  438. ldx #$0000
  439. stx OAMADDR
  440. ; Default DMA control; destination $2104 (OAM data register).
  441. stz DMA0CTRL
  442. lda #$04
  443. sta DMA0DST
  444. ; Our sprites start at $0100 in bank 0 and are #$220 bytes long.
  445. ldx #$0100
  446. stx DMA0SRC
  447. stz DMA0SRCBANK
  448. ldx #$0220
  449. stx DMA0SIZE
  450. ; Kick off the DMA transfer.
  451. lda #%00000001
  452. sta DMAENABLE
  453. rts
  454. FillScratch:
  455. lda #$42 ; ASCII "B"
  456. ldx #0
  457. -
  458. sta $00, X
  459. inx
  460. cpx #$10
  461. bne -
  462. rts
  463. .ENDS
  464. .BANK 1 SLOT 0
  465. .ORG 0
  466. .SECTION "SpriteData"
  467. .INCLUDE "sprites.asm"
  468. .ENDS
  469. .BANK 2 SLOT 0
  470. .ORG 0
  471. .SECTION "TileData"
  472. .INCLUDE "tiles.asm"
  473. .ENDS