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.

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