********************************************************************************
* Caverns of Freitag, by David Shapiro *
* Copyright 1982 *
* *
* Disassembly of "SB2". *
********************************************************************************
* Fireworks display, shown when the game is won. Uses GRAPHIX II to draw *
* particles. *
* *
* (This is one of the best hi-res fireworks displays I've seen.) *
********************************************************************************
* Disassembly by Andy McFadden, using 6502bench SourceGen v1.5. *
* Last updated 2020/01/26 *
********************************************************************************
ptr .eq $08 {addr/2}
ARG_DRAW_MODE .eq $1c ;0=draw, 1=erase, 2=invert
ARG_COLOR .eq $1f ;0=green, 1=orange, 2=purple, 3=blue
ptr_x0 .eq $40 {addr/2}
ptr_y0 .eq $42 {addr/2}
ptr_deltax .eq $44 {addr/2}
ptr_deltay .eq $46 {addr/2}
state_ptr48 .eq $48 {addr/2}
ptr_color .eq $4a {addr/2}
ARG_X0 .eq $f9 {addr/2} ;X coord #0 (0-255 or 0-139)
ARG_Y0 .eq $fb ;Y coord #0 (0-191)
ARG_FF .eq $ff ;shape fill flag -or- CLR color
center_x .eq $035c
center_y .eq $035d
saved_xreg .eq $035e
particle_index .eq $035f
counter_24bit .eq $0360 {addr/3}
update_cadence .eq $0363 {addr/5}
max_particle_count .eq $0368
next_particle_index .eq $0369 {addr/5}
next_explosion_ctr .eq $036e
always_one .eq $036f
CLR .eq $080f ;clear the screen
CDOT .eq $082a ;plot a colored pixel (140 mode)
storage .eq $9000 {addr/2304}
KBD .eq $c000 ;R last key pressed + 128
KBDSTRB .eq $c010 ;RW keyboard strobe
SPKR .eq $c030 ;RW toggle speaker
TXTCLR .eq $c050 ;RW display graphics
MIXSET .eq $c053 ;RW display split screen
HIRES .eq $c057 ;RW display hi-res graphics
.org $7a00
7a00: a9 00 lda #$00
7a02: aa tax
7a03: 2c 50 c0 bit TXTCLR ;enable hi-res graphics, mixed-mode
7a06: 2c 57 c0 bit HIRES
7a09: 2c 53 c0 bit MIXSET
7a0c: a9 00 lda #$00
7a0e: 85 ff sta ARG_FF
7a10: 20 0f 08 jsr CLR ;clear screen to black
; Init data area ($9000-99ff) to zeroes.
7a13: a9 00 lda #<storage
7a15: 85 08 sta ptr
7a17: a9 90 lda #>storage
7a19: 85 09 sta ptr+1
7a1b: a2 0a ldx #$0a
7a1d: a0 00 :ClrLoop1 ldy #$00
7a1f: 98 tya
7a20: 91 08 :ClrLoop2 sta (ptr),y
7a22: c8 iny
7a23: d0 fb bne :ClrLoop2
7a25: e6 09 inc ptr+1
7a27: ca dex
7a28: d0 f3 bne :ClrLoop1
; Zero out $360-364 and $369-36D. (This erases part of LBOLT.)
7a2a: a9 00 :ClrLoop3 lda #$00 ;note X=0
7a2c: 9d 69 03 sta next_particle_index,x
7a2f: 9d 60 03 sta counter_24bit,x
7a32: e8 inx
7a33: e0 05 cpx #$05
7a35: d0 f3 bne :ClrLoop3
; Copy data from 7E4C-7E51 to $363-367.
7a37: a2 00 ldx #$00
7a39: bd 4c 7e :CopyLoop lda move_cadences,x
7a3c: 9d 63 03 sta update_cadence,x
7a3f: e8 inx
7a40: e0 05 cpx #$05
7a42: d0 f5 bne :CopyLoop
; Save $40-4e so we can use it for variable storage.
7a44: a2 0e ldx #$0e
7a46: b5 40 :PreserveLoop lda ptr_x0,x
7a48: 9d 45 7d sta saved_dp_40,x
7a4b: ca dex
7a4c: 10 f8 bpl :PreserveLoop
7a4e: 8d 10 c0 sta KBDSTRB ;clear pending key
;
; It works like this:
; - Pick a random center point and a color for the explosion.
; - Start generating particles from that center point.
; - Every M cycles, move particles (M varies by particle set).
; - After N cycles, start a new firework.
;
; Existing particles continue to move until the explosion gets overwritten by a
; new one. They disappear when something else occupies their slot. The CDOT
; code performs clipping at screen boundaries.
;
; There are five particle sets. New particles pick a number from 0-23, then get
; a movement vector and a set number from a 24-entry table.
;
7a51: a9 01 OuterLoop lda #$01
7a53: 8d 6f 03 sta always_one
; Pick random values for firework position and inter-explosion delay.
7a56: a9 60 lda #96
7a58: 20 c5 7c jsr GetRandomLtN ;get 0-95
7a5b: 09 23 ora #$23 ;provide minimum duration
7a5d: 8d 6e 03 sta next_explosion_ctr
7a60: a9 64 lda #100
7a62: 20 c5 7c jsr GetRandomLtN ;get 0-99
7a65: 18 clc
7a66: 69 14 adc #20 ;now 20-119 (out of 0-139)
7a68: 8d 5c 03 sta center_x
7a6b: a9 78 lda #120
7a6d: 20 c5 7c jsr GetRandomLtN ;get 0-119
7a70: 18 clc
7a71: 69 14 adc #20 ;now 20-139 (out of 0-159)
7a73: 8d 5d 03 sta center_y
7a76: ce c4 7c dec next_color ;change color
7a79: 10 05 bpl :InnerLoop ;did it go negative?
7a7b: a9 03 lda #$03 ;yes, reset to 3
7a7d: 8d c4 7c sta next_color
; Fun fact: if you change $7A81 to $00, particles shoot out straight
; horizontally or vertically.
7a80: a2 04 :InnerLoop ldx #$04 ;for each of the 5 sets
7a82: de 63 03 :FworkLoop dec update_cadence,x ;time to move particles?
7a85: d0 0f bne :NoMove ;not yet
7a87: 8e 5e 03 stx saved_xreg ;preserve this
7a8a: 20 ce 7a jsr UpdateParticles ;move particles in the set
7a8d: ae 5e 03 ldx saved_xreg
7a90: bd 4c 7e lda move_cadences,x ;reset state
7a93: 9d 63 03 sta update_cadence,x
7a96: ca :NoMove dex
7a97: 10 e9 bpl :FworkLoop
7a99: ee 60 03 inc counter_24bit ;increment 24-bit counter
7a9c: d0 08 bne :NoInc24 ;(not actually used for anything?)
7a9e: ee 61 03 inc counter_24bit+1
7aa1: d0 03 bne :NoInc24
7aa3: ee 62 03 inc counter_24bit+2
7aa6: ce 6e 03 :NoInc24 dec next_explosion_ctr ;update explosion countdown
7aa9: ad 6e 03 lda next_explosion_ctr
7aac: c9 ff cmp #$ff ;time for a new explosion?
7aae: d0 08 bne :CheckKbd ;not yet
7ab0: ce 6f 03 dec always_one ;looks like this is always one
7ab3: d0 03 bne :CheckKbd ; so this branch is never taken
7ab5: 4c 51 7a jmp OuterLoop ;go make new explosion
7ab8: 20 6f 7b :CheckKbd jsr CreateParticle
7abb: ad 00 c0 lda KBD ;key hit?
7abe: 10 0b bpl :NoKeyHit ;no, branch
; Restore $40-4e.
7ac0: a2 0e ldx #$0e
7ac2: bd 45 7d :RestoreLoop lda saved_dp_40,x
7ac5: 95 40 sta ptr_x0,x
7ac7: ca dex
7ac8: 10 f8 bpl :RestoreLoop
7aca: 60 rts
7acb: 4c 80 7a :NoKeyHit jmp :InnerLoop
; Moves particles in one firework set.
;
; On entry:
; X-reg: firework set (0-4)
7ace: bd 40 7c UpdateParticles lda addr0_lo,x ;extract pointers into DP
7ad1: 85 40 sta ptr_x0
7ad3: bd 45 7c lda addr0_hi,x
7ad6: 85 41 sta ptr_x0+1
7ad8: bd 4a 7c lda addr1_lo,x
7adb: 85 42 sta ptr_y0
7add: bd 4f 7c lda addr1_hi,x
7ae0: 85 43 sta ptr_y0+1
7ae2: bd 54 7c lda addr2_lo,x
7ae5: 85 44 sta ptr_deltax
7ae7: bd 59 7c lda addr2_hi,x
7aea: 85 45 sta ptr_deltax+1
7aec: bd 5e 7c lda addr3_lo,x
7aef: 85 46 sta ptr_deltay
7af1: bd 63 7c lda addr3_hi,x
7af4: 85 47 sta ptr_deltay+1
7af6: bd 68 7c lda addr4_lo,x ;unused?
7af9: 85 48 sta state_ptr48
7afb: bd 6d 7c lda addr4_hi,x
7afe: 85 49 sta state_ptr48+1
7b00: bd 72 7c lda addr5_lo,x
7b03: 85 4a sta ptr_color
7b05: bd 77 7c lda addr5_hi,x
7b08: 85 4b sta ptr_color+1
7b0a: bd 45 7e lda max_particle_counts,x
7b0d: 8d 68 03 sta max_particle_count
7b10: bd 69 03 lda next_particle_index,x
7b13: 18 clc
7b14: 69 01 adc #$01
7b16: cd 68 03 cmp max_particle_count ;reached limit?
7b19: d0 02 bne :NotLimit ;not yet
7b1b: a9 00 lda #$00 ;reset to zero
7b1d: 8d 5f 03 :NotLimit sta particle_index
7b20: a8 tay
7b21: b1 40 :DrawLoop lda (ptr_x0),y ;get current X0/Y0
7b23: 85 f9 sta ARG_X0
7b25: b1 42 lda (ptr_y0),y
7b27: 85 fb sta ARG_Y0
7b29: a9 01 lda #$01
7b2b: 85 1c sta ARG_DRAW_MODE ;mode=erase
7b2d: b1 4a lda (ptr_color),y
7b2f: 85 1f sta ARG_COLOR
7b31: 20 2a 08 jsr CDOT ;erase dot
7b34: ac 5f 03 ldy particle_index
7b37: b1 40 lda (ptr_x0),y ;update X0 position
7b39: 18 clc
7b3a: 71 44 adc (ptr_deltax),y
7b3c: 91 40 sta (ptr_x0),y
7b3e: 85 f9 sta ARG_X0
7b40: b1 42 lda (ptr_y0),y ;update Y0 position
7b42: 18 clc
7b43: 71 46 adc (ptr_deltay),y
7b45: 91 42 sta (ptr_y0),y
7b47: 85 fb sta ARG_Y0
7b49: a9 00 lda #$00
7b4b: 85 1c sta ARG_DRAW_MODE ;mode=draw
7b4d: b1 4a lda (ptr_color),y
7b4f: 20 2a 08 jsr CDOT ;draw dot
7b52: ac 5f 03 ldy particle_index ;increment counter
7b55: c8 iny
7b56: cc 68 03 cpy max_particle_count ;reached end?
7b59: d0 02 bne :NotDone ;not yet
7b5b: a0 00 ldy #$00 ;reset to zero
7b5d: 8c 5f 03 :NotDone sty particle_index
7b60: ae 5e 03 ldx saved_xreg
7b63: bd 69 03 lda next_particle_index,x
7b66: cd 5f 03 cmp particle_index
7b69: f0 03 beq :Done
7b6b: 4c 21 7b jmp :DrawLoop
7b6e: 60 :Done rts
; Creates a new particle, with random velocity, from the current explosion
; center.
7b6f: 20 db 7c CreateParticle jsr GetRandom ;get random value 0-255
7b72: 29 1f and #$1f ;clamp to 0-31
7b74: c9 18 cmp #24 ;is it 0-23?
7b76: b0 f7 bcs CreateParticle ;no, retry
; Get randomly selected deltaX/deltaY and particle sets.
;
; Particle movement is not purely random. The possible movements are defined in
; tables, but the table entry is chosen at random.
;
; One consequence is that particles in certain sets move in certain ways. For
; example, particles in set 0 move vertically or horizontally. This is why
; setting 7A81:00 limits particle movement: we're only moving the particles in
; set 0.
7b78: aa tax
7b79: bd 7c 7c lda delta_x_24,x ;fe/ff/00/01/02
7b7c: 8d 51 7e sta cur_delta_x
7b7f: bd 94 7c lda delta_y_24,x ;fe/ff/00/01/02
7b82: 8d 52 7e sta cur_delta_y
7b85: bd ac 7c lda setindex_24,x ;particle set (0-4)
7b88: aa tax
7b89: 8e 5e 03 stx saved_xreg
; set up pointers based on state index (0-4)
7b8c: bd 40 7c lda addr0_lo,x
7b8f: 85 40 sta ptr_x0
7b91: bd 45 7c lda addr0_hi,x
7b94: 85 41 sta ptr_x0+1
7b96: bd 4a 7c lda addr1_lo,x
7b99: 85 42 sta ptr_y0
7b9b: bd 4f 7c lda addr1_hi,x
7b9e: 85 43 sta ptr_y0+1
7ba0: bd 54 7c lda addr2_lo,x
7ba3: 85 44 sta ptr_deltax
7ba5: bd 59 7c lda addr2_hi,x
7ba8: 85 45 sta ptr_deltax+1
7baa: bd 5e 7c lda addr3_lo,x
7bad: 85 46 sta ptr_deltay
7baf: bd 63 7c lda addr3_hi,x
7bb2: 85 47 sta ptr_deltay+1
7bb4: bd 68 7c lda addr4_lo,x ;unused?
7bb7: 85 48 sta state_ptr48
7bb9: bd 6d 7c lda addr4_hi,x
7bbc: 85 49 sta state_ptr48+1
7bbe: bd 72 7c lda addr5_lo,x
7bc1: 85 4a sta ptr_color
7bc3: bd 77 7c lda addr5_hi,x
7bc6: 85 4b sta ptr_color+1
7bc8: bd 45 7e lda max_particle_counts,x
7bcb: 8d 68 03 sta max_particle_count
7bce: bd 69 03 lda next_particle_index,x
7bd1: 8d 5f 03 sta particle_index
; erase the dot we're replacing
7bd4: a8 tay
7bd5: b1 40 lda (ptr_x0),y
7bd7: 85 f9 sta ARG_X0
7bd9: b1 42 lda (ptr_y0),y
7bdb: 85 fb sta ARG_Y0
7bdd: b1 4a lda (ptr_color),y
7bdf: 85 1f sta ARG_COLOR
7be1: a9 01 lda #$01
7be3: 85 1c sta ARG_DRAW_MODE ;mode=erase
7be5: 20 2a 08 jsr CDOT
7be8: ae 5e 03 ldx saved_xreg ;(not used?)
7beb: ac 5f 03 ldy particle_index
7bee: ad 51 7e lda cur_delta_x ;set up deltaX/deltaY
7bf1: 91 44 sta (ptr_deltax),y ; with values generated randomly earlier
7bf3: ad 52 7e lda cur_delta_y
7bf6: 91 46 sta (ptr_deltay),y
7bf8: ad c4 7c lda next_color ;set color
7bfb: 91 4a sta (ptr_color),y
7bfd: 85 1f sta ARG_COLOR
7bff: 2c 30 c0 bit SPKR ;click
7c02: ce 65 7e dec nine_count
7c05: 10 05 bpl :NotNeg
7c07: a9 08 lda #$08 ;reset counter
7c09: 8d 65 7e sta nine_count
; Set X0/Y0 to center + value from delta_x_tab/delta_y_tab. The initial
; positions are offset in a 9-point diamond pattern.
7c0c: ae 65 7e :NotNeg ldx nine_count ;rotate through 0-8
7c0f: ad 5c 03 lda center_x
7c12: 18 clc
7c13: 7d 53 7e adc delta_x_tab,x
7c16: 91 40 sta (ptr_x0),y
7c18: 85 f9 sta ARG_X0
7c1a: ad 5d 03 lda center_y
7c1d: 18 clc
7c1e: 7d 5c 7e adc delta_y_tab,x
7c21: 91 42 sta (ptr_y0),y
7c23: 85 fb sta ARG_Y0
7c25: a9 00 lda #$00
7c27: 85 1c sta ARG_DRAW_MODE ;mode=draw
7c29: 20 2a 08 jsr CDOT
7c2c: ae 5e 03 ldx saved_xreg ;firework index
7c2f: bd 69 03 lda next_particle_index,x ;update particle index
7c32: 18 clc
7c33: 69 01 adc #$01
7c35: cd 68 03 cmp max_particle_count ;reached limit?
7c38: d0 02 bne :MoreToGo ;not yet
7c3a: a9 00 lda #$00 ;wrap back to zero
7c3c: 9d 69 03 :MoreToGo sta next_particle_index,x
7c3f: 60 rts
; Five sets of 6 addresses, all pointing into $9xxx. These hold x0, y0, deltaX,
; deltaY, color, and something that isn't used.
;
; Each address is +62 or +124 bytes from the previous, suggesting 5 sets with up
; to 62 particles per set.
7c40: 00 74 e8 5c+ addr0_lo .bulk 0074e85cd0
7c45: 90 91 92 94+ addr0_hi .bulk 9091929495
7c4a: 3e b2 26 9a+ addr1_lo .bulk 3eb2269a4c
7c4f: 90 91 93 94+ addr1_hi .bulk 9091939496
7c54: 7c f0 64 d8+ addr2_lo .bulk 7cf064d8c8
7c59: 90 91 93 94+ addr2_hi .bulk 9091939496
7c5e: ba 2e a2 16+ addr3_lo .bulk ba2ea21644
7c63: 90 92 93 95+ addr3_hi .bulk 9092939597
7c68: f8 6c e0 54+ addr4_lo .bulk f86ce054c0
7c6d: 90 92 93 95+ addr4_hi .bulk 9092939597
7c72: 36 aa 1e 92+ addr5_lo .bulk 36aa1e923c
7c77: 91 92 94 95+ addr5_hi .bulk 9192949598
; 24 sets of stuff
7c7c: fe ff 00 01+ delta_x_24 .bulk feff000102feff000102feff0102feff000102feff000102
7c94: fe fe fe fe+ delta_y_24 .bulk fefefefefeffffffffff0000000001010101010202020202
7cac: 03 04 02 04+ setindex_24 .bulk 030402040304010001040200000204010001040304020403
7cc4: 00 next_color .dd1 $00 ;(0-3)
; Gets a random number in the range 0 <= value < max.
;
; (If you pass $10 as the limit, it will quickly lock up. $20 seems fine.
; Apparently the RNG could use some work.)
;
; On entry:
; A-reg: max value
;
; On exit:
; A-reg: value
7cc5: 8d 40 7d GetRandomLtN sta tmp_arg ;store arg
7cc8: 20 db 7c :Loop jsr GetRandom
7ccb: 8d 41 7d sta tmp_result ;store result
7cce: ad 41 7d lda tmp_result ;get result (redundant)
7cd1: 38 sec
7cd2: ed 40 7d sbc tmp_arg ;is arg >= result?
7cd5: b0 f1 bcs :Loop ;yes, try again
7cd7: ad 41 7d lda tmp_result ;load result
7cda: 60 rts
; Generate a random number.
;
; On exit:
; A-reg: random value (0-255)
7cdb: ad 3e 7d GetRandom lda rand4
7cde: 29 05 and #$05
7ce0: a8 tay
7ce1: a9 34 lda #52
7ce3: f9 3a 7d sbc rand0,y
7ce6: 8d 3a 7d sta rand0
7ce9: ad 3c 7d lda rand2
7cec: 4d 3f 7d eor rand5
7cef: 8d 3f 7d sta rand5
7cf2: 4e 3a 7d lsr rand0
7cf5: 6e 3b 7d ror rand1
7cf8: 6e 3c 7d ror rand2
7cfb: 6e 3d 7d ror rand3
7cfe: 6e 3e 7d ror rand4
7d01: 4e 3f 7d lsr rand5
7d04: 90 30 bcc L7D36
7d06: ad 3a 7d lda rand0
7d09: 49 ff eor #$ff
7d0b: 8d 3a 7d sta rand0
7d0e: ad 3b 7d lda rand1
7d11: 49 ff eor #$ff
7d13: 8d 3b 7d sta rand1
7d16: ad 3c 7d lda rand2
7d19: 49 ff eor #$ff
7d1b: 8d 3c 7d sta rand2
7d1e: ad 3d 7d lda rand3
7d21: 49 ff eor #$ff
7d23: 8d 3d 7d sta rand3
7d26: ad 3e 7d lda rand4
7d29: 49 ff eor #$ff
7d2b: 8d 3e 7d sta rand4
7d2e: ad 3f 7d lda rand5
7d31: 49 ff eor #$ff
7d33: 8d 3f 7d sta rand5
7d36: ad 3d 7d L7D36 lda rand3
7d39: 60 rts
; 48-bit RNG state
7d3a: 0c rand0 .dd1 $0c
7d3b: 62 rand1 .dd1 $62
7d3c: 49 rand2 .dd1 $49
7d3d: ff rand3 .dd1 $ff
7d3e: 11 rand4 .dd1 $11
7d3f: 00 rand5 .dd1 $00
; temp storage
7d40: 00 tmp_arg .dd1 $00
7d41: 00 tmp_result .dd1 $00
7d42: 00 00 00 .junk 3
7d45: 00 00 00 00+ saved_dp_40 .fill 15,$00
7d54: 00 00 00 00+ .junk 237
7e41: 1e 1e 1e 1e .junk 4
max_particle_counts
7e45: 1e 1e 1e 1e+ .bulk 1e1e1e1e3c
7e4a: 1e 0a .junk 2
; Every M iterations we move the particles in set N (0-4). This determines how
; often they move.
7e4c: 05 07 0a 0b+ move_cadences .bulk 05070a0b0e
7e51: 01 cur_delta_x .dd1 $01
7e52: fe cur_delta_y .dd1 $fe
; This yields 9 dots in a diamond pattern:
; *
; * *
; * * *
; * *
; *
7e53: 00 ff 01 fe+ delta_x_tab .bulk 00ff01fe0002ff0100
7e5c: fe ff ff 00+ delta_y_tab .bulk feffff000000010102
7e65: 04 nine_count .dd1 $04 ;0-9
No exported symbols found.