@ -33,7 +33,7 @@ LF .EQU 0AH ; Line feed
CS .EQU 0CH ; Clear screen
CR .EQU 0DH ; Carriage return
CTRLO .EQU 0FH ; Control "O"
CTRLQ .EQU 11H ; Control "Q"
CTRLQ .EQU 11H ; Control "Q"
CTRLR .EQU 12H ; Control "R"
CTRLS .EQU 13H ; Control "S"
CTRLU .EQU 15H ; Control "U"
@ -42,6 +42,11 @@ DEL .EQU 7FH ; Delete
; BASIC WORK SPACE LOCATIONS
; 0200H - 2000H BASIC EXECUTABLE
; 2000H - 2090H STACK
; 2090H - 20F8H BASIC EXECUTABLE VARAIABLES / WORKSPACE
; 20F9H - BASIC PROGRAM START
WRKSPC .EQU BAS_END + 90H ; WAS 4090H ; BASIC Work space
USR .EQU WRKSPC + 3H ; "USR (x)" jump
OUTSUB .EQU WRKSPC + 6H ; "OUT p,n"
@ -132,7 +137,7 @@ MO .EQU 24H ; Missing operand
HX .EQU 26H ; HEX error
BN .EQU 28H ; BIN error
.ORG BAS_LOC ; WAS 02000H
.ORG BAS_LOC ; WAS 02000H
COLD: JP STARTB ; Jump for cold start
WARM: JP WARMST ; Jump for warm start
@ -173,8 +178,8 @@ MSIZE: LD HL,MEMMSG ; Point to message
MLOOP: INC HL ; Next byte
; LD A,H ; Above address FFFF ?
; OR L
LD A , H ; Memory top set below HBIOS Proxy @ FE00
CP $ FD
LD A , H ; Memory top set below HBIOS Proxy @ FE00
CP $ FD
JP Z , SETTOP ; Yes - 64K RAM
LD A ,( HL ) ; Get contents
LD B , A ; Save it
@ -303,7 +308,7 @@ WORDS: .BYTE 'E'+80H,"ND"
.BYTE 'C' + 80H , "ONT"
.BYTE 'L' + 80H , "IST"
.BYTE 'C' + 80H , "LEAR"
.BYTE 'C ' + 80H , "LOAD "
.BYTE 'P ' + 80H , "LAY "
.BYTE 'C' + 80H , "SAVE"
.BYTE 'N' + 80H , "EW"
@ -392,7 +397,7 @@ WORDTB: .WORD PEND
.WORD CONT
.WORD LIST
.WORD CL EAR
.WORD REM
.WORD PLAY
.WORD REM
.WORD NEW
@ -573,10 +578,10 @@ CHKSTK: PUSH HL ; Save code string address
ADD HL , BC
.BYTE 3EH ; Skip "PUSH HL"
ENFMEM: PUSH HL ; Save code string address
LD A , 0D0H ;LOW -48 ; 48 Bytes minimum RAM
LD A , 0D0H ;LOW -48 ; 48 Bytes minimum RAM
SUB L
LD L , A
LD A , 0FFH ; HIGH (-48) ; 48 Bytes minimum RAM
LD A , 0FFH ; HIGH (-48) ; 48 Bytes minimum RAM
SBC A , H
JP C , OMERR ; Not enough - ?OM Error
LD H , A
@ -1257,34 +1262,36 @@ UPDATA: LD (NXTDAT),HL ; Update DATA pointer
EX DE , HL ; Restore code string address
RET
; GET CONSOLE INPUT STATUS VIA HBIOS
TSTBRK:
; SAVE INCOMING REGISTERS (AF IS OUTPUT)
PUSH BC
PUSH DE
PUSH HL
; GET CONSOLE INPUT STATUS VIA HBIOS
LD C , CIODEV_CONSOLE ; CONSOLE UNIT TO C
LD B , BF_CIOIST ; HBIOS FUNC: INPUT STATUS
RST 08 ; HBIOS RETURNS STATUS IN A
; RESTORE REGISTERS (AF IS OUTPUT)
POP HL
POP DE
POP BC
PUSH BC ; SAVE INCOMING REGISTERS (AF IS OUTPUT)
PUSH DE
PUSH HL
LD C , CIODEV_CONSOLE ; CONSOLE UNIT TO C
LD B , BF_CIOIST ; HBIOS FUNC: INPUT STATUS
RST 08 ; HBIOS RETURNS STATUS IN A
POP HL ; RESTORE REGISTERS (AF IS OUTPUT)
POP DE
POP BC
RET Z ; No key, go back
PUSH BC
PUSH DE
PUSH HL
; INPUT CHARACTER FROM CONSOLE VIA HBIOS
LD C , CIODEV_CONSOLE ; CONSOLE UNIT TO C
LD B , BF_CIOIN ; HBIOS FUNC: INPUT CHAR
RST 08 ; HBIOS READS CHARACTDR
LD A , E ; MOVE CHARACTER TO A FOR RETURN
PUSH BC
PUSH DE
PUSH HL
; INPUT CHARACTER FROM CONSOLE VIA HBIOS
LD C , CIODEV_CONSOLE ; CONSOLE UNIT TO C
LD B , BF_CIOIN ; HBIOS FUNC: INPUT CHAR
RST 08 ; HBIOS READS CHARACTDR
LD A , E ; MOVE CHARACTER TO A FOR RETURN
;
; RESTORE REGISTERS (AF IS OUTPUT)
POP HL
POP DE
POP BC
POP HL ; RESTORE REGISTERS (AF IS OUTPUT)
POP DE
POP BC
CP ES C ; Escape key?
JR Z , BRK ; Yes, break
@ -1292,22 +1299,22 @@ TSTBRK:
JR Z , BRK ; Yes, break
CP CTRLS ; Stop scrolling?
RET NZ ; Other key, ignore
STALL: ; Wait for key
; SAVE INCOMING REGISTERS (AF IS OUTPUT)
PUSH BC
PUSH DE
PUSH HL
STALL: ; Wait for key
PUSH BC ; SAVE INCOMING REGISTERS (AF IS OUTPUT)
PUSH DE
PUSH HL
;
; INPUT CHARACTER FROM CONSOLE VIA HBIOS
LD C , CIODEV_CONSOLE ; CONSOLE UNIT TO C
LD B , BF_CIOIN ; HBIOS FUNC: INPUT CHAR
RST 08 ; HBIOS READS CHARACTDR
LD A , E ; MOVE CHARACTER TO A FOR RETURN
; INPUT CHARACTER FROM CONSOLE VIA HBIOS
;
; RESTORE REGISTERS (AF IS OUTPUT)
POP HL
POP DE
POP BC
LD C , CIODEV_CONSOLE ; CONSOLE UNIT TO C
LD B , BF_CIOIN ; HBIOS FUNC: INPUT CHAR
RST 08 ; HBIOS READS CHARACTDR
LD A , E ; MOVE CHARACTER TO A FOR RETURN
;
POP HL ; RESTORE REGISTERS (AF IS OUTPUT)
POP DE
POP BC
CP CTRLQ ; Resume scrolling?
RET Z ; Release the chokehold
CP CTRLC ; Second break?
@ -1357,7 +1364,6 @@ NULL: CALL GETINT ; Get integer 0-255
LD ( NULLS ), A ; Set nulls number
RET
ACCSUM: PUSH HL ; Save address in array
LD HL ,( CH KSUM ) ; Get check sum
LD B , 0 ; BC - Value of byte
@ -1967,8 +1973,8 @@ OPRND: XOR A ; Get operand routine
JP C , ASCTFP ; Number - Get value
CALL CH KLTR ; See if a letter
JP NC , CONVAR ; Letter - Find variable
CP '&' ; &H = HEX, &B = BINARY
JR NZ , NOTAMP
CP '&' ; &H = HEX, &B = BINARY
JR NZ , NOTAMP
CALL GETCHR ; Get next character
CP 'H' ; Hex number indicated? [function added]
JP Z , HEXTFP ; Convert Hex to FPREG
@ -2981,14 +2987,14 @@ VAL: CALL GETLEN ; Get length of string
EX ( SP ), HL ; Save string end,get start
PUSH BC ; Save end+1 byte
LD A ,( HL ) ; Get starting byte
CP '$' ; Hex number indicated? [function added]
JP NZ , VAL1
CALL HEXTFP ; Convert Hex to FPREG
JR VAL3
CP '$' ; Hex number indicated? [function added]
JP NZ , VAL1
CALL HEXTFP ; Convert Hex to FPREG
JR VAL3
VAL1: CP '%' ; Binary number indicated? [function added]
JP NZ , VAL2
CALL BINTFP ; Convert Bin to FPREG
JR VAL3
JP NZ , VAL2
CALL BINTFP ; Convert Bin to FPREG
JR VAL3
VAL2: CALL ASCTFP ; Convert ASCII string to FP
VAL3: POP BC ; Restore end+1 byte
POP HL ; Restore end+1 address
@ -3054,7 +3060,7 @@ POKE: CALL GETNUM ; Get memory address
CALL DEINT ; Get integer -32768 to 3276
PUSH DE ; Save memory address
CALL CH KSYN ; Make sure ',' follows
.BYTE ','
.BYTE ','
CALL GETINT ; Get integer 0-255
POP DE ; Restore memory address
LD ( DE ), A ; Load it into memory
@ -4143,19 +4149,21 @@ ATNTAB: .BYTE 9 ; Table used by ATN
ARET: RET ; A RETurn instruction
; INPUT CHARACTER FROM CONSOLE VIA HBIOS
GETINP:
PUSH BC
PUSH DE
PUSH HL
; INPUT CHARACTER FROM CONSOLE VIA HBIOS
LD C , CIODEV_CONSOLE ; CONSOLE UNIT TO C
LD B , BF_CIOIN ; HBIOS FUNC: INPUT CHAR
RST 08 ; HBIOS READS CHARACTDR
LD A , E ; MOVE CHARACTER TO A FOR RETURN
; RESTORE REGISTERS (AF IS OUTPUT)
POP HL
POP DE
POP BC
PUSH BC
PUSH DE
PUSH HL
LD C , CIODEV_CONSOLE ; CONSOLE UNIT TO C
LD B , BF_CIOIN ; HBIOS FUNC: INPUT CHAR
RST 08 ; HBIOS READS CHARACTDR
LD A , E ; MOVE CHARACTER TO A FOR RETURN
POP HL ; RESTORE REGISTERS (AF IS OUTPUT)
POP DE
POP BC
RET
CLS:
LD A , CS ; ASCII Clear screen
@ -4199,26 +4207,26 @@ DOKE: CALL GETNUM ; Get a number
HEX: CALL TSTNUM ; Verify it's a number
CALL DEINT ; Get integer -32768 to 32767
PUSH BC ; Save contents of BC
LD HL , PBUFF
LD A , D ; Get high order into A
LD HL , PBUFF
LD A , D ; Get high order into A
CP $ 0
JR Z , HEX2 ; Skip output if both high digits are zero
JR Z , HEX2 ; Skip output if both high digits are zero
CALL BYT2ASC ; Convert D to ASCII
LD A , B
CP '0'
JR Z , HEX1 ; Don't store high digit if zero
LD ( HL ), B ; Store it to PBUFF
INC HL ; Next location
HEX1: LD ( HL ), C ; Store C to PBUFF+1
LD A , B
CP '0'
JR Z , HEX1 ; Don't store high digit if zero
LD ( HL ), B ; Store it to PBUFF
INC HL ; Next location
HEX1: LD ( HL ), C ; Store C to PBUFF+1
INC HL ; Next location
HEX2: LD A , E ; Get lower byte
HEX2: LD A , E ; Get lower byte
CALL BYT2ASC ; Convert E to ASCII
LD A , D
LD A , D
CP $ 0
JR NZ , HEX3 ; If upper byte was not zero then always print lower byte
LD A , B
CP '0' ; If high digit of lower byte is zero then don't print
JR Z , HEX4
JR NZ , HEX3 ; If upper byte was not zero then always print lower byte
LD A , B
CP '0' ; If high digit of lower byte is zero then don't print
JR Z , HEX4
HEX3: LD ( HL ), B ; to PBUFF+2
INC HL ; Next location
HEX4: LD ( HL ), C ; to PBUFF+3
@ -4254,6 +4262,7 @@ ADD301 ADD A,$30 ; And make it full ASCII
; Convert "&Hnnnn" to FPREG
; Gets a character from (HL) checks for Hexadecimal ASCII numbers "&Hnnnn"
; Char is in A, NC if char is ;<=>?@ A-z, CY is set if 0-9
;
HEXTFP EX DE , HL ; Move code string pointer to DE
LD HL , $ 0000 ; Zero out the value
CALL GETHEX ; Check the number for valid hex
@ -4362,40 +4371,40 @@ CHKBIN: INC DE
BINERR: LD E , BN ; ?BIN Error
JP ERROR
JJUMP1:
LD IX , - 1 ; Flag cold start
JP CS TART ; Go and initialise
; OUTPUT CHARACTER TO CONSOLE VIA HBIOS
MONOUT:
; SAVE ALL INCOMING REGISTERS
PUSH AF
PUSH BC
PUSH DE
PUSH HL
; OUTPUT CHARACTER TO CONSOLE VIA HBIOS
LD E , A ; OUTPUT CHAR TO E
LD C , CIODEV_CONSOLE ; CONSOLE UNIT TO C
LD B , BF_CIOOUT ; HBIOS FUNC: OUTPUT CHAR
RST 08 ; HBIOS OUTPUTS CHARACTDR
; RESTORE ALL REGISTERS
POP HL
POP DE
POP BC
POP AF
RET
MONITR: LD A , BID_BOOT ; BOOT BANK
LD HL , 0 ; ADDRESS ZERO
CALL HB_BNKCALL ; DOES NOT RETURN
PUSH AF ; SAVE ALL INCOMING REGISTERS
PUSH BC
PUSH DE
PUSH HL
LD E , A ; OUTPUT CHAR TO E
LD C , CIODEV_CONSOLE ; CONSOLE UNIT TO C
LD B , BF_CIOOUT ; HBIOS FUNC: OUTPUT CHAR
RST 08 ; HBIOS OUTPUTS CHARACTDR
POP HL ; RESTORE ALL REGISTERS
POP DE
POP BC
POP AF
RET
MONITR: LD A , BID_BOOT ; BOOT BANK
LD HL , 0 ; ADDRESS ZERO
CALL HB_BNKCALL ; DOES NOT RETURN
INITST: LD A , 0 ; Clear break flag
LD ( BRKFLG ), A
CALL SET_DUR_TBL ; SET UP SOUND TABLE
JP INIT
ARETN: RETN ; Return from NMI
TSTBIT: PUSH AF ; Save bit mask
AND B ; Get common bits
POP BC ; Restore bit mask
@ -4406,19 +4415,265 @@ TSTBIT: PUSH AF ; Save bit mask
OUTNCR: CALL OUTC ; Output character in A
JP PRNTCRLF ; Output CRLF
TXT_READY:
.DB CR , LF
.TEXT "BASIC READY "
.DB CR , LF , 0FFH
; ---------------------------------------------------------------------------------------
; PLAY O,N,D ; PLAY OCTAVE 0-8, NOTE N (0-11), DURATION (1-8) [1/8 - 8/8 SECONDS]
PLAY: CALL GETINT ; GET OCTAVE
PUSH AF ; AND SAVE
CALL CH KSYN ; Make sure ',' follows
.BYTE ','
CALL GETINT ; GET NOTE
PUSH HL ; SAVE SYNTAX POINTER
LD L , A
LD H , 0
ADD HL , HL ; X2
ADD HL , HL ; X4
LD DE , FRQDURTBL ; POINT TO NOTE ENTRY
ADD HL , DE ; ITS IN HL
EX ( SP ), HL ; RESTORE SYNTAX POINTER
; IN HL. NOTE PTR ON STACK
CALL CH KSYN ; Make sure ',' follows
.BYTE ','
CALL GETINT ; GET DURATION
POP DE ; GET NOTE PTR IN DE
EX ( SP ), HL ; GET OCTAVE IN HL. SYNTAX POINTER ON STACK
EX DE , HL ; PUT NOTE PTR IN HL, OCTAVE IN DE
PUSH BC ; SAVE SYNTAX POINTER
LD B , D ; SAVE OCTAVE
PUSH AF ; SAVE DURATION
LD A ,( HL ) ; LOAD 1ST ARG
INC HL ; IN DE
LD E , A ; WHICH IS THE
LD A ,( HL ) ; FREQUENCY FOR
INC HL ; THE EIGHTH
LD D , A ; OCTAVE
PUSH DE
LD A ,( HL ) ; LOAD 2ND ARG
INC HL ; IN DE
LD E , A ; WHICH IS THE
LD A ,( HL ) ; PITCH
LD D , A
PUSH DE ; SETUP ARGS IN HL
POP HL ; AND DE
POP DE ; DE = FREQUENCY
; HL = PITCH
LD A , 8 ; DIVIDE THE
SUB B ; FREQUENCY BASED
JR Z , SP K_OCTOK ; ON THE OCTAVE
LD B , A
SPK_OCTDIV:
SRL D ; 0>D>C ; MULTIPLY THE
RR E ; C>E>C ; DURATION EVERY
SLA L ; C<L<0 ; TIME WE DIVIDE
RL H ; C<H<C ; THE FREQUENCY
DJNZ SP K_OCTDIV
JR NC , SP K_OCTOK ; SET TO MAXIMUM
LD HL , $ FFFF ; IF OVERFLOW
SPK_OCTOK:
POP BC ; MULTIPLY CHL X B
PUSH HL ; SAVE
LD C , 0
LD H , C
LD L , C
CCF
MULSKP: ADD HL , DE
JR NC , MULDLP
INC C
MULDLP: DJNZ MULSKP ; DIVIDE BY 8
SRL C ; 0>C>C ; BCHL = BCHL / 2
RR H ; C>H>C ;
RR L ; C>L>C ;
SRL C ; 0>C>C ; BCHL = BCHL / 2
RR H ; C>H>C ;
RR L ; C>L>C ;
SRL C ; 0>C>C ; BCHL = BCHL / 2
RR H ; C>H>C ;
RR L ; C>L>C ;
POP DE
EX DE , HL
; The following SPK_BEEPER routine is a modification of code from
; "The Complete SPECTRUM ROM DISSASSEMBLY" by Dr Ian Logan & Dr Frank O’Hara
; https://www.esocop.org/docs/CompleteSpectrumROMDisassemblyThe.pdf
;
; DE Number of passes to make through the sound generation loop
; HL Loop delay parameter
PUSH IX
DI ; Disable the interrupt for the duration of a 'beep'.
LD A , L ; Save L temporarily.
SRL L ; Each '1' in the L register is to count 4 T states, but take INT (L/4) and count 16 T states instead.
SRL L
CPL ; Go back to the original value in L and find how many were lost by taking 3-(A mod 4).
AND $ 03
LD C , A
LD B , $ 00
LD IX , SP K_DLYADJ ; The base address of the timing loop.
ADD IX , BC ; Alter the length of the timing loop. Use an earlier starting point for each '1' lost by taking INT (L/4).
LD A ,( RTCVAL ) ; Fetch the present border colour from BORDCR and move it to bits 2, 1 and 0 of the A register.
;
; The HL register holds the 'length of the timing loop' with 16 T states being used for each '1' in the L register and 1024 T states for each '1' in the H register.
;
SPK_DLYADJ:
NOP ; Add 4 T states for each earlier entry point that is used.
NOP
NOP
INC B ; The values in the B and C registers will come from the H and L registers - see below.
INC C
BE_H_L_LP:
DEC C ; The 'timing loop', i.e. BC*4 T states. (But note that at the half-cycle point, C will be equal to L+1.)
JR NZ , BE_H_L_LP
LD C , $ 3 F
DEC B
JP NZ , BE_H_L_LP
;
XOR % 00000100 ; Flip bit 2. The loudspeaker is now alternately activated and deactivated.
OUT ( RTCIO ), A ; Perform the 'OUT' operation, leaving other bits unchanged.
LD B , H ; Reset the B register.
LD C , A ; Save the A register.
BIT 4 , A ; Jump if at the half-cycle point.
JR NZ , BE_AGAIN
;
LD A , D ; After a full cycle the DE register pair is tested.
OR E
JR Z , BE_END ; Jump forward if the last complete pass has been made already.
LD A , C ; Fetch the saved value.
LD C , L ; Reset the C register.
DEC DE ; Decrease the pass counter.
JP ( IX ) ; Jump back to the required starting location of the loop.
;
BE_AGAIN: ; The parameters for the second half-cycle are set up.
LD C , L ; Reset the C register.
INC C ; Add 16 T states as this path is shorter.
JP ( IX ) ; Jump back.
BE_END: EI
POP IX
POP BC ; RECALL SYNTAX POINTER
POP HL
RET
;
RTCVAL .DB 0
;
; SETUP THE ONE SECOND TONE DURATION TABLE BASED ON PROCESSOR SPEED AND TONE FREQUENCY
;
; DURATION = (CPUMHZ / 8) / FREQUENCY
; DURATION = (CPUKHZ * 1000 / 8 ) / FREQUENCY
; DURATION = (CPUKHZ * 125 ) / FREQUENCY
; DURATION = (CPUKHZ * 256 / 2 - CPUKHZ - (2 * CPUKHZ) ) / FREQUENCY
SET_DUR_TBL:
LD B , BF_SYSGET ; GET CPU SPEED
LD C , BF_SYSGET_CPUINFO ; FROM HBIOS
RST 08 ; IN DE
PUSH DE ; SAVE FOR CALCULATION
POP BC ; - CPUKHZ - (2 * CPUKHZ)
LD H , E ; DEHL = DE * 256
LD E , D
LD D , 0
LD L , D
SRL E ; 0>E>C ; DEHL = DEHL / 2
RR H ; C>H>C ;
RR L ; C>L>C ;
SBC HL , BC ; DEHL = DEHL - CPUKHZ
JR NC , FRQ_AJ1
DEC DE
FRQ_AJ1: SLA C ; C<C<0 ; DEHL = DEHL - (2 * CPUKHZ)
RL B ; C<B<C
SBC HL , BC
JR NC , FRQ_AJ2
DEC DE
FRQ_AJ2: ; AT THIS POINT DEHL = CPUKHZ * 125 E.G. 9982 KHZ * 125 = 0012:DE14
PUSH HL ; DEHL = CPUKHZ / 8) / FREQUENCY
POP IX ; HLIX = DENOMINATOR
EX DE , HL
LD IY , FRQDURTBL ; POINT TO THE TABLE WE
LD B , FDTBSIZ ; WANT TO READ AND UPDATE
TBL_LP: PUSH BC
LD C ,( IY + 0 ) ; READ THE FREQUENCY
LD B ,( IY + 1 ) ; FROM THE TABLE IN BC
PUSH IX ; STORE DENOMINATOR FOR NEXT LOOP
PUSH HL
SLACK .EQU ( BAS_END - $ )
.FILL SLACK , 00H
LD DE , 0 ; HLIX = HLIX / BC, DE = REMAINDER
LD A , 32
DIV_LP: ADD IX , IX
ADC HL , HL
EX DE , HL
ADC HL , HL
OR A
SBC HL , BC
INC IX
JR NC , DI V_CANSUB
ADD HL , BC
DEC IX
DIV_CANSUB:
EX DE , HL
DEC A
JR NZ , DI V_LP ; IX = RESULT
PUSH IX ; SAVE RESULT IN TABLE
POP DE
DUROVF: LD ( IY + 2 ), E
LD ( IY + 3 ), D
LD DE , 4 ; POINT TO NEXT TABLE
ADD IY , DE ; ENTRY TO UPDATE
POP HL ; RETREIVE THE DENOMINATOR
POP IX ; FOR THE NEXT LOOP
POP BC ; RETREIVE THE
DJNZ TBL_LP ; LOOP COUNTER
RET
;
BAS_STACK .EQU $
; ONE OCTAVE TONE TABLE IN FREQUENCY, DURATION FORMAT. TO COMPENSATE FOR DIFFERENT
; CPU FREQUENCIES THE DURATION IS CALCULATED AND POPULATED AT STARTUP. DIFFERENT OCTAVE
; AND DURATIONS ARE CALCULATE BY MODIFYING THE VALUES READ FROM THIS TABLE.
;
.ECHO "BASIC space remaining: "
.ECHO SLACK
.ECHO " bytes.\n"
FRQDURTBL:
.DW $ 105 A , $ 0 , $ 1152 , $ 0 ; C, C#
.DW $ 125 A , $ 0 , $ 1372 , $ 0 ; D, D#
.DW $ 149 A , $ 0 ; E
.DW $ 15 D3 , $ 0 , $ 171 F , $ 0 ; F, F#
.DW $ 1875 , $ 0 , $ 19 F4 , $ 0 ; G, G#
.DW $ 1B 80 , $ 0 , $ 1 D22 , $ 0 ; A, A#
.DW $ 1 EDE , $ 0 ; B
;
FDTBSIZ .EQU ( $ - FRQDURTBL ) / 4
.end
SLACK .EQU ( BAS_END - $ )
.FILL SLACK , 00H
;
.ECHO "BASIC space remaining: "
.ECHO SLACK
.ECHO " bytes.\n"
.END