.mcall	.module
.module	MENU,release=X01,version=12,comment=<Menu Manager>,audit=NO
;+
;			Copyright (c) 1985
;			   C. E. Chew
;
; This software is furnished under a license and may be used and  copied
; only  in  accordance  with  the  terms  of  such  license and with the
; inclusion of the above copyright notice.  This software or  any  other
; copies  thereof may not be provided or otherwise made available to any
; other person.  No title to and ownership of  the  software  is  hereby
; transferred.
;
; The information in this software is subject to change  without  notice
; and should not be construed as a commitment by the author.
;
; The author assumes no responsibility for the use or reliability of his
; software.
;-
	.include 'key.mac'
	.sbttl	Definitions
	.dsabl	gbl
	.globl	$tapl $tcur $tclr $tmov $tmod $tchr
	.mcall	.br .assume .gtjb .print .ttyout .spfun .peek
	.mcall	.rctrlo .twait .dstatu .fetch .lookup
;
jsw	=: 44
	ttspc$	=: 10000
	ttlc$	=: 40000
i.state	=: 0
	csirn$	=: 4
i.blok	=: 36
	ttiwt$	=: 40000
	ttowt$	=: 20000
	norun$	=: 1000
cichan	=: 16				;channel for CI operations
	swt$fn	=: 377			;switch terminal context
	cmd$fn	=: 376			;pass command
	get$fn	=: 375			;get context of console
;
	.psect	menu,ro
	.psect	data,d
	.psect	text,d
;
.macro	section name
	.sbttl	name
	.psect	menu
.endm	section
;
	.psect	data
area:	.blkw	6			;emt area for everyone
	section	Initialisation
;+
; The menu routines must be initialised before any operations are performed.
; Thus the main program must enter here to begin with.
;
; INPUTS:
;	sp ->	return
;		.word location
;		.word result
;
; location indicates where any handlers can be loaded.
;
; OUTPUTS:
;	sp ->	.word result
;
; result indicates the first free location above any loaded handlers, a
; result of zero indicates an error.
;
; EFFECTS:
;	Set up internal pointers and the like.
;-
	.enabl	lsb
$minit::
	mov	2(sp),r1		;where we can load
	mov	(sp)+,(sp)
	clr	2(sp)
;
	.gtjb	#area #jobblk ME	;who am I?
	bcs	20$
	mov	cntxt,mcntxt		;remember my identity
	.dstatu	#area #ciname
	bcs	20$			;can't find it
	tst	area+4
	bne	10$			;already loaded
	.fetch	r1 #ciname
	bcs	20$			;what happened
	mov	r0,r1			;first free location
;
10$:	.lookup	#area #cichan #ciname
	bcs	20$			;strange
;
	.spfun	#area #cichan #swt$fn #mcntxt #0 #0 #0
	bcs	20$
	mov	r1,2(sp)
20$:	return
;
	.psect	data
ciname:	.rad50	'ci          '
mcntxt:	.blkw	1
jobblk:	.blkw	12.
cntxt	=: jobblk + 10
	section	<Console Context Switch>
;+
; Switches the context of the console.
;
; INPUTS:
;	sp ->	return
;		.word flush screen
;		.word pointer to job name
;		.word result
;
; The screen is flushed if the flush screen argument is non-zero.
;
; OUTPUTS:
;	sp ->	.word result
;
; result of zero means failure.
;
; EFFECTS:
;	Switches the context of the console to the job specified.
;-
	.enabl	lsb
$mswt::
	clr	6(sp)			;assume we're doomed
	.gtjb	#area #jobblk 4(sp)
	bcs	99$
;
	tst	2(sp)
	beq	5$
	call	$tclr			;flush the screen
	clr	-(sp)
	call	$tmov
5$:	.spfun	#area #cichan #swt$fn #cntxt #0 #0 #0
	bcs	99$
	inc	6(sp)
;
99$:	mov	(sp)+,(sp)
	mov	(sp)+,(sp)
	return
	section	<Command Dispatcher>
;+
; Send a command to the console keyboard interrupt processor.
;
; INPUTS:
;	sp ->	return
;		.word pointer to command string
;		.word result
;
; command string must lie on a word boundary.
;
; OUTPUTS:
;	sp ->	.word result
;
; result of zero means failure. Command string must lie on an even address.
;
; EFFECTS:
;	Pretends that the string has been typed at the terminal.
;-
	.enabl	lsb
$mcmd::
	clr	4(sp)			;assume the worst
	.spfun	#area #cichan #cmd$fn 2(sp) #0 #0 #0
	bcs	10$
	inc	4(sp)
10$:	mov	(sp)+,(sp)
	return
	section	<Job Wait>
;+
; Waits for the specified job to enter a keyboard wait state.
;
; INPUTS:
;	sp ->	return
;		.word pointer to job name
;		.word result
;
; if bit zero is set on the pointer to the job name, then the routine will
; wait for the program to die.
;
; OUTPUTS:
;	sp ->	.word result
;
; result of zero means failure.
;
; EFFECT:
; This routine sits in a watchdog state until the job enters a csi kb wait
; state or dies. Background jobs are assumed to run from kmon and the
; routine will wait for a kb wait state (without csi). The alternative
; entry point $mjobt will cause the routine to loop until the job enters a
; kb wait state.
;-
	.enabl	lsb
$mjobt::
	mov	r2,-(sp)
	clr	6(sp)			;assume the worst
	mov	#1,die			;set wait for kb state flag
	br	10$
$mjob::
	mov	r2,-(sp)
	clr	6(sp)			;assume the worst
	clr	die			;clear flag
	ror	4(sp)			;get bit zero
	ror	die			;set flag
	asl	4(sp)
;
10$:	.gtjb	#area #jobblk 4(sp)
	bcs	60$
	mov	cntxt,jblock		;pointer to blocking bits
	add	#i.blok,jblock
	br	30$
;
20$:	.twait	#area #awhile
.assume	i.state eq 0
;
30$:	.peek	#area jblock
	bit	#norun$,r0
	bne	50$
;
40$:	tst	die
	blt	20$			;must wait for job to die
	bit	#ttiwt$,r0
	beq	20$			;not waiting for input
	tst	jobblk
	beq	50$			;background job probably kmon
	tst	die
	bgt	50$			;loop only until kb wait
	.peek	#area cntxt		;see if we're ready
	bit	#csirn$,r0
	beq	20$			;csi not running
;
50$:	inc	6(sp)			;we're right
;
60$:	mov	(sp)+,r2
	mov	(sp)+,(sp)
	return
;
	.psect	data
awhile:	.word	0, 30.			;watch dog
die:	.word	0			;wait for job to terminate flag
jblock:	.blkw	1
xcntxt:	.blkw	2
	section	<Switch Console Back to Me>
;+
; Switches the context of the console back to us.
;
; INPUTS:
;	None.
;
; OUTPUTS:
;	None.
;
; EFFECTS:
;	As above.
;-
	.enabl	lsb
$mme::
	.spfun	#area #cichan #swt$fn #mcntxt #0 #0 #0
	return
	section	<Menu Display>
;+
; Displays a menu on the console and accepts the user's input. The
; user is allowed to select an option from the menu and the option
; selected will be indicated to the caller.
;
; INPUTS:
;	sp ->	return
;		pointer to menu description text
;		pointer to option descriptor
;		initial option to select
;		result
;
; The menu description text will be a .byte 0 terminated string.
;
; The option descriptor consists of a list of elements in the following
; format:
;		byte indicating row on screen for display
;		byte indicating column on screen for display
;		pointer to options description text
;		pointer to help text
;
; The list is terminated by a single zero word and the text will be
; .byte 0 terminated.
;
; If the initial option to select is zero, then no clear screen is operation
; is performed.
;
; OUTPUTS:
;	sp ->	the option chosen (1..n)
;
; EFFECTS:
;	The user will be permitted to choose the desired option. enter
; must be typed to continue from a help description.
;-
	.enabl	lsb
$menu::
	mov	sp,r0
	mov	r2,-(sp)
	mov	(r0),r1			;return address
	mov	r3,(r0)+
	mov	(r0),r3			;menu description
	mov	r4,(r0)+
	mov	(r0),r4			;option list
	mov	r5,(r0)+
	mov	(r0),r5			;initial option
	mov	r1,(r0)
	mov	@#jsw,-(sp)
	bis	#<ttspc$!ttlc$>,@#jsw
	.rctrlo
;
	mov	r0,-(sp)		;non-zero
	call	$tapl			;set up terminal for application mode
	clr	-(sp)
	call	$tcur			;no cursor
	tst	r5
	bgt	5$
	neg	r5
	br	6$			;don't refresh display
;
5$:	call	$tclr			;make some room
	clr	-(sp)			;home the cursor
	call	$tmov
	.print	r3			;title
;
6$:	mov	r4,-(sp)
	mov	#-1,r2
;
10$:	mov	(r4)+,-(sp)
	beq	20$			;that's all folks
	inc	r2			;another one
	call	$tmov
	.print	(r4)+			;show them what's what
	tst	(r4)+
	br	10$
;
20$:	tst	(sp)+
	mov	(sp),r4
	mov	r2,(sp)
	dec	r5			;make an offset
;
30$:	mov	r5,r1			;see what's required
	mul	#6,r1
	mov	r1,r2
	add	r4,r2			;I have you now
	mov	#m.bold,-(sp)
	call	$tmod			;inverse mode
	mov	(r2)+,-(sp)
	mov	(sp),-(sp)
	call	$tmov
	.print	(r2)+			;highlight
	mov	#m.norm,-(sp)
	call	$tmod
	call	$tmov			;leave cursor at start
;
40$:	clr	-(sp)
	call	$tchr			;see what the user wants
	mov	(sp)+,r0
	cmp	r0,#c.help
	beq	60$
	cmp	r0,#c.do
	beq	110$
	cmp	r0,#c.up
	beq	80$
	cmp	r0,#c.down
	beq	100$
;
50$:	.ttyou	#bel			;none of the above
	br	40$
;
60$:	call	$tclr			;give me room
	clr	(sp)			;will be resurrected later
	call	$tmov
	.print	(r2)			;show them
	inc	r5			;will be decremented later
70$:	clr	-(sp)
	call	$tchr			;enter or return to continue
	mov	(sp)+,r0
	cmp	r0,#c.enter
	beq	5$
	cmp	r0,#lf
	beq	5$
	.ttyou	#bel
	br	70$
;
80$:	tst	r5
	beq	50$			;already at top
	dec	r5			;back up an option
;
90$:	sub	#4,r2
	mov	(r2)+,-(sp)		;rewrite
	call	$tmov
	.print	(r2)
	br	30$
;
100$:	cmp	r5,(sp)
	beq	50$			;already at bottom
	inc	r5
	br	90$
;
110$:	inc	r5
	mov	r5,14.(sp)		;this is it
	mov	pc,(sp)
	call	$tcur			;cursor on
	clr	-(sp)
	call	$tapl			;normal keys
	mov	(sp)+,@#jsw
	.rctrlo
	mov	(sp)+,r2
	mov	(sp)+,r3
	mov	(sp)+,r4
	mov	(sp)+,r5
	return
	section	<Filename Input>
;+
; Accepts a filename into a string.
;
; INPUTS:
;	sp ->	return
;		.word pointer to string
;		.byte row of input
;		.byte column of input
;
; Room should be allocated for 7 bytes (six characters plus a null).
;
; OUTPUTS:
;	None.
;
; EFFECTS:
;	An uppercase filename will be accepted into the string. The
; filename will be null terminated.
;-
	.enabl	lsb
$mfile::
	mov	sp,r0
	mov	r3,-(sp)
	mov	r2,-(sp)
	mov	(r0),r3
	mov	r4,(r0)+
	mov	(r0),r4			;pointer to string
	mov	r5,(r0)+
	mov	(r0),r5			;screen location
	mov	r3,(r0)
;
	mov	#m.under,-(sp)
	call	$tmod			;underline mode
	mov	r5,-(sp)		;show user where it goes
	call	$tmov
	.print	#80$
	mov	r5,-(sp)
	call	$tmov
	mov	r4,r3			;set up
	mov	r4,r5			;and mark the end
	add	#6,r5
;
10$:	clr	-(sp)
	call	$tchr			;see what's up
	mov	(sp)+,r0
	cmp	r0,#c.enter		;enter terminates
	beq	70$
	cmp	r0,#lf			;so does carriage return
	beq	70$
	cmp	r0,#del			;delete
	beq	60$
;
	cmp	r3,r5
	beq	30$			;no more room
;
	mov	r0,r2
	sub	#'0,r2
	cmp	r2,#9.
	blos	50$			;numbers are ok
;
	mov	r0,r2
	sub	#'A,r2
	cmp	r2,#'Z-'A
	blos	50$			;upper case is ok
;
	sub	#'a-'A,r2
	cmp	r2,#'z-'a
	blos	40$			;lower case is ok
30$:	.ttyou	#bel
	br	10$
;
40$:	sub	#'a-'A,r0		;convert to upper case
50$:	movb	r0,(r3)+		;remember it
	.ttyou
	br	10$
;
60$:	cmp	r3,r4
	beq	30$			;nothing to delete
	.print	#90$			;delete it from the display
	dec	r3
	br	10$
;
70$:	clrb	(r3)+			;mark the end
	cmp	r3,r5
	blos	70$
	mov	#m.norm,-(sp)		;back to normal
	call	$tmod
	mov	(sp)+,r2
	mov	(sp)+,r3
	mov	(sp)+,r4
	mov	(sp)+,r5
	return	
;
	.psect	text
80$:	.ascii	'      '<200>
90$:	.byte	10, 40, 10, 200
	section	<Octal Number Input>
;+
; Accepts a filename into a string.
;
; INPUTS:
;	sp ->	return
;		.word pointer to string
;		.byte row of input
;		.byte column of input
;
; Room should be allocated for 7 bytes (six characters plus a null).
;
; OUTPUTS:
;	None.
;
; EFFECTS:
;	An octal number will be accepted into the string. The number will be
; null terminated.
;-
	.enabl	lsb
$moct::
	mov	sp,r0
	mov	r3,-(sp)
	mov	r2,-(sp)
	mov	(r0),r3
	mov	r4,(r0)+
	mov	(r0),r4			;pointer to string
	mov	r5,(r0)+
	mov	(r0),r5			;screen location
	mov	r3,(r0)
;
	mov	#m.under,-(sp)
	call	$tmod			;underline mode
	mov	r5,-(sp)		;show user where it goes
	call	$tmov
	.print	#80$
	mov	r5,-(sp)
	call	$tmov
	mov	r4,r3			;set up
	mov	r4,r5			;and mark the end
	add	#6,r5
;
10$:	clr	-(sp)
	call	$tchr			;see what's up
	mov	(sp)+,r0
	cmp	r0,#c.enter		;enter terminates
	beq	70$
	cmp	r0,#lf			;so does carriage return
	beq	70$
	cmp	r0,#del			;delete
	beq	60$
;
	cmp	r3,r5
	beq	30$			;no more room
;
	mov	r0,r2
	sub	#'0,r2
	cmp	r2,#7.
	blos	50$			;octal digits are ok
;
30$:	.ttyou	#bel
	br	10$
;
50$:	movb	r0,(r3)+		;remember it
	.ttyou
	br	10$
;
60$:	cmp	r3,r4
	beq	30$			;nothing to delete
	.print	#90$			;delete it from the display
	dec	r3
	br	10$
;
70$:	clrb	(r3)			;mark the end
	mov	#m.norm,-(sp)		;back to normal
	call	$tmod
	mov	(sp)+,r2
	mov	(sp)+,r3
	mov	(sp)+,r4
	mov	(sp)+,r5
	return	
;
	.psect	text
80$:	.ascii	'      '<200>
90$:	.byte	10, 40, 10, 200
	section	<Decimal Number Input>
;+
; Accepts a decimal number into a string.
;
; INPUTS:
;	sp ->	return
;		.word pointer to string
;		.byte row of input
;		.byte column of input
;
; Room should be allocated for 5 bytes (four characters plus a null).
;
; OUTPUTS:
;	None.
;
; EFFECTS:
;	A decimal number will be accepted into the string. The number will be
; null terminated.
;-
	.enabl	lsb
$mdec::
	mov	sp,r0
	mov	r3,-(sp)
	mov	r2,-(sp)
	mov	(r0),r3
	mov	r4,(r0)+
	mov	(r0),r4			;pointer to string
	mov	r5,(r0)+
	mov	(r0),r5			;screen location
	mov	r3,(r0)
;
	mov	#m.under,-(sp)
	call	$tmod			;underline mode
	mov	r5,-(sp)		;show user where it goes
	call	$tmov
	.print	#80$
	mov	r5,-(sp)
	call	$tmov
	mov	r4,r3			;set up
	mov	r4,r5			;and mark the end
	add	#4,r5
;
10$:	clr	-(sp)
	call	$tchr			;see what's up
	mov	(sp)+,r0
	cmp	r0,#c.enter		;enter terminates
	beq	70$
	cmp	r0,#lf			;so does carriage return
	beq	70$
	cmp	r0,#del			;delete
	beq	60$
;
	cmp	r3,r5
	beq	30$			;no more room
;
	mov	r0,r2
	sub	#'0,r2
	cmp	r2,#9.
	blos	50$			;decimal digits are ok
;
30$:	.ttyou	#bel
	br	10$
;
50$:	movb	r0,(r3)+		;remember it
	.ttyou
	br	10$
;
60$:	cmp	r3,r4
	beq	30$			;nothing to delete
	.print	#90$			;delete it from the display
	dec	r3
	br	10$
;
70$:	clrb	(r3)			;mark the end
	mov	#m.norm,-(sp)		;back to normal
	call	$tmod
	mov	(sp)+,r2
	mov	(sp)+,r3
	mov	(sp)+,r4
	mov	(sp)+,r5
	return	
;
	.psect	text
80$:	.ascii	'    '<200>
90$:	.byte	10, 40, 10, 200
	section	<Console Pause>
;+
; Waits for the user to be ready before continuing.
;
; INPUTS:
;	None.
;
; OUTPUTS:
;	None.
;
; EFFECTS:
;	Prints a sign on message the waits for the user indicate that
; he/she is ready.
;-
	.enabl	lsb
$mwait::
	.print	#signon
10$:	clr	-(sp)
	call	$tchr
	mov	(sp)+,r0
	cmp	r0,#c.enter
	beq	20$
	cmp	r0,#lf
	beq	20$
	.ttyou	#bel
	br	10$
20$:	return
;
	.psect	text
signon:	.ascii	<15>' '<15><12><12>'Press ENTER or RETURN to continue : '<200>
	section	<Console Pause with Escape>
;+
; Waits for the user to be ready before continuing or aborting.
;
; INPUTS:
;	None.
;
; OUTPUTS:
;	sp->	.word	result
;
; where a zero result means abort.
;
; EFFECTS:
;	Waits for the user indicate that he/she is ready. The keyboard
; will be placed into application mode and returned to normal before exiting.
;-
	.enabl	lsb
$mpaus::
	mov	sp,2(sp)		;assume she wants to proceed
	mov	(sp),-(sp)		;return address is non-zero
	call	$tapl			;keyboard into application mode
10$:	clr	-(sp)
	call	$tchr
	mov	(sp)+,r0
	cmp	r0,#c.enter
	beq	20$
	cmp	r0,#lf
	beq	20$
	clr	2(sp)			;abort
20$:	clr	-(sp)
	call	$tapl			;back to normal
	return
	.end
