********************************************************************************
* Caverns of Freitag, by David Shapiro *
* Copyright 1982 *
* *
* Disassembly of "CF.OBJ". *
********************************************************************************
* This does all of the heavy lifting for the game, including: *
* - Initial generation of monsters and chests. *
* - Movement and attacks of all monsters. *
* - Special attacks (wizard zap, dragon breath). *
* - Rendering of mode 1 (hi-res), mode 2 (text mini-map), and mode 3 (text *
* map). *
* - Sound effects. *
* *
* The actual rendering on the hi-res screen is done with calls to GRAPHIX II. *
********************************************************************************
* Disassembly by Andy McFadden, using 6502bench SourceGen v1.5. *
* Last updated 2020/01/26 *
********************************************************************************
ptr1 .eq $06 {addr/2}
ptr2 .eq $08 {addr/2}
ARG_DRAW_MODE .eq $1c ;0=draw, 1=erase, 2=invert
ARG_ITEM_INDEX .eq $1e ;LINESET/CHARSET item index
ARG_COLOR .eq $1f ;0=green, 1=orange, 2=purple, 3=blue
MON_RNDL .eq $4e ;low byte of KEYIN "random" value
ARG_EXT_PTR .eq $e8 {addr/2} ;pointer to external data
ARG_X0 .eq $f9 {addr/2} ;X coord #0 (0-255 or 0-139)
ARG_Y0 .eq $fb ;Y coord #0 (0-191)
ARG_X1 .eq $fc {addr/2} ;X coord #1 (0-255 or 0-139)
ARG_Y1 .eq $fe ;Y coord #1 (0-191)
ARG_FF .eq $ff ;shape fill flag -or- CLR color
win_left .eq $0300 ;CX
win_top .eq $0301
player_win_x .eq $0302 ;position within window (AX) (0-9)
player_win_y .eq $0303 ;position within window (AY) (0-8)
display_mode .eq $0304
monster_map_x .eq $0305
monster_map_y .eq $0306
turn_timer .eq $0307
keyval_or_atk .eq $0308 ;key hit -or- monster attack flag
player_map_x .eq $0309 ;(0-79)
player_map_y .eq $030a ;(0-79)
monster_move_x .eq $030b
monster_move_y .eq $030c
monster_count .eq $030d
mon_attack_count .eq $030e ;number of monsters that attacked player (0-4)
mon_attack_id0 .eq $030f
mon_attack_id1 .eq $0310
mon_attack_id2 .eq $0311
mon_attack_id3 .eq $0312
sound_pitch .eq $0313 {addr/2}
sound_duration .eq $0315 {addr/2}
sound_pitch_adj .eq $0317 {addr/2}
turn_speed .eq $0319 ;speed - 1 (0-49)
skill_level_plus_4 .eq $031a ;Skill level + 4 (5-13)
atk_wiz1_x .eq $031b ;window X coord of 1st attacking wizard
atk_wiz1_y .eq $031c ;window Y coord of 1st attacking wizard
atk_wiz2_x .eq $031d ;window X coord of 2nd attacking wizard
atk_wiz2_y .eq $031e ;window Y coord of 2nd attacking wizard
hit_by_flame .eq $031f
dragon_breath_ready .eq $0320 ;zero when dragon ready to breathe
col_counter .eq $0323 {addr/1}
row_counter .eq $0324
map_row_addr .eq $0325 {addr/2}
row_tmp .eq $0327 {addr/2}
player_xc .eq $0329
player_yc .eq $032a
monster_move_tmp .eq $032b
mon_taxicab_dist .eq $032d
CHARSET .eq $081e ;draw dots from bitmap
BDRW .eq $0821 ;blit hi-res bitmap
CBOX .eq $0833 ;draw a colored box
monster_x .eq $4000 {addr/170}
monster_y .eq $40aa {addr/170}
monster_type .eq $4154 {addr/170}
sketchz_tiles .eq $7fc0 ;start of tile bitmap data
wall_tile .eq $8014 {addr/84} ;gfx inverted near dragon
map .eq $a000 {addr/6400}
bad_breath_data .eq $bc00 {addr/512}
KBD .eq $c000 ;R last key pressed + 128
KBDSTRB .eq $c010 ;RW keyboard strobe
SPKR .eq $c030 ;RW toggle speaker
.org $6d00
6d00: 4c 30 6d jmp DrawHiRes ;called from line 10000
6d03: 4c d1 6d jmp DrawMiniMap ;called from line 10210
6d06: 4c 3e 6e jmp DrawTextMap ;called from line 11000
6d09: 4c 01 6f jmp UpdateOneTile ;called from line 12000
6d0c: 4c 86 6f jmp WaitForKey ;called from line 20000
6d0f: 4c 60 70 jmp MoveMonsters ;called from line 30010
6d12: 4c db 74 jmp CreateMonsters ;called from lines 55-56 (also creates chests)
6d15: 4c 49 75 jmp Copy84Bytes ;called from line 16000
6d18: 4c 53 75 jmp Invert8014
6d1b: 4c 67 75 jmp CleanMap ;init; called from line 2
6d1e: 4c 87 75 jmp MoveMap ;init; called from line 2
6d21: 4c b1 75 jmp PlayTone
6d24: 4c 02 76 jmp PlayFuneral
6d27: 4c 35 76 jmp PlayIndexedTone
6d2a: 4c 5f 76 jmp PlayIndexedDualTone
6d2d: 4c 99 76 jmp FindMonsterIndex ;called from 20255, 54020
********************************************************************************
* Draws the mode 1 hi-res tiles. *
* *
* On entry: *
* $300: CX (map center X coord) *
* $301: CY (map center Y coord) *
********************************************************************************
]data_ptr .var $e8 {addr/2}
]tmp1 .var $f9 {addr/1}
]tmp2 .var $fb {addr/1}
6d30: a5 4e DrawHiRes lda MON_RNDL
6d32: 8d 47 77 sta random_value
6d35: ad 00 03 lda win_left ;get CX
6d38: c9 32 cmp #50 ;less than 50?
6d3a: 90 12 bcc :NotBotRight ;yes
6d3c: ad 01 03 lda win_top ;check CY
6d3f: c9 33 cmp #51 ;less than 51?
6d41: 90 0b bcc :NotBotRight ;yes
6d43: ad 40 80 lda wall_tile+44 ;bottom right; see if walls are already orange
6d46: 30 0e bmi :Cont ;yes, nothing to do
6d48: 20 53 75 jsr Invert8014 ;no, make them orange
6d4b: 4c 56 6d jmp :Cont
6d4e: ad 40 80 :NotBotRight lda wall_tile+44 ;are walls purple?
6d51: 10 03 bpl :Cont ;yes?
6d53: 20 53 75 jsr Invert8014 ;no, invert them
6d56: a9 00 :Cont lda #$00 ;init row counter (0-8)
6d58: 8d 24 03 sta row_counter
6d5b: ad 01 03 lda win_top ;get CY
6d5e: 20 ea 76 jsr GetMapRowAddr ;get row address
; loop for 9 rows
6d61: a9 09 :Loop2 lda #$09 ;init col counter (0-9)
6d63: 8d 23 03 sta col_counter
; loop for 10 columns
;
; compute col * 4
6d66: ad 23 03 :Loop1 lda col_counter
6d69: 0a asl A
6d6a: 0a asl A
6d6b: 85 f9 sta ]tmp1
; compute row * 21
6d6d: ad 24 03 lda row_counter
6d70: 0a asl A ;x2
6d71: 0a asl A ;x4
6d72: 85 fb sta ]tmp2
6d74: 0a asl A ;x8
6d75: 0a asl A ;x16
6d76: 18 clc
6d77: 65 fb adc ]tmp2 ;x16+x4 = x20
6d79: 6d 24 03 adc row_counter ;x20+x1 = x21
6d7c: 85 fb sta ]tmp2
; set data ptr to map address
6d7e: ad 25 03 lda map_row_addr ;current addr is $A000 + CY*80
6d81: 18 clc
6d82: 6d 00 03 adc win_left ;factor in CX
6d85: 85 e8 sta ]data_ptr
6d87: ad 26 03 lda map_row_addr+1
6d8a: 69 00 adc #$00
6d8c: 85 e9 sta ]data_ptr+1
6d8e: a5 e8 lda ]data_ptr
6d90: 18 clc
6d91: 6d 23 03 adc col_counter
6d94: 85 e8 sta ]data_ptr
6d96: a5 e9 lda ]data_ptr+1
6d98: 69 00 adc #$00
6d9a: 85 e9 sta ]data_ptr+1
6d9c: a0 00 ldy #$00
6d9e: b1 e8 lda (]data_ptr),y
6da0: 85 1e sta ARG_ITEM_INDEX
6da2: a9 c0 lda #<sketchz_tiles
6da4: 85 e8 sta ]data_ptr
6da6: a9 7f lda #>sketchz_tiles
6da8: 85 e9 sta ]data_ptr+1
6daa: 20 21 08 jsr BDRW
6dad: ce 23 03 dec col_counter
6db0: 10 b4 bpl :Loop1
6db2: ee 24 03 inc row_counter ;update row counter
6db5: ad 25 03 lda map_row_addr ;row += 80
6db8: 18 clc
6db9: 69 50 adc #80
6dbb: 8d 25 03 sta map_row_addr
6dbe: ad 26 03 lda map_row_addr+1
6dc1: 69 00 adc #$00
6dc3: 8d 26 03 sta map_row_addr+1
6dc6: ad 24 03 lda row_counter ;check row counter
6dc9: c9 09 cmp #$09 ;all done?
6dcb: f0 03 beq :Done ;yup, bail
6dcd: 4c 61 6d jmp :Loop2
6dd0: 60 :Done rts
********************************************************************************
* Draws the mode 2 text mini-map. *
* *
* On entry: *
* $300: CX (map center X coord) *
* $301: CY (map center Y coord) *
********************************************************************************
6dd1: a9 00 DrawMiniMap lda #$00
6dd3: 8d 24 03 sta row_counter
6dd6: ad 01 03 lda win_top
6dd9: 20 ea 76 jsr GetMapRowAddr ;get map address
6ddc: a9 09 :RowLoop lda #$09 ;10 across
6dde: 8d 23 03 sta col_counter
6de1: ae 24 03 :ColLoop ldx row_counter
6de4: bd 4b 77 lda minimap_addr_lo,x ;get text screen addr
6de7: 18 clc
6de8: 6d 23 03 adc col_counter ;add column
6deb: 85 06 sta ptr1 ;set dst pointer
6ded: bd 54 77 lda minimap_addr_hi,x
6df0: 85 07 sta ptr1+1
6df2: ad 25 03 lda map_row_addr ;set up src pointer
6df5: 18 clc
6df6: 6d 00 03 adc win_left ;map row += win left coord
6df9: 85 e8 sta ]data_ptr
6dfb: ad 26 03 lda map_row_addr+1
6dfe: 69 00 adc #$00
6e00: 85 e9 sta ]data_ptr+1
6e02: a5 e8 lda ]data_ptr ;+= current column
6e04: 18 clc
6e05: 6d 23 03 adc col_counter
6e08: 85 e8 sta ]data_ptr
6e0a: a5 e9 lda ]data_ptr+1
6e0c: 69 00 adc #$00
6e0e: 85 e9 sta ]data_ptr+1
; draw map char
6e10: a0 00 ldy #$00
6e12: b1 e8 lda (]data_ptr),y ;get src value
6e14: aa tax
6e15: bd 5d 77 lda minimap_chars,x ;look up char
6e18: 91 06 sta (ptr1),y ;store directly on screen (may be inv/flash)
6e1a: ce 23 03 dec col_counter ;move to next column
6e1d: 10 c2 bpl :ColLoop ;not < 0, continue
6e1f: ee 24 03 inc row_counter ;move to next row
6e22: ad 25 03 lda map_row_addr ;update map ptr
6e25: 18 clc
6e26: 69 50 adc #80 ;map width
6e28: 8d 25 03 sta map_row_addr
6e2b: ad 26 03 lda map_row_addr+1
6e2e: 69 00 adc #$00
6e30: 8d 26 03 sta map_row_addr+1
6e33: ad 24 03 lda row_counter ;check row counter
6e36: c9 09 cmp #$09 ;are we done?
6e38: f0 03 beq :Done
6e3a: 4c dc 6d jmp :RowLoop ;no, keep going
6e3d: 60 :Done rts
********************************************************************************
* Draws the mode 3 full-screen text map. *
* *
* On entry: *
* $300: CX (map center X coord) *
* $301: CY (map center Y coord) *
********************************************************************************
6e3e: ad 00 03 DrawTextMap lda win_left ;compute player map coordinates
6e41: 18 clc
6e42: 6d 02 03 adc player_win_x
6e45: 8d 29 03 sta player_xc
6e48: ad 01 03 lda win_top
6e4b: 18 clc
6e4c: 6d 03 03 adc player_win_y
6e4f: 8d 2a 03 sta player_yc
6e52: ad 29 03 lda player_xc ;center player on map
6e55: 38 sec
6e56: e9 14 sbc #20
6e58: 8d 29 03 sta player_xc
6e5b: ad 2a 03 lda player_yc
6e5e: 38 sec
6e5f: e9 0c sbc #12
6e61: 8d 2a 03 sta player_yc
6e64: ad 29 03 lda player_xc ;range-check X
6e67: 10 05 bpl :XLeftOk
6e69: a9 00 lda #$00 ;clamp
6e6b: 8d 29 03 sta player_xc
6e6e: c9 29 :XLeftOk cmp #41
6e70: 90 05 bcc :XRightOk
6e72: a9 28 lda #40 ;clamp (80-40 = 40)
6e74: 8d 29 03 sta player_xc
6e77: ad 2a 03 :XRightOk lda player_yc ;range-check Y
6e7a: 10 05 bpl :YTopOk
6e7c: a9 00 lda #$00 ;clamp
6e7e: 8d 2a 03 sta player_yc
6e81: c9 39 :YTopOk cmp #57
6e83: 90 05 bcc :YBotOk
6e85: a9 38 lda #56 ;clamp (80 - 24 = 56)
6e87: 8d 2a 03 sta player_yc
; draw
6e8a: a9 00 :YBotOk lda #$00 ;start at the top
6e8c: 8d 24 03 sta row_counter
6e8f: ad 2a 03 lda player_yc ;get player Y coord
6e92: 20 ea 76 jsr GetMapRowAddr ;get map addr for that row
6e95: a9 27 :RowLoop lda #39
6e97: 8d 23 03 sta col_counter ;draw 40 columns
6e9a: ae 24 03 :ColLoop ldx row_counter
6e9d: bd 85 77 lda text1_addr_lo,x ;setup ptr into text page 1
6ea0: 18 clc
6ea1: 6d 23 03 adc col_counter
6ea4: 85 06 sta ptr1
6ea6: bd 9d 77 lda text1_addr_hi,x
6ea9: 85 07 sta ptr1+1
6eab: ad 25 03 lda map_row_addr ;setup map pointer
6eae: 18 clc
6eaf: 6d 29 03 adc player_xc ;adding player X coord
6eb2: 85 e8 sta ]data_ptr
6eb4: ad 26 03 lda map_row_addr+1
6eb7: 69 00 adc #$00
6eb9: 85 e9 sta ]data_ptr+1
6ebb: a5 e8 lda ]data_ptr
6ebd: 18 clc
6ebe: 6d 23 03 adc col_counter ;add current column
6ec1: 85 e8 sta ]data_ptr
6ec3: a5 e9 lda ]data_ptr+1
6ec5: 69 00 adc #$00
6ec7: 85 e9 sta ]data_ptr+1
; show player, walls, and healers
; don't show monsters, chests, or the Inn
6ec9: a0 00 ldy #$00
6ecb: b1 e8 lda (]data_ptr),y ;get map value
6ecd: aa tax
6ece: bd 5d 77 lda minimap_chars,x ;get the character
6ed1: e0 03 cpx #$03 ;is it 0/1/2 (empty/wall/player)?
6ed3: 90 06 bcc :Show ;yes, show it
6ed5: e0 0e cpx #14 ;is it a healer?
6ed7: f0 02 beq :Show ;yes, show it
6ed9: a9 a0 lda #$a0 ;otherwise just show a space
6edb: 91 06 :Show sta (ptr1),y ;store it
6edd: ce 23 03 dec col_counter ;next column
6ee0: 10 b8 bpl :ColLoop ;branch if more to do
6ee2: ee 24 03 inc row_counter ;next row
6ee5: ad 25 03 lda map_row_addr ;update map ptr
6ee8: 18 clc
6ee9: 69 50 adc #80
6eeb: 8d 25 03 sta map_row_addr
6eee: ad 26 03 lda map_row_addr+1
6ef1: 69 00 adc #$00
6ef3: 8d 26 03 sta map_row_addr+1
6ef6: ad 24 03 lda row_counter ;check row counter
6ef9: c9 18 cmp #24 ;more to do?
6efb: f0 03 beq :Done ;no
6efd: 4c 95 6e jmp :RowLoop ;yes, loop
6f00: 60 :Done rts
********************************************************************************
* Updates a single tile on the mode 1 (hi-res) and mode 2 (text minimap) *
* displays. *
* *
* On entry: *
* $304: display mode (1-3) *
* $305: tile X position (0-79) *
* $306: tile Y position (0-79) *
********************************************************************************
6f01: ad 06 03 UpdateOneTile lda monster_map_y
6f04: 20 ea 76 jsr GetMapRowAddr
6f07: ad 05 03 lda monster_map_x
6f0a: 38 sec
6f0b: ed 00 03 sbc win_left
6f0e: 8d 23 03 sta col_counter
6f11: 0a asl A
6f12: 0a asl A
6f13: 85 f9 sta ]tmp1
6f15: ad 06 03 lda monster_map_y
6f18: 38 sec
6f19: ed 01 03 sbc win_top
6f1c: 8d 24 03 sta row_counter
6f1f: 0a asl A
6f20: 0a asl A
6f21: 85 fb sta ]tmp2
6f23: 0a asl A
6f24: 0a asl A
6f25: 18 clc
6f26: 65 fb adc ]tmp2
6f28: 6d 24 03 adc row_counter
6f2b: 85 fb sta ]tmp2
6f2d: ad 25 03 lda map_row_addr
6f30: 18 clc
6f31: 6d 05 03 adc monster_map_x
6f34: 85 e8 sta ]data_ptr
6f36: ad 26 03 lda map_row_addr+1
6f39: 69 00 adc #$00
6f3b: 85 e9 sta ]data_ptr+1
6f3d: a0 00 ldy #$00
6f3f: b1 e8 lda (]data_ptr),y
6f41: 85 1e sta ARG_ITEM_INDEX
6f43: a9 c0 lda #<sketchz_tiles
6f45: 85 e8 sta ]data_ptr
6f47: a9 7f lda #>sketchz_tiles
6f49: 85 e9 sta ]data_ptr+1
6f4b: ad 23 03 lda col_counter ;check column number
6f4e: 30 10 bmi :NoDraw
6f50: c9 0a cmp #10 ;if < 0 or >= 10, it's offscreen
6f52: b0 0c bcs :NoDraw
6f54: ad 24 03 lda row_counter ;check row number
6f57: 30 07 bmi :NoDraw
6f59: c9 09 cmp #9 ;if < 0 or >= 9, it's offscreen
6f5b: b0 03 bcs :NoDraw
6f5d: 4c 61 6f jmp :DrawTile
6f60: 60 :NoDraw rts
6f61: 20 21 08 :DrawTile jsr BDRW
6f64: ad 04 03 lda display_mode ;get display mode
6f67: c9 03 cmp #$03 ;text map mode?
6f69: f0 1a beq :Done ;yup, bail
6f6b: ae 24 03 ldx row_counter
6f6e: bd 4b 77 lda minimap_addr_lo,x
6f71: 18 clc
6f72: 6d 23 03 adc col_counter
6f75: 85 06 sta ptr1
6f77: bd 54 77 lda minimap_addr_hi,x
6f7a: 85 07 sta ptr1+1
6f7c: a6 1e ldx ARG_ITEM_INDEX
6f7e: bd 5d 77 lda minimap_chars,x ;look up screen char
6f81: a0 00 ldy #$00
6f83: 91 06 sta (ptr1),y ;store onto text page
6f85: 60 :Done rts
********************************************************************************
* Waits for the player to hit a key. *
* *
* On entry: *
* $304: display mode (1-3) *
* $307: zero (turn timer) *
* $319: turn speed (0-49) *
* *
* On exit: *
* $308: key hit, or 0 if timeout *
********************************************************************************
• Clear variables
6f86: a9 01 WaitForKey lda #$01
6f88: 85 1f sta ARG_COLOR
6f8a: e6 4e inc MON_RNDL ;update random seed
6f8c: ad 07 03 lda turn_timer
6f8f: 85 f9 sta ARG_X0
6f91: 85 fc sta ARG_X1
6f93: e6 fc inc ARG_X1
6f95: a9 bd lda #189 ;draw box at bottom of screen
6f97: 85 fb sta ARG_Y0
6f99: a9 bf lda #191
6f9b: 85 fe sta ARG_Y1
6f9d: a9 00 lda #$00
6f9f: 85 1c sta ARG_DRAW_MODE ;draw
6fa1: 85 ff sta ARG_FF
6fa3: 20 11 70 jsr DrawTurnTimer ;draw timer
6fa6: ee 07 03 inc turn_timer ;advance
6fa9: ac 19 03 ldy turn_speed ;get speed (0-49, 0 is no delay)
6fac: d0 0a bne :DelayLoop
6fae: c8 iny ;set Y=1
6faf: ee 07 03 inc turn_timer
6fb2: ee 07 03 inc turn_timer
6fb5: 4c bd 6f jmp :CheckKey
6fb8: a2 00 :DelayLoop ldx #$00 ;spin for 256 iterations
6fba: ca :Stall dex
6fbb: d0 fd bne :Stall
6fbd: ad 00 c0 :CheckKey lda KBD ;key hit?
6fc0: 30 03 bmi :KeyHit ;yes
6fc2: 88 dey ;no, wait some more
6fc3: d0 f3 bne :DelayLoop
; A key has been hit, or we've waited for one timer tick.
6fc5: ad 07 03 :KeyHit lda turn_timer ;fiddle with the coordinates
6fc8: 85 f9 sta ARG_X0
6fca: 85 fc sta ARG_X1
6fcc: c6 f9 dec ARG_X0
6fce: ac 19 03 ldy turn_speed ;are we delaying at all?
6fd1: d0 08 bne L6FDB ;yes
6fd3: c6 f9 dec ARG_X0
6fd5: c6 f9 dec ARG_X0
6fd7: c6 fc dec ARG_X1
6fd9: c6 fc dec ARG_X1
6fdb: a9 bd L6FDB lda #189 ;set the Y coordinates
6fdd: 85 fb sta ARG_Y0
6fdf: a9 bf lda #191
6fe1: 85 fe sta ARG_Y1
6fe3: a9 01 lda #$01 ;erase
6fe5: 85 1c sta ARG_DRAW_MODE
6fe7: 20 11 70 jsr DrawTurnTimer ;erase timer indicator
6fea: ad 00 c0 lda KBD ;check key again
6fed: 30 0c bmi :KeyHit2 ;key hit
6fef: ad 07 03 :ReCheck lda turn_timer ;check timer
6ff2: c9 8b cmp #139 ;reached limit?
6ff4: 90 90 bcc WaitForKey ;no, go again
6ff6: a9 00 lda #$00 ;reset the turn timer
6ff8: 8d 07 03 sta turn_timer
6ffb: 8d 08 03 :KeyHit2 sta keyval_or_atk ;store key where CF can find it
6ffe: c9 93 cmp #$93 ;Ctrl+S?
7000: d0 0e bne :Done ;nope
7002: ad fe 75 lda _SpeakerClick+2 ;yes, toggle speaker by changing $C030 to $3F30
7005: 49 ff eor #$ff
7007: 8d fe 75 sta _SpeakerClick+2
700a: 8d 10 c0 sta KBDSTRB ;clear key, loop
700d: 4c ef 6f jmp :ReCheck ;doesn't count as a move
7010: 60 :Done rts
; Draws the turn-timer box on the hi-res screen, and an asterisk on the text
; screen.
;
; On entry:
; $1C: draw mode (0 for draw, 1 for erase)
7011: ad 04 03 DrawTurnTimer lda display_mode
7014: c9 03 cmp #$03 ;mode 3 (map)?
7016: f0 0e beq :SkipText ;don't put asterisk on text screen
7018: a6 f9 ldx ARG_X0 ;0-139
701a: bd b9 77 lda hi_res_col,x ;convert to column byte offset
701d: aa tax
701e: a4 1c ldy ARG_DRAW_MODE
7020: b9 4d 78 lda text_time_indic,y
7023: 9d d0 07 sta $07d0,x ;line 24
7026: 4c 33 08 :SkipText jmp CBOX ;go draw on the hi-res screen
7029: d4 c8 c9 d3+ .dstr “THIS PROGRAM COPYRIGHT 1982 BY DAVID (DR. CAT) SHAPIRO.”
********************************************************************************
* Move all monsters. Report attacks on player. *
* *
* Also draws dragon breath and checks for a hit. *
* *
* On entry: *
* $302: player X within window (AX) (0-9) *
* $303: player Y within window (AY) (0-8) *
* $304: display mode (1-3) *
* $309: player X coord (CX+AX) (0-79) *
* $30A: player Y coord (CY+AY) (0-79) *
* $30D: number of active monsters (NM) *
* *
* On exit: *
* $30E: number of monsters that attacked (0-4) *
* $30F-312: indexes of attacking monsters *
* (scribbles up to $316) *
* $31B/31C: X/Y of 1st zapping wizard ($FF/$FF if none) *
* $31D/31E: X/Y of 2nd zapping wizard ($FF/$FF if none) *
********************************************************************************
7060: a9 00 MoveMonsters lda #$00
7062: 8d 0f 03 sta mon_attack_id0
7065: 8d 10 03 sta mon_attack_id1
7068: 8d 11 03 sta mon_attack_id2
706b: 8d 12 03 sta mon_attack_id3
706e: 8d 0e 03 sta mon_attack_count
7071: 20 af 70 :MonsterLoop jsr MoveMonster
7074: ae 0d 03 ldx monster_count ;start at end of list
7077: bd 54 41 lda monster_type,x ;get type
707a: c9 0a cmp #10 ;thunderbug?
707c: d0 03 bne :NotTbug ;no
707e: 20 af 70 jsr MoveMonster ;yes, go again: thunderbugs move 2x
7081: ad 08 03 :NotTbug lda keyval_or_atk ;did monster attack player?
7084: f0 0c beq :NoAttack ;no
7086: ad 0d 03 lda monster_count ;ID of monster that attacked
7089: ae 0e 03 ldx mon_attack_count ;X-reg=next available entry
708c: 9d 0f 03 sta mon_attack_id0,x ;add to list ($30f-316)
708f: ee 0e 03 inc mon_attack_count
7092: ce 0d 03 :NoAttack dec monster_count ;move on to next monster
7095: d0 da bne :MonsterLoop
7097: ad 0e 03 lda mon_attack_count ;at most 4 monsters can attack
709a: c9 05 cmp #$05 ; (even if completely surrounded)
709c: 90 05 bcc :ValOk
709e: a9 04 lda #$04 ;cap it
70a0: 8d 0e 03 sta mon_attack_count
70a3: 20 5e 73 :ValOk jsr CheckRangedAttack
70a6: a9 00 lda #$00 ;clear hit-by-flame flag
70a8: 8d 1f 03 sta hit_by_flame
70ab: 20 3a 74 jsr CheckDragonBreath
70ae: 60 rts
;
; Moves a single monster.
;
; On entry:
; $30d: monster index
;
; On exit:
; $308: nonzero if monster attacked player
; also, wizard attack values may be updated
;
70af: a9 00 MoveMonster lda #$00 ;clear attack flag
70b1: 8d 08 03 sta keyval_or_atk
70b4: ae 0d 03 ldx monster_count ;get index
70b7: bd 00 40 lda monster_x,x ;get map X/Y
70ba: 8d 05 03 sta monster_map_x
70bd: bd aa 40 lda monster_y,x
70c0: 8d 06 03 sta monster_map_y
70c3: 38 sec
70c4: ed 0a 03 sbc player_map_y ;calc Y distance
70c7: 10 05 bpl :YPos
70c9: 49 ff eor #$ff ;negative, invert
70cb: 18 clc
70cc: 69 01 adc #$01
70ce: 8d 27 03 :YPos sta row_tmp ;y_dist
70d1: ad 05 03 lda monster_map_x ;calc X distance
70d4: 38 sec
70d5: ed 09 03 sbc player_map_x
70d8: 10 05 bpl :XPos
70da: 49 ff eor #$ff ;negative, invert
70dc: 18 clc
70dd: 69 01 adc #$01
70df: 18 :XPos clc
70e0: 6d 27 03 adc row_tmp ;add Y dist ("taxicab distance")
70e3: c9 14 cmp #20 ;less than 20?
70e5: 90 01 bcc :CloseEnough ;yes, update this monster
70e7: 60 rts ;no, too far, bail
70e8: 8d 2d 03 :CloseEnough sta mon_taxicab_dist
70eb: 20 b1 76 jsr Random ;generate random 0-255
70ee: c9 20 cmp #32 ;is it 0-31? (1 in 8 chance)
70f0: b0 03 bcs :MoveNonRandom ;no, move with purpose
70f2: 4c 78 72 jmp MoveRandomly ;yes, do something random
70f5: a9 00 :MoveNonRandom lda #$00 ;zero out moves
70f7: 8d 0b 03 sta monster_move_x
70fa: 8d 0c 03 sta monster_move_y
70fd: ad 09 03 lda player_map_x ;compare X coords
7100: 38 sec
7101: ed 05 03 sbc monster_map_x
7104: f0 0f beq :SameCol ;in same column
7106: 30 08 bmi :PlayerToLeft
7108: a9 01 lda #$01 ;player to right, move right
710a: 8d 0b 03 sta monster_move_x
710d: 4c 15 71 jmp :SameCol
7110: a9 ff :PlayerToLeft lda #$ff ;player to left, move left
7112: 8d 0b 03 sta monster_move_x
7115: ad 0a 03 :SameCol lda player_map_y ;compare Y coords
7118: 38 sec
7119: ed 06 03 sbc monster_map_y
711c: f0 0f beq :SameRow ;in same row
711e: 30 08 bmi :PlayerAbove
7120: a9 01 lda #$01 ;player below, move down
7122: 8d 0c 03 sta monster_move_y
7125: 4c 2d 71 jmp :SameRow
7128: a9 ff :PlayerAbove lda #$ff ;player above, move up
712a: 8d 0c 03 sta monster_move_y
712d: ae 0d 03 :SameRow ldx monster_count
7130: bd 54 41 lda monster_type,x ;get the type
7133: c9 0f cmp #15 ;wizard?
7135: d0 22 bne :NotWizard
7137: ad 2d 03 lda mon_taxicab_dist ;wizard; check distance
713a: c9 02 cmp #$02 ;distance = 2?
713c: d0 01 bne L713F ;no, keep going
713e: 60 rts ;yes, get no closer
713f: c9 01 L713F cmp #$01 ;distance = 1?
7141: d0 16 bne :NotWizard ;no, do regular move
7143: ad 0b 03 lda monster_move_x ;invert X move (run away!)
7146: 49 ff eor #$ff
7148: 8d 0b 03 sta monster_move_x
714b: ee 0b 03 inc monster_move_x
714e: ad 0c 03 lda monster_move_y ;invert Y move
7151: 49 ff eor #$ff
7153: 8d 0c 03 sta monster_move_y
7156: ee 0c 03 inc monster_move_y
7159: ad 0b 03 :NotWizard lda monster_move_x ;movement in X direction?
715c: f0 08 beq :NoMoveDir ;no, branch
715e: ad 0c 03 lda monster_move_y ;movement in Y direction?
7161: f0 03 beq :NoMoveDir ;no, branch
7163: 4c b4 71 jmp :MoveDiagonal
7166: 20 94 72 :NoMoveDir jsr MoveOrAttack ;try to move or attack
7169: ae 0b 03 ldx monster_move_x ;didn't work, reverse axes
716c: ac 0c 03 ldy monster_move_y
716f: 8e 0c 03 stx monster_move_y
7172: 8c 0b 03 sty monster_move_x
7175: ae 0d 03 ldx monster_count
7178: bd 54 41 lda monster_type,x ;check the monster type
717b: 29 01 and #$01 ;odd?
717d: f0 16 beq :Odd
717f: ad 0b 03 lda monster_move_x ;invert X/Y
7182: 49 ff eor #$ff
7184: 8d 0b 03 sta monster_move_x
7187: ee 0b 03 inc monster_move_x
718a: ad 0c 03 lda monster_move_y
718d: 49 ff eor #$ff
718f: 8d 0c 03 sta monster_move_y
7192: ee 0c 03 inc monster_move_y
7195: 20 94 72 :Odd jsr MoveOrAttack ;try to move or attack
7198: ad 0b 03 lda monster_move_x ;didn't work, reverse axes
719b: 49 ff eor #$ff
719d: 8d 0b 03 sta monster_move_x
71a0: ee 0b 03 inc monster_move_x
71a3: ad 0c 03 lda monster_move_y
71a6: 49 ff eor #$ff
71a8: 8d 0c 03 sta monster_move_y
71ab: ee 0c 03 inc monster_move_y
71ae: 20 94 72 jsr MoveOrAttack ;try again
71b1: 4c 78 72 jmp MoveRandomly ;give up, just move randomly
71b4: ae 0d 03 :MoveDiagonal ldx monster_count
71b7: bd 54 41 lda monster_type,x ;check type
71ba: c9 04 cmp #4 ;mad robot (no diagonal movement)
71bc: f0 13 beq :TryFirst
71be: c9 0d cmp #13 ;mimic
71c0: f0 0f beq :TryFirst
71c2: c9 09 cmp #9 ;invisoid
71c4: f0 0b beq :TryFirst
71c6: c9 0a cmp #10 ;thunderbug
71c8: f0 07 beq :TryFirst
71ca: 4c d4 71 jmp :NoTryFirst
71cd: c9 0e f0 00 .junk 4
71d1: 20 94 72 :TryFirst jsr MoveOrAttack
71d4: ad 0a 03 :NoTryFirst lda player_map_y ;get Y distance
71d7: 38 sec
71d8: ed 06 03 sbc monster_map_y
71db: 8d 27 03 sta row_tmp
71de: 10 0b bpl :YPos
71e0: ad 27 03 lda row_tmp ;make positive
71e3: 49 ff eor #$ff
71e5: 8d 27 03 sta row_tmp
71e8: ee 27 03 inc row_tmp
71eb: ad 09 03 :YPos lda player_map_x ;get X distance
71ee: 38 sec
71ef: ed 05 03 sbc monster_map_x
71f2: 8d 28 03 sta row_tmp+1
71f5: 10 0b bpl :XPos
71f7: ad 28 03 lda row_tmp+1 ;make positive
71fa: 49 ff eor #$ff
71fc: 8d 28 03 sta row_tmp+1
71ff: ee 28 03 inc row_tmp+1
7202: ae 0d 03 :XPos ldx monster_count
7205: bd 54 41 lda monster_type,x ;get type
7208: 29 02 and #$02 ;subset
720a: f0 0c beq :FlipPattern
720c: ad 27 03 lda row_tmp ;compute deltaY - deltaX
720f: 38 sec
7210: ed 28 03 sbc row_tmp+1
7213: 90 39 bcc :MovePattern2 ;deltaX is greater
7215: 4c 21 72 jmp :MovePattern1
7218: ad 27 03 :FlipPattern lda row_tmp ;compute deltaY - deltaX
721b: 38 sec
721c: ed 28 03 sbc row_tmp+1
721f: b0 2d bcs :MovePattern2 ;deltaY is greater
7221: ad 0c 03 :MovePattern1 lda monster_move_y ;zero out moveY
7224: 8d 2b 03 sta monster_move_tmp
7227: a9 00 lda #$00
7229: 8d 0c 03 sta monster_move_y
722c: 20 94 72 jsr MoveOrAttack ;try it
722f: a9 00 lda #$00 ;restore moveY, zero out moveX
7231: 8d 0b 03 sta monster_move_x
7234: ad 2b 03 lda monster_move_tmp
7237: 8d 0c 03 sta monster_move_y
723a: 20 94 72 jsr MoveOrAttack ;try it
723d: ad 0c 03 lda monster_move_y ;invert moveY
7240: 49 ff eor #$ff
7242: 8d 0c 03 sta monster_move_y
7245: ee 0c 03 inc monster_move_y
7248: 20 94 72 jsr MoveOrAttack ;try it
724b: 4c 78 72 jmp MoveRandomly ;give up
724e: ad 0b 03 :MovePattern2 lda monster_move_x ;zero out moveX
7251: 8d 2b 03 sta monster_move_tmp
7254: a9 00 lda #$00
7256: 8d 0b 03 sta monster_move_x
7259: 20 94 72 jsr MoveOrAttack ;try it
725c: a9 00 lda #$00 ;restore moveX, zero out moveY
725e: 8d 0c 03 sta monster_move_y
7261: ad 2b 03 lda monster_move_tmp
7264: 8d 0b 03 sta monster_move_x
7267: 20 94 72 jsr MoveOrAttack ;try it
726a: ad 0b 03 lda monster_move_x ;invert moveX
726d: 49 ff eor #$ff
726f: 8d 0b 03 sta monster_move_x
7272: ee 0b 03 inc monster_move_x
7275: 20 94 72 jsr MoveOrAttack ;try it
7278: 20 b1 76 MoveRandomly jsr Random ;generate random number
727b: 29 03 and #$03 ;reduce to 0-3
727d: aa tax
727e: bd b5 77 lda move_dir,x
7281: 8d 0b 03 sta monster_move_x
7284: 20 b1 76 jsr Random ;again
7287: 29 03 and #$03
7289: aa tax
728a: bd b5 77 lda move_dir,x
728d: 8d 0c 03 sta monster_move_y
7290: 20 94 72 jsr MoveOrAttack
7293: 60 rts
;
; Evaluate the monster's movement. If we tried to move onto the player,
; register an attack instead.
;
; If we move or attack, the caller's return address will be popped off, which
; will short-circut the caller's logic. If we don't move, we return to the
; caller.
;
7294: ad 0b 03 MoveOrAttack lda monster_move_x ;did we move at all?
7297: 0d 0c 03 ora monster_move_y
729a: d0 01 bne :DidMove ;yes, do the update
729c: 60 rts ;no, bail
729d: ae 0d 03 :DidMove ldx monster_count
72a0: bd 54 41 lda monster_type,x
72a3: c9 0e cmp #14 ;healer
72a5: d0 16 bne :NotHealer
72a7: ad 0b 03 lda monster_move_x ;invert move -- healers tend to run
72aa: 49 ff eor #$ff ; away from player
72ac: 8d 0b 03 sta monster_move_x
72af: ee 0b 03 inc monster_move_x
72b2: ad 0c 03 lda monster_move_y
72b5: 49 ff eor #$ff
72b7: 8d 0c 03 sta monster_move_y
72ba: ee 0c 03 inc monster_move_y
72bd: ad 06 03 :NotHealer lda monster_map_y ;update monster X/Y
72c0: 18 clc
72c1: 6d 0c 03 adc monster_move_y
72c4: 8d 24 03 sta row_counter
72c7: ad 05 03 lda monster_map_x
72ca: 18 clc
72cb: 6d 0b 03 adc monster_move_x
72ce: 8d 23 03 sta col_counter
72d1: ad 24 03 lda row_counter ;get pointer to new map position
72d4: 20 ea 76 jsr GetMapRowAddr
72d7: ad 25 03 lda map_row_addr
72da: 18 clc
72db: 6d 23 03 adc col_counter
72de: 85 06 sta ptr1
72e0: ad 26 03 lda map_row_addr+1
72e3: 69 00 adc #$00
72e5: 85 07 sta ptr1+1
72e7: a0 00 ldy #$00 ;check what we're trying to move onto
72e9: b1 06 lda (ptr1),y
72eb: c9 02 cmp #$02 ;is this the player?
72ed: d0 08 bne :NotPlayer
72ef: a9 7b lda #$7b ;set attack flag
72f1: 8d 08 03 sta keyval_or_atk ; instead of moving
72f4: 68 pla ;pop caller address off
72f5: 68 pla
72f6: 60 rts
72f7: c9 00 :NotPlayer cmp #$00 ;empty space?
72f9: f0 01 beq DoMoveMaybe
72fb: 60 rts ;no, can't move there
72fc: ae 0d 03 DoMoveMaybe ldx monster_count
72ff: bd 54 41 lda monster_type,x ;check monster type
7302: c9 0d cmp #13 ;mimic?
7304: d0 01 bne MoveUpdateMap ;mimics don't move
7306: 60 rts
;
; Update the map for a monster move.
;
; On entry:
; $06/07: pointer to map entry for new location
; $305: old location X
; $306: old location Y
;
; Important: the caller's return address is popped off before returning.
;
7307: a5 06 MoveUpdateMap lda ptr1 ;save ptr1
7309: 48 pha
730a: a5 07 lda ptr1+1
730c: 48 pha
; erase monster from old position
730d: ad 06 03 lda monster_map_y ;get pointer to monster's position on map
7310: 20 ea 76 jsr GetMapRowAddr
7313: ad 25 03 lda map_row_addr
7316: 18 clc
7317: 6d 05 03 adc monster_map_x
731a: 85 06 sta ptr1
731c: ad 26 03 lda map_row_addr+1
731f: 69 00 adc #$00
7321: 85 07 sta ptr1+1
7323: a0 00 ldy #$00
7325: a9 00 lda #$00 ;change it to empty space
7327: 91 06 sta (ptr1),y
7329: 68 pla ;restore ptr1
732a: 85 07 sta ptr1+1
732c: 68 pla
732d: 85 06 sta ptr1
; store monster in map in new position
732f: ae 0d 03 ldx monster_count
7332: bd 54 41 lda monster_type,x
7335: 91 06 sta (ptr1),y
; call UpdateOneTile to redraw the old and new positions
7337: ad 23 03 lda col_counter
733a: 9d 00 40 sta monster_x,x
733d: 8d 29 03 sta player_xc
7340: ad 24 03 lda row_counter
7343: 9d aa 40 sta monster_y,x
7346: 8d 2a 03 sta player_yc
7349: 20 01 6f jsr UpdateOneTile
734c: ad 29 03 lda player_xc
734f: 8d 05 03 sta monster_map_x
7352: ad 2a 03 lda player_yc
7355: 8d 06 03 sta monster_map_y
7358: 20 01 6f jsr UpdateOneTile
735b: 68 pla ;remove caller address
735c: 68 pla
735d: 60 rts
CheckRangedAttack
735e: a9 ff lda #$ff ;clear wizard attack values
7360: 8d 1b 03 sta atk_wiz1_x
7363: 8d 1d 03 sta atk_wiz2_x
; look for a wizard on the same vertical line
7366: a9 00 lda #$00 ;top of window
7368: 85 fb sta ARG_Y0
736a: ad 02 03 lda player_win_x ;player X coord
736d: 85 f9 sta ARG_X0
736f: 20 8f 73 :CheckVert jsr CheckWizard ;if wizard here, try zap
7372: e6 fb inc ARG_Y0
7374: a5 fb lda ARG_Y0
7376: c9 09 cmp #9 ;reached bottom of window?
7378: d0 f5 bne :CheckVert ;not yet
; look for a wizard on the same horizontal line
737a: a9 00 lda #$00 ;left of window
737c: 85 f9 sta ARG_X0
737e: ad 03 03 lda player_win_y ;player Y coord
7381: 85 fb sta ARG_Y0
7383: 20 8f 73 :CheckHoriz jsr CheckWizard ;if wizard here, try zap
7386: e6 f9 inc ARG_X0
7388: a5 f9 lda ARG_X0
738a: c9 0a cmp #10 ;reached right edge of window?
738c: d0 f5 bne :CheckHoriz ;not yet
738e: 60 rts
738f: 20 1d 74 CheckWizard jsr GetMapValue
7392: c9 0f cmp #15 ;wizard?
7394: f0 01 beq CheckWizardZap
7396: 60 rts
; Try to zap the player with a wizard. Must be lined up vertically or
; horizontally.
;
; (the code re-uses the storage locations that the sound code uses, so the names
; are a bit funny)
7397: a9 01 CheckWizardZap lda #$01 ;assume shooting down/right
7399: 8d 17 03 sta sound_pitch_adj
739c: a5 fb lda ARG_Y0 ;are we lined up?
739e: cd 03 03 cmp player_win_y
73a1: d0 29 bne :CheckVertical ;not horizontally
73a3: a5 f9 lda ARG_X0 ;yes, check position
73a5: cd 02 03 cmp player_win_x
73a8: 90 05 bcc :WizLeft ;wizard to left, shooting right
73aa: a9 ff lda #$ff ;wizard to right, shooting left
73ac: 8d 17 03 sta sound_pitch_adj
73af: a5 f9 :WizLeft lda ARG_X0
73b1: 8d 15 03 sta sound_duration
; scan tiles, looking for non-empty squares
73b4: a5 f9 :Loop lda ARG_X0
73b6: 18 clc
73b7: 6d 17 03 adc sound_pitch_adj
73ba: 85 f9 sta ARG_X0
73bc: cd 02 03 cmp player_win_x ;reached the player?
73bf: f0 34 beq ZapPlayer_H ;yes, zap them
73c1: 20 1d 74 jsr GetMapValue ;no, see what's there
73c4: f0 ee beq :Loop ;empty space, keep going
73c6: ad 15 03 lda sound_duration ;not empty; restore X0 and bail
73c9: 85 f9 sta ARG_X0
73cb: 60 rts
73cc: a5 fb :CheckVertical lda ARG_Y0 ;check position
73ce: cd 03 03 cmp player_win_y
73d1: 90 05 bcc :WizAbove ;wizard above, shooting down
73d3: a9 ff lda #$ff ;wizard below, shooting up
73d5: 8d 17 03 sta sound_pitch_adj
73d8: a5 fb :WizAbove lda ARG_Y0
73da: 8d 15 03 sta sound_duration
; scan tiles, looking for non-empty squares
73dd: a5 fb :Loop lda ARG_Y0
73df: 18 clc
73e0: 6d 17 03 adc sound_pitch_adj
73e3: 85 fb sta ARG_Y0
73e5: cd 03 03 cmp player_win_y ;reached the player?
73e8: f0 13 beq ZapPlayer_V ;yes, zap them
73ea: 20 1d 74 jsr GetMapValue ;no, see what's there
73ed: f0 ee beq :Loop ;empty space, keep going
73ef: ad 15 03 lda sound_duration ;not empty; restore Y0 and bail
73f2: 85 fb sta ARG_Y0
73f4: 60 rts
73f5: ad 15 03 ZapPlayer_H lda sound_duration ;restore X0
73f8: 85 f9 sta ARG_X0
73fa: 4c 02 74 jmp ZapPlayer
73fd: ad 15 03 ZapPlayer_V lda sound_duration ;restore Y0
7400: 85 fb sta ARG_Y0
7402: ad 1b 03 ZapPlayer lda atk_wiz1_x ;already attacked by a wizard?
7405: 10 0b bpl L7412 ;yes, try second storage
7407: a5 f9 lda ARG_X0 ;no, put in first storage
7409: 8d 1b 03 sta atk_wiz1_x
740c: a5 fb lda ARG_Y0
740e: 8d 1c 03 sta atk_wiz1_y
7411: 60 rts
7412: a5 f9 L7412 lda ARG_X0
7414: 8d 1d 03 sta atk_wiz2_x
7417: a5 fb lda ARG_Y0
7419: 8d 1e 03 sta atk_wiz2_y
741c: 60 rts
; Gets the tile value for the tile at left+X0,top+Y0.
;
; On exit:
; A-reg: value
741d: ad 01 03 GetMapValue lda win_top
7420: 18 clc
7421: 65 fb adc ARG_Y0
7423: 20 ea 76 jsr GetMapRowAddr
7426: ad 25 03 lda map_row_addr
7429: 85 06 sta ptr1
742b: ad 26 03 lda map_row_addr+1
742e: 85 07 sta ptr1+1
7430: ad 00 03 lda win_left
7433: 18 clc
7434: 65 f9 adc ARG_X0
7436: a8 tay
7437: b1 06 lda (ptr1),y
7439: 60 rts
; If we're close enough to see the dragon breathe, show it and play the sound
; effect. If we're hit by it, set the flag.
;
; The dragon breathes every-other turn. CF sets the value to zero, and we
; toggle the low bit on every breath. When the dragon dies, CF sets the value
; to 9 to disable it.
;
; On Exit:
; $31f: 0 if not hit by flames, $ff if hit
CheckDragonBreath
743a: ad 20 03 lda dragon_breath_ready ;set to 9 on dragon death?
743d: 49 01 eor #$01 ; (line 40020)
743f: 8d 20 03 sta dragon_breath_ready
7442: f0 01 beq :ReadyToBreathe
7444: 60 rts
7445: ad 00 03 :ReadyToBreathe lda win_left ;are we far enough east to see it?
7448: c9 42 cmp #66
744a: b0 01 bcs L744D ;maybe, branch
744c: 60 rts ;no
744d: ad 01 03 L744D lda win_top ;are we far enough south to see it?
7450: c9 3f cmp #63
7452: b0 01 bcs :CalcXY ;yes, branch
7454: 60 rts ;no
; find screen position; map tiles are 28x21 pixels
7455: a9 47 :CalcXY lda #71 ;start in col 71
7457: 38 sec
7458: ed 00 03 sbc win_left ;adjust for window position
745b: 0a asl A
745c: 0a asl A ;A=col*4
745d: 85 f9 sta ARG_X0
745f: 06 f9 asl ARG_X0 ;x0=col*8
7461: 18 clc
7462: 65 f9 adc ARG_X0 ;A=col*4 + col*8 = col*12
7464: 06 f9 asl ARG_X0 ;x0=col*16
7466: 18 clc
7467: 65 f9 adc ARG_X0 ;A=col*12 + col*16 = col*28
7469: 85 f9 sta ARG_X0
746b: a9 00 lda #$00 ;update high byte if needed
746d: 85 fa sta ARG_X0+1
746f: a9 47 lda #71 ;start in row 71
7471: 38 sec
7472: ed 01 03 sbc win_top ;adjust for window position
7475: 85 fb sta ARG_Y0
7477: 06 fb asl ARG_Y0
7479: 06 fb asl ARG_Y0 ;y0=row*4
747b: 18 clc
747c: 65 fb adc ARG_Y0 ;A=row + row*4 = row*5
747e: 06 fb asl ARG_Y0
7480: 06 fb asl ARG_Y0 ;y0=row*16
7482: 18 clc
7483: 65 fb adc ARG_Y0 ;A=row*5 + row*16 = row*21
7485: 85 fb sta ARG_Y0
; draw
7487: a9 00 lda #<bad_breath_data
7489: 85 e8 sta ARG_EXT_PTR
748b: a9 bc lda #>bad_breath_data
748d: 85 e9 sta ARG_EXT_PTR+1
748f: a9 02 lda #$02
7491: 85 1c sta ARG_DRAW_MODE ;mode=invert
7493: a9 00 lda #$00
7495: 85 1e sta ARG_ITEM_INDEX ;item 0
7497: 20 bc 74 jsr CharsetSaveXY
749a: 20 cc 74 jsr PlayDragonSound
749d: 20 bc 74 jsr CharsetSaveXY
74a0: ad 01 03 lda win_top
74a3: 18 clc
74a4: 6d 03 03 adc player_win_y
74a7: c9 47 cmp #71 ;on row 71?
74a9: d0 10 bne :NotHit ;nope, not hit
74ab: ad 00 03 lda win_left
74ae: 18 clc
74af: 6d 02 03 adc player_win_x
74b2: c9 47 cmp #71 ;on col >= 71?
74b4: 90 05 bcc :NotHit ;(71-74 open, 75-77 dragon, 78-79 wall)
74b6: a9 ff lda #$ff ;hit!
74b8: 8d 1f 03 sta hit_by_flame
74bb: 60 :NotHit rts
; Invokes CHARSET function, preserving X0/Y0.
74bc: a5 f9 CharsetSaveXY lda ARG_X0
74be: 48 pha
74bf: a5 fb lda ARG_Y0
74c1: 48 pha
74c2: 20 1e 08 jsr CHARSET
74c5: 68 pla
74c6: 85 fb sta ARG_Y0
74c8: 68 pla
74c9: 85 f9 sta ARG_X0
74cb: 60 rts
; Plays the dragon breath sound effects.
74cc: a9 06 PlayDragonSound lda #$06
74ce: 85 06 sta ptr1
74d0: 20 35 76 jsr PlayIndexedTone
74d3: a9 07 lda #$07
74d5: 85 06 sta ptr1
74d7: 20 35 76 jsr PlayIndexedTone
74da: 60 rts
********************************************************************************
* Create a set of objects (monsters or chests). *
* *
* On entry: *
* $300: 3 or 37 (monsters or chests) *
* $301: number of items to create *
* *
* Called from lines 55-56 with: *
* $300=3 $301=110 (create monsters) *
* $300=37 $301=62 (create chests) *
********************************************************************************
74db: a5 4e CreateMonsters lda MON_RNDL ;update random seed
74dd: 8d 47 77 sta random_value
; Generate random (X,Y) coordinate.
74e0: 20 b1 76 :Retry1 jsr Random ;generate random number
74e3: 29 7f and #$7f ;reduce to 0-127
74e5: c9 50 cmp #80
74e7: b0 f7 bcs :Retry1 ;>= 80, try again
74e9: 8d 05 03 sta monster_map_x ;store X coord (0-79)
74ec: 20 b1 76 :Retry2 jsr Random ;do same thing again
74ef: 29 7f and #$7f
74f1: c9 50 cmp #80
74f3: b0 f7 bcs :Retry2
74f5: 8d 06 03 sta monster_map_y ;store Y coord (0-79)
74f8: 20 ea 76 jsr GetMapRowAddr ;get row address
74fb: ad 25 03 lda map_row_addr
74fe: 18 clc
74ff: 6d 05 03 adc monster_map_x ;add X coord
7502: 85 06 sta ptr1
7504: ad 26 03 lda map_row_addr+1
7507: 69 00 adc #$00
7509: 85 07 sta ptr1+1
; check to see if cell is empty
750b: a0 00 ldy #$00
750d: b1 06 lda (ptr1),y
750f: d0 cf bne :Retry1 ;not empty, pick another location
7511: ad 00 03 lda win_left ;get object type
7514: 91 06 sta (ptr1),y ;store it
7516: c9 03 cmp #$03 ;is it wall/empty/player?
7518: 90 29 bcc :Next ;yes, move on
751a: c9 20 cmp #$20 ;is it a monster?
751c: b0 25 bcs :Next ;no, move on
751e: ae 01 03 ldx win_top ;get index
7521: ad 05 03 lda monster_map_x ;get XC
7524: 9d 00 40 sta monster_x,x ;store in monster table
7527: ad 06 03 lda monster_map_y ;get YC
752a: 9d aa 40 sta monster_y,x ;store in monster table
752d: ad 00 03 lda win_left ;get type
7530: 9d 54 41 sta monster_type,x ;store in monster table
; pick a new monster type
7533: 20 b1 76 :ChangeMonster jsr Random
7536: 29 1f and #$1f ;0-31
7538: 18 clc
7539: 69 03 adc #$03 ;3-34
753b: cd 1a 03 cmp skill_level_plus_4 ;exceeds skill level?
753e: b0 f3 bcs :ChangeMonster ;yes, re-roll
7540: 8d 00 03 sta win_left ;use for next one
7543: ce 01 03 :Next dec win_top ;done yet?
7546: d0 98 bne :Retry1 ;nope (note we don't populate entry 0)
7548: 60 rts
********************************************************************************
* Copies (4*21)=84 bytes, the size of one hi-res tile. *
* *
* On entry: *
* $08/09: src ptr *
* $06/07: dst ptr *
********************************************************************************
7549: a0 53 Copy84Bytes ldy #83
754b: b1 08 :Loop lda (ptr2),y
754d: 91 06 sta (ptr1),y
754f: 88 dey
7550: 10 f9 bpl :Loop
7552: 60 rts
********************************************************************************
* Inverts the bytes from $8014-8067. *
* *
* This is the 4x21 wall bitmap. The walls in most of the cavern are purple *
* with black cross-hatching, but the walls near the dragon are orange with *
* white cross-hatching. There's a large empty zone around the dragon area so *
* we don't have to worry about drawing both kinds. *
********************************************************************************
7553: a0 53 Invert8014 ldy #83 ;(21 * 4) - 1
7555: a9 14 lda #<wall_tile
7557: 85 06 sta ptr1
7559: a9 80 lda #>wall_tile
755b: 85 07 sta ptr1+1
755d: b1 06 :Loop lda (ptr1),y
755f: 49 ff eor #$ff
7561: 91 06 sta (ptr1),y
7563: 88 dey
7564: 10 f7 bpl :Loop
7566: 60 rts
********************************************************************************
* Removes all non-wall elements from the map. Useful when restarting the *
* game. *
********************************************************************************
7567: a9 00 CleanMap lda #<map
7569: 85 06 sta ptr1
756b: a9 a0 lda #>map
756d: 85 07 sta ptr1+1
756f: a0 00 ldy #$00
7571: b1 06 :Loop lda (ptr1),y
7573: c9 02 cmp #$02 ;empty space or wall?
7575: 90 04 bcc :NoWrite ;yes, leave it alone
7577: a9 00 lda #$00 ;no, turn it into blank space
7579: 91 06 sta (ptr1),y
757b: c8 :NoWrite iny
757c: d0 f3 bne :Loop
757e: e6 07 inc ptr1+1
7580: a5 07 lda ptr1+1
7582: c9 bc cmp #$bc ;erasing $1c00=7168, but map is only $1900=6400?
7584: d0 eb bne :Loop
7586: 60 rts
********************************************************************************
* If it looks like the map is sitting on the hi-res screen, move it to $A000. *
* *
* This also relocates "BAD BREATH" from $3C00-3DFF to $BC00-BDFF. *
********************************************************************************
7587: ad 00 20 MoveMap lda $2000 ;check first byte of hi-res page
758a: c9 01 cmp #$01 ;does it look like the map is here?
758c: f0 01 beq :MoveIt ;yes, copy it
758e: 60 rts
758f: a9 00 :MoveIt lda #$00 ;copy $2000-3fff to $a000-bfff
7591: 85 06 sta ptr1
7593: 85 08 sta ptr2
7595: a9 20 lda #$20
7597: 85 07 sta ptr1+1
7599: a9 a0 lda #$a0
759b: 85 09 sta ptr2+1
759d: a0 00 ldy #$00
759f: b1 06 :Loop lda (ptr1),y
75a1: 91 08 sta (ptr2),y
75a3: c8 iny
75a4: d0 f9 bne :Loop
75a6: e6 07 inc ptr1+1
75a8: e6 09 inc ptr2+1
75aa: a5 07 lda ptr1+1
75ac: c9 40 cmp #$40
75ae: d0 ef bne :Loop
75b0: 60 rts
********************************************************************************
* Play a tone *
* *
* On entry: *
* $313-314 - pitch *
* $315-316 - duration *
* $317-318 - pitch adjustment *
* *
* If the pitch adjustment is nonzero, instead of a steady tone the code will *
* generate a rising or falling note. *
********************************************************************************
75b1: a0 00 PlayTone ldy #$00
75b3: ad 13 03 L75B3 lda sound_pitch ;copy pitch value to var
75b6: 8d 27 03 sta row_tmp
75b9: ad 14 03 lda sound_pitch+1
75bc: 8d 28 03 sta row_tmp+1
75bf: c8 :SilenceLoop iny ;duration=1 means 256 iterations
75c0: d0 27 bne :DecPitch ;not time to dec duration; go dec pitch
75c2: ad 15 03 lda sound_duration ;decrement duration
75c5: d0 03 bne :NoDec
75c7: ce 16 03 dec sound_duration+1
75ca: ce 15 03 :NoDec dec sound_duration
75cd: ad 13 03 lda sound_pitch ;update pitch value with adjustment
75d0: 18 clc
75d1: 6d 17 03 adc sound_pitch_adj
75d4: 8d 13 03 sta sound_pitch
75d7: ad 14 03 lda sound_pitch+1
75da: 6d 18 03 adc sound_pitch_adj+1
75dd: 8d 14 03 sta sound_pitch+1
75e0: ad 15 03 lda sound_duration ;is duration zero?
75e3: 0d 16 03 ora sound_duration+1
75e6: d0 01 bne :DecPitch ;not yet
75e8: 60 rts
75e9: ad 27 03 :DecPitch lda row_tmp ;count down pitch value
75ec: d0 03 bne :NoDec
75ee: ce 28 03 dec row_tmp+1
75f1: ce 27 03 :NoDec dec row_tmp
75f4: ad 27 03 lda row_tmp ;is pitch counter zero?
75f7: 0d 28 03 ora row_tmp+1
75fa: d0 c3 bne :SilenceLoop ;not yet
75fc: 2c 30 c0 _SpeakerClick bit SPKR ;click speaker
75ff: 4c b3 75 jmp L75B3
********************************************************************************
* Play Chopin's Funeral March when the player dies. *
********************************************************************************
7602: a2 00 PlayFuneral ldx #$00 ;note number
7604: 8e 14 03 stx sound_pitch+1
7607: 8e 16 03 stx sound_duration+1
760a: 8e 17 03 stx sound_pitch_adj
760d: 8e 18 03 stx sound_pitch_adj+1
7610: bd 50 78 :PlayLoop lda funeral_song_pitch,x
7613: 8d 13 03 sta sound_pitch
7616: bd 5b 78 lda funeral_song_dur,x
7619: 4a lsr A
761a: 8d 15 03 sta sound_duration
761d: 20 b1 75 jsr PlayTone
; Pause briefly.
7620: a0 09 ldy #$09 ;delay length
7622: 8a txa
7623: 48 pha
7624: a2 00 :DelayLoop1 ldx #$00
7626: ea :DelayLoop2 nop
7627: ca dex
7628: d0 fc bne :DelayLoop2
762a: 88 dey
762b: d0 f7 bne :DelayLoop1
762d: 68 pla
762e: aa tax
762f: e8 inx ;advance to next note
7630: e0 0b cpx #11 ;played 11 notes?
7632: d0 dc bne :PlayLoop ;not yet
7634: 60 rts
********************************************************************************
* Play a single tone, determined by index. *
* *
* On entry: *
* $06: tone to play (0-7) *
* *
* Tones used by CF: *
* 0: attack with sword *
* 1: hit monster with sword *
* 2: hit by monster *
* 3: dragon killed *
* 4: monster killed *
* 5: attacked by wizard *
* Tones used by CF.OBJ: *
* 6/7: dragon breath *
********************************************************************************
7635: a6 06 PlayIndexedTone ldx ptr1
7637: bd 66 78 lda st_pitch_lo,x
763a: 8d 13 03 sta sound_pitch
763d: bd 6e 78 lda st_pitch_hi,x
7640: 8d 14 03 sta sound_pitch+1
7643: bd 76 78 lda st_dur_lo,x
7646: 8d 15 03 sta sound_duration
7649: bd 7e 78 lda st_dur_hi,x
764c: 8d 16 03 sta sound_duration+1
764f: bd 86 78 lda st_pitchadj_lo,x
7652: 8d 17 03 sta sound_pitch_adj
7655: bd 8e 78 lda st_pitchadj_hi,x
7658: 8d 18 03 sta sound_pitch_adj+1
765b: 20 b1 75 jsr PlayTone
765e: 60 rts
********************************************************************************
* Play two tones, determined by index. *
* *
* On entry: *
* $06: tone to play (0-2) *
* *
* Tones used by CF: *
* 0: moved, regular (20161) *
* 1: moved, running (20161) *
* 2: "OOF! HIT A WALL" (20120), "BLOCKED BY <monster>" (20135) *
********************************************************************************
PlayIndexedDualTone
765f: a9 00 lda #$00
7661: 8d 14 03 sta sound_pitch+1
7664: 8d 16 03 sta sound_duration+1
7667: 8d 18 03 sta sound_pitch_adj+1
766a: a6 06 ldx ptr1
766c: bd 96 78 lda dt_pitch1,x
766f: 8d 13 03 sta sound_pitch
7672: bd 99 78 lda dt_dur1,x
7675: 8d 15 03 sta sound_duration
7678: bd 9c 78 lda dt_pitchadj1,x
767b: 8d 17 03 sta sound_pitch_adj
767e: 20 b1 75 jsr PlayTone
7681: a6 06 ldx ptr1
7683: bd 9f 78 lda dt_pitch2,x
7686: 8d 13 03 sta sound_pitch
7689: bd a2 78 lda dt_dur2,x
768c: 8d 15 03 sta sound_duration
768f: bd a5 78 lda dt_pitchadj2,x
7692: 8d 17 03 sta sound_pitch_adj
7695: 20 b1 75 jsr PlayTone
7698: 60 rts
********************************************************************************
* Finds the index of the monster at (X,Y). *
* *
* Called from 20255 to identify which monster the player is swinging at. *
* *
* On entry: *
* $06: map X coordinate *
* $07: map Y coordinate *
* $30d: number of monsters *
* *
* On exit: *
* $30d: index of monster, or 0 if not found *
********************************************************************************
• Clear variables
]map_x .var $06 {addr/1}
]map_y .var $07 {addr/1}
FindMonsterIndex
7699: ae 0d 03 ldx monster_count
769c: bd 00 40 lda monster_x,x
769f: c5 06 cmp ]map_x
76a1: d0 08 bne :Nope
76a3: bd aa 40 lda monster_y,x
76a6: c5 07 cmp ]map_y
76a8: d0 01 bne :Nope
76aa: 60 rts
76ab: ce 0d 03 :Nope dec monster_count
76ae: d0 e9 bne FindMonsterIndex
76b0: 60 rts
;
; Random number generator (16-bit state).
;
; On exit:
; A-reg: random value (0-255)
;
76b1: a2 07 Random ldx #$07
76b3: ad 48 77 :Loop lda random_value+1
76b6: 6a ror A
76b7: 4d 48 77 eor random_value+1
76ba: 6a ror A
76bb: 6a ror A
76bc: 4d 48 77 eor random_value+1
76bf: 6a ror A
76c0: 4d 47 77 eor random_value
76c3: 6a ror A
76c4: 6a ror A
76c5: 6a ror A
76c6: 49 01 eor #$01
76c8: 29 01 and #$01
76ca: a8 tay
76cb: ad 47 77 lda random_value
76ce: 18 clc
76cf: 6d 47 77 adc random_value
76d2: 8d 47 77 sta random_value
76d5: ad 48 77 lda random_value+1
76d8: 6d 48 77 adc random_value+1
76db: 8d 48 77 sta random_value+1
76de: 98 tya
76df: 18 clc
76e0: 6d 47 77 adc random_value
76e3: 8d 47 77 sta random_value
76e6: ca dex
76e7: d0 ca bne :Loop
76e9: 60 rts
;
; Given a map row number, generate the row address.
;
; On entry:
; A-reg: map row (0-79)
;
; On exit:
; $325-326: map row address ($A000+)
;
76ea: 8d 27 03 GetMapRowAddr sta row_tmp
76ed: a9 00 lda #$00
76ef: 8d 28 03 sta row_tmp+1
76f2: a9 00 lda #<map ;get map base address
76f4: 8d 25 03 sta map_row_addr
76f7: a9 a0 lda #>map
76f9: 8d 26 03 sta map_row_addr+1
76fc: 0e 27 03 asl row_tmp ;multiply row by 80
76ff: 2e 28 03 rol row_tmp+1
7702: 0e 27 03 asl row_tmp
7705: 2e 28 03 rol row_tmp+1
7708: 0e 27 03 asl row_tmp
770b: 2e 28 03 rol row_tmp+1
770e: 0e 27 03 asl row_tmp
7711: 2e 28 03 rol row_tmp+1
7714: ad 25 03 lda map_row_addr
7717: 18 clc
7718: 6d 27 03 adc row_tmp
771b: 8d 25 03 sta map_row_addr
771e: ad 26 03 lda map_row_addr+1
7721: 6d 28 03 adc row_tmp+1
7724: 8d 26 03 sta map_row_addr+1
7727: 0e 27 03 asl row_tmp
772a: 2e 28 03 rol row_tmp+1
772d: 0e 27 03 asl row_tmp
7730: 2e 28 03 rol row_tmp+1
7733: ad 25 03 lda map_row_addr ;add result to map base address
7736: 18 clc
7737: 6d 27 03 adc row_tmp
773a: 8d 25 03 sta map_row_addr
773d: ad 26 03 lda map_row_addr+1
7740: 6d 28 03 adc row_tmp+1
7743: 8d 26 03 sta map_row_addr+1
7746: 60 rts
7747: 0c 62 random_value .dd2 $620c
7749: ff .dd1 $ff ;unused?
774a: 00 .dd1 $00 ;unused?
; Text page 1 base addresses for mini-map lines (upper-right corner).
774b: 9d 1d 9d 1d+ minimap_addr_lo .bulk 9d1d9d1d9d1d9d45c5
7754: 04 05 05 06+ minimap_addr_hi .bulk 040505060607070404
; Characters to use for mini-map and full map.
775d: a0 minimap_chars .dd1 “ ” ;normal space
775e: 20 .dd1 ‘ ’ ;wall: inverse space
775f: aa .dd1 “*” ;player
7760: d3 .dd1 “S”
7761: cd .dd1 “M”
7762: d2 .dd1 “R”
7763: c2 .dd1 “B”
7764: c7 .dd1 “G”
7765: c6 .dd1 “F”
7766: a0 .dd1 “ ”
7767: d4 .dd1 “T”
7768: c3 .dd1 “C”
7769: d0 .dd1 “P”
776a: a4 .dd1 “$” ;mimic
776b: c8 .dd1 “H”
776c: d7 .dd1 “W”
776d: 00 00 00 00+ .bulk 0000000000000000000000000000 ;16-29 not stored in map
777b: 00 00 00 00+ .bulk 000000000000 ;dragon parts: inverse '@'
7781: c0 .dd1 “@” ;(blank)
7782: a4 .dd1 “$” ;chest
7783: 63 .dd1 $63 ;Inn: flashing '#'
7784: 2a .dd1 ‘*’ ;"poof": inverse asterisk
; Text page 1 base addresses for all lines. Used for mode 3 map.
7785: 00 80 00 80+ text1_addr_lo .bulk 008000800080008028a828a828a828a850d050d050d050d0
779d: 04 04 05 05+ text1_addr_hi .bulk 040405050606070704040505060607070404050506060707
; Move direction, for random movement.
77b5: 00 move_dir .dd1 $00
77b6: 01 .dd1 $01
77b7: 00 .dd1 $00
77b8: ff .dd1 $ff
; Convert a color X coordinate (0-139) to (0-39).
77b9: 00 00 00 01+ hi_res_col .bulk 0000000101010102020203030303040404050505050606060707070708080809
+ 0909090a0a0a0b0b0b0b0c0c0c0d0d0d0e0e0e0e0f0f0f101010101111111212
+ 12121313131414141415151516161616171717181818181919191a1a1a1a1b1b
+ 1b1c1c1c1c1d1d1d1e1e1e1e1f1f1f2020202021212122222222232323242424
+ 242525252626262627272727
7845: 27 27 27 27+ .bulk 2727272727272727 ;unused?
784d: aa text_time_indic .dd1 “*” ;turn timeout indicator char
784e: a0 .dd1 “ ”
784f: 00 .dd1 $00 ;unused?
;
; Pitch/duration for funeral song.
;
funeral_song_pitch
7850: 57 57 57 57+ .bulk 57575757484b4c57575a57
funeral_song_dur
785b: 6f 6f 17 6f+ .bulk 6f6f176f424f7a63535353
;
; Pitch/duration data for indexed single-tone.
;
7866: 10 44 40 90+ st_pitch_lo .bulk 10444090080170ec
786e: 00 00 00 00+ st_pitch_hi .bulk 0000000000000000
7876: 10 1c 0d 28+ st_dur_lo .bulk 101c0d280c401522
787e: 00 00 00 00+ st_dur_hi .bulk 0000000000000000
7886: ff 02 fb fc+ st_pitchadj_lo .bulk ff02fbfc00070cfe
788e: ff 00 ff ff+ st_pitchadj_hi .bulk ff00ffff000000ff
;
; Pitch/duration data for indexed dual-tone.
;
7896: 77 40 c0 dt_pitch1 .bulk 7740c0
7899: 07 07 20 dt_dur1 .bulk 070720
789c: 00 00 00 dt_pitchadj1 .bulk 000000
789f: f0 60 ff dt_pitch2 .bulk f060ff
78a2: 07 07 20 dt_dur2 .bulk 070720
78a5: 00 00 00 dt_pitchadj2 .bulk 000000
No exported symbols found.