;;; REMOVE DISK LOADING OF MORE

; This is an entirely new version of V, the front end for Commodore's More.
; The old version ran More as a Workbench process, even when V was run from
; CLI, so that More would always open its own window.  This version always
; runs More as a CLI process, even when run from Workbench.  The reason for
; the change is that this way V can open More's window, and you can specify
; which window V will use with the environment variable MOREWINDOW (the
; default is "CON:0/0/640/200/More"), and also More's "E" command, which makes
; it pass the current file to the program in the environment variable EDITOR,
; only works when More is a CLI process.  (Note that it does NOT work when
; you run More (or V) with no argument, and it asks for a file and you tell
; it.  There is a bug in More that makes it think the name of the file it's
; showing is the same as the command in ENV:EDITOR.  Also the E command
; is not available when More is displaying from its standard input.)  This
; version of V runs More as part of its own process, and does not use the
; Execute() function.  This makes it more efficient at a slight cost in size.
; It is written in assembly to make it as small as possible, to minimize disk
; loading time when it is run from workbench, because the workbench can't use
; a resident copy of V (yes, it's pure).  Since V can use a resident copy of
; More, the workbench doesn't have to load that.  If More is not resident V
; will look for C:More or SYS:Utilities/More.  Maybe I should have an
; environment variable for that too?

; V is by Paul Kienitz and is in the public domain.  I assembled it with the
; Aztec 5.0 assembler, but it should be compatible with other Amiga
; assemblers.  Requires no link libraries.  Use Aztec's -n one pass option.
; Last update 12/24/90.


		include		"exec/types.i"
		include		"exec/memory.i"
		include		"exec/ports.i"
		include		"workbench/startup.i"
		include		"libraries/dosextens.i"


fdef:		macro
_LVO\1		  equ		-\2
		endm

; exec funcs:
		fdef		Forbid,132
		fdef		AllocMem,198
		fdef		FreeMem,210
		fdef		FindTask,294
		fdef		GetMsg,372
		fdef		ReplyMsg,378
		fdef		WaitPort,384
		fdef		CloseLibrary,414
		fdef		OpenLibrary,552

; DOS funcs:
		fdef		Open,30
		fdef		Close,36
		fdef		Read,42
		fdef		Write,48
		fdef		LoadSeg,150
		fdef		UnLoadSeg,156
		fdef		Delay,198

; I found a mistake in Aztec's c.lib in version 5.0d:  all the dos.library
; offsets (_LVO...) are accidentally defined as positive numbers!  oooooops
; ... the mistake did not exist in 3.6a ... oh well, we make our own _LVO's.


callib:		macro
		  jsr		_LVO\1(a6)
		endm

callx:		macro
		  move.l	4,a6
		  callib	\1
		endm

calld:		macro
		  move.l	dosbase,a6
		  callib	\1
		endm


; a node on the resident program list:

		STRUCTURE	theResidents,0
		  BPTR		next
		  LONG		usecount
		  BPTR		seglist
		  UBYTE		namelength
		  BYTE		name		; first char of var-length array


; local vars on the main program stack frame:

		STRUCTURE	mainargs,0
		  STRUCT	newa,300	; char [300]
		  CPTR		wbm		; workbench startup message
		  LONG		ret		; our program return code
		  CPTR		cli		; struct CommandLineInterface
		  LABEL		main_sizeof

winf		equr		d5		; window filehandle
mos		equr		d6		; seglist of More if from disk
alen		equr		d7		; length of cmd argument line
aptr		equr		a2		; ptr to cmd argument line
me		equr		a3		; this process
rezzy		equr		a4		; struct theResidents
; rezzy is also temporarily used to point to our process's MsgPort
dosbase		equr		a5		; dos.library base

; note: we follow a convention that you can trash regs d0-d4/a0/a1/a6.


; for debugging:
		xdef		BEGIN,Start,dosname,morename,umorename,exit
		xdef		ewinname,defaultwin,loaderrmsg,warnmsg
		xdef		winerrmsg,makeCLI,doQuotes,runit,unLoad
		xdef		GO_FER_IT,FindResiProg,CBsame,OpenWin,Mash
		xdef		cmorename,emptyarg,MeasureAptr,getMore,notWB


BEGIN:		bra		Start		;;; ENTRY POINT


dosname:	dc.b		"dos.library",0
morename:	dc.b		"More",0
umorename:	dc.b		"SYS:Utilities/More",0
cmorename:	dc.b		"C:More",0
ewinname:	dc.b		"ENV:MOREWINDOW",0
defaultwin:	dc.b		"CON:0/0/640/200/More",0
emptyarg:	dc.b		0
loaderrmsg:	dc.b		"Can't find More in resident list, "
		dc.b		   "SYS:Utilities, or C:.",10,0
loaderrlength	equ		56
warnmsg:	dc.b		10,"*** Remember, DO NOT use the 'E' "
		dc.b		   "command this time!",10,0
warnlength	equ		54
winerrmsg:	dc.b		"Couldn't open window!",10,0
winerrlength	equ		22


; ===================  here we go!

Start:		lea		-main_sizeof(sp),sp
		move.l		d0,alen
		move.l		a0,aptr
		lea		dosname,a1
		moveq		#0,d0
		callx		OpenLibrary
		move.l		d0,dosbase

		clr.l		ret(sp)
		clr.l		wbm(sp)
		sub.l		a1,a1
		callx		FindTask
		move.l		d0,me
		add.l		#pr_MsgPort,d0
		move.l		d0,rezzy

		tst.l		pr_CLI(me)
		bne		notWB
		  move.l	rezzy,a0
		  callx		WaitPort
		  move.l	rezzy,a0
		  callib	GetMsg
		  move.l	d0,wbm(sp)

notWB:		bsr		OpenWin
		move.l		d0,winf
		lea		morename,a1
		bsr		FindResiProg
		move.l		d0,rezzy

		tst.l		pr_CLI(me)
		beq		makeCLI
		  move.l	pr_CLI(me),d0
		  asl.l		#2,d0
		  move.l	d0,cli(sp)
		bra		doQuotes

makeCLI:	  tst.l		winf
		  beq		ret10
		  move.l	#cli_SIZEOF,d0
		  move.l	#MEMF_PUBLIC|MEMF_CLEAR,d1
		  callx		AllocMem
		  move.l	d0,cli(sp)
		  beq		ret10

		    move.l	d0,a0
		    move.l	#1000,cli_DefaultStack(a0)
		    asr.l	#2,d0
		    move.l	d0,pr_CLI(me)
		    move.l	winf,pr_COS(me)

		    lea		emptyarg,aptr
		    move.l	wbm(sp),a0
		    cmp.l	#1,sm_NumArgs(a0)
		    ble		emptyWBarg
		      move.l	sm_ArgList(a0),a0
		      addq	#wa_SIZEOF,a0
		      move.l	wa_Lock(a0),pr_CurrentDir(me)
		      move.l	wa_Name(a0),aptr
emptyWBarg:	    bsr		MeasureAptr
		  bra		doQuotes
ret10:		    move.l	#10,ret(sp)
		  bra		doQuotes

doQuotes:	lea		newa(sp),a0
		bsr		Mash
		lea		newa(sp),aptr
		bsr		MeasureAptr

		tst.l		winf
		beq		getMore
		tst.l		alen
		bne		getMore
		move.l		pr_CIS(me),d0
		move.l		cli(sp),a0
		cmp.l		cli_StandardInput(a0),d0
		bne		getMore
		  move.l	winf,d1
		  lea		warnmsg,a0
		  move.l	a0,d2
		  move.l	#warnlength,d3
		  calld		Write

getMore:	move.l		rezzy,d0		; tst.l rezzy
		bne		bumpCount
		  lea		umorename,a0		; load More
		  move.l	a0,d1
		  calld		LoadSeg
		  move.l	d0,mos
		  bne		runit
		    lea		cmorename,a0
		    move.l	a0,d1
		    callib	LoadSeg
		    move.l	d0,mos
		    bne		runit
		      tst.l	pr_COS(me)		; Can't load it!
		      beq	fail10
			moveq	#loaderrlength,d3
			lea	loaderrmsg,a0
			move.l	a0,d2
			move.l	pr_COS(me),d1
			callib	Write
fail10:		      move.l	#10,ret(sp)
		      bra	runit
bumpCount:	  move.l	seglist(rezzy),mos
		  tst.l		usecount(rezzy)
		  ble		runit			; >= 1 means addable
		    addq.l	#1,usecount(rezzy)	; bump use count

runit:		tst.l		winf
		beq		winErr
		tst.l		ret(sp)
		bne		winErr
		  move.l	cli(sp),a0
		  bsr		GO_FER_IT
		  move.l	d0,ret(sp)
		bra		releaseIt
winErr:		  tst.l		pr_COS(me)
		  beq		nouh
		    move.l	pr_COS(me),d1
		    lea		winerrmsg,a0
		    move.l	a0,d2
		    move.l	#winerrlength,d3
		    calld	Write
nouh:		  move.l	#10,ret(sp)

releaseIt:	move.l		rezzy,d0		; tst.l rezzy
		beq		unLoad
		  cmp.l		#1,usecount(rezzy)
		  ble		flee
		    subq.l	#1,usecount(rezzy)
		bra		flee
unLoad:		  move.l	mos,d1
		  calld		UnLoadSeg

flee:		tst.l		wbm(sp)
		beq		exit
		  tst.l		cli(sp)
		  beq		noCLIf
		    move.l	cli(sp),a1
		    move.l	#cli_SIZEOF,d0
		    callx	FreeMem
noCLIf:		  callx		Forbid
		  move.l	wbm(sp),a1
		  callib	ReplyMsg

exit:		move.l		ret(sp),d0
		lea		main_sizeof(sp),sp
		rts



; ====== this function actually runs More.  It returns More's return code.
; a0 contains pointer to our struct CommandLineInterface.

savregs		reg	a2-a6/d2-d7

GO_FER_IT:	move.l		pr_CIS(me),-(sp)
		move.l		pr_COS(me),-(sp)
		move.l		cli_StandardInput(a0),-(sp)
		move.l		pr_ConsoleTask(me),-(sp)
		move.l		a0,-(sp)
		move.l		winf,pr_COS(me)
		move.l		winf,d0
		asl.l		#2,d0
		move.l		d0,a1
		move.l		fh_Type(a1),pr_ConsoleTask(me)
		move.l		cli_StandardInput(a0),d0
		cmp.l		pr_CIS(me),d0
		bne		piped
		  move.l	winf,cli_StandardInput(a0)
		  move.l	winf,pr_CIS(me)

piped:		movem.l		savregs,-(sp)
		lea		pr_ReturnAddr(me),a5
		move.l		(a5),-(sp)		; old pr_ReturnAddr value
		move.l		a5,-(sp)		; pr_ReturnAddr address
		pea		3000			; stack size for More
		move.l		sp,(a5)			; new pr_ReturnAddr
		move.l		mos,d1			; seglist
		asl.l		#2,d1
		move.l		d1,a3
		move.l		alen,d0
		move.l		aptr,a0

		jsr		4(a3)			; More's entry point!

		addq		#4,sp			; pop stack size
		move.l		(sp)+,a5		; get pr_ReturnAddr ptr
		move.l		(sp)+,(a5)		; restore old value
		movem.l		(sp)+,savregs

		move.l		d0,d4			; More's return code
		beq		jesfine
		  move.l	#75,d1
		  calld		Delay
jesfine:	move.l		winf,d1
		calld		Close
		move.l		(sp)+,a0
		move.l		(sp)+,pr_ConsoleTask(me)
		move.l		(sp)+,cli_StandardInput(a0)
		move.l		(sp)+,pr_COS(me)
		move.l		(sp)+,pr_CIS(me)
		move.l		d4,d0
		rts


; ====== this procedure sets alen to be the strlen of aptr.

MeasureAptr:	move.l		aptr,a0
nextaptr:	tst.b		(a0)+
		bne.s		nextaptr
		sub.l		aptr,a0
		subq.l		#1,a0
		move.l		a0,alen
		rts


; ====== this procedure re-makes the argline with quotes around it.
; a0 points to buffer space for new line.

Mash:		move.l		aptr,a1
		add.l		alen,a1
		subq.l		#1,a1
trim:		  cmp.l		aptr,a1
		  blt		trimDone
		  cmp.b		#' ',(a1)
		  bhi		trimDone	; unsigned char compare
		    subq	#1,a1
		  bra		trim
trimDone:	sub.l		aptr,a1
		addq.l		#1,a1
		move.l		a1,alen
		beq		nuthin
		  move.b	#'"',(a0)+
		  move.l	aptr,a1
		  move.l	alen,d0
tram:		    beq		nuthin
		    cmp.b	#'*',(a0)
		    beq		starit
		    cmp.b	#'"',(a0)
		    bne		nostar
starit:		      move.b	#'*',(a0)+
nostar:		    move.b	(a1)+,(a0)+
		    subq	#1,d0
		    bra		tram
nuthin:		clr.b		(a0)
		rts



; ====== this function opens the window and returns the file handle.

bufsize		equ		100

OpenWin:	lea		-bufsize(sp),sp
		move.l		pr_WindowPtr(me),d4
		move.l		#-1,pr_WindowPtr(me)
		move.l		#MODE_OLDFILE,d2
		lea		ewinname,a0
		move.l		a0,d1
		calld		Open
		move.l		d4,pr_WindowPtr(me)
		tst.l		d0
		beq		default
		  move.l	d0,d4		; read environment var value
		  move.l	d4,d1
		  move.l	sp,d2		; result goes on stack
		  move.l	#bufsize,d3
		  callib	Read
		  move.l	d0,-(sp)
		  move.l	d4,d1
		  callib	Close
		  move.l	(sp)+,d0
		  ble		default
		  cmp.l		#bufsize,d0	; value too long for buffer?
		  bge		default
		    move.l	sp,a0
		    clr.b	0(a0,d0)	; make it nul terminated
		  bra		openIt
default:	lea		defaultwin,a0
openIt:		move.l		a0,d1
		move.l		#MODE_OLDFILE,d2
		callib		Open
		lea		bufsize(sp),sp
		rts


; ====== this function tests whether a resident node has a given name.
; a0 = (struct theResidents *), a1 = (char *), returns a zero byte if the
; name of the resident node is the same as the string, ignoring case.

CBsame:		move.b		namelength(a0),d2
		lea		name(a0),a0
loupe:		  move.b	(a1)+,d0		; toupper(string[x])
		  cmp.b		#'a',d0
		  blt		notLow1
		    cmp.b	#'z',d0
		    bgt		notLow1
		      sub.b	#'a'-'A',d0
notLow1:	  move.b	(a0)+,d1		; toupper(name[x])
		  cmp.b		#'a',d1
		  blt		notLow2
		    cmp.b	#'z',d1
		    bgt		notLow2
		      sub.b	#'a'-'A',d1
notLow2:	  cmp.b		d0,d1
		  beq		continu
		    moveq	#-1,d0			; names are different
		    rts
continu:	  subq.b	#1,d2
		  beq		dunne
		    tst.b	(a1)
		    bne		loupe
dunne:		move.b		(a1),d0
		or.b		d2,d0			; do lengths match?
		rts


; ====== this function returns the resident node of More, if it exists.
; a1 points to name, return value is pointer to struct theResidents.

FindResiProg:	move.l		a1,d3
		move.l		dosbase,a0
		move.l		dl_Root(a0),a0
		move.l		rn_Info(a0),d0
		asl.l		#2,d0
		move.l		d0,a0
		move.l		di_NetHand(a0),d0
		asl.l		#2,d0
		move.l		d0,a0		; the resident segment list
ooop:		  move.l	a0,d4
		  bsr		CBsame
		  move.l	d4,a0
		  move.l	d3,a1
		  tst.b		d0
		  bne		agin
		    move.l	a0,d0		; found it!
		    rts
agin:		  move.l	next(a0),d0
		  asl.l		#2,d0
		  move.l	d0,a0
		tst.l		d0
		bne		ooop
		rts				; didn't find it, return 0

