	TITLE	BBC BASIC (C) R.T.RUSSELL 1984-2024
;
;PATCH FOR BBC BASIC TO CP/M 2.2 & 3.0
;*    PLAIN VANILLA CP/M VERSION     *
;(C) COPYRIGHT R.T.RUSSELL, 25-12-1986
;VERSION 5.0, 25-05-2024
;
	GLOBAL	OSINIT
	GLOBAL	OSRDCH
	GLOBAL	OSWRCH
	GLOBAL	OSLINE
	GLOBAL	OSSAVE
	GLOBAL	OSLOAD
	GLOBAL	OSOPEN
	GLOBAL	OSSHUT
	GLOBAL	OSBGET
	GLOBAL	OSBPUT
	GLOBAL	OSSTAT
	GLOBAL	GETEXT
	GLOBAL	GETPTR
	GLOBAL	PUTPTR
	GLOBAL	PROMPT
	GLOBAL	RESET
	GLOBAL	LTRAP
	GLOBAL	OSCLI
	GLOBAL	TRAP
	GLOBAL	OSKEY
	GLOBAL	OSCALL
;
	EXTRN	BYE
	EXTRN	GETKEY
;
	EXTRN	ESCAPE
	EXTRN	EXTERR
	EXTRN	CHECK
	EXTRN	CRLF
;
	EXTRN	ACCS
	EXTRN	FREE
	EXTRN	HIMEM
	EXTRN	CURLIN
	EXTRN	AUTONO
	EXTRN	USER
	EXTRN	VERMSG
;
;
;OSSAVE - Save an area of memory to a file.
;   Inputs: HL addresses filename (term CR)
;           DE = start address of data to save
;           BC = length of data to save (bytes)
; Destroys: A,B,C,D,E,H,L,F
;
STSAVE:	CALL	SAVLOD		;*SAVE
	JP	C,HUH		;"Bad command"
	PUSH	HL
	JR	OSS1
;
OSSAVE:	PUSH	BC		;SAVE
	CALL	SETUP0
OSS1:	EX	DE,HL
	CALL	CREATE
	JR	NZ,SAVE
DIRFUL:	LD	A,190
	CALL	EXTERR
	DEFM	'Directory full'
	DEFB	0
SAVE:	CALL	WRITE
	ADD	HL,BC
	EX	(SP),HL
	SBC	HL,BC
	EX	(SP),HL
	JR	Z,SAVE1
	JR	NC,SAVE
SAVE1:	POP	BC
CLOSE:	LD	A,16
	CALL	BDOS1
	INC	A
	RET	NZ
	LD	A,200
	CALL	EXTERR
	DEFM	'Close error'
	DEFB	0
;
;OSSHUT - Close disk file(s).
;   Inputs: E = file channel
;           If E=0 all files are closed (except SPOOL)
; Destroys: A,B,C,D,E,H,L,F
;
OSSHUT:	LD	A,E
	OR	A
	JR	NZ,SHUTIT
SHUT0:	INC	E
	BIT	3,E
	RET	NZ
	PUSH	DE
	CALL	SHUT1
	POP	DE
	JR	SHUT0
;
SHUTIT:	CALL	FIND1
	JR	NZ,SHUT2
	JP	CHANER
;
SESHUT:	LD	HL,FLAGS
	RES	0,(HL)		;STOP EXEC
	RES	1,(HL)		;STOP SPOOL
	LD	E,8		;SPOOL/EXEC CHANNEL
SHUT1:	CALL	FIND1
	RET	Z
SHUT2:	XOR	A
	LD	(HL),A
	DEC	HL
	LD	(HL),A
	LD	HL,37
	ADD	HL,DE
	BIT	7,(HL)
	INC	HL
	CALL	NZ,WRITE
	LD	HL,FCBSIZ
	ADD	HL,DE
	LD	BC,(FREE)
	SBC	HL,BC
	JR	NZ,CLOSE
	LD	(FREE),DE	;RELEASE SPACE
	JR	CLOSE
;
;TYPE - *TYPE command.
;Types file to console output.
;
TYPE:	SCF			;*TYPE
	CALL	OSOPEN
	OR	A
	JR	Z,NOTFND
	LD	E,A
TYPE1:	LD	A,(FLAGS)	;TEST
	BIT	7,A		;FOR
	JR	NZ,TYPESC	;ESCape
	CALL	OSBGET
	CALL	OSWRCH		;N.B. CALLS "TEST"
	JR	NC,TYPE1
	JR	OSSHUT
;
TYPESC:	CALL	OSSHUT		;CLOSE!
	JP	ABORT
;
;OSLOAD - Load an area of memory from a file.
;   Inputs: HL addresses filename (term CR)
;           DE = address at which to load
;           BC = maximum allowed size (bytes)
;  Outputs: Carry reset indicates no room for file.
; Destroys: A,B,C,D,E,H,L,F
;
STLOAD:	CALL	SAVLOD		;*LOAD
	PUSH	HL
	JR	OSL1
;
OSLOAD:	PUSH	BC		;LOAD
	CALL	SETUP0
OSL1:	EX	DE,HL
	CALL	OPEN
	JR	NZ,LOAD0
NOTFND:	LD	A,214
	CALL	EXTERR
	DEFM	'File not found'
	DEFB	0
LOAD:	CALL	READ
	JR	NZ,LOAD1
	CALL	INCSEC
	ADD	HL,BC
LOAD0:	EX	(SP),HL
	SBC	HL,BC
	EX	(SP),HL
	JR	NC,LOAD
LOAD1:	POP	BC
	PUSH	AF
	CALL	CLOSE
	POP	AF
	CCF
OSCALL:	RET
;
;OSOPEN - Open a file for reading or writing.
;   Inputs: HL addresses filename (term CR)
;           Carry set for OPENIN, cleared for OPENOUT.
;  Outputs: A = file channel (=0 if cannot open)
;           DE = file FCB
; Destroys: A,B,C,D,E,H,L,F
;
OPENIT:	PUSH	AF		;SAVE CARRY
	CALL	SETUP0
	POP	AF
	CALL	NC,CREATE
	CALL	C,OPEN
	RET
;
OSOPEN:	CALL	OPENIT
	RET	Z		;ERROR
	LD	B,7		;MAX. NUMBER OF FILES
	LD	HL,TABLE+15
OPEN1:	LD	A,(HL)
	DEC	HL
	OR	(HL)
	JR	Z,OPEN2		;FREE CHANNEL
	DEC	HL
	DJNZ	OPEN1
	LD	A,192
	CALL	EXTERR
	DEFM	'Too many open files'
	DEFB	0
;
OPEN2:	LD	DE,(FREE)	;FREE SPACE POINTER
	LD	(HL),E
	INC	HL
	LD	(HL),D
	LD	A,B		;CHANNEL (1-7)
	LD	HL,FCBSIZ
	ADD	HL,DE		;RESERVE SPACE
	LD	(FREE),HL
OPEN3:	LD	HL,FCB		;ENTRY FROM SPOOL/EXEC
	PUSH	DE
	LD	BC,36
	LDIR			;COPY FCB
	EX	DE,HL
	INC	HL
	LD	(HL),C		;CLEAR PTR
	INC	HL
	POP	DE
	LD	B,A
	CALL	RDF		;READ OR FILL
	LD	A,B
	JP	CHECK
;
;OSBPUT - Write a byte to a random disk file.
;   Inputs: E = file channel
;           A = byte to write
; Destroys: A,B,C,F
;
OSBPUT:	PUSH	DE
	PUSH	HL
	LD	B,A
	CALL	FIND
	LD	A,B
	LD	B,0
	DEC	HL
	LD	(HL),B		;CLEAR EOF
	INC	HL
	LD	C,(HL)
	RES	7,C
	SET	7,(HL)
	INC	(HL)
	INC	HL
	PUSH	HL
	ADD	HL,BC
	LD	(HL),A
	POP	HL
	CALL	Z,WRRDF		;WRITE THEN READ/FILL
	POP	HL
	POP	DE
	RET
;
;OSBGET - Read a byte from a random disk file.
;   Inputs: E = file channel
;  Outputs: A = byte read
;           Carry set if LAST BYTE of file
; Destroys: A,B,C,F
;
OSBGET:	PUSH	DE
	PUSH	HL
	CALL	FIND
	LD	C,(HL)
	RES	7,C
	INC	(HL)
	INC	HL
	PUSH	HL
	LD	B,0
	ADD	HL,BC
	LD	B,(HL)
	POP	HL
	CALL	PE,INCRDF	;INC SECTOR THEN READ
	CALL	Z,WRRDF		;WRITE THEN READ/FILL
	LD	A,B
	POP	HL
	POP	DE
	RET
;
;OSSTAT - Read file status.
;   Inputs: E = file channel
;  Outputs: Z flag set - EOF
;           (If Z then A=0)
;           DE = address of file block.
; Destroys: A,D,E,H,L,F
;
OSSTAT:	CALL	FIND
	DEC	HL
	LD	A,(HL)
	INC	A
	RET
;
;GETEXT - Find file size.
;   Inputs: E = file channel
;  Outputs: DEHL = file size (0-&800000)
; Destroys: A,B,C,D,E,H,L,F
;
GETEXT:	CALL	FIND
	EX	DE,HL
	LD	DE,FCB
	LD	BC,36
	PUSH	DE
	LDIR			;COPY FCB
	EX	DE,HL
	EX	(SP),HL
	EX	DE,HL
	LD	A,35
	CALL	BDOS1		;COMPUTE SIZE
	POP	HL
	XOR	A
	JR	GETPT1
;
;GETPTR - Return file pointer.
;   Inputs: E = file channel
;  Outputs: DEHL = pointer (0-&7FFFFF)
; Destroys: A,B,C,D,E,H,L,F
;
GETPTR:	CALL	FIND
	LD	A,(HL)
	ADD	A,A
	DEC	HL
GETPT1:	DEC	HL
	LD	D,(HL)
	DEC	HL
	LD	E,(HL)
	DEC	HL
	LD	H,(HL)
	LD	L,A
	SRL	D
	RR	E
	RR	H
	RR	L
	RET
;
;PUTPTR - Update file pointer.
;   Inputs: A = file channel
;           DEHL = new pointer (0-&7FFFFF)
; Destroys: A,B,C,D,E,H,L,F
;
PUTPTR:	LD	D,L
	ADD	HL,HL
	RL	E
	LD	B,E
	LD	C,H
	LD	E,A		;CHANNEL
	PUSH	DE
	CALL	FIND
	POP	AF
	AND	7FH
	BIT	7,(HL)		;PENDING WRITE?
	JR	Z,PUTPT1
	OR	80H
PUTPT1:	LD	(HL),A
	PUSH	DE
	PUSH	HL
	DEC	HL
	DEC	HL
	DEC	HL
	LD	D,(HL)
	DEC	HL
	LD	E,(HL)
	EX	DE,HL
	OR	A
	SBC	HL,BC
	POP	HL
	POP	DE
	RET	Z
	INC	HL
	OR	A
	CALL	M,WRITE
	PUSH	HL
	DEC	HL
	DEC	HL
	DEC	HL
	LD	(HL),0
	DEC	HL
	LD	(HL),B
	DEC	HL
	LD	(HL),C		;NEW RECORD NO.
	POP	HL
	JR	RDF
;
;WRRDF - Write, read; if EOF fill with zeroes.
;RDF - Read; if EOF fill with zeroes.
;   Inputs: DE address FCB.
;           HL addresses data buffer.
;  Outputs: A=0, Z-flag set.
;           Carry set if fill done (EOF)
; Destroys: A,H,L,F
;
WRRDF:	CALL	WRITE
RDF:	CALL	READ
	DEC	HL
	RES	7,(HL)
	DEC	HL
	LD	(HL),A		;CLEAR EOF FLAG
	RET	Z
	LD	(HL),-1		;SET EOF FLAG
	INC	HL
	INC	HL
	PUSH	BC
	XOR	A
	LD	B,128
FILL:	LD	(HL),A
	INC	HL
	DJNZ	FILL
	POP	BC
	SCF
	RET
;
;INCRDF - Increment record, read; if EOF fill.
;   Inputs: DE addresses FCB.
;           HL addresses data buffer.
;  Outputs: A=1, Z-flag reset.
;           Carry set if fill done (EOF)
; Destroys: A,H,L,F
;
INCRDF:	CALL	INCSEC
	CALL	RDF
	INC	A
	RET
;
;READ - Read a record from a disk file.
;   Inputs: DE addresses FCB.
;           HL = address to store data.
;  Outputs: A<>0 & Z-flag reset indicates EOF.
;           Carry = 0
; Destroys: A,F
;
;BDOS1 - CP/M BDOS call.
;   Inputs: A = function number
;          DE = parameter
;  Outputs: AF = result (carry=0)
; Destroys: A,F
;
READ:	CALL	SETDMA
	LD	A,33
BDOS1:	CALL	BDOS0
	JR	NZ,CPMERR
	OR	A
	RET
CPMERR:	LD	A,255
	CALL	EXTERR
	DEFM	'CP/M Error'
	DEFB	0
;
BDOS0:	PUSH	BC
	PUSH	DE
	PUSH	HL
	PUSH	IX
	PUSH	IY
	LD	C,A
	CALL	BDOS
	INC	H
	DEC	H
	POP	IY
	POP	IX
	POP	HL
	POP	DE
	POP	BC
	RET
;
;WRITE - Write a record to a disk file.
;   Inputs: DE addresses FCB.
;           HL = address to get data.
; Destroys: A,F
;
WRITE:	CALL	SETDMA
	LD	A,40
	CALL	BDOS1
	JR	Z,INCSEC
	LD	A,198
	CALL	EXTERR
	DEFM	'Disk full'
	DEFB	0
;
;INCSEC - Increment random record number.
;   Inputs: DE addresses FCB.
; Destroys: F
;
INCSEC:	PUSH	HL
	LD	HL,33
	ADD	HL,DE
INCS1:	INC	(HL)
	INC	HL
	JR	Z,INCS1
	POP	HL
	RET
;
;OPEN - Open a file for access.
;   Inputs: FCB set up.
;  Outputs: DE = FCB
;           A=0 & Z-flag set indicates Not Found.
;           Carry = 0
; Destroys: A,D,E,F
;
OPEN:	LD	DE,FCB
	LD	A,15
	CALL	BDOS1
	INC	A
	RET
;
;CREATE - Create a disk file for writing.
;   Inputs: FCB set up.
;  Outputs: DE = FCB
;           A=0 & Z-flag set indicates directory full.
;           Carry = 0
; Destroys: A,D,E,F
;
CREATE:	CALL	CHKAMB
	LD	DE,FCB
	LD	A,19
	CALL	BDOS1		;DELETE
	LD	A,22
	CALL	BDOS1		;MAKE
	INC	A
	RET
;
;CHKAMB - Check for ambiguous filename.
; Destroys: A,D,E,F
;
CHKAMB:	PUSH	BC
	LD	DE,FCB
	LD	B,12
CHKAM1:	LD	A,(DE)
	CP	'?'
	JR	Z,AMBIG		;AMBIGUOUS
	INC	DE
	DJNZ	CHKAM1
	POP	BC
	RET
AMBIG:	LD	A,204
	CALL	EXTERR
	DEFM	'Bad name'
	DEFB	0
;
;SETDMA - Set "DMA" address.
;   Inputs: HL = address
; Destroys: A,F
;
SETDMA:	LD	A,26
	EX	DE,HL
	CALL	BDOS0
	EX	DE,HL
	RET
;
;FIND - Find file parameters from channel.
;   Inputs: E = channel
;  Outputs: DE addresses FCB
;           HL addresses pointer byte (FCB+37)
; Destroys: A,D,E,H,L,F
;
FIND:	INC	E		;N.B. channel 8 is SPOOL/EXEC
	DEC	E
	JR	Z,CHANER
	CALL	FIND1
	LD	HL,37
	ADD	HL,DE
	RET	NZ
CHANER:	LD	A,222
	CALL	EXTERR
	DEFM	'Invalid channel'
	DEFB	0
;
;FIND1 - Look up file table.
;   Inputs: E = channel
;  Outputs: Z-flag set = file not opened
;           If NZ, DE addresses FCB
;                  HL points into table
; Destroys: A,D,E,H,L,F
;
FIND1:	LD	A,E
	AND	7
	ADD	A,A
	LD	E,A
	LD	D,0
	LD	HL,TABLE
	ADD	HL,DE
	LD	E,(HL)
	INC	HL
	LD	D,(HL)
	LD	A,D
	OR	E
	RET
;
;SETUP - Set up File Control Block.
;   Inputs: HL addresses filename
;           Format  [A:]FILENAME[.EXT]
;           Device defaults to current drive
;           Extension defaults to .BBC
;           A = fill character
;  Outputs: HL updated
;           A = terminator
;           BC = 128
; Destroys: A,B,C,H,L,F
;
;FCB FORMAT (36 BYTES TOTAL):
; 0      0=SAME DISK, 1=DISK A, 2=DISK B (ETC.)
; 1-8    FILENAME, PADDED WITH SPACES
; 9-11   EXTENSION, PADDED WITH SPACES
; 12     CURRENT EXTENT, SET TO ZERO
; 32-35  CLEARED TO ZERO
;
SETUP0:	LD	A,' '
SETUP:	PUSH	DE
	PUSH	HL
	LD	DE,FCB+9
	LD	HL,BBC
	LD	BC,3
	LDIR
	LD	HL,FCB+32
	LD	B,4
SETUP1:	LD	(HL),C
	INC	HL
	DJNZ	SETUP1
	POP	HL
	LD	C,A
	XOR	A
	LD	(DE),A
	POP	DE
	CALL	SKIPSP
	CP	'"'
	JR	NZ,SETUP2
	INC	HL
	CALL	SKIPSP
	CALL	SETUP2
	CP	'"'
	INC	HL
	JR	Z,SKIPSP
BADSTR:	LD	A,253
	CALL	EXTERR
	DEFM	'Bad string'
	DEFB	0
;
PARSE:	LD	A,(HL)
	INC	HL
	CP	'`'
	RET	NC
	CP	'?'
	RET	C
	XOR	40H
	RET
;
SETUP2:	PUSH	DE
	INC	HL
	LD	A,(HL)
	CP	':'
	DEC	HL
	LD	A,B
	JR	NZ,DEVICE
	LD	A,(HL)		;DRIVE
	AND	31
	INC	HL
	INC	HL
DEVICE:	LD	DE,FCB
	LD	(DE),A
	INC	DE
	LD	B,8
COPYF:	LD	A,(HL)
	CP	'.'
	JR	Z,COPYF1
	CP	' '
	JR	Z,COPYF1
	CP	CR
	JR	Z,COPYF1
	CP	'='
	JR	Z,COPYF1
	CP	'"'
	JR	Z,COPYF1
	LD	C,'?'
	CP	'*'
	JR	Z,COPYF1
	LD	C,' '
	INC	HL
	CP	'|'
	JR	NZ,COPYF2
	CALL	PARSE
	JR	COPYF0
COPYF1:	LD	A,C
COPYF2:	CALL	UPPRC
COPYF0:	LD	(DE),A
	INC	DE
	DJNZ	COPYF
COPYF3:	LD	A,(HL)
	INC	HL
	CP	'*'
	JR	Z,COPYF3
	CP	'.'
	LD	BC,3*256+' '
	LD	DE,FCB+9
	JR	Z,COPYF
	DEC	HL
	POP	DE
	LD	BC,128
SKIPSP:	LD	A,(HL)
	CP	' '
	RET	NZ
	INC	HL
	JR	SKIPSP
;
BBC:	DEFM	'BBC'
;
;HEX - Read a hex string and convert to binary.
;   Inputs: HL = text pointer
;  Outputs: HL = updated text pointer
;           DE = value
;            A = terminator (spaces skipped)
; Destroys: A,D,E,H,L,F
;
HEX:	LD	DE,0		;INITIALISE
	CALL	SKIPSP
HEX1:	LD	A,(HL)
	CALL	UPPRC
	CP	'0'
	JR	C,SKIPSP
	CP	'9'+1
	JR	C,HEX2
	CP	'A'
	JR	C,SKIPSP
	CP	'F'+1
	JR	NC,SKIPSP
	SUB	7
HEX2:	AND	0FH
	EX	DE,HL
	ADD	HL,HL
	ADD	HL,HL
	ADD	HL,HL
	ADD	HL,HL
	EX	DE,HL
	OR	E
	LD	E,A
	INC	HL
	JR	HEX1
;
;OSCLI - Process an "operating system" command
;
OSCLI:	CALL	SKIPSP
	CP	CR
	RET	Z
	CP	'|'
	RET	Z
	CP	'.'
	JP	Z,DOT		;*.
	EX	DE,HL
	LD	HL,COMDS
OSCLI0:	LD	A,(DE)
	CALL	UPPRC
	CP	(HL)
	JR	Z,OSCLI2
	JR	C,HUH
OSCLI1:	BIT	7,(HL)
	INC	HL
	JR	Z,OSCLI1
	INC	HL
	INC	HL
	JR	OSCLI0
;
OSCLI2:	PUSH	DE
OSCLI3:	INC	DE
	INC	HL
	LD	A,(DE)
	CALL	UPPRC
	CP	'.'		;ABBREVIATED?
	JR	Z,OSCLI4
	XOR	(HL)
	JR	Z,OSCLI3
	CP	80H
	JR	Z,OSCLI4
	POP	DE
	JR	OSCLI1
;
OSCLI4:	POP	AF
	INC	DE
OSCLI5:	BIT	7,(HL)
	INC	HL
	JR	Z,OSCLI5
	LD	A,(HL)
	INC	HL
	LD	H,(HL)
	LD	L,A
	PUSH	HL
	EX	DE,HL
	JP	SKIPSP
;
;
ERA:	CALL	SETUP0		;*ERA, *ERASE
	LD	C,19
	JR	XEQ		;"DELETE"
;
RES:	LD	C,13		;*RESET
	JR	XEQ		;"RESET"
;
DRV:	CALL	SETUP0		;*DRIVE
	LD	A,(FCB)
	DEC	A
	JP	M,HUH
	LD	E,A
	LD	C,14
	JR	XEQ0
;
REN:	CALL	SETUP0		;*REN, *RENAME
	CP	'='
	JR	NZ,HUH
	INC	HL		;SKIP "="
	PUSH	HL
	CALL	EXISTS
	LD	HL,FCB
	LD	DE,FCB+16
	LD	BC,12
	LDIR
	POP	HL
	CALL	SETUP0
	CALL	CHKAMB
	LD	C,23
XEQ:	LD	DE,FCB
XEQ0:	LD	A,(HL)
	CP	CR
	JR	NZ,HUH
BDC:	LD	A,C
	CALL	BDOS1
	RET	P
HUH:	LD	A,254
	CALL	EXTERR
	DEFM	'Bad command'
	DEFB	0
;
EXISTS:	LD	HL,DSKBUF
	CALL	SETDMA
	LD	DE,FCB
	LD	A,17
	CALL	BDOS1		;SEARCH
	INC	A
	RET	Z
	LD	A,196
	CALL	EXTERR
	DEFM	'File exists'
	DEFB	0
;
SAVLOD:	CALL	SETUP0		;PART OF *SAVE, *LOAD
	CALL	HEX
	CP	'+'
	PUSH	AF
	PUSH	DE
	JR	NZ,SAVLO1
	INC	HL
SAVLO1:	CALL	HEX
	CP	CR
	JR	NZ,HUH
	EX	DE,HL
	POP	DE
	POP	AF
	RET	Z
	OR	A
	SBC	HL,DE
	RET	NZ
	JR	HUH
;
DOT:	INC	HL
DIR:	LD	A,'?'		;*DIR
	CALL	SETUP
	CP	CR
	JR	NZ,HUH
	LD	C,17
DIR0:	LD	B,4
DIR1:	CALL	LTRAP
	LD	DE,FCB
	LD	HL,DSKBUF
	CALL	SETDMA
	LD	A,C
	CALL	BDOS1		;SEARCH DIRECTORY
	JP	M,CRLF
	RRCA
	RRCA
	RRCA
	AND	60H
	LD	E,A
	LD	D,0
	LD	HL,DSKBUF+1
	ADD	HL,DE
	PUSH	HL
	LD	DE,8
	ADD	HL,DE
	LD	E,(HL)
	INC	HL
	BIT	7,(HL)		;SYSTEM FILE?
	POP	HL
	LD	C,18
	JR	NZ,DIR1
	PUSH	BC
	LD	A,(FCB)
	DEC	A
	LD	C,25
	CALL	M,BDC
	ADD	A,'A'
	CALL	OSWRCH
	LD	B,8
	LD	A,' '
	BIT	7,E		;READ ONLY?
	JR	Z,DIR3
	LD	A,'*'
DIR3:	CALL	CPTEXT
	LD	B,3
	LD	A,' '
	CALL	SPTEXT
	POP	BC
	DJNZ	DIR2
	CALL	CRLF
	JR	DIR0
;
DIR2:	PUSH	BC
	LD	B,5
PAD:	LD	A,' '
	CALL	OSWRCH
	DJNZ	PAD
	POP	BC
	JR	DIR1
;
OPT:	CALL	HEX		;*OPT
	LD	A,E
	AND	3
SETOPT:	LD	(OPTVAL),A
	RET
;
RESET:	XOR	A
	JR	SETOPT
;
EXEC:	LD	A,00000001B	;*EXEC
	DEFB	1		;SKIP 2 BYTES (LD BC)
SPOOL:	LD	A,00000010B	;*SPOOL
	PUSH	AF
	PUSH	HL
	CALL	SESHUT		;STOP SPOOL/EXEC
	POP	HL
	POP	BC
	LD	A,(HL)
	CP	CR		;JUST SHUT?
	RET	Z
	LD	A,(FLAGS)
	OR	B
	LD	(FLAGS),A	;SPOOL/EXEC FLAG
	RRA			;CARRY=1 FOR EXEC
	CALL	OPENIT		;OPEN SPOOL/EXEC FILE
	RET	Z		;DIR FULL / NOT FOUND
	POP	IX		;RETURN ADDRESS
	LD	HL,(HIMEM)
	OR	A
	SBC	HL,SP		;SP=HIMEM?
	ADD	HL,SP
	JR	NZ,JPIX		;ABORT
	LD	BC,-FCBSIZ
	ADD	HL,BC		;HL=HL-FCBSIZ
	LD	(HIMEM),HL	;NEW HIMEM
	LD	(TABLE),HL	;FCB/BUFFER
	LD	SP,HL		;NEW SP
	EX	DE,HL
	CALL	OPEN3		;FINISH OPEN OPERATION
JPIX:	JP	(IX)		;"RETURN"
;
UPPRC:	AND	7FH
	CP	'`'
	RET	C
	AND	5FH		;CONVERT TO UPPER CASE
	RET
;
HELP:	LD	B,32
	LD	HL,VERMSG
	JP	PTEXT
;
;*ESC COMMAND
;
ESCCTL:	LD	A,(HL)
	CALL	UPPRC		;**
	CP	'O'
	JR	NZ,ESCC1
	INC	HL
ESCC1:	CALL	HEX
	LD	A,E
	OR	A
	LD	HL,FLAGS
	RES	6,(HL)		;ENABLE ESCAPE
	RET	Z
	SET	6,(HL)		;DISABLE ESCAPE
	RET
;
;
COMDS:	DEFM	'BY'
	DEFB	'E'+80H
	DEFW	BYE
	DEFM	'DI'
	DEFB	'R'+80H
	DEFW	DIR
	DEFM	'DRIV'
	DEFB	'E'+80H
	DEFW	DRV
	DEFM	'ERAS'
	DEFB	'E'+80H
	DEFW	ERA
	DEFM	'ER'
	DEFB	'A'+80H
	DEFW	ERA
	DEFM	'ES'
	DEFB	'C'+80H
	DEFW	ESCCTL
	DEFM	'EXE'
	DEFB	'C'+80H
	DEFW	EXEC
	DEFM	'HEL'
	DEFB	'P'+80H
	DEFW	HELP
	DEFM	'LOA'
	DEFB	'D'+80H
	DEFW	STLOAD
	DEFM	'OP'
	DEFB	'T'+80H
	DEFW	OPT
	DEFM	'QUI'
	DEFB	'T'+80H
	DEFW	BYE
	DEFM	'RENAM'
	DEFB	'E'+80H
	DEFW	REN
	DEFM	'RE'
	DEFB	'N'+80H
	DEFW	REN
	DEFM	'RESE'
	DEFB	'T'+80H
	DEFW	RES
	DEFM	'SAV'
	DEFB	'E'+80H
	DEFW	STSAVE
	DEFM	'SPOO'
	DEFB	'L'+80H
	DEFW	SPOOL
	DEFM	'TYP'
	DEFB	'E'+80H
	DEFW	TYPE
	DEFB	0FFH
;
;PTEXT - Print text
;   Inputs: HL = address of text
;            B = number of characters to print
; Destroys: A,B,H,L,F
;
CPTEXT:	PUSH	AF
	LD	A,':'
	CALL	OSWRCH
	POP	AF
SPTEXT:	CALL	OSWRCH
PTEXT:	LD	A,(HL)
	AND	7FH
	INC	HL
	CALL	OSWRCH
	DJNZ	PTEXT
	RET
;
;OSINIT - Initialise RAM mapping etc.
;If BASIC is entered by BBCBASIC FILENAME then file
;FILENAME.BBC is automatically CHAINed.
;   Outputs: DE = initial value of HIMEM (top of RAM)
;            HL = initial value of PAGE (user program)
;            Z-flag reset indicates AUTO-RUN.
;  Destroys: A,B,C,D,E,H,L,F
;
OSINIT:	LD	C,45		;*
	LD	E,254		;*
	CALL	BDOS		;*
	XOR	A
	LD	B,INILEN
	LD	HL,TABLE
CLRTAB:	LD	(HL),A		;CLEAR FILE TABLE ETC.
	INC	HL
	DJNZ	CLRTAB
	LD	DE,ACCS
	LD	HL,DSKBUF
	LD	C,(HL)
	INC	HL
	CP	C		;N.B. A=B=0
	JR	Z,NOBOOT
	LDIR			;COPY TO ACCS
NOBOOT:	EX	DE,HL
	LD	(HL),CR
	LD	DE,(6)		;DE = HIMEM
	LD	E,A		;PAGE BOUNDARY
	LD	HL,USER
	RET
;
;
;TRAP - Test ESCAPE flag and abort if set;
;       every 20th call, test for keypress.
; Destroys: A,H,L,F
;
;LTRAP - Test ESCAPE flag and abort if set.
; Destroys: A,F
;
TRAP:	LD	HL,TRPCNT
	DEC	(HL)
	CALL	Z,TEST20	;TEST KEYBOARD
LTRAP:	LD	A,(FLAGS)	;ESCAPE FLAG
	OR	A		;TEST
	RET	P
ABORT:	LD	HL,FLAGS	;ACKNOWLEDGE
	RES	7,(HL)		;ESCAPE
	JP	ESCAPE		;AND ABORT
;
;TEST - Sample for ESCape and CTRL/S. If ESCape
;       pressed set ESCAPE flag and return.
; Destroys: A,F
;
TEST20:	LD	(HL),20
TEST:	PUSH	DE
	LD	A,6
	LD	E,0FFH
	CALL	BDOS0
	POP	DE
	OR	A
	RET	Z
	CP	'S' AND 1FH	;PAUSE DISPLAY?
	JR	Z,OSRDCH
	CP	ESC
	JR	Z,ESCSET
	LD	(INKEY),A
	RET
;
;OSRDCH - Read from the current input stream (keyboard).
;  Outputs: A = character
; Destroys: A,F
;
KEYGET:	LD	B,(IX-12)	;SCREEN WIDTH
	CALL	OSRDCH
	CP	DEL
	JR	Z,KEYDEL
	CP	224
	RET	NZ
	CALL	OSRDCH
	SUB	65
	RET
;
KEYDEL:	LD	A,BS
	RET
;
OSRDCH:	LD	A,(FLAGS)
	RRA			;*EXEC ACTIVE?
	JR	C,EXECIN
	PUSH	HL
	SBC	HL,HL		;HL=0
	CALL	OSKEY
	POP	HL
	RET	C
	JR	OSRDCH
;
;EXECIN - Read byte from EXEC file
;  Outputs: A = byte read
; Destroys: A,F
;
EXECIN:	PUSH	BC		;SAVE REGISTERS
	PUSH	DE
	PUSH	HL
	LD	E,8		;SPOOL/EXEC CHANNEL
	LD	HL,FLAGS
	RES	0,(HL)
	CALL	OSBGET
	SET	0,(HL)
	PUSH	AF
	CALL	C,SESHUT	;END EXEC IF EOF
	POP	AF
	POP	HL		;RESTORE REGISTERS
	POP	DE
	POP	BC
	RET
;
;
;OSKEY - Read key with time-limit, test for ESCape.
;Main function is carried out in user patch.
;   Inputs: HL = time limit (centiseconds)
;  Outputs: Carry reset if time-out
;           If carry set A = character
; Destroys: A,H,L,F
;
OSKEY:	PUSH	HL
	LD	HL,INKEY
	LD	A,(HL)
	LD	(HL),0
	POP	HL
	OR	A
	SCF
	RET	NZ
	PUSH	DE
	CALL	GETKEY
	POP	DE
	RET	NC
	CP	ESC
	SCF
	RET	NZ
ESCSET:	PUSH	HL
	LD	HL,FLAGS
	BIT	6,(HL)		;ESC DISABLED?
	JR	NZ,ESCDIS
	SET	7,(HL)		;SET ESCAPE FLAG
ESCDIS:	POP	HL
	RET
;
;OSWRCH - Write a character to console output.
;   Inputs: A = character.
; Destroys: Nothing
;
OSWRCH:	PUSH	AF
	PUSH	DE
	PUSH	HL
	LD	E,A
	CALL	TEST
	CALL	EDPUT
	POP	HL
	POP	DE
	POP	AF
	RET
;
EDPUT:	LD	A,(FLAGS)
	BIT	3,A
	JR	Z,WRCH
	LD	A,E
	CP	' '
	RET	C
	LD	HL,(EDPTR)
	LD	(HL),E
	INC	L
	RET	Z
	LD	(EDPTR),HL
	RET
;
PROMPT:	LD	E,'>'
WRCH:	LD	A,(OPTVAL)	;FAST ENTRY
	ADD	A,3
	CP	3
	JR	NZ,WRCH1
	ADD	A,E
	LD	A,2
	JR	C,WRCH1
	LD	A,6
WRCH1:	CALL	BDOS0
	LD	HL,FLAGS
	BIT	2,(HL)
	LD	A,5		;PRINTER O/P
	CALL	NZ,BDOS0
	BIT	1,(HL)		;SPOOLING?
	RET	Z
	RES	1,(HL)
	LD	A,E		;BYTE TO WRITE
	LD	E,8		;SPOOL/EXEC CHANNEL
	PUSH	BC
	CALL	OSBPUT
	POP	BC
	SET	1,(HL)
	RET
;
TOGGLE:	LD	BC,0
	LD	A,(FLAGS)
	XOR	00000100B
	LD	(FLAGS),A
	RET
;
INSERT:	LD	A,(FLAGS)
	XOR	00010000B
	LD	(FLAGS),A
	RET
;
;OSLINE - Read/edit a complete line, terminated by CR.
;   Inputs: HL addresses destination buffer.
;           (L=0)
;  Outputs: Buffer filled, terminated by CR.
;           A=0.
; Destroys: A,B,C,D,E,H,L,F
;
OSLINE:	LD	IX,200H
	LD	A,(FLAGS)
	BIT	3,A		;EDIT MODE?
	JR	Z,OSLIN1
	RES	3,A
	LD	(FLAGS),A
	LD	HL,(EDPTR)
	CP	L
OSLIN1:	LD	A,CR
	LD	(HL),A
	CALL	NZ,OSWRCH
	LD	L,0
	LD	C,L		;REPEAT FLAG
	JR	Z,OSWAIT	;SUPPRESS UNWANTED SPACE
UPDATE:	LD	B,0
UPD1:	LD	A,(HL)
	INC	B
	INC	HL
	CP	CR
	PUSH	AF
	PUSH	HL
	LD	E,A
	CALL	NZ,WRCH		;FAST WRCH
	POP	HL
	POP	AF
	JR	NZ,UPD1
	LD	A,' '
	CALL	OSWRCH
	LD	E,BS
UPD2:	PUSH	HL
	CALL	WRCH		;FAST WRCH
	POP	HL
	DEC	HL
	DJNZ	UPD2
OSWAIT:	LD	A,C
	DEC	B
	JR	Z,LIMIT
	OR	A		;REPEAT COMMAND?
LIMIT:	CALL	Z,KEYGET	;READ KEYBOARD
	LD	C,A		;SAVE FOR REPEAT
	LD	DE,OSWAIT	;RETURN ADDRESS
	PUSH	DE
	LD	A,(FLAGS)
	OR	A		;TEST FOR ESCAPE
	LD	A,C
	JP	M,OSEXIT
	CP	'P' AND 1FH
	JP	Z,TOGGLE
	CP	(IX-11)		;CURSOR UP     (IX-11)
	JP	Z,LEFT
	CP	(IX-10)		;CURSOR DOWN   (IX-10)
	JP	Z,RIGHT
	LD	B,0
	CP	(IX-5)		;CLEAR LEFT    (IX-5)
	JR	Z,BACK
	CP	(IX-9)		;START OF LINE (IX-9)
	JR	Z,LEFT
	CP	(IX-8)		;END OF LINE   (IX-8)
	JP	Z,RIGHT
	CP	(IX-7)		;CLEAR RIGHT   (IX-7)
	JR	Z,DELETE
	LD	C,0		;INHIBIT REPEAT
	CP	(IX-1)		;INSERT / OVR  (IX-1)
	JP	Z,INSERT
	CP	(IX-6)		;DELETE LEFT   (IX-6)
	JR	Z,BACK
	CP	(IX-4)		;CURSOR LEFT   (IX-4)
	JR	Z,LEFT
	CP	(IX-2)		;DELETE RIGHT  (IX-2)
	JR	Z,DELETE
	CP	(IX-3)		;CURSOR RIGHT  (IX-3)
	JP	Z,RIGHT
	CP	' '		;PRINTING CHARACTER
	JR	NC,SAVECH
	CP	CR		;ENTER LINE
	RET	NZ
OSEXIT:	LD	A,(HL)
	CALL	OSWRCH		;WRITE REST OF LINE
	INC	HL
	SUB	CR
	JR	NZ,OSEXIT
	POP	DE		;DITCH RETURN ADDRESS
	CP	C
	JP	NZ,ABORT	;ESCAPE
	LD	A,LF
	CALL	OSWRCH
	XOR	A
	LD	L,A
	LD	(EDPTR),HL
	LD	DE,(CURLIN)
	CP	D
	RET	NZ
	CP	E
	RET	NZ
	LD	DE,(AUTONO)
	CP	D
	RET	NZ
	CP	E
	RET	NZ
	LD	DE,EDITST
	LD	B,4
CMPARE:	LD	A,(DE)
	CP	(HL)
	LD	A,0
	RET	NZ
	INC	HL
	INC	DE
	LD	A,(HL)
	CP	'.'
	JR	Z,ABBR
	DJNZ	CMPARE
ABBR:	XOR	A
	LD	B,A
	LD	C,L
	LD	L,A
	LD	DE,LISTST
	EX	DE,HL
	LDIR
	LD	HL,FLAGS
	SET	3,(HL)
	RET
;
BACK:	SCF			;DELETE LEFT
LEFT:	INC	L		;CURSOR LEFT
	DEC	L
	JR	Z,STOP
	LD	A,BS
	CALL	OSWRCH
	DEC	L
	RET	NC
DELETE:	LD	A,(HL)		;DELETE RIGHT
	CP	CR
	JR	Z,STOP
	LD	D,H
	LD	E,L
DEL1:	INC	DE
	LD	A,(DE)
	DEC	DE
	LD	(DE),A
	INC	DE
	CP	CR
	JR	NZ,DEL1
DEL2:	POP	DE		;DITCH
	JP	UPDATE
;
SAVECH:	LD	D,A
	LD	A,(FLAGS)
	AND	00010000B
	LD	A,D
	JR	NZ,RIGHT1
	LD	D,A
	LD	A,CR		;INSERT SPACE
	CP	(HL)
	LD	A,D
	JR	Z,RIGHT1
	LD	D,H
	LD	E,254
	PUSH	AF
INS1:	INC	DE
	LD	(DE),A
	DEC	DE
	LD	A,E
	CP	L
	DEC	DE
	LD	A,(DE)
	JR	NZ,INS1
	POP	AF
	LD	(HL),A
	INC	L
	JR	Z,WONTGO
	CALL	OSWRCH
	JR	DEL2
;
RIGHT:	LD	A,(HL)		;CURSOR RIGHT
	CP	CR
	JR	Z,STOP
RIGHT1:	LD	D,(HL)		;PRINTING CHARACTER
	LD	(HL),A
	INC	L
	JR	Z,WONTGO	;LINE TOO LONG
	CALL	OSWRCH
	LD	A,CR
	CP	D
	RET	NZ
	LD	(HL),A
	RET
;
WONTGO:	DEC	L
	LD	(HL),CR
	LD	A,BEL
	CALL	OSWRCH		;BEEP!
STOP:	LD	C,0		;STOP REPEAT
	RET
;
;
EDITST:	DEFM	'EDIT'
LISTST:	DEFM	'LIST'
;
BEL	EQU	7
BS	EQU	8
HT	EQU	9
LF	EQU	0AH
VT	EQU	0BH
CR	EQU	0DH
ESC	EQU	1BH
DEL	EQU	7FH
;
BDOS	EQU	5
;
FCB	EQU	5CH
DSKBUF	EQU	80H
;
FCBSIZ	EQU	128+36+2
;
TRPCNT:	DEFB	10
TABLE:	DEFS	16		;FILE BLOCK POINTERS
FLAGS:	DEFB	0
INKEY:	DEFB	0
EDPTR:	DEFW	0
OPTVAL:	DEFB	0
INILEN	EQU	$-TABLE
;
FIN:	END
