		title	'MAKE Utility RSX for CP/M Plus'
		
FALSE		equ	0
TRUE		equ	NOT FALSE
EOF		equ	1AH
CR		equ	13
LF		equ	10
TAB		equ	9

	; BDOS function calls

WBOOT		equ	0		; warm boot
CONOUT		equ	2		; output char to console
PRINTS		equ	9		; print string at console
RDCON		equ	10		; read console input buffer
CLOSEF		equ	16		; close disk file
SFIRST		equ	17		; search for first file match
READSQ		equ	20		; read sequential sector
SETDMA		equ	26		; set DMA address
SETUSR		equ	32		; set/get user number
SETMUL		equ	44		; set multi-sector-count
GETSCB		equ	49		; get/set system control block
GETPRC		equ	108		; get/set program return code
PARFCB		equ	152		; parse file name into FCB

		cseg
		
	; RSX- Header
		
serial:		db	0,0,0,0,0,0
start:		jmp	ftest		; start of program
next:		jmp	0		; link to next RSX in chain
prev:		dw	0		; link to prev. RSX in chain
remove:		db	TRUE		; remove RSX on warm start (altered by MAKE.COM)
nonbank:	db	FALSE		; load in every CP/M Plus system
rname:		db	'MAKERSX '	; name of RSX (tested by MAKE.COM)
loader:		db	0,0,0		; reserved for LOADER module usage

	; FCB of make file

make$fcb:	ds	36		; filled in from MAKE.COM
make$user:	ds	1		;    "

	; filter for BDOS 10 : read console input

ftest:		mvi	a,RDCON		; is it read console buffer ?
		cmp	c
		jnz	next		; no : jump to next RSX in chain
		
		mov	a,e		; is prewritten input used ?
		ora	d
		jz	next		; skip RSX, cannot handle, CCP don't use it

		lda	remove		; is RSX active ?
		ora	a
		jnz	next		; no: skip RSX (waiting for removal)

		xchg			; save address of console buffer
		shld	rdcon$addr

		mvi	c,GETSCB	; is ccp executing ? (bit7 of 18H in SCB set)
		lxi	d,test$ccp
		call	next
		ora	a
		jm	trap$rdcon	; yes: trap RDCON call

	; unfiltered BDOS 10 call

origin:		lhld	rdcon$addr	; restore registers
		xchg
		mvi	c,RDCON
		jmp	next		; and reach call to next RSX in chain

	; process valid read console call

trap$rdcon:	mvi	c,GETPRC	; get program return code
		lxi	d,0FFFFH	; ( NOT reset in CCP so far )
		call	next
		inr	h		; H = 0FFH?
		jz	abort$make	; because of faulty program execution

	; save BDOS DMA address

		mvi	c,GETSCB	; get entry in SCB
		lxi	d,scb$dma
		call	next
		shld	user$dma	; because we need it

	; save current user number
	
		mvi	c,SETUSR
		mvi	e,0FFH		; get user number code
		call	next
		sta	default$user

	; get drive search chain out of SCB
	
		mvi	c,GETSCB
		lxi	d,scb$drive12
		call	next
		shld	drive1
		mvi	c,GETSCB
		lxi	d,scb$drive34
		call	next
		shld	drive3

	; execute or skip subsequent command lines
	
test$next:	call	getc		; read char from make file
		cpi	EOF		; end of file reached ?
		jz	make$end	; yes: terminate make normaly
		cpi	' '		; space or TAB ?
		jz	test$condition	; yes: execute or skip tabbed line
		cpi	TAB
		jz	test$condition
		cpi	20H		; other control char ?
		jc	test$next	; skip it

		call	ungetc		; char back into make file

	; read object file name from makefile

make$next:	mvi	a,FALSE		; clear make-flag (default := no operation)
		sta	make$flag
		sta	EOL$flag	; end of line of make file not reached
		sta	EOF$flag	; end of make file not reached

		call	read$name	; read file name into name buffer

		lda	EOF$flag	; end of file ?
		ora	a
		jz	make$next1
		lda	name$buffer	; and name buffer empty ?
		ora	a
		jz	make$end	; yes: terminate make rsx
		jmp	EOF$error	; no : rapport EOF error

make$next1:	lda	name$buffer	; test first char of name buffer
		ora	a
		jz	test$next	; skip empty line

		lda	EOL$flag	; (unexpected) end of input line after file name?
		ora	a
		jnz	format$error	; yes: bad formatted make file

		call	parse$name	; parse file name into RSX$FCB
		ora	a		; parse error ?
		jnz	format$error	; yes: bad formatted make file

		call	search$first	; try to find this file
		cpi	3		; file not found or no time stamps ?
		jnc	make$object	; yes: make file

		lda	RSX$DMA+96	; valid time stamp ?
		cpi	21H
		jnz	make$object	; no: make file anyway

		call	get$date	; get date out of directory record (BC=date,DE=time)
		mov	a,b		; date =0 ? (stamp inactive)
		ora	c
		jz	make$object	; make file anyway

		lxi	h,obj$date	; store date & time of object file
		mov	m,c		; BC = date
		inx	h
		mov	m,b
		inx	h
		mov	m,d		; D = hour
		inx	h
		mov	m,e		; E = minute

		jmp	make$colon	; read colon after object filename		

make$object:	call	set$makef	; set make flag

	; read colon from makefile

make$colon:	call	read$name	; read over ':'

		lda	EOF$flag	; unexpected end of file ?
		ora	a
		jnz	EOF$error	; rapport error

		lda	name$buffer	; name buffer contains ':' ?
		cpi	':'
		jnz	format$error	; no: bad formatted make file

		lda	EOL$flag	; (unexpected) end of input line after colon?
		ora	a
		jnz	format$error	; yes: bad formatted make file

	; read list of dependency files

depend$list:	lda	EOL$flag	; end of input line reached ?
		ora	a
		jnz	test$condition	; yes: execute line if make$flag is set

		call	read$name	; read next file name

		lda	EOF$flag	; unexpected end of file ?
		ora	a
		jnz	EOF$error	; rapport error

		lda	name$buffer	; name buffer empty (=empty rest of line) ?
		ora	a
		jz	test$condition	; test & execute make line

		call	parse$name	; parse file name into RSX$FCB
		ora	a		; parse error ?
		jnz	format$error	; yes: bad formatted make file

		lda	make$flag	; nesessary to test file ?
		ora	a
		jnz	depend$list	

		call	search$first	; try to find this file
		cpi	0FFH		; file found ?
		jnz	test$date

		lda	RSX$FCB		; no: drive specified ?
		ora	a
		jnz	not$found	; ERROR: file not found, abort make

test$1:		lda	drive1		; first drive in search chain
		cpi	0FFH		; empty ?
		jz	test$2
		ora	a		; default drive ?
		jz	test$2
		sta	RSX$FCB		; drive ID into FCB
		call	search$first	; try to find this file
		cpi	0FFH		; file found ?
		jnz	test$date

test$2:		lda	drive2		; second drive in search chain
		cpi	0FFH		; empty ?
		jz	test$3
		ora	a		; default drive ?
		jz	test$3
		sta	RSX$FCB		; drive ID into FCB
		call	search$first	; try to find this file
		cpi	0FFH		; file found ?
		jnz	test$date

test$3:		lda	drive3		; third drive in search chain
		cpi	0FFH		; empty ?
		jz	test$4
		ora	a		; default drive ?
		jz	test$4
		sta	RSX$FCB		; drive ID into FCB
		call	search$first	; try to find this file
		cpi	0FFH		; file found ?
		jnz	test$date

test$4:		lda	drive4		; fourth drive in search chain
		cpi	0FFH		; empty ?
		jz	not$found
		ora	a		; default drive ?
		jz	not$found
		sta	RSX$FCB		; drive ID into FCB
		call	search$first	; try to find this file
		cpi	0FFH		; file found ?
		jz	not$found

test$date:	cpi	3		; directory without stamp ?
		jz	obj$older	; yes : set make flag

		call	get$date	; get date & time of dep. file
		mov	a,b		; stamp inactive ?
		ora	c
		jz	obj$older

		lxi	h,obj$date+1	; compare with date & time of object file
		mov	a,m		; obj.date - dep.date
		sub	b		; high byte first
		jc	obj$older
		jnz	depend$list
		dcx	h		; then low byte
		mov	a,m
		sub	c
		jc	obj$older	; than dep. list
		jnz	depend$list	; object younger than dep. : 

		inx	h		; obj.time - dep.time
		inx	h
		mov	a,m		; A = hour
		sub	d
		jc	obj$older	; than dep. list
		jnz	depend$list	; object younger than dep. : 

		inx	h
		mov	a,m		; A = minute
		sub	e
		jc	obj$older	; than dep. list
		jnz	depend$list	; object younger than dep. : 
obj$older:	call	set$makef	; set make flag
		jmp	depend$list	; continue with parsing

	; test make$flag and execute/skip make command line

test$condition:	lda	make$flag	; is make$flag set ?
		ora	a
		jz	test$false

test$true:	lhld	rdcon$addr	; copy line into RDCON buffer
		mov	c,m		; max. count
		inx	h
		inx	h
		mvi	b,0

skip$loop:	push	b
		push	h
		call	getc		; into a
		pop	h
		pop	b
		cpi	CR		; end of line ?
		jz	term$line
		cpi	EOF
		jz	eof$line
		cpi	21H
		jc	skip$loop
		jmp	insert$char		

line$loop:	push	b
		push	h
		call	getc		; into a
		pop	h
		pop	b
		cpi	CR		; end of line ?
		jz	term$line
		cpi	EOF
		jz	eof$line

insert$char:	mov	m,a
		mvi	a,TRUE		; valid command line assumed
		sta	execute$flag
		inx	h
		inr	b
		dcr	c
		jnz	line$loop

term$line:	lhld	rdcon$addr	; store char count in buffer
		inx	h
		mov	m,b
		inx	h		; HL points to first buffer char

out$loop:	mov	e,m
		push	b
		push	h
		mvi	c,CONOUT
		call	next
		pop	h
		pop	b
		inx	h
		dcr	b
		jnz	out$loop

		mvi	c,CONOUT	; display cr
		mvi	e,CR
		call	next

		call	restore$dma
		ret

eof$line:	call	set$remove	; this is the last call
		jmp	term$line

test$false:	call	getc		; get char in A
		cpi	EOF
		jz	make$end
		cpi	CR
		jnz	test$false
		jmp	test$next	; try next line of make file

	; normal end of make file
	
make$end:	lda	execute$flag	; is at least one line executed ?
		ora	a
		cz	disp$done
		call	restore$dma
		call	set$remove
		jmp	origin		; original CCP RDCON call

	; restore user dma address

restore$dma:	mvi	c,SETDMA	; set dma address to old value
		lhld	user$dma
		xchg
		call	next
		ret

	; set remove flag in RSX
	
set$remove:	mvi	a,TRUE
		sta	remove
		ret

	; one character from <A> back into make file

ungetc:		sta	char$store	; save char
		mvi	a,TRUE		; set flag
		sta	char$flag
		ret

	; read a character from make file into <A>

getc:		lda	char$flag	; waiting char from ungetc ?
		ora	a
		jnz	getc$store	; yes : read char from storage

		lda	make$index	; index into record buffer
		ora	a		; exhausted
		jm	getc$more
		mov	e,a		; -> DE
		mvi	d,0
		lxi	h,MAKE$DMA
		dad	d		; HL points into make dma buffer
		inr	a		; update index
		sta	make$index
		mov	a,m		; char into <A>
		cpi	EOF		; last char ?
		rnz

getc$eof:	mvi	c,SETUSR	; set user number of make file
		lda	make$user
		mov	e,a
		call	next
		mvi	c,CLOSEF	; close make file
		lxi	d,MAKE$FCB
		call	next
		mvi	c,SETUSR	; restore default user number
		lda	default$user
		mov	e,a
		call	next
		mvi	a,EOF		; return EOF to caller
		ret

getc$more:	mvi	c,SETDMA	; set DMA address to makefile buffer
		lxi	d,MAKE$DMA
		call	next
		
		mvi	c,GETSCB	; get active multi-sector-count
		lxi	d,scb$multio
		call	next
		sta	multio

		mvi	c,SETMUL	; reset multi-sector count
		mvi	e,1
		call	next

		mvi	c,SETUSR	; set user number of make file
		lda	make$user
		mov	e,a
		call	next

		mvi	c,READSQ	; read next sector of make file
		lxi	d,MAKE$FCB
		call	next
		push	psw

		mvi	c,SETUSR	; restore default user number
		lda	default$user
		mov	e,a
		call	next

		mvi	c,SETMUL
		lda	multio
		mov	e,a
		call	next
		pop	psw

		ora	a		; end of file ?
		jnz	getc$eof

		sta	make$index	; reset make file index
		jmp	getc		; try once again

getc$store:	mvi	a,FALSE		; clear storage flag
		sta	char$flag
		lda	char$store
		ret

	; abort MAKE:

abort$make:	mvi	c,PRINTS	; send error msg
		lxi	d,abort$msg
		call	next

abort$close:	call	getc$eof	; close make file

abort:		call	set$remove
		mvi	c,WBOOT
		jmp	next

EOF$error:	mvi	c,PRINTS	; send error msg
		lxi	d,EOF$msg
		call	next
		jmp	abort		

disp$done:	mvi	c,PRINTS
		lxi	d,done$msg
		jmp	next

format$error:	mvi	c,PRINTS
		lxi	d,format$msg
		call	next
		jmp	abort$close

not$found:	mvi	c,PRINTS
		lxi	d,found$msg
		call	next
		jmp	abort$close

	; read a sequence of characters from make file

read$name:	lxi	h,name$buffer	; let HL point into (empty) name buffer
		mvi	b,29		; number of free chars in buffer

skip$lead$sp:	push	b		; save B, HL
		push	h
		call	getc		; from make file into A
		pop	h
		pop	b
		cpi	EOF		; end of file ?
		jz	read$name$EOF
		cpi	CR		; end of line ?
		jz	read$name$EOL
		cpi	21H		; space or other control char ?
		jc	skip$lead$sp
		cpi	'\'		; line extender ?
		jz	skip$EOL	
		cpi	';'		; comment-line ?
		jz	skip$EOL

store$char:	mov	m,a		; store into buffer
		inx	h
		dcr	b		; free chars -1
		jz	format$error

next$char:	push	b
		push	h
		call	getc		; read subsequent chars
		pop	h
		pop	b
		cpi	EOF		; end of file ?
		jz	read$name$EOF
		cpi	CR		; end of line ?
		jz	read$name$EOL
		cpi	TAB		; tabulator ?
		jz	read$name$end
		cpi	20H		; space ?
		jz	read$name$end
		jc	next$char	; skip control characters
		cpi	'\'
		jz	skip$EOL
		jmp	store$char	; else store character into buffer

read$name$EOF:	mvi	a,TRUE		; set EOF-Flag
		sta	EOF$flag

read$name$EOL:	mvi	a,TRUE		; set EOL-Flag
		sta	EOL$flag

read$name$end:	mvi	m,0		; append NUL delimiter
		ret

skip$EOL:	push	b		; save B, HL
		push	h
		call	getc		; from make file into A
		pop	h
		pop	b
		cpi	EOF		; end of file ?
		jz	read$name$EOF
		cpi	CR		; end of line ?
		jz	skip$lead$sp
		jmp	skip$EOL

	; parse a filename in name buffer into RSX$FCB

parse$name:	lxi	h,name$buffer	; leading user number ?
		mvi	b,0		; character count

search$colon:	mov	a,m		; colon ?
		cpi	':'
		jz	colon$found
		inx	h
		inr	b
		ora	a		; end of buffer ?
		jnz	search$colon

set$default:	lda	default$user	; file user number = actual user number
		sta	RSX$user
		lxi	h,name$buffer	; parse from beginning
		shld	pfcb
		jmp	parse$file

colon$found:	shld	pfcb		; start parsing at first non-digit char
		dcx	h		; test char before colon
		dcr	b
		jm	set$default	; nothing before colon
		mov	a,m		; digit ?
		sui	'0'
		jc	colon$found
		cpi	10
		jnc	colon$found
		mov	c,a		; store digit in c
		dcx	h		; test char before digit ?
		dcr	b
		jm	store$user	; nothing before this digit
		mov	a,m		; digit ?
		sui	'0'
		jc	parse$wrong	; no: format error
		cpi	10
		jnc	parse$wrong
		add	a		; A := A * 10
		mov	b,a
		add	a
		add	a
		add	b
		add	c		; + c
		mov	c,a

store$user:	lxi	h,RSX$user	; store user number of tested file
		mov	m,c
		
parse$file:	lhld	pfcb		; skip colon
		mov	a,m
		cpi	':'
		jnz	parse$it
		inx	h
		shld	pfcb

parse$it:	mvi	c,PARFCB	; parse filename
		lxi	d,pfcb		
		call	next
		mov	a,l		; MUST be end code (HL=0)
		ora	h
		ret

parse$wrong:	mvi	a,TRUE
		ret

	; search a file in RSX$FCB in directory

search$first:	lxi	h,RSX$FCB+1	; check to find ? in file name/type
		mvi	a,'?'
		mvi	b,11
check$loop:	cmp	m		; '?' ?
		jz	ambig$error	; is not allowed in any file name
		inx	h
		dcr	b
		jnz	check$loop

search$next:	mvi	c,SETDMA
		lxi	d,RSX$DMA
		call	next
		mvi	c,SETUSR
		lda	RSX$user
		mov	e,a
		call	next
		mvi	c,SFIRST	; try to find this file
		lxi	d,RSX$FCB
		call	next
		sta	dir$index
		mvi	c,SETUSR
		lda	default$user
		mov	e,a
		call	next
		lda	dir$index
		ret
		
ambig$error:	mvi	c,PRINTS
		lxi	d,ambig$msg
		call	next
		jmp	abort$close

	; get update date&time stamp out of directory record

get$date:	lda	dir$index	; MUST be 0..2
		add	a		; * 10
		mov	b,a
		add	a
		add	a
		add	b
		mov	e,a
		mvi	d,0
		lxi	h,RSX$DMA+101
		dad	d		; HL points to update stamp
		mov	c,m		; BC := date
		inx	h
		mov	b,m
		mov	a,c		; valid stamp ?
		ora	b
		cz	get$create	; no:try create stamp
		inx	h
		mov	d,m		; D := hour
		inx	h
		mov	e,m		; E := minute
;		call	out$date
		ret

get$create:	lxi	d,-5		; to begin of create subfield
		dad	d
		mov	c,m		; BC := date
		inx	h
		mov	b,m
		ret		

;out$date:	push	b
;		push	d
;		mvi	a,' '
;		call	out$c
;		pop	d
;		pop	b
;		push	b
;		push	d
;		mov	a,b
;		call	out$hex
;		pop	d
;		pop	b
;		push	b
;		push	d
;		mov	a,c
;		call	out$hex
;		mvi	a,' '
;		call	out$c
;		pop	d
;		pop	b
;		push	b
;		push	d
;		mov	a,d
;		call	out$hex
;		mvi	a,':'
;		call	out$c
;		pop	d
;		pop	b
;		push	b
;		push	d
;		mov	a,e
;		call	out$hex		
;		pop	d
;		pop	b
;		ret
;
;out$hex:	push	psw
;		rlc
;		rlc
;		rlc
;		rlc
;		call	out$nib
;		pop	psw
;
;out$nib:	ani	0Fh
;		adi	'0'
;		cpi	'9'+1
;		cnc	add$nib
;
;out$c:		mvi	c,CONOUT
;		mov	e,a
;		jmp	next
;
;add$nib:	adi	'A'-'9'-1
;		ret

	; set make flag

set$makef:	mvi	a,TRUE
		sta	make$flag
		sta	execute$flag
		ret

	; data area :

scb$multio:	db	4AH		; multi-sector count
		db	0
scb$drive12:	db	4CH		; get drive 1,2
		db	0
scb$drive34:	db	4EH		; get drive 3,4
		db	0
test$ccp:	db	18H		; offset of ccp running flag
		db	0
scb$dma:	db	3CH		; offset of current DMA address
		db	0
pfcb:		dw	name$buffer	; address of name buffer
		dw	RSX$FCB		; address of directory FCB
make$index:	db	128		; index into make file record
make$flag:	db	FALSE		; is TRUE if make condition satisfied
execute$flag:	db	FALSE		; set if at least one make line executed
char$flag:	db	FALSE		; set if character waiting in char$store

abort$msg:	db	'MAKE aborted by unsuccessfull program return',13,10,'$'
ambig$msg:	db	'MAKE aborted by ambiguous file name',13,10,'$'
format$msg:	db	'MAKE aborted by bad format of make file',13,10,'$'
EOF$msg:	db	'MAKE aborted by wrong end of make file',13,10,'$'
found$msg:	db	'MAKE aborted because file not found',13,10,'$'
done$msg:	db	'DONE>$'

char$store:	ds	1		; waiting char for getc
rdcon$addr:	ds	2		; holds address of read console buffer
user$dma:	ds	2		; holds address of users dma buffer
name$buffer:	ds	30		; buffer for CP/M Plus filename
RSX$FCB:	ds	36		; FCB for directory search
RSX$DMA:	ds	128		; dma buffer for directory operations
MAKE$DMA:	ds	128		; dma buffer for make file
dir$index:	ds	1		; contains directory index from last SFIRST
obj$date:	ds	2		; date of object file
obj$time:	ds	2		; time of object file
EOF$flag:	ds	1		; TRUE if end of makefile reached
EOL$flag:	ds	1		; TRUE if end of line in makefile reached
drive1:		ds	1		; drive search chain
drive2:		ds	1
drive3:		ds	1
drive4:		ds	1
multio:		ds	1		; multi-sector-count
default$user:	ds	1		; default (ccp) user number
RSX$user:	ds	1		; user number of currently parsed file

		end
