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.

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