Metropoli BBS
VIEWER: eqstk.s MODE: TEXT (ASCII)
**********************************************************************
*		EQUATION STACK
*
* ASTK	- Stack display replacement which shows symbolics
*	  in equation writer form.
* AGROB	- Related object decompiler
**********************************************************************

**********************************************************************
*		UFL DEFINITIONS
**********************************************************************

DEFINE xFNTSTR	"FNT"
DEFINE xFNT	ROMPTR 101 0
DEFINE Font	ROMPTR 101 1
DEFINE Font?	ROMPTR 101 2
DEFINE FNT1	ROMPTR 101 3
DEFINE FNT2	ROMPTR 101 4

**********************************************************************
*		CACHE DEFINITIONS
**********************************************************************

* Cache size
DEFINE CacheSize    TEN
* Twice cache size
DEFINE DoubleSize   TWENTY
* Total number of lambda variables
DEFINE TotalSize    TWENTYFIVE

ASSEMBLE
=CacheDummy	EQU 0	* Equate for zero cache
CACHEPOS	EQU 5	* Number of non-cache variables
CACHESIZE	EQU 10	* Cache size
CACHESTK	EQU 20	* Number of stack levels checked to keep ob in cache
RPL

* Don't forget to check GetCache uses the proper lambda name!

**********************************************************************
*		EQUATES
**********************************************************************

ASSEMBLE

* Flag for removing fancy user function features
fFANCYUSER	EQU 1
* Flag for removing built-in special identifiers
fFANCYIDS	EQU 2
* Flag for removing dotted identifiers
fFANCYDOT	EQU 3

		SETFLAG	fFANCYUSER	Enabled by default
		SETFLAG	fFANCYIDS	Enabled by default
		SETFLAG	fFANCYDOT	Enabled by default

* Max number of elements in {} for fancy display in ASTK

MAXLIST		EQU 20

* Max number of elements in 1D or 2D array

MAXARRY		EQU 10*10

* Font size variable address is within TESTMSG RAM area (44 nibbles)
*	1 - small
*	2 - medium
*	3 - big
* The areas overlap (excluding IRAM@) so we use the bigger start
* address (G_TESTMSG) as the storage place.

=FNT		EQU #80092	SX: 008C	GX: 0092

* Unsupported entries

=aTEMPENV	EQU #04E68
=aSystemFlags	EQU #1C645

=STUFF		EQU #0675F
=push#c		EQU #07230
=push#		EQU #07233
=grob!		EQU #116B5
=UnScroll	EQU #13695
=UNDO_TOP?	EQU #14E6D
=HALTTempEnv?	EQU #1506B
=STKDECOMP$	EQU #15955
=GETPTREVALC	EQU #15C77
=LastRomWord@	EQU #18802
='EvalNoCK:	EQU #18F6A
=GPPushALp	EQU #307D5
=DoCont/Kill	EQU #3875F
=FixStk&Menu	EQU #387BE
=SysErrFixUI	EQU #3881D
=MakeErrMesg	EQU #3889F
=ProcessAlarm	EQU #3894E
=DA1Temp?	EQU #3931D
=NoRollDA2?	EQU #39569
=?PICK		EQU #44197
=NULLPAINT	EQU #505B2
=ClrnewEditL	EQU #53A90
=KeyObLam	EQU #5A777
=numargs	EQU #5E889
=CKNfcn?	EQU #5E9A7
=splitonearg	EQU #5EAC2
=pullDROP	EQU #5EAF4
=roll2ND	EQU #5EB58
=unroll2ND	EQU #5EBDB
=scanahead	EQU #5F821
=SCANAHEAD	EQU #5F926
=rpnsplit	EQU #5F996
=push#a		EQU #62704
=#_123		EQU #64E6E
=Err#Cont	EQU #64EFA
=#CAlarmErr	EQU #65080
=ROMPTAB	EQU #707D9
=G_ROMPTAB	EQU #809A3

* Functions

*=x+	EQU #1AB67
*=x-	EQU #1AD09
*=x*	EQU #1ADEE
*=x/	EQU #1AF05
=xNEG	EQU #1A995
*=x^	EQU #1B02D
=xSQRT	EQU #1B374
*=xIFTE	EQU #1A3FE	* No fancy display implemented
=x=	EQU #1A8D8
=xABS	EQU #1AA1F
=xCONJ	EQU #1AA6E
=xXROOT	EQU #1B1CA
=xINV	EQU #1B278
=xSQ	EQU #1B426
=xEXP	EQU #1B905
=xALOG	EQU #1BA3D
*=xFACT	EQU #1BB02
=xMOD	EQU #1BE4D
*=xOBJ>	EQU #1CF7B
=xAND	EQU #1E783
=xOR	EQU #1E809
=xNOT	EQU #1E88F
=xXOR	EQU #1E8F6
=x==	EQU #1E972
=x<>	EQU #1EA9D
=x<	EQU #1EBBE
=x>	EQU #1EC5D
=x<=	EQU #1ECFC
=x>=	EQU #1ED9B
*=xDER	EQU #1EFD2
=xINTG	EQU #1F223
=xSUM	EQU #1F2C9
*=xWHERE	EQU #1F3F3
RPL

**********************************************************************
*	Library declarations
**********************************************************************

xROMID 2E4 ( 740 )

EXTERNAL xAGROB
EXTERNAL xASTK
EXTERNAL AGrob

ASSEMBLE
=CfgEqStk
RPL
:: 740 TOSRRP
  ' ID agrob PuHiddenVar
;

**********************************************************************
*	AGROB Command
**********************************************************************
ASSEMBLE
	CON(1)	8
RPL
xNAME AGROB
::
 CK2&Dispatch
 ONE
 ::
   COERCE TRUE		( ob %size flg )
   FNT1 FNT2
   { LAM #FNT1 LAM #FNT2 }
   BIND
   ::
     ( Evaluate from hidden dir if already there )

     ' ID agrob RclHiddenVar case EVAL

     ( Fetch parser program )
     RESOROMP AGrob
     ( Test if it is in temporary object area )
     CODE
	A=DAT1	A
	?A<=B	A
	GOYES	+
+	GOVLNG	=PushT/FLoop
     ENDCODE

     ( If not then it is safe to evaluate directly )

     NOTcase EVAL

     ( Else store it to hidden directory for evaluation )

     ' ID agrob DUPUNROT StoHiddenVar RclHiddenVar DROP
     EVAL
     ' ID agrob PuHiddenVar

   ;

   DROP ABND
 ;
;

**********************************************************************
*	ASTK Command
**********************************************************************
ASSEMBLE
	CON(1)	8
RPL
xNAME ASTK
::
  CK0NOLASTWD

* If ASTK already running change font and clear cache

 ' LAM #ASTK @ case
 ::
    NOT ' LAM #ASTK STO
    CODE
		GOSUB	GetCache		D0 = ->ob1
clrcache	LC(2)	2*(CACHESIZE)-1		Number of bindings - 1
		A=0	A
-		DAT0=A	A
		D0=D0+	10
		C=C-1	B
		GONC	-
		GOVLNG	=GETPTRLOOP
    ENDCODE
 ;

* Cache variables

 ' CacheDummy DoubleSize NDUPN DROP 

* System flags associated with the cache

  RCLSYSF

* Store AGrob in hidden dir if we are covered

 RESOROMP AGrob
 CODE
	A=DAT1	A
	?A<=B	A
	GOYES	+
+	GOVLNG	=PushT/FLoop
 ENDCODE
 IT :: ' ID agrob DUPUNROT StoHiddenVar RclHiddenVar DROP ;

 TRUE FNT1 FNT2

* Bind lambda variables

 ' NULLLAM DoubleSize NDUPN DROP
 { LAM #SYSF LAM #AGRB LAM #ASTK LAM #FNT1 LAM #FNT2 }
 INCOMPDROP TotalSize DOBIND

**
** Main Loop
**

 BEGIN
* Regular updates
  ClrDA1IsStat AtUserStack SysMenuCheck

* Display stack
  DA2aOK?NOTIT
  ::
   KEYINBUFFER? NOT DA2aLess1OK? OR NOTcase SetDA2aBad
   TOADISP

   DA2aLess1OK? ClrnewEditL case
   ::
    SetDA2aValid NoRollDA2? case ClrNoRollDA2
    CODE
	GOSBVL	=SAVPTR
	GOSBVL	=D0->Row1
	D1=A
	LC(5)	10*34
	A=A+C	A
	D0=A
	LC(5)	(56-8)*34
	GOSBVL	=MOVEDOWN
	GOSBVL	=GETPTRLOOP
    ENDCODE
   ;

* Stack display about to be done - check cache
* We remove any ob not contained in the CACHESTK lowest stack levels.
* Then the cache is packed so that cleared slots are last in the cache.
* First we check changes in display mode flags though, if any changes
* are detected we automatically clear the cache.

   CODE
		GOSUB	GetCache	D0 = ->ob1

* The system flags are bound right before the cache objects

		D0=D0-	10		D0 = ->sysf	
		A=DAT0	A
		AD0EX
		B=A	A
		B=B+CON	A,10		Fix B[A] to ->ob1 in cache
		D0=D0+	10
		A=DAT0	W		A[W] = old flags
		D1=(5)	=aSystemFlags
		C=DAT1	A
		D1=C
		C=DAT1	W		C[W] = new flags
		DAT0=C	W		save new flags

		D=C	W		D[W] = new flags
		LCHEX	7F0000403C000	mask for display flags
		A=A&C	W		A[W] = old display flags
		C=C&D	W		C[W] = new display flags
		?A=C	W
		GOYES	ckcache		The same - check stack

		A=B	A
		D0=A			D0 = ->ob1 in cache
		GOTO	clrcache	Clear cache

ckcache		C=B	A
		D0=C			D0 = ->ob1

* Clear slots which are not in stack

		LC(2)	(CACHESIZE)-1	Number of slots to check
--		RSTK=C
		A=DAT0	A
		?A=0	A
		GOYES	++		Empty slot - no need to check it
		GOSBVL	=D1=DSKTOP
		LC(2)	(CACHESTK)-1
		D=C	B		Number of stack levels to check
-		C=DAT1	A
		?A=C	A
		GOYES	++		Is on stack - keep it
		?C=0	A
		GOYES	+		End of stack - don't keep it
		D1=D1+	5
		D=D-1	B
		GONC	-
+		A=0	A		Clear cache slot
		DAT0=A	A
		D0=D0+	10
		DAT0=A	A
		D0=D0-	10
++		D0=D0+	10		Skip to next cache slot
		D0=D0+	10
		C=RSTK
		C=C-1	B
		GONC	--		Check CACHESIZE slots

* Pack the cache

		C=B	A
		D0=C			->ob1 (input)
		D1=C			->ob1 (output)
		LC(2)	(CACHESIZE)-1
		D=C	B		Number of slots to check
--		A=DAT0	A		->ob[i]
		D0=D0+	10
		C=DAT0	A		->grob[i]
		D0=D0+	10
		?A=0	A
		GOYES	+		If empty then don't output it
		DAT1=A	A		Output the used slot
		D1=D1+	10
		DAT1=C	A
		D1=D1+	10
+		D=D-1	B
		GONC	--		Check all slots
		AD0EX			A[A] = ->cache end
		C=0	A
--		CD1EX
		?C>=A	A
		GOYES	+		Done clearing remaining slots
		CD1EX
		DAT1=C	A
		D1=D1+	10
*		DAT1=C	A		These two instructions could be
*		D1=D1+	10		commented out to save space
		GONC	--		Keep clearing until end of cache
+		GOVLNG	=GETPTRLOOP

* This subroutine saves variables and finds the cache.
* If cache is not found a warmstart is generated
* Else D0 = ->ob1
* Note: cache slot =  nulllam ob nulllam grob

GetCache	GOSBVL	=SAVPTR
		D0=(5)	=aTEMPENV
		A=DAT0	A
		D0=A
		A=DAT0	A
--		D0=A			->tempenv[i]
		D0=D0+	10
		C=DAT0	A		C[A] = ->name1[i]
		CD0EX
		D0=D0+	5
		A=DAT0	12
		D0=C
		C=A	W
		LC(N)	2*5+2		First lambda name in the environment
		CON(2)	5		the cache is in is LAM #FNT2
		NIBASC	'#FNT2'
		?A=C	W
		GOYES	++		Found the environment!
		D0=D0-	10		D0 = ->tempenv[i]
		C=DAT0	A
		AD0EX
		A=A+C	A		A[A] = ->tempenv[i++]
		?C#0	A
		GOYES	--		Keep searching until end of tempenvs
		GOVLNG	=Warmstart	End of tempenvs - warmstart
++		P=	16-(CACHEPOS)	Skip regular lams
-		D0=D0+	10
		P=P+1
		GONC	-
		D0=D0+	5		Skip to ->ob1 binding
		RTN
   ENDCODE

   ClrNoRollDA2 ClrAbbrevStk

* Lowest display line is 56, modified by no of editlines
   FIFTYSIX FOUR LINESOFSTACK #- TEN #* #-

* Stack displaying from stack level 1
   ONE

   BEGIN
    DUP #2+ ?PICK

* Some embedding is done below to avoid skipob calls

ASSEMBLE
		CON(5)	=DOCODE
		REL(5)	->parse
		GOSBVL	=popflag
		GOC	+

* Decompile empty stack level to "1:"

		A=DAT1	A		"DUP" is safe here (flag popped)
		D1=D1-	5
		D=D-1	A
		DAT1=A	A
		GOVLNG	(=#:>$)+5

* Nonempty stack level. Dispatch to regular/fancy decompiler
* based on object type (and other characteristics)
+		GOSBVL	=SAVPTR
		C=DAT1	A
		D0=C
-		A=DAT0	A		Skip all leading tags
		LC(5)	=DOTAG	#2AFC
		?A#C	A
		GOYES	+
		D0=D0+	5		Skip DOTAG
		A=0	A
		A=DAT0	B
		CD0EX
		C=C+A	A
		C=C+A	A		Skip name
		CD0EX
		D0=D0+	2		Skip name length
		GONC	-
+

* Now test objects with size limits

		LC(2)	=DOLIST	#2A74
		?A=C	A
		GOYES	tstlist
		LC(3)	=DOARRY	#29E8
		?A=C	A
		GOYES	tstarry
		GOTO	tstregu

* Disable fancy display for too big arrays

tstarry		D0=D0+	15		->dimensions
		C=DAT0	A
		D=C	A		D[A] = dims
		D=D-1	A		dims--
		GOC	tstarrybad	0-dimension array
		C=0	A
		LC(1)	2-1
		?D>C	A
		GOYES	tstarrybad	> 2D array
		D0=D0+	5
		A=DAT0	A		A[A]=dim[1]
		D=D-1	A		dims--
		GOC	tstarrynow
		D0=D0+	5
		C=DAT0	A		C[A]=dim[2]
		GOSBVL	=MUL#
		GOC	tstarrybad	Way too big array
		A=B	A		A[A]=dim1*dim2
		?A=0	A
		GOYES	tstarrybad	One dimension is zero - bad

tstarrynow	LC(5)	MAXARRY
		?A>C	A
		GOYES	tstarrybad
		GOTO	dcfancy		Small enough - use AGrob
tstarrybad	GOTO	dcregular	Too big - use DECOMP$

* Disable fancy display for too long lists

tstlist		D0=D0+	5		** BUG: REMOVED A=DAT1 A D0=A
		B=0	A		elements = 0
-		A=DAT0	A
		LC(5)	=SEMI
		?A=C	A
		GOYES	+
		B=B+1	A		elements++
		GOSBVL	=SKIPOB
		GOTO	-
+		A=B	A		elements
		GOSBVL	=GETPTR
		LC(5)	MAXLIST
		?A>=C	A
		GOYES	dcregular
		GOTO	dcfancy

* Test regular objects

tstregu		LC(3)	=DOSYMB	#2AB8
		?C=A	A
		GOYES	dcfancy
		LC(2)	=DOEXT	#2ADA
		?C=A	A
		GOYES	dcfancy
		LC(3)	=DOIDNT	#2E48
		?C=A	A
		GOYES	dcfancy
		LC(2)	=DOLAM	#2E6D
		?C=A	A
		GOYES	dcfancy
		LC(2)	=DOROMP	#2E92
		?A=C	A
		GOYES	dcfancy
		LC(3)	=DOARRY	#29E8
		?C=A	A
		GOYES	dcfancy
		LC(3)	=DOGROB	#2B1E
		?C=A	A
		GOYES	dcfancy

* Regular decompiler
dcregular	GOSBVL	=GETPTR
		C=DAT1	10		"OVERSWAP"
		D1=D1-	5
		D=D-1	A
		DAT1=C	10
		LA(5)	=STKDECOMP$
		PC=(A)

* Fancy decompiler
-		GOSBVL	=GETPTR
		C=RSTK
		D0=C
		GOVLNG	=Loop
dcfancy		GOSUB	-

RPL

* We want to parse with AGrob. First check if ob is in cache
* Stack: ( .. #topline #level ob --> #topline #level ob/grob FALSE/TRUE )
  CODE
		GOSUB	GetCache	D0 = ->ob1
		A=DAT1	A
		B=A	A		B[A] = ->ob
		LC(2)	(CACHESIZE)-1
--		A=DAT0	A
		D0=D0+	10
		?A=0	A
		GOYES	+		Empty slot - don't test it (safety)
		?A=B	A
		GOYES	++		Match! Return grob
+		D0=D0+	10
		C=C-1	B
		GONC	--
		GOVLNG	=GPPushFLoop	( --> #topline #level ob FALSE )
++		A=DAT0	A		->grob
		DAT1=A	A
		GOVLNG	=GPPushTLoop	( --> #topline #level grob TRUE )
  ENDCODE

* If not cached then decompile and add to cache

  ?SKIP
  ::
    DUP
    LAM #ASTK ITE ONE TWO FALSE		( #topline #level ob ob #fnt false )
    LAM #AGRB EVAL DROP			( #topline #level ob grob )
    CODE
		GOSUB	GetCache	D0 = ->ob1
		LC(2)	(CACHESIZE)-2
		D=C	B		Number of levels to roll
		C=DAT1	A		C[A] = ->grob
		D1=D1+	5
		A=DAT1	A
		B=A	A		B[A] = ->ob
--		A=DAT0	A		A[A] = ->obnext
		ABEX	A		B[A] = ->obnext
		DAT0=A	A		Save ob
		D0=D0+	10
		A=DAT0	A		A[A] = ->grobnext
		ACEX	A		C[A] = ->grobnext
		DAT0=A	A		Save grob
		D0=D0+	10
		D=D-1	B
		GONC	--
		GOVLNG	=GETPTRLOOP
    ENDCODE
    SWAPDROP				( #topline #level grob )
  ;

* Decompile stack level indicator

* Make index grob
   OVER #:>$ LAM #ASTK ITE $>grob $>GROB	( #index G g )
* Modify index grob if g+G is too wide for display
   DUP GROBDIMw 3PICK GROBDIMw #+		( #index G g #w )
   XHI #> IT
   :: DROPOVER #>$ "; " !append$ LAM #ASTK ITE $>grob $>GROB ;
* Make display grob
   DUPGROBDIM DROP 3PICK GROBDIM DROP #MAX XHI MAKEGROB
* Bang index onto display grob
   SWAP DUPGROBDIM SWAPDROP UNROT OVER ZEROZERO GROB!
* Bang ob into it
   UNROTOVER GROBDIMw DUPUNROT #+ XHI #MAX SWAP#-
   ZERO CKGROBFITS
   4PICK UNROT GROB!
ASSEMBLE
->parse
RPL

* Stack: ( $ | grob )

* Make sure result is a grob
    DTYPECSTR? IT
    :: DUPLEN$ TWENTYTWO #> case :: ONE TWENTYONE SUB$ CHR_... >T$ $>GROB ;
       $ "                     " !append$ ONE TWENTYTWO SUB$ $>GROB
    ;

* Take subgrob if too high and bang into display
    DUPGROBDIM 5PICK 3PICK
* Change max if DA1 frozen
    DA1Temp? ITE SIXTEEN ZERO #+ #< ITE
    ::
     5ROLL DA1Temp? ITE SIXTEEN ZERO #-
     3PICK SWAP#- UNROTSWAP
     ZERO 4UNROLL SUBGROB
     ABUFF ZERO DA1Temp? ITE SIXTEEN ZERO GROB!
     ZEROSWAP		( new max is zero )
    ;
    ::
     DROP 4ROLL SWAP#-	( new max )
     DUP4UNROLL
     ABUFF ZERO ROT GROB!
    ;

* Stacklevel++
    #1+

   OVER#0= UNTIL	( Repeat until maxline = 0 )

   2DROP		( Drop stack level & maxline )

   SetDA2aValid
  ;

* Display editline
  DA2bOK?NOTIT DispEditLine
* Display menu
  DA3OK?NOTIT ?DispMenu
* Validate display
  ClrDAsOK

* Wait and eval key
  GetKeyOb ERRSET DoKeyOb
* Special trap to spot CONT exit
  ERRTRAP
  ::
   FixStk&Menu
   ERROR@ ZERO #=casedrop SysErrFixUI
   Err#Cont #=casedrop
   ::
    HALTTempEnv? caseERRJMP
    SysErrFixUI RSKIP TRUE		 ( Exit! )
   ;
   #_123 #=casedrop DoCont/Kill
   #CAlarmErr #=case ProcessAlarm

   LastRomWord@ ERROR@
   SysErrFixUI ERRBEEP MakeErrMesg
   TOADISP UnScroll
   DISPROW1 DISPROW2
   SetDA1Temp
  ;

  FALSE					( no exit )

  ClrDA1Bad				( Validate DA1 for ON-key )

 UNTIL

* Local exit preparations

 ' ID agrob PuHiddenVar

* This exit avoids problems with stack save modes when EQSTK
* was entered by typing the command ASTK.

 UNDO_TOP? IT ABND		( Abandon possible saved stack )
 ABND				( Abandon EQSTK local variables )
 TEN GETLAMPAIR			( Fetch first local variable with name )
 casedrop CacheStack		( Unlikely possibility of no 1st lam )
 UNROT2DROP			( Leave only the name on stack )
 EQ: KeyObLam			( Is it KeyOb lam? )
 NOTcase CacheStack		( No - just update stack save )

* The stack save mode may have been changed from OFF to ON during EQSTK,
* thus there may not be a stack save below KeyOb and thus an immediate
* UNDO after exit may cause a crash. Thus we explicitly fix the situation.

 1GETABND KeyOb!		( This is what the system would do anyway )
 CacheStack			( Fix stack save condition - this is enough )
 KeyOb@ ' KeyObLam ONE DOBIND	( And bind the KeyOb back in )
;				( And fall through to ROM code )

**********************************************************************
* Fancy symbolic decompiler.
*
* Principle:
*
*	Each symbolic can be considered to be of the form
*
*		function(expr1,expr2,expr3..exprN)
*
*	The decompiler descends recursively into the subexpressions,
*	and then combines the resulting grobs as is suitable for
*	the top level function.
*
*	At the lowest level the expressions have zero arguments, eg
*	they are constants or variables. For these the decompiler
*	outputs the term in grob form, and returns the 'middle'
*	y-coordinate as a system binary.
*	At higher levels the functions then combine the grobs
*	as indicated by their dimensions, their middle lines and of
*	course by the function itself.
*
*	For example + simply puts the grobs side by side (with a "+"
*	grob in between) so that the middle lines coinside. From
*	simple geometric considerations the new middle coordinate
*	is simply the greater of the two middle coordinates, and
*	the y-coordinates for the grobs are then simply obtained
*	as
*		max(middle1, middle2) - middle<i>
*	and x coordinates of course simply come from the grob x
*	dimensions.
*
* Notes:
*	For speed all the subroutines are embedded in a single
*	object so that slow ROMPTR calls can be avoided.
*	This implies that all calls to both RPL and assembly must
*	be done in assembly.
**********************************************************************

NULLNAME AGrob
ASSEMBLE

sSTKDC	EQU 12	* Used by wakeup code - safe to be used here

		CON(5)	=DOCODE		Embedd everything into a single
		REL(5)	->AGrobEnd	code object

* Set font size variable in fixed location to avoid recursive
* local variable creation. This of course requires the font
* size to be fixed back explicitly when recursive calls are finished.

		ST=0	sSTKDC		Assume no stack decompile
		GOSBVL	=popflag
		GOC	+
		ST=1	sSTKDC		Set stack decompile
+		GOSBVL	=POP#
		GOSBVL	=SAVPTR
		D0=(5)	(=IRAM@)-4
		C=DAT0	A
		D0=C
		D0=(4)	FNT
		DAT0=A	A
		GOSBVL	=GETPTR

**********************************************************************
* Recursion entry point with given font size
**********************************************************************
FontOb

* Explode symbolic on stack. (Internal version is very slow)

		ST=0	10		No GC yet
InnerRetry	GOSBVL	=PopASavptr
		R1=A	A		R1[A] = ->ob (In case GC is forced)
		D0=A
		B=0	A		complevel = 0
		C=0	A
		RSTK=C			elements = 0
InnerLoop	C=DAT0	A
		CD0EX
		A=DAT0	A		@element type
		D0=C
		LC(5)	=PRLG
		?A=C	A
		GOYES	InnerOb		element is embedded object
		A=DAT0	A
		D0=D0+	5
		LC(5)	=SEMI
		?A#C	A
		GOYES	InnerPsh	Push address if not SEMI
		B=B-1	A		complevel--
		GONC	InnerNxt
InnerOb		A=DAT0	A
		LC(5)	=DOSYMB
		?A=C	A
		GOYES	InnerSymb
		CD0EX
		R0=C	A		->element
		D0=C
		GOSBVL	=SKIPOB
		A=R0	A		->element
		GONC	InnerPsh
InnerSymb	D0=D0+	5
		B=B+1	A		complevel++
		GONC	InnerLoop
InnerPsh	D=D-1	A		mem--
		GOC	InnerGC
		D1=D1-	5
		DAT1=A	A
		C=RSTK
		C=C+1	A		elements++
		RSTK=C
InnerNxt	?B#0	A
		GOYES	InnerLoop
		C=RSTK
		R0=C	A		R0[A] = elements
		AD1EX
		C=D	A
		RSTK=C
		GOSBVL	=GETPTR
		D1=A
		C=RSTK
		D=C	A
		GOSBVL	=SAVPTR
		GOSBVL	=PUSH#
* Explode done, start decompiling
		GOTO	Font0

InnerGC		A=R1	A		->object
		GOSBVL	=GPPushA
		GOSBVL	=SAVPTR
		GOSBVL	=DOGARBAGE	Errors if ST10 is set (does GETPTR)
		GOTO	InnerRetry

**********************************************************************
*		The embedded subroutines
**********************************************************************

**********************************************************************
* Dispatch on function given priority table.
* The dispatcher enters the table at some point based on the
* priority of the last function, if the next function is found
* later in the table then dispatch occurs normally, if it is
* before in the table then parenthesis are added after the dispatchee
* finishes.
*
* Note:	The table is based on mathematical priority, but is adjusted
*	for functions which have special fancy output that makes
*	priority obvious.
*
* Note:	FCNAPPLY is last in the table, although fancy user function
*	output might not support the highest priority. This is
*	unavoidable with the current algorithm.
**********************************************************************

Dispatch	R0=C.F	A		offset into function table

		A=DAT1	A		See if constant term
		LC(5)	=ONE
		?A#C	A
		GOYES	+
		D1=D1+	5		Yes - drop ONE and decompile term
		D=D+1	A
		GOLONG	addobj.1	Use given priority

+		GOSUB	PassDsp

PORG		CON(5)	0
		REL(5)	addarg		Default dispatchee
PRI0
*		CON(5)	=xIFTE
*		REL(5)	addifte
PRI1		CON(5)	=xWHERE
		REL(5)	addwhere
PRI2		CON(5)	=xOR
		REL(5)	addor
PRI3		CON(5)	=xAND
		REL(5)	addand
		CON(5)	=xXOR
		REL(5)	addxor
		CON(5)	=xNOT
		REL(5)	addnot
PRI4		CON(5)	=x=
		REL(5)	add=
		CON(5)	=x==
		REL(5)	add==
		CON(5)	=x<>
		REL(5)	add<>
		CON(5)	=x<
		REL(5)	add<
		CON(5)	=x>
		REL(5)	add>
		CON(5)	=x<=
		REL(5)	add<=
		CON(5)	=x>=
		REL(5)	add>=
PRI5		CON(5)	=x+
		REL(5)	add+
		CON(5)	=x-
		REL(5)	add-
PRI6		CON(5)	=xNEG
		REL(5)	addneg
		CON(5)	=x*
		REL(5)	add*
		CON(5)	=x/
		REL(5)	add/
		CON(5)	=xMOD
		REL(5)	addmod
PRI7		CON(5)	=x^
		REL(5)	add^
		CON(5)	=xINV
		REL(5)	addinv
		CON(5)	=xSQ
		REL(5)	addsq
		CON(5)	=xEXP
		REL(5)	addexp
		CON(5)	=xALOG
		REL(5)	addalog
		CON(5)	=xCONJ
		REL(5)	addconj
		CON(5)	=xDER
		REL(5)	addderiv
PRI8		CON(5)	=xSQRT
		REL(5)	addsqrt
		CON(5)	=xABS
		REL(5)	addabs
		CON(5)	=xINTG
		REL(5)	addintg
		CON(5)	=xSUM
		REL(5)	addsum
		CON(5)	=xFACT
		REL(5)	addfact
		CON(5)	=xFCNAPPLY
		REL(5)	addfcn
		CON(5)	=xXROOT
		REL(5)	addxroot
		CON(5)	0		End of table mark

PassDsp		C=RSTK
		A=R0.F	A	offset
		C=C+A	A	->start
		R0=C.F	A
		D1=D1+	5
		A=DAT1	A
		D1=D1-	5
		CD0EX
		RSTK=C
* Search >= priority
-		C=DAT0	A
		D0=D0+	10
		?C=0	A
		GOYES	+
		?A#C	A
		GOYES	-
* Eval >= priority as is
		D0=D0-	5
		A=DAT0	A
		C=RSTK
		CD0EX
		A=A+C	A
		PC=(A)
* Search < priority
+		C=R0.F	A
		D0=C
-		D0=D0-	10
		C=DAT0	A
		?C=0	A
		GOYES	++
		?A#C	A
		GOYES	-
* Eval < priority, then add ()
		D0=D0+	5
		A=DAT0	A
		C=RSTK
		CD0EX
		C=C+A	A	dispatchee address
		R0=C
		A=PC
		A=A+CON	A,10
		PC=(A)
		CON(5)	=DOCOL
		CON(5)	=DOCODE
		REL(5)	+
		A=R0
		PC=(A)		dispatch
+		CON(5)	=DOCODE
		REL(5)	+
		GOLONG	AddPar
+		CON(5)	=SEMI

* No match, eval the default
++		D0=D0+	5
		A=DAT0	A
		C=RSTK
		CD0EX
		A=A+C	A
		PC=(A)	

* Priority entry dispatch points

Font8		LC(5)	(PRI8)-(PORG)
		GOTO	Dispatch
Font7		LC(5)	(PRI7)-(PORG)
		GOTO	Dispatch
Font6		LC(5)	(PRI6)-(PORG)
		GOTO	Dispatch
Font5		LC(5)	(PRI5)-(PORG)
		GOTO	Dispatch
Font4		LC(5)	(PRI4)-(PORG)
		GOTO	Dispatch
Font3		LC(5)	(PRI3)-(PORG)
		GOTO	Dispatch
Font2		LC(5)	(PRI2)-(PORG)
		GOTO	Dispatch
Font1		LC(5)	(PRI1)-(PORG)
		GOTO	Dispatch
FontIt+		GOSUB	FNT+
		GOTO	Font0
FontIt-		GOSUB	FNT-
Font0		LC(5)	(PRI0)-(PORG)
		GOTO	Dispatch

**********************************************************************
* Font size handling
**********************************************************************

* Fetch FNT variable
D0->FNT		CD0EX
		D0=(5)	(=IRAM@)-4
		A=DAT0	A
		D0=A
		D0=(4)	FNT
		A=DAT0	A
		RTNCC
* Decrease font size
FNT-LOOP	GOSUB	FNT-
		GOVLNG	=Loop
FNT-		GOSUB	D0->FNT
		A=A-1	A
		DAT0=A	A
		CD0EX
		RTNCC
* Increase font size
FNT+LOOP	GOSUB	FNT+
		GOVLNG	=Loop
FNT+		GOSUB	D0->FNT
		A=A+1	A
		DAT0=A	A
		CD0EX
		RTNCC

* Choose grob from return stack and push it

GChoose:	GOSBVL	=SAVPTR
		GOSUB	D0->FNT
		C=RSTK
		D0=C
		A=A+A	A
		GOC	+
		ASRB.F	A
		A=A-CON	A,2
		GOC	+
		D0=D0+	5
		C=DAT0	A
		AD0EX
		A=A+C	A
		AD0EX
		A=A-1	A
		GOC	+
		D0=D0+	5
		C=DAT0	A
		AD0EX
		A=A+C	A
		AD0EX
+		AD0EX
		GOVLNG	=GPPushALp		

**********************************************************************
* For speed unrolling & rolling is not used, instead the RPL
* return stack is used as a stack.
**********************************************************************

* Push objects
Push2		P=P-1
Push1		P=P-1
		C=B	A
		CD0EX
-		A=DAT1	A
		DAT0=A	A
		D0=D0+	5
		D1=D1+	5
		P=P+1
		GONC	-
		CD0EX
		B=C	A
		RTNCC
* Pop objects
Pop2		P=P-1
Pop1		P=P-1
		C=B	A
		CD0EX
-		D0=D0-	5
		D1=D1-	5
		A=DAT0	A
		DAT1=A	A
		P=P+1
		GONC	-
		CD0EX
		B=C	A
		RTNCC
* Pop 1 while saving 2
PopSave		A=DAT1	10
		D1=D1+	5
		C=B	A
		CD0EX
		RSTK=C
		D0=D0-	5
		C=DAT0	A
		DAT1=C	A
		DAT0=A	10
		D0=D0+	10
		C=RSTK
		CD0EX
		B=C	A
		RTNCC

Pop1Loop	GOSUB	Pop1
		GONC	+
Pop2Loop	GOSUB	Pop2
		GONC	+
Push1Loop	GOSUB	Push1
		GONC	+
Push2Loop	GOSUB	Push2
+		GOVLNG	=Loop

**********************************************************************
* Find size of the last argument of the topmost function
* ( expr1 expr2 fcn #n1+n2+1 --> expr1 expr2 #n2 #n1 )
**********************************************************************
Split		GOSBVL	=SAVPTR
		D1=D1+	10
		C=0	A
		LC(1)	1
		R0=C	A		Stack level
		R1=C	A		1 expression to extract
		GOSBVL	=scanahead
		GOSBVL	=D1=DSKTOP
		GOSBVL	=POP#
		C=R0	A
		A=A-C	A
		R0=A	A
		C=C-1	A
		R1=C	A
		GOSBVL	=PUSH2#
		A=DAT1	A
		D1=D1+	5
		D=D+1	A
		C=DAT1	A
		D1=D1+	5
		D=D+1	A
		DAT1=C	A
		D1=D1+	5
		DAT1=A	A
		D1=D1-	5
		RTNCC
*********************************
* SWAPDROP#1-
*********************************
Pull		GOSBVL	=POP#
		A=A-1	A
		R0=A
		GOSBVL	=PopASavptr
		GOVLNG	=PUSH#
*********************************
* ( Meta grb #mid --> grb #mid )
* iff STKDC and grb wide
*********************************
StkAbort?	?ST=0	sSTKDC
		RTNYES
		D1=D1+	5
		C=DAT1	A		C[A] = ->grob
		D1=D1-	5
		CD0EX
		D0=D0+	15
		A=DAT0	A		grob width
		D0=C
		LC(5)	131
		?A<C	A
		RTNYES
StkAbort	C=RSTK
		A=DAT1	10
		D1=D1+	10
		R0=A	W
		GOSBVL	=POP#
		C=A	A
		D=D+C	A
		AD1EX
		A=A+C	A
		C=C+C	A
		C=C+C	A
		A=A+C	A
		D1=A
		A=R0	W
		D1=D1-	10
		DAT1=A	10
		GOVLNG	(=SEMI)+5
RPL

**********************************************************************
*		Function dispatchees
**********************************************************************

LABEL add+
::
  plDRPpZparg			( B A )
  CODE
		GOTO	Font5
  ENDCODE
  CODE
		GOSUB	StkAbort?
		GOSUB	Push2
		GOTO	Font5
  ENDCODE
  CODE
		GOSUB	Pop2
		GOSUBL	Swap2
		GOSUB	GChoose:
		NIBHEX	E1B2071000400004000020702000
		NIBHEX	E1B20B100060000600004040F1404000
		NIBHEX	E1B20B100060000600004040F1404000
  ENDCODE
  CODE
		GOLONG	Middle
  ENDCODE
;
**********************************************************************
LABEL add-
::
  plDRPpZparg			( B A )
  CODE
		GOTO	Font5
  ENDCODE
  CODE
		GOSUB	StkAbort?
		GOSUB	Push2
		GOTO	Font6
  ENDCODE
  CODE
		GOSUB	Pop2
		GOSUBL	Swap2
		GOSUB	GChoose:
		NIBHEX	E1B203100020000400007000
		NIBHEX	E1B20310002000060000F100
		NIBHEX	E1B20310002000060000F100
  ENDCODE
  CODE
		GOLONG	Middle
  ENDCODE
;
**********************************************************************
LABEL addneg
::
 CODE
		GOSUB	Pull
		GOTO	Font6
 ENDCODE
 CODE
		GOSUB	GChoose:
		NIBHEX	E1B203100020000400007000
		NIBHEX	E1B20310002000060000F100
		NIBHEX	E1B20310002000060000F100
 ENDCODE
 CODE
		GOLONG	Insert
 ENDCODE
;
**********************************************************************
LABEL add*
::
  plDRPpZparg				( B A )
  CODE
		GOTO	Font6		( B lg # )
  ENDCODE
  CODE
		GOSUB	StkAbort?
		GOSUB	Push2
		CD1EX
		RSTK=C			Save D1
		D1=C
-		D1=D1+	5	<--+
		A=DAT1	A	   |	Skip possible * functions
		LC(5)	=x*	   |
		?A=C	A	   |
		GOYES	-	---+
		D1=A
		A=DAT1	A		Type of non *
		LC(5)	=DOEXT
		?A=C	A
		GOYES	+		Save test in CRY
+		C=RSTK
		D1=C			Restore D1
		GOVLNG	=PushT/FLoop	Push flag
 ENDCODE
 CODE
		GOSUB	Push1		Save the flag in return stack
		GOTO	Font6		And parse right hand arg
 ENDCODE
 CODE
		GOSUB	Pop1		Pop the flag to data stack
		GOSUB	Pop2		( rg # flg lg # )
		GOSUBL	Rot		( rg # lg # flg )
		GOSBVL	=popflag	And pop it from there
		GONC	+		If not unit then exit
		GOSUBL	AddPar!
+		GOSUBL	Swap2		( lg # rg # )
		GOVLNG	=Loop
 ENDCODE
 GROB E 20000200001000
 CODE
		GOLONG	Middle
 ENDCODE
;
**********************************************************************
LABEL add/
::
 CODE
part1font0	GOSUB	Split
		GOSUB	Push1
		GOTO	Font0
 ENDCODE
 CODE
		GOSUB	PopSave
		GOTO	Font0
 ENDCODE
 CODE
		GOSUB	Pop2
		GOLONG	AddDiv
 ENDCODE
;
**********************************************************************
LABEL add^
::
 CODE
		GOSUB	Split
		GOSUB	Push1
		GOTO	FontIt-
 ENDCODE
 CODE
		GOSUB	FNT+
		GOSUB	PopSave
		GOTO	Font8
 ENDCODE
 CODE
		GOSUB	Pop2
		GOLONG	AddExp
 ENDCODE
;
**********************************************************************
LABEL addinv
::
 CODE
pullfont8	GOSUB	Pull
		GOTO	Font8
 ENDCODE
 "-1"
 CODE
		GOSUB	FNT-
		GOLONG	ObMisc
 ENDCODE
 CODE
		GOSUB	FNT+
		GOLONG	AddExp
 ENDCODE
;
**********************************************************************
LABEL addsq
::
 CODE
		GOTO	pullfont8
 ENDCODE
 "2"
 CODE
		GOSUB	FNT-
		GOLONG	ObMisc
 ENDCODE
 CODE
		GOSUB	FNT+
		GOLONG	AddExp
 ENDCODE
;
**********************************************************************
LABEL addconj
::
 CODE
		GOTO	pullfont8
 ENDCODE
 GROB 16 6000040000502070205000
 THREE
 CODE
		GOLONG	AddExp
 ENDCODE
;
**********************************************************************
LABEL addexp
::
 CODE
		GOSUB	Pull
		GOTO	FontIt-
 ENDCODE
 GROB 1A 80000600000000E011F110E000
 FOUR
 CODE
		GOSUBL	Swap2
		GOSUB	FNT+
		GOLONG	AddExp
 ENDCODE
;
**********************************************************************
LABEL addalog
::
 CODE
		GOSUB	Pull
		GOTO	FontIt-
 ENDCODE
 "10"
 CODE
		GOSUB	FNT+
		GOLONG	ObMisc
 ENDCODE
 CODE
		GOSUBL	Swap2
		GOLONG	AddExp
 ENDCODE
;
**********************************************************************
LABEL addsqrt
::
 CODE
pullfont0	GOSUB	Pull
		GOTO	Font0
 ENDCODE
 CODE
		GOLONG	AddSqrt
 ENDCODE
;
**********************************************************************
LABEL addmod
::
 CODE
part1font6	GOSUB	Split
		GOSUB	Push1
		GOTO	Font6
 ENDCODE
 CODE
part2font6	GOSUB	PopSave
		GOTO	Font6
 ENDCODE
 " MOD "
 CODE
Pop2RotFont$	GOSUB	Pop2
		GOSUBL	Rot
		GOLONG	$>font
 ENDCODE
 CODE
		GOLONG	Middle
 ENDCODE
;
**********************************************************************
LABEL add=
::
  "="
  CODE
equgeneral	A=PC			Change stream into following codeob
		LC(5)	(+)-(*)
		A=A+C	A
		D0=A
		GOSUB	Push1
		GOVLNG	=Loop
+
  ENDCODE
  plDRPpZparg				( B A )
  CODE
		GOLONG	Font4
  ENDCODE
  CODE
		GOSUB	Pop1		( B lg # $ )
		GOVLNG	=Loop
  ENDCODE
  4UNROLL ROT#1+UNROT			( B&$ lg # )
  CODE
		GOSUB	StkAbort?
		GOVLNG	=Loop
  ENDCODE
  ROT #1-UNROT 4ROLL			( B lg # $ )
  CODE
		GOSUB	Push1		Save $
		GOSUB	Push2		Save lg #
		GOLONG	Font4		( rg # )
  ENDCODE
  CODE
		GOSUB	Pop2		( rg # lg # )
		GOSUB	Swap2		( lg # rg # )
		GOSUB	Pop1		( lg # rg # $ )
		GOLONG	$>font		( lf # rg # G )
  ENDCODE
  CODE
		GOTO	Middle
  ENDCODE
;
*********************************
LABEL add==
::
  "=="
  CODE
		GOTO	equgeneral
  ENDCODE
(;)
*********************************
LABEL add<>
::
  "\8B"
  CODE
		GOTO	equgeneral
  ENDCODE
(;)
*********************************
LABEL add<
::
  "<"
  CODE
		GOTO	equgeneral
  ENDCODE
(;)
*********************************
LABEL add>
::
  ">"
  CODE
		GOTO	equgeneral
  ENDCODE
(;)
*********************************
LABEL add<=
::
  "\89"
  CODE
		GOTO	equgeneral
  ENDCODE
(;)
*********************************
LABEL add>=
::
  "\8A"
  CODE
		GOTO	equgeneral
  ENDCODE
(;)
**********************************************************************
LABEL addfact
::
 CODE
		GOTO	pullfont8
 ENDCODE
 "!"
 CODE
		GOLONG	$>font
 ENDCODE
 CODE
		GOLONG	Append
 ENDCODE
;
**********************************************************************
LABEL addnot
::
 CODE
		GOSUB	Pull
		GOLONG	Font4
 ENDCODE
 GROB E 2000070000F080
 CODE
		GOTO	Insert
 ENDCODE
;
**********************************************************************
LABEL addand
::
 CODE
part1font3	GOSUBL	Split
		GOSUBL	Push1
		GOLONG	Font3
 ENDCODE
 CODE
part2font3	GOSUBL	PopSave
		GOLONG	Font3
 ENDCODE
 GROB 16 6000080000804141222200
 CODE
mergemiddle	GOSUBL	Pop2
		GOSUBL	Rot
		GOLONG	Middle
 ENDCODE
;
**********************************************************************
LABEL addxor
::
 CODE
		GOTO	part1font3
 ENDCODE
 CODE
		GOTO	part2font3
 ENDCODE
 GROB 16 60000800002222414180E3
 CODE
		GOTO	mergemiddle
 ENDCODE
;
**********************************************************************
LABEL addor
::
 CODE
		GOSUBL	Split
		GOSUBL	Push1
		GOLONG	Font2
 ENDCODE
 CODE
		GOSUBL	PopSave
		GOLONG	Font2
 ENDCODE
 GROB 16 6000080000222241418000
 CODE
		GOTO	mergemiddle
 ENDCODE
;
**********************************************************************
LABEL addfcn
::
 SWAPDROP #3-
 CODE
		GOSUB	Unrot
		A=PC			Change stream into common code
		LC(5)	(addargsub)-(*)
		A=A+C	A
		D0=A
		GOVLNG	=Loop
 ENDCODE
(;)
**********************************************************************
LABEL addarg
::
  #1-SWAP CKNfcn? ITE
   :: SWAP#1- UNROTSWAP ;
   numargs

* arg1 arg2 .. argN m name n 
  LABEL addargsub

* Try romptr -> id conversion for name

  OVER TYPEROMP? IT
  :: SWAP palrompdcmp DROP $>ID SWAP ;


* Try special .n and .nm forms and special function names

ASSEMBLE
	IF	fFANCYUSER
RPL
* Check special cases
  CODE
		GOSBVL	=SAVPTR
		GOSBVL	=POP#
		ASRC
		P=	4
		A=0	P
		P=	0
		?A=0	A
		GOYES	+
		A=0	S	~inf
+		A=DAT1	A
		D0=A
		A=DAT0	A
		LC(5)	=DOIDNT
		?A=C	A
		GOYES	+
-		GOVLNG	=GETPTRLOOP

+		D0=D0+	5
		A=0	A
		A=DAT0	B
		B=A	A
		A=0	P
		?A#0	B
		GOYES	-
		D0=D0+	2

* Now:	A[S] = args (0 if more than 15)
*	B[A] = chars in idnt (0<x<16)
*	D0   = ->name

* Try .n and .nm endings

		CD0EX
		D0=C
		C=C+B	A
		C=C+B	A
		D1=C		->name end

		D1=D1-	4
		A=DAT1	A
		C=A	A
		LCSTR	'.n'
		?A#C	A
		GOYES	+
		A=PC
		LC(5)	(fun.n)-(*)
		GOTO	fungo

+		D1=D1-	2

		A=DAT1	6
		C=A	W
		LCSTR	'.nm'
		?A#C	W
		GOYES	fundispatch
		C=C-1	S		Match - but must have >=2 args
		GOC	+
		?C=0	S
		GOYES	fundispatch
+		A=PC
		LC(5)	(fun.nm)-(*)
		GOTO	fungo

* Now try special names

fundispatch	GOSUB	+


SPECFUN	MACRO
	CON(1)	$1		Argument count
	CON(1)	$2		Name length in characters
	NIBASC	'$3'		Name
	REL(5)	add$3		Dispatchee offset
SPECFUN	ENDM

		SPECFUN	2,2,If
		SPECFUN	0,3,Top
		SPECFUN	2,2,So
		SPECFUN	2,3,Equ
		SPECFUN	2,3,Mul
		SPECFUN	2,2,PM
		SPECFUN	2,2,MP
		SPECFUN	1,2,pm
		SPECFUN	1,2,mp
		SPECFUN	2,5,Cross
		SPECFUN	1,4,Grad
		SPECFUN	1,4,Curl
		SPECFUN	1,3,Div
		SPECFUN	2,3,Der
		SPECFUN	2,3,DER
		SPECFUN	2+8,3,der
		SPECFUN	1,3,Lap
		SPECFUN	1,4,LapT
		SPECFUN	1,4,FouT
		SPECFUN	1,5,ILapT
		SPECFUN	1,5,IFouT
		SPECFUN	1,6,Vector
		SPECFUN	0,2,Lt
		SPECFUN	0,2,Rt
		SPECFUN	1,3,Par
		SPECFUN	1,2,fp
		SPECFUN	1,2,fm
		CON(2)	0		End of table
-		GOVLNG	=GETPTRLOOP

+		C=RSTK
		D1=C
funloop		C=DAT1	B
		?C=0	B
		GOYES	-
		D1=D1+	2
		CSRC
		?C#B	P		If not same length then continue
		GOYES	funcont
		?C=0	S		If inf args allowed then compare
		GOYES	funtest
		?C=A	S		If same args then compare
		GOYES	funtest
		C=C+C	S		If absolute args then cont
		GONC	funcont
		?A=0	S		If lots of args then compare
		GOYES	funtest		(must be more than required then)
		CSRB.F	S		If more args than required then comp
		?A>=C	S
		GOYES	funtest
funcont		P=C	0		Else skip to next slot
		CD1EX
		C+P+1
		C+P+1
		CD1EX
		P=	0
		D1=D1+	3
		GONC	funloop

funtest		C=C+C	A
		P=C	0
		P=P-1
		A=DAT0	WP
		C=DAT1	WP
		?A=C	WP
		GOYES	funmatch
		C=B	A
		GONC	funcont	
funmatch	CD1EX
		C+P+1
		D1=C
		P=	0
		A=DAT1	A
fungo		A=A+C	A
		GOSBVL	=GETPTR
		D0=A			Change stream
*		D0=D0+	5		Skip DOCOL !!!!
		GOVLNG	=Loop
  ENDCODE
ASSEMBLE
	ENDIF			* fFANCYUSER
RPL

* Regular name, put arguments in parenthesis

  CODE
		GOSUB	Swap
		GOLONG	addobj
  ENDCODE

* Special entry point for adding arguments for a grob
* ( Meta #args grob #mid )

  CODE
addfunargs	A=PC
		A=A+CON	A,16		[6]
		D0=A			[3]
		GOVLNG	=Loop		[7]
  ENDCODE

* Zero arguments

  ROT DUP#0=csedrp ROTDROP		( --> grob #mid )

* One argument

  DUP#1= casedrop
  ::
    CODE
push2font0	GOSUBL	Push2
		GOLONG	Font0
    ENDCODE
    CODE
mergefcn	GOSUBL	AddPar!
		GOSUBL	Pop2
		GOTO	InsertG
    ENDCODE
  ;

* More than one argument

  CODE
		GOSUB	Unrot
		GOLONG	Push2Loop
  ENDCODE
  DUP
  CODE
		GOLONG	Push1Loop
  ENDCODE

  ONE_DO (DO) pshzerpsharg LOOP

  CODE
		GOLONG	Font0
  ENDCODE
  CODE
		GOLONG	Pop1Loop
  ENDCODE

  ONE_DO (DO)
    CODE
		GOTO	push2font0
    ENDCODE
    CODE
		GOLONG	Pop2Loop
    ENDCODE
    2SWAP
    ","
    CODE
		GOLONG	$>font
    ENDCODE
    CODE
		GOTO	Middle
    ENDCODE
  LOOP

 CODE
		GOTO	mergefcn
 ENDCODE
;

ASSEMBLE
**********************************************************************
*		STACK HANDLING SUBROUTINES
**********************************************************************
Swap		A=DAT1	A		( 1 2 --> 2 1 )
		D1=D1+	5
		C=DAT1	A
		DAT1=A	A
		D1=D1-	5
		DAT1=C	A
		RTN
Swap2		A=DAT1	10		( 1 2 3 4 --> 3 4 1 2 )
		D1=D1+	10
		C=DAT1	10
		DAT1=A	10
		D1=D1-	10
		DAT1=C	10
		RTN
Rot		A=DAT1	10		( 1 2 3 --> 2 3 1 )
		D1=D1+	10
		C=DAT1	A
		D1=D1-	5
		DAT1=A	10
		D1=D1-	5
		DAT1=C	A
		RTN
Unrot		A=DAT1	A		( 1 2 3 --> 3 1 2 )
		D1=D1+	5
		C=DAT1	10
		D1=D1+	5
		DAT1=A	A
		D1=D1-	10
		DAT1=C	10
		RTN
**********************************************************************
*		GROB MERGING SUBROUTINES
**********************************************************************

**********************************************************************
* Append 2 arguments with given grob in between
* Stack:	g1 m1 g2 m2 g3	--> G M
**********************************************************************

Middle		GOSUB	Middle!
		GOVLNG	=Loop
Middle!		GOSBVL	=SAVPTR
		D1=D1+	5
		A=DAT1	A
		D0=A
		D0=D0+	5
		A=DAT0	A	h2
		B=A	A	h2
		D1=D1+	10
		C=DAT1	A
		D0=C
		D0=D0+	5
		C=DAT0	A	h1
		D=C	A	h1
		?C>=A	A
		GOYES	x8maxh
		ACEX	A
x8maxh		RSTK=C		maxh

* RSTK	maxh
* B[A]	h2
* D[A]	h1

		D1=D1+	5
		C=DAT1	A	g1
		D0=C
		D0=D0+	10
		C=DAT0	A	y1
		C=C-D	A	y1-h1
		D=C	A	y1-h1
		D0=D0+	5
		A=DAT0	A	x1
		D1=D1-	10
		C=DAT1	A	g2
		D0=C
		D0=D0+	10
		C=DAT0	A	y2
		C=C-B	A	y2-h2
		?D>=C	A
		GOYES	x8maxyh
		D=C	A	MAX(y1-h1,y2-h2)
x8maxyh		C=RSTK
		RSTK=C
		C=C+D	A	Y
		R1=C		Y
		D0=D0+	5
		C=DAT0	A	x2
		A=A+C	A	x1+x2
		D1=D1-	10
		C=DAT1	A	g3
		D0=C
		D0=D0+	15
		C=DAT0	A	x3
		A=A+C	A	x1+x2+x3
		R0=A		X
		GOSBVL	=makegrob
		D0=D0-	10
		D0=D0-	10	G
		CD0EX
		RSTK=C		G maxh
* Put g3
		GOSBVL	=D1=DSKTOP
		A=DAT1	A	g3
		D1=D1+	10
		D1=D1+	10
		C=DAT1	A	g1
		D0=C
		D0=D0+	15
		C=DAT0	A	x1
		R1=C		x1
		D0=A		g3
		D0=D0+	10
		A=DAT0	A	y3
		D0=D0-	10
		ASRB.F	A	y3/2
		C=RSTK
		D1=C		G
		C=RSTK		maxh
		RSTK=C
		C=C-A	A	maxh-y3/2
		R0=C		maxh-y3/2
		CD1EX
		RSTK=C
		CD1EX
		GOSUBL	grb!
* Put g2
		GOSBVL	=D1=DSKTOP
		C=DAT1	A	g3
		D0=C
		D0=D0+	15
		C=DAT0	A	x3
		D1=D1+	5
		A=DAT1	A
		D0=A
		D0=D0+	5
		A=DAT0	A	h2
		B=A	A	h2
		D1=D1+	15
		A=DAT1	A	g1
		D0=A
		D0=D0+	15
		A=DAT0	A	x1
		A=A+C	A	x1+x3
		R1=A		x1+x3
		D1=D1-	10
		A=DAT1	A	g2
		D0=A
		C=RSTK
		D1=C		G
		C=RSTK		maxh
		RSTK=C
		C=C-B	A	maxh-h2
		R0=C
		CD1EX
		RSTK=C
		CD1EX
		GOSUBL	grb!
* Put g1
		GOSBVL	=D1=DSKTOP
		D1=D1+	15
		A=DAT1	A
		D0=A
		D0=D0+	5
		A=DAT0	A	h1
		D1=D1+	5
		C=DAT1	A	g1
		D0=C
		C=RSTK		G
		DAT1=C	A	OVERWR!
		D1=C
		C=RSTK		maxh
		RSTK=C
		C=C-A	A	maxh-h1
		R0=C
		C=0	A
		R1=C
		GOSUBL	grb!
* Push maxh
		GOSBVL	=GETPTR
		D1=D1+	10
		D1=D1+	10
		D=D+CON	A,4
		C=RSTK		maxh
		R0=C
		GOSBVL	=SAVPTR
		GOVLNG	=PUSH#

**********************************************************************
* Insert symmetric grob
* Stack:	g1 m1 g2 --> g2+g1 M
**********************************************************************
Insert		GOSUB	Insert!
		GOVLNG	=Loop
Insert!		GOSBVL	=SAVPTR
		A=DAT1	A
		D0=A
		D0=D0+	10
		A=DAT0	A	y
		ASRB.F	A	y/2
		R0=A
		GOSBVL	=PUSH#
		GOTO	InsertG!
**********************************************************************
* Append symmetric grob
* Stack:	g1 m1 g2 --> g1+g2 M
**********************************************************************
Append		GOSUB	Append!
		GOVLNG	=Loop
Append!		GOSBVL	=SAVPTR
		A=DAT1	A
		D0=A
		D0=D0+	10
		A=DAT0	A	y
		ASRB.F	A	y/2
		R0=A
		GOSBVL	=PUSH#
		A=DAT1	10
		D1=D1+	10
		C=DAT1	10
		DAT1=A	10
		D1=D1-	10
		DAT1=C	10	
		GOTO	InsertG!
**********************************************************************
* Insert grob in front
* Stack:	g1 m1 g2 m2 --> g2+g1 M
**********************************************************************
InsertG		GOSUB	InsertG!
		GOVLNG	=Loop
InsertG!	GOSBVL	=SAVPTR
		D1=D1+	15
		A=DAT1	A	g1
		D0=A
		D0=D0+	15
		C=DAT0	A	x1
		D0=D0-	5
		A=DAT0	A
		B=A	A	y1
		D1=D1-	10
		A=DAT1	A	g2
		D0=A
		D0=D0+	15
		A=DAT0	A	x2
		A=A+C	A
		R0=A		x1+x2
		D0=D0-	5
		A=DAT0	A	y2
		D1=D1+	5
		C=DAT1	A
		D0=C
		D0=D0+	5
		C=DAT0	A
		D=C	A	h1
		D1=D1-	10
		C=DAT1	A
		D0=C
		D0=D0+	5
		C=DAT0	A	h2
* Registers:
*	A[A] y2		C[A] h2
*	B[A] y1		D[A] h1
		R1=C		h2
		R2=A		y2
		A=A-C	A	y2-h2
		C=B	A	y1
		C=C-D	A	y1-h1
		?C<=A	A
		GOYES	okdu00
		ACEX	A	mindif
okdu00		CR1EX		h2
		?C<=D	A
		GOYES	okdu01
		CDEX	A
okdu01		A=R1
		A=A+C	A	mins
		A=B-A	A	y1-mins
		C=R2
		A=A+C	A	+y2
		R1=A
		GOSBVL	=makegrob
		D0=D0-	10
		D0=D0-	10
		CD0EX
		RSTK=C
* Put g2
		GOSBVL	=D1=DSKTOP	
		A=DAT1	A
		D0=A
		D0=D0+	5
		A=DAT0	A	h2
		D1=D1+	10
		C=DAT1	A
		D0=C
		D0=D0+	5
		C=DAT0	A	h1
		C=C-A	A
		GONC	okhd00
		C=0	A
okhd00		A=0	A
		R1=A
		R0=C
		D1=D1-	5
		A=DAT1	A	g2
		D0=A
		C=RSTK
		RSTK=C
		D1=C
		GOSUBL	grb!
* Put g1
		GOSBVL	=D1=DSKTOP
		A=DAT1	A
		D0=A
		D0=D0+	5
		A=DAT0	A	h2
		D1=D1+	5
		C=DAT1	A
		D0=C
		D0=D0+	15
		C=DAT0	A	x2
		R1=C
		D1=D1+	5
		C=DAT1	A
		D0=C
		D0=D0+	5
		C=DAT0	A	h1
		B=A	A	h2
		?C<=B	A
		GOYES	okmh00
		B=C	A	maxh	
okmh00		A=A-C	A	h2-h1
		GONC	okdh01
		A=0	A
okdh01		R0=A
		D1=D1+	5
		A=DAT1	A	g1
		D0=A
		C=RSTK
		DAT1=C	A
		D1=C
		C=B	A
		RSTK=C
		GOSUBL	grb!
		GOSBVL	=GETPTR
		D1=D1+	15
		D=D+CON	A,3
		C=RSTK
		R0=C
		GOSBVL	=SAVPTR
		GOVLNG	=PUSH#

**********************************************************************
* Merge grobs in exponent form
* Stack:	g1 m1 g2 m2 --> G H
**********************************************************************
AddExp		GOSUB	AddExp!
		GOVLNG	=Loop
AddExp!		D1=D1+	5
		D=D+1	A
		GOSBVL	=SAVPTR
		A=DAT1	A	g2
		D0=A
		D0=D0+	10
		A=DAT0	A
		B=A	A	y2
		D0=D0+	5
		C=DAT0	A	x2
		D1=D1+	10
		A=DAT1	A	g1
		D0=A
		D0=D0+	10
		A=DAT0	A	y1
		A=A+B	A	y1+y2
		A=A-1	A
		A=A-1	A	y1+y2-2
		R1=A
		D0=D0+	5
		A=DAT0	A
		A=A+C	A	x1+x2
		R0=A
		GOSBVL	=makegrob
		D0=D0-	10
		D0=D0-	10	G
* Put g1
		GOSBVL	=D1=DSKTOP
		C=DAT1	A	g2
		CD0EX
		RSTK=C		G
		D0=D0+	10
		A=DAT0	A	y2
		A=A-1	A
		A=A-1	A	y2-2
		R0=A
		A=0	A
		R1=A
		D1=D1+	10
		A=DAT1	A	g1
		D0=A
		D1=C		G
		GOSUBL	grb!
* Put g2
		GOSBVL	=D1=DSKTOP
		D1=D1+	10
		A=DAT1	A	g1
		C=RSTK		G
		DAT1=C	A	OVERWR!
		D0=A
		D0=D0+	15
		A=DAT0	A	x1
		R1=A
		A=0	A
		R0=A
		D1=D1-	10
		A=DAT1	A	g2
		D0=A
		D1=C
		GOSUBL	grb!
* Push h1+y2-2
		GOSBVL	=GETPTR
		A=DAT1	A
		AD0EX
		D0=D0+	10
		C=DAT0	A	y2
		D0=A
		RSTK=C
		D1=D1+	5
		D=D+1	A
		GOSBVL	=POP#	h1
		C=RSTK
		A=A+C	A
		A=A-1	A
		A=A-1	A	h1+y2-2
		R0=A
		GOSBVL	=SAVPTR
		GOVLNG	=PUSH#
**********************************************************************
* Merge 2 grobs in division form
* Stack:	g1 m1 g2 m2 --> G M
**********************************************************************
AddDiv		GOSUB	AddDiv!
		GOVLNG	=Loop
AddDiv!		D1=D1+	5
		D=D+1	A
		A=DAT1	A
		D1=D1+	5
		D=D+1	A
		DAT1=A	A
		GOSBVL	=SAVPTR
		D0=A
		D0=D0+	15
		A=DAT0	A
		B=A	A	x2
		D0=D0-	5
		C=DAT0	A	y2
		D1=D1+	5
		A=DAT1	A	g1
		D0=A
		D0=D0+	10
		A=DAT0	A	y1
		C=C+A	A
		C=C+1	A
		C=C+1	A	y1+y2+2
		R1=C
		D0=D0+	5
		C=DAT0	A	x1
		?C>=B	A
		GOYES	x3okmx
		CBEX	A
x3okmx		R0=C		maxx
		RSTK=C		maxx
		GOSBVL	=makegrob
		D0=D0-	10
		D0=D0-	10	G

		C=RSTK
		B=C	A	maxx
		RSTK=C
		GOSBVL	=D1=DSKTOP
		A=DAT1	A	g2
		D1=D1+	5
		C=DAT1	A	g1
		CD0EX
		RSTK=C		G maxx
		D1=C		G
		D0=D0+	10
		C=DAT0	A	y1
		D0=A		g2
		C=C+1	A
		C=C+1	A	y1+2
		R0=C		y'
		D0=D0+	15
		A=DAT0	A	x2
		A=B-A	A	maxx-x2
		ASRB.F	A
		R1=A		x'
		D0=D0-	15
		GOSUBL	grb!

		GOSBVL	=D1=DSKTOP
		D1=D1+	5
		C=RSTK		G
		A=DAT1	A	g1
		D1=C		G
		D0=A		g1
		D0=D0+	15
		A=DAT0	A	x1
		D0=D0-	15
		C=RSTK		maxx
		RSTK=C
		C=C-A	A
		CSRB.F	A
		R1=C		x'
		C=0	A
		R0=C		y'
		CD1EX
		RSTK=C		G maxx
		CD1EX
		GOSUBL	grb!
		GOSBVL	=D1=DSKTOP
		D1=D1+	5
		A=DAT1	A	g1
		C=RSTK
		DAT1=C	A	OVERWR!
		D0=A
		D1=C
		D0=D0+	10
		A=DAT0	A	y1
		A=A+1	A
		R0=A		y1+1
		A=A-1	A	y1
		D1=D1+	10
		D1=D1+	10
		C=RSTK		maxx
		D=C	A
		ACEX	A
		GOSBVL	=w->W	W
		GOSBVL	=MUL#	y1*W
		CD1EX
		C=C+B	A
		D1=C
		D=D-1	A	maxx-1
		C=D	A	X
		CSRB.F	A
		CSRB.F	A	X/4
		A=0	W
		A=A-1	W
		GOSBVL	=STUFF
		LC(1)	3
		C=C&D	P
		P=C	0
		LCHEX	0137
		P=C	3
		C=0	B
		CPEX	0
		A=DAT1	B
		A=A!C	P
		DAT1=A	B	
		GOSBVL	=GETPTR
		D1=D1+	5
		D=D+1	A
		GOSBVL	=SAVPTR
		GOVLNG	=PUSH#
**********************************************************************
* Draw square root around grob
* Stack:	g1 m1 --> G M
**********************************************************************
AddSqrt		GOSBVL	=SAVPTR
		D1=D1+	5
		A=DAT1	A
		D0=A
		D0=D0+	10
		A=DAT0	A	y
		A=A+CON	A,3	y+3
		R1=A
		D0=D0+	5
		A=DAT0	A	x
		A=A+CON	A,5	x+5
		R0=A
		GOSBVL	=makegrob
		D0=D0-	10
		D0=D0-	10
		GOSBVL	=D1=DSKTOP
		D1=D1+	5
		C=DAT1	A	g
		CD0EX
		DAT1=C	A	OVERWR!
		D1=C
		RSTK=C
		C=0	A
		LC(1)	2
		R0=C		y'
		LC(1)	4
		R1=C		x'
		GOSUBL	grb!
		C=RSTK
		R0=C		G
		D1=C
		D1=D1+	10
		C=DAT1	A
		D=C	A	y+3
		D1=D1+	5
		A=DAT1	A	w+5
		R2=A
		D1=D1+	5
		GOSBVL	=w->W	W
		B=A	A

*	  +++++++
*	  +
*	  + *****
*	+ + *****
*	 ++ *****
*	  + *****

		D=D-1	A
		D=D-1	A
		LC(1)	4
sqhlp		A=DAT1	B
		A=A!C	P
		DAT1=A	B
		CD1EX
		C=C+B	A
		CD1EX
		D=D-1	A
		GONC	sqhlp
sqllp		CD1EX
		C=C-B	A
		CD1EX
		A=DAT1	B
		A=A!C	P
		DAT1=A	B
		CSRB.F	P
		?C#0	P
		GOYES	sqllp
		C=R0
		D1=C
		D1=D1+	10
		D1=D1+	10	G
		LC(1)	#C
		A=DAT1	B
		A=A!C	P
		DAT1=A	B
		D1=D1+	1
		C=R2		w+5
		C=C-CON	A,5	w	
		D=C	A	w
		CSRB.F	A
		CSRB.F	A	X/4
		A=0	W
		A=A-1	W
		GOSBVL	=STUFF
		LC(1)	3
		C=C&D	P
		P=C	0
		LCHEX	0137
		P=C	3
		C=0	B
		CPEX	0
		A=DAT1	B
		A=A!C	P
		DAT1=A	B	
		GOSBVL	=GETPTR
		GOSBVL	=POP#
		A=A+CON	A,2
		GOVLNG	=push#a
**********************************************************************
* Draw parenthesis or brackets around grob
* Stack:	g1 m1 --> G M
**********************************************************************

sPAR	EQU 5

AddBra		GOSUB	AddBra!
		GOVLNG	=Loop
AddPar		GOSUB	AddPar!
		GOVLNG	=Loop

AddBra!		ST=0	sPAR
		GOTO	+
AddPar!		ST=1	sPAR
+		GOSBVL	=SAVPTR
		D1=D1+	5
		A=DAT1	A
		D0=A
		D0=D0+	10
		A=DAT0	A	y
		R1=A
		D0=D0+	5
		A=DAT0	A	x
		A=A+CON	A,6	x+6
		R0=A
		GOSBVL	=makegrob
		D0=D0-	10
		D0=D0-	10
		GOSBVL	=D1=DSKTOP
		D1=D1+	5
		C=DAT1	A	g
		CD0EX
		DAT1=C	A
		D1=C
		RSTK=C
		C=0	A
		R0=C		y'
		LC(1)	3
		R1=C		x'
		GOSUBL	grb!
		C=RSTK
		R0=C		G
		D1=C
		D1=D1+	10
		A=DAT1	A
		R1=A		y
		D1=D1+	5
		A=DAT1	A	w
		R2=A
		D1=D1+	5
		GOSBVL	=w->W	W
		B=A	A

*	-+ *****+-
*	+  ***** +
*	+  ***** +
*	+  ***** +
*	-+ *****+-
*	   *****

		A=DAT1	B
		LC(1)	2
		?ST=1	sPAR
		GOYES	+
		LC(1)	3
+		A=A!C	P
		DAT1=A	B
		C=R1		y
		C=C-CON	A,4
		D=C	A
		LC(1)	1
lprlp		CD1EX
		C=C+B	A
		CD1EX
		A=DAT1	B
		A=A!C	P
		DAT1=A	B
		D=D-1	A
		GONC	lprlp
		CD1EX
		C=C+B	A
		CD1EX
		A=DAT1	B
		LC(1)	2
		?ST=1	sPAR
		GOYES	+
		LC(1)	3
+		A=A!C	P
		DAT1=A	B

		A=R0		G
		D1=A
		D1=D1+	10
		D1=D1+	10
		C=R1		y
		C=C-CON	A,4
		D=C	A
		A=R2		w
		A=A-CON	A,3
		LC(1)	3
		C=C&A	P
		ASRB.F	A
		ASRB.F	A
		CD1EX
		C=C+A	A
		CD1EX
		P=C	0
		LCHEX	1248
		P=C	3
		C=0	B
		CPEX	0
		A=DAT1	B
		A=A!C	B
		C=C+C	B
		?ST=1	sPAR
		GOYES	+
		A=A!C	B
+		DAT1=A	B
rprlp		CD1EX
		C=C+B	A
		CD1EX
		A=DAT1	B
		A=A!C	B
		DAT1=A	B
		D=D-1	A
		GONC	rprlp
		CD1EX
		C=C+B	A
		CD1EX
		A=DAT1	B
		?ST=1	sPAR
		GOYES	+
		A=A!C	B
+		CSRB.F	B
		A=A!C	B
		DAT1=A	B
		GOVLNG	=GETPTR
RPL

**********************************************************************
*		More function dispatchees
**********************************************************************

**********************************************************************
LABEL addderiv
::
* Count der X's
 CODE
		GOSBVL	=SAVPTR
		GOSBVL	=POP#
		R0=A	A	N
		CD1EX
		D1=C
		C=C+A	A
		A=A+A	A
		A=A+A	A
		C=C+A	A
		D=C	A	->top

		LC(5)	=xDER
		B=0	A	ders
		B=B-1	A
-		B=B+1	A	
		A=DAT1	A
		D1=D1+	5
		?A=C	A
		GOYES	-
		C=D	A
		D1=C		->top
		LC(N)	5+2+2
		CON(5)	=DOIDNT
		CON(2)	1
		NIBASC	'X'
		A=C	W
		D=0	A	xses
		D=D-1	A
-		D=D+1	A
		D1=D1-	5	
		A=DAT1	A
		D0=A
		A=DAT0	5+2+2
		?A=C	W
		GOYES	-
		C=B	A	ders
		?C<=D	A
		GOYES	+
		GOVLNG	=GPPushFLoop
+		R0=C
		GOSBVL	=PUSH#
		GOVLNG	=PushTLoop	
 ENDCODE
 case

* Multiple X derivatives, output ' form
 ::
   DUP
   CODE
		GOLONG	Push1Loop
   ENDCODE
   ZERO_DO (DO)
     plDRPpZparg 2DROP
   LOOP
LABEL dertick
   CODE
		GOLONG	Font8
   ENDCODE
   CODE
		GOLONG	Pop1Loop
   ENDCODE
   NULL$SWAP ZERO_DO (DO) "'" !append$ LOOP
   CODE
		GOLONG	$small
   ENDCODE
   THREE
   CODE
		GOTO	AddExp
   ENDCODE
 ;

* Collect variables
 NULLPAINT ZERO
 BEGIN
    CODE
		D1=D1+	15
		A=DAT1	A
		D1=D1-	15
		LC(5)	=xDER
		?A=C	A
		GOYES	+
+		GOVLNG	=PushT/FLoop
    ENDCODE
 WHILE
   CODE
		GOLONG	Push2Loop
   ENDCODE
   plDRPpZparg DROP
   CODE
		GOLONG	addobj
   ENDCODE
   "\88"
   CODE
		GOLONG	$>font
   ENDCODE
   CODE
		GOSUBL	Insert!
		GOSUBL	Pop2
		GOLONG	InsertG
   ENDCODE
 REPEAT

LABEL derivmerge

 ( Meta G m )
 4PICK TYPEIDNT? case
 ::
   ROTDROP ROT
   CODE
		GOLONG	addobj
   ENDCODE
   "\88"
   CODE
		GOLONG	$>font
   ENDCODE
   CODE
		GOSUBL	Insert!
		GOSUBL	Swap2
		GOTO	AddDiv
   ENDCODE
 ;

 "\88"
 CODE
		GOLONG	ObMisc
 ENDCODE
 CODE
		GOSUBL	Swap2
		GOSUB	AddDiv!
		GOSUBL	Push2
		GOLONG	Font6
 ENDCODE
 CODE
		GOSUBL	Pop2
		GOLONG	InsertG
 ENDCODE
;

**********************************************************************
LABEL addintg
::
 plDRPpZparg				( [Id] [Rest] )
 pshzerpsharg				( [Id] [Expr] [Rest] )
 unroll2ND				( [Rest] [Id] [Expr] )
 CODE
		GOLONG	Font0
 ENDCODE
 ROTDROP ROT				( [Rest] g #mid id )
 CODE
		GOLONG	ObIdnt		( [Rest] g #mid gid #m )
 ENDCODE
 "d"
 CODE
		GOLONG	ObMisc		( [Rest] g #mid gid #m gd #md )
 ENDCODE
 CODE
		GOSUBL	InsertG!	( [Rest] g #mid g' #m' )
		GOSUBL	Swap2
		GOLONG	InsertG		( [Rest] g' #m' )
 ENDCODE
 3PICK #3+ UNROLL OVER #3+ UNROLL

 pshzerpsharg		( I [Hi] [Lo] )

 LABEL intglimit
 ::
   CODE
TstIspc		GOSBVL	=SAVPTR
		LC(5)	=ONE
		A=DAT1	A
		?A#C	A
		GOYES	notIspc
		D1=D1+	5
		A=DAT1	A
		D1=A
		A=DAT1	5+2+2
		C=A	W
		NIBHEX	38
		CON(5)	=DOIDNT
		CON(2)	1
		NIBASC	'?'
		?A#C	W
		GOYES	notIspc
notIspc		GOVLNG	=GPPushT/FLp
   ENDCODE
   NOTcase2drop NULLPAINT
   CODE
		GOLONG	FontIt-
   ENDCODE
   CODE
		D1=D1+	5
		D=D+1	A
		GOLONG	FNT+LOOP
   ENDCODE
 ;
 OVER #2+ UNROLL
 CODE
		A=PC
		LC(5)	(intglimit)-(*)
		A=A+C	A
		PC=(A)
 ENDCODE

 ( g1 h1 g2 g3 )
 DUPGROBDIM 4PICK GROBDIM 8PICK GROBDIM
 ( 10  9  8  7  6  5  4  3  2  1 )
 ( g1 h1 g2 g3 y3 x3 y2 x2 y1 x1 )
 ' NULLLAM 10 NDUPN DOBIND
* Create result grob
 2GETLAM 4GETLAM #+ 6GETLAM #+			( y1+y2+y3 )
 3GETLAM #2+ 5GETLAM #MAX #2/ #1+		( max[x2+2,x3]/2+1 )
 5GETLAM 3GETLAM #MAX #1+ #2/			( [max[x3,x2]+1]/2 )
 1GETLAM #5+					( x1+5 )
 #MAX #+ MAKEGROB
* Bang G1
 10GETLAM OVER
 3GETLAM #2+ 5GETLAM #MAX #2/
 #4+ 6GETLAM GROB!
* Bang G2
 8GETLAM OVER
 3GETLAM #2+ 5GETLAM #MAX #2/ #1+
 3GETLAM #1+ #2/ #-
 2GETLAM 6GETLAM #+ GROB!  
* Bang G3
 7GETLAM OVER
 3GETLAM #2+ 5GETLAM #MAX #2/
 #2+ 5GETLAM #2/ #- ZERO GROB!
* Draw integral
 ( 10  9  8  7  6  5  4  3  2  1 )
 ( g1 h1 g2 g3 y3 x3 y2 x2 y1 x1 )
 2GETLAM #3- ONE MAKEGROB INVGROB		( Ysize = y1-3 )
 OVER
 3GETLAM #2+ 5GETLAM #MAX #2/ #1+		( Xpos = max[x2+2,x3]/2+1 )
 6GETLAM #1+ GROB!				( Ypos = y3+1 )

 GROB E 20000200001020 OVER
 3GETLAM #2+ 5GETLAM #MAX #2/ #2+		( Xpos = max[x2+2,x3]/2+2 )
 6GETLAM GROB!					( Ypos = y3 )
  
 GROB E 20000200001020 OVER
 3GETLAM #2+ 5GETLAM #MAX #2/ #1-		( Xpos = max[x2+2,x3]/2-1 )
 6GETLAM 2GETLAM #+ #3- GROB!			( Ypos = y3+y1-3 )

 9GETLAM 6GETLAM #+				( Mid = h1+y3 )
 ABND 
;
**********************************************************************
LABEL addsum
::
 plDRPpZparg
 pshzerpsharg pshzerpsharg
 DROP ID>$ CHR = >T$
 CODE
		GOLONG	$small
 ENDCODE
 CODE
		GOSUBL	Push1
		GOSUBL	FNT-
		GOLONG	FontIt-
 ENDCODE
 CODE
		GOSUBL	Pop1
		GOSUBL	Insert!
		D1=D1+	5
		D=D+1	A
		GOSUBL	Push1
		GOLONG	Font0
 ENDCODE
 CODE
		GOSUBL	FNT+
		GOSUBL	FNT+
		D1=D1+	5	DROP
		D=D+1	A
		GOLONG	Pop1Loop
 ENDCODE
 GROB 52 2100001000FFFFFFFFD0088100030006000C000810003008100C00060003008100D008FFFFFFFF0000
 ( [Expr] Hi Lo Sig )
 DUPGROBDIM 4PICK GROBDIM 7PICK GROBDIM
 ( 9 8 7 6  5  4  3  2  1 )
 ( H L S Sy Sx Ly Lx Hy Hx )
 ' NULLLAM 9 NDUPN DOBIND
* Make result grob
 2GETLAM 4GETLAM #+ 6GETLAM #+
 1GETLAM 3GETLAM #MAX 5GETLAM #MAX
 MAKEGROB
* Put high limit
 9GETLAM OVER
 DUPGROBDIM SWAPDROP 1GETLAM #- #2/
 ZERO GROB!
 ( 9 8 7 6  5  4  3  2  1  )
 ( H L S Sy Sx Ly Lx Hy Hx )
* Put low limit
 8GETLAM OVER
 DUPGROBDIM SWAPDROP 3GETLAM #- #2/
 2GETLAM 6GETLAM #+ GROB!
* Put Sigma
 7GETLAM OVER
 DUPGROBDIM SWAPDROP 5GETLAM #- #2/
 2GETLAM GROB!
* Output middle
 6GETLAM #2/ 2GETLAM #+
 ABND
 CODE
		GOSUBL	Push2
		GOLONG	Font0
 ENDCODE
 CODE
		GOSUBL	AddPar!
		GOSUBL	Pop2
		GOLONG	InsertG
 ENDCODE
;
**********************************************************************
LABEL addwhere
::
 #2- UNROTDROP #2/
 CODE
		GOLONG	Push1Loop
 ENDCODE

LABEL takeequ
 ::
  TWO TWO SCANAHEAD
  #2- 2DUP #- ROTDROP OVER#2+UNROLL
  ' x= SWAP#1+
  CODE
	GOLONG	FontIt-
  ENDCODE
  CODE
	GOLONG	FNT+LOOP
  ENDCODE
 ;

 CODE
	GOLONG	Pop1Loop
 ENDCODE
 DUP#1= ITE
  DROP					( --> Meta G #M )
  ::
   ONE_DO (DO)
     CODE
		GOSUBL	Push2		Save current grob
		A=PC
		LC(5)	(takeequ)-(*)
		A=A+C	A
		PC=(A)			Fetch next equate
     ENDCODE
     ","
     CODE
		GOSUBL	FNT-		"," must be in the smaller font too!
		GOLONG	Pop2RotFont$
     ENDCODE
     CODE
		GOSUBL	FNT+
		GOLONG	Middle		Merge new equate into old one
     ENDCODE
   LOOP
  ;
 CODE
		GOSUBL	Push2
		GOLONG	Font0
 ENDCODE
 CODE
		GOLONG	Pop2Loop
 ENDCODE
* g1 m1 g2 m2
 DROP DUPGROBDIM
 5PICK GROBDIM
* g1 m1 g2 y2 x2 y1 x1
 ROT#+ #2+ UNROT #+SWAP
 MAKEGROB
* g1 m1 g2 G
 4PICK OVER ZEROZERO GROB!
* g1 m1 g2 G
 SWAP OVER 5PICK GROBDIM #2+
 SWAP GROB!
* g1 m1 G
 SWAP ROT
 CODE
		GOSBVL	=PopASavptr
		D0=A
		D0=D0+	15
		A=DAT0	A
		B=A	A	x
		D1=D1+	5
		C=DAT1	A
		D1=C
		D1=D1+	10
		C=DAT1	A
		D=C	A	h
		D1=D1+	5
		A=DAT1	A	w
		D1=D1+	5
		GOSBVL	=w->W	W
		C=B	A
		CSRB.F	A
		CSRB.F	A	x/4
		AD1EX
		A=A+C	A
		AD1EX
		LC(1)	3
		C=C&B	P
		P=C	0
		LCHEX	1248
		P=C	3
		C=0	A
		CPEX	0	bit
		B=A	A	W
-		A=DAT1	B
		A=A!C	P
		DAT1=A	B
		AD1EX
		A=A+B	A
		AD1EX
		D=D-1	A
		?D#0	A
		GOYES	-
		GOVLNG	=GETPTRLOOP	
 ENDCODE
;
**********************************************************************
LABEL addabs
::
 CODE
		GOSUBL	Pull
		GOLONG	Font0
 ENDCODE
 CODE
AddAbs		GOSBVL	=SAVPTR
		D1=D1+	5
		A=DAT1	A
		D0=A
		D0=D0+	10
		A=DAT0	A	y
		R1=A
		D0=D0+	5
		A=DAT0	A	x
		A=A+CON	A,4	x+4
		R0=A
		GOSBVL	=makegrob
		D0=D0-	10
		D0=D0-	10
		GOSBVL	=D1=DSKTOP
		D1=D1+	5
		C=DAT1	A	g
		CD0EX
		DAT1=C	A
		D1=C
		RSTK=C
		C=0	A
		R0=C		y'
		LC(1)	2
		R1=C		x'
		GOSUBL	grb!
		C=RSTK
		R0=C		G
		D1=C
		D1=D1+	10
		A=DAT1	A
		R1=A		y
		D1=D1+	5
		A=DAT1	A	w
		R2=A
		D1=D1+	5
		GOSBVL	=w->W	W
		B=A	A

*	+ *****+
*	+ *****+
*	+ *****+
*	+ *****+
*	  *****

		C=R1		y
		C=C-CON	A,2
		D=C	A
		LC(1)	1
lablp		A=DAT1	B
		A=A!C	P
		DAT1=A	B
		CD1EX
		C=C+B	A
		CD1EX
		D=D-1	A
		GONC	lablp
		A=R0		G
		D1=A
		D1=D1+	10
		D1=D1+	10
		C=R1		y
		C=C-CON	A,2	y-2
		D=C	A
		A=R2		w
		A=A-CON	A,2	w-2
		LC(1)	3
		C=C&A	P
		ASRB.F	A
		ASRB.F	A
		CD1EX
		C=C+A	A
		CD1EX
		P=C	0
		LCHEX	1248
		P=C	3
		C=0	B
		CPEX	0
rablp		A=DAT1	B
		A=A!C	B
		DAT1=A	B
		CD1EX
		C=C+B	A
		CD1EX
		D=D-1	A
		GONC	rablp
		GOVLNG	=GETPTRLOOP
 ENDCODE
;
**********************************************************************
LABEL addxroot
::
 CODE
		GOLONG	part1font0
 ENDCODE
 CODE
		GOSUBL	PopSave
		GOLONG	FontIt-
 ENDCODE
 CODE
		GOSUBL	FNT+
		D1=D1+	5
		D=D+1	A
		GOSUBL	Pop2
		GOLONG	AddSqrt
 ENDCODE
 DUP EIGHT #MIN
 4PICK GROBDIM DROP
 2DUP#> ITE
   :: 2DROP ZERO ;
   SWAP#-
 DUPUNROT #+SWAP
 4PICK GROBDIMw 4PICK GROBDIM
 ROT#+ #2- UNROTSWAP #+SWAP
 MAKEGROB
 ( g1 g2 mid G )
 ROTOVER DUPGROBDIM 4PICK GROBDIM
 ROTSWAP #- UNROT #- GROB!
 ( g1 mid G )
 ROTOVER ZEROZERO GROB!
 SWAP
;

**********************************************************************
*		Special functions
**********************************************************************
ASSEMBLE
	IF	fFANCYUSER
RPL

* All the special function name dispatchees must leave out the 1st DOCOL

**********************************************************************
LABEL addfp
(::)
  CODE
	D1=D1+	10		2DROP
	D=D+1	A
	D=D+1	A
	GOLONG	FontIt+
  ENDCODE
  CODE
	GOLONG	FNT-LOOP
  ENDCODE
;
**********************************************************************
LABEL addfm
(::)
  CODE
	D1=D1+	10		2DROP
	D=D+1	A
	D=D+1	A
	GOLONG	FontIt-
  ENDCODE
  CODE
	GOLONG	FNT+LOOP
  ENDCODE
;
**********************************************************************
LABEL addIf
(::)
 2DROP splitonearg
 CODE
		GOLONG	Font0
 ENDCODE
 CODE
		GOSUBL	Push2
		GOLONG	Font0
 ENDCODE
 CODE
		GOLONG	Pop2Loop
 ENDCODE
 2SWAP " ,"
 CODE
		GOLONG	$>font
 ENDCODE
 CODE
		GOLONG	Middle
 ENDCODE
;
**********************************************************************
LABEL addGrad
(::)
 2DROP
 CODE
		GOLONG	Font8
 ENDCODE
 GROB 16 6000060000F111A0A04000
 CODE
		GOLONG	Insert
 ENDCODE
;
**********************************************************************
LABEL addDiv
(::)
 2DROP
 CODE
		GOLONG	Font8
 ENDCODE
 GROB 22 60000C0000F1001100A810A81040000000
 CODE
		GOLONG	Insert
 ENDCODE
;
**********************************************************************
LABEL addCurl
(::)
 2DROP
 CODE
		GOLONG	Font8
 ENDCODE
 GROB 22 60000C0000F5401920A010A82044400000
 CODE
		GOLONG	Insert
 ENDCODE
;
**********************************************************************
LABEL addLap
(::)
 2DROP
 CODE
		GOLONG	Font8
 ENDCODE
 GROB 3A C0000A00000C1000100C1004000C10F1001100A000A000400000000000
 EIGHT
 CODE
		GOLONG	InsertG
 ENDCODE
;
**********************************************************************
LABEL addLapT
(::)
 2DROP
 CODE
		GOLONG	Font0
 ENDCODE
 CODE
		GOLONG	AddPar
 ENDCODE
 GROB 3E D0000B00008C0042104A0087000200020002000200C20023002500C8100000
 CODE
		GOLONG	Insert
 ENDCODE
;
**********************************************************************
LABEL addILapT
(::)
 2DROP
 CODE
		GOLONG	Font0
 ENDCODE
 CODE
		GOLONG	AddPar
 ENDCODE
 GROB 3E D0000B00008C0042104A0087000200020002000200C20023002500C8100000
 SIX "-1"
 CODE
itransfirst	GOSUBL	FNT-
		GOLONG	ObMisc
 ENDCODE
 CODE
itranslast	GOSUBL	FNT+
		GOSUBL	AddExp!
		GOLONG	InsertG
 ENDCODE
;
**********************************************************************
LABEL addFouT
(::)
 2DROP
 CODE
		GOLONG	Font0
 ENDCODE
 CODE
		GOLONG	AddPar
 ENDCODE
 GROB 3E D0000D0000C10026802870C80008000C000A200C1008000800440083000000
 CODE
		GOLONG	Insert
 ENDCODE
;
**********************************************************************
LABEL addIFouT
(::)
 2DROP
 CODE
		GOLONG	Font0
 ENDCODE
 CODE
		GOLONG	AddPar
 ENDCODE
 GROB 3E D0000D0000C10026802870C80008000C000A200C1008000800440083000000
 SIX "-1"
 CODE
		GOTO	itransfirst
 ENDCODE
 CODE
		GOTO	itranslast
 ENDCODE
;
**********************************************************************
LABEL addCross
(::)
 2DROP splitonearg
 CODE
		GOLONG	Font7
 ENDCODE
 CODE
		GOSUBL	Push2
		GOLONG	Font7
 ENDCODE
 "\D7"
 CODE
		GOLONG	Pop2RotFont$
 ENDCODE
 CODE
		GOLONG	Middle
 ENDCODE
;
**********************************************************************
LABEL addMul
(::)
 2DROP splitonearg
 CODE
		GOLONG	Font6
 ENDCODE
 CODE
		GOSUBL	Push2
		GOLONG	Font6
 ENDCODE
 CODE
		GOSUBL	Pop2
		GOSUBL	Swap2
		GOLONG	InsertG
 ENDCODE
;
**********************************************************************
LABEL addPM
(::)
 2DROP splitonearg
 CODE
		GOLONG	Font6
 ENDCODE
 CODE
push2font5	GOSUBL	Push2
		GOLONG	Font5
 ENDCODE
 CODE
		GOSUBL	Pop2
choosepm	GOSUBL	GChoose:
		NIBHEX	E1B2071000400004000020702070
		NIBHEX	E1B20B100060000600004040F14040F1
		NIBHEX	E1B20B100060000600004040F14040F1
 ENDCODE
 CODE
		GOLONG	Middle
 ENDCODE
;
**********************************************************************
LABEL addpm
(::)
 2DROP
 CODE
		GOLONG	Font6
 ENDCODE
 CODE
		GOTO	choosepm
 ENDCODE
 CODE
		GOLONG	Insert
 ENDCODE
;
**********************************************************************
LABEL addMP
(::)
 2DROP splitonearg
 CODE
		GOLONG	Font6
 ENDCODE
 CODE
		GOTO	push2font5
 ENDCODE
 CODE
		GOSUBL	Pop2
choosemp	GOSUBL	GChoose:
		NIBHEX	E1B2071000400004000070207020
		NIBHEX	E1B20B10006000060000F14040F14040
		NIBHEX	E1B20B10006000060000F14040F14040
 ENDCODE
 CODE
		GOLONG	Middle
 ENDCODE
;
**********************************************************************
LABEL addmp
(::)
 2DROP
 CODE
		GOLONG	Font6
 ENDCODE
 CODE
		GOTO	choosemp
 ENDCODE
 CODE
		GOLONG	Insert
 ENDCODE
;
**********************************************************************
LABEL addSo
(::)
 2DROP splitonearg
 CODE
		GOLONG	Font4
 ENDCODE
 CODE
push2font4	GOSUBL	Push2
		GOLONG	Font4
 ENDCODE
 GROB 22 60000C00000800EF100020EF1008000000
 CODE
		GOLONG	mergemiddle
 ENDCODE
;
**********************************************************************
LABEL addEqu
(::)
 2DROP splitonearg
 CODE
		GOLONG	Font4
 ENDCODE
 CODE
		GOTO	push2font4
 ENDCODE
 GROB 22 60000010008080CFF12002CFF180800000
 CODE
		GOLONG	mergemiddle
 ENDCODE
;
**********************************************************************
LABEL addPar
(::)
 2DROP
 CODE
		GOLONG	Font0
 ENDCODE
 CODE
		GOLONG	AddPar
 ENDCODE
;
**********************************************************************
LABEL addLt
(::)
 DUP#0=ITE
	DROP
	::
	  CODE
callTop		A=PC
		LC(5)	(addTop)-(*)
		A=A+C	A
		D0=A
		GOVLNG	=Loop
	  ENDCODE
	;

 CODE
		GOSBVL	=POP#
		GOSBVL	=SAVPTR
		A=DAT1	A
		D0=A
		D0=D0+	10
		C=DAT0	A	Y
		D0=D0+	5
		A=DAT0	A	X
		A=A+CON	A,5	X+5
		C=C+CON	A,2	Y+2
		R0=A
		R1=C
		GOSBVL	=makegrob
		A=D0
		LC(5)	20
		A=A-C	A
		AR3EX		->G
		B=A	A	W

		LCHEX	C
		DAT0=C	P
		CD0EX
		C=C+B	A
		CD0EX

		C=R1		Y+2
		C=C-CON	A,4	Y-2
		D=C	A
		DSRB.F	A	(Y-2)/2
		C=C-D	A	Y-2-(Y-2)/2
		LAHEX	2
-		C=C-1	A
		GOC	+
		DAT0=A	P
		CD0EX
		C=C+B	A
		CD0EX
		GONC	-
+		LAHEX	1
		DAT0=A	P
		CD0EX
		C=C+B	A
		CD0EX
		LAHEX	2
-		D=D-1	A
		GOC	+
		DAT0=A	P
		CD0EX
		C=C+B	A
		CD0EX
		GONC	-
+		LCHEX	C
		DAT0=C	P
	
		GOSBVL	=D1=DSKTOP
		A=DAT1	A	->g
		C=R3	A	->G
		DAT1=C	A
		D1=C
		D0=A
		C=0	A
		C=C+1	A
		R0=C		Y
		LC(1)	5
		R1=C		X
		GOSUBL	grb!
		GOSBVL	=D1=DSKTOP
		A=DAT1	A
		D0=A
		D0=D0+	10
		A=DAT0	A
		ASRB.F	A
		GOVLNG	=PUSH#ALOOP
 ENDCODE
;
**********************************************************************
LABEL addRt
(::)
 DUP#0=ITE
	DROP
	::
	  CODE
		GOTO	callTop
	  ENDCODE
	;

 CODE
		GOSBVL	=POP#
		GOSBVL	=SAVPTR
		A=DAT1	A
		D0=A
		D0=D0+	10
		C=DAT0	A	Y
		D0=D0+	5
		A=DAT0	A	X
		A=A+CON	A,5	X+5
		C=C+CON	A,2	Y+2
		R0=A
		R1=C
		GOSBVL	=makegrob
		A=D0
		LC(5)	20
		A=A-C	A
		AR3EX		->G
		B=A	A	W

		C=R1		Y+2
		C=C-CON	A,4	Y-2
		D=C	A
		DSRB.F	A	(Y-2)/2
		C=C-D	A	Y-2-(Y-2)/2
		CDEX	A
		R1=C		(Y-2)/2

		A=R0		X+5
		A=A-CON	A,5	X
		LC(1)	3
		C=C&A	P
		ASRB.F	A
		ASRB.F	A
		CD0EX
		C=C+A	A
		CD0EX
	
		C=C+C	P
		C=C+C	P
		P=C	0
		LCHEX	0806100C20180403	
		P=	0

		A=DAT0	B
		A=A!C	B
		DAT0=A	B	
		AD0EX
		A=A+B	A
		AD0EX
		CSRC
		CSRC

-		D=D-1	A
		GOC	+
		A=DAT0	B
		A=A!C	B
		DAT0=A	B
		AD0EX
		A=A+B	A
		AD0EX
		GONC	-
+		C=C+C	A
		A=DAT0	B
		A=A!C	B
		DAT0=A	B
		CSRB.F	A
		AD0EX
		A=A+B	A
		AD0EX
		CR1EX
		D=C	A
		CR1EX	

-		D=D-1	A
		GOC	+
		A=DAT0	B
		A=A!C	B
		DAT0=A	B
		AD0EX
		A=A+B	A
		AD0EX
		GONC	-
+		CSLC
		CSLC
		A=DAT0	B
		A=A!C	B
		DAT0=A	B
		GOSBVL	=D1=DSKTOP
		A=DAT1	A	->g
		C=R3	A	->G
		DAT1=C	A
		D1=C
		D0=A
		C=0	A
		R1=C		X
		C=C+1	A
		R0=C		Y
		GOSUBL	grb!
		GOSBVL	=D1=DSKTOP
		A=DAT1	A
		D0=A
		D0=D0+	10
		A=DAT0	A
		ASRB.F	A
		GOVLNG	=PUSH#ALOOP
 ENDCODE
;
**********************************************************************
LABEL addVector
(::)
 2DROP
 CODE
		GOLONG	Font0
 ENDCODE
 #4+ SWAP

 DUPGROBDIM 4 #MAX
 SWAP #4+ SWAP MAKEGROB

 DUPUNROT
 2DUP GROBDIMw SWAP GROBDIMw
 #- #2/ FOUR GROB!

 GROB 10 3000020000103010
 OVERDUP GROBDIMw #3- ZERO GROB!

 ONE OVER GROBDIMw #3- MAKEGROB
 INVGROB OVER ZERO ONE GROB!

 SWAP
;
**********************************************************************
LABEL addTop
(::)
 SWAPDROP
 DUP 3PICK #3+ UNROLL
 ZERO_DO (DO)
   splitonearg
   CODE
		GOLONG	Font0
   ENDCODE
   DROP OVER #3+ UNROLL
 LOOP
 DROP

 CODE
formtop		GOSBVL	=POP#
		GOSBVL	=SAVPTR
		R4=A
		B=A	A	Y=N
		B=B+B	A
		B=B+B	A	Y=N*4
		D=0	A	X=0

-		C=DAT1	A
		D1=D1+	5
		D0=C
		D0=D0+	10
		C=DAT0	A	y
		B=B+C	A	Y += y
		D0=D0+	5
		C=DAT0	A	x
		?D>=C	A
		GOYES	+
		D=C	A	X = max x
+		A=A-1	A
		?A#0	A
		GOYES	-

		C=D	A	X
		A=B	A	Y
		C=C+CON	A,4	X+4
		R0=C
		R1=A
		GOSBVL	=makegrob
		D0=D0-	10
		D0=D0-	10

		GOSUB	GetIRAM
		AD0EX
		DAT1=A	A
		D1=D1+	5
		A=R4		N
		DAT1=A	A
		D1=D1+	5
		C=0	A
		DAT1=C	A	j
		D1=D1+	5
		LCHEX	2
		DAT1=C	A	y

		D1=D1-	5
		C=DAT1	A	j

toploop		GOSBVL	=D0=DSKTOP
		AD0EX
		A=A+C	A
		C=C+C	A
		C=C+C	A
		A=A+C	A
		D0=A
		A=DAT0	A
		D0=A		->g
		D1=D1+	5
		C=0	A
		R1=C		x
		C=DAT1	A	y
		R0=C
		D0=D0+	10
		A=DAT0	A	yj
		D0=D0-	10
		C=C+A	A	y+yj
		C=C+CON	A,4
		DAT1=C	A	y'
		D1=D1-	15
		A=DAT1	A	->G
		D1=A
		GOSUBL	grb!
		GOSUB	GetIRAM
		D1=D1+	5
		A=DAT1	A	N
		D1=D1+	5
		C=DAT1	A	j
		C=C+1	A	j++
		DAT1=C	A
		?C<A	A
		GOYES	toploop
		D1=D1-	10
		C=DAT1	A	->G
		R0=C
		GOSBVL	=GETPTR
		C=A	A	N
		D=D+C	A	N++
		AD1EX
		A=A+C	A
		C=C+C	A
		C=C+C	A
		A=A+C	A
		AD1EX
		C=R0
		D1=D1-	5
		D=D-1	A
		DAT1=C	A
		CD0EX
		D0=D0+	10
		A=DAT0	A
		CD0EX
		ASRB.F	A
		GOVLNG	=push#a	

GetIRAM		D1=(5)	(=IRAM@)-4
		C=DAT1	A
		LCHEX	0100
		D1=C
		RTN	
 ENDCODE
;

**********************************************************************
* Add derivative in the tick form
**********************************************************************
LABEL addDer
(::)
   2DROP				( --> MetaFull )
   splitonearg				( --> Meta MetaDer )

   OVER TYPEREAL? ITE
     ::
       OVER %FP %0= NOTcaseFALSE	( Non-integer - show with [] )
       OVER %1 %< caseFALSE		( < 1 - show with [] )
       OVER %4 %<=			( If 1<=der<=4 then use ' )
     ;
     FALSE

   casedrop
   ::
     COERCE				( Meta #n )
     CODE
		GOSUBL	Push1
		A=PC
		LC(5)	(dertick)-(*)
		A=A+C	A
		D0=A
		GOVLNG	=Loop
     ENDCODE
   ;

   CODE
		GOLONG	FontIt-
   ENDCODE
   CODE
		GOLONG	AddPar
   ENDCODE
   CODE
		GOSUBL	Push2		Save 'exponent'
		GOSUBL	FNT+
		GOLONG	Font8
   ENDCODE
   CODE
		GOSUBL	Pop2		Pop exponent
		GOLONG	AddExp
   ENDCODE
;

**********************************************************************
* Add derivative with respect to given expression
**********************************************************************
LABEL addDER
(::)
   2DROP				( --> MetaFull )
   splitonearg				( --> Meta MetaDer )

   CODE
		GOLONG	Font0		( Meta dgrob #mid )
   ENDCODE
   "d"
   CODE
		GOLONG	ObMisc		( Meta dgrob #mid d #m )
   ENDCODE
   OVER 5UNROLL DUP 5UNROLL		( Meta d #m dgrob #mid d #m )
   CODE
		GOSUBL	InsertG!	( Meta d #m G1 #M1 )
		GOSUBL	AddDiv!		( Meta G2 #M2 )
		GOSUBL	Push2		( Meta )
		GOLONG	Font8		( g #m )
   ENDCODE
   CODE
		GOSUBL	Pop2		( g #m G2 #M2 )
		GOLONG	InsertG		( G #M )
   ENDCODE
;
**********************************************************************
* Add partial derivatives with respect to given expressions
**********************************************************************
LABEL addder
(::)
   SWAPDROP					( --> Meta #args )
   NULLPAINT ZERO
   ROT ONE_DO (DO)			( Meta g #m )
	CODE
		GOLONG	Push2Loop
	ENDCODE
	splitonearg
	CODE
		GOLONG	Font7
	ENDCODE
	"\88"
	CODE
		GOLONG	$>font
	ENDCODE
	CODE
		GOSUBL	Insert!
		GOSUBL	Pop2
		GOLONG	InsertG
	ENDCODE
   LOOP
   CODE
		A=PC
		LC(5)	(derivmerge)-(*)
		A=A+C	A
		D0=A
		GOVLNG	=Loop
   ENDCODE
;

**********************************************************************
LABEL fun.n
(::)
   #1-SWAP				( Meta #args-1 name )
   ID>$ DUPLEN$ #2- ONESWAP SUB$ $>ID	( Meta #args-1 name' )
   TWO psh				( #args-1 name' 2 Meta )
   TWO OVER #5+ PICK			( #args-1 name' 2 Meta 2 #args-1 )
   DUP#0=ITE
	:: 2DROP pshzer ;		( #args-1 name' 2 0     Metan )
	:: SCANAHEAD #1- rpnsplit ;	( #args-1 name' 2 Meta' Metan )
   CODE
	GOLONG	FontIt-			( #args-1 name' 2 Meta' ngrob #mid )
   ENDCODE
   CODE
	D1=D1+	5			( #args-1 name' 2 Meta' ngrob )
	D=D+1	A
	GOLONG	FNT+LOOP
   ENDCODE
   SWAP#1+ psh DROPROT
   #1-UNROT 4ROLLSWAP			( Meta #args-1 ngrob name' )
   CODE
	GOLONG	ObIdnt			( Meta #args-1 ngrob grob #mid )
   ENDCODE
   ROTDUP				( Meta #args-1 grob #mid ngrob dummy )
   CODE
	GOLONG	AddSubscript		( Meta #args-1 grob' #mid )
   ENDCODE
   CODE
	GOLONG	addfunargs		( --> g #m )
   ENDCODE
;
**********************************************************************
LABEL fun.nm
(::)
   #2- SWAP				( Meta #args-2 name )
   ID>$ DUPLEN$ #3- ONESWAP SUB$ $>ID	( Meta #args-2 name' )
   TWO psh				( #a-2 n' 2 Meta )
   TWO OVER #5+ PICK			( #a-2 n' 2 Meta 2 #args-2 )
   DUP#0=ITE
	:: 2DROP pshzer ;		( #a-2 n' 2 #0    Metanm )
	:: SCANAHEAD #1- rpnsplit ;	( #a-2 n' 2 Meta' Metanm )
   splitonearg				( #a-2 n' 2 Meta' Metan Metam )
   CODE
	GOLONG	FontIt-			( #a-2 n' 2 Meta' Metan mgrob #mmid )
   ENDCODE
   TWO psh
   CODE
	GOLONG	Font0			( #a-2 n' 2 Meta' mgrob #m 2 ngrob #n )
   ENDCODE
   CODE
	D1=D1+	5			( #a-2 n' 2 Meta' mgrob #m 2 ngrob )
	D=D+1	A
	GOLONG	FNT+LOOP
   ENDCODE
   SWAPDROP THREE roll2ND DROP ROTDROP	( Meta' mgrob #m ngrob #a-2 name' )
   CODE
	GOLONG	ObIdnt			( Meta' mgrob #m ngrob #a-2 grob #g )
   ENDCODE
   6ROLL 6ROLL				( Meta' ngrob #a-2 grob #g mgrob #m )
   4PICK GROBDIMw 5UNROLL		( Meta' ngrob #a-2 #w g #g m #m )
   CODE
	GOLONG	AddExp			( Meta' ngrob #a-2 #w G #M )
   ENDCODE

* Now add the subscript but at x-position #w instead of #W
* Xsize = max(X,w+x)	Xpos = w	Mid = M
* Ysize = Y+y-2		Ypos = Y-2

   5ROLL DUPGROBDIM		( Meta' #a-2 #w G #M n y x )
   5PICK GROBDIM		( Meta' #a-2 #w G #M n y x Y X )
   OVER 5ROLL #+ #2-		( Meta' #a-2 #w G #M n x Y X Y+y-2 )
   8PICK 5ROLL #+ ROT #MAX	( Meta' #a-2 #w G #M n Y Ysize max[w+x,X] )
   MAKEGROB			( Meta' #a-2 #w G #M n Y G' )

   5ROLL OVER			( Meta' #a-2 #w #M n Y G' G G' )
   ZEROZERO GROB!		( Meta' #a-2 #w #M n Y G' )

   ROTOVER			( Meta' #a-2 #w #M Y G' n G' )
   6ROLL 5ROLL #2-		( Meta' #a-2 #M G' n G' w Y-2 )
   GROB!			( Meta' #a-2 #M G' )

   SWAP
   CODE
	GOLONG	addfunargs	( --> g #m )
   ENDCODE
;

**********************************************************************
ASSEMBLE
	ENDIF		* fFANCYUSER
RPL

**********************************************************************
*		Regular Objects
**********************************************************************
ASSEMBLE
addobj		C=0	A
		R0=C.F	A		0-priority		
addobj.1	C=DAT1	A
		CD0EX
		A=DAT0	A
		CD0EX
		GOSUB	obdispatch:
		CON(5)	=DOREAL
		REL(5)	obreal
		CON(5)	=DOIDNT
		REL(5)	obidnt
		CON(5)	=DOLAM
		REL(5)	oblam
		CON(5)	=DOEXT
		REL(5)	obunit
		CON(5)	=DOARRY
		REL(5)	obarry
		CON(5)	=DOLIST
		REL(5)	oblist
		CON(5)	=DOGROB
		REL(5)	obgrob
		CON(5)	=DOROMP
		REL(5)	obromp
		CON(5)	=DOTAG
		REL(5)	obtag
		CON(5)	0
		REL(5)	obmisc
obdispatch:	C=RSTK
		CD0EX
		RSTK=C
-		C=DAT0	A
		D0=D0+	10
		?C=0	A
		GOYES	+
		?A#C	A
		GOYES	-
+		D0=D0-	5
		A=DAT0	A
		C=RSTK
		CD0EX
		A=A+C	A
		PC=(A)
RPL
**********************************************************************
LABEL obtag
::
 'EvalNoCK: xOBJ> $>ID
 CODE
		GOTO	ObIdnt
 ENDCODE
 ROT
 CODE
		GOLONG	FontOb
 ENDCODE
 ": "
 CODE
		GOLONG	$>font
 ENDCODE
 CODE
		GOLONG	Middle
 ENDCODE
;
**********************************************************************
LABEL obreal
::
 CODE					
		A=R0	A		Push false if large priority..
		LC(5)	(PRI7)-(PORG)
		?A<C	A
		GOYES	+
		C=DAT1	A		.. and number is negative
		CD0EX
		D0=D0+	5
		A=DAT0	W
		CD0EX
		?A=0	S
		GOYES	+
+		GOVLNG	=PushT/FLoop
 ENDCODE
 SWAP a%>$
 CODE
		GOTO	ObMisc
 ENDCODE
 ROT NOTcase				( Add parenthesis if needed )
 CODE
		GOLONG	AddPar
 ENDCODE
;
**********************************************************************
LABEL oblam
::
 ID>$
 CODE
	GOTO	ObMisc
 ENDCODE
;
**********************************************************************
CODE
ObMisc	A=PC
	A=A+CON	A,10
	PC=(A)
ENDCODE

LABEL obmisc
::
 CODE
		C=DAT1	A
		CD0EX
		A=DAT0	A
		D0=C
		LC(5)	=DOCSTR
		?A=C	A
		GOYES	+
* Hmm, how to handle this if stack decompile is on?
		LA(5)	=DO>STR
		PC=(A)
+		GOVLNG	=Loop
 ENDCODE

 CODE
		GOLONG	$>font
 ENDCODE

LABEL obgrob
 CODE
pushmid		C=DAT1	A
		CD0EX
		D0=D0+	10
		A=DAT0	A	Y
		CD0EX
		ASRB.F	A	Y/2
		GOVLNG	=push#a
 ENDCODE
;
**********************************************************************
LABEL obromp
::
 palrompdcmp DROP $>ID
 CODE
		GOTO	ObIdnt
 ENDCODE
;
**********************************************************************
LABEL obunit
::
 DUPNULLCOMP? case
 CODE
		GOTO	ObMisc
 ENDCODE

 >R ticR DROP
 DTYPEREAL? ?SKIP
 CODE
		A=PC
		LC(5)	(unitcont)-(*)
		A=A+C	A
		D0=A
		GOVLNG	=Loop
 ENDCODE

 a%>$
 CODE
		GOTO	ObMisc
 ENDCODE

LABEL unitnext

 ticR NOT?SEMI

LABEL unitcont

 DTYPECSTR? case
 ::
   CODE
		GOTO	ObMisc
   ENDCODE
   CODE
unext		A=PC
		LC(5)	(unitnext)-(*)
		A=A+C	A
		D0=A
		GOVLNG	=Loop
   ENDCODE
 ;

 DUPTYPECHAR? case
 ::
    CHR>$
    CODE
		GOTO	ObMisc
    ENDCODE
    CODE
		GOTO	unext
    ENDCODE
 ;

 DUPTYPEREAL? case
 ::
    a%>$
    CODE
		GOTO	ObMisc
    ENDCODE
    CODE
		GOTO	unext
    ENDCODE
 ;

 REQcasedrop umP
 ::
  CODE
		GOSUBL	Swap2
		GOSUBL	InsertG!
		GOTO	unext
  ENDCODE
 ;

 REQcasedrop um*
 ::
  GROB E 20000200001000
  CODE
		GOSUBL	Middle!
		GOTO	unext
  ENDCODE
 ;

 REQcasedrop um/
 ::
  CODE
		GOSUBL	AddDiv!
		GOTO	unext
  ENDCODE
 ;

 REQcasedrop um^
 ::
  CODE
		GOSUBL	AddExp!
		GOTO	unext
  ENDCODE
 ;

 DROP
 "_"
 CODE
		GOLONG	$>font
 ENDCODE
 CODE
		GOSUBL	Middle!
		GOTO	unext
 ENDCODE
;
*********************************
* Check special symbols
* Make dotted part a subindex
* Make trailing digits ..
*********************************
ASSEMBLE
ObIdnt		A=PC
		A=A+CON	A,10
		PC=(A)
RPL

LABEL obidnt
::

 ASSEMBLE
	IF	fFANCYIDS
		CON(5)	=DOCODE
		REL(5)	->obid10

		GOSBVL	=SAVPTR
		GOSUB	D0->ids
		C=DAT1	A
		D1=C
		D1=D1+	5
--		P=	0
		D0=D0+	5
		A=DAT0	B
		?A=0	B
		GOYES	++
		C=DAT1	B
		?A=C	B
		GOYES	+
		?A>C	B
		GOYES	++
-		C=0	A
		C=A	B
		AD0EX
		A=A+C	A
		A=A+C	A
		AD0EX
		D0=D0+	2
		GOSBVL	=SKIPOB
		GONC	--
+		C=C+C	A
		C=C+1	A	
		P=C	0
		A=DAT0	WP
		C=DAT1	WP
		?A#C	WP
		GOYES	-
		CD0EX
		C+P+1
		A=C	A
		P=	0
		GONC	+
++		GOVLNG	=GETPTRLOOP
+		GOSBVL	=GETPTR
		DAT1=A	A
		D0=A
		A=DAT0	A
		LC(5)	=DOGROB
		?A=C	A
		GOYES	+
		LC(5)	=COLA_EVAL
		GOVLNG	=GETPTREVALC
+		D0=D0+	10
		A=DAT0	A
		ASRB.F	A
		R0=A	A
		GOSBVL	=PUSH#
		GOVLNG	(=SEMI)+5
D0->ids		A=PC
		D0=A
		D0=D0+	8
		RTN
 RPL

  ID \9F
  GROB 12 4000060000A05151A0
  ID \9A
  GROB 16 600006000090115151A000
  ID Phi 
  GROB 1A 8000060000E0E0515151E0E000
  ID phi
  GROB 1A 8000070000219252E140502000
  ID Psi
  GROB 1A 800006000040515151E040E000
  ID Cont
  ::
    "\1F"
    CODE
		GOTO	ObMisc
    ENDCODE
  ;
  ID Gamma
  GROB 1A 8000060000F121202020207000
  ID GAMMA
  GROB 1A 8000060000F121202020207000
  ID Nabla
  GROB 16 6000060000F111A0A04000
  ID Blank
  ::
    " "
    CODE
		GOTO	ObMisc
    ENDCODE
  ;
  ID Planck
  GROB 1A 8000060000206030E021212100
ASSEMBLE
	NIBHEX	84E2000			Nullid terminator
->obid10
	ENDIF				* fFANCYIDS
RPL

*********************************
* Check for dot *inside* body
*********************************
 ID>$
 ASSEMBLE
	IF	fFANCYDOT
		CON(5)	=DOCODE
		REL(5)	->iddot
		GOSBVL	=SAVPTR
		A=DAT1	A
		D0=A
		D0=D0+	5
		C=DAT0	A
		D0=D0+	5
		C=C-CON	A,5
		CSRB.F	A	chrs
		D=C	A
		D=D-CON	A,3
		GOC	nodot
		B=0	A	pos
		LCASC	'.'
		B=B+1	A	minpos
-		B=B+1	A	pos++
		D0=D0+	2
		A=DAT0	B
		?A=C	B
		GOYES	gotdot
		D=D-1	A
		GONC	-	
nodot		GOVLNG	=GETPTRLOOP
newrpl:		C=RSTK
		D0=C
		GOVLNG	=Loop
gotdot		A=B	A
		R0=A
		GOSBVL	=PUSH#
		GOSUB	newrpl:
 RPL

   2DUP ONESWAP #1- SUB$
   CODE
		GOTO	ObMisc
   ENDCODE
   2SWAP #1+ MINUSONE SUB$
   $>ID ONE
   CODE
		GOLONG	FontIt-
   ENDCODE
   CODE
		GOSUBL	FNT+
		GOTO	AddSubscript
   ENDCODE
 SEMI
ASSEMBLE
->iddot
	ENDIF			* fFANCYDOT
RPL

*********************************
* No dot in idnt, try digits
*********************************
 CODE
		GOSBVL	=SAVPTR		Count trailing digits
		A=DAT1	A
		D0=A
		D0=D0+	5
		C=DAT0	A
		AD0EX
		A=A+C	A
		D0=A
		B=0	A
		B=B-1	A
isnumlp		B=B+1	A
		D0=D0-	2
		A=DAT0	B
		LCASC	'0'
		?A<C	B
		GOYES	nonnum
		LC(1)	9
		?A<=C	B
		GOYES	isnumlp
nonnum		A=B	A
		GOVLNG	=PUSH#ALOOP
  ENDCODE
  DUP#0=csedrp

* No digits either
  CODE
	GOTO	ObMisc
  ENDCODE

* Found #n digits, check length
  OVERLEN$ OVER#= casedrop
  CODE
		GOTO	ObMisc
  ENDCODE
* Ok to make subindex

  OVER ONE OVERLEN$ 4PICK #- SUB$
  ( $ #n $1 )
  CODE
		GOTO	ObMisc
  ENDCODE
  2SWAP
  ( g1 #mid1 $ #n )
  OVERLEN$ SWAP#- #1+ MINUSONE SUB$
  CODE
		GOSUBL	FNT-
		GOTO	ObMisc
  ENDCODE
  ( g1 #mid1 g2 #mid2 )  
  CODE
		GOSUBL	FNT+
		GOTO	AddSubscript
  ENDCODE
;

( g1 #mid1 g2 #mid2 )
CODE
AddSubscript
	A=PC
	A=A+CON	A,10
	PC=(A)
ENDCODE
::
 DROP
 DUPGROBDIM 5PICK GROBDIM
 ( g1 m1 g2 y2 x2 y1 x1 )
 ROT#+ UNROT #+ #2-
 ( g1 m1 g2 xsize ysize )
 SWAP MAKEGROB
 ( g1 m1 g2 G )
 4PICK OVER ZEROZERO GROB!
 ( g1 m1 g2 G )
 4ROLL GROBDIM SWAP #2-
 ( m1 g2 G x1 y1-2 )
 3PICK 6UNROLL GROB!
 ( G m1 )
;
**********************************************************************
LABEL obarry
::
* Check for 1-2 dimensions
 CODE
		GOSBVL	=SAVPTR
		A=DAT1	A
		D0=A
		D0=D0+	15
		A=DAT0	A
		?A=0	A
		GOYES	+
		C=0	A
		LC(1)	2
		?A>C	A
		GOYES	+
+		GOVLNG	=GPPushT/FLp
 ENDCODE
 case
 CODE
		GOLONG	ObMisc
 ENDCODE

 DUP ARSIZE #1+_ONE_DO (DO)
   INDEX@ OVER GETATELN DROP
   CODE
		GOLONG	FontOb
   ENDCODE
   ROT
 LOOP

 DIMLIMITS INNERCOMP
 DUP #1<> IT DROP
 CODE
		GOTO	FormArry
 ENDCODE
 CODE
		GOLONG	AddBra
 ENDCODE
;
**********************************************************************
LABEL oblist
::

* First test matrix dimensions
* ( list --> list FALSE )		- not 1D or 2D
* ( list --> list #n ZERO TRUE )	- 1D
* ( list --> list #n #m TRUE )		- 2D

 CODE
ListDim		GOSBVL	=SAVPTR

* If lenght = 0 then not matrix

* First count the number of elements

		A=DAT1	A
		D0=A

		B=0	A		elements=0
		D0=D0+	5
-		A=DAT0	A
		LC(5)	=SEMI
		?A=C	A
		GOYES	+
		B=B+1	A		elements++
		GOSBVL	=SKIPOB
		GOTO	-
+

* If zero then NULL{}

		?B=0	A
		GOYES	notmatrix
		A=B	A
		R0=A	A		R0[A] = Y

* Check that all elements are lists or non-lists

		A=DAT1	A
		D0=A
		D0=D0+	5
		A=DAT0	A
		LC(5)	=DOLIST
		?A=C	A
		GOYES	matrix2D?

* 1st element not list - check none of the others is

matrix1D?	A=DAT0	A
		LC(5)	=DOLIST
		?A=C	A
		GOYES	notmatrix	found list - not matrix
		GOSBVL	=SKIPOB
		B=B-1	A
		?B#0	A
		GOYES	matrix1D?
* Passed the 1D test - exit
		C=0	A
		R1=C	A		ZERO
		GOSBVL	=PUSH2#
		GOVLNG	=PushTLoop

* Failed test - not matrix
notmatrix	GOVLNG	=GPPushFLoop

* Test if matrix is 2D

sGOT1st		EQU 5		Flag to indicate got first dimension

matrix2D?	ST=0	sGOT1st		Haven't calculated first X yet
matrix2Dloop	A=DAT0	A
		LC(5)	=DOLIST
		?A#C	A
		GOYES	notmatrix	Non-list element - fail
		C=B	A
		RSTK=C			Save counter

		D0=D0+	5		Count elements and verify no lists
		B=0	A		elements = 0
-		A=DAT0	A
		LC(5)	=SEMI
		?A=C	A
		GOYES	+
		LC(5)	=DOLIST
		?A=C	A
		GOYES	notmatrix
		B=B+1	A		elements++
		GOSBVL	=SKIPOB
		GOTO	-
+		D0=D0+	5		Skip SEMI
		C=RSTK
		CBEX	A		B[A]=Y	C[A]=elements
		CDEX	A		D[A]=elements	C[A]=oldelements
		?ST=0	sGOT1st
		GOYES	matrix2Dnxt	Ignore equal X test - no 2nd X yet
		?C#D	A
		GOYES	notmatrix	Not equal dimensions - fail
matrix2Dnxt	ST=1	sGOT1st		Have 1st X now
		B=B-1	A
		?B#0	A
		GOYES	matrix2Dloop	Loop until passed all elements
		?D=0	A
		GOYES	notmatrix	Fail if 2nd dim == 0
		C=D	A
		R1=C	A		2nd dim
		GOSBVL	=PUSH2#
		GOVLNG	=PushTLoop

 ENDCODE
 NOTcase
 CODE
		GOLONG	ObMisc
 ENDCODE
* {} #y #x

* Handle 1D matrix

 DUP#0= ITE				( {} #n #0 )
 ::
   ROT ZERO NEXTCOMPOB DROP		( #n #0 {} #off ob1 )
   BEGIN
     CODE
		GOLONG	FontOb
     ENDCODE
     6UNROLL 6UNROLL			( ob #mid .. #n #0 {} #off )
     NEXTCOMPOB	
   NOT_UNTIL (UNTIL)
   DROP
 ;
* Handle 2D matrix
 ::
   ROT ZERO NEXTCOMPOB DROP
   BEGIN
    ZERO NEXTCOMPOB DROP
    BEGIN
     CODE
		GOLONG	FontOb
     ENDCODE
     8UNROLL 8UNROLL
     NEXTCOMPOB
    NOT_UNTIL (UNTIL)
    DROP NEXTCOMPOB
  NOT_UNTIL (UNTIL)
  DROP
 ;

 DUP#0= IT #1+				( Fix #0 to #1 )

 CODE
		GOTO	FormArry
 ENDCODE
 CODE
		GOLONG	AddPar
 ENDCODE
;


**********************************************************************
* Build array from grobs
* g1 m1 .. gn mn #y #x --> g m
**********************************************************************
ASSEMBLE

FormArry

		ABASE	0
GRB		ALLOC	5	FIRST!
XSIZE		ALLOC	5
YSIZE		ALLOC	5
ROW		ALLOC	5
COLUMN		ALLOC	5
XPOS		ALLOC	5
YPOS		ALLOC	5
STKPOS		ALLOC	5
ROWMID		ALLOC	5
ROWBOT		ALLOC	5

		GOSBVL	=POP2#
		R3=A	A	Y
		R2=C	A	X
		GOSBVL	=SAVPTR
	
		D1=(5)	(=IRAM@)-4
		C=DAT1	A
		LC(4)	#100
		R4=C	A
		D1=C
		D1=(2)	XSIZE
		C=R2
		DAT1=C	A
		D1=(2)	YSIZE
		C=R3
		DAT1=C	A

* Calculate resulting grob size

* Xsize first

		A=R2	A	X
		C=A	A
		C=C+C	A
		C=C+C	A	4X
		R0=C		gaps
-		GOSUB	ColumnSize
		C=R0	A
		C=C+A	A
		R0=C	A
		A=R2	A
		A=A-1	A
		R2=A	A
		?A#0	A
		GOYES	-

* Ysize next
	
		A=R3	A	X
		C=A	A
		C=C+C	A
		C=C+C	A	4Y
		R1=C		gaps
-		GOSUB	RowSize
		C=R1	A
		C=C+A	A
		C=C+B	A
		R1=C	A
		A=R3	A
		A=A-1	A
		R3=A	A
		?A#0	A
		GOYES	-
* Create grob
		GOSBVL	=makegrob
		D0=D0-	10
		D0=D0-	10
		C=R4	A
		CD0EX
		DAT0=C	A	GRB
* Start banging grobs
		C=R4	A
		D1=C
		C=0	A
		LC(1)	1
		D1=(2)	ROW
		DAT1=C	A
		LC(1)	4/2
		D1=(2)	YPOS
		DAT1=C	A

		D1=(2)	XSIZE
		A=DAT1	A
		D1=(2)	YSIZE
		C=DAT1	A
		GOSBVL	=MUL#
		A=B	A
		A=A+A	A
		A=A+A	A
		B=B+A	A
		B=B+B	A	10XY
		GOSBVL	=D0=DSKTOP
		A=A+B	A
		D1=(2)	STKPOS
		DAT1=A	A
newrow		C=R4	A
		D1=C
		D1=(2)	ROW
		A=DAT1	A
		GOSUB	RowSize
		C=R4	A
		RSTK=C
		D1=C
		D1=(2)	ROWMID
		DAT1=A	A
		D1=(2)	ROWBOT
		A=B	A
		DAT1=A	A
		D1=(2)	COLUMN
		C=0	A
		LC(1)	1
		DAT1=C	A
		D1=(2)	XPOS
		LC(1)	4/2
		DAT1=C	A

newcol		C=RSTK
		R4=C.F	A
		RSTK=C
		D0=C
		D0=(2)	COLUMN
		A=DAT0	A
		GOSUB	ColumnSize
		R2=A	A	XMAX
		D0=(2)	XPOS
		C=DAT0	A
		R1=C	A
		C=C+A	A
		C=C+CON	A,4
		DAT0=C	A
		D0=(2)	YPOS
		C=DAT0	A
		R0=C	A
		D0=(2)	ROWMID
		C=DAT0	A
		D=C	A
		D0=(2)	STKPOS
		C=DAT0	A
		C=C-CON	A,10
		DAT0=C	A
		D1=C
		D1=D1+	5
		A=DAT1	A	->grob
		D1=D1-	5
		C=DAT1	A
		D1=C
		D1=D1+	5
		C=DAT1	A	mid
		D=D-C	A	MID-mid
		C=R0	A
		C=C+D	A
		R0=C	A	ypos
		D0=(2)	GRB
		C=DAT0	A
		D1=C
		D0=A
		D0=D0+	15
		A=DAT0	A	x
		D0=D0-	15
		C=R2	A	XMAX
		C=C-A	A
		CSRB.F	A
		A=R1	A
		A=A+C	A
		R1=A
		GOSUBL	grb!
	
		C=RSTK
		RSTK=C
		D0=C
		D0=(2)	XSIZE
		A=DAT0	A
		D0=(2)	COLUMN
		C=DAT0	A
		C=C+1	A
		DAT0=C	A
		?C>A	A
		GOYES	+
		GOTO	newcol

+		C=RSTK
		R4=C	A	->data
		D0=(2)	ROWMID
		A=DAT0	A
		D0=(2)	ROWBOT
		C=DAT0	A
		A=A+C	A
		A=A+CON	A,4
		D0=(2)	YPOS
		C=DAT0	A
		C=C+A	A
		DAT0=C	A
		D0=(2)	YSIZE
		A=DAT0	A
		D0=(2)	ROW
		C=DAT0	A
		C=C+1	A
		DAT0=C	A
		?C>A	A
		GOYES	+
		GOTO	newrow

+		D0=(2)	GRB
		A=DAT0	A
		R0=A	A	->GROB
		D0=(2)	XSIZE
		A=DAT0	A
		D0=(2)	YSIZE
		C=DAT0	A
		GOSBVL	=MUL#	XY
		A=B	A
		GOSBVL	=GETPTR
		C=D	A
		C=C+A	A
		D=C	A
		CD1EX
		A=A+A	A	2XY
		C=C+A	A
		A=A+A	A
		A=A+A	A	8XY
		C=C+A	A
		CD1EX
		A=R0	A	->AGROB
		D1=D1-	5
		D=D-1	A
		DAT1=A	A
		GOSBVL	=SAVPTR
		D0=A
		D0=D0+	10
		A=DAT0	A	Y
		ASRB.F	A
		GOVLNG	=PUSH#ALOOP

*********************************
* A[A]  = column (>=1)
* R4[A] = ->data
* Out:
* A[A]  = maxx
*********************************
ColumnSize
		C=A	A
		C=C+C	A
		C=C+C	A
		A=A+C	A
		A=A+A	A	10i

		C=R4	A
		D1=C
		D1=(2)	YSIZE
		C=DAT1	A
		D=C	A	Y
		D1=(2)	XSIZE
		C=DAT1	A
		B=C	A
		C=C+C	A
		C=C+C	A
		B=B+C	A
		B=B+B	A	10X	

		GOSBVL	=D1=DSKTOP
		C=C+B	A
		C=C-A	A
		D1=C
		D1=D1+	5	->grob
		A=0	A	maxx

-		C=DAT1	A
		CD1EX
		RSTK=C
		D1=D1+	15
		C=DAT1	A	x
		?A>=C	A
		GOYES	+
		A=C	A	new maxx
+		C=RSTK
		C=C+B	A
		D1=C
		D=D-1	A
		?D#0	A
		GOYES	-
		RTN	
*********************************
* A[A]  = row (>=1)
* R4[A] = ->data
* Out:
* A[A]  = maxmid
* B[A]  = maxbot
*********************************
RowSize
		C=R4	A
		D1=C
		D1=(2)	=YSIZE
		C=DAT1	A
		C=C-A	A	Y-i
		D1=(2)	=XSIZE
		A=DAT1	A	X
		GOSBVL	=MUL#
		C=DAT1	A
		RSTK=C
		GOSBVL	=D1=DSKTOP
		A=B	A
		A=A+A	A
		A=A+A	A
		A=A+B	A
		A=A+A	A
		C=C+A	A
		D1=C
			
		A=0	A	maxmid
		B=0	A	maxbot
-		C=DAT1	A
		CD1EX
		RSTK=C
		D1=D1+	5
		C=DAT1	A	mid
		?A>=C	A
		GOYES	+
		A=C	A	maxmid'
+		D=C	A	mid
		C=RSTK
		D1=C
		D1=D1+	5
		C=DAT1	A
		CD1EX
		RSTK=C
		D1=D1+	10
		C=DAT1	A	y
		C=C-D	A	bot
		?B>=C	A
		GOYES	+
		B=C	A	maxbot'
+		C=RSTK
		D1=C
		D1=D1+	5
		C=RSTK
		C=C-1	A
		RSTK=C
		?C#0	A
		GOYES	-
		C=RSTK
		RTN

**********************************************************************
*		Miscellaneous Utilities
**********************************************************************

**********************************************************************
* Fast grob! replacement
**********************************************************************

g1ST		EQU 0
MAXPIX		EQU 15*4-3

*GRB!		GOSBVL	=POP2#
*		R0=C
*		R1=A	
*		C=DAT1	A
*		RSTK=C		->target
*		D1=D1+	5
*		D=D+1	A
*		GOSBVL	=PopASavptr
*		D0=A
*		C=RSTK
*		D1=C
*		GOSUB	grb!
*		GOVLNG	=GETPTRLOOP

* Input:
* R0[A]=Y	D0=->src
* R1[A]=X	D1=->target

grb!
		AR0EX.F	A
		AR1EX.F	A
		AR0EX.F	A

		D0=D0+	5
		C=DAT0	A
		C=C-CON	A,16
		RTNC
		D0=D0+	5
		C=DAT0	A
		?C=0	A
		RTNYES
		D0=D0+	5
		A=DAT0	A
		?A=0	A
		RTNYES
		D0=D0+	5
		B=A	A	gx
		C=C-1	A	lines-1
		R4=C.F	A
		D1=D1+	5
		C=DAT1	A
		C=C-CON	A,16
		RTNC
		D1=D1+	5
		C=DAT1	A
		?C=0	A
		RTNYES
		D1=D1+	5
		A=DAT1	A
		?A=0	A
		RTNYES
		D1=D1+	5
		GOSBVL	=w->W
		R2=A.F	A	GW
* R0 X
* R1 Y
* R2 GW
* R4 lines-1
* B  gx
		C=R1.F	A	Y
--		SB=0		Skip top of G
		CSRB.F	A
		?SB=0
		GOYES	+
		CD1EX
		C=C+A	A
		CD1EX
+		A=A+A	A
		?C#0	A
		GOYES	--
		A=B	A
		GOSBVL	=w->W
		R1=A.F	A	gW
* R0 X
* R1 gW
* R2 GW
* R4 lines-1
* B  gx

		C=R0.F	A	X
		CSRB.F	A
		CSRB.F	A
		AD1EX
		A=A+C	A	GSKIP
		AD1EX

		LC(5)	MAXPIX
		?B<C	A		THIS WAS BUGGED (<=)
		GOYES	g!xs
		GOTO	g!xl
*********************************
* Narrow grob to bang
*********************************
g!xs		C=0	A
		C=R0.F	P	X
		CBIT=0	3
		CBIT=0	2	X&3
		B=B+C	A	gx+shift

		P=C	0	X&3
		C=P	15	shift
		LCHEX	0137
		P=C	3
		C=P	0	mask1
		P=	0
		A=B	B
		B=0	W
		B=C	P	mask
		C=A	B
		SB=0
		CSRB.F	B
		?SB=0
		GOYES	g!x0
g!x1		SB=0
		CSRB.F	B
		P=C	0	WP
		?SB=0
		GOYES	g!01
g!11		LCHEX	8
		GONC	+
g!01		LCHEX	E
		GOC	+
g!x0		CSRB.F	B
		P=C	0	WP
		?SB=0
		GOYES	g!00
g!10		LCHEX	C
		GONC	+
g!00		LCHEX	F
+		B=B!C	P	mask
		C=R4.F	A	lines-1
		D=C	A
--		A=DAT0	WP
		A=C	S	shift
		A=A-1	S
		GOC	+
		A=A+A	WP
		A=A-1	S
		GOC	+
		A=A+A	WP
		A=A-1	S
		GOC	+
		A=A+A	WP
+		C=DAT1	WP
		C=C&B	WP
		B=-B-1	WP
		A=A&B	WP
		B=-B-1	WP
		A=A!C	WP
		DAT1=A	WP
		C=R1.F	A	gW
		AD0EX
		A=A+C	A
		D0=A
		C=R2.F	A	GW
		AD1EX
		A=A+C	A
		D1=A
		D=D-1	A
		GONC	--	
		P=	0
		RTNCC
*********************************
* Wide grob to bang
*********************************
* R0 X
* R1 gW
* R2 GW
* R4 lines-1
* B  gx

g!xl		C=B	A	gx
		CR0EX.F	A
		D=C	A	X
		C=0	A
		LC(1)	3
		C=C&D	P
		D=C	P	X&3
		B=B+C	A	gx+(X&3)
		BSRB.F	A
		BSRB.F	A
		C=R1.F	A
		C=C-B	A	gskip
		R1=C.F	A
		C=R2.F	A
		C=C-B	A	Gskip
		R2=C.F	A
		C=D	P
		CSRC		shift
		C=R0.F	P	gx&F
		C=C+D	P
		CBIT=0	3
		CBIT=0	2
		P=C	0
		LCHEX	FEC8
		P=C	3
		C=P	1
		D=C	B	mask2
		P=C	15
		LCHEX	0137
		P=C	3
		C=P	0
		P=	0
		D=C	P	mask1

g!line		A=R0.F	A	lpix
		ST=1	g1ST
g!next		R3=A.F	A	pix
		C=0	A
		LC(1)	4
		?A<C	A
		GOYES	g!last
		B=A	A
		BSRB.F	A
		BSRB.F	A	nibs
		LC(1)	#E
		?B<C	A
		GOYES	+
		B=C	A
+		A=DAT0	W
		GOSUB	gshift
		C=B	P
		P=C	0
		P=P-1		nibs-1
		B=B+B	B
		B=B+B	B	4*nibs
* Mask if start of line
		?ST=0	g1ST
		GOYES	+
		CPEX	15
		C=P	0
		CPEX	15
		B=B-C	B
		C=DAT1	1
		C=C&D	B
		A=A!C	B
		?C=0	S
		GOYES	+
		D0=D0-	1
+		DAT1=A	WP
		CD1EX
		C+P+1
		D1=C
		CD0EX
		C+P+1
		D0=C
		P=	0
		ST=0	g1ST
		A=R3.F	A
		A=A-B	A
		GOTO	g!next
g!last		?A=0	A
		GOYES	+
		A=DAT0	B
		GOSUB	gshift
		B=A	P
		C=D	B
		CSR	B
		A=DAT1	B
		A=A&C	P
		C=-C-1	P
		B=B&C	P
		A=A!B	P
		DAT1=A	P
+		A=R4.F	A
		A=A-1	A
		RTNC
		R4=A.F	A
		C=R1.F	A
		AD0EX
		A=A+C	A
		AD0EX
		C=R2.F	A
		AD1EX
		A=A+C	A
		AD1EX
		?C=0	S
		GOYES	+
		D0=D0+	1
+		GOTO	g!line
gshift		?C=0	S
		RTNYES
		P=C	15
		?ST=1	g1ST
		GOYES	+
-		ASRB
		P=P+1
		?P#	4
		GOYES	-
		P=	0
		RTNCC
+
-		A=A+A	W
		P=P-1
		?P#	0
		GOYES	-
		RTNCC

**********************************************************************
*		General $ to grob conversion using FNT
**********************************************************************

$>font		CD0EX
		D0=(5)	(=IRAM@)-4
		A=DAT0	A
		D0=A
		D0=(4)	FNT
		A=DAT0	A		A[A] = font size
		D0=C

		A=A+A	A		If size < 0 then use small font
		GOC	$small
		ASRB.F	A

		A=A-CON	A,2		1 --> -1
		GOC	$small
		A=A-1	A		2 --> -1
		GONC	$big
		GOTO	$med

*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* Desc:		Convert $ to large font grob
* Stack:	( $ --> grob )
* Notes:	UFL has no standard large font
*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
$big		GOVLNG	(=$>BIGGROB)+5

*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* Desc:		Convert $ to small font grob using UFL small font
* Stack:	( $ --> grob )
* Notes:	Requires LAM #FNT1
*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

$small		GOSBVL	=SAVPTR
		LC(N)	12
		CON(2)	5
		NIBASC	'#FNT1'
		GOSUB	SearchLam
*		GOC	fonterror		Should never happen

		R4=A				R4[A] = ->fnt1

		GOSBVL	=GetStrLenStk
		C=C+C	A
		C=C+C	A
		R0=C				R0[A] = X-size = 4*chars
		C=0	A
		LC(1)	6
		R1=C				R1[A] = Y-size = 6 pixels
		GOSBVL	=makegrob		R3[A] = rownibbles
		AD0EX
		D0=A
		LC(5)	#14
		A=A-C	A
		R0=A				R0[A] = ->grob

		GOSBVL	=D1=DSKTOP
		GOSBVL	=GetStrLenStk
		D=C	A			D[A] = chars
		C=R3
		B=C	A			B[A] = rownibbles
--		D=D-1	A			D[A] = chars--
		GOC	fontok
		A=0	A
		A=DAT1	B			A[A] = char
		D1=D1+	2
		C=R4				C[A] = ->fnt1
		A=A+A	A
		C=C+A	A
		A=A+A	A
		C=C+A	A			C[A] = ->fnt1 + 6*char
		CD0EX
		A=DAT0	6			A[0-5] = chardata
		D0=C
-		DAT0=A	P			Write chardata
		AD0EX
		A=A+B	A
		AD0EX
		P=P+1
		?P#	6
		GOYES	-
		P=	0
		D0=C
		D0=D0+	1
		GONC	--
fontok		GOVLNG	=GPOverWrR0Lp

*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* Desc:		Convert $ to small font grob using UFL small font
* Stack:	( $ --> grob )
* Notes:	Requires LAM #FNT2
*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

$med		GOSBVL	=SAVPTR
		LC(N)	12
		CON(2)	5
		NIBASC	'#FNT2'
		GOSUB	SearchLam
*		GOC	fonterror		Should never happen

		R4=A	A			R4[A] = ->fnt2

		GOSBVL	=GetStrLenStk
		A=C	A
		A=A+A	A
		C=C+A	A
		C=C+C	A
		R0=C				R0[A] = X-size = 6*chars
		C=0	A
		LC(1)	8
		R1=C				R1[A] = Y-size = 8 pixels
		GOSBVL	=makegrob		R3[A] = rownibbles
		AD0EX
		D0=A
		LC(5)	#14
		A=A-C	A
		R0=A				R0[A] = ->grob

		GOSBVL	=D1=DSKTOP
		GOSBVL	=GetStrLenStk
		D=C	A			D[A] = chars
		C=R3
		B=C	A			B[A] = rownibbles
		ST=0	0			Flag even X-position
--		D=D-1	A			D[A] = chars--
		GOC	fontok
		A=0	A
		A=DAT1	B			A[A] = char
		D1=D1+	2
		C=R4				C[A] = ->fnt2
		ASL	A
		C=C+A	A			C[A] = ->fnt2 + 16*char
		CD0EX
		A=DAT0	W			A[W] = chardata
		D0=C
		?ST=1	0
		GOYES	+
		P=	16-8			Handle even X-position
-		DAT0=A	B
		ASR	W
		ASR	W
		AD0EX
		A=A+B	A
		AD0EX
		P=P+1
		GONC	-
		D0=C
		D0=D0+	1
		ST=1	0
		GONC	--

+		RSTK=C				Handle odd X-position
		A=A+A	W
		A=A+A	W
		P=	16-8
-		C=DAT0	B
		?CBIT=0	0
		GOYES	+
		ABIT=1	0
+		?CBIT=0	1
		GOYES	+
		ABIT=1	1
+		DAT0=A	B
		ASR	W
		ASR	W
		AD0EX
		A=A+B	A
		AD0EX
		P=P+1
		GONC	-
		C=RSTK
		D0=C
		D0=D0+	2
		ST=0	0
		GOTO	--

*---------------------------------------------------------------------
* Search for the 5 character LAM name given in C[W] with length field
*---------------------------------------------------------------------
SearchLam	D=C	W		D[W] = lam name
		D0=(5)	=aTEMPENV
		A=DAT0	A
		D0=A
		A=DAT0	A
--		D0=A			D0 = ->tempenv1
		C=DAT0	A
		?C=0	A
		RTNYES			CS: Font not found
		A=A+C	A
		B=A	A		B[A] = ->nextenv
-		D0=D0+	10		Skip to next LAM name address
		A=B	A		A[A] = ->nextenv
		CD0EX			C[A] = ->current ptr
		?C>=A	A		If searched this env then switch
		GOYES	--		to the next one
		CD0EX
		A=DAT0	A		A[A] = ->LAM
		AD0EX
		D0=D0+	5
		C=DAT0	12		C[W] = LAM start
		D0=A
		?C#D	W
		GOYES	-		Loop until match or next env
		D0=D0+	5		Skip to address part
		A=DAT0	A		A[A] = ->FNT
		A=A+CON	A,10		A[A] = ->FNT body
		RTNCC			CC: Found font
**********************************************************************
->AGrobEnd
**********************************************************************
RPL

[ RETURN TO DIRECTORY ]