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.

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