A simulator for Activision's PITFALL! for the Atari 2600. I converted the LFSR from 6502 code that David Crain wrote to generate the maps.
; ; PITFALL! Simulator ; ; A simulator for Activision's PITFALL! for the Atari 2600. ; I converted the LFSR from 6502 code that David Crain ; wrote to generate the maps. I used this article to help ; determine how the map worked: ; article: https://evoniuk.github.io/posts/pitfall.html ; However, I found that some of the descriptions weren't ; complete. Digging around, I found a more complete version ; here: https://meatfighter.com/pitfall/ ; The map link provided in the original article also has some ; descrpancies. I ended up finding actual footage of the rooms ; to validate the routines. This program is mmeant as an easy ; way to traverse the map, but without all the game play. ; For testing, I used this long play to check the board was ; accurate: https://www.youtube.com/watch?v=pslbO6Fddhw ; ; 01/04/2023 - Steven Reid ; ; +++ ; Header and startup ; org 16514 ; stored in REM at top (ZX81) jr start ; jump to start ; Title and copyright (will show when listed) copy: db _as,_p_,_i_,_t_,_f_,_a_,_l_,_l_,_sp,_s_ db _i_,_m_,_as,_as,_s_,_l_,_r_,_sl,_2_,_3_ db _as,$76,$76 ; some data (since we are here) room: db $c4 player: db 0 ; player bits as follow: ; 0 - above/below ; 1 - player left/right ; 6 - no wall/wall ; 7 - wall left/right start: call slow ; SLOW is required. call cls ; clear screen / exapnd screen ; end header ; --- ; +++ ; Main Program ; call print_title ; print title and legend call move_start ; move to seed (start of game) main_loop: call print_rooms ; will print rooms on screen call get_move call check_move jr main_loop ; end main ; --- ; +++ ; Get Move ; ; Routine won't return until a key is pressed. ; Screen is upated until that happens. ; ; in: none ; out: a = character code of pressed key ; destroys: af, bc, hl, de ; get_move: ; did the player make a move? ld bc,(last_k) ; get key information inc c ; Test if NOKEY pressed jr z,get_move ; not yet then loop dec c ; restore c call findchar ; yep, grab character pressed ld a,(hl) ret ; return ; end get move ; --- ; +++ ; Check Move ; check_move: ; compare keys (only left or right moves) cp $70 ; (cursor up) jp z,move_up cp $71 ; (cursor down) jp z,move_down cp $72 ; (cursor left) jp z,move_left cp $73 ; (cursor right) jp z,move_right cp _5_ ; '5' (left) jp z,move_left cp _6_ ; '6' (down) jp z,move_down cp _7_ ; '7' (up) jp z,move_up cp _8_ ; '8' (right) jp z,move_right cp _a_ ; 'a' (left) jp z,move_left cp _d_ ; 'd' (right) jp z,move_right cp _r_ ; 'r' (reset) jp z,move_start cp _s_ ; 's' (down) jp z,move_down cp _w_ ; 'w' (up) jp z,move_up ret ; do nothing! ; end check move ; --- ; +++ ; Move right - LSFR ; room' = room << 1 | (bit3 + bit4 + bit5 + bit7) ; input: none ; uses: room, a, hl, b ; move_right: ld hl,player ; get player information bit 0,(hl) ; above ground? jr z,move_right_above ; yes! ; check if player can move past wall bit 1,(hl) ; player on left? jr nz,move_right_below ; no, allow move bit 6,(hl) ; is there a wall? ret nz ; yes, then return (no move) move_right_below: ld b,3 jr move_right_lsfr move_right_above: ld b,1 move_right_lsfr: res 1,(hl) ; move player to left (set bit 1 to 0) ld hl,room ; set hl to room mr_loop: ld a,(hl) ; load a with room sla a ; could also be add a,a xor (hl) ; XOR with room sla a xor (hl) sla a sla a xor (hl) sla a rl (hl) ; rotate carry into room djnz mr_loop ret ; end move right ; --- ; +++ ; Move left - LFSR ; room' = room >> 1 | ((bit4 + bit5 + bit6 + bit0) * 128) ; input: none ; uses: room, a, hl, b ; move_left: ld hl,player ; get player information bit 0,(hl) ; above ground? jr z,move_left_above ; yes! ; check if player can move past wall bit 1,(hl) ; player on right? jr z,move_left_below ; no, allow move bit 6,(hl) ; is there a wall? ret nz ; yes, then return (no move) move_left_below: ld b,3 jr move_left_lsfr move_left_above: ld b,1 move_left_lsfr: set 1,(hl) ; move player to right (set bit 1 to 1) ld hl,room ; set hl to room ml_loop: ld a,(hl) ; load a with room sla a xor (hl) sla a xor (hl) sla a sla a rl a xor (hl) srl a rr (hl) ; rotate carry into room djnz ml_loop ret ; end move left ; --- ; +++ ; Move up ; Move player to above ground move_up: ld hl,player ; get player information bit 0,(hl) ; below ground? (bit 0 is 1) ret z ; no, then return (already there) bit 6,(hl) ; at ladder? (is wall bit 1?) ret z ; no, then return (no move) ; okay, see if can reach ladder bit 1,(hl) ; on left (bit 1 is 0?) jr z,check_left ; yes, then check left ; check right bit 7,(hl) ; wall on right? (bit 7 is 1) ret nz ; yes, then return (no move) jr can_move_up check_left: bit 7,(hl) ; wall on left? (bit 7 is 0) ret z ; yes, then return (no move) can_move_up: ; okay, player can move up! res 0,(hl) ; move player up (bit 0 set to 0) ret ; end move up ; --- ; +++ ; Move Down ; Move player to above ground move_down: ld hl,player ; get player information bit 0,(hl) ; above ground? (bit 0 is 0) ret nz ; no, then return (already there) bit 6,(hl) ; at ladder? (is wall bit 1?) ret z ; no, then return (no move) ; okay, player can move down! set 0,(hl) ; move player down (bit 0 set to 1) ; okay, move player to opposite side of wall bit 1,(hl) ; player on left (bit 1 is 0?) jr z,check_wall_left ; yes, then check left ; check wall right bit 7,(hl) ; wall on right? (bit 7 is 1) ret z ; no, then return (all done) res 1,(hl) ; yes, then move player to left ret check_wall_left: bit 7,(hl) ; wall on left? (bit 7 is 0) ret nz ; no, then return (all done) set 1,(hl) ; yes, then move player to right ret ; end move down ; --- ; +++ ; Move to start ; Resest's player's location to seed value. move_start: ld hl,room ; set hl to room ld (hl),$c4 ; set to start of game inc hl ld (hl),$00 ; move player to left/above ground (0) ret ; end move to start ; --- ; +++ ; Print Rooms ; print_rooms: call print_borders ; and print the intial room stuff call print_player ; place player in room call print_room ; print room ld hl,$0250 ; longer pause call delay ret ; end print rooms ; --- ; +++ ; Print Player ; Places player in room (left/right and up/down) ; print_player: ld bc,12+9*256 ; left/top to start ld a,_p_ ; what to print ld hl,player ; grab player bit 0,(hl) ; above ground? jr z,above_ground ; yes, skip ahead ; below ground ld a,$b5 ; inverted p ld b,11 ; move to below ground above_ground: bit 1,(hl) ; on left? jr z,on_left ; yes, skip ahead ; on right ld c,19 ; move to right on_left: push af ; save a call print_at pop af jp $10 ; print player ; end print player ; --- ; +++ ; Print Room ; For now, will print the value of the room ; In future, I want to print what the room decodes to. print_room: ; print map number ; move cursor to room ld bc,17+15*256 call printat ld a,(room) ; load a with room ld d,a ; put in d call print_binary ld a,_sp rst $10 ; print trees first ld bc,12+7*256 call print_at ld a,d ; load a with room ld hl,trees rlca ; rotate them to start rlca and %00000011 ; only need last two bits ld b,a xor a loop: add a,9 djnz loop ld c,a ; add them into bc ld b,0 add hl,bc ; move to message call print ; and print it! ; now print hazard in room ld bc,13+9*256 call print_at ld a,d ; load a with room rrca ; rotate them to start rrca rrca and %00000111 ; only need last three bits ld e,a ; save hazard in e ; rest any wall information ld hl,player res 6,(hl) ; assume no wall ; is this a hole location? cp %00000000 ; 1 hole? jr z,check_if_left_wall ; yes, jump to check which wall to print cp %00000001 ; 3 holes? jr nz,print_hazard ; no, keep going ; print where wall is check_if_left_wall: set 6,(hl) ; there is a wall! (bit 6 is 1) res 7,(hl) ; assume on left (bit 7 is 0) bit 7,d ; check if wall on left jr nz,print_wall_right print_wall_left: ld bc,13+11*256 ; move position down call print_at ld a,$05 jr print_wall print_wall_right: set 7,(hl) ; set wall on right (bit 7 is 1) ld bc,18+11*256 ; move position down call print_at ld a,$85 print_wall: rst $10 ; now print ladder ld bc,15+11*256 ; move position down call print_at ld a,$ad rst $10 ; now go print holes! ld bc,13+10*256 ; reset location call print_at print_hazard: ld hl,pits ld b,e ; get hazard xor a loop2: add a,6 djnz loop2 ld c,a ; add them into bc ld b,0 add hl,bc ; move to message call print ; and print it! test_treasure: ld a,e ; restore hazard cp %00000101 ; is treasure? jr nz,check_croc_vine ; no ld a,_sp rst $10 ld a,d ; restore room and %00000011 ; only need last two bits ld hl,treasure ld b,0 ld c,a add hl,bc ld a,(hl) jp $10 check_croc_vine: ld bc,15+7*256 ; reset to vine call print_at ld a,e ; restore hazard cp %00000100 ; is croc? jr nz,test_vines bit 1,d ; test bit 1 (vine) ld a,_sp ; print space jr z,print_croc ; no vine ld a,_sl ; print vine print_croc: rst $10 ld e,a print_bottom_vine: ld bc,14+8*256 ; reset to vine call print_at ld a,e jp $10 test_vines: ld e,0 ; no vine printed cp %00000010 ; tar pit with vine jr z,print_vine cp %00000011 ; quicksand with vine jr z,print_vine cp %00000110 ; shifting tar pit with vine jr nz,print_object ; skip ahead! print_vine: ld a,_sl rst $10 ld e,a ; vine was printed! print_object: ; print objects ld bc,13+8*256 call print_at ld a,d ; load a with room ld hl,objects and %000000111 ; only need last three bits ld b,a xor a loopo: add a,7 djnz loopo ld c,a ; add them into bc ld b,0 add hl,bc ; move to message call print ; and print it! ld a,e ; check if bottom vine and a jr nz,print_bottom_vine all_done: ret ; --- ; +++ ; Print Digit String and Copy Digit String ; ; in: a (binary value) ; h = 1 if no leading spaces, 0 if leading spaces ; destroys: af,bc,hl ; preserves: de ; ; Prints a binary string to the text window. print_binary: ld c,0 ld b,1 ; set skip flag ld h,256-100 ; hundreds call pb_num1 ld h,256-10 ; tens call pb_num1 ld b,0 ; unset skip flag for last digit ld h,256-1 ; ones pb_num1: ld l,$1c-1 ; reset l to zx81 0 -1 pb_num2: inc l ; next digit add a,h ; sub digit jr c,pb_num2 ; until we are too far sub h ; then add back push af ; save digit ld a,l ; set a to digit in l djnz printnum ; is b==0? Then jump to printnum cp $1c ; at zero? jr nz,printnum ; no, print number inc b ; reset skip flag ld a,c ; check if leading spaces and a ; check if zero jr nz,skipnum ; no, then skip printing ; if yes, a is 0 which is a space! printnum: rst $10 ; print digit (or leading space) skipnum: pop af ; restore digit ret ; end print binary ; --- ; +++ ; My Print At ; stops when reaches $ff ; print_at: push hl push de push bc ; call printat ; this is faster... ld hl,(d_file) inc hl ld de,33 pa_loop: add hl,de djnz pa_loop ld b,0 add hl,bc ld (df_cc),hl pop bc pop de pop hl ret ; end my print at ; --- ; +++ ; Print string at HL ; stops when reaches $ff ; print: ld a,(hl) ; load character inc hl ; increment memory cp $ff ; last character? ret z ; yep, return rst $10 ; nope, print it! jr print ; loop ; end print ; --- ; +++ ; Delay ; ; set bc to speed ; uses check_break to exit delay_count: dw $0000 delay: ld (delay_count),hl ; save delay call check_break ; check if done ld hl,(delay_count) ; grab what to test dec hl ; subtract 1 ld a,h ; check if done or l jr nz,delay ; not zero, keep going! ret ; pause is done! ; end delay ; --- ; +++ ; Break ; ; preserves state, but will exit if SPACE is pushed check_break: exx ; save register states ; did the player press break key (space)? call $0f46 ; was break pressed? (break-1 ROM routine) jr nc,break ; no, exit as normal exx ; restore registers ret ; and return ; yes, exit the program as normal break: rst $0008 ; call ERROR-1 reset db $ff ; with error code 0 (normal exit) ; end break ; --- ; +++ ; Print Title print_title: ; first, print title ld bc,10+2*256 call printat ld hl,title call print ; priht left legend ld bc,0+4*256 ld hl,left_legend call print_line_loop ; priht right legend ld bc,22+4*256 ld hl,right_legend call print_line_loop ; print where at ld bc,12+15*256 call printat ld hl,whereat call print ; priht treasure legend ld bc,12+17*256 ld hl,treasures call print_line_loop ret ; end print titles ; --- ; +++ ; Print Loop ; print_line_loop: ; hl points to what to print at ld e,(hl) inc hl ld d,(hl) inc hl ld a,(hl) inc hl ld (loc_jump+1),de ; save what to add each loop add a,b ; add b to a (new end) ld (loc_length+1),a ; save end of loop print_loop: call print_at location: push hl call print pop hl loc_jump: ld de,loc_jump add hl,de inc b ld a,b loc_length: cp 14 jr nz,print_loop ret ; end print loop ; --- ; +++ ; Print Borders print_borders: ; then print each row of screen (clears previous room) ld bc,11+4*256 ld hl,screen call print_line_loop ret ; end print borders ; --- ; +++ ; Data! ; bits 0-2: Objects objects: db _sp,_sp,_sp,_sp,_sp,_0_,$ff ; bit 000 - one rolling log db _sp,_sp,_sp,_sp,_0_,_0_,$ff ; bit 001 - two rolling logs together db _sp,_sp,_sp,_0_,_sp,_0_,$ff ; bit 010 - two rolling logs spread out db _0_,_sp,_sp,_0_,_sp,_0_,$ff ; bit 011 - three rolling logs db _sp,_sp,_sp,_sp,_sp,_o_,$ff ; bit 100 - one stationary log db _o_,_sp,_sp,_o_,_sp,_o_,$ff ; bit 101 - three stationay logs db _sp,_sp,_sp,_sp,_sp,_f_,$ff ; bit 110 - fire db _sp,_sp,_sp,_sp,_sp,_s_,$ff ; bit 111 - snake ; if bit 1 and bits 3-5 are 100 (crocodiles), add vine db _v_,_i_,_n_,_e_,_sp ; add vine ; bits 0-1 determine treasure type if bits 3-5 are 101 (treasure) treasure: db _m_,_s_,_g_,_r_ ; bit 000 - money ; bit 001 - silver ; bit 010 - gold ; bit 011 - ring ; bits 3-5: Pit Type pits: db $88,$88,$89,$88,$88,$ff ; bit 000 - hole with ladder db $89,$88,$89,$88,$89,$ff ; bit 001 - hole with ladder with 2 holes db _mi,_lp,_eq,_rp,_mi,$ff ; bit 010 - tar pit with vine db _mi,_lt,_eq,_gt,_mi,$ff ; bit 011 - quicksand with vine db _mi,_c_,_c_,_c_,_mi,$ff ; bit 100 - crocodiles in the water db _sp,_lp,_eq,_rp,$ff,$ff ; bit 101 - shifting tar pit with treasure db _sp,_lp,_eq,_rp,_sp,$ff ; bit 110 - shifting tar pit with vine db _sp,_lt,_eq,_gt,_sp,$ff ; bit 111 - shifting quicksand ; bits 6&7: Trees trees: db _y_,_sp,_t_,_sp,_sp,_t_,_sp,_y_,$ff ; bit 00 - trees | | | | db _y_,_sp,_y_,_sp,_y_,_sp,_y_,_sp,$ff ; bit 01 - trees | | | | db _t_,_sp,_t_,_sp,_t_,_sp,_t_,_sp,$ff ; bit 10 - trees | | | | db _y_,_sp,_t_,_sp,_t_,_sp,_y_,_sp,$ff ; bit 11 - trees | || | ; bits 7: Underground Wall ; if a ladder in scene, wall is is either left or right ; bit 0 - wall left ; bit 1 - wall right ; screen title: db _p_,_i_,_t_,_f_,_a_,_l_,_l_,_sl,_sp,_s_,_i_,_m_,$ff screen: dw 11 ; increment amount db 10 ; and number db $80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$ff db $80,$0a,$09,$0a,$09,$0a,$09,$0a,$09,$80,$ff db $80,$0a,$09,$0a,$09,$0a,$09,$0a,$09,$80,$ff db $80,$00,$00,$00,$00,$00,$00,$00,$00,$80,$ff db $80,$00,$00,$00,$00,$00,$00,$00,$00,$80,$ff db $80,$00,$00,$00,$00,$00,$00,$00,$00,$80,$ff db $80,$88,$88,$88,$88,$88,$88,$88,$88,$80,$ff db $80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$ff db $80,$8a,$8a,$8a,$8a,$8a,$8a,$8a,$8a,$80,$ff db $80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$ff whereat: db _r_,_o_,_o_,_m_,_sp,$ff ; ; legends ; left_legend: dw 10 ; increment amount db 12 ; and number db _sl,_sp,_v_,_i_,_n_,_e_,$ff,$ff,$ff,$ff db _o_,_sp,_l_,_o_,_g_,$ff,$ff,$ff,$ff,$ff db _0_,_sp,_r_,_o_,_l_,_l_,_i_,_n_,_g_,$ff db _c_,_sp,_c_,_r_,_o_,_c_,_s_,$ff,$ff,$ff db _s_,_sp,_s_,_n_,_a_,_k_,_e_,$ff,$ff,$ff db _f_,_sp,_f_,_i_,_r_,_e_,$ff,$ff,$ff,$ff db $89,_sp,_h_,_o_,_l_,_e_,$ff,$ff,$ff,$ff db $ad,_sp,_l_,_a_,_d_,_d_,_e_,_r_,$ff,$ff db $85,_sp,_r_,_i_,_g_,_h_,_t_,$ff,$ff,$ff db _sp,_sp,_w_,_a_,_l_,_l_,$ff,$ff,$ff,$ff db $05,_sp,_l_,_e_,_f_,_t_,$ff,$ff,$ff,$ff db _sp,_sp,_w_,_a_,_l_,_l_,$ff,$ff,$ff,$ff right_legend: dw 11 ; increment amount db 10 ; and number db _mi,_lp,_rp,_mi,_sp,_t_,_a_,_r_,$ff,$ff,$ff db _sp,_p_,_i_,_t_,$ff,$ff,$ff,$ff,$ff,$ff,$ff db _mi,_lt,_gt,_mi,_sp,_q_,_u_,_i_,_c_,_k_,$ff db _sp,_s_,_a_,_n_,_d_,$ff,$ff,$ff,$ff,$ff,$ff db _sp,_lp,_rp,_sp,_s_,_h_,_i_,_f_,_t_,_mi,$ff db _sp,_i_,_n_,_g_,_sp,_t_,_a_,_r_,$ff,$ff,$ff db _sp,_p_,_i_,_t_,$ff,$ff,$ff,$ff,$ff,$ff,$ff db _sp,_lt,_gt,_sp,_s_,_h_,_i_,_f_,_t_,_mi,$ff db _sp,_i_,_n_,_g_,_sp,_q_,_u_,_i_,_c_,_k_,$ff db _sp,_s_,_a_,_n_,_d_,$ff,$ff,$ff,$ff,$ff,$ff treasures: dw 9 ; increment amount db 4 ; and number db _m_,_sp,_m_,_o_,_n_,_e_,_y_,$ff,$ff db _g_,_sp,_g_,_o_,_l_,_d_,$ff,$ff,$ff db _s_,_sp,_s_,_i_,_l_,_v_,_e_,_r_,$ff db _r_,_sp,_r_,_i_,_n_,_g_,$ff,$ff,$ff ; --- ; +++ ; ; Data and Defines ; ; ZX81 system vars d_file: equ $400c df_cc: equ 16398 last_k: equ 16421 margin: equ 16424 s_posn: equ 16441 frames: equ 16436 ; ZX81 ROM functions kscan: equ $02bb findchar: equ $07bd stop: equ $0cdc slow: equ $0f2b fast: equ $02e7 save: equ $02f9 printat: equ $08f5 pause: equ $0f35 cls: equ $0a2a ; ZX81 Characters (not ASCII) _sp: equ $00 _qu: equ $0b _lb: equ $0c _dl: equ $0d _cl: equ $0e _lp: equ $10 _rp: equ $11 _gt: equ $12 _lt: equ $13 _eq: equ $14 _pl: equ $15 _mi: equ $16 _as: equ $17 _sl: equ $18 _sc: equ $19 _cm: equ $1a _pr: equ $1b _0_: equ $1c _1_: equ $1d _2_: equ $1e _3_: equ $1f _4_: equ $20 _5_: equ $21 _6_: equ $22 _7_: equ $23 _8_: equ $24 _9_: equ $25 _a_: equ $26 _b_: equ $27 _c_: equ $28 _d_: equ $29 _e_: equ $2a _f_: equ $2b _g_: equ $2c _h_: equ $2d _i_: equ $2e _j_: equ $2f _k_: equ $30 _l_: equ $31 _m_: equ $32 _n_: equ $33 _o_: equ $34 _p_: equ $35 _q_: equ $36 _r_: equ $37 _s_: equ $38 _t_: equ $39 _u_: equ $3a _v_: equ $3b _w_: equ $3c _x_: equ $3d _y_: equ $3e _z_: equ $3f ; end defines ; ---