******************************************************************************** * 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.