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
; ---