Metropoli BBS
VIEWER: use.s MODE: TEXT (ASCII)
**********************************************************************
* Name:		USE
* Stack:	(   id --> list )		* Recursive *
*		( romp --> list )		* Recursive *
*		(  dir --> list )
*		(  ob --> list )
* Desc:		Subroutine usage analyzer
**********************************************************************
ASSEMBLE
	CON(1)	8
RPL
xNAME USE
::
  CK1
  ::
    DUPTYPEIDNT? case :: DUP@ TRUE ;
    DUPTYPEROMP? NOTcaseFALSE
    DUPROMPTR@ TRUE
  ;
  ( id ob TRUE TRUE / romp ob TRUE TRUE / ob FALSE )
  NOTcase
  ::
    GARBAGE				( * Make safe * )
    CODE
USEstart	GOSBVL	=SAVPTR
USErestart	GOSBVL	=ROOM		C[A] = room
		LA(5)	200		Safety requirement
		C=C-A	A
		GONC	+
		GOVLNG	=GPMEMERR
+		ASRB.F	A
		C=C+A	A		Allocate atleast 100 nibbles
		R1=C	A		R1[A] = free
		GOSBVL	=MAKE$N		* Is safe now *
		A=DAT1	A
		AD0EX			D0 = ->ob
		D1=A			D1 = ->output

* Dispatch based on object type

		A=DAT0	A		A[A] = obtype
		LC(5)	=DORRP
		?A=C	A
		GOYES	USErrp
		LC(5)	=DOLIB
		?A#C	A
		GOYES	+
		GOTO	USElib

* Scan a regular object

+		GOSUB	USEscan

* Exit scanner

USEexit		CD1EX			Remove trailing extra space
		RSTK=C
		D0=C
		GOSBVL	=Shrink$
		C=RSTK			Remove leading 10 nibbles due to $
		D0=C
		C=0	A
		LC(1)	10
		A=DAT0	A
		A=A-C	A
		DAT0=A	A
		A=R0
		A=A+CON	A,10
		GOSBVL	=MOVERSD	* IS SAFE *
		GOVLNG	=GPOverWrR0Lp	And push result

**********************************************************************
* Scan a directory object
**********************************************************************

USErrp		GOSUB	USEalloc{}
		AD0EX			A[A]=->rrp
		GOSBVL	=FNDLOPTR	D0 = ->lastram-word offset
		C=DAT0	A		C[A] = offset
		AD0EX
		A=A+C	A		A[A] = ->lastram-word
		?C=0	A
		GOYES	userrpdone	Empty rrp - done
		R4=A			R4[A] = ->last variable

userrploop	A=R4
		D0=A			D0 = ->variable
		GOSBVL	=TRAVERSE+	D0 = ->ob
		A=R4
		AD0EX			D0 = ->variable
		B=A	A		B[A] = ->ob
		C=0	A
		C=DAT0	B		C[A] = chars in name
		C=C+C	A		C[A] = nibbles in name
		C=C+CON	A,7		+ 7 for DOIDNT and length
		GOSUB	USEallocC	  allocate name
		LA(5)	=DOIDNT		Output prolog
		DAT1=A	A
		D1=D1+	5
		C=C-CON	A,5		And copy length & body
		GOSBVL	=MOVEDOWN
		C=B	A
		D0=C			D0 = ->ob
		GOSUB	USEscan		Scan usage
		GOSUB	USEfix		Change to NULL{} if got { }
		A=R4
		A=A-CON	A,5		A[A] = ->variable offset
		D0=A	
		C=DAT0	A		C[A] = variable offset
		A=A-C	A
		R4=A			R4[A] = ->variable'
		?C#0	A
		GOYES	userrploop	Continue until no more variables

userrpdone	GOSUB	USEoutsemi
		GOTO	USEexit

* Check if had no usage at all - if so then output NULL{} instead

USEfix		D1=D1-	10
		A=DAT1	A
		D1=D1+	10
		LC(5)	=DOLIST
		?C#A	A
		RTNYES
		D1=D1-	10
		LC(5)	=NULL{}
		DAT1=C	A
		D1=D1+	5
		C=R1
		C=C+CON	A,5		free = free + 5
		R1=C
		RTNCC



**********************************************************************
* Scan a library object - not implemented
**********************************************************************

USElib		LC(5)	=SYNTAXERR
		GOVLNG	=GETPTREVALC


**********************************************************************

* Allocate a list and output leading "{"

USEalloc{}	P=	5+5
		GOSUB	USEallocP
		LC(5)	=DOLIST
		DAT1=C	A
		D1=D1+	5
		RTN

* Output trailing "}"

USEoutsemi	LC(5)	=SEMI
		DAT1=C	A
		D1=D1+	5
		RTN

**********************************************************************
* Scan a single _regular_ object for usage of ID's & ROMPTR's
*
* Input:	D0 = ->ob		R1[A] = free
*		D1 = ->output
**********************************************************************

USEscan		CD1EX
		D1=C
		R2=C			R2[A] = ->output
		GOSUB	USEalloc{}	Allocate list & output DOLIST
		AD0EX
		B=A	A		B[A] = ->ob
		D0=A
		GOSBVL	=SKIPOB
		A=B	A
		AD0EX			D0 = ->ob
		B=A	A		A[A] = ->obend

USEloop		AD0EX			Check current address first
		D0=A
		?A>=B	A
		GOYES	USEoutsemi	Reached end - output semi & exit
		A=DAT0	A
		LC(5)	=DOCOL		#02D9D
		?C=A	A
		GOYES	usecomp
		LC(3)	=DOLIST		#02A74
		?C=A	A
		GOYES	usecomp
		LC(2)	=DOSYMB		#02AB8
		?C=A	A
		GOYES	usecomp
		LC(2)	=DOEXT		#02ADA
		?C=A	A
		GOYES	usecomp
		LC(3)	=DOIDNT		#02E48
		?C=A	A
		GOYES	useidromp
		LC(2)	=DOROMP		#02E92
		?C=A	A
		GOYES	useidromp
		LC(4)	=SEMI		#0312B
		?C=A	A
		GOYES	usecomp
usecont		GOSBVL	=SKIPOB
		GOTO	USEloop

* Found composite - just skip into it & continue

usecomp		D0=D0+	5
		GOTO	USEloop

* Found idnt/romp - add it to usage list

useidromp	C=R2.F	A		C[A] = ->usage
		CD0EX			D0 = ->usage
		CD1EX			D1 = ->ob
		D=C	A		D[A] = ->output
		D0=D0+	5		Skip prolog

-		CD0EX			* Search a match from usage list
		D0=C
		?C>=D	A
		GOYES	useadd		Reached end - add new usage
		GOSUB	USEcompare
		GOC	+		Match - no need to add
		GOSBVL	=SKIPOB
		GOTO	-

+		C=D	A		C[A] = ->output
		CD1EX			D1 = ->output
		D0=C			D0 = ->ob
		GOTO	usecont		And continue

* Add object to usage list

useadd		C=D	A		C[A] = ->output
		CD1EX			D1 = ->output
		D0=C			D0 = ->ob
		RSTK=C			RSTK = ->ob
		GOSBVL	=SKIPOB		D0 = ->obend
		C=RSTK			C[A] = ->ob
		AD0EX
		D0=C			D0 = ->ob
		C=A-C	A		C[A] = obsize
		GOSUB	USEallocC	Allocate it
		GOSBVL	=MOVEDOWN	And append to the list
		GOTO	USEloop		And continue until object is scanned

* Compare objects in D0 & D1

USEcompare	A=DAT0	7		Quick exit if first 7 nibbles don't
		C=A	W		match (7 = size of DOCHAR)
		C=DAT1	7
		?A=C	W
		GOYES	+		7 nibbles match - compare the rest
		RTNCC			CC: No match
+		C=B	A		Save B[A] in RSTK
		RSTK=C			
		CD0EX
		B=C	A		B[A] = ->ob0
		CD0EX
		GOSBVL	=SKIPOB		D0 = ->ob0end
		C=B	A
		RSTK=C			Save ->ob0 in RSTK
		CD0EX
		C=C-B	A		C[A] = ob0 size
		C=C-1	A
		GOC	usecmpno	HUH!
		P=C	0		P = nibbles to compare
		CSR	A
		B=C	A		B[A] = words to compare
		A=DAT0	WP
		C=DAT1	WP
		?C#A	WP
		GOYES	usecmpno	No match - recover & exit
		CD0EX
		C+P+1
		CD0EX
		CD1EX
		C+P+1
		CD1EX
		P=	0
-		B=B-1	A		Compare words until end/match
		GOC	usecmpyes	Match - recover & exit
		A=DAT0	W
		C=DAT1	W
		D0=D0+	16
		D1=D1+	16
		?C=A	W
		GOYES	-
usecmpno	P=	1		Indicate failure
usecmpyes	C=RSTK			Recalculate ->ob0 and ->ob1
		AD0EX
		D0=C
		A=A-C	A
		CD1EX
		C=C-A	A
		D1=C
		C=RSTK
		B=C	A
		?P=	0
		RTNYES			CS: Match
		P=	0
		RTNCC			CC: No match

* Memory allocation subroutines

USEallocP	C=0	A
		CPEX	0
USEallocC	AR1EX.F	A
		A=A-C	A
		AR1EX.F	A
		RTNNC
		GOVLNG	=GPMEMERR
    ENDCODE
  ;

  ( id ob TRUE / id FALSE / romp ob TRUE / romp FALSE )

  NOTcase SETNONEXTERR		( * No contents! * )

* Scan the object

  DUP GARBAGE
  CODE
	GOTO	USEstart
  ENDCODE				( id ob list )

* Avoid double recursion for lists

  SWAP TYPERRP? case SWAPDROP		( --> list )

* Recurse deeper			( id list )

  2DUP TWO{}N ROT ONE{}N ROT		( Luse Ldone Ltodo )

  BEGIN
	DUPNULLCOMP? NOT
  WHILE
	DUP TWO MINUSONE SUBCOMP
	SWAP CARCOMP			( Luse Ldone Ltodo' name )
	3PICKOVER EQUALPOSCOMP
	#0<> caseDROP			( --> Luse Ldone Ltodo' )
	ROTOVER >TCOMP UNROT		( Luse Ldone' Ltodo' name )
	::
	  DUPTYPEIDNT? case DUP@
	  DUPTYPEROMP? NOTcaseFALSE
	  DUPROMPTR@
	;
	NOTcaseDROP			( --> Luse Ldone' Ltodo' )
	GARBAGE
	CODE
		GOTO	USEstart
	ENDCODE				( Luse Ldone' Ltodo' name use )
	ROTOVER &COMP UNROT		( Luse Ldone' Ltodo'' name use )
	TWO{}N 4ROLLSWAP &COMP UNROT	( Luse' Ldone' Ltodo'' )
  REPEAT
  2DROP					( --> Luse )
;

**********************************************************************
[ RETURN TO DIRECTORY ]