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.

869 lines
19 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
  1. .INCLUDE "header.asm"
  2. .INCLUDE "init.asm"
  3. .INCLUDE "registers.asm"
  4. ; Memory layout:
  5. ; 0000-000F: scratch space for functions.
  6. ; 0010-0011: controller state of joypad #1.
  7. ; 0012-0013: controller state of joypad #2.
  8. ; 0014-0016: 24-bit counter of vblanks.
  9. ; 0017-0019: RGB color values to use for background color, from [0-31].
  10. ; 001A-001B: 16-bit pointer to next random byte.
  11. ; [gap]
  12. ; 0020-0021: (x, y) coordinates of player.
  13. ; 0022: shot cooldown timer.
  14. ; 0023: next-shot state.
  15. ; [gap]
  16. ; 0030-003F: (x, y) velocities of each of the 8 possible shot states.
  17. ; 0040-009F: {sprite, x, y, x-velocity, y-velocity, unused} per shot.
  18. ; If sprite is 0, the shot is disabled.
  19. ; [gap]
  20. ; Sprite table buffers -- copied each frame to OAM during VBlank, using DMA.
  21. ; 1000-11FF: table 1 (4 bytes each: x/y coord, tile #, flip/priority/palette)
  22. ; 1200-121F: table 2 (2 bits each: high x-coord bit, size)
  23. ; 1220-12A0: scratch table. One byte per sprite for high x-coord & size.
  24. .define joy1 $10
  25. .define joy2 $12
  26. .define vBlankCounter $14
  27. .define backgroundRed $17
  28. .define backgroundGreen $18
  29. .define backgroundBlue $19
  30. .define randomBytePtr $1A
  31. .define playerX $20
  32. .define playerY $21
  33. .define shotCooldown $22
  34. .define nextShotState $23
  35. .define shotVelocityTable $30
  36. .define shotArray $40
  37. .define shotArrayLength 16
  38. .define shotSize 6
  39. .define numSprites 128
  40. .define spriteTableStart $1000
  41. .define spriteTable1Size $200
  42. .define spriteTable2Start $1200
  43. .define spriteTableSize $220
  44. .define spriteTableScratchStart $1220
  45. ; Sets A to 8-bit (& enables 8-bit "B" register).
  46. .MACRO SetA8Bit
  47. sep #%00100000 ; 8-bit A/B.
  48. .ENDM
  49. ; Sets A to 16-bit.
  50. .MACRO SetA16Bit
  51. rep #%00100000 ; 16-bit A.
  52. .ENDM
  53. ; Sets X/Y to 16-bit.
  54. .MACRO SetXY16Bit
  55. rep #%00010000 ; 16-bit X/Y.
  56. .ENDM
  57. ; Stores result to A.
  58. ; Assumes 16-bit X & 8-bit A.
  59. ; Modifies X.
  60. ; Updates randomBytePtr.
  61. .MACRO GetRandomByte
  62. ldx randomBytePtr
  63. lda $028000, X ; $028000: beginning of ROM bank 2.
  64. inx
  65. cpx #$8000 ; This is the size of the entire ROM bank.
  66. bne +++
  67. ldx #0
  68. +++
  69. stx randomBytePtr
  70. .ENDM
  71. .BANK 0 SLOT 0
  72. .ORG 0
  73. .SECTION "MainCode"
  74. Start:
  75. InitializeSNES
  76. ; By default we assume 16-bit X/Y and 8-bit A.
  77. ; If any code wants to change this, it's expected to do so itself,
  78. ; and to change them back to the defaults before returning.
  79. SetXY16Bit
  80. SetA8Bit
  81. jsr LoadPaletteAndTileData
  82. ; TODO(mcmillen): do we even need to init the sprite tables any more?
  83. jsr InitializeSpriteTables
  84. jsr InitializeWorld
  85. ; Set screen mode: 16x16 tiles for backgrounds, mode 1.
  86. lda #%11000001
  87. sta BGMODE
  88. ; Set sprite size to 16x16 (small) and 32x32 (large).
  89. lda #%01100000
  90. sta OAMSIZE
  91. ; Main screen: enable sprites & BG3.
  92. lda #%00010100
  93. sta MSENABLE
  94. ; Turn on the screen.
  95. ; Format: x000bbbb
  96. ; x: 0 = screen on, 1 = screen off, bbbb: Brightness ($0-$F)
  97. lda #%00001111
  98. sta INIDISP
  99. jmp MainLoop
  100. LoadPaletteAndTileData:
  101. ; For more details on DMA, see:
  102. ; http://wiki.superfamicom.org/snes/show/Grog%27s+Guide+to+DMA+and+HDMA+on+the+SNES
  103. ; http://wiki.superfamicom.org/snes/show/Making+a+Small+Game+-+Tic-Tac-Toe
  104. ;
  105. ; A lot of the graphics-related registers are explained in Qwertie's doc:
  106. ; http://emu-docs.org/Super%20NES/General/snesdoc.html
  107. ; ... but be careful, because there are some errors in this doc.
  108. ;
  109. ; bazz's tutorial (available from http://wiki.superfamicom.org/snes/) is
  110. ; quite helpful with palette / sprites / DMA, especially starting at
  111. ; http://wiki.superfamicom.org/snes/show/Working+with+VRAM+-+Loading+the+Palette
  112. ; Initialize the palette memory in a loop.
  113. ; We could also do this with a DMA transfer (like we do with the tile data
  114. ; below), but it seems overkill for just a few bytes. :)
  115. ; TODO(mcmillen): do it with a DMA transfer.
  116. ; First, sprite palette data:
  117. ldx #0
  118. lda #128 ; Palette entries for sprites start at 128.
  119. sta CGADDR
  120. -
  121. lda.l SpritePalette, X
  122. sta CGDATA
  123. inx
  124. cpx #32 ; 32 bytes of palette data.
  125. bne -
  126. ; Now, BG3 palette data.
  127. ; Palette entries for BG3 start at 0.
  128. ldx #0
  129. lda #0
  130. sta CGADDR
  131. -
  132. lda.l TilePalette, X
  133. sta CGDATA
  134. inx
  135. cpx #8 ; 8 bytes of palette data.
  136. bne -
  137. ; TODO(mcmillen): make the "DMA stuff into VRAM" a macro or function.
  138. ; Set VMADDR to where we want the DMA to start. We'll store sprite data
  139. ; at the beginning of VRAM.
  140. ldx #$0000
  141. stx VMADDR
  142. ; DMA 0 source address & bank.
  143. ldx #SpriteData
  144. stx DMA0SRC
  145. lda #:SpriteData
  146. sta DMA0SRCBANK
  147. ; DMA 0 transfer size. Equal to the size of sprites32.pic.
  148. ldx #2048
  149. stx DMA0SIZE
  150. ; DMA 0 control register.
  151. ; Transfer type 001 = 2 addresses, LH.
  152. lda #%00000001
  153. sta DMA0CTRL
  154. ; DMA 0 destination.
  155. lda #$18 ; The upper byte is assumed to be $21, so this is $2118 & $2119.
  156. sta DMA0DST
  157. ; Enable DMA channel 0.
  158. lda #%00000001
  159. sta DMAENABLE
  160. ; Store background tile data at byte $2000 of VRAM.
  161. ; (VMADDR is a word address, so multiply by 2 to get the byte address.)
  162. ldx #$1000
  163. stx VMADDR
  164. ; DMA 0 source address & bank.
  165. ldx #TileData
  166. stx DMA0SRC
  167. lda #:TileData
  168. sta DMA0SRCBANK
  169. ; DMA 0 transfer size. Equal to the size of tiles.pic.
  170. ldx #512
  171. stx DMA0SIZE
  172. ; DMA 0 control register.
  173. ; Transfer type 001 = 2 addresses, LH.
  174. lda #%00000001
  175. sta DMA0CTRL
  176. ; DMA 0 destination.
  177. lda #$18 ; The upper byte is assumed to be $21, so this is $2118 & $2119.
  178. sta DMA0DST
  179. ; Enable DMA channel 0.
  180. lda #%00000001
  181. sta DMAENABLE
  182. ; Tell the system that the BG3 tilemap starts at $4000.
  183. lda #%00100000
  184. sta BG3TILEMAP
  185. ; ... and that the background tile data for BG3 starts at $2000.
  186. lda #%00000001
  187. sta BG34NBA
  188. ; Set up the BG3 tilemap.
  189. ; VRAM write mode: increments the address every time we write a word.
  190. lda #%10000000
  191. sta VMAIN
  192. ; Set word address for accessing VRAM.
  193. ldx #$2000 ; BG 3 tilemap starts here. (Byte address $4000.)
  194. stx VMADDR
  195. ; Now write entries into the tile map.
  196. ldy #0
  197. -
  198. GetRandomByte
  199. sta $00
  200. ldx #$0000 ; This is a blank tile.
  201. ; 1 in 8 chance that we choose a non-blank tile.
  202. bit #%00000111
  203. bne +
  204. ldx #$0002
  205. bit #%10000000
  206. bne +
  207. ldx #$8002 ; Flip vertically.
  208. +
  209. stx VMDATA
  210. iny
  211. ; The tile map is 32x32 (1024 entries).
  212. cpy #1024
  213. bne -
  214. rts
  215. InitializeSpriteTables:
  216. ; This page is a good reference on SNES sprite formats:
  217. ; http://wiki.superfamicom.org/snes/show/SNES+Sprites
  218. ; It uses the same approach we're using, in which we keep a buffer of the
  219. ; sprite tables in RAM, and DMA the sprite tables to the system's OAM
  220. ; during VBlank.
  221. SetA16Bit
  222. ldx #$0000
  223. ; Fill sprite table 1. 4 bytes per sprite, laid out as follows:
  224. ; Byte 1: xxxxxxxx x: X coordinate
  225. ; Byte 2: yyyyyyyy y: Y coordinate
  226. ; Byte 3: cccccccc c: Starting tile #
  227. ; Byte 4: vhoopppc v: vertical flip h: horizontal flip o: priority bits
  228. ; p: palette #
  229. lda #$01
  230. -
  231. sta spriteTableStart, X
  232. .rept 4
  233. inx
  234. .endr
  235. cpx #spriteTable1Size
  236. bne -
  237. ; Fill sprite table 2. 2 bits per sprite, like so:
  238. ; bits 0,2,4,6 - High bit of the sprite's x-coordinate.
  239. ; bits 1,3,5,7 - Toggle Sprite size: 0 - small size 1 - large size
  240. ; Setting all the high bits keeps the sprites offscreen.
  241. lda #$FFFF
  242. -
  243. sta spriteTableStart, X
  244. inx
  245. inx
  246. cpx #spriteTableSize
  247. bne -
  248. SetA8Bit
  249. rts
  250. InitializeWorld:
  251. ; Start the background color as a dark blue.
  252. lda #4
  253. sta backgroundBlue
  254. ; Player's initial starting location.
  255. lda #(256 / 4)
  256. sta playerX
  257. lda #((224 - 32) / 2)
  258. sta playerY
  259. ; (x-velocity, y-velocity) of 4 different player shot patterns.
  260. lda #6
  261. sta shotVelocityTable
  262. lda #0
  263. sta shotVelocityTable + 1
  264. lda #3
  265. sta shotVelocityTable + 2
  266. lda #3
  267. sta shotVelocityTable + 3
  268. lda #0
  269. sta shotVelocityTable + 4
  270. lda #6
  271. sta shotVelocityTable + 5
  272. lda #-3
  273. sta shotVelocityTable + 6
  274. lda #3
  275. sta shotVelocityTable + 7
  276. lda #-6
  277. sta shotVelocityTable + 8
  278. lda #0
  279. sta shotVelocityTable + 9
  280. lda #-3
  281. sta shotVelocityTable + 10
  282. lda #-3
  283. sta shotVelocityTable + 11
  284. lda #0
  285. sta shotVelocityTable + 12
  286. lda #-6
  287. sta shotVelocityTable + 13
  288. lda #3
  289. sta shotVelocityTable + 14
  290. lda #-3
  291. sta shotVelocityTable + 15
  292. rts
  293. MainLoop:
  294. lda #%10000001 ; Enable NMI interrupt & auto joypad read.
  295. sta NMITIMEN
  296. wai ; Wait for interrupt.
  297. lda #%00000001 ; Disable NMI interrupt while processing.
  298. sta NMITIMEN
  299. jsr JoypadRead
  300. jsr JoypadHandler
  301. jsr UpdateWorld
  302. jsr UpdateSprites
  303. jsr FillSecondarySpriteTable
  304. jsr SetBackgroundColor
  305. jmp MainLoop
  306. JoypadRead:
  307. ; Load joypad registers into RAM for easy inspection & manipulation.
  308. -
  309. lda HVBJOY
  310. bit #$01 ; If auto-joypad read is happening, loop.
  311. bne -
  312. ldx JOY1L
  313. stx joy1
  314. ldx JOY2L
  315. stx joy2
  316. rts
  317. JoypadHandler:
  318. JoypadUp:
  319. lda joy1 + 1
  320. bit #$08 ; Up
  321. beq JoypadDown ; Button not pressed.
  322. lda playerY
  323. cmp #0
  324. beq JoypadDown ; Value saturated.
  325. dec playerY
  326. dec playerY
  327. JoypadDown:
  328. lda joy1 + 1
  329. bit #$04 ; Down
  330. beq JoypadLeft ; Button not pressed.
  331. lda playerY
  332. cmp #(224 - 32)
  333. beq JoypadLeft ; Value saturated.
  334. inc playerY
  335. inc playerY
  336. JoypadLeft:
  337. lda joy1 + 1
  338. bit #$02 ; Left
  339. beq JoypadRight ; Button not pressed.
  340. lda playerX
  341. cmp #0
  342. beq JoypadRight ; Value saturated.
  343. dec playerX
  344. dec playerX
  345. JoypadRight:
  346. lda joy1 + 1
  347. bit #$01 ; Right
  348. beq JoypadStart ; Button not pressed.
  349. lda playerX
  350. cmp #(256 - 32)
  351. beq JoypadStart ; Value saturated.
  352. inc playerX
  353. inc playerX
  354. JoypadStart:
  355. lda joy1 + 1
  356. bit #$10 ; Start
  357. beq JoypadSelect ; Button not pressed.
  358. lda backgroundRed
  359. cmp #31
  360. beq JoypadSelect ; Value saturated.
  361. inc backgroundRed
  362. JoypadSelect:
  363. lda joy1 + 1
  364. bit #$20 ; Select
  365. beq JoypadY ; Button not pressed.
  366. lda backgroundRed
  367. cmp #0
  368. beq JoypadY ; Value saturated.
  369. dec backgroundRed
  370. JoypadY:
  371. lda joy1 + 1
  372. bit #$40 ; Y
  373. beq JoypadX ; Button not pressed.
  374. lda backgroundGreen
  375. cmp #0
  376. beq JoypadX ; Value saturated.
  377. dec backgroundGreen
  378. JoypadX:
  379. lda joy1
  380. bit #$40 ; X
  381. beq JoypadL ; Button not pressed.
  382. lda backgroundGreen
  383. cmp #31
  384. beq JoypadL ; Value saturated.
  385. inc backgroundGreen
  386. JoypadL:
  387. lda joy1
  388. bit #$20 ; L
  389. beq JoypadR ; Button not pressed.
  390. lda backgroundBlue
  391. cmp #0
  392. beq JoypadR ; Value saturated.
  393. dec backgroundBlue
  394. JoypadR:
  395. lda joy1
  396. bit #$10 ; R
  397. beq JoypadB ; Button not pressed.
  398. lda backgroundBlue
  399. cmp #31
  400. beq JoypadB ; Value saturated.
  401. inc backgroundBlue
  402. JoypadB:
  403. lda joy1 + 1
  404. bit #$80 ; B
  405. beq JoypadDone
  406. jsr MaybeShoot
  407. JoypadDone:
  408. rts
  409. MaybeShoot:
  410. ; If the cooldown timer is non-zero, don't shoot.
  411. lda shotCooldown
  412. cmp #0
  413. bne MaybeShootDone
  414. ; Find the first empty spot in the shots array.
  415. ldx #shotArray
  416. -
  417. lda 0, X
  418. cmp #0
  419. beq +
  420. .rept shotSize
  421. inx
  422. .endr
  423. ; If we went all the way to the end, bail out.
  424. cpx #(shotArray + shotArrayLength * shotSize)
  425. beq MaybeShootDone
  426. jmp -
  427. +
  428. ; Enable shot; set its position based on player position.
  429. ; TODO(mcmillen): it might be easier/faster to keep N arrays: one for each
  430. ; field of shot (shotSpriteArray, shotXArray, shotYArray, ...)
  431. lda #8 ; Sprite number.
  432. sta 0, X
  433. lda playerX
  434. clc
  435. adc #20
  436. sta 1, X
  437. lda playerY
  438. sta 2, X
  439. ; Get x- and y-velocity out of shotVelocityTable.
  440. lda nextShotState
  441. and #%00000111 ; 8 possibilities.
  442. ldy #0
  443. -
  444. cmp #0
  445. beq +
  446. .rept 2
  447. iny
  448. .endr
  449. dec A
  450. bra -
  451. +
  452. inc nextShotState
  453. ; x-velocity.
  454. lda shotVelocityTable, Y
  455. sta 3, X
  456. ; y-velocity.
  457. lda shotVelocityTable + 1, Y
  458. sta 4, X
  459. ; Set cooldown timer.
  460. lda #8
  461. sta shotCooldown
  462. MaybeShootDone:
  463. rts
  464. UpdateWorld:
  465. ; Update shot cooldown.
  466. lda shotCooldown
  467. cmp #0
  468. beq +
  469. dec A
  470. sta shotCooldown
  471. +
  472. ldx #0
  473. ; Update shot position.
  474. UpdateShot:
  475. lda shotArray, X
  476. cmp #0
  477. beq ShotDone
  478. ; Add to the x-coordinate. If the carry bit is set, we went off the edge
  479. ; of the screen, so disable the shot.
  480. lda shotArray + 3, X ; x-velocity.
  481. sta $00
  482. bit #%10000000 ; Check whether the velocity is negative.
  483. bne UpdateShotWithNegativeXVelocity
  484. lda shotArray + 1, X
  485. clc
  486. adc $00
  487. bcs DisableShot
  488. sta shotArray + 1, X ; Store new x-coord.
  489. jmp UpdateShotY
  490. UpdateShotWithNegativeXVelocity:
  491. ; TODO(mcmillen): wrap sprites when they go negative here, like we do
  492. ; with y-velocities.
  493. lda shotArray + 1, X ; Current x.
  494. clc
  495. adc $00
  496. bcc DisableShot
  497. sta shotArray + 1, X
  498. jmp UpdateShotY
  499. UpdateShotY:
  500. ; Add to the y-coordinate.
  501. lda shotArray + 4, X ; y-velocity.
  502. sta $00
  503. bit #%10000000 ; Check whether the velocity is negative.
  504. bne UpdateShotWithNegativeYVelocity
  505. lda shotArray + 2, X
  506. clc
  507. adc $00
  508. cmp #224
  509. bcs DisableShot
  510. sta shotArray + 2, X ; Store new y-coord.
  511. jmp ShotDone
  512. UpdateShotWithNegativeYVelocity:
  513. lda shotArray + 2, X ; Current y.
  514. cmp #224
  515. bcs + ; If the shot was "off the top" before moving, maybe we'll reap it.
  516. adc $00 ; Otherwise, just update it,
  517. sta shotArray + 2, X ; save the result,
  518. jmp ShotDone ; and we know it shouldn't be reaped.
  519. +
  520. clc
  521. adc $00
  522. cmp #224
  523. bcc DisableShot ; If it's now wrapped around, reap it.
  524. sta shotArray + 2, X
  525. jmp ShotDone
  526. DisableShot:
  527. stz shotArray, X
  528. ShotDone:
  529. ; TODO(mcmillen): in places where we .rept inx (etc), is it faster to use
  530. ; actual addition?
  531. .rept shotSize
  532. inx
  533. .endr
  534. cpx #(shotArrayLength * shotSize)
  535. bne UpdateShot
  536. ; Make the background scroll. Horizontal over time; vertical depending on
  537. ; player's y-coordinate.
  538. lda vBlankCounter
  539. sta BG3HOFS
  540. lda vBlankCounter + 1
  541. sta BG3HOFS
  542. lda playerY
  543. .rept 3
  544. lsr
  545. .endr
  546. sta BG3VOFS
  547. stz BG3VOFS
  548. rts
  549. UpdateSprites:
  550. ; Zero out the scratch space for the secondary sprite table.
  551. ldx #0
  552. -
  553. stz spriteTableScratchStart, X
  554. inx
  555. cpx #numSprites
  556. bne -
  557. ldx #0 ; Index into sprite table 1.
  558. ldy #0 ; Index into sprite table 2.
  559. ; Copy player coords into sprite table.
  560. lda playerX
  561. sta spriteTableStart, X
  562. lda playerY
  563. sta spriteTableStart + 1, X
  564. lda #0
  565. sta spriteTableStart + 2, X
  566. ; Set priority bits so that the sprite is drawn in front.
  567. lda #%00110000
  568. sta spriteTableStart + 3, X
  569. lda #%11000000 ; Enable large sprite.
  570. sta spriteTableScratchStart, Y
  571. .rept 4
  572. inx
  573. .endr
  574. iny
  575. ; Now add shots.
  576. sty $00 ; Save sprite table 2 index.
  577. ldy #0 ; Index into shotArray.
  578. -
  579. lda shotArray, Y
  580. cmp #0
  581. beq + ; If not enabled, skip to next shot.
  582. ; Update sprite table 1.
  583. sta spriteTableStart + 2, X ; sprite number
  584. lda shotArray + 1, Y
  585. sta spriteTableStart, X ; x
  586. lda shotArray + 2, Y
  587. sta spriteTableStart + 1, X ; y
  588. ; Update secondary sprite table.
  589. phy ; Save shotArray index.
  590. ldy $00
  591. lda #%11000000
  592. sta spriteTableScratchStart, Y
  593. iny
  594. sty $00
  595. ply ; Restore shotArrayIndex.
  596. .rept 4
  597. inx
  598. .endr
  599. +
  600. .rept shotSize
  601. iny
  602. .endr
  603. cpy #(shotArrayLength * shotSize)
  604. bne -
  605. ; Now clear out the unused entries in the sprite table.
  606. -
  607. cpx #spriteTable1Size
  608. beq +
  609. lda #1
  610. sta spriteTableStart, X
  611. .rept 4
  612. inx
  613. .endr
  614. +
  615. rts
  616. FillSecondarySpriteTable:
  617. ; The secondary sprite table wants 2 bits for each sprite: one to set the
  618. ; sprite's size, and one that's the high bit of the sprite's x-coordinate.
  619. ; It's annoying to deal with bitfields when thinking about business logic,
  620. ; so the spriteTableScratch array contains one byte for each sprite, in
  621. ; which the two most significant bits are the "size" and "upper x" bits.
  622. ; This function is meant to be called after UpdateWorld, and packs those
  623. ; bytes into the actual bitfield that the OAM wants for the secondary
  624. ; sprite table.
  625. ;
  626. ; The expected format of every byte in the scratch sprite table is:
  627. ; sx------ s = size (0 = small, 1 = large)
  628. ; x = flipped high x-coordinate (so 1 behaves like "enable").
  629. ldx #0 ; Index into input table.
  630. ldy #0 ; Index into output table.
  631. -
  632. stz $00 ; Current byte; filled out by a set of 4 input table entries.
  633. .rept 4
  634. ; For each byte, the lower-order bits correspond to the lower-numbered
  635. ; sprites; therefore we insert the current sprite's bits "at the top"
  636. ; and shift them right for each successive sprite.
  637. lsr $00
  638. lsr $00
  639. lda spriteTableScratchStart, X
  640. ora $00
  641. sta $00
  642. inx
  643. .endr
  644. lda $00
  645. eor #%01010101
  646. sta spriteTable2Start, Y
  647. iny
  648. cpx #numSprites
  649. bne -
  650. rts
  651. SetBackgroundColor:
  652. ; The background-color bytes are (R, G, B), each ranging from [0-31].
  653. ; The palette color format is 15-bit: [0bbbbbgg][gggrrrrr]
  654. ; Set the background color.
  655. ; Entry 0 corresponds to the SNES background color.
  656. stz CGADDR
  657. ; Compute and the low-order byte and store it in CGDATA.
  658. lda backgroundGreen
  659. .rept 5
  660. asl
  661. .endr
  662. ora backgroundRed
  663. sta CGDATA
  664. ; Compute the high-order byte and store it in CGDATA.
  665. lda backgroundBlue
  666. .rept 2
  667. asl
  668. .endr
  669. sta $00
  670. lda backgroundGreen
  671. .rept 3
  672. lsr
  673. .endr
  674. ora $00
  675. sta CGDATA
  676. rts
  677. VBlankHandler:
  678. jsr VBlankCounter
  679. jsr DMASpriteTables
  680. rti
  681. VBlankCounter:
  682. ; Increment a counter of how many VBlanks we've done.
  683. ; This is a 24-bit counter. At 60 vblanks/second, this will take
  684. ; 77 hours to wrap around; that's good enough for me :)
  685. inc vBlankCounter
  686. bne +
  687. inc vBlankCounter + 1
  688. bne +
  689. inc vBlankCounter + 2
  690. +
  691. rts
  692. DMASpriteTables:
  693. ; Store at the base OAM address.
  694. ldx #$0000
  695. stx OAMADDR
  696. ; Default DMA control; destination $2104 (OAM data register).
  697. stz DMA0CTRL
  698. lda #$04
  699. sta DMA0DST
  700. ; Our sprites start at $0100 in bank 0 and are #$220 bytes long.
  701. ldx #spriteTableStart
  702. stx DMA0SRC
  703. stz DMA0SRCBANK
  704. ldx #spriteTableSize
  705. stx DMA0SIZE
  706. ; Kick off the DMA transfer.
  707. lda #%00000001
  708. sta DMAENABLE
  709. rts
  710. .ENDS
  711. ; Bank 1 is used for our graphics assets.
  712. .BANK 1 SLOT 0
  713. .ORG 0
  714. .SECTION "GraphicsData"
  715. SpriteData:
  716. .INCBIN "sprites32.pic"
  717. SpritePalette:
  718. .INCBIN "sprites32.clr"
  719. TileData:
  720. .INCBIN "tiles.pic"
  721. TilePalette:
  722. .INCBIN "tiles.clr"
  723. .ENDS
  724. ; Fill an entire bank with random numbers.
  725. .SEED 1
  726. .BANK 2 SLOT 0
  727. .ORG 0
  728. .SECTION "RandomBytes"
  729. .DBRND 32 * 1024, 0, 255
  730. .ENDS