From c1aa4afa4c41614a7aa0b1fb4e596a28911748e8 Mon Sep 17 00:00:00 2001
From: David Schmenk <dschmenk@gmail.com>
Date: Fri, 30 May 2014 22:54:41 -0700
Subject: [PATCH] Apple /// PLASMA WIP

---
 src/class.pla   |   31 --
 src/cmd.pla     |   24 +-
 src/cmdstub.s   |    1 +
 src/codegen.c   |    2 +-
 src/hello.pla   |    8 -
 src/makefile    |   69 ++-
 src/plvm02.s    |   38 +-
 src/plvm03.s    | 1016 ++++++++++++++++++++++++++++++++++++
 src/soscmd.pla  | 1306 +++++++++++++++++++++++++++++++++++++++++++++++
 src/testcls.pla |   32 --
 10 files changed, 2384 insertions(+), 143 deletions(-)
 delete mode 100755 src/class.pla
 delete mode 100644 src/hello.pla
 create mode 100644 src/plvm03.s
 create mode 100644 src/soscmd.pla
 delete mode 100755 src/testcls.pla

diff --git a/src/class.pla b/src/class.pla
deleted file mode 100755
index d5c7b43..0000000
--- a/src/class.pla
+++ /dev/null
@@ -1,31 +0,0 @@
-;
-; Declare all imported modules and their data/functions.
-;
-import stdlib
-  predef putc, puts
-end
-import testcls
-  word print
-  const dec = 0
-  const hex = 2
-end
-
-byte spaces[] = "   "
-
-def putln
-  putc($0D)
-end
-
-def printnums
-  word i
-  i = 10000
-  repeat
-    print:dec(i)
-    puts(@spaces)
-    print:hex(i)
-    putln
-    i = i / 10
-  until i == 0
-end
-printnums
-done
diff --git a/src/cmd.pla b/src/cmd.pla
index c13fe94..fd503a9 100644
--- a/src/cmd.pla
+++ b/src/cmd.pla
@@ -167,11 +167,9 @@ asm syscall
 	INX
 	LDA	ESTKL,X
 	STA	CMD
-	STX	ESP
 	JSR	$BF00
 CMD:	!BYTE	00
 PARAMS:	!WORD	0000
-	LDX	ESP
 	STA	ESTKL,X
 	LDY    	#$00
 	STY	ESTKH,X
@@ -1018,7 +1016,7 @@ def adddef(bank, addr, deflast)
 end
 def loadmod(mod)
     word refnum, rdlen, modsize, bytecode, defofst, defcnt, init, fixup
-    word addr, defaddr, modaddr, modfix
+    word addr, defaddr, modaddr, modfix, modend
     word deftbl, deflast
     word moddep, rld, esd, sym
     byte defbank, str[16], filename[64]
@@ -1048,7 +1046,7 @@ def loadmod(mod)
 	    ; Load module dependencies.
 	    ;
             while ^moddep
-                if lookupmod(moddep) == 0
+                if !lookupmod(moddep)
 		    close(refnum)
 	            refnum = 0
                     if loadmod(moddep) < 0
@@ -1063,7 +1061,7 @@ def loadmod(mod)
 	    deftbl   = allocheap(defcnt * 5 + 1)
 	    deflast  = deftbl
 	    ^deflast = 0
-	    if refnum == 0
+	    if !refnum
 	        ;
 		; Reset read pointer.
 		;
@@ -1094,9 +1092,10 @@ def loadmod(mod)
 	;
         modfix   = modaddr - modfix
         bytecode = defofst + modfix - MODADDR
-        rld      = modaddr + modsize ; Re-Locatable Directory
-        esd      = rld               ; Extern+Entry Symbol Directory
-        while ^esd <> $00 ; Scan to end of ESD
+        modend   = modaddr + modsize
+        rld      = modend  ; Re-Locatable Directory
+        esd      = rld     ; Extern+Entry Symbol Directory
+        while !^esd        ; Scan to end of ESD
             esd = esd + 4
 	loop
         esd = esd + 1
@@ -1106,6 +1105,7 @@ def loadmod(mod)
 	if ^MACHID & $30 == $30
 	    defbank = 1
 	    defaddr = allocxheap(rld - bytecode)
+	    modend  = bytecode
 	else
 	    defbank  = 0
 	    defaddr = bytecode
@@ -1173,11 +1173,11 @@ def loadmod(mod)
 	    ; Move bytecode to AUX bank.
 	    ;
 	    memxcpy(0, defaddr, bytecode, modsize - (bytecode - modaddr))
-	    ;
-	    ; Free up the bytecode in main memory.
-	    ;
-	    releaseheap(bytecode)
        	fin
+	;
+	; Free up the end-of-module in main memory.
+	;
+	releaseheap(modend)
     else
         perr = perr | 0x100
         return -perr
diff --git a/src/cmdstub.s b/src/cmdstub.s
index 6696417..6baa674 100644
--- a/src/cmdstub.s
+++ b/src/cmdstub.s
@@ -1,3 +1,4 @@
+INTERP	=	$03D0
 ;*
 ;* MOVE CMD DOWN TO $1000-$2000
 ;*
diff --git a/src/codegen.c b/src/codegen.c
index bb5ec00..5a48b96 100755
--- a/src/codegen.c
+++ b/src/codegen.c
@@ -509,7 +509,7 @@ void emit_def(char *name, int is_bytecode)
     {
         //printf("%s%c\n", name, LBL);
         if (is_bytecode)
-            printf("\tJSR	$03D0\n");
+            printf("\tJSR\tINTERP\n");
     }
 }
 void emit_codetag(int tag)
diff --git a/src/hello.pla b/src/hello.pla
deleted file mode 100644
index 36f52b6..0000000
--- a/src/hello.pla
+++ /dev/null
@@ -1,8 +0,0 @@
-import STDLIB
-    predef puts
-end
-
-byte hellostr[] = "Hello, world.\n"
-
-puts(@hellostr)
-done
diff --git a/src/makefile b/src/makefile
index da8c635..b204b4f 100755
--- a/src/makefile
+++ b/src/makefile
@@ -2,8 +2,11 @@
 AFLAGS	= -o $@
 LFLAGS	= -C default.cfg
 PLVM    = plvm
-PLVM02	= PLVM02.SYS
-CMD     = CMD.SYS
+PLVM02	= PLASMA.SYSTEM\#FF2000
+PLVM03	= SOS.INTERP\#050000
+CMD     = CMD\#FF2000
+ROD     = ROD\#FE1000
+HGR1    = HGR1\#FE1000
 PLASM   = plasm
 INCS    = tokens.h symbols.h lex.h parse.h codegen.h
 OBJS	= plasm.c parse.o lex.o codegen.o
@@ -17,15 +20,15 @@ TXTTYPE	= .TXT
 #
 # Image filetypes for CiderPress
 #
-#PLATYPE	= \#ed0000
+#RELTYPE	= \#fe1000
 #BINTYPE	= \#060000
-#SYSTYPE	= \#ff0000
+#SYSTYPE	= \#ff2000
 #TXTTYPE	= \#040000
 
-all: $(PLASM) $(PLVM) $(PLVM02) $(CMD) TESTLIB ROD.REL
+all: $(PLASM) $(PLVM) $(PLVM02) $(PLVM03) $(CMD) $(ROD) $(HGR1)
 
 clean:
-	-rm *.o *~ *.a *.SYM *.SYS *.REL TESTLIB $(PLASM) $(PLVM)
+	-rm *.o *~ *.a *.SYM $(CMD) *\#FE1000 $(ROD) $(HGR1) $(PLASM) $(PLVM)
 
 $(PLASM): $(OBJS) $(INCS)
 	cc $(OBJS) -o $(PLASM)
@@ -39,44 +42,34 @@ cmdexec.a: cmdexec.pla $(PLASM)
 $(PLVM02): plvm02.s cmdexec.a
 	acme -o $(PLVM02) -l PLVM02.SYM plvm02.s
 
-$(CMD): cmd.pla cmdstub.s $(PLVM) $(PLASM)
+soscmd.a: soscmd.pla $(PLASM)
+	./$(PLASM) -A < soscmd.pla > soscmd.a
+
+$(PLVM03): plvm03.s soscmd.a
+	acme -o $(PLVM03) -l PLVM03.SYM plvm03.s
+
+$(CMD): cmd.pla cmdstub.s $(PLVM02) $(PLASM)
 	./$(PLASM) -A < cmd.pla > cmd.a
 	acme --setpc 8192 -o $(CMD) cmdstub.s
 
-TESTLIB: testlib.pla $(PLVM) $(PLASM)
-	./$(PLASM) -AM < testlib.pla > testlib.a
-	acme --setpc 4094 -o TESTLIB testlib.a
-
-test: test.pla TESTLIB $(PLVM) $(PLASM)
+test:	test.pla testlib.pla $(PLVM) $(PLASM)
 	./$(PLASM) -AM < test.pla > test.a
-	acme --setpc 4094 -o TEST.REL test.a
-	./$(PLVM) TEST.REL
+	acme --setpc 4094 -o TEST#FE1000 test.a
+	./$(PLASM) -AM < teslib.pla >testlib.a 
+	acme --setpc 4094 -o TESTLIB#FE1000 testlib.a
+	./$(PLVM) TEST\#FE1000
 
-TESTCLS: testcls.pla $(PLVM) $(PLASM)
-	./$(PLASM) -AM < testcls.pla > testcls.a
-	acme --setpc 4094 -o TESTCLS testcls.a
-
-class: class.pla TESTCLS $(PLVM) $(PLASM)
-	./$(PLASM) -AM < class.pla > class.a
-	acme --setpc 4094 -o CLASS.REL class.a
-	./$(PLVM) CLASS.REL
-
-debug: test.pla TESTLIB $(PLVM) $(PLASM)
-	./$(PLASM) -AM < test.pla > test.a
-	acme --setpc 4094 -o TEST.REL test.a
-	./$(PLVM) -s TEST.REL MAIN
-
-hello: hello.pla $(PLVM) $(PLASM)
-	./$(PLASM) -AM < hello.pla > hello.a
-	acme --setpc 4094 -o HELLO.REL hello.a
-	./$(PLVM) HELLO.REL
-
-ROD.REL: rod.pla $(PLVM) $(PLASM)
+$(ROD): rod.pla $(PLVM02) $(PLASM)
 	./$(PLASM) -AM < rod.pla > rod.a
-	acme --setpc 4094 -o ROD.REL rod.a
+	acme --setpc 4094 -o $(ROD) rod.a
 
-HGR1:	hgr1.pla hgr1test.pla $(PLVM) $(PLASM)
+$(HGR1):	hgr1.pla hgr1test.pla $(PLVM02) $(PLASM)
 	./$(PLASM) -AM < hgr1test.pla > hgr1test.a
-	acme --setpc 4094 -o HGR1TEST.REL hgr1test.a
+	acme --setpc 4094 -o HGR1TEST.REL#FE1000 hgr1test.a
 	./$(PLASM) -AM < hgr1.pla > hgr1.a
-	acme --setpc 4094 -o HGR1 hgr1.a
+	acme --setpc 4094 -o $(HGR1) hgr1.a
+
+hello:	hello.pla $(PLVM) $(PLASM)
+	m4 < hello.pla | ./$(PLASM) -AM > hello.a
+	acme --setpc 4094 -o HELLO#FE1000 hello.a
+	./$(PLVM) HELLO#FE1000
diff --git a/src/plvm02.s b/src/plvm02.s
index 57035ec..58ee6ca 100644
--- a/src/plvm02.s
+++ b/src/plvm02.s
@@ -209,8 +209,8 @@ PAGE3	=	*
 ;*
 ;* PAGE 3 VECTORS INTO INTERPRETER
 ;*
-	BIT	LCRDEN+LCBNK2	; $03D0 - DIRECT INTERP ENTRY
-	JMP	INTERP
+INTERP	BIT	LCRDEN+LCBNK2	; $03D0 - DIRECT INTERP ENTRY
+	JMP	DINTERP
 	BIT	LCRDEN+LCBNK2	; $03D6 - INDIRECT INTERP ENTRY
 	JMP	IINTRP
 	BIT	LCRDEN+LCBNK2	; $03DC - INDIRECT INTERPX ENTRY
@@ -306,7 +306,7 @@ DISABLE80 !BYTE	21, 13, '1', 26, 13
 ;*
 ;* ENTER INTO BYTECODE INTERPRETER
 ;*
-INTERP	BIT	LCRWEN+LCBNK2	; WRITE ENABLE LANGUAGE CARD
+DINTERP	BIT	LCRWEN+LCBNK2	; WRITE ENABLE LANGUAGE CARD
 	BIT	LCRWEN+LCBNK2
 	PLA
         STA     IPL
@@ -384,10 +384,6 @@ TIMER	JSR	JMPTMR
 ;*
 JMPTMR	JMP	(TMRVEC)
 ;*
-;* INDIRECT JUMP TO (TMP)
-;*
-JMPTMP	JMP	(TMP)
-;*
 ;* ADD TOS TO TOS-1
 ;*
 ADD 	LDA	ESTKL,X
@@ -1818,17 +1814,17 @@ IBRNCHX	LDA	IPL
 ;*
 CALL 	+INC_IP
 	LDA	(IP),Y
-	STA	TMPL
+	STA	CALLADR+1
 	+INC_IP
 	LDA	(IP),Y
-	STA	TMPH
+	STA	CALLADR+2
 	LDA	IPH
 	PHA
 	LDA	IPL
 	PHA
 	TYA
 	PHA
-	JSR	JMPTMP
+CALLADR	JSR	$FFFF
 	PLA
 	TAY
 	PLA
@@ -1841,10 +1837,10 @@ CALL 	+INC_IP
 ;
 CALLX 	+INC_IP
 	LDA	(IP),Y
-	STA	TMPL
+	STA	CALXADR+1
 	+INC_IP
 	LDA	(IP),Y
-	STA	TMPH
+	STA	CALXADR+2
 	LDA	IPH
 	PHA
 	LDA	IPL
@@ -1853,7 +1849,7 @@ CALLX 	+INC_IP
 	PHA
 	STA	ALTRDOFF
 	CLI
-	JSR	JMPTMP
+CALXADR	JSR	$FFFF
 	SEI
 	STA	ALTRDON
 	PLA
@@ -1869,9 +1865,9 @@ CALLX 	+INC_IP
 ;* INDIRECT CALL TO ADDRESS (NATIVE CODE)
 ;*
 ICAL 	LDA	ESTKL,X
-	STA	TMPL
+	STA	ICALADR+1
 	LDA	ESTKH,X
-	STA	TMPH
+	STA	ICALADR+2
 	INX
 	LDA	IPH
 	PHA
@@ -1879,7 +1875,7 @@ ICAL 	LDA	ESTKL,X
 	PHA
 	TYA
 	PHA
-	JSR	JMPTMP
+ICALADR	JSR	$FFFF
 	PLA
 	TAY
 	PLA
@@ -1891,9 +1887,9 @@ ICAL 	LDA	ESTKL,X
 	JMP	NEXTOP
 ;
 ICALX 	LDA	ESTKL,X
-	STA	TMPL
+	STA	ICLXADR+1
 	LDA	ESTKH,X
-	STA	TMPH
+	STA	ICLXADR+2
 	INX
 	LDA	IPH
 	PHA
@@ -1903,7 +1899,7 @@ ICALX 	LDA	ESTKL,X
 	PHA
 	STA	ALTRDOFF
 	CLI
-	JSR	JMPTMP
+ICLXADR	JSR	$FFFF
 	SEI
 	STA	ALTRDON
 	PLA
@@ -1952,7 +1948,7 @@ ENTER4  LDA	ESTKH,X
 	STA	(IFP),Y
 	DEY
 	INX
-	DEC	TMPL
+	DEC	NPARMS
 	BNE	ENTER4
 ENTER5  LDY	IPY
 	JMP	NEXTOP
@@ -1992,7 +1988,7 @@ ENTERX4	LDA	ESTKH,X
 	STA	(IFP),Y
 	DEY
 	INX
-	DEC	TMPL
+	DEC	NPARMS
 	BNE	ENTERX4
 ENTERX5	STA	ALTRDON
 	LDY	IPY
diff --git a/src/plvm03.s b/src/plvm03.s
new file mode 100644
index 0000000..6b1c6ca
--- /dev/null
+++ b/src/plvm03.s
@@ -0,0 +1,1016 @@
+;**********************************************************
+;*
+;* SYSTEM ROUTINES AND LOCATIONS
+;*
+;**********************************************************
+;
+; HARDWARE REGISTERS
+;
+MEMBANK =	$FFEF
+XPAGE	=	$1600
+;*
+;* VM ZERO PAGE LOCATIONS
+;*
+ESTKSZ	=	$20
+ESTK	=	$C0
+ESTKL	=	ESTK
+ESTKH	=	ESTK+ESTKSZ/2
+VMZP	=	ESTK+ESTKSZ
+IFP	=	VMZP
+IFPL	=	IFP
+IFPH	=	IFP+1
+IFPX	=	XPAGE+IFPH
+IP     	=	IFP+2
+IPL	=	IP
+IPH	=	IP+1
+IPX	=	XPAGE+IPH
+IPY	=	IP+2
+TMP	=	IP+3
+TMPL	=	TMP
+TMPH	=	TMP+1
+TMPX	=	XPAGE+TMPH
+NPARMS	=	TMPL
+FRMSZ	=	TMPH
+DVSIGN	=	TMP+2
+ESP     =       TMP+2
+SRC	=	$06
+SRCL	=	SRC
+SRCH	=	SRC+1
+SRCX	=	XPAGE+SRCH
+DST	=	SRC+2
+DSTL	=	DST
+DSTH	=	DST+1
+DSTX	=	XPAGE+DSTH
+;*
+;* SOS
+;*
+	!MACRO	SOS .CMD, .LIST	{
+	BRK
+	!BYTE	.CMD
+	!WORD	.LIST
+	}
+;*
+;* INTERPRETER INSTRUCTION POINTER INCREMENT MACRO
+;*
+	!MACRO	INC_IP	{
+	INY
+	BNE	* + 4
+	INC	IPH
+	}
+;*
+;* INTERPRETER HEADER+INITIALIZATION
+;*
+	SEGSTART	=	$A000
+	*=	SEGSTART-$0E
+	!TEXT	"SOS NTRP"
+	!WORD	$0000
+	!WORD	SEGSTART
+	!WORD	SEGEND-SEGSTART
+	
+	LDA	#$00		; CLEAR ALL EXTENDED POINTERS
+	STA	TMPX
+	STA	SRCX
+	STA	DSTX
+	STA	IFPX		; INIT FRAME POINTER
+	LDA	#<INTERP
+	STA	IFPL
+	LDA	#>INTERP
+	STA	IFPH
+	LDA	#<SEGEND	; SAVE END OF SEGMENT FOR SYMBOL TABLE
+	STA	SEGSTART
+	LDA	#>SEGEND
+	STA	SEGSTART+1
+        LDX	#ESTKSZ/2	; INIT EVAL STACK INDEX
+	JMP	SOSCMD
+;*
+;* SYSTEM INTERPRETER ENTRYPOINT
+;*
+INTERP	LDY	#$00
+	STY	IPX
+	PLA
+        STA     IPL
+        PLA
+        STA     IPH
+	INY			; MAP BANK $01
+	STY	MEMBANK
+	BNE	FETCHOP
+;*
+;* ENTER INTO USER BYTECODE INTERPRETER
+;*
+XINTERP	PLA
+        STA     TMPL
+        PLA
+        STA     TMPH
+	LDY	#$03
+	LDA     (TMP),Y
+        STA	IPX
+	DEY
+	LDA     (TMP),Y
+        STA	IPH
+	DEY
+	LDA     (TMP),Y
+	STA	IPL
+        DEY
+	BEQ	FETCHOP
+;*
+;* INTERP BYTECODE
+;*
+NEXTOPH	INC	IPH
+	BNE	FETCHOP
+DROP	INX
+NEXTOP	INY
+	BEQ	NEXTOPH
+FETCHOP LDA	(IP),Y
+	STA	*+4
+	JMP	(OPTBL)
+;*
+;* INTERNAL MULTIPLY ALGORITHM
+;*
+_MUL	STY	IPY
+	LDY	#$00
+	STY	TMPL		; PRODL
+	STY	TMPH		; PRODH
+	LDY	#$10
+MUL1 	LSR	ESTKH,X		; MULTPLRH
+	ROR	ESTKL,X		; MULTPLRL
+	BCC	MUL2
+	LDA	ESTKL+1,X	; MULTPLNDL
+	CLC
+	ADC	TMPL		; PRODL
+	STA	TMPL
+	LDA	ESTKH+1,X	; MULTPLNDH
+	ADC	TMPH		; PRODH
+	STA	TMPH
+MUL2 	ASL	ESTKL+1,X	; MULTPLNDL
+	ROL	ESTKH+1,X	; MULTPLNDH
+	DEY
+	BNE	MUL1
+	INX
+	LDA	TMPL		; PRODL
+	STA	ESTKL,X
+	LDA	TMPH		; PRODH
+	STA	ESTKH,X
+	LDY	IPY
+	RTS
+;*
+;* INTERNAL DIVIDE ALGORITHM
+;*
+_NEG 	LDA	#$00
+	SEC
+	SBC	ESTKL,X
+	STA	ESTKL,X
+	LDA	#$00
+	SBC	ESTKH,X
+	STA	ESTKH,X
+	RTS
+_DIV	STY	IPY
+	LDA	ESTKH,X
+	AND	#$80
+	STA	DVSIGN
+	BPL	_DIV1
+	JSR	_NEG
+	INC	DVSIGN
+_DIV1 	LDA	ESTKH+1,X
+	BPL	_DIV2
+	INX
+	JSR	_NEG
+	DEX
+	INC	DVSIGN
+	BNE	_DIV3
+_DIV2 	ORA	ESTKL+1,X	; DVDNDL
+	BNE	_DIV3
+	STA	TMPL
+	STA	TMPH
+	RTS
+_DIV3 	LDY	#$11		; #BITS+1
+	LDA	#$00
+	STA	TMPL		; REMNDRL
+	STA	TMPH		; REMNDRH
+_DIV4 	ASL	ESTKL+1,X	; DVDNDL
+	ROL	ESTKH+1,X	; DVDNDH
+	DEY
+	BCC	_DIV4
+	STY	ESTKL-1,X
+_DIV5 	ROL	TMPL		; REMNDRL
+	ROL	TMPH		; REMNDRH
+	LDA	TMPL		; REMNDRL
+	SEC
+	SBC	ESTKL,X		; DVSRL
+	TAY
+	LDA	TMPH		; REMNDRH
+	SBC	ESTKH,X		; DVSRH
+	BCC	_DIV6
+	STA	TMPH		; REMNDRH
+	STY	TMPL		; REMNDRL
+_DIV6 	ROL	ESTKL+1,X	; DVDNDL
+	ROL	ESTKH+1,X	; DVDNDH
+	DEC	ESTKL-1,X
+	BNE	_DIV5
+	LDY	IPY
+	RTS
+;*
+;* OPCODE TABLE
+;*
+	!ALIGN	255,0
+OPTBL 	!WORD	ZERO,ADD,SUB,MUL,DIV,MOD,INCR,DECR		; 00 02 04 06 08 0A 0C 0E
+	!WORD	NEG,COMP,BAND,IOR,XOR,SHL,SHR,IDXW		; 10 12 14 16 18 1A 1C 1E
+	!WORD	LNOT,LOR,LAND,LA,LLA,CB,CW,SWAP			; 20 22 24 26 28 2A 2C 2E
+	!WORD	DROP,DUP,PUSH,PULL,BRGT,BRLT,BREQ,BRNE		; 30 32 34 36 38 3A 3C 3E
+	!WORD	ISEQ,ISNE,ISGT,ISLT,ISGE,ISLE,BRFLS,BRTRU	; 40 42 44 46 48 4A 4C 4E
+	!WORD	BRNCH,IBRNCH,CALL,ICAL,ENTER,LEAVE,RET,NEXTOP 	; 50 52 54 56 58 5A 5C 5E
+	!WORD	LB,LW,LLB,LLW,LAB,LAW,DLB,DLW			; 60 62 64 66 68 6A 6C 6E
+	!WORD	SB,SW,SLB,SLW,SAB,SAW,DAB,DAW			; 70 72 74 76 78 7A 7C 7E
+;*
+;* ADD TOS TO TOS-1
+;*
+ADD 	LDA	ESTKL,X
+	CLC
+	ADC	ESTKL+1,X
+	STA	ESTKL+1,X
+	LDA	ESTKH,X
+	ADC	ESTKH+1,X
+	STA	ESTKH+1,X
+	INX
+	JMP	NEXTOP
+;*
+;* SUB TOS FROM TOS-1
+;*
+SUB 	LDA	ESTKL+1,X
+	SEC
+	SBC	ESTKL,X
+	STA	ESTKL+1,X
+	LDA	ESTKH+1,X
+	SBC	ESTKH,X
+	STA	ESTKH+1,X
+	INX
+	JMP	NEXTOP
+;
+;*
+;* SHIFT TOS-1 LEFT BY 1, ADD TO TOS-1
+;*
+IDXW 	LDA	ESTKL,X
+	ASL
+	ROL	ESTKH,X
+	CLC
+	ADC	ESTKL+1,X
+	STA	ESTKL+1,X
+	LDA	ESTKH,X
+	ADC	ESTKH+1,X
+	STA	ESTKH+1,X
+	INX
+	JMP	NEXTOP
+;*
+;* MUL TOS-1 BY TOS
+;*
+MUL	JSR	_MUL
+	JMP	NEXTOP
+;*
+;* NEGATE TOS
+;*
+NEG 	LDA	#$00
+	SEC
+	SBC	ESTKL,X
+	STA	ESTKL,X
+	LDA	#$00
+	SBC	ESTKH,X
+	STA	ESTKH,X
+	JMP	NEXTOP
+;*
+;* DIV TOS-1 BY TOS
+;*
+DIV 	JSR	_DIV
+	INX
+	LSR	DVSIGN		; SIGN(RESULT) = (SIGN(DIVIDEND) + SIGN(DIVISOR)) & 1
+	BCS	NEG
+	JMP	NEXTOP
+;*
+;* MOD TOS-1 BY TOS
+;*
+MOD	JSR	_DIV
+	INX
+	LDA	TMPL		; REMNDRL
+	STA	ESTKL,X
+	LDA	TMPH		; REMNDRH
+	STA	ESTKH,X
+	LDA	DVSIGN		; REMAINDER IS SIGN OF DIVIDEND
+	BMI	NEG
+	JMP	NEXTOP
+;*
+;* INCREMENT TOS
+;*
+INCR 	INC	ESTKL,X
+	BNE	INCR1
+	INC	ESTKH,X
+INCR1 	JMP	NEXTOP
+;*
+;* DECREMENT TOS
+;*
+DECR 	LDA	ESTKL,X
+	BNE	DECR1
+	DEC	ESTKH,X
+DECR1 	DEC	ESTKL,X
+	JMP	NEXTOP
+;*
+;* BITWISE COMPLIMENT TOS
+;*
+COMP 	LDA	#$FF
+	EOR	ESTKL,X
+	STA	ESTKL,X
+	LDA	#$FF
+	EOR	ESTKH,X
+	STA	ESTKH,X
+	JMP	NEXTOP
+;*
+;* BITWISE AND TOS TO TOS-1
+;*
+BAND 	LDA	ESTKL+1,X
+	AND	ESTKL,X
+	STA	ESTKL+1,X
+	LDA	ESTKH+1,X
+	AND	ESTKH,X
+	STA	ESTKH+1,X
+	INX
+	JMP	NEXTOP
+;*
+;* INCLUSIVE OR TOS TO TOS-1
+;*
+IOR 	LDA	ESTKL+1,X
+	ORA	ESTKL,X
+	STA	ESTKL+1,X
+	LDA	ESTKH+1,X
+	ORA	ESTKH,X
+	STA	ESTKH+1,X
+	INX
+	JMP	NEXTOP
+;*
+;* EXLUSIVE OR TOS TO TOS-1
+;*
+XOR 	LDA	ESTKL+1,X
+	EOR	ESTKL,X
+	STA	ESTKL+1,X
+	LDA	ESTKH+1,X
+	EOR	ESTKH,X
+	STA	ESTKH+1,X
+	INX
+	JMP	NEXTOP
+;*
+;* SHIFT TOS-1 LEFT BY TOS
+;*
+SHL	STY	IPY
+	LDA	ESTKL,X
+	CMP	#$08
+	BCC	SHL1
+	LDY	ESTKL+1,X
+	STY	ESTKH+1,X
+	LDY	#$00
+	STY	ESTKL+1,X
+	SBC	#$08
+SHL1 	TAY
+	BEQ	SHL3
+SHL2 	ASL	ESTKL+1,X
+	ROL	ESTKH+1,X
+	DEY
+	BNE	SHL2
+SHL3 	INX
+	LDY	IPY
+	JMP	NEXTOP
+;*
+;* SHIFT TOS-1 RIGHT BY TOS
+;*
+SHR	STY	IPY
+	LDA	ESTKL,X
+	CMP	#$08
+	BCC	SHR2
+	LDY	ESTKH+1,X
+	STY	ESTKL+1,X
+	CPY	#$80
+	LDY	#$00
+	BCC	SHR1
+	DEY
+SHR1 	STY	ESTKH+1,X
+	SEC
+	SBC	#$08
+SHR2 	TAY
+	BEQ	SHR4
+	LDA	ESTKH+1,X
+SHR3 	CMP	#$80
+	ROR
+	ROR	ESTKL+1,X
+	DEY
+	BNE	SHR3
+	STA	ESTKH+1,X
+SHR4 	INX
+	LDY	IPY
+	JMP	NEXTOP
+;*
+;* LOGICAL NOT
+;*
+LNOT	LDA	ESTKL,X
+	ORA	ESTKH,X
+	BEQ	LNOT1
+	LDA	#$FF
+LNOT1	EOR	#$FF
+	STA	ESTKL,X
+	STA	ESTKH,X
+	JMP	NEXTOP
+;*
+;* LOGICAL AND
+;*
+LAND 	LDA	ESTKL,X
+	ORA	ESTKH,X
+	BEQ	LAND1
+	LDA	ESTKL+1,X
+	ORA	ESTKH+1,X
+	BEQ	LAND1
+	LDA	#$FF
+LAND1 	STA	ESTKL+1,X
+	STA	ESTKH+1,X
+	INX
+	JMP	NEXTOP
+;*
+;* LOGICAL OR
+;*
+LOR 	LDA	ESTKL,X
+	ORA	ESTKH,X
+	ORA	ESTKL+1,X
+	ORA	ESTKH+1,X
+	BEQ	LOR1
+	LDA	#$FF
+LOR1 	STA	ESTKL+1,X
+	STA	ESTKH+1,X
+	INX
+	JMP	NEXTOP
+;*
+;* SWAP TOS WITH TOS-1
+;*
+SWAP	STY	IPY
+	LDA	ESTKL,X
+	LDY	ESTKL+1,X
+	STA	ESTKL+1,X
+	STY	ESTKL,X
+	LDA	ESTKH,X
+	LDY	ESTKH+1,X
+	STA	ESTKH+1,X
+	STY	ESTKH,X
+	LDY	IPY
+	JMP	NEXTOP
+;*
+;* DUPLICATE TOS
+;*
+DUP 	DEX
+	LDA	ESTKL+1,X
+	STA	ESTKL,X
+	LDA	ESTKH+1,X
+	STA	ESTKH,X
+	JMP	NEXTOP
+;*
+;* PUSH FROM EVAL STACK TO CALL STACK
+;*
+PUSH 	LDA	ESTKL,X
+	PHA
+	LDA	ESTKH,X
+	PHA
+	INX
+	JMP	NEXTOP
+;*
+;* PULL FROM CALL STACK TO EVAL STACK
+;*
+PULL 	DEX
+	PLA
+	STA	ESTKH,X
+	PLA
+	STA	ESTKL,X
+	JMP	NEXTOP
+;*
+;* CONSTANT
+;*
+ZERO 	DEX
+	LDA	#$00
+	STA	ESTKL,X
+	STA	ESTKH,X
+	JMP	NEXTOP
+CB 	DEX
+	+INC_IP
+	LDA	(IP),Y
+	STA	ESTKL,X
+	LDA	#$00
+	STA	ESTKH,X
+	JMP	NEXTOP
+;*
+;* LOAD ADDRESS & LOAD CONSTANT WORD (SAME THING, WITH OR WITHOUT FIXUP)
+;*
+LA	=	*
+CW	DEX
+	+INC_IP
+ 	LDA	(IP),Y
+	STA	ESTKL,X
+	+INC_IP
+ 	LDA	(IP),Y
+	STA	ESTKH,X
+	JMP	NEXTOP
+;*
+;* LOAD VALUE FROM ADDRESS TAG
+;*
+LB 	LDA	ESTKL,X
+	STA	TMPL
+	LDA	ESTKH,X
+	STA	TMPH
+	STY	IPY
+	LDY	#$00
+	LDA	(TMP),Y
+	STA	ESTKL,X
+	STY	ESTKH,X
+	LDY	IPY
+	JMP	NEXTOP
+LW 	LDA	ESTKL,X
+	STA	TMPL
+	LDA	ESTKH,X
+	STA	TMPH
+       	STY	IPY
+	LDY	#$00
+	LDA	(TMP),Y
+	STA	ESTKL,X
+	INY
+	LDA	(TMP),Y
+	STA	ESTKH,X
+	LDY	IPY
+	JMP	NEXTOP
+;*
+;* LOAD ADDRESS OF LOCAL FRAME OFFSET
+;*
+LLA 	+INC_IP
+ 	LDA	(IP),Y
+	DEX
+	CLC
+	ADC	IFPL
+	STA	ESTKL,X
+	LDA	#$00
+	ADC	IFPH
+	STA	ESTKH,X
+	JMP	NEXTOP
+;*
+;* LOAD VALUE FROM LOCAL FRAME OFFSET
+;*
+LLB 	+INC_IP
+ 	LDA	(IP),Y
+	STY	IPY
+	TAY
+	DEX
+	LDA	(IFP),Y
+	STA	ESTKL,X
+	LDA	#$00
+	STA	ESTKH,X
+	LDY	IPY
+	JMP	NEXTOP
+LLW 	+INC_IP
+ 	LDA	(IP),Y
+	STY	IPY
+	TAY
+	DEX
+	LDA	(IFP),Y
+	STA	ESTKL,X
+	INY
+	LDA	(IFP),Y
+	STA	ESTKH,X
+	LDY	IPY
+	JMP	NEXTOP
+;*
+;* LOAD VALUE FROM ABSOLUTE ADDRESS
+;*
+LAB 	+INC_IP
+	LDA	(IP),Y
+	STA	TMPL
+	+INC_IP
+	LDA	(IP),Y
+	STA	TMPH
+	STY	IPY
+	LDY	#$00
+	LDA	(TMP),Y
+	DEX
+	STA	ESTKL,X
+	STY	ESTKH,X
+	LDY	IPY
+	JMP	NEXTOP
+LAW 	+INC_IP
+	LDA	(IP),Y
+	STA	TMPL
+	+INC_IP
+	LDA	(IP),Y
+	STA	TMPH
+	STY	IPY
+	LDY	#$00
+	LDA	(TMP),Y
+	DEX
+	STA	ESTKL,X
+	INY
+	LDA	(TMP),Y
+	STA	ESTKH,X
+	LDY	IPY
+	JMP	NEXTOP
+;*
+;* STORE VALUE TO ADDRESS
+;*
+SB 	LDA	ESTKL+1,X
+	STA	TMPL
+	LDA	ESTKH+1,X
+	STA	TMPH
+	LDA	ESTKL,X
+	STY	IPY
+	LDY	#$00
+	STA	(TMP),Y
+	INX
+	INX
+	LDY	IPY
+	JMP	NEXTOP
+SW 	LDA	ESTKL+1,X
+	STA	TMPL
+	LDA	ESTKH+1,X
+	STA	TMPH
+	STY	IPY
+	LDY	#$00
+	LDA	ESTKL,X
+	STA	(TMP),Y
+	INY
+	LDA	ESTKH,X
+	STA	(TMP),Y
+	INX
+	INX
+	LDY	IPY
+	JMP	NEXTOP
+;*
+;* STORE VALUE TO LOCAL FRAME OFFSET
+;*
+SLB 	+INC_IP
+ 	LDA	(IP),Y
+	STY	IPY
+	TAY
+	LDA	ESTKL,X
+	STA	(IFP),Y
+	INX
+	LDY	IPY
+	JMP	NEXTOP
+SLW 	+INC_IP
+ 	LDA	(IP),Y
+	STY	IPY
+	TAY
+	LDA	ESTKL,X
+	STA	(IFP),Y
+	INY
+	LDA	ESTKH,X
+	STA	(IFP),Y
+	INX
+	LDY	IPY
+	JMP	NEXTOP
+;*
+;* STORE VALUE TO LOCAL FRAME OFFSET WITHOUT POPPING STACK
+;*
+DLB 	+INC_IP
+	LDA	(IP),Y
+	STY	IPY
+	TAY
+	LDA	ESTKL,X
+	STA	(IFP),Y
+	LDY	IPY
+	JMP	NEXTOP
+DLW 	+INC_IP
+	LDA	(IP),Y
+	STY	IPY
+	TAY
+	LDA	ESTKL,X
+	STA	(IFP),Y
+	INY
+	LDA	ESTKH,X
+	STA	(IFP),Y
+	LDY	IPY
+	JMP	NEXTOP
+;*
+;* STORE VALUE TO ABSOLUTE ADDRESS
+;*
+SAB 	+INC_IP
+	LDA	(IP),Y
+	STA	TMPL
+	+INC_IP
+	LDA	(IP),Y
+	STA	TMPH
+	LDA	ESTKL,X
+	STY	IPY
+	LDY	#$00
+	STA	(TMP),Y
+	INX
+	LDY	IPY
+	JMP	NEXTOP
+SAW 	+INC_IP
+	LDA	(IP),Y
+	STA	TMPL
+	+INC_IP
+	LDA	(IP),Y
+	STA	TMPH
+	STY	IPY
+	LDY	#$00
+	LDA	ESTKL,X
+	STA	(TMP),Y
+	INY
+	LDA	ESTKH,X
+	STA	(TMP),Y
+	INX
+	LDY	IPY
+	JMP	NEXTOP
+;*
+;* STORE VALUE TO ABSOLUTE ADDRESS WITHOUT POPPING STACK
+;*
+DAB 	+INC_IP
+	LDA	(IP),Y
+	STA	TMPL
+	+INC_IP
+	LDA	(IP),Y
+	STA	TMPH
+	STY	IPY
+	LDY	#$00
+	LDA	ESTKL,X
+	STA	(TMP),Y
+	LDY	IPY
+	JMP	NEXTOP
+DAW 	+INC_IP
+	LDA	(IP),Y
+	STA	TMPL
+	+INC_IP
+	LDA	(IP),Y
+	STA	TMPH
+	STY	IPY
+	LDY	#$00
+	LDA	ESTKL,X
+	STA	(TMP),Y
+	INY
+	LDA	ESTKH,X
+	STA	(TMP),Y
+	LDY	IPY
+	JMP	NEXTOP
+;*
+;* COMPARES
+;*
+ISEQ	STY	IPY
+	LDY	#$00
+	LDA	ESTKL,X
+	CMP	ESTKL+1,X
+	BNE	ISEQ1
+	LDA	ESTKH,X
+	CMP	ESTKH+1,X
+	BNE	ISEQ1
+	DEY
+ISEQ1 	STY	ESTKL+1,X
+	STY	ESTKH+1,X
+	INX
+	LDY	IPY
+	JMP	NEXTOP
+ISNE	STY	IPY
+	LDY	#$FF
+	LDA	ESTKL,X
+	CMP	ESTKL+1,X
+	BNE	ISNE1
+	LDA	ESTKH,X
+	CMP	ESTKH+1,X
+	BNE	ISNE1
+	INY
+ISNE1 	STY	ESTKL+1,X
+	STY	ESTKH+1,X
+	INX
+	LDY	IPY
+	JMP	NEXTOP
+ISGE	STY	IPY
+	LDY	#$00
+	LDA	ESTKL+1,X
+	CMP	ESTKL,X
+	LDA	ESTKH+1,X
+	SBC	ESTKH,X
+	BVC	ISGE1
+	EOR	#$80
+ISGE1 	BMI	ISGE2
+	DEY
+ISGE2 	STY	ESTKL+1,X
+	STY	ESTKH+1,X
+	INX
+	LDY	IPY
+	JMP	NEXTOP
+ISGT	STY	IPY
+	LDY	#$00
+	LDA	ESTKL,X
+	CMP	ESTKL+1,X
+	LDA	ESTKH,X
+	SBC	ESTKH+1,X
+	BVC	ISGT1
+	EOR	#$80
+ISGT1 	BPL	ISGT2
+	DEY
+ISGT2 	STY	ESTKL+1,X
+	STY	ESTKH+1,X
+	INX
+	LDY	IPY
+	JMP	NEXTOP
+ISLE	STY	IPY
+	LDY	#$00
+	LDA	ESTKL,X
+	CMP	ESTKL+1,X
+	LDA	ESTKH,X
+	SBC	ESTKH+1,X
+	BVC	ISLE1
+	EOR	#$80
+ISLE1 	BMI	ISLE2
+	DEY
+ISLE2 	STY	ESTKL+1,X
+	STY	ESTKH+1,X
+	INX
+	LDY	IPY
+	JMP	NEXTOP
+ISLT	STY	IPY
+	LDY	#$00
+	LDA	ESTKL+1,X
+	CMP	ESTKL,X
+	LDA	ESTKH+1,X
+	SBC	ESTKH,X
+	BVC	ISLT1
+	EOR	#$80
+ISLT1 	BPL	ISLT2
+	DEY
+ISLT2 	STY	ESTKL+1,X
+	STY	ESTKH+1,X
+	INX
+	LDY	IPY
+	JMP	NEXTOP
+;*
+;* BRANCHES
+;*
+BRTRU 	INX
+	LDA	ESTKH-1,X
+	ORA	ESTKL-1,X
+	BNE	BRNCH
+NOBRNCH	+INC_IP
+	+INC_IP
+	JMP	NEXTOP
+BRFLS 	INX
+	LDA	ESTKH-1,X
+	ORA	ESTKL-1,X
+	BNE	NOBRNCH
+BRNCH	LDA	IPH
+	STA	TMPH
+	LDA	IPL
+	+INC_IP
+	CLC
+	ADC	(IP),Y
+	STA	TMPL
+	LDA	TMPH
+	+INC_IP
+	ADC	(IP),Y
+	STA	IPH
+	LDA	TMPL
+	STA	IPL
+	DEY
+	DEY
+	JMP	NEXTOP
+BREQ 	INX
+	LDA	ESTKL-1,X
+	CMP	ESTKL,X
+	BNE	NOBRNCH
+	LDA	ESTKL-1,X
+	CMP	ESTKL,X
+	BEQ	BRNCH
+	BNE	NOBRNCH
+BRNE 	INX
+	LDA	ESTKL-1,X
+	CMP	ESTKL,X
+	BNE	BRNCH
+	LDA	ESTKL-1,X
+	CMP	ESTKL,X
+	BEQ	NOBRNCH
+	BNE	BRNCH
+BRGT 	INX
+	LDA	ESTKL-1,X
+	CMP	ESTKL,X
+	LDA	ESTKH-1,X
+	SBC	ESTKH,X
+	BMI	BRNCH
+	BPL	NOBRNCH
+BRLT 	INX
+	LDA	ESTKL,X
+	CMP	ESTKL-1,X
+	LDA	ESTKH,X
+	SBC	ESTKH-1,X
+	BMI	BRNCH
+	BPL	NOBRNCH
+IBRNCH	LDA	IPL
+	CLC
+	ADC	ESTKL,X
+	STA	IPL
+	LDA	IPH
+	ADC	ESTKH,X
+	STA	IPH
+	INX
+	JMP	NEXTOP
+;*
+;* CALL INTO ABSOLUTE ADDRESS (NATIVE CODE)
+;*
+CALL 	+INC_IP
+	LDA	(IP),Y
+	STA	CALLADR+1
+	+INC_IP
+	LDA	(IP),Y
+	STA	CALLADR+2
+	LDA	IPX
+	PHA
+	LDA	IPH
+	PHA
+	LDA	IPL
+	PHA
+	TYA
+	PHA
+CALLADR	JSR	$FFFF
+	PLA
+	TAY
+	PLA
+	STA	IPL
+	PLA
+	STA	IPH
+	PLA
+	STA	IPX
+	JMP	NEXTOP
+;*
+;* INDIRECT CALL TO ADDRESS (NATIVE CODE)
+;*
+ICAL 	LDA	ESTKL,X
+	STA	ICALADR+1
+	LDA	ESTKH,X
+	STA	ICALADR+2
+	INX
+	LDA	IPX
+	PHA
+	LDA	IPH
+	PHA
+	LDA	IPL
+	PHA
+	TYA
+	PHA
+ICALADR	JSR	$FFFF
+	PLA
+	TAY
+	PLA
+	STA	IPL
+	PLA
+	STA	IPH
+	PLA
+	STA	IPX
+	JMP	NEXTOP
+;*
+;* ENTER FUNCTION WITH FRAME SIZE AND PARAM COUNT
+;*
+ENTER 	+INC_IP
+	LDA	(IP),Y
+	STA	FRMSZ
+	+INC_IP
+	LDA	(IP),Y
+	STA	NPARMS
+	STY	IPY
+        LDA	IFPL
+	PHA
+	SEC
+	SBC	FRMSZ
+	STA	IFPL
+	LDA	IFPH
+	PHA
+	SBC	#$00
+	STA	IFPH
+	LDY	#$01
+	PLA
+	STA	(IFP),Y
+	DEY
+	PLA
+	STA	(IFP),Y
+	LDA	NPARMS
+	BEQ	ENTER5
+	ASL
+	TAY
+	INY
+ENTER4  LDA	ESTKH,X
+	STA	(IFP),Y
+	DEY
+	LDA	ESTKL,X
+	STA	(IFP),Y
+	DEY
+	INX
+	DEC	NPARMS
+	BNE	ENTER4
+ENTER5  LDY	IPY
+	JMP	NEXTOP
+;*
+;* LEAVE FUNCTION
+;*
+LEAVE 	LDY	#$01
+	LDA	(IFP),Y
+	DEY
+	PHA
+	LDA	(IFP),Y
+	STA	IFPL
+	PLA
+	STA	IFPH
+RET 	RTS
+SOSCMD	=	*
+	!SOURCE "soscmd.a"
+SEGEND	=	*
diff --git a/src/soscmd.pla b/src/soscmd.pla
new file mode 100644
index 0000000..f9807b4
--- /dev/null
+++ b/src/soscmd.pla
@@ -0,0 +1,1306 @@
+const membank     = $FFEF
+const MODADDR     = $1000
+;
+; ROMCALL return register structure.
+;
+const acc         = 0
+const xreg        = 1
+const yreg        = 2
+const preg        = 3
+;
+; System flags: memory allocator screen holes.
+;
+const restxt1     = $0001
+const restxt2     = $0002
+const reshgr1     = $0004
+const reshgr2     = $0008
+const resxhgr1    = $0010
+const resxhgr2    = $0020
+;
+; SOS flags
+;
+const O_READ      = 1
+const O_WRITE     = 2
+const O_READ_WRITE = 3
+;
+; Pedefined functions.
+;
+predef home, gotoxy, viewport, crout, cout, prstr, cin, rdstr
+predef syscall, romcall
+predef markheap, allocheap, allocalignheap, releaseheap, availheap
+predef memset, memcpy, xmemcpy, memxcpy
+predef uword_isgt, uword_isge, uword_islt, uword_isle
+predef execmod
+;
+; Standard Library exported functions.
+;
+byte stdlibstr[]  = "STDLIB"
+byte clsstr[]     = "CLS"
+byte gotoxystr[]  = "GOTOXY"
+byte viewstr[]    = "VIEWPORT"
+byte putcstr[]    = "PUTC"
+byte putsstr[]    = "PUTS"
+byte getcstr[]    = "GETC"
+byte getsstr[]    = "GETS"
+byte sysstr[]     = "SYSCALL"
+byte hpmarkstr[]  = "HEAPMARK"
+byte hpalignstr[] = "HEAPALLOCALIGN"
+byte hpallocstr[] = "HEAPALLOC"
+byte hprelstr[]   = "HEAPRELEASE"
+byte hpavailstr[] = "HEAPAVAIL"
+byte memsetstr[]  = "MEMSET"
+byte memcpystr[]  = "MEMCPY"
+byte memxcpystr[] = "MEMXCPY"
+byte uisgtstr[]   = "ISUGT"
+byte uisgestr[]   = "ISUGE"
+byte uisltstr[]   = "ISULT"
+byte uislestr[]   = "ISULE"
+byte execstr[]    = "EXEC"
+word exports[]    = @clsstr,    @home
+word              = @gotoxystr, @gotoxy
+word		  = @viewstr,   @viewport
+word		  = @putcstr,   @cout
+word		  = @putsstr,   @prstr
+word		  = @getcstr,   @cin
+word		  = @getsstr,   @rdstr
+word		  = @sysstr,    @syscall
+word		  = @hpmarkstr, @markheap
+word		  = @hpallocstr,@allocheap
+word		  = @hpalignstr,@allocalignheap
+word		  = @hprelstr,  @releaseheap
+word		  = @memsetstr, @memset
+word		  = @memcpystr, @memcpy
+word		  = @memxcpystr, @memxcpy
+word              = @uisgtstr,  @uword_isgt
+word              = @uisgestr,  @uword_isge
+word              = @uisltstr,  @uword_islt
+word              = @uislestr,  @uword_isle
+word              = @execstr,   @execmod
+word              = 0
+word stdlibsym    = @exports     
+;
+; String pool.
+;
+byte console[]    = ".CONSOLE"
+byte devtovol[]   = " => /"
+byte version[]    = "PLASMA 0.9\n"
+byte freestr[]    = "MEM FREE:$"
+byte errorstr[]   = "ERR:$"
+byte okstr[]      = "OK"
+byte huhstr[]     = "?\n"
+byte prefix[128]  = ""
+byte hexchar[]    = '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
+;
+; System variable.
+;
+word systemflags = 0
+word heap        = $2000
+word symtbl, lastsym
+word refcons, devcons
+word perr, terr
+word cmdptr
+;
+; CALL SOS
+; SYSCALL(CMD, PARAMS)
+;
+asm syscall
+	LDA	ESTKL,X
+	LDY	ESTKH,X
+	STA	PARAMS
+	STY	PARAMS+1
+	INX
+	LDA	ESTKL,X
+	STA	CMD
+	BRK
+CMD	!BYTE	00
+PARAMS	!WORD	0000
+	STA	ESTKL,X
+	LDY    	#$00
+	STY	ESTKH,X
+	RTS
+end
+;
+; SET MEMORY TO VALUE
+; MEMSET(ADDR, SIZE, VALUE)
+;
+asm memset
+	LDY     #$00
+	LDA     ESTKL+2,X
+	STA     DSTL
+	LDA     ESTKH+2,X
+	STA     DSTH
+	INC     ESTKL+1,X
+	INC     ESTKH+1,X
+SETMLP	DEC     ESTKL+1,X
+	BNE     +
+	DEC     ESTKH+1,X
+	BEQ     SETMEX
++	LDA     ESTKL,X
+	STA     (DST),Y
+	INY
+	BNE     +
+	INC     DSTH
++	DEC     ESTKL+1,X
+	BNE     +
+	DEC     ESTKH+1,X
+	BEQ     SETMEX
++	LDA     ESTKH,X
+	STA     (DST),Y
+	INY
+	BNE     SETMLP
+	INC     DSTH
+	BNE     SETMLP
+SETMEX	INX
+	INX
+	RTS
+end
+;
+; COPY MEMORY
+; MEMCPY(DSTADDR, SRCADDR, SIZE)
+;
+asm memcpy
+	LDY	#$00
+	LDA	ESTKL,X
+	BNE 	+
+	LDA	ESTKH,X
+	BEQ	CPYMEX
++	LDA	ESTKL+2,X
+	STA 	DSTL
+	LDA	ESTKH+2,X
+	STA 	DSTH
+	LDA	ESTKL+1,X
+	STA 	SRCL
+	LDA	ESTKH+1,X
+	STA 	SRCH
+	CMP	DSTH
+	BCC	REVCPY
+	BNE	FORCPY
+	LDA 	SRCL
+	CMP	DSTL
+	BCS	FORCPY
+REVCPY				; REVERSE DIRECTION COPY
+;	CLC
+	LDA 	ESTKL,X
+	ADC	DSTL
+	STA	DSTL
+	LDA	ESTKH,X
+	ADC	DSTH
+	STA	DSTH
+	CLC
+	LDA 	ESTKL,X
+	ADC	SRCL
+	STA	SRCL
+	LDA	ESTKH,X
+	ADC	SRCH
+	STA	SRCH
+	INC 	ESTKH,X
+REVCPYLP
+	LDA	DSTL
+	BNE	+
+	DEC	DSTH
++	DEC	DSTL
+	LDA	SRCL
+	BNE	+
+	DEC	SRCH
++	DEC	SRCL
+	LDA	(SRC),Y
+	STA	(DST),Y
+	DEC 	ESTKL,X
+	BNE	REVCPYLP
+	DEC	ESTKH,X
+	BNE	REVCPYLP
+	BEQ	CPYMEX
+FORCPY	INC 	ESTKH,X
+FORCPYLP
+	LDA	(SRC),Y
+	STA	(DST),Y
+	INC	DSTL
+	BNE	+
+	INC	DSTH
++	INC	SRCL
+	BNE	+
+	INC	SRCH
++	DEC 	ESTKL,X
+	BNE	FORCPYLP
+	DEC	ESTKH,X
+	BNE	FORCPYLP
+CPYMEX	INX
+	INX
+	RTS
+end
+;
+; COPY FROM MAIN MEM TO EXT MEM.
+;
+; MEMXCPY(DIR, EXT, DST, SRC, SIZE)
+;   DIR = 0 : COPY FROM MAIN TO EXT
+;   DIR = 1 : COPY FROM EXT  TO MAIN
+;
+asm memxcpy
+	RTS
+end
+;
+; Unsigned word comparisons.
+;
+asm uword_isge
+	LDY	#$00
+	LDA	ESTKL+1,X
+	CMP	ESTKL,X
+	LDA	ESTKH+1,X
+	SBC	ESTKH,X
++ 	BCC	+
+	DEY
++ 	STY	ESTKL+1,X
+	STY	ESTKH+1,X
+	INX
+	RTS
+end
+asm uword_isle
+	LDY	#$00
+	LDA	ESTKL,X
+	CMP	ESTKL+1,X
+	LDA	ESTKH,X
+	SBC	ESTKH+1,X
++ 	BCC	+
+	DEY
++ 	STY	ESTKL+1,X
+	STY	ESTKH+1,X
+	INX
+	RTS
+end
+asm uword_isgt
+	LDY	#$FF
+	LDA	ESTKL,X
+	CMP	ESTKL+1,X
+	LDA	ESTKH,X
+	SBC	ESTKH+1,X
++ 	BCC	+
+	INY
++ 	STY	ESTKL+1,X
+	STY	ESTKH+1,X
+	INX
+	RTS
+end
+asm uword_islt
+	LDY	#$FF
+	LDA	ESTKL+1,X
+	CMP	ESTKL,X
+	LDA	ESTKH+1,X
+	SBC	ESTKH,X
++ 	BCC	+
+	INY
++ 	STY	ESTKL+1,X
+	STY	ESTKH+1,X
+	INX
+	RTS
+end
+;
+; Addresses of internal routines.
+;
+asm interp
+	DEX
+	LDA	#<XINTERP
+	STA	ESTKL,X
+	LDA	#>XINTERP
+	STA	ESTKH,X
+	RTS
+end
+; 
+; A DCI string is one that has the high bit set for every character except the last.
+; More efficient than C or Pascal strings.
+;
+;def dcitos(dci, str)
+;    byte len, c
+;    len = 0
+;    repeat
+;        c = (dci).[len]
+;        len = len + 1
+;        (str).[len] = c & $7F
+;    until !(c & $80)
+;    ^str = len
+;    return len
+;end
+asm dcitos
+	LDA	ESTKL,X
+	STA	DSTL
+	LDA	ESTKH,X
+	STA	DSTH
+	LDA	ESTKL+1,X
+	STA	SRCL
+	LDA	ESTKH+1,X
+	STA	SRCH
+	LDY	#$00
+-	LDA	(SRC),Y
+	INY
+	PHA
+	AND	#$7F
+	STA	(DST),Y
+	PLA
+	BMI	-
+	TYA
+	LDY	#$00
+	STA	(DST),Y
+	INX
+	STA	ESTKL,X
+	STY	ESTKH,X
+	RTS
+end
+;def stodci(str, dci)
+;    byte len, c
+;    len = ^str
+;    if len == 0
+;        return
+;    fin
+;    c = toupper((str).[len]) & $7F
+;    len = len - 1
+;    (dci).[len] = c
+;    while len
+;    	c = toupper((str).[len]) | $80
+;    	len = len - 1
+;    	(dci).[len] = c
+;    loop
+;    return ^str
+;end
+asm stodci
+	LDA	ESTKL,X
+	STA	DSTL
+	LDA	ESTKH,X
+	STA	DSTH
+	LDA	ESTKL+1,X
+	STA	SRCL
+	LDA	ESTKH+1,X
+	STA	SRCH
+	INX
+	LDY	#$00
+	LDA	(SRC),Y
+	BEQ	++
+	TAY
+	LDA	(SRC),Y
+	JSR	TOUPR
+	BNE	+
+-	LDA	(SRC),Y
+	JSR	TOUPR
+	ORA	#$80
++	DEY
+	STA	(DST),Y
+	BNE	-
+	LDA	(SRC),Y
+++	STA	ESTKL,X
+	STY	ESTKH,X
+	RTS
+end
+asm toupper
+	LDA     ESTKL,X
+TOUPR	AND	#$7F
+	CMP     #'a'
+	BCC     +
+	CMP     #'z'+1
+	BCS     +
+	SEC
+	SBC     #$20
++	STA     ESTKL,X
+	RTS
+end
+;
+; Module symbols are entered into the symbol table
+; pre-pended with a '#' to differentiate them
+; from normal symbols.
+;
+;def modtosym(mod, dci)
+;    byte len, c
+;    (dci).0 = '#'|$80
+;    len = 0
+;    repeat
+;        c = (mod).[len]
+;        len = len + 1
+;        (dci).[len] = c
+;    until !(c & $80)
+;    return dci
+;end
+asm modtosym
+	LDA	ESTKL+1,X
+	STA	SRCL
+	LDA	ESTKH+1,X
+	STA	SRCH
+	LDA	ESTKL,X
+	STA	ESTKL+1,X
+	STA	DSTL
+	LDA	ESTKH,X
+	STA	ESTKH+1,X
+	STA	DSTH
+	INX
+	LDY	#$00
+	LDA	#'#'+$80
+	STA	(DST),Y
+-	LDA	(SRC),Y
+	INY
+	STA	(DST),Y
+	AND	#$80
+	BMI	-
+	RTS
+end
+;
+; Lookup routines.
+;
+;def lookuptbl(dci, tbl)
+;    word match
+;    while ^tbl
+;        match = dci
+;        while ^tbl == ^match
+;            if !(^tbl & $80)
+;                return (tbl):1
+;            fin
+;            tbl   = tbl   + 1
+;            match = match + 1
+;        loop
+;        while (^tbl & $80)
+;            tbl = tbl + 1
+;        loop
+;        tbl = tbl + 3
+;    loop
+;    return 0
+asm lookuptbl
+	LDA	ESTKL,X
+	STA	DSTL
+	LDA	ESTKH,X
+	STA	DSTH
+	LDA	ESTKL+1,X
+	STA	SRCL
+	LDA	ESTKH+1,X
+	STA	SRCH
+	LDY	#$00
+-	LDA	(DST),Y
+	BEQ	+
+	CMP	(SRC),Y
+	BNE	++
+	INY
+	AND	#$80
+	BMI	-
+	LDA	(DST),Y
+	PHA
+	INY
+	LDA	(DST),Y
+	TAY
+	PLA
++	INX
+	STA	ESTKL,X
+	STY	ESTKH,X
+	RTS
+++	LDY	#$00
+--	LDA	(DST),Y
+	INC	DSTL
+	BNE	++
+	INC	DSTH
+++	AND	#$80
+	BMI	--
+	LDA	#$02
+	CLC
+	ADC	DSTL
+	STA	DSTL
+	TYA
+	ADC	DSTH
+	STA	DSTH
+	BNE	-
+end
+def lookupdef(addr, deftbl)
+    while (deftbl).0 == $20
+        if (deftbl):3 == addr
+            return deftbl
+        fin
+        deftbl = deftbl + 6
+    loop
+    return 0
+;asm lookupdef
+;	LDA	ESTKL,X
+;	STA	DSTL
+;	LDA	ESTKH,X
+;	STA	DSTH
+;	INX
+;-	LDY	#$00
+;	LDA	#$20
+;	AND	(DST),Y
+;	BEQ	++
+;	LDY	#$03
+;	LDA	(DST),Y
+;	CMP	ESTKL,X
+;	BNE	+++
+;	INY
+;	LDA	(DST),Y
+;	CMP	ESTKH,X
+;	BNE	++
+;+	LDA	DSTL
+;	LDY	DSTH
+;++	STA	ESTKL,X
+;	STY	ESTKH,X
+;	RTS
+;+++	LDA	#$05
+;	CLC
+;	ADC	DSTL
+;	STA	DSTL
+;	LDA	#$00
+;	ADC	DSTH
+;	STA	DSTH
+;	BNE	-
+end
+;
+; SOS routines
+; FILE I/O
+;
+def getpfx(path)
+    byte params[4]
+
+    ^path    = 0
+    params.0 = 2
+    params:1 = path
+    params.3 = 128
+    perr     = syscall($C7, @params)
+    return path
+end
+def setpfx(path)
+    byte params[3]
+
+    params.0 = 1
+    params:1 = path
+    perr     = syscall($C6, @params)
+    return path
+end
+def volume(devname, volname, ttlblks, freblks)
+    byte params[9]
+
+    params.0 = 4
+    params:1 = devname
+    params:3 = volname
+    params:5 = 0
+    params:7 = 0
+    perr     = syscall($C5, @params)
+    *ttlblks = params:5
+    *freblks = params:7
+    return perr
+end
+def open(path, access)
+    byte params[7]
+
+    params.0 = 4
+    params:1 = path
+    params.3 = 0
+    params:4 = @access
+    params.6 = 1
+    perr     = syscall($C8, @params)
+    return params.3
+end
+def close(refnum)
+    byte params[2]
+
+    params.0 = 1
+    params.1 = refnum
+    perr     = syscall($CC, @params)
+    return perr
+end
+def read(refnum, buff, len)
+    byte params[8]
+
+    params.0 = 4
+    params.1 = refnum
+    params:2 = buff
+    params:4 = len
+    params:6 = 0
+    perr     = syscall($CA, @params)
+    return params:6
+end
+def write(refnum, buff, len)
+    byte params[6]
+
+    params.0 = 3
+    params.1 = refnum
+    params:2 = buff
+    params:4 = len
+    perr     = syscall($CB, @params)
+    return perr
+end
+;
+; DEVICE I/O
+;
+def dev_status(devnum, list)
+    byte params[5]
+    
+    params.0 = 3
+    params.1 = devnum
+    params.2 = 0
+    params:3 = list
+    perr     = syscall($82, @params)
+    return params.2
+end
+def dev_control(devnum, code, list)
+    byte params[5]
+    
+    params.0 = 3
+    params.1 = devnum
+    params.2 = code
+    params:3 = list
+    perr     = syscall($83, @params)
+    return perr
+end
+def dev_getnum(name)
+    byte params[4]
+    
+    params.0 = 2
+    params:1 = name
+    params.3 = 0
+    perr     = syscall($84, @params)
+    return params.3
+end
+def dev_info(devnum, name, list, listlen)
+    byte params[7]
+    
+    params.0 = 4
+    params.1 = devnum
+    params:2 = name
+    params:4 = list
+    params.6 = listlen
+    perr     = syscall($85, @params)
+    return perr
+end
+;
+; MEMORY CALLS
+;
+def seg_request(base, limit, id)
+    byte params[7]
+    
+    params.0 = 4
+    params:1 = base
+    params:3 = limit
+    params.5 = id
+    params.6 = 0
+    perr     = syscall($40, @params)
+    return params.6
+end
+def seg_find(search, base, limit, pages, id)
+    byte params[10]
+    
+    params.0 = 6
+    params.1 = search
+    params.2 = id
+    params:3 = 0
+    params:5 = 0
+    params:7 = 0
+    params.9 = 0
+    perr     = syscall($41, @params)
+    *pages   = params:3
+    *base    = params:5
+    *limit   = params:7
+    return params.9
+end
+def seg_change(segnum, mode, pages)
+    byte params[5]
+    
+    params.0 = 3
+    params.1 = segnum
+    params.2 = mode
+    params:3 = pages
+    perr     = syscall($42, @params)
+    return params:3
+end
+def seg_getinfo(segnum, base, limit, pages, id)
+    byte params[9]
+    
+    params.0 = 6
+    params.1 = segnum
+    params:2 = 0
+    params:4 = 0
+    params:6 = 0
+    params.8 = 0
+    perr      = syscall($43, @params)
+    *base    = params:2
+    *limit   = params:4
+    *pages   = params:6
+    return params.8
+end
+def seg_getnum(segaddr)
+    byte params[4]
+    
+    params.0 = 2
+    params:1 = segaddr
+    params.3 = 0
+    perr     = syscall($44, @params)
+    return params.3
+end
+def seg_release(segnum)
+    byte params[2]
+    
+    params.0 = 1
+    params.1 = segnum
+    perr     = syscall($45, @params)
+    return perr
+end
+;
+; CONSOLE I/O
+;
+def init_cons
+    byte nlmode[2]
+    refcons  = open(@console, O_READ_WRITE)
+    devcons  = dev_getnum(@console)
+    nlmode.0 = $80
+    nlmode.1 = $0D
+    dev_control(devcons, $02, @nlmode)
+end
+def cout(ch)
+    if ch == $0D
+        ch = $0A0D
+	write(refcons, @ch, 2)
+    else
+        write(refcons, @ch, 1)
+    fin
+end
+def cin
+    byte ch
+    read(refcons, @ch, 1)
+    return ch
+end
+def prstr(str)
+    write(refcons, str + 1, ^str)
+    if (str).[^str] == $0D
+        cout($0A)
+    fin
+end
+def rdstr(prompt)
+    cout(prompt)
+    ^heap = read(refcons, heap + 1, 128)
+    if (heap).[^heap] == $0D
+        ^heap = ^heap - 1
+    fin
+    cout($0D)
+    return heap
+end
+def home
+    return cout(28)
+end
+def gotoxy(x, y)
+    cout(26)
+    cout(x)
+    return cout(y)
+end
+def viewport(left, top, width, height)
+    cout(1) ; Reset viewport
+    gotoxy(left, top)
+    cout(2)
+    gotoxy(width, height)
+    cout(3)
+    return gotoxy(0, 0)
+end
+def crout
+    return cout($0D)
+end
+def prbyte(v)
+    cout(hexchar[(v >> 4) & $0F])
+    return cout(hexchar[v & $0F])
+end
+def prword(v)
+    prbyte(v >> 8)
+    return prbyte(v)
+end
+;
+; Heap routines.
+;
+def availheap
+    byte fp
+    return @fp - heap
+end
+def allocheap(size)
+    word addr
+    addr = heap
+    heap = heap + size
+    if systemflags & reshgr1
+        if uword_isle(addr, $4000) and uword_isgt(heap, $2000)
+	    addr = $4000
+	    heap = addr + size
+	fin
+    fin
+    if systemflags & reshgr2
+        if uword_isle(addr, $6000) and uword_isgt(heap, $4000)
+	    addr = $6000
+	    heap = addr + size
+	fin
+    fin
+    if uword_isge(heap, @addr)
+        return 0
+    fin
+    return addr
+end
+def allocalignheap(size, pow2, freeaddr)
+    word align, addr
+    if freeaddr
+        *freeaddr = heap
+    fin
+    align = (1 << pow2) - 1
+    addr = (heap | align) + 1
+    heap = addr + size
+    if uword_isge(heap, @addr)
+        return 0
+    fin
+    return addr
+end
+def markheap
+    return heap;
+end
+def releaseheap(newheap)
+    heap = newheap;
+    return @newheap - heap;
+end
+;
+; DCI table routines,
+;
+;def dumptbl(tbl)
+;    byte len
+;
+;    while ^tbl
+;        len = 0
+;        while ^tbl & $80
+;            cout(^tbl)
+;            tbl = tbl + 1
+;            len = len + 1
+;        loop
+;        cout(^tbl)
+;        tbl = tbl + 1
+;        cout(':')
+;        while len < 15
+;            cout(' ')
+;            len = len + 1
+;        loop
+;        cout('$')
+;        prword(*tbl)
+;        crout
+;        tbl = tbl + 2
+;    loop
+;end
+def addtbl(dci, val, last)
+    while ^dci & $80
+        ^*last = ^dci
+        *last  = *last + 1
+        dci    = dci   + 1
+    loop
+    ^*last = ^dci
+    *last  = *last + 1
+    **last = val
+    *last  = *last + 2
+    ^*last = 0
+end
+;
+; Symbol table routines.
+;
+def lookupsym(sym)
+    return lookuptbl(sym, symtbl)
+end
+def addsym(sym, addr)
+    return addtbl(sym, addr, @lastsym);
+end
+;
+; Module routines.
+;
+def lookupmod(mod)
+    byte dci[17]
+    return lookuptbl(modtosym(mod, @dci), symtbl)
+end
+def addmod(mod, addr)
+    byte dci[17]
+    return addtbl(modtosym(mod, @dci), addr, @lastsym)
+end
+def lookupextern(esd, index)
+    word sym
+    byte str[16]
+    while ^esd
+        sym = esd
+        esd = esd + dcitos(esd, @str)
+        if (esd).0 & $10 and (esd).1 == index
+            return lookupsym(sym)
+        fin
+        esd = esd + 3
+    loop
+    return 0
+end
+def adddef(ext, addr, deflast)
+    (*deflast).0 = $20
+    (*deflast):1 = interp
+    (*deflast):3 = addr
+    (*deflast).5 = ext
+    *deflast = *deflast + 6
+    (*deflast).0 = 0
+    return *deflast - 6
+end
+def loadmod(mod)
+    word refnum, rdlen, modsize, bytecode, defofst, defcnt, init, fixup
+    word addr, defaddr, modaddr, modfix, modend
+    word deftbl, deflast
+    word moddep, rld, esd, sym
+    byte defext, str[16], filename[64]
+    byte header[128]
+
+    ;
+    ; Read the RELocatable module header (first 128 bytes)
+    ;
+    dcitos(mod, @filename)
+    refnum = open(@filename, O_READ)
+    if refnum > 0
+        rdlen   = read(refnum, @header, 128)
+	modsize = header:0
+	moddep  = @header.1
+	defofst = modsize
+        init    = 0
+        if rdlen > 4 and header:2 == $DA7E ; DAVE = magic number :-)
+            ;
+            ; This is an EXTended RELocatable (data+bytecode) module.
+            ;
+	    systemflags = header:4 | systemflags
+            defofst  = header:6
+            defcnt   = header:8
+            init     = header:10
+            moddep   = @header.12
+	    ;
+	    ; Load module dependencies.
+	    ;
+            while ^moddep
+                if !lookupmod(moddep)
+		    close(refnum)
+	            refnum = 0
+                    if loadmod(moddep) < 0
+	      	        return perr
+		    fin
+                fin
+                moddep = moddep + dcitos(moddep, @str)
+            loop
+	    ;
+	    ; Init def table.
+	    ;
+	    deftbl   = allocheap(defcnt * 6 + 1)
+	    deflast  = deftbl
+	    ^deflast = 0
+	    if !refnum
+	        ;
+		; Reset read pointer.
+		;
+	        refnum = open(@filename, O_READ)
+		rdlen  = read(refnum, @header, 128)
+	    fin
+        fin
+	;
+	; Alloc heap space for relocated module (data + bytecode).
+	;
+	moddep  = moddep  + 1
+	modfix  = moddep  - @header.2 ; Adjust to skip header
+	modsize = modsize - modfix
+	rdlen   = rdlen   - modfix - 2
+	modaddr = allocheap(modsize)
+	memcpy(modaddr, moddep, rdlen)
+	;
+	; Read in remainder of module into memory for fixups.
+	;
+	addr = modaddr;
+        repeat
+            addr  = addr + rdlen
+            rdlen = read(refnum, addr, 4096)
+        until rdlen <= 0
+        close(refnum)
+	;
+	; Apply all fixups and symbol import/export.
+	;
+        modfix   = modaddr - modfix
+        bytecode = defofst + modfix - MODADDR
+        modend   = modaddr + modsize
+        rld      = modend  ; Re-Locatable Directory
+        esd      = rld     ; Extern+Entry Symbol Directory
+        while !^esd        ; Scan to end of ESD
+            esd = esd + 4
+	loop
+        esd = esd + 1
+	;
+	; Locate bytecode defs in appropriate bank.
+	;
+	;if ^MACHID & $30 == $30
+	    ;defext  = 1
+	    ;defaddr = allocxheap(rld - bytecode)
+	    ;modend  = bytecode
+	;else
+	    defext  = 0
+	    defaddr = bytecode
+	;fin
+        ;
+        ; Run through the Re-Location Dictionary.
+        ;
+        while ^rld
+            if ^rld == $02
+	        ;
+		; This is a bytcode def entry - add it to the def directory.
+		;
+                adddef(defext, (rld):1 - defofst + defaddr, @deflast)
+            else
+                addr = (rld):1 + modfix
+                if addr >= modaddr ; Skip fixups to header
+                    if ^rld & $80 ; WORD sized fixup.
+                        fixup = *addr
+                    else	      ; BYTE sized fixup.
+                        fixup = ^addr
+                    fin
+                    if ^rld & $10 ; EXTERN reference.
+                        fixup = fixup + lookupextern(esd, (rld).3)
+                    else	  ; INTERN fixup.
+                        fixup = fixup + modfix - MODADDR
+                        if uword_isge(fixup, bytecode)
+                            ;
+                            ; Bytecode address - replace with call def directory.
+                            ;
+                            fixup = lookupdef(fixup - bytecode + defaddr, deftbl)
+                        fin
+                    fin
+                    if ^rld & $80 ; WORD sized fixup.
+                        *addr = fixup
+                    else	      ; BYTE sized fixup.
+                        ^addr = fixup
+                    fin
+                fin
+            fin
+            rld = rld + 4
+        loop
+	;
+        ; Run through the External/Entry Symbol Directory.
+	;
+        while ^esd
+            sym = esd
+            esd = esd + dcitos(esd, @str)
+            if ^esd & $08
+		;
+                ; EXPORT symbol - add it to the global symbol table.
+		;
+                addr = (esd):1 + modfix - MODADDR
+                if uword_isge(addr, bytecode)
+		    ;
+		    ; Use the def directory address for bytecode.
+		    ;
+                    addr = lookupdef(addr - bytecode + defaddr, deftbl)
+                fin
+                addsym(sym, addr)
+            fin
+            esd = esd + 3
+        loop
+	if defext
+	    ;
+	    ; Move bytecode to AUX bank.
+	    ;
+	    memxcpy(0, defext, defaddr, bytecode, modsize - (bytecode - modaddr))
+       	fin
+	;
+	; Free up end-of-module main memory.
+	;
+	releaseheap(modend)
+    else
+        perr = perr | 0x100
+        return -perr
+    fin
+    ;
+    ; Call init routine if it exists.
+    ;
+    if init
+        return adddef(defext, init - defofst + defaddr, @deflast)()
+    fin
+    return 0
+end
+;
+; Command mode
+;
+def volumes
+    byte info[11]
+    byte devname[17]
+    byte volname[17]
+    byte i
+    word ttlblks, freblks
+
+    for i = $01 to $18
+        if dev_info(i, @devname, @info, 11) == 0
+	    prstr(@devname)
+	    if volume(@devname, @volname, @ttlblks, @freblks) == 0
+	        prstr(@devtovol)
+	        prstr(@volname)
+	        cout('/')
+	    fin
+	    crout
+	fin
+    next
+    perr = 0
+end
+def catalog(optpath)
+    byte path[64]
+    byte refnum
+    byte firstblk
+    byte entrylen, entriesblk
+    byte i, type, len
+    word entry, filecnt
+
+    if ^optpath
+        memcpy(@path, optpath, ^optpath + 1)
+    else
+        getpfx(@path)
+        prstr(@path)
+        crout()
+    fin
+    refnum = open(@path, O_READ)
+    if perr
+        return perr
+    fin
+    firstblk = 1
+    repeat
+        if read(refnum, heap, 512) == 512
+            entry = heap + 4
+            if firstblk
+                entrylen   = (heap).$23
+                entriesblk = (heap).$24
+                filecnt    = (heap):$25
+                entry      = entry + entrylen
+            fin
+            for i = firstblk to entriesblk
+                type = ^entry
+                if type <> 0
+                    len = type & $0F
+                    ^entry = len
+                    prstr(entry)
+                    if type & $F0 == $D0 ; Is it a directory?
+                        cout('/')
+                        len = len + 1
+		    elsif (entry).$10 == $FF
+		        cout('-')
+			len = len + 1
+		    elsif (entry).$10 == $FE
+		        cout('+')
+			len = len + 1
+                    fin
+                    for len = 19 - len downto 0
+                        cout(' ')
+                    next
+                    filecnt = filecnt - 1
+                fin
+                entry = entry + entrylen
+            next
+            firstblk = 0
+        else
+            filecnt = 0
+        fin
+    until filecnt == 0
+    close(refnum)
+    crout()
+    return 0
+end
+def stripchars(strptr)
+    while ^strptr and ^(strptr + 1) <> ' '
+        memcpy(strptr + 1, strptr + 2, ^strptr)
+        ^strptr = ^strptr - 1
+    loop
+    return ^strptr
+end
+def stripspaces(strptr)
+    while ^strptr and ^(strptr + ^strptr) <= ' '
+        ^strptr = ^strptr - 1
+    loop
+    while ^strptr and ^(strptr + 1) <= ' '
+        memcpy(strptr + 1, strptr + 2, ^strptr)
+        ^strptr = ^strptr - 1
+    loop
+end
+def striptrail(strptr)
+    byte i
+
+    for i = 1 to ^strptr
+        if (strptr)[i] == ' '
+	    ^strptr = i - 1
+	    return
+	fin
+    next
+end
+def parsecmd(strptr)
+    byte cmd
+
+    cmd = 0
+    stripspaces(strptr)
+    if ^strptr
+        cmd = ^(strptr + 1)
+        memcpy(strptr + 1, strptr + 2, ^strptr)
+        ^strptr = ^strptr - 1
+    fin
+    stripspaces(strptr)
+    return cmd
+end
+def execmod(modfile)
+    byte moddci[17]
+    word saveheap, savesym, saveflags
+  
+    if stodci(modfile, @moddci)
+        saveheap  = heap
+	savesym     = lastsym
+	saveflags   = systemflags
+	^lastsym    = 0
+	perr        = loadmod(@moddci)
+	systemflags = saveflags
+	lastsym     = savesym
+	heap        = saveheap
+    fin
+end
+
+;
+; Save Symbol Table start.
+;
+symtbl = *$A000
+;
+; Request bank $01.
+;
+seg_request($0120, $019F, $10)
+;
+; Init console
+;
+init_cons
+;
+; Print some startup info.
+;
+prstr(@version)
+prstr(@freestr)
+prword(availheap)
+crout
+;
+; Init symbol table.
+;
+lastsym  = symtbl
+^lastsym = 0
+stodci(@stdlibstr, heap)
+addmod(heap, 1)
+while *stdlibsym
+    stodci((stdlibsym):0, heap)
+    addsym(heap, (stdlibsym):2)
+    stdlibsym = stdlibsym + 4
+loop
+;
+; Handle commands.
+;
+while 1
+    prstr(getpfx(@prefix))
+    cmdptr = rdstr($BA)
+    if ^cmdptr
+        when toupper(parsecmd(cmdptr))
+            is 'Q'
+	        ; reboot()
+	    is 'C'
+	        catalog(cmdptr)
+	    is 'P'
+	        setpfx(cmdptr)
+	    is 'V'
+	        volumes()
+	    is '+'
+	        execmod(cmdptr)
+	    otherwise
+	        prstr(@huhstr)
+        wend
+        if perr
+	    terr = perr
+            prstr(@errorstr)
+	    prbyte(terr)
+	    perr = 0
+        else
+            prstr(@okstr)
+        fin
+	crout()
+    fin
+loop
+done
diff --git a/src/testcls.pla b/src/testcls.pla
deleted file mode 100755
index e2ae4d6..0000000
--- a/src/testcls.pla
+++ /dev/null
@@ -1,32 +0,0 @@
-;
-; Declare all imported modules and their data/functions.
-;
-import stdlib
-  predef putc
-end
-predef puti, puth
-export word print[] = @puti, @puth
-byte valstr[] = '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
-;
-; Define functions.
-;
-def puti(i)
-  if i < 0
-    putc('-')
-    i = -i
-  fin
-  if i < 10
-    putc(i + '0')
-  else
-    puti(i / 10)
-    putc(i % 10 + '0')
-  fin
-end
-def puth(h)
-  putc('$')
-  putc(valstr[(h >> 12) & $0F])
-  putc(valstr[(h >> 8)  & $0F])
-  putc(valstr[(h >> 4)  & $0F])
-  putc(valstr[ h        & $0F])
-end
-done