; Level 1 Basic for the TRS-80 Model 1 and Model 3.
;
; Uncomment one of these to choose the version.
;
;model  equ     1
;model  equ     3
;
; What's different between the Model 1 and Model 3 versions?
;
; Not very much.  They're quite similar at the source code level and the Model 3
; version is obviously meant to be compatible.  The Model 3 version has two
; new features:
;
;       Runs on Model 3 hardware
;       Line printer support via LPRINT and LLIST
;
; Due to LLIST the model 3 LIST command is different.  It lists lines continuously
; rather than listing 12 and waiting for the up-arrow key for each line
; thereafter or the enter key to quit.
;
; The Model 3 version allows anything after a GOTO's line number.  Perhaps that
; was to save 3 bytes in the ROM by eliminating a call.  This program will work
; on the Model 3 but not the Model 1:
;
;       10 GOTO20A
;       20 END
;
; Otherwise the implementation of BASIC is identical.
;
; At a binary level the ROMs look quite different but that's because the Model 3
; version was rebuilt from source.  A small number of changes account for I/O
; hardware differences.  Mostly things were rearranged to use a few wasted bytes.
; The cassette routines manage to save the most through slightly tighter coding.
; Another saving comes from the Model 3 NEW resetting the machine rather than
; reinitialize the BASIC program.
;
; The cassette bit rate is practically the same.  Counting cycles in putbit
; I found the ratios a bit off.  The entire routine is 3380 cycles on the Model 1
; and 4017 cycles on the Model 3 with pulse spaces of 222 and 251 cycles respectively.
; The putbit routine takes 1.188 times more cycles while the pulse spacing cycle
; ratio is 1.13.  Both are a little off the Z-80 clock ratio of about 1.17 for
; the Model 3's 2.0752 MHz over the Model 1's 1.77408 MHz.  Nevertheless, I
; have no doubt they're interoperable.
;
; With the surprising exception of the BASIC READY prompt the Level 1 ROM
; subroutine addresses from the T-BUG manual are the same.  I suspect part of
; the code re-arrangement was intended to preserve these.
;
;       $0010   display byte
;       $0B40   keyboard scan
;       $0EF4   load memory from cassette
;       $0F4B   save memory to cassette
;       $0FE9   cassette on
;       $01C9   BASIC READY prompt (Model 1)
;       $01B7   BASIC READY prompt (Model 3)
;
; In what may be no coincidence, $01C9 is the READY entry point for Model 1
; Level II BASIC and Model 3 Level II BASIC.  $01C9 will sort of work on
; Model 3 Level 1 but will not display the prompt and won't quite be
; properly initialized.
;
; The RST vector subroutines are re-arranged slightly.
;
; Most of the disassembly here was automatically generated.  By and large I've
; only annotated things in order to sort out the differences between the two
; ROMs.  More than a few of the labels are guesses -- beware!
;
 
drowmacron
defb[n]/256
defb[n]&255
endm
 
org00000h
sub00:di
ldhl,0ffh
ifmodel == 1
jpfindtop
nop
endif
ifmodel == 3
findtop:dech
lda,(hl)
jrcont_findtop
endif
 
; Searches for byte following rst.  If found returns to ret addr + 2.
; If not found, then adds 2nd byte after rst to address of that byte + 1
; and returns there.  Think of it as 'if not char then label'
lkchar:ex(sp),hl
rstskipspace
cp(hl)
jpcont_lkchar
 
put_lf:lda,00dh
; I don't use the official name as this outputs to multiple devices.
putch:
ifmodel == 1
exx
exaf,af'
callgtstat
endif
ifmodel == 3
exaf,af'
ina,(0ffh)
and002h
endif
jpcont_putch
 
def_skipspacemacro
skipspace:lda,(de)
cp020h
retnz
incde
jpskipspace
endm
 
ifmodel == 1
expr:callsub68
nop
jpcont_expr
nop
call_exprmacro
rstexpr
endm
endif
 
ifmodel == 3
def_skipspace
call_exprmacro
callexpr
endm
endif
 
cpHL_DE:lda,h
cpd
retnz
lda,l
cpe
ret
 
ifmodel == 1
nop
nop
def_skipspace
endif
 
ifmodel == 3
; Like put_lf, but does a bit more.
put_lf2:lda,00dh
; Sets output device to video.
vidputc:pushaf
xora
ld(outdev),a
popaf
jrputch
endif
 
sub07:popaf
callsub84
jpsay_what_de
 
nop
; Parse variable reference (either A,B,...Z or A(n)).  Returns address of
; variable in HL if and carry clear if successful.  Carry set if error.
lkvar:rstskipspace
sub041h
retc
cp01ah
ccf
retc
incde
anda
jrnz,getvarA
rstlkchar
defb'(',getvars-$-1
callsub75
inchl
addhl,hl
addhl,hl
jpc,say_how_de
pushhl
rstlkchar
defb')',say_what_de2-$-1
 
lkvar_chunkmacro
pophl
pushde
pushhl
ldhl,(memtop)
ldde,(freemem)
xora
sbchl,de
popde
rstcpHL_DE
endm
 
ifmodel == 1
lkvar_chunk
jpcont_lkvar
endif
 
ifmodel == 3
jrcont_lkvar
cont_findtop:cpl
ld(hl),a
cp(hl)
cpl
ld(hl),a
jrnz,findtop
ld(memtop),hl
ldhl,basic_mem
ld(freemem),hl
endif
 
; NMI entry point as triggered by reset button at back of keyboard.
nmi:di
ldsp,basic_mem
xora
 
ifmodel == 1
ld(status),a
out(0ffh),a
lda,00ch
rstputch
endif
 
ifmodel == 3
out(0e0h),a
out(0ffh),a
out(0e4h),a
lda,020h
out(0ech),a
lda,00ch
rstvidputc
endif
 
jpready
 
cont_lkvar:
ifmodel == 3
lkvar_chunk
endif
 
jpc,say_sorry
ldhl,(memtop)
sbchl,de
popde
xora
ret
 
getvars:xora
getvarA:ldh,040h
rla
rla
ldl,a
xora
ret
 
say_what_de2:jpsay_what_de
 
; Parse 0-9 and returns value in A and carry clear.  Carry set if not a digit.
getdig:lda,(de)
cp030h
retc
cp03ah
ccf
retc
incde
and00fh
ret
 
cont_lkchar:inchl
jrz,_2getdig
pushbc
ldc,(hl)
ldb,000h
addhl,bc
popbc
decde
_2getdig:incde
inchl
ex(sp),hl
ret
 
sub10:callclr24b
ldb,000h
ldc,b
rstskipspace
_1sub10:callsub11
jr_1sub10
 
sub11:callgetdig
jrc,_2sub12
set6,b
bit7,b
jrnz,_1sub12
callsub12
bit0,b
retz
decc
ret
 
sub12:callshiftdig
retz
exx
ldh,d
ldl,e
exaf,af'
ldc,a
exx
set7,b
popaf
_1sub12:bit0,b
retnz
incc
ret
 
_2sub12:rstlkchar
defb'.',_3sub12-$-1
bit0,b
set0,b
retz
_3sub12:popaf
bit6,b
retz
ldl,018h
ldh,000h
pushbc
pushde
exx
callnormCHL
ldbc,10
addix,bc
popde
ldbc,_4sub12
pushbc
pushde
jp_3sub128
 
_4sub12:popbc
pushde
rstlkchar
defb'E',_8sub12-$-1
rstlkchar
defb'+',_5sub12-$-1
jr_6sub12
 
_5sub12:rstlkchar
defb'-',_6sub12-$-1
set1,b
_6sub12:callclear24
_7sub12:callgetdig
jrc,_9sub12
set5,b
callshiftdig
jpnz,say_how_de
jr_7sub12
 
_8sub12:popde
xora
jr_10sub12
 
_9sub12:bit5,b
jrz,_8sub12
popaf
exx
lda,c
orh
jrnz,_15sub12
lda,l
exx
bit7,a
jpnz,say_how_de
bit1,b
jrz,_10sub12
neg
_10sub12:adda,c
_11sub12:anda
jrz,_14sub12
bit7,a
jrz,_12sub12
inca
pushaf
callsub122
jr_13sub12
 
_12sub12:deca
pushaf
callsub120
_13sub12:popaf
jr_11sub12
 
_14sub12:bit6,b
ret
 
_15sub12:exx
jpsay_how_de
 
; Clear the 24 bit C'H'L' register.  And clear bit 6 of B for some reason.
clr24b:res6,b
; Clear the 24 bit C'H'L' register.
clear24:exx
ldl,000h
ldh,l
ldc,l
exx
ret
 
; Multiply 24 bit C'H'L' register by 10 and add A with overflow into B'
; That is, a 32 bit result in B'C'H'L'
shiftdig:exaf,af'
exx
ldd,h
lde,l
lda,c
ldb,000h
pushaf
addhl,hl
rlc
rlb
addhl,hl
rlc
rlb
addhl,de
adca,c
ldc,a
lda,000h
adca,b
ldb,a
popaf
addhl,hl
rlc
rlb
exaf,af'
adda,l
ldl,a
lda,000h
adca,h
ldh,a
lda,000h
adca,c
ldc,a
lda,000h
adca,b
ldb,a
exx
ret
 
ifmodel == 1
; Figure out how much memory we have.
findtop:dech
lda,(hl)
cpl
ld(hl),a
xor(hl)
jrnz,findtop
ld(memtop),hl
xora
ld(status),a
ldsp,basic_mem
jp_1NEW_
endif
 
say_how_de:pushde
say_how:ldde,msg_HOW
jperrmsg
 
msg_HOW:ascii'HOW?',13
rws_msgmacro
msg_READY:ascii'READY',13
msg_WHAT:ascii'WHAT?',13
msg_SORRY:ascii'SORRY',13
endm
ifmodel == 1
rws_msg
endif
msg_BREAK_AT:ascii'BREAK AT',0
ready:ldsp,basic_mem
callctoff
ifmodel == 1
callput_lf
endif
ifmodel == 3
callput_lf2
endif
ldde,msg_READY
callprint
; Like ready but doesn't show the full 'READY\n>' prompt.  Just '>'.
getcmd:ldsp,basic_mem
callclear
; Ready but with short prompt and no clearing of basic run state.
cmdlive:ldix,input_buffer+72
callgetline
pushde
ldde,input_buffer
callgetint
lda,h
orl
popbc
jpz,exec
decde
lda,h
ld(de),a
decde
lda,l
ld(de),a
pushbc
pushde
lda,c
sube
pushaf
callfindline
pushde
jrnz,insert_line
; Line found; delete old line.
pushde
callskipfindHL
popbc
ldhl,(freemem)
callcopyDEtoBC
ldh,b
ldl,c
ld(freemem),hl
; Now put in new line unless it was just an empty line number in which
; case we just let the line be deleted and/or do nothing.
insert_line:popbc
ldhl,(freemem)
popaf
pushhl
cp003h
jrz,getcmd
adda,l
ldl,a
lda,000h
adca,h
ldh,a
ldde,(memtop)
rstcpHL_DE
jpnc,say_sorry_de
ld(freemem),hl
popde
callcopyDEtoHL
popde
pophl
callcopyDEtoBC
jrgetcmd
 
; Clear current execution position, continue position, gosub and for stacks.
clear:ldb,00ah
pushhl
ldhl,cont_ptr
_1clear:ld(hl),000h
inchl
djnz_1clear
ldde,basic_mem
pophl
all_tok:ret
 
ascii'LIST'
drowLIST_|8000h
ifmodel == 3
ascii'LLIST'
drowLLIST_|8000h
endif
ascii'RUN'
drowRUN_|8000h
ascii'NEW'
drowNEW_|8000h
ascii'CONT'
drowCONT_|8000h
ascii'CLOAD'
drowCLOAD_|8000h
ascii'CSAVE'
prog_tok:drowCSAVE_|8000h
ascii'NEXT'
drowNEXT_|8000h
ascii'LET'
drowLET_|8000h
ascii'INPUT'
drowINPUT_|8000h
ascii'IF'
drowIF_|8000h
ascii'ON'
drowON_|8000h
ascii'GOTO'
drowGOTO_|8000h
ascii'GOSUB'
drowGOSUB_|8000h
ascii'RESET'
drowRESET_|8000h
ascii'RETURN'
drowRETURN_|8000h
ascii'READ'
drowREAD_|8000h
ascii'RESTORE'
drowRESTORE_|8000h
ascii'REM'
drowREM_|8000h
ascii'DATA'
drowREM_|8000h
ascii'FOR'
drowFOR_|8000h
ascii'PRINT'
drowPRINT_|8000h
ascii'SET'
drowSET_|8000h
ascii'STOP'
drowSTOP_|8000h
ascii'END'
drowEND_|8000h
ascii'CLS'
drowCLS_|8000h
ifmodel == 3
ascii'LPRINT'
drowLPRINT_|8000h
endif
ong_tok:drowassign|8000h
ascii'GOTO'
drowon_goto|8000h
ascii'GOSUB'
drowon_gosub|8000h
func_tok:drowsay_what_de|8000h
ascii'RND'
drowRND_|8000h
ascii'ABS'
drowABS_|8000h
ascii'MEM'
drowMEM_|8000h
ascii'INT'
drowINT_|8000h
ascii'POINT'
drowPOINT_|8000h
to_tok:drowexpterm|8000h
ascii'TO'
drowTO_|8000h
step_tok:drowsay_what_de|8000h
ascii'STEP'
drowSTEP_|8000h
drowfor_top|8000h
 
def_print_tokmacro
print_tok:ascii'TAB'
drowTAB_|8000h
ascii'AT'
drowAT_|8000h
ascii'A$'
drowAstr|8000h
ascii'B$'
drowBstr|8000h
drowprint_lit|8000h
endm
 
ifmodel == 1
def_print_tok
endif
 
op_tokequ$-2
ascii'>'
drowgt_|8000h
ascii'='
droweq_|8000h
ascii'<'
drowlt_|8000h
then_tok:drowprog_sn|8000h
ascii'THEN'
drowTHEN_|8000h
drowstatement2|8000h
; Runs basic commands pointed to by DE.  Terminated by CR, I suspect.
; Can be handed either the input buffer or basic program.
; Checks DE to see if it matches a token in the table at HL+1.
; The table has an ascii string followed by a high-bit set big-endian word.
; Looks for match with dot allowing abbreviations.
; The clever bit is ending the table with an empty string by having
; two addresses in a row.  Thus when you see that second address
; you know that will be taken.
exec:ldhl,all_tok
_1exec:rstskipspace
pushde
lda,(de)
incde
inchl
cp(hl)
jrz,_2exec
bit7,(hl)
jrnz,tokmatches
jrskiptok
 
_2exec:lda,(de)
incde
inchl
cp(hl)
jrz,_2exec
bit7,(hl)
jrz,_4exec
tokmatches:decde
jrruntok
 
_4exec:cp02eh
jrz,_6exec
skiptok:inchl
bit7,(hl)
jrz,skiptok
inchl
popde
jr_1exec
 
_6exec:inchl
bit7,(hl)
jrz,_6exec
runtok:lda,(hl)
inchl
ldl,(hl)
and07fh
ldh,a
popaf
jp(hl)
 
NEW_:callcheckEOL
ifmodel == 1
_1NEW_:lda,00ch
rstputch
ldhl,basic_mem
ld(freemem),hl
ready4:jpready
endif
 
ifmodel == 3
rstsub00
endif
 
; Odd that we don't allow anything after the END.  Guess there's little
; point when you don't have ELSE.
END_:callcheckEOL
ifmodel == 1
jrready4
endif
ifmodel == 3
ready4:jpready
endif
 
RUN_:callgetintEOL
callclear
jr_2RUN_
 
_1RUN_:ldhl,0
_2RUN_:callfindlineDE
jrc,ready4
_3RUN_:ld(cur_line),de
incde
incde
statement:callkbscan
cp003h
jrz,STOP_
callctoff
ldix,input_buffer+72
ldhl,prog_tok+1
jr_1exec
 
GOTO_:callsub141
pushde
 
ifmodel == 1
callcheckEOL
endif
 
_1GOTO_:callfindline
jpnz,say_how
popaf
jr_3RUN_
 
STOP_:ld(cont_ptr),de
 
ifmodel == 1
lda,00dh
rstputch
endif
 
ifmodel == 3
callput_lf2
endif
 
ldde,msg_BREAK_AT
callprint
ldhl,(cur_line)
ld(cont_line),hl
lde,(hl)
inchl
ldd,(hl)
ldhl,0
ld(cur_line),hl
exde,hl
callsub101
lda,00dh
rstputch
_1STOP_:jpcmdlive
 
CONT_:callcheckEOL
ldhl,(cont_ptr)
lda,h
orl
jrz,ready4
exde,hl
ldhl,(cont_line)
ld(cur_line),hl
callsub84
_1CONT_:jrstatement
 
ifmodel == 3
LLIST_:lda,001h
ld(outdev),a
endif
 
; The LIST keyword handler.
LIST_:callgetintEOL
callfindline
 
ifmodel == 1
; List 12 lines and wait for either up-arrow to list the next line
; or ENTER to stop listing.
ldc,00ch
_1LIST_:jrc,_4LIST_
decc
jrz,_2LIST_
pushbc
callsub104
popbc
callfindlineDE
jr_1LIST_
 
_2LIST_:callsub104
callfindlineDE
jrc,_4LIST_
_3LIST_:callkbscan
jrz,_3LIST_
cp01bh
jrz,_2LIST_
cp00dh
jrnz,_3LIST_
_4LIST_:jr_1STOP_
endif
 
ifmodel == 3
jrc,_4LIST_
_1LIST_:callsub104
callkbscan
jrz,_3LIST_
cp003h
jrz,_4LIST_
_2LIST_:callkbscan
jrz,_2LIST_
cp003h
jrz,_4LIST_
_3LIST_:callfindlineDE
jrnc,_1LIST_
_4LIST_:jr_1STOP_
endif
 
ifmodel == 3
LPRINT_:lda,001h
jrprint_curdev
endif
 
; The PRINT keyword handler.
PRINT_:
ifmodel == 3
xora
print_curdev:ld(outdev),a
endif
 
rstlkchar
defb'#',pnotdev-$-1
ldhl,input_buffer
ld(pnum_ptr),hl
callcton
pnotdev:rstlkchar
defb':',pnotcln-$-1
callput_lf
jr_1CONT_
 
pnotcln:rstlkchar
defb00dh,pnoteol-$-1
callput_lf
jp_1RUN_
 
pnoteol:ldhl,print_tok-1
jp_1exec
 
print_lit:callstrlit
jr_5AT_
 
jr_2Bstr
 
Astr:ldhl,stringA
jr_1Bstr
 
Bstr:ldhl,stringB
_1Bstr:callsub35
_2Bstr:rstlkchar
defb',',_2AT_-$-1
_3Bstr:lda,(cursor)
and00fh
jrz,_3AT_
lda,020h
rstputch
jr_3Bstr
 
AT_:callsub73
ldbc,(cursor)
lda,020h
ld(bc),a
lda,h
or0fch
and03fh
ldh,a
ld(hl),05fh
ld(cursor),hl
_1AT_:rstlkchar
defb',',_2AT_-$-1
jr_3AT_
 
_2AT_:rstlkchar
defb';',_4AT_-$-1
_3AT_:callsub84
jrpnoteol
 
_4AT_:callput_lf
rstsub07
_5AT_:call_expr
callsub102
jr_2Bstr
 
TAB_:callsub76
lda,l
and03fh
ldl,a
_1TAB_:lda,(cursor)
and03fh
cpl
jrz,_1AT_
jrnc,_1AT_
lda,020h
rstputch
jr_1TAB_
 
CLS_:lda,00ch
rstputch
rstsub07
sub35:lda,(hl)
inchl
anda
retz
rstputch
lda,l
and00fh
retz
jrsub35
 
GOSUB_:callsub108
callsub141
_1GOSUB_:pushde
callfindline
jpnz,say_how
ldhl,(cur_line)
pushhl
ldhl,(gosub_sp)
pushhl
ldhl,0
ld(for_sp),hl
addhl,sp
ld(gosub_sp),hl
jp_3RUN_
 
RETURN_:callcheckEOL
ldhl,(gosub_sp)
lda,h
orl
jpz,say_what_de
ldsp,hl
pophl
ld(gosub_sp),hl
pophl
ld(cur_line),hl
popde
jp_5NEXT_
 
ON_:callsub75
lda,h
orl
jrz,_1ON_
pushhl
ldhl,ong_tok+1
jr_1FOR_
 
_1ON_:callsub42
rstsub07
on_goto:pophl
callsub41
_1on_goto:pushde
jp_1GOTO_
 
on_gosub:pophl
callsub41
ld(tmpish),hl
callsub108
ldhl,(tmpish)
jr_1GOSUB_
 
sub41:decl
jrz,_2sub41
_1sub41:lda,(de)
cp00dh
jrz,_1ON_
cp03ah
jrz,_1ON_
incde
cp02ch
jrz,sub41
jr_1sub41
 
_2sub41:callsub141
sub42:lda,(de)
cp03ah
retz
cp00dh
retz
incde
jrsub42
 
FOR_:callsub108
callsub83
ld(for_sp),hl
ldhl,to_tok+1
_1FOR_:jp_1exec
 
TO_:callsub73
ld(for_to),hl
ldhl,step_tok+1
jr_1FOR_
 
STEP_:callsub73
jr_1for_top
 
for_top:ldhl,1
_1for_top:ld(for_a),hl
ldhl,(cur_line)
ld(for_b),hl
exde,hl
ld(for_c),hl
ldbc,10
ldhl,(for_sp)
exde,hl
ldh,b
ldl,b
addhl,sp
_2for_top:lda,009h
lda,(hl)
inchl
or(hl)
jrz,_3for_top
lda,(hl)
dechl
cpd
jrnz,_2for_top+1
lda,(hl)
cpe
jrnz,_2for_top+1
exde,hl
ldhl,0
addhl,sp
ldb,h
ldc,l
ldhl,10
addhl,de
callcopyDEtoHL
ldsp,hl
_3for_top:ldhl,(for_c)
exde,hl
rstsub07
NEXT_:rstlkvar
jpc,say_what_de
ld(tmpish),hl
_1NEXT_:pushde
exde,hl
ldhl,(for_sp)
lda,h
orl
jpz,say_what
rstcpHL_DE
jrz,_2NEXT_
popde
callsub107
ldhl,(tmpish)
jr_1NEXT_
 
_2NEXT_:callsub113
callsub115
exde,hl
ldhl,(for_a)
pushhl
addhl,de
pushhl
callsub117
ldhl,(for_sp)
callsub114
popde
ldhl,(for_to)
popaf
ora
jpp,_3NEXT_
exde,hl
_3NEXT_:lda,h
xord
jpp,_4NEXT_
exde,hl
_4NEXT_:rstcpHL_DE
popde
jrc,_5NEXT_
ldhl,(for_b)
ld(cur_line),hl
jr_3for_top
 
_5NEXT_:callsub107
rstsub07
REM_:ldhl,0
jr_1IF_
 
IF_:callsub73
_1IF_:lda,h
orl
jrnz,_2IF_
calleolfindHL
jpnc,_3RUN_
jpready
 
_2IF_:ldhl,then_tok+1
jp_1exec
 
THEN_:callgetint
jpnz,_1on_goto
statement2:jpstatement
 
_1statement2:ldsp,(tmpish)
pophl
ld(cur_line),hl
popde
INPUT_:rstlkchar
defb'#',_1INPUT_-$-1
callcasloadblock
pushde
ldhl,(cur_line)
pushhl
jr_3INPUT_
 
_1INPUT_:pushde
callstrlit
jr_2INPUT_
 
rstlkchar
defb';',_4INPUT_-$-1
_2INPUT_:ldhl,(cur_line)
pushhl
ldhl,_1INPUT_
ld(cur_line),hl
ld(tmpish),sp
pushde
lda,03fh
callsub93
popde
_3INPUT_:jp_1RESTORE_
 
_4INPUT_:popaf
jr_3LET_
 
sub53:jpz,_1sub83
rstskipspace
_1sub53:lda,(de)
cp00dh
jrz,_2sub53
cp022h
jrz,_3sub53
cp02ch
jrz,_2sub53
ld(hl),a
inchl
incde
lda,l
and00fh
jrz,_5sub53
jr_1sub53
 
_2sub53:ld(hl),000h
inchl
lda,l
and00fh
jrnz,_2sub53
ret
 
_3sub53:incde
_4sub53:lda,(de)
cp00dh
jrz,_2sub53
incde
cp022h
jrz,_2sub53
ld(hl),a
inchl
lda,l
and00fh
jrnz,_4sub53
_5sub53:lda,(de)
cp02ch
retz
cp00dh
retz
incde
jr_5sub53
 
sub54:rstlkvar
retc
decde
lda,(de)
incde
cp029h
retz
lda,(de)
cp024h
jrz,_1sub54
xora
ret
 
_1sub54:lda,l
cp007h
jpnc,say_what_de
incde
slaa
slaa
adda,070h
ldl,a
anda
ret
 
assign:lda,(de)
cp00dh
jrz,_1LET_
LET_:callsub54
jrc,_3LET_
pushaf
rstlkchar
defb'=',_3LET_-$-1
popaf
callsub53
_1LET_:rstsub07
_2LET_:pophl
ld(cur_line),hl
_3LET_:jpsay_what_de
 
RESTORE_:ldhl,0
ld(data_ptr),hl
rstsub07
_1RESTORE_:callsub54
jrc,_2LET_
pushde
ldde,input_buffer
_2RESTORE_:callsub53
ld(pnum_ptr),de
popde
rstlkchar
defb',',_5READ_-$-1
callsub54
jrc,_2LET_
pushde
pushaf
ldde,(pnum_ptr)
rstlkchar
defb',',_7READ_-$-1
popaf
jr_2RESTORE_
 
READ_:callsub54
pushde
jrc,_8READ_
pushaf
ldde,(data_ptr)
lda,d
ore
jrnz,_3READ_
ldde,basic_mem
_1READ_:pushhl
callsub61
pophl
jrc,_9READ_
_2READ_:popaf
ldbc,(cur_line)
pushbc
ldbc,0
ld(cur_line),bc
callsub53
popbc
ld(cur_line),bc
ld(data_ptr),de
popde
rstlkchar
defb',',_6READ_-$-1
jrREAD_
 
_3READ_:rstlkchar
defb',',_4READ_-$-1
jr_2READ_
 
_4READ_:rstlkchar
defb00dh,_7READ_-$-1
jr_1READ_
 
_5READ_:pophl
ld(cur_line),hl
popaf
_6READ_:rstsub07
_7READ_:popaf
_8READ_:jpsay_what
 
_9READ_:popaf
jpsay_how
 
DATA_:xora
ret
 
find_data:lda,(de)
incde
cp00dh
jrnz,find_data
sub61:incde
incde
ldhl,(freemem)
rstcpHL_DE
retc
ldhl,data_tok-1
jr_1sub67
 
ifmodel == 1
cont_expr:callsub67
jpsub117
endif
 
ifmodel == 3
expr:callsub68
callsub67
ldh,000h
jpsub117
endif
 
gt_:rstlkchar
defb'=',_1gt-$-1
callsub66
retc
jr_1sub66
 
_1gt:callsub66
retz
retc
jr_1sub66
 
lt_:rstlkchar
defb'=',_1lt-$-1
callsub66
ifmodel == 1
ldhl,1
retz
retc
ldhl,0
endif
ifmodel == 3
ldl,001h
retz
retc
decl
endif
ret
 
_1lt:rstlkchar
defb'>',_1eq-$-1
callsub66
retz
jr_1sub66
 
eq_:callsub66
retnz
jr_1sub66
 
_1eq:callsub66
retnc
jr_1sub66
 
; This has effect of dropping out of execution and listing the current line.
prog_sn:popaf
ret
 
sub66:callsub68
callsub125
ifmodel == 1
ldhl,0
endif
ifmodel == 3
ldl,000h
endif
ret
 
_1sub66:
ifmodel == 1
ldhl,1
endif
ifmodel == 3
ldl,001h
endif
ret
 
sub67:ldhl,op_tok+1
_1sub67:jp_1exec
 
sub68:rstlkchar
defb'-',_1sub68-$-1
ldhl,0
callsub117
jr_5sub68
 
_1sub68:rstlkchar
defb'+',_2sub68-$-1
_2sub68:callsub69
_3sub68:rstlkchar
defb'+',_4sub68-$-1
callsub69
callsub127
jr_3sub68
 
_4sub68:rstlkchar
defb'-',_1sub72-$-1
_5sub68:callsub69
callsub126
jr_3sub68
 
sub69:callsub70
_1sub69:rstlkchar
defb'*',_2sub69-$-1
callsub70
callsub121
jr_1sub69
 
_2sub69:rstlkchar
defb'/',_1sub72-$-1
callsub70
callsub123
jr_1sub69
 
sub70:ldhl,func_tok+1
jr_1sub67
 
expterm:rstlkvar
jrc,_1expterm
jpsub113
 
_1expterm:callsub10
retnz
sub72:rstlkchar
defb'(',_2sub72-$-1
call_expr
rstlkchar
defb')',_2sub72-$-1
_1sub72:ret
 
_2sub72:jpsay_what_de
 
sub73:call_expr
sub74:jpsub115
 
sub75:callsub73
bit7,h
jpnz,say_how_de
ret
 
sub76:callsub72
jrsub74
 
ABS_:callsub72
res7,(ix-001h)
ret
 
MEM_:pushde
ldde,(freemem)
ldhl,(memtop)
xora
sbchl,de
popde
jr_1INT_
 
INT_:callsub72
callsub74
_1INT_:jpsub117
 
RESET_:lda,080h
jr_1POINT_
 
SET_:lda,001h
jr_1POINT_
 
POINT_:lda,000h
_1POINT_:anda
pushaf
rstlkchar
defb'(',_6POINT_-$-1
callsub75
pushhl
rstlkchar
defb',',_6POINT_-$-1
callsub75
rstlkchar
defb')',_6POINT_-$-1
ldbc,sub07
anda
_2POINT_:sbchl,bc
jrnc,_2POINT_
addhl,bc
lda,l
ldh,0ffh
_3POINT_:inch
sub003h
jrnc,_3POINT_
adda,003h
popbc
ldb,h
slac
rrb
rrc
rrb
rrc
rla
inca
scf
ldh,000h
_4POINT_:rlh
deca
jrnz,_4POINT_
lda,b
or0fch
and03fh
ldb,a
lda,(bc)
bit7,a
jrnz,_5POINT_
lda,080h
ld(bc),a
_5POINT_:popaf
lda,(bc)
jrz,_7POINT_
jpp,_9POINT_
lda,h
cpl
ldh,a
lda,(bc)
andh
ld(bc),a
rstsub07
_6POINT_:jrsay_what_de
 
_7POINT_:andh
ldhl,0
jrz,_8POINT_
incl
_8POINT_:jpsub117
 
_9POINT_:orh
ld(bc),a
rstsub07
sub83:rstlkvar
jrc,say_what_de
rstlkchar
defb'=',say_what_de-$-1
_1sub83:pushhl
call_expr
jp_1sub113
 
sub84:rstlkchar
defb':',_1sub84-$-1
popaf
jpstatement
 
_1sub84:rstlkchar
defb00dh,_2sub84-$-1
popaf
jp_1RUN_
 
_2sub84:ret
 
; Converts ascii int at DE into HL and errors if not last thing in line.
getintEOL:callgetint
; Insists only whitespace remains in line.  WHAT? if not
checkEOL:rstskipspace
cp00dh
retz
say_what_de:pushde
say_what:ldde,msg_WHAT
errmsg:
ifmodel == 3
xora
ld(outdev),a
endif
callprint
ldde,(cur_line)
lda,d
ore
jrz,ready2
incde
lda,(de)
decde
ora
jpm,_1statement2
pophl
lda,(hl)
pushaf
suba
ld(hl),a
callsub104
decde
popaf
ld(de),a
lda,03fh
rstputch
suba
callprint
ready2:jpready
 
say_sorry_de:pushde
say_sorry:ldde,msg_SORRY
jrerrmsg
 
; Displays > prompt and polls for input.
getline:lda,03eh
sub93:
ifmodel == 1
rstputch
endif
ifmodel == 3
rstvidputc
endif
 
ldde,input_buffer
_1sub93:callkbscan
jrz,_1sub93
cp00dh
jrz,_2sub93
cp01dh
jrz,_3sub93
cp003h
jrz,ready2
cp020h
jrc,_1sub93
_2sub93:ld(de),a
incde
cp00dh
retz
lda,e
cp0f3h
jrnz,_1sub93
lda,01dh
rstputch
_3sub93:lda,e
cp0ach
jrz,getline
decde
jr_1sub93
 
; Find line number HL.
findline:ldde,basic_mem
; Find line number HL starting at position DE.
; Carry set if line not found but does point at line just after where
; line HL would be.  Returns NZ if not exact line number match.
findlineDE:pushhl
lda,d
cp042h
jrc,ready2
ldhl,(freemem)
dechl
rstcpHL_DE
pophl
retc
lda,(de)
subl
ldb,a
incde
lda,(de)
sbca,h
jrc,_1skipfindHL
decde
orb
ret
 
; Skip full line at DE, search for line HL.
skipfindHL:incde
_1skipfindHL:incde
; Go to end of line then search for line HL.
eolfindHL:lda,(de)
cp00dh
jrnz,_1skipfindHL
incde
jrfindlineDE
 
; Print string from DE until $0 or $d (the latter will be printed).
print:xora
sub99:ldb,a
_1sub99:lda,(de)
incde
cpb
retz
rstputch
cp00dh
jrnz,_1sub99
ret
 
; Process a string literal (double quotes)
strlit:rstlkchar
defb'"',_1strlit-$-1
lda,022h
callsub99
cp00dh
pophl
jpz,_1RUN_
inchl
inchl
jp(hl)
 
_1strlit:ret
 
sub101:callsub117
sub102:pushde
pushbc
pushhl
lda,(ix-002h)
cp080h
jrnz,_1sub102
lda,020h
rstputch
lda,030h
rstputch
jp_18sub102
 
_1sub102:lda,(ix-001h)
anda
lda,020h
jrz,_2sub102
lda,02dh
_2sub102:rstputch
xora
ld(ix-001h),a
lda,0ffh
_3sub102:pushaf
ldhl,_2rand
callsub124
jrnc,_4sub102
callsub120
popaf
deca
jr_3sub102
 
_4sub102:ldhl,_1rand
callsub124
jrc,_5sub102
callsub122
popaf
inca
pushaf
jr_4sub102
 
_5sub102:lda,(ix-002h)
neg
_6sub102:jrz,_7sub102
exx
srlc
rrh
rrl
exx
deca
jr_6sub102
 
_7sub102:ldb,007h
pushix
pophl
ld(hl),000h
inchl
_8sub102:lda,000h
callshiftdig
exx
lda,b
exx
ld(hl),a
inchl
djnz_8sub102
ldb,006h
ldc,000h
dechl
lda,(hl)
cp005h
_9sub102:ccf
lda,000h
dechl
adca,(hl)
slac
cp00ah
jrc,_10sub102
lda,000h
_10sub102:ld(hl),a
pushaf
anda
jrz,_11sub102
set0,c
_11sub102:popaf
djnz_9sub102
lda,c
popbc
jrc,_12sub102
incb
pushbc
ldb,001h
jr_9sub102
 
_12sub102:ldc,a
lda,b
inca
jpm,_13sub102
cp007h
jrnc,_13sub102
ldb,a
callsub103
jr_18sub102
 
_13sub102:pushbc
ldb,001h
callsub103
lda,045h
rstputch
popbc
bit7,b
lda,02bh
jrz,_14sub102
lda,02dh
rstputch
lda,b
neg
jr_15sub102
 
_14sub102:rstputch
lda,b
_15sub102:ldb,000h
_16sub102:cp00ah
jrc,_17sub102
adda,0f6h
incb
jr_16sub102
 
_17sub102:or030h
ldc,a
lda,b
or030h
rstputch
lda,c
rstputch
_18sub102:lda,020h
rstputch
pophl
popbc
popde
ldbc,-5
addix,bc
ret
 
sub103:incb
_1sub103:decb
jrnz,_2sub103
lda,02eh
rstputch
_2sub103:lda,(hl)
or030h
rstputch
inchl
srlc
jrnz,_1sub103
decb
decb
retm
incb
jrsub103
 
sub104:lda,(de)
ldl,a
incde
lda,(de)
ldh,a
incde
callsub101
jpprint
 
; Memory copy from DE to BC until DE reaches HL (if DE == HL does nothing)
copyDEtoBC:rstcpHL_DE
retz
lda,(de)
ld(bc),a
incde
incbc
jrcopyDEtoBC
 
; Memory copy from DE to HL until DE reaches BC (if BC == DE does nothing)
copyDEtoHL:lda,b
subd
jrnz,_1copyDEtoHL
lda,c
sube
retz
_1copyDEtoHL:decde
dechl
lda,(de)
ld(hl),a
jrcopyDEtoHL
 
sub107:popbc
pophl
ld(for_sp),hl
lda,h
orl
jrz,_1sub107
pophl
ld(for_a),hl
pophl
ld(for_to),hl
pophl
ld(for_b),hl
pophl
ld(for_c),hl
_1sub107:pushbc
ret
 
sub108:ldhl,input_buffer+212
lda,h
cpl
ldh,a
lda,l
cpl
ldl,a
inchl
popbc
addhl,sp
jpnc,say_sorry_de
ldhl,(for_sp)
lda,h
orl
jrz,_1sub108
ldhl,(for_c)
pushhl
ldhl,(for_b)
pushhl
ldhl,(for_to)
pushhl
ldhl,(for_a)
pushhl
ldhl,(for_sp)
_1sub108:pushhl
pushbc
ret
 
; Continuing putch; jump taken if cassette is off.
; Obviously the cassette on flag redirects output to the cassette.
cont_putch:
ifmodel == 1
jrz,vdchar
endif
ifmodel == 3
exx
jrz,not_casputc
endif
 
exaf,af'
ldde,(pnum_ptr)
ld(de),a
incde
ld(pnum_ptr),de
cp00dh
exx
retnz
pushde
exx
ldhl,input_buffer
callcaswriteblock
popde
ret
 
ifmodel == 3
not_casputc:lda,(outdev)
anda
jrz,vdchar
lputc:ina,(0f8h)
and0f0h
cp030h
jrnz,lputc
exaf,af'
out(0f8h),a
exx
ret
endif
 
vdchar:ldhl,(cursor)
exaf,af'
pushaf
cp020h
jpm,vdctlchar
ld(hl),a
inchl
checkscroll:lda,h
cp040h
jrnz,showcursor
ldde,03c00h
ldhl,03c40h
ldbc,03c0h
ldir
exde,hl
callcleartoEOL
ldhl,03fc0h
showcursor:ld(hl),05fh
ld(cursor),hl
popaf
exx
ret
 
; Character $0d means clear to end of line and move to next line.
vdctlchar:cp00dh
jrnz,not_newline
callcleartoEOL
jrcheckscroll
 
; Character $0c means clear screen, home cursor.
not_newline:cp00ch
jrnz,not_cls
ldhl,03c00h
linelp:callcleartoEOL
lda,h
cp040h
jrnz,linelp
ldhl,03c00h
jrshowcursor
 
; Character $1d means backspace.
not_cls:cp01dh
jrnz,showcursor
; Any other character < $20 is shown as space.
ld(hl),020h
dechl
jrshowcursor
 
cleartoEOL:ld(hl),020h
inchl
lda,l
and03fh
jrnz,cleartoEOL
ret
 
; Returns byte in A register and displays it at current cursor.
; A = 0 if no key.
; About 122 bytes for a keyboard scan; pretty good.
kbscan:lda,(0387fh)
anda
retz
exx
callget_key
exx
rstputch
anda
pushaf
; A side effect is that holding any key while a program is running
; will pause it.
wait_release:lda,(0387fh)
anda
jrnz,wait_release
popaf
ret
 
;  @   A   B   C   D   E   F   G
;  H   I   J   K   L   M   N   O
;  P   Q   R   S   T   U   V   W
;  X   Y   Z
;  0   1   2   3   4   5   6   7
;  8   9   :   ;   ,   -   .   /
;  ENT CLR BRK UP  DN   LT  RT SPC
;  shift
;
; First we delay for a bit (to avoid bounce?)
; That 'inc e' in there is unecessarily frugal!
; We only scan 7 rows in the loop.
get_key:ldb,0ffh
delay:djnzdelay
ldde,keycolstart
ldhl,03801h
lda,000h
scan_row:or(hl)
jrnz,got_key
ince
slal
jpp,scan_row
exx
popaf
jrkbscan
 
got_key:exde,hl
ldb,(hl)
scan_column:incb
srla
jrnz,scan_column
lda,b
cp040h
jrnc,alpha_key
; Checking for <=>?@ (hmmm, only @ can come from the matrix).
cp03ch
jrnc,at_key_misc
cp030h
jrnc,digit_colon_semi
ldhl,specialkeys
find_special:inchl
decb
jrnz,find_special
ldb,(hl)
callcheck_shift
retnz
; If no shift, leave most alone but the up, down, left, right keys
; map to $1b, $1c, $1d, $1e which is why $1d is backspace.
and03fh
ret
 
alpha_key:callcheck_shift
retz
; Hey, shift maps alpha keys into control characters 1 to 27.
; So shift-M is the same as enter, shift-L is clear, shift-C is break.  Cool.
; The Level I manual warns not to use the shift key; this is why.
; However, shift-H is _not_ backspace because that is $1D in our world.
and03fh
ret
 
digit_colon_semi:
callcheck_shift
retz
; Map '0' to ' ', '1' to '!', etc.
maskit:and02fh
ret
 
; All this ends up doing is turning shift-@ into a 0.
at_key_misc:callcheck_shift
retnz
jrmaskit
 
; Return NZ if shift pressed.
check_shift:lda,(03880h)
anda
lda,b
ret
 
def_keycolmacro
keycolstart:ascii'?'
ascii'G'
ascii'O'
ascii'W'
ascii'/'
ascii'7'
; Enter, clear, break, up, down, left, right, space-bar
specialkeys:ascii0
ascii13
ascii12
ascii3
ascii'['
ascii92
ascii']'
ascii'^'
ascii' '
endm
 
ifmodel == 1
; Ohmigosh; a wasted byte!
ret
 
def_keycol
endif
 
ifmodel == 3
def_print_tok
endif
 
data_tok:ascii'DATA'
drowDATA_|8000h
drowfind_data|8000h
sub113:pushde
pushhl
pushaf
ldbc,4
pushix
popde
ldir
rl(ix+002h)
rl(ix+003h)
lda,b
rra
ld(ix+004h),a
scf
rr(ix+002h)
ldc,005h
addix,bc
popaf
pophl
popde
ret
 
_1sub113:pophl
sub114:ldbc,-5
addix,bc
ldbc,4
pushde
pushhl
exde,hl
pushix
pophl
ldir
exde,hl
dechl
dechl
rl(hl)
inchl
lda,(ix+004h)
rla
rr(hl)
dechl
rr(hl)
pophl
popde
ret
 
sub115:exx
ldbc,-5
addix,bc
ldde,0
lda,(ix+003h)
ldc,(ix+004h)
cp080h
jrz,_6sub115
cp001h
jpm,_1sub115
cp010h
jpm,_2sub115
exx
jpsay_how_de
 
_1sub115:lda,0ffh
jr_4sub115
 
_2sub115:ldb,a
lda,(ix+000h)
ldl,(ix+001h)
ldh,(ix+002h)
_3sub115:slaa
rll
rlh
rle
rld
djnz_3sub115
_4sub115:slac
jrnc,_6sub115
orh
orl
jrz,_5sub115
incde
_5sub115:callsub119
_6sub115:pushde
exx
pophl
ret
 
sub116:ldhl,10
sub117:pushde
exde,hl
ldbc,10
addix,bc
callsub118
pushde
ldh,000h
rrh
ldl,010h
exx
popde
ldl,000h
ldh,e
ldc,d
callnormCHL
jr_1sub123
 
sub118:xora
adda,d
retp
sub119:lda,e
neg
lde,a
lda,d
cpl
ccf
adca,000h
ldd,a
scf
ret
 
sub120:callsub116
sub121:callsub136
jrz,_2sub126
cpe
jpz,_2sub128
callsub129
jr_1sub123
 
sub122:callsub116
sub123:callsub136
jrz,_2sub126
cpe
jpz,say_how
callsub130
_1sub123:jr_3sub128
 
sub124:callsub113
callsub136
ldbc,-5
jr_1sub125
 
sub125:callsub136
ldbc,-10
_1sub125:addix,bc
cpl
callsub131
popde
ret
 
sub126:callsub136
jrnz,_1sub126
callsub128
jr_4sub127
 
_1sub126:cpe
_2sub126:jrz,_4sub128
xord
ldd,a
jr_1sub127
 
ifmodel == 1
callsub113
endif
 
sub127:callsub136
jrz,_1sub128
cpe
jrz,_4sub128
_1sub127:callsub132
jrz,_3sub127
jrnc,_2sub127
exde,hl
exx
exde,hl
lda,c
ldc,b
ldb,a
exx
_2sub127:callsub133
jr_3sub128
 
_3sub127:lda,h
xord
jrnz,_2sub128
lde,001h
callsub134
jr_3sub128
 
_4sub127:lda,(ix-001h)
xor080h
ld(ix-001h),a
popde
ret
 
sub128:pushde
_1sub128:ldh,d
ldl,e
exx
ldl,e
ldh,d
ldc,b
exx
jr_3sub128
 
_2sub128:ldl,080h
_3sub128:ld(ix-006h),h
ld(ix-007h),l
exx
ld(ix-00ah),l
ld(ix-009h),h
ld(ix-008h),c
exx
_4sub128:ldbc,-5
addix,bc
popde
ret
 
sub129:lda,h
xord
ldh,a
dece
pushhl
pushbc
ldb,018h
ldl,(ix-00ah)
ldh,(ix-009h)
ldc,(ix-008h)
exx
xora
ldl,a
ldh,a
ldc,a
_1sub129:exx
srlc
rrh
rrl
exx
jrnc,_2sub129
addhl,de
lda,c
adca,b
ldc,a
_2sub129:exx
djnz_3sub129
popbc
pophl
exx
jr_3sub130
 
_3sub129:exx
rrc
rrh
rrl
jr_1sub129
 
sub130:lda,e
neg
lde,a
lda,h
xord
ldh,a
pushhl
pushbc
ldb,019h
exx
_1sub130:sbchl,de
lda,c
sbca,b
ldc,a
jrnc,_2sub130
addhl,de
adca,b
ldc,a
_2sub130:exx
ccf
adchl,hl
rlc
djnz_4sub130
pushhl
pushbc
exx
popbc
pophl
exx
popbc
pophl
exx
_3sub130:jr_3sub133
 
_4sub130:exx
addhl,hl
rlc
jrnc,_1sub130
ccf
sbchl,de
lda,c
sbca,b
ldc,a
ora
jr_2sub130
 
sub131:jrz,_1sub131
cpe
jrz,_3sub131
lda,h
xord
callz,sub132
jr_2sub131
 
_1sub131:cpe
retz
scf
bit7,d
jr_4sub131
 
_2sub131:retz
_3sub131:bit7,h
_4sub131:jrnz,_5sub131
rra
scf
rla
ret
 
_5sub131:ccf
ret
 
sub132:lda,l
sube
jrz,_2sub132
jppo,_1sub132
neg
_1sub132:rlca
ret
 
_2sub132:exx
lda,c
cpb
jrnz,_3sub132
lda,h
cpd
jrnz,_3sub132
lda,l
cpe
_3sub132:exx
ret
 
sub133:lda,l
sube
jrz,_2sub133
cp018h
retnc
exx
_1sub133:srlb
rrd
rre
deca
jrnz,_1sub133
exx
_2sub133:lde,000h
lda,h
xord
jpm,_2sub134
exx
addhl,de
lda,c
adca,b
ldc,a
_3sub133:jrnc,_4sub133
rrc
rrh
rrl
scf
_4sub133:exx
sub134:lda,l
adca,e
_1sub134:jppe,_4normCHL
ldl,a
ret
 
_2sub134:exx
sbchl,de
lda,c
sbca,b
ldc,a
normCHL:ldb,018h
xora
incc
decc
_1normCHL:jpm,_3normCHL
deca
addhl,hl
rlc
djnz_1normCHL
_2normCHL:ldl,080h
ret
 
_3normCHL:exx
adda,l
jr_1sub134
 
_4normCHL:lda,h
ora
jpm,_2normCHL
popaf
jpsay_how
 
sub136:pophl
pushde
pushhl
ldd,(ix-001h)
lde,(ix-002h)
exx
lde,(ix-005h)
ldd,(ix-004h)
ldb,(ix-003h)
exx
ldh,(ix-006h)
ldl,(ix-007h)
exx
ldl,(ix-00ah)
ldh,(ix-009h)
ldc,(ix-008h)
exx
lda,080h
cpl
ret
 
RND_:callsub76
lda,h
orl
jpz,sub138
bit7,h
jpnz,say_how_de
callsub117
callsub138
callsub121
callsub115
inchl
jpsub117
 
sub138:pushde
exx
ldhl,rnd_seed
lde,(hl)
inchl
ldd,(hl)
inchl
ldb,(hl)
exx
callclr24b
ldhl,rand
ldc,003h
_1sub138:ldb,008h
ldd,(hl)
_2sub138:exx
addhl,hl
rlc
exx
rld
jrnc,_3sub138
exx
addhl,de
lda,c
adca,b
ldc,a
exx
_3sub138:djnz_2sub138
inchl
decc
jrnz,_1sub138
ldhl,0
exx
ldde,rnd_seed
lda,l
adda,065h
ld(de),a
incde
ldl,a
lda,h
adca,0b0h
ld(de),a
incde
ldh,a
lda,c
adca,005h
ld(de),a
ldc,a
callnormCHL
ldbc,10
addix,bc
jp_3sub128
 
rand:defb040h
defb0e6h
defb04dh
_1rand:defb000h
defb000h
defb080h
defb000h
_2rand:defb0cch
defb0cch
defb0cch
sub140:lda,(hl)
sub141:callgetint
retnz
say_what_de3:jpsay_what_de
 
; Convert ascii number to integer in HL but only 0 to 32767.
; B is the number of digits with Z set if zero digits.
getint:callclr24b
ldhl,0
ldb,l
rstskipspace
_1getint:callgetdig
jrc,_3getint
incb
callshiftdig
jrnc,_1getint
_2getint:jpsay_how_de
 
_3getint:exx
pushhl
lda,c
bit7,h
exx
pophl
jrnz,_2getint
anda
jrnz,_2getint
lda,b
anda
ret
 
CLOAD_:callcasloadblock
ld(freemem),hl
jrnz,say_what_de3
ready3:jpready
 
; On return, HL = last + 1 address.  Z set if checksum OK, NZ otherwise.
casloadblock:callcton
pushde
xora
_1casloadblock:callgetbit
cp0a5h
jrnz,_1casloadblock
lda,02ah
ld(03c00h),a
ld(03c01h),a
callgtbyte
ldd,a
callgtbyte
lde,a
callgtbyte
ldh,a
callgtbyte
ldl,a
ldc,000h
_2casloadblock:callgtbyte
ld(de),a
incde
cp00dh
jrnz,_3casloadblock
pushaf
lda,(03c01h)
xor00ah
ld(03c01h),a
popaf
_3casloadblock:adda,c
ldc,a
rstcpHL_DE
jrnc,_2casloadblock
pushhl
callctoff
pophl
popde
lda,c
anda
ret
 
CSAVE_:callcton
ldhl,basic_mem
ldde,(freemem)
callcaswriteblock
ifmodel == 1
jpready
endif
ifmodel == 3
jpready3
endif
 
; HL = start address, DE = last+1 address.
ifmodel == 1
caswriteblock:lda,080h
pushbc
_1caswriteblock:
exaf,af'
xora
callputbyte
exaf,af'
deca
jrnz,_1caswriteblock
endif
 
ifmodel == 3
caswriteblock:ldb,080h
_1caswriteblock:
xora
callputbyte
djnz_1caswriteblock
endif
 
lda,0a5h
callputbyte
lda,h
callputbyte
lda,l
callputbyte
ifmodel == 1
popbc
endif
lda,d
callputbyte
lda,e
callputbyte
ldc,000h
_2caswriteblock:
lda,(hl)
callputbyte
inchl
rstcpHL_DE
jrnz,_2caswriteblock
lda,c
neg
callputbyte
ifmodel == 1
callctoff
ret
endif
ifmodel == 3
jrctoff
endif
 
; Read single bit from tape and shift into A register.
ifmodel == 1
getbit:exx
exaf,af'
_1getbit:ina,(0ffh)
rla
jrnc,_1getbit
ldb,07ch
_2getbit:djnz_2getbit
callgtstat
ldb,0f8h
_3getbit:djnz_3getbit
ina,(0ffh)
ldb,a
exaf,af'
rlb
rla
pushaf
callgtstat
popaf
exx
ret
endif
 
ifmodel == 3
getbit:exx
ldc,a
_1getbit:ina,(0ffh)
rla
jrnc,_1getbit
ldb,0bbh
_2getbit:djnz_2getbit
out(0ffh),a
ldb,000h
_3getbit:djnz_3getbit
ina,(0ffh)
rla
lda,c
rla
out(0ffh),a
exx
ret
endif
 
; Calls gtbit 8 times to get a full byte.
gtbyte:ldb,008h
_1gtbyte:callgetbit
djnz_1gtbyte
ret
 
putbyte:exx
ldc,008h
ldd,a
_1putbyte:callputbit
rlcd
jrnc,_3putbyte
callputbit
_2putbyte:decc
jrnz,_1putbyte
lda,d
exx
adda,c
ldc,a
ret
 
ifmodel == 1
_3putbyte:ldb,000h
_4putbyte:djnz_4putbyte
jr_2putbyte
 
putbit:ldhl,0fc01h
callcstat
ldb,00ah
_1putbit:djnz_1putbit
ldhl,0fc02h
callcstat
ldb,00ah
_2putbit:djnz_2putbit
ldhl,0fc00h
callcstat
ldb,0dah
_3putbit:djnz_3putbit
ret
 
ctoff:ldhl,0fb00h
jrcstat
 
cton:ldhl,0ff04h
callcstat
ret
 
; Returns NZ if cassette motor is on, Z if off.
gtstat:ldhl,0ff00h
cstat:lda,(status)
andh
orl
out(0ffh),a
ld(status),a
bit2,a
ret
endif
 
ifmodel == 3
_3putbyte:ldb,034h
_4putbyte:djnz_4putbyte
_5putbyte:djnz_5putbyte
jr_2putbyte
 
putbit:lda,001h
out(0ffh),a
ldb,012h
_1putbit:djnz_1putbit
inca
out(0ffh),a
ldb,012h
_2putbit:djnz_2putbit
xora
out(0ffh),a
ldb,00dh
_3putbit:djnz_3putbit
_4putbit:djnz_4putbit
ret
 
nop
nop
nop
def_keycol
ctoff:lda,020h
jr_1cton
 
; And disables video waits, too!
cton:lda,002h
_1cton:out(0ech),a
ret
rws_msg
endif
 
org04000h
vA:defs4
vB:defs4
vC:defs4
vD:defs4
vE:defs4
vF:defs4
vG:defs4
vH:defs4
vI:defs4
vJ:defs4
vK:defs4
vL:defs4
vM:defs4
vN:defs4
vO:defs4
vP:defs4
vQ:defs4
vR:defs4
vS:defs4
vT:defs4
vU:defs4
vV:defs4
vW:defs4
vX:defs4
vY:defs4
vZ:defs4
cursor:defs2
; Pointer just past top of memory.
memtop:defs2
; Pointer to free memory (and/or end of basic program)
freemem:defs2
for_to:defs2
stringA:defs16
stringB:defs16
 
ifmodel == 1
; Mirrors port $ff
status:defs1
endif
 
ifmodel == 3
; Output device; 0 == screen, 1 == printer
outdev:defs1
endif
 
for_a:defs2
for_b:defs2
for_c:defs2
cont_line:defs2
pnum_ptr:defs2
tmpish:defs2
cont_ptr:defs2
cur_line:defs2
data_ptr:defs2
gosub_sp:defs2
for_sp:defs2
rnd_seed:defs3
 
org040ach
; 72 characters max.
; Last 10 may be used as two 5 byte floating point accumulators.
input_buffer:defs72
basic_memequ04200h
 
endsub00