;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
ctype: defb 0 ;CALL type of current call operation (0/1/2/3)
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
var_ops: ;VAR:0 1 2 3 4 5 6 7
defw call_fv, zstorew, zstoreb,zpprop, zread, prchar, prnum,z_rand
defw zput, zpull, split_w, sel_w,call_fv, erase_w,erase_l,setcur
defw getcpos, style, sbfmde, ostrm, istrm, sfx, rchr, scantbl
defw znot, call_pv, call_pv, ztok, zencode, cpytab, prtbl,ck_arg
;
call_pv:
ld a,1
jr call_gen
;
callfv2:
ld b,8
ld hl,v_arg1
lp2: ld e,(hl)
inc hl
ld d,(hl)
inc hl
inc hl
inc hl
djnz lp2
call_fv:
xor a
call_gen:
ld (ctype),a
ld hl,(v_arg1) ;Packed routine address
ld a,h
or l ;<< v0.04 Return false if HL = 0
jr nz,call_g1
scf
jp ret_hl
call_g1: ;>> v0.04
call upack_addr ;Unpack it
call ipeek
ld b,a ;B = no. of local variables
ld a,(v_argc)
dec a
ld c,a ;C = no. of parameters
cp b ;IF C <= B, ok
jr c,cfv1
jr z,cfv1
ld c,b ;Ignore surplus parameters
cfv1: push hl
push de ;EHL -> start of routine
call mkframe
jp nc,spfail2
push hl
pop ix ;IX -> new frame
ld hl,(zpc)
ld (ix+0),l
ld (ix+1),h
ld hl,(zpc+2)
ld (ix+2),l
ld (ix+3),h ;Old ZPC into frame
ld (ix+35),c ;No. of parameters
ld a,c
or a
jr z,noarg
push ix
ld hl,v_arg2
arglp: ld a,(hl) ;Args copied into frame
ld (ix+4),a
inc hl
inc ix
ld a,(hl)
ld (ix+4),a
inc ix
inc hl
inc hl
inc hl
dec c
jr nz,arglp
pop ix
noarg: ld hl,(rsp)
ld a,(ctype)
ld (ix+34),a ;Procedure, function or interrupt?
ld (ix+36),l
ld (ix+37),h ;Routine stack pointer
pop hl
ld (zpc+2),hl
pop hl ;New ZPC
ld (zpc),hl
ld a,(zver) ;<< v0.04 support for v3-style calls
cp 5 ;<< v1.10 v4 uses v3-style calls >>
ccf
ret c ;v1-v4: SET up the initial values of locals
ld hl,(v_arg1) ;Packed routine address
call upack_addr ;Unpack it
call ipeek
or a
scf
ret z ;No locals
ld b,a ;B = no. of local variables
ld hl,(zsp)
ld de,4
add hl,de ;HL->local vars
ld a,(v_argc) ;A = no. of parameters (which override the
;initial values)
ld d,a ;D = no. of parameters
argl: push bc
call zpcipeek ;Get default parameter into BC
ld b,a
call zpcipeek
ld c,a
ld a,d
dec d
jr z,argl1 ;Argument was provided. Skip.
inc hl
inc hl
jr argl2
;
argl1: inc d ;To counterbalance the dec d above, so that
ld (hl),c ;the test works the next time round
inc hl ;Copy in the initial values, flipping them
ld (hl),b ;to little-endian as we go
inc hl
argl2: pop bc
djnz argl
scf
ret ;>> v0.04
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
ck_arg: ld ix,(zsp)
ld a,(v_arg1) ;Argument number to check
cp (ix+35) ;No. of arguments
jp z,branch
jp c,branch ;IF A <= no. of args, it is provided.
jp nbranch ;Not provided.
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;PUSH and pop...
;
zput: ld hl,(v_arg1)
call zpush
scf
ret
;
zpull: call zpop ;HL = value
ld a,(v_arg1) ;A = variable number
scf
jp put_var
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
zstorew:
ld hl,(v_arg1)
ld de,(v_arg2)
add hl,de
add hl,de
ld bc,(v_arg3)
ld a,b
call trap_poke ; << v0.03 >> trap writes to location 11h
inc hl
ld a,c
jr trap_poke ; << v0.03 >> trap writes to location 11h
;
zstoreb:
ld hl,(v_arg1)
ld de,(v_arg2)
add hl,de
ld a,(v_arg3)
;
;<< v0.03 IF there's a write to Flags 2 (at address 11h) then flush buffers
;
trap_poke:
push af
ld a,h
or a
jr nz,trap1
ld a,l
cp 11h
jr nz,trap1
push hl
call flush_buf
call ZXUSCR
pop hl
trap1: pop af
call ZXPOKE
scf
ret
;
; >> v0.03
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
zpprop: ld de,(v_arg1) ;DE = object no.
ld a,d ;<< v1.01
or e
scf
ret z ;>> v1.01 Object 0 errors
ld bc,(v_arg2) ;C = property no.
ld hl,(v_arg3) ;HL = value
jp putprop
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;Screen operations. These mostly map directly to ZXIO functions
;
erase_w:
call flush_buf
ld a,(v_arg1)
jp ZXERAW
;
erase_l:
ld a,(v_arg1)
dec a
scf
ret nz
call flush_buf
jp ZXERAL
;
split_w:
call flush_buf
ld a,(v_arg1)
jp ZXSWND
sel_w: call flush_buf
ld a,(v_arg1)
xor 1
ld (cwin),a
xor 1
jp ZXUWND
;
getcpos:
call flush_buf
call ZXGETX
push hl
call ZXGETY
ex de,hl
pop bc
ld d,0 ;DE = Y
ld b,0 ;BC = X
ld hl,(v_arg1)
ld a,b
call ZXPOKE
inc hl
ld a,c
call ZXPOKE
inc hl
ld a,d
call ZXPOKE
inc hl
ld a,e
call ZXPOKE
scf
ret
;
style: call flush_buf
ld a,(v_arg1)
jp ZXSTYL
;
setcur: call flush_buf
ld a,(v_arg1)
ld b,a
ld a,(v_arg2)
ld c,a
jp ZXSCUR
;
prchar: ld hl,(v_arg1)
call ll_zchr
scf
ret
;
sbfmde: ld a,(v_arg1) ;SET buffer mode
ld (bufmde),a
or a
call z,flush_buf
scf
ret
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
prnum:
ld de,numbuf
ld hl,(v_arg1)
bit 7,h ;Negative?
jr z,prnum1
ld a,'-'
ld (de),a
inc de
call neghl
prnum1: call spdec
ld a,'$'
ld (de),a
ld de,numbuf
opbuf: ld a,(de)
cp '$'
scf
ret z
ld l,a
ld h,0
push de
call ll_zchr
pop de
inc de
jr opbuf
numbuf:
defb '-00000$'
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;Text entry interrupt...
;
tr_a1: defw 0
tr_a2: defw 0
tr_a3: defw 0
tr_a4: defw 0 ;Arguments
;
timer: defb 0 ;IN the timer already?
;
rch_timer:
ld c,3 ;Timer called from read_char
ld de,(v_arg3)
ld (tr_a3),de
ld hl,(v_arg4)
ld (tr_a4),hl
ld a,(timer)
or a
ret nz
inc a
ld (timer),a
jr rch_t1
;
timer_int:
ld c,2 ;Timer called from read_line
ld a,(timer)
or a
ret nz
inc a
ld (timer),a
;
;DE = routine, HL = time
;
ld (tr_a3),hl
ld (tr_a4),de
rch_t1: ld hl,(v_arg1)
ld (tr_a1),hl
ld hl,(v_arg2)
ld (tr_a2),hl
ld (v_arg1),de ;Routine address
ld a,1
ld (v_argc),a
pop hl ;Return address, we don't use this.
ld a,c
jp call_gen
;
rchr_iret:
ld hl,(tr_a1)
ld (v_arg1),hl
ld hl,(tr_a2)
ld (v_arg2),hl
ld hl,(tr_a3)
ld (v_arg3),hl
ld a,3
ld (v_argc),a
ld a,(timer)
dec a
ld (timer),a
ld a,d
or e
jp z,rchr
ld hl,0
scf
jp ret_hl
;
timer_iret:
ld hl,(tr_a1)
ld (v_arg1),hl
ld hl,(tr_a2)
ld (v_arg2),hl
ld hl,(tr_a3)
ld (v_arg3),hl
ld hl,(tr_a4)
ld (v_arg4),hl
ld a,4
ld (v_argc),a
ld a,(timer)
dec a
ld (timer),a
ld a,d
or e
jr nz,iterm
;
;The input routine wants the z-program to have printed its data for it again
;after a return from a timer interrupt...
;
ld hl,(v_arg1)
inc hl
call peek64 ;Length of line
or a
jr z,zread
ld b,a
ld a,(zver)
cp 5
jr nc,iplp1
iplp4: call peek64 ;<< v1.10 reprint input line in the v4 model
or a
jp z,sreadt ;Reprint the line
inc hl
push hl
push bc
ld l,a
ld h,0
call ll_zchr
pop bc
pop hl
jr iplp4 ;>> v1.10 end reprint input line
iplp1: inc hl
call peek64 ;Print it
push hl
push bc
ld l,a
ld h,0
call ll_zchr
pop bc
pop hl
djnz iplp1
jr zread
;
;Terminate input at once...
;
iterm: ld hl,(v_arg1) ;Input buffer
call peek64
ld b,a ;B = no. of bytes
inc b ;+1 for length
xor a
ztlp: inc hl
call ZXPOKE ;Zap the buffer
djnz ztlp
jr aread2
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;Reading text
;
zread: ld a,(zver)
cp 5
jr c,sread
aread: ld hl,(v_arg1)
ld a,(v_argc)
ld de,0
cp 3
jr c,aread1
ld de,(v_arg3)
aread1: call flush_buf
call ZXINP ;Input line
ld a,b
or a ;Timeout
jr nz,aread2
ld hl,(v_arg3) ;Timeout
ld a,h
or l
jr z,aread2
ld de,(v_arg4)
ld a,d
or e
call nz,timer_int ;This function only returns if unsuccessful.
aread2:
ld hl,(v_arg1) ;<< v0.04 remove invalid characters
inc hl ;Length
call ZXPK64 ;A = actual length
or a
jr z,areadw ;No length?
ld b,a
areadv: inc hl
call ZXPK64 ;Character
call valid_char
call ZXPOKE
djnz areadv ;>> v0.04
areadw: ld hl,0dh
call ll_zchr
ld hl,(v_arg2)
ld a,h
or l
jr z,nopse
;
;Tokenise?
;
push bc
ld bc,0
ld a,c
ld hl,(v_arg1)
ld de,(v_arg2)
call tokenise
pop bc
nopse: ld l,b
ld h,0
scf
jp ret_hl
;
;<< v0.04 read for v3
;
;I'm going to do v3 input by converting the input format to v5-style before,
;and back afterwards. There's enough room, because the 1 byte for length
;matches the 1-byte terminator.
;
sreadt: call sstatus ;<< v1.10 rewritten for timer events in v4
call line_from_v5
ld hl,(v_arg1)
jr sreads
sread: call sstatus
ld hl,(v_arg1)
inc hl
xor a
call ZXPOKE ;Write current length = 0 (no passed data)
dec hl
sreads: ld de,0
ld a,(zver)
cp 4
jr c,sreadu
ld a,(v_argc)
ld de,0
cp 3
jr c,sreadu
ld de,(v_arg3) ;>> v1.10
sreadu: call flush_buf ;
call ZXINP ;Input line
push bc ;<< v1.10
call line_to_v5
pop bc ;>> v1.10
ld a,b ;<< v1.10 Handle timeouts in v4 input
or a ;Timeout
jr nz,sreadr
ld hl,(v_arg3) ;Timeout
ld a,h
or l
jr z,sreadr
ld de,(v_arg4)
ld a,d
or e
call nz,timer_int ;This function only returns if unsuccessful.
;>> v1.10
sreadr: ld hl,0dh
call ll_zchr
;
; << v1.10 >> v5 -> v4 line converter was here
;
sread1: ld hl,(v_arg2)
ld a,h
or l
scf
ret z
;
;Tokenise?
;
push bc
ld bc,0
ld a,c
ld hl,(v_arg1)
ld de,(v_arg2)
call tokenise
pop bc
scf
ret
;>> v0.04
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;Read a character
;
rchr: ld a,(v_argc)
cp 2
ld de,0
jr c,rchr1
ld de,(v_arg2)
rchr1: call flush_buf
call ZXRCHR
or a
call z,rch_timer
call valid_char
ld l,a
ld h,0
scf
jp ret_hl
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;Output and input streams
;
ostrm: ld a,(v_arg1)
ld hl,(v_arg2)
jp ll_strm
;
istrm: ld a,(v_arg1)
ld hl,(v_arg2)
scf ;Instruction has no effect
ret
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
sfx: ld hl,(v_arg1)
ld de,(v_arg2)
ld bc,(v_arg3)
call ZXSND
scf
ret
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
z_rand: ld hl,(v_arg1)
call random
scf
jp ret_hl
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
znot: ld hl,(v_arg1)
ld a,h
cpl
ld h,a
ld a,l
cpl
ld l,a
scf
jp ret_hl
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;Tokenise & encode text
;
ztok: ld a,(v_arg4)
ld hl,(v_arg1)
ld de,(v_arg2)
ld bc,(v_arg3)
jp tokenise
;
zencode:
ld hl,(v_arg1) ;Text in
ld de,(v_arg3) ;offset
add hl,de
ld a,(v_arg2)
ld b,a
call encode
ld a,(encptr)
ld hl,(v_arg4) ;Destination
ld de,encbuf
zenc1: or a
scf
ret z
push af ;Transfer 3 Z-characters
ld a,(de)
ld b,a
call ZXPOKE
inc hl
inc de
ld a,(de)
ld c,a
call ZXPOKE
inc hl
inc de
pop af
sub 3 ;3 characters moved
ret c
jr zenc1
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;Scan table for byte or word pattern
;
scantbl:
ld a,(v_argc)
cp 4
ld a,82h
jr c,scant1
ld a,(v_arg4)
scant1: bit 7,a
jr z,scanbyt
and 7Fh ;<< v1.10: Only bits 0-7 are entry size >>
ld e,a
ld d,0 ;DE = step between table entries
ld bc,(v_arg3)
ld hl,(v_arg2)
scanw1:
push de
call peek64
ld d,a
inc hl
call peek64
ld e,a ;DE = word from table
dec hl
push hl
ld hl,(v_arg1)
call cphlde
pop hl
pop de
jr z,tmatch
add hl,de
dec bc
ld a,b
or c
jr nz,scanw1
jr tnmatch
;
scanbyt:
ld e,a
ld d,0 ;DE = step between table entries
ld bc,(v_arg3) ;Table length
ld hl,(v_arg2)
scanb1: push de
call peek64
ld e,a
ld a,(v_arg1)
cp e ;Byte to find
pop de
jr z,tmatch
add hl,de
dec bc
ld a,b
or c
jr nz,scanb1
tnmatch:
ld hl,0
call ret_hl
jp nbranch
;
tmatch: call ret_hl
jp branch
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;Copy_table
;
cpytab: ld hl,(v_arg2) ;Destination
ld a,l
or h
jr z,zerotab
ld de,(v_arg1) ;Source
ld bc,(v_arg3) ;Count
bit 7,b
jr nz,fwdcpy
;
;IF source < dest, use backward copy
;
call cphlde
jr c,fwdcpy
bwdcpy: add hl,bc
ex de,hl
add hl,bc
ex de,hl
bcpylp: dec de
dec hl
ex de,hl
call peek64
ex de,hl
call ZXPOKE
dec bc
ld a,b
or c
jr nz,bcpylp
scf
ret
;
fwdcpy: call absbc
fcpylp: ex de,hl
call peek64 ;Read from source
ex de,hl
call ZXPOKE ;Write to dest
inc hl
inc de
dec bc
ld a,b
or c
jr nz,fcpylp
scf
ret
;
zerotab:
ld hl,(v_arg1)
ld bc,(v_arg3) ;Size
call absbc
zerot1: xor a
call ZXPOKE
inc hl
dec bc
ld a,b
or c
jr nz,zerot1
scf
ret
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
prtbl: call flush_buf ;<< v0.04 >> Ensure the screen is up to date
ld a,(v_argc)
cp 3
jr nc,prtbl0 ;<< v0.04 >> IF there is 1 line, initialise
ld a,1 ; properly
ld (v_arg3),a
prtbl0: ld hl,(v_arg3) ;<< v1.04 deal with degenerate case
ld a,h ; when height is 0
or l
scf
ret z ;>> v1.04
call ZXGETX
ld e,l
call ZXGETY
ld d,l ;D=Y E=X
ld bc,(v_arg2) ;<< v1.02 rewritten for lines
ld hl,(v_arg1) ; longer than 256 chars
xor a
ld a,b ;<< v1.04 deal with degenerate case
or c ; when width is 0
scf
ret z ;>> v1.04
prtbl1: call doline
push de
ld de,(v_arg4)
add hl,de
pop de
inc a
inc d ;Next line
push hl
ld hl,v_arg3
dec (hl)
pop hl
jr nz,prtbl1 ;>> v1.02
scf
ret
;
doline: push de ;<< v1.02 rewritten for lines >256 chars
push bc
or a ;<< v0.04 Don't position the cursor if on 1st line
jr z,doln1 ;>> v0.04
ld b,d
ld c,e
inc b
inc c ;1-based
call ZXSCUR
pop bc
push bc
doln1: push bc
call peek64 ;ZSCII character
push hl
ld l,a
ld h,0
call ll_zchr
pop hl
inc hl ;HL = address of character to read
pop bc
dec bc
ld a,b
or c
jr nz,doln1
pop bc
pop de ;>> v1.02
ret
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; << v1.10: New line conversion functions
;
; Convert line from v3 format to v5.
;
line_to_v5:
ld hl,(v_arg1) ;input buffer
inc hl ;Length
call ZXPK64 ;A = actual length
or a
ret z ;No input; the 0 becomes a terminating null
ld b,a
sreadv: inc hl
call ZXPK64 ;Character
call valid_char ;Ensure it's valid
dec hl
call ZXPOKE ;Write it to previous slot
inc hl
djnz sreadv
sreadw:
xor a
jp ZXPOKE ;Terminating 0
;
; Convert line from v5 format to v3. Only used when recovering from a
; timeout.
;
line_from_v5:
ld hl,(v_arg1)
inc hl
ld bc,0 ;B = length, C = character being moved up
lv501: call ZXPK64
or a
jr z,l5eol
ld e,a ;E = character just read
ld a,c
call ZXPOKE ;Write character from previous slot
ld c,e ;C = next character to write back
inc hl
inc b
jr lv501
;
l5eol: ld a,c
call ZXPOKE ;Write back the last character
ld hl,(v_arg1)
inc hl
ld a,b ;A = count
jp ZXPOKE
;