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.

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