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.

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