TITLE ZAP -- New Z-Language Assembler

; ZAP version 3 - Expanded word table to 96 words
;  MARC/JMB - 1/7/82

	.DECSAV

SUBTTL ACS

	O=0
	A=1
	B=2
	C=3
	D=4
	E=5
	F=6
	G=7
	ZCHR=7
	H=10
	FRMT=10
	I=11
	J=12	;called J only during word-frequency pass
;acs below this point are used for special purposes
	AB=12	;pointer into argument table ARGBUF
	Z=13	;pointer into output buffer OUTBUF
	ZPC=14	;pc
	FREE=15	;free storage pointer for symbol tables
	TP=16	;pointer into token table TOKENS
	P=17	;stack

%FWDCT==512.-16.

;bits in symbol table words
%UNDEF==400000	;undefined symbol; right half will be ptr to references
%VAR==200000	;symbol is a variable
%BITS==600000	;all defined bits in symbol table

;bits in reference words
%RBYTE==400000	;byte refs are flagged
%RJUMP==200000	;as are jump refs

;random macros
DEFINE	MSG M
	HRROI	A,[ASCIZ /!M!/]
TERMIN

DEFINE	NXTARG N
	ADD	TP,[<2*N>,,<2*N>]
TERMIN

	LOC 140

SUBTTL PSEUDO-OPS AND OPCODES

%PSEUD==400000	;pseudo-op

;pseudo-op definition macro
DEFINE	DISP SYM
	440700,,[ASCIZ /.!SYM/]
	%PSEUD,,Z!SYM
TERMIN

%PRED==200000	;predicate inst.
%VAL==100000	;value inst.
%JUMP==40000	;jump inst.
%STR==20000	;string instr.
%XARG==10000	;??

;opcode definition macro
DEFINE	DEFOP OP,OPCODE,FLAGS
	440700,,[ASCIZ /OP/]
	FLAGS,,OPCODE
TERMIN
SUBTTL PSEUDOS

OPS:
PSUTBL:	DISP BYTE
	DISP END
	DISP ENDI
	DISP ENDT
	DISP EQUAL
	DISP FALSE
	DISP FSTR
	DISP FUNCT
	DISP GSTR
	DISP GVAR
	DISP INSERT
	DISP LEN
	DISP OBJECT
	DISP PDEF
	DISP PROP
	DISP SEQ
	DISP STR
	DISP STRL
	DISP TABLE
	DISP TRUE
	DISP WORD
	DISP ZWORD
OPRTBL:	DEFOP ADD,20.,%VAL
	DEFOP BAND,9.,%VAL
	DEFOP BCOM,143.,%VAL
	DEFOP BOR,8.,%VAL
	DEFOP BTST,7.,%PRED
	DEFOP CALL,224.,%VAL
	DEFOP CRLF,187.
	DEFOP DEC,134.
	DEFOP DIV,23.,%VAL
	DEFOP DLESS?,4.,%PRED
	DEFOP EQUAL?,1.,%PRED+%XARG
	DEFOP FCLEAR,12.
	DEFOP FIRST?,130.,%PRED+%VAL
	DEFOP FSET,11.
	DEFOP FSET?,10.,%PRED
	DEFOP FSTACK,185.
	DEFOP GET,15.,%VAL
	DEFOP GETB,16.,%VAL
	DEFOP GETP,17.,%VAL
	DEFOP GETPT,18.,%VAL
	DEFOP GRTR?,3.,%PRED
	DEFOP IGRTR?,5.,%PRED
	DEFOP IN?,6.,%PRED
	DEFOP INC,133.
	DEFOP JUMP,140.,%JUMP
OPJMP=.-1	;full opcode for jump
	DEFOP LESS?,2.,%PRED
	DEFOP LOC,131.,%VAL
	DEFOP MOD,24.,%VAL
	DEFOP MOVE,14.
	DEFOP MUL,22.,%VAL
	DEFOP NEXT?,129.,%PRED+%VAL
	DEFOP NEXTP,19.,%VAL
	DEFOP NOOP,180.
	DEFOP POP,233.
	DEFOP PRINT,141.
	DEFOP PRINTB,135.
	DEFOP PRINTC,229.
	DEFOP PRINTD,138.
	DEFOP PRINTI,178.,%STR
	DEFOP PRINTN,230.
	DEFOP PRINTR,179.,%STR
	DEFOP PTSIZE,132.,%VAL
	DEFOP PUSH,232.
	DEFOP PUT,225.
	DEFOP PUTB,226.
	DEFOP PUTP,227.
	DEFOP QUIT,186.
	DEFOP RANDOM,231.,%VAL
	DEFOP READ,228.
	DEFOP REMOVE,137.
	DEFOP RESTART,183.
	DEFOP RESTORE,182.,%PRED
	DEFOP RETURN,139.
	DEFOP RFALSE,177.
	DEFOP RSTACK,184.
	DEFOP RTRUE,176.
	DEFOP SAVE,181.,%PRED
	DEFOP SET,13.
	DEFOP SUB,21.,%VAL
	DEFOP USL,188.
	DEFOP VALUE,142.,%VAL
	DEFOP VERIFY,189.,%PRED
	DEFOP ZERO?,128.,%PRED

OPCNT==<.-OPS>/2	;number of pseudos and operators altogether

SUBTTL START UP -- READ JCL AND OPEN INPUT FILE

START:	RESET
	MOVE	P,[-77,,PDL]
	SETZ	A,
	RSCAN
	 JFCL
	JUMPE	A,NOJCL			; NO JCL, FLUSH

;read jcl line
	MOVN	C,A
	MOVEI	A,.PRIIN
	MOVE	B,[440700,,FILBUF]
	SIN				; READ JCL

;parse jcl line
	MOVE	B,[440700,,FILBUF]
NAMLOP:	ILDB	A,B
	CAILE	A,40
	 JRST	NAMLOP
NAMDON:	CAIE	A,^M
	 CAIN	A,^J
	  JRST	NOJCL
	MOVEM	B,FILPTR	;should be file spec start
	ILDB	A,B
	CAIL	A,40
	 JRST	.-2
	MOVEI	A,0
	DPB	A,B
	MOVE	B,FILPTR
	PUSHJ	P,OPEN		;open file
	JRST	BEGIN

;here if no jcl, read file name from tty
NOJCL:	PUSHJ	P,TOPEN
	JRST	BEGIN

SUBTTL FILE NAME READING AND FILE OPENING

OPEN:	PUSHJ	P,FOPEN
	 JRST	TOPEN		;open failed, try from tty
	POPJ	P,

;read file name from tty
TOPEN:	MSG	[
File: ]
	PSOUT
	MOVEI	A,GTJFNT
	MOVEI	B,0
	PUSHJ	P,FOPEN1
	 JRST	TOPEN
	POPJ	P,

;open a file
; b/ file name
;skips if wins
FOPEN:	MOVEI	A,GTJFNB
	PUSH	P,B
	GTJFN
	 SKIPA
	  JRST	FOPEN2
	MOVEI	A,GTJFNX
	MOVE	B,(P)
	JRST	FOPEN0

FOPEN1:	PUSH	P,B
FOPEN0:	GTJFN
	 JRST	NOFILE
FOPEN2:	TLZ	A,-1
	MOVEM	A,IJFN			; SAVE CURRENT INPUT JFN
	MOVE	B,[070000,,240000]
	OPENF				; HAS TO BE OPEN
	 JRST	NOFIL1
	POP	P,B
	AOS	(P)
	POPJ	P,

;gtjfn failed for some reason
NOFILE:	MOVE	B,A
	MSG	[Open failed?]
NOFIL4:	PSOUT
	POP	P,C
	JUMPE	C,NOFIL3
	MSG	[ (]
	PSOUT
	MOVE	A,C
NOFIL2:	PSOUT
	MSG	[)]
	PSOUT
NOFIL3:	MSG	[: ]
	PSOUT

;print error string
ERPRNT:	HRRZI	A,-1
	HRLI	B,400000
	MOVEI	C,0
	ERSTR		; PRINT ERROR
	 POPJ	P,	;UNDEFINED ERROR.
	 POPJ	P,	;CHOMPING DEST.
	 POPJ	P,	;WON.
	POPJ	P,

;openf failed for some reason
NOFIL1:	MOVE	B,A
	MSG	[Can't OPENF file?]
	JRST	NOFIL4


SUBTTL BEGIN ASSEMBLING

;print filename being assembled
BEGIN:	SKIPN	DOFREQ
	 JRST	BEGINF
	MSG	[Counting ]
	SKIPA
BEGINF:  MSG	[Assembling ]
	PUSHJ	P,PFNAME	;tell name of file being read

;find out release number since it's alway wrong in the ZAP file
	MSG	[Time Mode?: ]
	PSOUT
	PBIN
	SETZ	B,
	CAIE	A,"T
	 CAIN	A,"Y
	  JRST	[TRO	B,%TIMESL
		 MSG	[ <yes>]
		 JRST	.+2]
	MSG	[ <no>]
	PSOUT
	PUSHJ	P,PCRLF
;	MSG	[Byte Swapped?: ]
;	PSOUT
;	PBIN
;	CAIE	A,"T
;	 CAIN	A,"Y
;	  TRO	B,%BYTSWP
;	PUSHJ	P,PCRLF
	MOVEM	B,FLGWRD
	MSG	[Release: ]
	PSOUT
	MOVEI	A,.PRIIN
	MOVEI	C,10.
	SETOM	RELEAS
	NIN
	 JRST	GETFNM		;lost, use default
	JUMPL	B,GETFNM
	MOVEM	B,RELEAS	;save and use instead of supplied

;get goodies so can open correct output file
GETFNM:	MOVE	A,OUTPTR
	MOVE	B,IJFN
	MOVE	C,[222000,,JS%PAF] ;output dev:<dir>name.
	JFNS
	MOVEM	A,OUTPTR	;save for outputting other exts.
	SKIPE	DOFREQ
	 JRST	BEGLUP		;do frequency assembly

	MOVE	Z,[441000,,OUTBUF]	;byte ptr to output buffer
	MOVEI	ZPC,0		;pc initially zero
	PUSHJ	P,SCRIPT	;open script channel if asked
	PUSHJ	P,GLBINI	;initialize global symbol table
	PUSHJ	P,LCLINI	;initialize local symbol table

;here to create references to the first n words, which are special
	MOVE	A,ZAPID
	PUSHJ	P,OUTBYT
	MOVE	A,FLGWRD
	PUSHJ	P,OUTBYT
	SKIPGE	A,RELEAS		;user gave a release number?
	 JRST	NORELE
	PUSHJ	P,OUTWRD
	JRST	DEFWDS

NORELE:	HRROI	B,[ASCIZ /.WORD ZORKID
/]
	HRROI	A,BUFFER
	MOVEI	C,0
	SOUT
	PUSHJ	P,ASSEM

;output always defined words
DEFWDS:	HRROI	B,[ASCIZ /.WORD ENDLOD,START,VOCAB,OBJECT,GLOBAL,IMPURE,0,0,0,0,WORDS
/]
	HRROI	A,BUFFER	;copy to buffer
	MOVEI	C,0
	SOUT
	PUSHJ	P,ASSEM		;assemble it

BEGWDS:	MOVEI	A,0
	PUSHJ	P,OUTWRD
	CAIGE	ZPC,100
	 JRST	BEGWDS

BEGLUP:	PUSHJ	P,RDLINE		;read a line, no skip if done
	 JRST	DONE
	SKIPE	PDEBUG
	 PUSHJ	P,PINPUT
	PUSHJ	P,ASSEM			;assemble line
	SKIPE	PDEBUG
	 CAMN	Z,SAVZ
	  JRST	BEGLUP
	PUSHJ	P,OPC
	JRST	BEGLUP

PINPUT:	PUSH	P,A
	PUSH	P,B
	PUSH	P,C
	MOVE	A,PDEBUG
	MOVEI	C,0
	HRROI	B,[ASCIZ /
	;/]
	SOUT
	HRROI	B,BUFFER
	SOUT			;print it (for debugging)
	MOVEM	ZPC,SAVZPC
	MOVEM	Z,SAVZ
	JRST	POPCBA

SUBTTL DONE - FINISH UP, PRINT STATS, ETC.

DONE:	SKIPE	DOFREQ
	 JRST	FILEND
	PUSHJ	P,UNDGLB	;print undefined globals
	MSG	[
]
	PSOUT
	MOVEI	A,.PRIOU
	MOVE	B,ZPC
	MOVEI	C,10.
	NOUT
	 JFCL
	MSG	[ bytes.
]
	PSOUT
	MOVEI	A,.PRIOU
	MOVE	B,OBJTOT
	MOVEI	C,10.
	NOUT
	 JFCL
	MSG	[ objects.
]
	PSOUT
	MOVEI	A,.PRIOU
	MOVE	B,GLBTOT
	MOVEI	C,10.
	NOUT
	 JFCL
	MSG	[ globals.
]
	PSOUT
	SKIPE	TWOPAS	;don't bother if two pass assembly
	 JRST	OUTPUT
	MOVEI	A,.PRIOU
	MOVE	B,SHRIMP
	MOVEI	C,10.
	NOUT
	 JFCL
	MSG	[ wasted long jumps.
]
	PSOUT


;here to force pc to value in A
SETZPC:	MOVE	ZPC,A
	MOVE	Z,[441000,,OUTBUF]
	EXCH	A,Z
	ADJBP	Z,A
	POPJ	P,

;here to output date stuff for serial number in ascii
;a/ number
OUTDAT:	PUSH	P,B
	IDIVI	A,10.
	ADDI	A,"0
	PUSHJ	P,OUTBYT
	MOVEI	A,"0(B)
	PUSHJ	P,OUTBYT
	POP	P,B
	POPJ	P,

;here to output the data
OUTPUT:	MOVEM	Z,SAVZ
	MOVEM	ZPC,SAVZPC
	MOVEI	A,32		; where the length lives
	PUSHJ	P,SETZPC	
	MOVE	A,SAVZPC	; get back the final top pc
	LSH	A,-1		; make it in words
	PUSHJ	P,OUTWRD
	MOVEI	A,77		; start at byte 100 octal
	PUSHJ	P,SETZPC
	SETZ	D,		; zero the checksum
OUTCL:	CAMN	ZPC,SAVZPC	; loop until through the entire file
	 JRST	OUTCHK
	ILDB	B,Z		; get the byte
	ADD	D,B		; and add it into checksum
	AOJA	ZPC,OUTCL
OUTCHK:	MOVEI	A,34		; where the checksum lives
        PUSHJ	P,SETZPC
	MOVE	A,D
	ANDI	A,177777	; only 15 bits worth, though
	PUSHJ	P,OUTWRD
	MOVEI	A,22		; where serial number lives
	PUSHJ	P,SETZPC
	MOVNI	B,1
	ODCNV			; get current time/date
	HLRZ	A,B		; here's the year
	SUBI	A,1900.		; we will take only the mod 100 part
	PUSHJ	P,OUTDAT
	HRRZ	A,B		; here's the month (starting at 0)
	ADDI	A,1		; so fix it up here
	PUSHJ	P,OUTDAT
	HLRZ	A,C		; here's the day (starting at 0)
	ADDI	A,1		; so fix it up here
	PUSHJ	P,OUTDAT

	MOVE	Z,SAVZ
	MOVE	ZPC,SAVZPC
	MOVE	A,[440700,,[ASCIZ /.ZIP/]]
	MOVE	B,OUTPTR
	ILDB	0,A
	IDPB	0,B
	JUMPN	0,.-2
	MOVSI	A,(GJ%SHT+GJ%FOU)
	HRROI	B,OUTFIL
	GTJFN
	 JRST	ERPRNT
	HRRZ	A,A
	MOVE	B,[440000,,OF%WR]
	OPENF
	 JRST	ERPRNT
;blat out stupid gcdump header
	HRRM	ZPC,HEADER+5
	MOVEI	C,3(Z)
	SUBI	C,OUTBUF
	HRLM	C,FOOTER+1
	ADDI	C,2006
	HRRM	C,FOOTER+1
	SUBI	C,2006-2
	MOVEM	C,HEADER
	MOVEM	C,HEADER+1
	MOVEM	C,HEADER+2
	MOVE	B,[444400,,HEADER]
	MOVNI	C,7
	SOUT
;blat out data
	MOVE	B,[444400,,OUTBUF]
	MOVEI	C,1(Z)
	SUBI	C,OUTBUF
	MOVN	C,C
	SOUT
;blat out stupid footer
	MOVE	B,[444400,,FOOTER]
	MOVNI	C,2
	SOUT
;close up and go home
	CLOSF
	 JFCL
	SKIPE	A,PDEBUG
	 CLOSF
	HALTF
	HALTF

;print name of IJFN file, takes prefix string in A
PFNAME:	PSOUT
	MOVEI	A,.PRIOU
	MOVE	B,IJFN
	MOVE	C,[222220,,JS%PAF]
	JFNS
	PUSHJ	P,PCRLF
	POPJ	P,

SCRIPT:	SKIPL	PDEBUG
	 POPJ	P,
	MOVE	A,[440700,,[ASCIZ /.SCRIPT/]]
	MOVE	B,OUTPTR
	ILDB	0,A
	IDPB	0,B
	JUMPN	0,.-2
	MOVSI	A,(GJ%SHT+GJ%FOU)
	HRROI	B,OUTFIL
	GTJFN
	 JRST	ERPRNT
	HRRZ	A,A
	MOVEM	A,PDEBUG
	MOVE	B,[070000,,OF%WR]
	OPENF
	 JRST	ERPRNT
	POPJ	P,

SUBTTL READ A LINE FROM INPUT FILE

RDLINE:	SKIPN	A,IJFN		;no eof yet?
	 POPJ	P,		; eof, return
	PUSH	P,B
	HRROI	B,BUFFER
	MOVEI	C,512.*5
	MOVEI	D,^J		;stop on crlf
	SIN			;read a line
	 ERJMP	RDEOF
	MOVEI	A,0		;terminate with nul
	IDPB	A,B		;zero byte
	POP	P,B
POPJ1:	AOS	(P)
CPOPJ:	POPJ	P,

RDEOF:	MOVE	A,IJFN
	CLOSF			;close input file
	 JRST	ERPRNT
	SETZM	IJFN		;eof found
	POP	P,B
	JRST	POPJ1

;parse a line into tokens; may require reading more lines if it's a string
GTLINE:	MOVE	A,[440700,,TOKEN]
	MOVEM	A,TOKPTR
	MOVE	TP,TPDL
GTLIN1:	PUSHJ	P,GTOKEN	;get a token
	PUSH	TP,B		;push string
	PUSH	TP,A		;push terminator
	JUMPN	A,GTLIN1
	PUSH	TP,[0]		;end of line, push zeros
	PUSH	TP,[0]		;end of line, push zeros
	POPJ	P,

;print a token
PTOKEN:	SKIPN	TDEBUG
	 POPJ	P,
	EXCH	A,B
	SKIPE	A
	 PSOUT			;string part
	EXCH	A,B
	JUMPE	A,PCRLF
	 PBOUT			;terminator part
	POPJ	P,
PCRLF:	MSG	[
]
	PSOUT
	MOVEI	A,0
	POPJ	P,

SUBTTL PARSE A TOKEN FROM INPUT LINE
;returns a/ break char, b/ ptr to token
GTOKEN:	MOVE	B,TOKPTR
GTOKE1:	ILDB	A,C
	JUMPE	A,RTERM
	CAIG	A,40
	 JRST	GTOKE1		;skip over leading junk
	JRST	RTOK3
RTOKEN:	ILDB	A,C
RTOK3:	CAIG	A,40
	 JRST	RTERM
	CAIE	A,":		;label
	CAIN	A,"+		;sum
	 JRST	RTERM
	CAIE	A,"=		;definition
	CAIN	A,"/		;then jump
	 JRST	RTERM
	CAIE	A,"\		;else jump
	CAIN	A,",		;separator
	 JRST	RTERM
	CAIE	A,">		;assignment
	CAIN	A,"'		;quoting
	 JRST	RTERM
	CAIN	A,";		;start of comment
	 JRST	RCOMNT		; ignore comment
	CAIN	A,""		;start of string
	 JRST	RSTRNG		;read string
;else part of token
RTOK1:	IDPB	A,B		;build token
	JRST	RTOKEN		;loop

;here to read a string
RSTRNG:	CAME	B,TOKPTR	;anything read yet?
	 JRST	RSTR3		; yes
RSTR1:	ILDB	A,C
	JUMPE	A,[PUSHJ P,MORSTR
		   JRST	RSTR1]	;need to read another line from file
	CAIN	A,""		;end of string
	 JRST	RSTRQ
RSTR2:	IDPB	A,B
	JRST	RSTR1

RSTR3:	DPB	C	;here if string bung up against other token
	MOVEI	A,40	;fake a space
	JRST	RTERM	;and return

;here to check for ""
RSTRQ:	MOVE	0,C
	ILDB	A,C
	JUMPE	A,[PUSHJ P,MORSTR
		   JRST	RSTRQ]
	CAIN	A,""
	 JRST	RSTR2	;is ", ship it
	MOVE	C,0	;restore bptr
	MOVEI	A,""	;pretend was "
	JRST	RTERM	;not a ", return

;here to snarf another line for multi-line strings
MORSTR:	PUSHJ	P,RDLINE
	 JRST	STRERR
	MOVE	C,[440700,,BUFFER]
	POPJ	P,

STRERR:	MSG	[String not terminated at eof.]
	PUSHJ	P,ERROR
	POPJ	P,

;here to read and ignore a comment
RCOMNT:	MOVEI	A,0
RTERM:	CAMN	B,TOKPTR
	 CAIN	A,""		;allow empty strings
	  SKIPA
	   JRST	RNONE
	MOVEI	0,0
	IDPB	0,B		;asciz
	EXCH	B,TOKPTR
	POPJ	P,

;here for nothing read
RNONE:	MOVEI	B,0
	POPJ	P,


SUBTTL SYMBOL LOOKUP FOR CONSTANT TABLES

;takes: a/ symbol to lookup
;retns +2 won, b/ value
;      +2 lost
LOOKUP:	MOVNI	C,1		;low bound
	MOVEI	E,OPCNT		;high bound
LOOKLP:	MOVE	D,C
	ADD	D,E
	TRZ	D,1		;make it an even number
	MOVE	B,OPS(D)
	HRLI	B,440700
	PUSHJ	P,COMPAR	; a/ token b/ table
	 JRST	LOOKWN		; a=b
	 JRST	LOOKLS		; a>b
	LSH	D,-1
	MOVE	C,D		; a<b
	JRST	LOOKND

LOOKLS:	LSH	D,-1
	MOVE	E,D

LOOKND:	CAIGE	C,-1(E)
	 JRST	LOOKLP
	POPJ	P,		;lost, no skip

LOOKWN:	MOVE	B,OPS+1(D)	;return value
	AOS	(P)
	POPJ	P,

;compare two strings
;a/ token b/ table
;no skip: a=b
;+1 skip: a>b
;+2 skip: a<b
COMPAR:	PUSH	P,A
	PUSH	P,B
	PUSH	P,C
	PUSH	P,D
COMPA1:	ILDB	C,A
	ILDB	D,B
	CAIN	C,(D)
	 JRST	COMEQU		;characters same
	CAIL	C,(D)
	 AOS	-4(P)		;a>b
	AOS	-4(P)		;a<b
COMEXI:	POP	P,D
	POP	P,C
	POP	P,B
	POP	P,A
	POPJ	P,

COMEQU:	JUMPE	C,COMEXI	;if end of string, win
	JRST	COMPA1		;else continue	

LOOKER:	MOVE	F,[-OPCNT,,OPS]
LOOKIT:	MOVE	A,(F)
	PSOUT
	PUSHJ	P,PCRLF
	PUSHJ	P,LOOKUP
	 HALTF	
	ADDI	F,1
	AOBJN	F,LOOKIT
	POPJ	P,

SUBTTL SOME DEBUGGING ROUTINES

;used to make sure zpc and z are always in tandem
CHKZPC:	PUSH	P,A
	PUSH	P,Z
	PUSH	P,ZPC
	HRRZ	A,Z
	SUBI	A,OUTBUF
	LSH	A,2
	HLRZ	Z,Z
	CAIN	Z,441000
	 ADDI	A,0
	CAIN	Z,341000
	 ADDI	A,1
	CAIN	Z,241000
	 ADDI	A,2
	CAIN	Z,141000
	 ADDI	A,3
	CAIN	Z,41000
	 ADDI	A,4
	CAME	A,ZPC
	 HALTF
	POP	P,ZPC
	POP	P,Z
	POP	P,A
	POPJ	P,

;here start printing goodies if pc has reached a certain value
STOPPE:	CAMGE	ZPC,STOP
	 POPJ	P,
	MOVEM	ZPC,SAVZPC
	MOVEM	Z,SAVZ
	MOVEI	.PRIOU
	MOVEM	PDEBUG
	SETZM	STOP
	POPJ	P,

SUBTTL ASSEMBLE A LINE

ASSEM:	SKIPE	STOP		;supposed to stop sometime?
	 PUSHJ	P,STOPPER	; yes, see if now
	SETZM	NOREF		;produce references
	SETZM	WRDBYT		;initially assume assembling word

;here to check that symbol pname tables haven't overflowed
	MOVE	C,LCLPTR
	CAIL	C,LCLTAB
	 HALTF
	MOVE	C,GLBPTR
	CAIL	C,GLBTAB
	 HALTF
;read and parse input line
	MOVE	C,[440700,,BUFFER] ;set up ptr to input buffer
	PUSHJ	P,GTLINE
	MOVE	TP,TPDL
	ADD	TP,[1,,1]
	SKIPN	(TP)
	 POPJ	P,		;nothing on this line

;if frequency assembly, ignore all this foofaraw
	SKIPE	DOFREQ
	 JRST	FREQ		;do something else instead

;label?
	MOVE	A,1(TP)		;get terminator
	CAIE	A,":
	 JRST	AOP
;line starts with a label
	SKIPN	2(TP)		;second token?
	 SKIPN	3(TP)
	  JRST	LCLLBL		;empty line, more or less
	MOVE	A,3(TP)		;get terminator
	CAIE	A,":
	 JRST	BDLBSY		;bad label syntax: foo:<x> for x not :
;global label
GLBLBL:	SKIPE	FZ		;time for function second pass?
	 PUSHJ	P,FPASS2	; yes
       	MOVE	B,(TP)		;global label
	MOVE	C,ZPC		;label is current pc
	PUSHJ	P,DEFGLB	;define it
	 JRST	BDMDGL		;multiply defined global label
	NXTARG	2		;move over label and colons
	JRST	AOP
;local label
LCLLBL:	SKIPN	A,FUNCT		;is there a function these days?
	 JRST	GLBLBL		;else it might as well be a global
	MOVE	B,(TP)		;get token
	MOVE	C,ZPC		;label is current pc
	PUSHJ	P,DEFLCL	;define it
	 JRST	BDMDLL		;multiply defined local label
	NXTARG	1		;move over local label
	JRST	AOP

BDLABL:	MSG	[Multiply defined label]
BDLAB1:	MOVE	B,(TP)
	PUSHJ	P,ERRMSG	;shout lossage
	JRST	AOP		;but continue

BDLBSY:	MSG	[Label followed by :, non-colon]
	JRST	BDLAB1

;here we have reached an opcode or pseudo after flushing label
AOP:	SKIPN	A,(TP)
	 SKIPE	1(TP)
	  SKIPA
	 POPJ	P,
	PUSHJ	P,LOOKUP	;takes symbol in A
	 JRST	AEQUAL		; not any sort of op.
	JUMPL	B,APSEUDO	;pseudo
	JRST	AOPER		;regular op

;here not oper or pseudo

;see if it's an atom=foo
AEQUAL:	SKIPE	A,1(TP)
	 CAIE	A,"=
	  JRST	AATOM
	MOVE	B,2(TP)		;value
	PUSHJ	P,FIXQ
	 JRST	BDEQUA		;FOO=<non-fix>?
	MOVE	C,B
	MOVE	B,(TP)
	PUSHJ	P,DEFGLB
	 JRST	BDEQU1		;already defined?
	SKIPN	4(TP)
	 SKIPE	5(TP)
	  JRST	BDEQU2		;too many args to equal?
	POPJ	P,

;see if it's an atom
AATOM:	PUSHJ	P,AWORD
	 JFCL
	POPJ	P,

SUBTTL ASSEMBLE WORDS AND BYTES

;get value of symbol
; returns A/ terminator B/ value
ALCL:	PUSH	P,C
	MOVEI	C,0		;symbol is a zero
	MOVE	B,(TP)
	PUSHJ	P,REFLCL
	MOVE	B,SYMVAL(A)
	JRST	AGNEXT

AGET:	PUSH	P,C
	MOVEI	C,0		;symbol is a zero
AGLOOP:	MOVE	B,(TP)
	PUSHJ	P,FIXQ
	 JRST	[MOVE	B,(TP)
		 PUSHJ	P,REFSYM
		 SKIPGE	B,SYMVAL(A)
		  MOVSI	B,%UNDEF
		 JRST	.+1]
AGNEXT:	ADD	C,B		;accumulate value
	NXTARG	1
	SKIPN	A,-1(TP)	;terminator?
	 JRST	AGEXI1		;no skip if last thing on line
	CAIN	A,"+
	 JRST	AGLOOP
AGEXIT:	AOS	-1(P)
AGEXI1:	MOVE	B,C
	POP	P,C
	POPJ	P,

AWORD:	SETZM	WRDBYT	;means working on word
	PUSHJ	P,AGET
	 SOS	(P)
	MOVE	A,B
	TLZ	A,%BITS
	PUSHJ	P,OUTWRD
	AOS	(P)
	POPJ	P,

ABYTE:	SETOM	WRDBYT	;means working on byte
	PUSHJ	P,AGET
	 SOS	(P)
	MOVE	A,B
	TLZ	A,%BITS
	PUSHJ	P,OUTBYT
	AOS	(P)
	POPJ	P,


SUBTTL OUTPUT WORDS

;output a word
; a/ word
OUTWRD:	CAILE	A,177777	;check size
	 JRST	WRDBIG		; lose, too big
OUTWR1:	LSHC	A,-8.
	PUSHJ	P,OUTBY1	;output first byte
	MOVEI	A,0
	ROTC	A,8.
	PUSHJ	P,OUTBY1	;output second byte
	POPJ	P,

;add a value to an already output word (used for fixups)
; a/ word
ADDWRD:	CAILE	A,177777	;too big?
	 JRST	WRDBIG		; yes, lose
	LSHC	A,-8.
	PUSHJ	P,ADDBYT	;add first byte
	MOVEI	A,0
	ROTC	A,8.
	PUSHJ	P,ADDBYT	;add second byte
	POPJ	P,

;output word reference
; a/ word
OUTWRF:	CAILE	A,177777	;too big?
	 JRST	WRDBIG		; yes, lose
	LSHC	A,-8.
	PUSHJ	P,OUTBY1
	MOVEI	A,0
	ROTC	A,8.
	PUSHJ	P,OUTBY1
	POPJ	P,

;error, word is too large
WRDBIG:	MSG	[Word too large]
	PUSHJ	P,ERROR
	MOVEI	A,0
	JRST	OUTWR1

SUBTTL OUTPUT BYTES

;output a byte
; a/ byte
OUTBYT:	CAILE	A,377		;too big?
	 JRST	BYTBIG		; too big, lose
;enter here to just output the byte directly
OUTBY1:	IDPB	A,Z		;output byte
	ADDI	ZPC,1		;increment pc
	MOVE	0,(P)
	SKIPN	TABLE
	 CAIL	0,SLOOK
	  POPJ	P,
	SKIPN	PASS2
	 AOS	CODLEN'
	POPJ	P,

;output byte reference
; a/ byte
OUTBRF:	CAILE	A,377		;too big?
	 JRST	BYTBIG		; yes, lose
	PUSHJ	P,OUTBY1
	POPJ	P,

;same as outbyt, but adds in new value (for fixup)
; a/ byte
ADDBYT:	CAILE	A,377
	 JRST	BYTBIG
	PUSH	P,B
	ILDB	B,Z		;pick up current contents
	ADD	A,B		;add new stuff in
	DPB	A,Z		;put it back out
	ADDI	ZPC,1
	POP	P,B
	POPJ	P,

;here byte was too large (>255.)
BYTBIG:	MSG	[Byte too large]
	PUSHJ	P,ERROR
	MOVEI	A,0
	JRST	OUTBY1

SUBTTL PRINT BYTES AND PCS (FOR DEBUGGING)

OBYTE:	PUSH	P,A
	PUSH	P,B
	PUSH	P,C
	MOVE	B,A
	MOVE	A,PDEBUG
	MOVEI	C,8
	HRLI	C,(NO%LFL+NO%ZRO)+3
	NOUT
	 JFCL
	MOVEI	B," 
	BOUT
	JRST	POPCBA

OPC:	PUSH	P,A
	PUSH	P,B
	PUSH	P,C
	MOVE	B,SAVZPC
	MOVE	A,PDEBUG
	MOVEI	C,8
	NOUT
	 JFCL
	HRROI	B,[ASCIZ !/  !]
	MOVEI	C,0
	SOUT
OBYLUP:	ILDB	A,SAVZ
	PUSHJ	P,OBYTE
	CAME	Z,SAVZ
	 JRST	OBYLUP
	JRST	POPCBA

SUBTTL VARIOUS ERRORS

BDMDGL:	MSG	[Multiply defined global label]
	JRST	BDERRO
BDMDLL:	MSG	[Multiply defined local label]
	JRST	BDERRO
BDMDLV:	MSG	[Multiply defined local variable]
	JRST	BDERRO
BDEQUA:	MSG	[Something assigned to non-fix]
	JRST	BDERRO
BDEQU1:	MSG	[Something already assigned]
	JRST	BDERRO
BDEQU2:	MSG	[Too many args to equal]
BDERRO:	PUSHJ	P,ERROR
	POPJ	P,


SUBTTL IS IT A FIX?
;given string pointer, skips if it's a number
;returns number in B
FIXQ:	PUSH	P,C
	PUSH	P,D
	MOVE	C,B
	MOVEI	B,0
	SETZ	D,
FIXQ1:	ILDB	A,C
	JUMPE	A,FIXEND
	CAIN	A,"-
	 JRST	[SETO D,
		 JRST FIXQ1]
	CAIL	A,"0
	 CAILE	A,"9
	  JRST	[POP P,D
		 POP P,C
		 POPJ P,]
	SUBI	A,"0
	IMULI	B,10.
	ADD	B,A
	JRST	FIXQ1

FIXEND:	CAILE	B,177777
	 JRST	FIXBIG
	SKIPE	D
	 MOVN	B,B
	ANDI	B,177777
FIXEN1:	POP	P,D
	POP	P,C
	JRST	POPJ1

FIXBIG:	MSG	[Fix too big for a word]
	PUSHJ	P,ERROR
	MOVE	B,177777
	JRST	FIXEN1

SUBTTL PSEUDO-OPS

;dispatch for pseudo-ops
APSEUD:	SKIPE	FZ		;time for a function second pass?
	 PUSHJ	P,FPASS2	; yes, go do it
APSEU1:	SETZM	PASS2
	HRRZ	B,B
	CAIN	B,ZFUNCT	;if not .funct, skip
	 PUSHJ	P,UNDLCL
	JRST	(B)

SUBTTL .END .INSERT AND .ENDI

;end of assembly
ZEND:	MOVE	A,IJFN
	CLOSF
	 JRST	ERPRNT
	SETZM	IJFN
	POPJ	P,

;insert another file
ZINSER:	SKIPE	OJFN
	 JRST	ZINSIN
	MOVE	A,3(TP)
	CAIE	A,""
	 JRST	ZINSTR	;not a string
	MOVE	A,IJFN
	MOVEM	A,OJFN
	MOVE	B,2(TP)
	PUSHJ	P,OPEN
	MSG	[Inserting  ]
	PUSHJ	P,PFNAME
	POPJ	P,

ZINSIN:	MSG	[Already in .INSERT?]
	PUSHJ	P,ERROR
	POPJ	P,

ZINSTR:	MSG	[Argument to .INSERT not string?]
	PUSHJ	P,ERROR
	POPJ	P,

;end an insertion
ZENDI:	SKIPN	B,OJFN
	 JRST	ZENDLS
	MOVE	A,IJFN
	CLOSF
	 JRST	ZENDCL
	SETZM	OJFN
	MOVEM	B,IJFN
	POPJ	P,

ZENDLS:	MSG	[.ENDI not in .INSERT?]
	PUSHJ	P,ERROR
	POPJ	P,

ZENDCL:	MSG	[.ENDI close failed?]
	PUSHJ	P,ERROR
	POPJ	P,

SUBTTL TABLES

ZTABLE:	MOVEM	ZPC,TABLE
	SETOM	TABLEN
	NXTARG	1
	SKIPN	B,(TP)
	 POPJ	P,
	PUSHJ	P,FIXQ
	 JRST	ZTNOTF
	MOVEM	B,TABLEN
	POPJ	P,

ZTNOTF:	MSG	[Argument to .TABLE not fix]
	PUSHJ	P,ERROR
	POPJ	P,

ZENDT:	SKIPN	TABLE
	 JRST	ZETNOT
	SKIPGE	A,TABLEN
	 JRST	ZENDTX
	ADD	A,TABLE
	CAML	A,ZPC
	 JRST	ZENDTX
	MSG	[Table too large]
	PUSHJ	P,ERROR
	POPJ	P,

ZENDTX:	SETZM	TABLE
	SETZM	TABLEN
	POPJ	P,

ZETNOT:	MSG	[.ENDT not after .TABLE]
	PUSHJ	P,ERROR
	POPJ	P,

ZEQUAL:	SKIPN	B,4(TP)
	 JRST	ZEQTFA
	PUSHJ	P,FIXQ
	 JRST	ZEQANF
	MOVE	C,B
	PUSHJ	P,DEFNAM
	 JRST	ZEQMDG
	POPJ	P,

ZEQMDG:	MSG	[Already defined]
	PUSHJ	P,ERROR
	POPJ	P,
ZEQANF:	MSG	[Second argument to .EQUAL not fix]
	PUSHJ	P,ERROR
	POPJ	P,
ZEQTFA:	MSG	[Too few arguments to .EQUAL]
	PUSHJ	P,ERROR
	POPJ	P,

SUBTTL NAMED THINGS: FUNCTIONS, GLOBAL STRINGS, VARIABLES, OBJECTS

;define a named thing, value in C
DEFNAM:	MOVE	B,2(TP)		;pname
	PUSHJ	P,DEFGLB	;define symbol
	 JRST	DEFMLT		;already defined
	NXTARG	2		;move over pseudo and name
	AOS	(P)
	POPJ	P,
;complain about multiply defined thing
DEFMLT:	MSG	[Multiply defined ]
	MOVE	B,(TP)
	PUSHJ	P,ERRMSG
	POPJ	P,

;force a word boundary
WRDBDY:	TRNN	ZPC,1
	 POPJ	P,
	PUSH	P,A
	MOVEI	A,0
	PUSHJ	P,OUTBYT
	POP	P,A
	POPJ	P,

SUBTTL FUNCTIONS

ZFUNCT:	PUSHJ	P,WRDBDY	;force word boundary
	SKIPN	2(TP)
	 JRST	ZFNONE		;no name?
	MOVE	C,ZPC
	LSH	C,-1		;functions are always on word bdy.
	MOVEM	C,FSYM		;save symbol value of last function
	PUSHJ	P,DEFNAM
	 POPJ	P,
	MOVE	A,LSTSYM	;pick up last defined symbol
	MOVEM	A,FUNCT		;new function
;print functions and locs if asked for
	SKIPE	FDEBUG
	 PUSHJ	P,PFUNCT
;here hack arguments
	MOVEI	D,0		;current lval
	MOVE	E,Z		;save current bptr
	IDPB	D,Z		;start with zero
	ADDI	ZPC,1
ZFLOOP:	SKIPN	B,(TP)		;is there one?
	 JRST	ZFDONE		;nope, done
	ADDI	D,1		;bump arg count
	MOVE	C,D		;which local?
	TLO	C,%VAR
	PUSHJ	P,DEFLCL	;define it as a local
	 JRST	BDMDLV
	SKIPE	A,1(TP)
	 CAIE	A,"=
	  JRST	ZFNEXT
	NXTARG	1		;move over variable name
	SKIPN	B,(TP)
	 JRST	ZFNOEQ
	PUSHJ	P,AWORD		;assemble word
	 JFCL
	JRST	ZFLOOP

ZFNEXT:	MOVEI	A,0
	PUSHJ	P,OUTWRD	;bind it to 0
	NXTARG	1		;move over variable name
	JRST	ZFLOOP

ZFDONE:	IDPB	D,E		;now fake output of argument count	

;save goodies for function pass two
;can be called on its own, be careful!
FMARK:	MOVE	A,IJFN
	RFPTR
	 HALTF
	MOVEM	B,FPOS		;save file pointer
	MOVEM	Z,FZ		;save output pointer
	MOVEM	ZPC,FZPC	;save pc
	MOVE	A,SHRIMP
	MOVEM	A,OSHRIM
	POPJ	P,

ZFNONE:	MSG	[No name given to function?]
	PUSHJ	P,ERROR
	POPJ	P,
ZFNOEQ:	MSG	[Argument = not followed by value?]
	PUSHJ	P,ERROR
	POPJ	P,

;here to set up second pass over functions with short jumps
FPASS2:	SKIPN	TWOPASS		;skip if two pass assembly of functions
	 POPJ	P,		;else return immediately
	CAMN	ZPC,FZPC
	 JRST	[PUSHJ P,FMARK
		 POPJ P,]
       	SETOM	PASS2
       	MOVE	A,OSHRIM	;count of wasted long jumps
	;CAML	A,SHRIMP	; what it was when function started
	 ;POPJ	P,		;resume, false alarm
       	MOVEM	A,SHRIMP	
	MOVE	A,IJFN
	MOVE	B,FPOS
	SFPTR
	 HALTF
	MOVE	Z,FZ
	MOVEM	Z,SAVZ		;fool debugging printer
	MOVE	ZPC,FZPC
	SETZM	FPOS		;file pointer of start of function
	SETZM	FZ		;z at start of function
	SETZM	FZPC		;zpc at start of function
	SETZM	FSHORT		;count of short jumps
	POP	P,0		;flush call to fpass2
	POPJ	P,		;return from caller

;.FSTR -- like .GSTR but adds to table of frequent strings
ZFSTR:	SKIPN	A,4(TP)
	 JRST	TFARG
	PUSHJ	P,WLOOK
	 SKIPA
	JRST	ZFDUP	;duplicate of frequent string?  lose!
;here to add new string to table
	MOVE	A,TABPTR
	TLNN	A,400000
	 JRST	[HRLI A,440700
		 ADDI A,1
		 JRST .+1]
	MOVE	H,A
	MOVE	B,4(TP)
	MOVEI	C,0
	SOUT		;copy string to buffer
	IDPB	C,A
	MOVEM	A,TABPTR
;update table pointer
	PUSH	P,G
	MOVE	G,WRDTAB
	SUB	G,[2,,2]
	MOVEM	G,WRDTAB
	POP	P,G
;make a slot for new entry
	HRRZ	A,WRDTAB
	HRLI	A,2(A)
	BLT	A,-1(G)
;put out new entry
	MOVEM	H,-1(G)	;string
	AOS	H,FSTRS
	MOVEM	H,-2(G)	;count
	CAIG	H,%FWDCT
	 JRST	ZFSTR1
	MSG	[Too many .FSTRs]
ZFERR:	PUSHJ	P,ERROR
	POPJ	P,

ZFDUP:	MSG	[Duplicate .FSTR]
	JRST	ZFERR

ZFSTR1:	PUSHJ	P,WRDBDY
	MOVE	C,ZPC
	LSH	C,-1
	PUSHJ	P,DEFNAM
	 POPJ	P,
	SKIPN	A,(TP)
	 JRST	TFARG
	PUSHJ	P,MAKFST
	POPJ	P,


;.GSTR -- global string
ZGSTR:	PUSHJ	P,WRDBDY
	MOVE	C,ZPC
	LSH	C,-1
	PUSHJ	P,DEFNAM
	 POPJ	P,
	SKIPN	A,(TP)
	 JRST	TFARG
	PUSHJ	P,MAKSTR
	POPJ	P,

ZGVAR:	AOS	GLBTOT
	AOS	C,GLBCNT
	CAILE	C,255.	;real high limit
	 JRST	TMGLB
	TLO	C,%VAR
	PUSHJ	P,DEFNAM
	 POPJ	P,	;multiply defined
	PUSHJ	P,AWORD
	 POPJ	P,
	POPJ	P,

TMGLB:	MSG	[Too many globals]
	PUSHJ	P,ERROR
	POPJ	P,

ZOBJEC:	AOS	OBJTOT	;how many he tried to make
	AOS	C,OBJCNT
	CAILE	C,255.
	 JRST	TMOBJ	;more than 255 objects	
	PUSHJ	P,DEFNAM
	 POPJ	P,	;multiply defined
;process parts of object line
	PUSHJ	P,AWORD
	 JRST	TFAOBJ
	PUSHJ	P,AWORD	;flags
	 JRST	TFAOBJ
	PUSHJ	P,ABYTE
	 JRST	TFAOBJ
	PUSHJ	P,ABYTE
	 JRST	TFAOBJ
	PUSHJ	P,ABYTE
	 JRST	TFAOBJ
	PUSHJ	P,AWORD	;property table ptr
	 JRST	TFAOBJ
	POPJ	P,

TFAOBJ:	MSG	[Too few arguments to .OBJECT]
	PUSHJ	P,ERROR
	POPJ	P,

TMOBJ:	MSG	[Too many objects]
	PUSHJ	P,ERROR
	POPJ	P,

ZLEN:	POPJ	P,

ZPDEF:	PUSHJ	P,WRDBDY	;guarantee word boundary
	POPJ	P,

ZPROP:	SKIPN	TABLE
	 JRST	ZPROPL
	NXTARG	1
	PUSHJ	P,AGET		;get property length
	 JFCL
	TLZ	B,%BITS
	CAILE	B,0
	 CAILE	B,8
	  JRST	ZPOFL		;property length out of range
	MOVE	C,B
	PUSHJ	P,AGET		;get property number
	 JFCL
	TLZ	B,%BITS
	CAILE	B,0
	 CAIL	B,40
	  JRST	ZPOFR		;property number out of range
	SUBI	C,1		;length minus one
	LSH	C,5		;left shifted
	ADD	C,B		;plus number
	MOVE	A,C
	PUSHJ	P,OUTBYT	;output it
	POPJ	P,

ZPOFR:	MSG	[Property out of range]
	SKIPA
ZPOFL:	MSG	[Property length too long]
	PUSHJ	P,ERROR
	POPJ	P,

ZPROPL:	MSG	[Property definition not during table?]
	PUSHJ	P,ERROR
	POPJ	P,

ZSEQ:	MOVEI	D,0
	NXTARG	1
ZSEQL:	SKIPN	B,(TP)
	 POPJ	P,
	MOVE	C,D
	PUSHJ	P,DEFGLB
	 JRST	ZSEMDG
ZSEQN:	AOJA	D,ZSEQL

ZSEMDG:	MSG	[Multiply defined global]
	PUSHJ	P,ERROR
	JRST	ZSEQN


SUBTTLE STRING PSEUDOS

ZSTR:	SKIPN	A,2(TP)
	 JRST	TFARG
	PUSHJ	P,MAKSTR
	POPJ	P,

ZSTRL:	MOVEI	A,0
	PUSHJ	P,OUTBYT
	PUSH	P,Z		;save bptr
	PUSH	P,ZPC		;save pc
	PUSHJ	P,ZSTR
	POP	P,A		;restore pc
	POP	P,B		;restore bptr
	SUBM	ZPC,A
	TRNE	A,1
	 ADDI	A,1		;round up
	LSH	A,-1		;convert to words
	DPB	A,B		;output length of string
	POPJ	P,

ZZWORD:	NXTARG	1
	SKIPN	A,(TP)
	 JRST	TFARG
	PUSHJ	P,MAKZWD
	POPJ	P,

TFARG:	MSG	[Too few arguments]
	PUSHJ	P,ERROR
	POPJ	P,


SUBTTL SIMPLE THINGS: TRUTH, WORDS, BYTES

ZTRUE:	MOVEI	A,1
	PUSHJ	P,OUTWRD
	POPJ	P,

ZFALSE:	MOVEI	A,0
	PUSHJ	P,OUTWRD
	POPJ	P,

ZWORD:	NXTARG	1		;flush .WORD
ZWORD1:	PUSHJ	P,AWORD
	 POPJ	P,
	SKIPN	(TP)
	 SKIPE	1(TP)
	  JRST	ZWORD1
	POPJ	P,

ZBYTE:	NXTARG	1		;flush .BYTE
ZBYTE1:	PUSHJ	P,ABYTE
	 POPJ	P,
	SKIPN	(TP)
	 SKIPE	1(TP)
	  JRST	ZBYTE1
	POPJ	P,

SUBTTL OPERAND ASSEMBLY

;assembly of real opers
AOPER:	SETOM	NOREF	;don't produce references, just do lookups
	MOVEM	B,OPER	;save operand (and bits!)
	SETOM	PRED	;not pred instruction
	TLNE	B,%PRED
	 SETZM	PRED	; yes it is!
	SETZM	SENSE	;initialize jump sense
	SETOM	VAL	;not val instruction
	TLNE	B,%VAL
	 SETZM	VAL	; yes it is!
	MOVEI	F,0	;first count arguments
;set up buffer for arguments
	MOVE	AB,[ARGBUF,,ARGBUF+1]
	SETOM	ARGBUF
	BLT	AB,ARGBUF+12
	MOVEI	AB,ARGBUF

	MOVE	B,OPER
	TLNE	B,%JUMP	;don't skip if it's a jump
	 JRST	AOPERJ
	NXTARG	1	;move over op
{;now hack arguments
AOPER1:	SKIPN	(TP)
	 SKIPE	1(TP)
	  SKIPA
	   JRST	AOPERN	;done, no more args
	MOVE	A,1(TP)	;pick up terminator
;here for string
	CAIE	A,""
	 JRST	AOPERQ
	MOVE	A,OPER
	TLNN	A,%STR	;must be string operator
	 JRST	AOPSTR	;string given to non-string operator
	HRRZ	A,A
	PUSHJ	P,OUTBYT
	MOVE	A,(TP)
	PUSHJ	P,MAKSTR
	SKIPN	2(TP)
	 SKIPE	3(TP)
	  JRST	TMAPRI
	POPJ	P,

TMAPRI:	MSG	[Too many arguments to PRINTI]
	PUSHJ	P,ERROR
	POPJ	P,

AOPSTR:	MSG	[String given to non-string operator?]
	PUSHJ	P,ERROR
	POPJ	P,

;here for quoted variable name
AOPERQ:	CAIE	A,"'	;quoted variable?
	 JRST	AOPERP
	ADDI	F,1	;that's an argument
	NXTARG	1
	SKIPN	(TP)
	 JRST	AOPQUT	;bad variable name
	PUSHJ	P,AGET
	 JFCL
	TLNN	B,%VAR
	 JRST	AOPQUT
	TLZ	B,%VAR	;quoting devariablizes variables
	JRST	AOPOUT

AOPGET:	PUSHJ	P,AGET	;get value if any
	 JFCL
AOPOUT:	MOVEM	B,(AB)	;put out theory on arg
	MOVE	B,-2(TP)
	MOVEM	B,1(AB)	;put out symbol
	ADDI	AB,2
	JRST	AOPER1

;here arg is nothing special
AOPERC:	AOJA	F,AOPGET

AOPERJ:	MOVEI	G,0
	JRST	AOPERK

;here for predicate jump
AOPERP:	CAIE	A,"/	;'then' predicate?
	 CAIN	A,"\	;'else' predicate?
	  SKIPA
	   JRST	AOPERV
	MOVEI	G,0
	CAIN	A,"/
	 TRO	G,100000
	MOVEM	G,SENSE
AOPERK:	NXTARG	1
	SKIPN	(TP)
	 JRST	AOPQUT	;bad variable name
	PUSHJ	P,ALCL	;get value if any
	 JFCL
	MOVEM	B,PRED
	MOVE	B,-2(TP)
	MOVEM	B,PRED+1
	JRST	AOPER1

;here for value variable
AOPERV:	CAIE	A,">	;term. for assignment
	 JRST	AOPERC
	NXTARG	1
	SKIPN	(TP)
	 JRST	AOPQUT	;bad variable name
	PUSHJ	P,AGET	;get value if any
	 JFCL
	MOVEM	B,VAL
	MOVE	B,-2(TP)
	MOVEM	B,VAL+1
	JRST	AOPER1

AOPQUT:	MSG	[Bad variable name after value or predicate]
	PUSHJ	P,ERROR
	POPJ	P,

;here we know how many args, so frotz with operand value appropriately
;f/ # of args.
AOPERN:	SKIPE	ODEBUG		;print theory of operator
	 PUSHJ	P,OPRNT		; if odebug is non-zero
	SKIPE	TWOPASS		;if non two pass, then can make refs
	 SKIPE	PASS2		;can't make refs in pass 1
	  SETZM	NOREF		;can make refs now
	MOVEI	AB,ARGBUF
	MOVE	B,OPER		;pick up operator
	ANDI	B,377		;flush various funny bits
;dispatch on operand value
	CAIL	B,300		;ext?
	 JRST	OUTEXT		; yes, this one is always an ext
	CAIL	B,260		;0op? 
	 JRST	OUT0OP		; yes
	CAIL	B,200		;1op?
	 JRST	OUT1OP		; yes
;falls through

;remainder are all 2op (but can be ext!)
OUT2OP:	CAIE	F,2
	 JRST	TMA2OP
	MOVEI	C,0
	MOVE	A,(AB)
	JUMPL	A,CNVEXT	;if undefined, must be ext.
	TLNE	A,%VAR
	 JRST	CHK1VR
	CAIL	A,0
	 CAIL	A,400
	  JRST	CNVEXT		;if long immediate, must be ext.
	SKIPA			;arg 1 is immediate
CHK1VR:	TRO	B,100		;arg 1 is a variable
CHK2ND:	MOVE	A,2(AB)
	JUMPL	A,CNVEXT	;if undefined, must be ext.
	TLNE	A,%VAR
	 JRST	CHK2VR
	CAIL	A,0
	 CAIL	A,400
	  JRST	CNVEXT		;if long immediate, must be ext.
	SKIPA			;arg 2 is immediate
CHK2VR:	TRO	B,40		;arg 2 is a variable

;here it's really a 2op
	MOVE	A,B
	PUSHJ	P,OUTBYT	;output operator
	HRRZ	A,(AB)
	PUSHJ	P,OUTBYT
	HRRZ	A,2(AB)
	PUSHJ	P,OUTBYT	
	JRST	OUTPV		;go do value and pred

;here if wrong number of arguments (might be 4 arg EQUAL?)
TMA2OP:	MOVE	B,OPER
	TLNN	B,%XARG		;4 arg equal?, so convert to ext.
	 JRST	TMA2O1		;real wna, too bad

;here to convert a 2op to an ext
CNVEXT:	MOVE	B,OPER
	ADDI	B,300		;convert to ext
	MOVEM	B,OPER
	ANDI	B,377
	MOVEI	AB,ARGBUF
	JRST	OUTEXT

TMA2O1:	MSG	[Too many arguments to 2op]
	PUSHJ	P,ERROR
	POPJ	P,

;here to output a 1op instruction
OUT1OP:	MOVE	B,OPER
	TLNE	B,%JUMP		;special case jumps
	 JRST	OUTJMP
	CAIE	F,1		;one arg?
	 JRST	TMA1OP		;no, lose!
	MOVE	A,(AB)		;pick up argument
	TLNN	A,%VAR		;variable?
	 JRST	1OPI		; no.
	TRO	B,40		;variable arg bit
1OPBYT:	EXCH	A,B
	HRRZ	A,A
	PUSHJ	P,OUTBYT	;output oper
	HRRZ	A,B
	PUSHJ	P,OUTBYT	;output variable byte
	JRST	OUTPV

OUTJMP:	JUMPG	F,TMA1OP
	HRRZ	A,B
	PUSHJ	P,OUTBYT	;output it for now
	MOVE	B,OPER
	JRST	OUTP1

1OPI:	CAIL	A,0
	 CAIL	A,400		;will it fit in one word?
	  JRST	1OPNO
	TRO	B,20		;immediate bit
	JRST	1OPBYT		;output oper and imm. byte

1OPNO:	EXCH	A,B
	HRRZ	A,A
	PUSHJ	P,OUTBYT	;output oper.
	JUMPL	B,1OPREF
1OPNO1:	HRRZ	A,B
	PUSHJ	P,OUTWRD	;output long arg.
	JRST	OUTPV

;here single arg is reference to unknown
1OPREF:	MOVE	B,1(AB)		;must make an appropriate fixup
	PUSHJ	P,REFSYM
	MOVE	B,(AB)		;output what we have of value
	JRST	1OPNO1

TMA1OP:	MSG	[Too many args to 1op instruction]
	PUSHJ	P,ERROR
	POPJ	P,

;here to output extended op
OUTEXT:	CAILE	F,4
	 JRST	TMAEXT
	MOVE	A,B
	PUSHJ	P,OUTBYT	;operator
	MOVEI	A,0
	PUSHJ	P,OUTBYT	;ext byte (will be filled in later)
	MOVE	G,Z		;save output ptr
	MOVEI	D,0		;ext byte under construction
	MOVEI	E,4		;max arguments
;here loop through args to ext instruction
EXTLUP:	MOVE	A,(AB)		;get arg
	TLNN	A,%VAR		;variable?
	 JRST	EXTIMM
	TRO	D,2		;yes, turn on variable bit
EXTBYT:	HRRZ	A,A
	PUSHJ	P,OUTBYT	;output variable byte
	JRST	EXTNXT
EXTIMM:	CAIL	A,0		;immediate?
	 CAIL	A,400
	  JRST	EXTLIM		;no, long
	TRO	D,1		;turn on immediate bit
	JRST	EXTBYT		;output immediate byte
EXTLIM:	JUMPL	A,EXTREF	;undefined?
	HRRZ	A,A		;no, output full word
	PUSHJ	P,OUTWRD
	JRST	EXTNXT

EXTREF:	MOVE	B,1(AB)
	PUSHJ	P,REFSYM
	HRRZ	A,(AB)
	PUSHJ	P,OUTWRD

EXTNXT:	SOJE	E,EXTEXT	;if done four args, leave
	SUBI	F,1		;reduce count
	ADDI	AB,2		;move to next
	LSH	D,2		;update ext byte
	JUMPG	F,EXTLUP	;if still args, do them
	TRO	D,3		;turn on last arg bits
	JRST	EXTNXT		;if not, loop filling ext byte with 3

EXTEXT:	DPB	D,G		;output ext word
	JRST	OUTPV		;go output val and pred stuff

TMAEXT:	MSG	[Too many arguments to EXT instruction]
	PUSHJ	P,ERROR
	POPJ	P,

;here to output a 0op instruction
OUT0OP:	JUMPG	F,TMA0OP	;better not have any args!
	MOVE	A,B		;pick up operand from B
	PUSHJ	P,OUTBYT

;here to output value and predicate stuff for instructions
OUTPV:	MOVE	B,OPER
	TLNN	B,%VAL
	 JRST	OUTP
	MOVE	A,VAL
	CAMN	A,[-1]
	 JRST	NOVAL
	JUMPL	A,OUTVRF	;reference to value
	HRRZ	A,A
	PUSHJ	P,OUTBYT

OUTP:	TLNN	B,%PRED+%JUMP
	 POPJ	P,
;comes here from outputting jump instruction
OUTP1:	MOVE	A,PRED
	CAMN	A,[-1]
	 JRST	NOPRED
	MOVE	C,A
	JUMPL	A,OUTPRF	;reference to predicate
;produce jump offset
	TRNN	A,37776		;check for /true /false jump
	 JRST	OUTPSH		;short
	SUB	A,ZPC
	TLNE	B,%JUMP
	 ANDI	A,177777	;16 bit jump inst.
	TLNN	B,%JUMP
	 ANDI	A,37777		;14 bit pred. jumps
;determine whether short or long jump
	CAIGE	A,77		;test if pred jump is short
	 JRST	OUTPSH
	CAMN	B,OPJMP		;jump instruction can take larger "shorts"
	 CAIL	A,377		;small enough?
	  JRST	OUTPLN		; no, long jump.  sigh.

;short jump: <polarity>+<short=1>+<offset:6bits>
; such are always forward jumps of less than 64 bytes
OUTPSH:	CAMN	B,OPJMP
	 JRST	OUTSJ		;output short jump byte
	TRO	A,100		;short jump
	MOVE	C,SENSE
	TRNE	C,100000
	 TRO	A,200		;move jump sense to second byte
OUTPS1:	ANDI	A,377		;and make it a byte
	PUSHJ	P,OUTBYT
	POPJ	P,

OUTSJ:	PUSH	P,A
	HRRZ	A,B
	TRO	A,20		;turn on immediate bit
	DPB	A,Z
	POP	P,A
	JRST	OUTPS1

;long jump
OUTPLN:	MOVE	C,SENSE
       	TRNE	C,100000
	 TRO	A,100000
	PUSHJ	P,OUTWRD
	POPJ	P,

;here when predicate jump is a forward reference
OUTPRF:	SETOM	JMPREF		;say it's a jump reference
	SKIPE	TWOPAS
	 SKIPE	FZ
	  JRST	OUTPRL
	HRRZ	A,A		;get value part of ref
	SUB	A,ZPC
	SUB	A,FSHORT
	TLNE	B,%JUMP
	 ANDI	A,177777	;16 bit jump inst.
	TLNN	B,%JUMP
	 ANDI	A,37777		;14 bit pred. jumps
;determine whether short or long jump
	TLNN	B,%JUMP		;real jumps are always long
	 CAIL	A,77		;test if pred jump is short
	  JRST	OUTPRL		;long jump.  sigh.
;here short jump reference
	MOVEI	A,100		;short jump
	MOVE	C,SENSE
	TRNE	C,100000
	 TRO	A,200		;move jump sense to second byte
	HRRM	A,PRED		;save it
;make the reference
	SETOM	WRDBYT		;say it's a byte ref
	MOVE	B,PRED+1
	PUSHJ	P,REFLCL
	SETZM	JMPREF
	SETZM	WRDBYT
;output the byte
	HRRZ	A,PRED
	PUSHJ	P,OUTBRF
	AOS	FSHORT
	POPJ	P,

OUTPRL:	MOVE	B,PRED+1
	PUSHJ	P,REFLCL	;all jumps are local
	SETZM	JMPREF
	MOVE	A,SENSE
	PUSHJ	P,OUTWRF	;output reference
	POPJ	P,

NOPRED:	MSG	[Predicate instruction lacks predicate]
	PUSHJ	P,ERROR
	POPJ	P,

OUTVRF:	MSG	[Value indefined]
	SKIPA
NOVAL:	MSG	[Value instruction lacks value]
	PUSHJ	P,ERROR
	POPJ	P,

TMA0OP:	MSG	[Too many args to 0op instruction]
	PUSHJ	P,ERROR
	POPJ	P,

OPRNT:	PUSH	P,A
	PUSH	P,B
	PUSH	P,C
	PUSH	P,D
	HRROI	A,BUFFER
	PSOUT
	MOVEI	A,^M
	PBOUT
	MOVEI	A,^J
	PBOUT
	MOVEI	D,0
OPLOOP:	MOVE	A,ARGBUF(D)
	CAMN	A,[-1]
	 JRST	OPPV
	MOVE	A,ARGBUF+1(D)
	PSOUT
	MOVEI	A,^I
	PBOUT
	MOVE	B,ARGBUF(D)
	PUSHJ	P,NUM
	PUSHJ	P,CRLF
	ADDI	D,2
	JRST	OPLOOP

CRLF:	MOVEI	A,^M
	PBOUT
	MOVEI	A,^J
	PBOUT
	POPJ	P,

NUM:	PUSH	P,A
	PUSH	P,C
	JUMPGE	B,OPNV
	MOVEI	A,"?
	PBOUT
	MOVEI	A," 
	PBOUT
	TLZ	B,%UNDEF
OPNV:	TLNN	B,%VAR
	 JRST	OPNUM
	MOVEI	A,"#
	PBOUT
	TLZ	B,%VAR
OPNUM:	MOVEI	A,.PRIOU
	MOVEI	C,8.
	NOUT
	 JFCL
	POP	P,C
	POP	P,A
	POPJ	P,

OPPV:	MOVE	A,VAL
	CAMN	A,[-1]
	 JRST	OPPRED
	MOVEI	A,">
	PBOUT
	MOVE	A,VAL+1
	PSOUT
	MOVEI	A,^I
	PBOUT
	MOVE	B,VAL
	PUSHJ	P,NUM
	PUSHJ	P,CRLF
OPPRED:	MOVE	B,PRED
	CAMN	B,[-1]
	 JRST	OPPEX
	MOVEI	A,"\
	MOVE	B,SENSE
	TRNE	B,100000
	 MOVEI	A,"/
	PBOUT
	MOVE	A,PRED+1
	PSOUT
	MOVEI	A,^I
	PBOUT
	MOVE	B,PRED
	PUSHJ	P,NUM
	PUSHJ	P,CRLF
OPPEX:	POP P,D
	POP P,C
	POP P,B
	POP P,A
	POPJ P,


SUBTTL SYMBOL HACKING

; symbols look like:
;  SYMNAM <pname loc> ,, <next symbol>
;  SYMVAL <value>
;  SYMREF <references>
; where
;	<value> if for a defined symbol
; includes
;	%VAR,,	if the symbol is for a variable (local or global)
; and
;	<value> if for an undefined symbol
; includes
;	%UNDEF,, <value if local label>

; a reference chain consists of
;	<pc> ,, <next reference>
;	<output ptr>
; where
;	<pc> includes
;		%RBYTE if the reference is a byte reference
;		%RJUMP if the reference is a jump reference

;look up a symbol in a symbol list
; a/ symbol table, b/ symbol
; +1 a/ table loc of symbol, won
; +2 a/ potential table loc of symbol, lost
SLOOK:	PUSH	P,B
	PUSH	P,C
	PUSH	P,D
	PUSH	P,E
;hash the symbol
	SETZ	C,
HASH1:	ILDB	E,B
	JUMPE	E,HASH2
	ROT	C,3
	XOR	C,E
	JRST	HASH1
HASH2:	TLZ	C,400000
	IDIVI	C,BUCKN		;number of buckets to D
	IMULI	D,BUCKL		;length of buckets
	HRL	D,D
	ADDM	A,D
	SKIPL	D
	 HALTF			;symbol table overflow
;look for it
	MOVE	A,-3(P)		;pick up symbol being looked for
SLKLUP:	SKIPN	B,SYMNAM(D)	;symbol here?
	 JRST	SLKLOS		; nothing here
	HLR	B,B
	HRLI	B,440700	;produce byte pointer
	PUSHJ	P,COMPAR	;compare
	 JRST	SLKWON		;same, win
	 JFCL
	ADDI	D,SYMSIZ	;move to next symbol
	JRST	SLKLUP		;and loop

SLKLOS:	MOVE	A,D		; rtn ptr to symbol slot in A
	POP	P,E
	POP	P,D
	POP	P,C
	POP	P,B
	JRST	POPJ1

SLKWON:	HLR	B,SYMNAM(D)	;found it, stuff it for future use
	HRLI	B,440700
	MOVEM	B,LSTSYM
	MOVE	A,D		; return ptr
	POP	P,E
	POP	P,D
	POP	P,C
	POP	P,B	; return ptr to cell
	POPJ	P,

; insert symbol in table
; a/ where (as returned by SLOOK)
; b/ symbol
; c/ value
SINSRT:	PUSH	P,A
	PUSH	P,B
	PUSH	P,C
	PUSH	P,D
	HRLZM	FREE,SYMNAM(A)	;symbol will be copied here
	MOVEM	C,SYMVAL(A)	;value
;copy symbol into appropriate symbol area
	MOVE	A,FREE
	HRLI	A,440700	;bptr to output
	MOVE	D,A		;save a copy
	SETZM	(A)		;make sure its zero
	MOVEM	A,LSTSYM	;most recent symbol defn.
	ILDB	C,B
	IDPB	C,A
	JUMPN	C,.-2
	CAMN	A,D		;not a nul symbol?
	 HALTF			; should be no nul symbols
	HRRZI	FREE,1(A)	;update free pointer
	POP	P,D
	POP	P,C
	POP	P,B
	POP	P,A
	POPJ	P,

SUBTTL SYMBOL TABLE DEBUGGING

;print a symbol list, takes it in A
SPRNT:	PUSH	P,A
	PUSH	P,B
	SKIPN	B,A
	 JRST	SPRNT2
SPRNT1:	HLRZ	A,SYMNAM(B)
	JUMPE	A,SPRNT3
	HRLI	A,-1
	PSOUT
	MOVEI	A,"?
	SKIPGE	SYMVAL(B)
	 PBOUT		;? if undefined
	MOVEI	A,",
	PBOUT
SPRNT3:	HRRZ	B,SYMNAM(B)
	JUMPN	B,SPRNT1
SPRNT2:	HRROI	A,[ASCIZ /
/]
	PSOUT
POPBAJ:	POP	P,B
	POP	P,A
	POPJ	P,

;print the global symbol table 
GPRNT:	PUSH	P,A
	MOVE	A,GLBLST
	PUSHJ	P,SPRNT
	POP	P,A
	POPJ	P,

;print the local symbol table
LPRNT:	PUSH	P,A
	MOVE	A,LCLLST
	PUSHJ	P,SPRNT
	POP	P,A
	POPJ	P,

SUBTTL INITIALIZE SYMBOL TABLES

;initialize global symbol table
GLBINI:	PUSH	P,A
	MOVEI	A,GLBBUF
	MOVEM	A,GLBPTR
	SETZM	GLBLST
	SETZM	GLBTAB
	MOVE	A,[GLBTAB,,GLBTAB+1]
	BLT	A,GLBEND
	POP	P,A
	POPJ	P,

;initialize local symbol table
LCLINI:	PUSH	P,A
	PUSH	P,B
	PUSH	P,C
	MOVEI	A,LCLBUF
	MOVEM	A,LCLPTR
	SETZM	LCLLST
	SETZM	LCLTAB
	MOVE	A,[LCLTAB,,LCLTAB+1]
	BLT	A,LCLEND
;local tables start with these three symbols in them
	MOVE	B,[440700,,[ASCIZ /FALSE/]]
	MOVEI	C,0
	PUSHJ	P,DEFLCL
	 JFCL
	MOVE	B,[440700,,[ASCIZ /TRUE/]]
	MOVEI	C,1
	PUSHJ	P,DEFLCL
	 JFCL
	MOVE	B,[440700,,[ASCIZ /STACK/]]
	MOVSI	C,%VAR
	PUSHJ	P,DEFLCL
	 JFCL
	JRST	POPCBA

SUBTTL PRINT UNDEFINED LOCALS

;print names of undefined locals in function
;done whenever a function is finished
UNDLCL:	SKIPN	FUNCT		;skip if was assembling a function
	 POPJ	P,
	PUSH	P,A
	PUSH	P,B
	PUSH	P,C
	PUSH	P,D
	MOVE	C,LCLLST
UNDLC2:	SKIPL	D,SYMVAL(C)	;value slot
	 JRST	UNDLC1		;defined symbol
	SKIPN	A,FUNCT		;undefined symbol
	 JRST	UNDLC3		;don't print function name
	PSOUT			;print function name
	MSG	[
]
	PSOUT
	SETZM	FUNCT		;zero it since one print is enough
;here to print undefined symbol and pcs at which it is referenced
UNDLC3:	MSG	[ ]
	PSOUT
	HLRO	A,SYMNAM(C)	;bptr to symbol
	PSOUT
	MSG	[ undefined:  ]
	PSOUT
	PUSH	P,C
	MOVEI	C,10.
	HRRZ	D,SYMREF(C)
	JRST	UNDLC5
UNDLC4:	MOVEI	A,.PRIOU
	HLRZ	B,(D)		;pc at which referenced
	TRZ	B,%RBYTE+%RJUMP
	NOUT			;output pc
	 JFCL
	MSG	[, ]
	PSOUT
UNDLC5:	HRRZ	D,(D)		;move to next pc
	JUMPN	D,UNDLC4	;and leave if last
	PUSHJ	P,PCRLF
	POP	P,C

UNDLC1:	HRRZ	C,SYMNAM(C)	;move to next symbol
	JUMPN	C,UNDLC2	;or leave if it was last
;produce symbol table if asked
	SKIPN	SYMFLG
	 JRST	UNDLCX
	MOVE	A,LCLLST
	PUSHJ	P,SYMTAB
	MOVE	B,FCNPTR
	SUBI	A,SYMBUF
	MOVEM	A,(B)
	MOVE	A,FSYM		;last function defined
	MOVEM	A,1(B)
	ADDI	B,2
	MOVEM	B,FCNPTR

;do rest of cleanup
UNDLCX:	PUSHJ	P,LCLINI	;reinit local symbol table
	JRST	POPDA

SUBTTL PRINT UNDEFINED GLOBALS

;print undefined globals
UNDGLB:	PUSH	P,A
	PUSH	P,B
	PUSH	P,C
	PUSH	P,D
	MOVE	C,GLBLST
UNDGL2:	SKIPL	D,SYMVAL(C)	;value slot
	 JRST	UNDGL1
	HLRO	A,SYMNAM(C)	;bptr to symbol
	PSOUT
	MSG	[ global undefined:  ]
	PSOUT
	PUSH	P,C
	MOVEI	C,10.
	HRRZ	D,SYMREF(C)
	JRST	UNDGL5
UNDGL4:	MOVEI	A,.PRIOU
	HLRZ	B,(D)		;pc at which referenced
	TRZ	B,%RBYTE+%RJUMP
	NOUT			;output pc
	 JFCL
	MSG	[, ]
	PSOUT
	HRRZ	D,(D)		;move to next pc
UNDGL5:	JUMPN	D,UNDGL4	;and leave if last
	PUSHJ	P,PCRLF
	POP	P,C
UNDGL1:	HRRZ	C,SYMNAM(C)	;move to next symbol
	JUMPN	C,UNDGL2	;or leave if it was last

;produce symbol table if was asked
	SKIPN	SYMFLG
	 JRST	POPDA
	MOVE	A,GLBLST
	PUSHJ	P,SYMTAB
	SUBI	A,SYMBUF
	MOVEM	A,SYMBUF	;ptr to global symbol table
;sort function table and copy it into symbol area
	MOVE	A,FCNPTR
	SETZM	(A)
	AOS	FCNPTR
	MOVEI	A,FCNBUF
	PUSHJ	P,SSORT
	HRLI	A,FCNBUF
	HRR	A,SYMPTR
	SUBI	A,SYMBUF
	HRRZM	A,SYMBUF+1	;ptr to function symbol table
	ADDI	A,SYMBUF
	MOVE	B,FCNPTR
	SUBI	B,FCNBUF
	ADD	B,SYMPTR
	MOVEM	B,SYMPTR
	BLT	A,(B)

;output symbols file
OUTSYM:	MOVE	A,[440700,,[ASCIZ /.SYMS/]]
	MOVE	B,OUTPTR
	ILDB	0,A
	IDPB	0,B
	JUMPN	0,.-2
	MOVSI	A,(GJ%SHT+GJ%FOU)
	HRROI	B,OUTFIL
	GTJFN
	 JRST	ERPRNT
	HRRZ	A,A
	MOVE	B,[440000,,OF%WR]
	OPENF
	 JRST	ERPRNT
	MOVE	B,[444400,,SYMBUF]
	MOVEI	C,SYMBUF
	SUB	C,SYMPTR
	SOUT
;close up and go home
	CLOSF
	 JFCL

POPDA:	POP	P,D	
	JRST	POPCBA

SUBTTL OUTPUT SYMBOL TABLES

SYMTAB:	PUSH	P,B
	PUSH	P,C
	PUSH	P,D
       	MOVE	C,A
	MOVE	D,A
;copy strings
SYMCPY:	HLR	A,SYMNAM(C)
	HRLI	A,440700
	HRRZ	B,SYMPTR
	SUBI	B,SYMBUF
	HRLM	B,SYMNAM(C)
	ADDI	B,SYMBUF
	HRLI	B,440700
	ILDB	A
	IDPB	B
	JUMPN	.-2
	HRRZI	B,1(B)
	MOVEM	B,SYMPTR
	HRRZ	C,(C)
	JUMPN	C,SYMCPY
	MOVE	C,D
;copy symbols themselves
SYMCP1:	HLR	A,SYMNAM(C)
	HRLI	A,440700
	MOVEM	A,(B)
	MOVE	A,SYMVAL(C)
	MOVEM	A,1(B)
	ADDI	B,2
	HRRZ	C,(C)
	JUMPN	C,SYMCP1
	SETZM	(B)
	ADDI	B,1
	EXCH	B,SYMPTR
	MOVE	A,B
	PUSHJ	P,SSORT		;sort the table
	POP	P,D
	POP	P,C
	POP	P,B
	POPJ	P,

;sort a symbol table by value words
; a/ ptr to symbol table
SSORT:	PUSH	P,A
	PUSH	P,B
	PUSH	P,C
	PUSH	P,D
SSORT1:	SKIPN	(A)
	 JRST	POPDA
	MOVE	C,A		;save destination
	MOVE	D,A		;ptr to best candidate
SSORT0:	ADDI	A,2		;ptr to first test
	SKIPN	(A)		;better be a test...
	 JRST	SSORT2		; zero, end of table
	MOVE	B,1(D)
	CAMLE	B,1(A)		;test better than best?
	 MOVE	D,A		;new best
	JRST	SSORT0		;move to next

SSORT2:	CAMN	D,C		;must move one?
	 JRST	SSORT3
	MOVE	A,(D)
	EXCH	A,(C)
	MOVEM	A,(D)
	MOVE	A,1(D)
	EXCH	A,1(C)
	MOVEM	A,1(D)
SSORT3:	MOVEI	A,2(C)
	JRST	SSORT1

SUBTTL GLOBAL SYMBOL REFERENCE AND DEFINITION

DEFGLB:	MOVE	A,GLBOBL	;look it up in global symbol table
	PUSHJ	P,SLOOK
	 JRST	DEFOLD		;already there
;symbol not in global table
INSGLB:	MOVE	FREE,GLBPTR
	PUSHJ	P,SINSRT	;insert it
	MOVEM	FREE,GLBPTR
	HRR	0,GLBLST	;chain together all globals
	HRRM	0,(A)
	MOVEM	A,GLBLST	;by consing into a list
	SKIPN	SDEBUG
	 JRST	POPJ1
;print symbol table here if debugging
	PUSH	P,A
	MOVE	A,GLBLST
	PUSHJ	P,SPRNT
	POP	P,A
	JRST	POPJ1

;here to define a symbol that already has been referenced
DEFOLD:	MOVE	B,A		;move ptr to symbol
	SKIPL	SYMVAL(B)	;is it undefined?
	 JRST	CPOPJ		; if defined, lose
	MOVE	A,C		;save value
	MOVEM	C,SYMVAL(B)	;define it
	MOVE	C,SYMREF(B)	;pick up reference chain to C
	PUSHJ	P,FIXUP		;fix up references already accumulated
	JRST	POPJ1	

SUBTTL LOCAL SYMBOL REFERENCE AND DEFINITION

DEFLCL:	MOVE	A,LCLOBL	;look it up in local symbol table
	PUSHJ	P,SLOOK
	 JRST	DEFOLL		;here for forward references
;here to add symbol to local symbol table
INSLCL:	MOVE	FREE,LCLPTR
	PUSHJ	P,SINSRT
	MOVEM	FREE,LCLPTR
	HRR	0,LCLLST
	HRRM	0,(A)
	MOVEM	A,LCLLST
	JRST	POPJ1

;here to define already referenced local symbol
DEFOLL:	SKIPN	TWOPAS
	 JRST	DEFOLD
       	SKIPN	PASS2		;only do fixups if pass 2
	 JRST	DEFOL1		; do usual thing in pass 1
;do hair in pass 2
       	MOVEM	C,SYMVAL(A)	;redefine local label
;fix up for short jumps
	MOVE	C,SYMREF(A)	;get reference chain
	MOVE	A,SYMVAL(A)	;get value to be fixed up
	PUSHJ	P,FIXUP
	JRST	POPJ1	

;here to "define" local symbol during pass one
DEFOL1:	MOVE	B,A
	SKIPL	SYMVAL(B)	;should be undefined
	 JRST	CPOPJ		; if defined, lose
	MOVE	A,C		;save value
	HRRM	C,SYMVAL(B)	;pretend to define it
	JRST	POPJ1	

BPASS2:	MSG	[Label inconsistency, pass 2]
	PUSHJ	P,ERROR
	JRST	POPJ1


SUBTTL REFERENCE AND DEFINE SYMBOLS

;reference a symbol
; takes b/ symbol
; returns a/ ptr to cell for symbol
REFSYM:	PUSH	P,B
	PUSH	P,C
	MOVE	A,LCLOBL	;look up as local first
	PUSHJ	P,SLOOK
	 JRST	[SKIPL SYMVAL(A) ;skip if undefined
		  JRST POPCB	;has a value, return it
		 JRST REFLLD]	;refer to old local
	MOVE	A,GLBOBL
	MOVE	B,-1(P)
	PUSHJ	P,SLOOK
	 JRST	[SKIPL SYMVAL(A)
		  JRST POPCB	;has a gval, return it
		 JRST REFGLD]	;refer to old global
	MOVE	B,-1(P)
	PUSHJ	P,REFGLB
POPCB:	POP	P,C
	POP	P,B
	POPJ	P,

;reference a global
; b/ symbol
REFGLB:	PUSH	P,B
	PUSH	P,C
	MOVE	A,GLBOBL
	MOVE	B,-1(P)
	PUSHJ	P,SLOOK
	 JRST	REFGLD		;refer to old global
	MOVE	B,-1(P)
	HRLZI	C,%UNDEF	;undefined
	PUSHJ	P,INSGLB
	 HALTF
REFGLD:	SKIPE	NOREF
	 JRST	POPCB
	MOVE	FREE,GLBPTR
	HRRZ	B,SYMREF(A)	;get pc chain
	HRRM	FREE,SYMREF(A)	;and put new cell in symbol cell
	SKIPE	WRDBYT
	 TLO	B,%RBYTE	;indicate byte reference
	MOVEM	B,(FREE)
	MOVEM	ZPC,1(FREE)	;pc
	MOVEM	Z,2(FREE)	;bptr
	ADDI	FREE,3
	MOVEM	FREE,GLBPTR
	JRST	POPCB

;reference a local
; b/ symbol
REFLCL:	PUSH	P,B
	PUSH	P,C
	MOVE	A,LCLOBL
	MOVE	B,-1(P)
	PUSHJ	P,SLOOK
	 JRST	REFLLD		;refer to old local
	MOVE	B,-1(P)
	HRLZI	C,%UNDEF	;undefined
	PUSHJ	P,INSLCL
	 HALTF
REFLLD:	SKIPE	NOREF
	 JRST	POPCB
	MOVE	FREE,LCLPTR	;get free storage from local area
	HRRZ	B,SYMREF(A)	;get ptr to reference chain
	HRRM	FREE,SYMREF(A)	;and update chain ptr
	SKIPE	WRDBYT
	 TLO	B,%RBYTE
	SKIPE	JMPREF
	 TLO	B,%RJUMP	;indicate jump reference
	MOVEM	B,(FREE)	;put it in right half of new ref
	MOVEM	ZPC,1(FREE)	;put out pc of ref
	MOVEM	Z,2(FREE)	;put of bptr of ref
	ADDI	FREE,3
	MOVEM	FREE,LCLPTR	;update free ptr
	JRST	POPCB

SUBTTL FIXUPS

;fixup forward references
; a/ value
; c/ ptr chain
FIXUP:	TRNN	C,-1		;if empty fixup chain, return immediately
	 POPJ	P,		; only happens for local labels
      	PUSH	P,SAVZPC
	PUSH	P,SAVZ
	PUSH	P,ZPC
	PUSH	P,Z		;fix up references
	PUSH	P,A
FIXUPL:	HRRZ	A,(P)		;pick up value to output
	MOVE	Z,2(C)		;pick up reference output ptr
	MOVEM	Z,SAVZ
	MOVE	ZPC,1(C)
	MOVEM	ZPC,SAVZPC
	MOVE	B,(C)
	TLNE	B,%RJUMP	;jump ref?
	 JRST	FIXUPJ		; yes
	JUMPGE	B,[PUSHJ P,ADDWRD
		   JRST FIXUPN]
	PUSHJ	P,ADDBYT
FIXUPN:	SKIPE PDEBUG
	 PUSHJ	P,PFIXUP
	HRRZ	C,(C)		;move to next one
	JUMPN	C,FIXUPL
FIXUPX:	POP	P,A
	POP	P,Z
	POP	P,ZPC
	POP	P,SAVZ
	POP	P,SAVZPC
	POPJ	P,

;here to fix up jumps
FIXUPJ:	MOVE	1(C)		;pc of ref
	SUB	A,0		;pc difference (true/false and pc diff cancel?)
	TLNE	B,%RBYTE	;byte ref?
	 JRST	FIXSHJ		; means short jump
	ANDI	A,177777	;and it down (two's comp.)
	CAIGE	A,77		;skip if couldn't have been short
	 AOS	SHRIMP		;keep count of short jumps
	PUSHJ	P,ADDWRD
	MOVE	A,(P)		;get value back
	JRST	FIXUPN		;and continue

;here to fix up short jumps
FIXSHJ:	ADDI	A,1		;pc offset
	ANDI	A,177777	;max size of a reference
	CAILE	A,77		;can it be a short jump?
	 HALTF			; better be!
	ANDI	A,377		;and it down just ofr good measure
	PUSHJ	P,ADDBYT	;output byte
	MOVE	A,(P)		;resnarf value
	JRST	FIXUPN		;and loop

;when debugging, print fixups when they are done
PFIXUP:	PUSH	P,A
	PUSH	P,B
	PUSH	P,C
	MOVE	A,PDEBUG
	MOVEI	B,"{
	BOUT
	PUSHJ	P,OPC
	MOVEI	C,0
	HRROI	B,[ASCIZ /}
/]
	SOUT
	JRST	POPCBA

SUBTTL ERROR MESSAGES

ERROR:	PUSH	P,B
	SETZ	B,
	PUSHJ	P,ERRMSG
	POP	P,B
	POPJ	P,

;takes message in A, token in B
ERRMSG:	PUSH	P,A
	PUSH	P,B
	PUSH	P,C
	MOVEI	A,.PRIOU
	MOVE	B,ZPC
	MOVEI	C,8
	NOUT
	 JFCL
	SKIPN	FUNCT
	 JRST	ERRMS1
	MSG	[  (in ]
	PSOUT
	MOVE	A,FUNCT
	PSOUT
	MSG	[)]
	PSOUT
ERRMS1:	MSG	[	]
	PSOUT
	MOVE	A,-2(P)
	PSOUT
	MOVE	B,-1(P)
	JUMPE	B,ERREND
	MOVEI	A,[ASCIZ /:	/]
	PSOUT
	MOVE	A,B
	PSOUT
	PUSHJ	P,PCRLF
	HRROI	A,BUFFER
	PSOUT
	SKIPA
ERREND:	PUSHJ	P,PCRLF
	POP	P,C
	POP	P,B
	POP	P,A
	POPJ	P,

SUBTTL STRING ASSEMBLY

;zstrings from strings
; a/ ptr to string to translate

MAKFST:	SETOM	MKFSTR'
	SETOM	ZWDFLG
	JRST	MAKS

MAKZWD:	SETOM	ZWDFLG'
	SETOM	MKFSTR
	JRST	MAKS

MAKSTR:	SETZM	ZWDFLG
	SETZM	MKFSTR
MAKS:	SKIPE	ZDEBUG
	 JRST	[PUSH	P,A
		 MOVEI	A,^M
		 PBOUT
		 MOVEI	A,^J
		 PBOUT
		 MOVE	A,(P)
		 PSOUT
		 MOVEI	A,40
		 PBOUT
		 POP	P,A
		 JRST	.+1]
	MOVEI	ZCHR,0		; initialize ZCHR byte	
	SKIPA	FRMT,[%FSPC+%FCAP]; at start, default is cap + space
MAKSTL:	 MOVEI	FRMT,%FSPC	; except at start, default is space
	MOVEM	FRMT,FRMDFL	; set up default
	MOVE	C,A
	ILDB	B,C		; get first character
	JUMPE	B,MAKSTE	; done
	PUSHJ	P,BALPHA	; check for alphabetic
	 JRST	MAKS1		; no. goto ascii escape
MAKST0:	MOVEI	FRMT,%FCAP
	CAIG	B,"Z
	 CAIGE	B,"A
	  TRZ	FRMT,%FCAP	; turn off capitalize bit if not upper case
	SKIPN	MKFSTR		; don't bother with freq stuff for fstrs
	 PUSHJ	P,WFREQ		; lookup word in table (a is updated)
	  JRST	MAKS2		; not there, loser!
	PUSH	P,C		; save the word number
	SKIPE	INZASC
	 PUSHJ	P,ENZASC
	PUSHJ	P,MAKFRM	; setup the format for the word
	CAME	FRMT,FRMDFL'	; if it's the default, don't bother
	 PUSHJ	P,OUTFRM	; output the format
	POP	P,C		; restore the word number
	SKIPE	ZDEBUG
	 JRST	[PUSH	P,A
		 MOVEI	A,"W
		 PBOUT
		 POP	P,A
		 JRST	.+1]
	CAIL	C,240.
	 JRST	[SUBI	C,240.
		 PUSH	P,C
		 MOVEI	C,%FNXT	; output next 256-word byte
		 PUSHJ	P,OUTBYC
		 POP	P,C
		 JRST	.+2]	
	ADDI	C,16.		; frob with word number
	PUSHJ	P,OUTBYC
	JRST	MAKSTL		; and loop

MAKS1:	MOVEI	C,%FASC
	SKIPN	INZASC
	 PUSHJ	P,OUTBYC
MAKS1L:	ILDB	B,A
	JUMPE	B,MAKSTX
	PUSHJ	P,BALPHA
	 JRST	MAKS1A
MAKSEZ:	SETOM	INZASC'
	PUSHJ	P,BACKA
	MOVEI	FRMT,%FSPC	; except at start, default is space
	MOVEM	FRMT,FRMDFL	; set up default
	JRST	MAKST0

BACKA:	MOVNI	B,1
	ADJBP	B,A
	MOVE	A,B
	POPJ	P,

MAKS1A:	PUSHJ	P,MAKZBT
	JRST	MAKS1L

MAKS2:	MOVEI	C,%FASC		; escape to ZASCII
	SETZM	MAKSAF'
	SKIPE	MKFSTR
	 JRST	MAKS2L
	SKIPN	INZASC
	 PUSHJ	P,OUTBYC
MAKS2L:	ILDB	B,A		; get next character
	JUMPE	B,MAKSTX
	CAIN	B,"'
	 JRST	MAKS2A
	PUSHJ	P,BALPHA
	 CAIA
	  JRST	MAKS2A
	SETOM	MAKSAF
	PUSHJ	P,MAKZBT
	JRST	MAKS2L

MAKS2A:	SKIPE	MAKSAF
	 JRST	MAKSEZ
	PUSHJ	P,MAKZBT
	JRST	MAKS2L

ENZASC:	JUMPN	ZCHR,ENZAS1
	MOVEI	C,0
	PUSHJ	P,OUTBYC
	SETZM	INZASC
	POPJ	P,

ENZAS1:	MOVEI	0,0
	PUSHJ	P,ADDZCH
	SETZM	INZASC
	POPJ	P,

%FEOS==0
%FSPC==1
%FCOM==2
%FCAP==4
%FFLG==8

%FESS==5
%FNXT==4
%FESN==3
%FEOL==2
%FASC==1

CHR1T:	"e ? "t ? "s ? "a ? "o ? "n ? "r ? "i
	"l ? "d ? "h ? "u ? "g ? 0

CHR2T:	"c ? "b ? "m ? "w ? "y ? "p ? "f ? "k
	"v ? "z ? "j ? "x ? "q ? 40 ? "! ? "?

MAKZBT:	MOVEI	D,CHR1T
MAKZL1:	SKIPN	C,(D)
	 JRST	MAKZB1
	CAME	C,B
	 AOJA	D,MAKZL1
	MOVEI	0,-CHR1T+3(D)
	PUSHJ	P,ADDZCH
	POPJ	P,

MAKZB1:	MOVEI	D,CHR2T
MAKZL2:	SKIPN	C,(D)
	 JRST	MAKZB2
	CAME	C,B
	 AOJA	D,MAKZL2
	MOVEI	0,1
	PUSHJ	P,ADDZCH
	MOVEI	0,-CHR2T(D)
	PUSHJ	P,ADDZCH
	POPJ	P,

MAKZB2:	MOVEI	0,2
	PUSHJ	P,ADDZCH
	PUSH	P,B
	LSH	B,-4
	MOVE	0,B
	PUSHJ	P,ADDZCH
	POP	P,B
	ANDI	B,17
	MOVE	0,B
	PUSHJ	P,ADDZCH
	POPJ	P,

ADDZCH:	JUMPN	ZCHR,ADDZC1
	MOVE	ZCHR,0
	POPJ	P,

ADDZC1:	LSH	ZCHR,4
	ADD	ZCHR,0
	MOVE	C,ZCHR
	PUSHJ	P,OUTBYC
	SKIPE	ZDEBUG
	 JRST	[PUSH	P,A
		 PUSH	P,B
		 PUSH	P,C
		 MOVEI	A,"(
		 PBOUT
		 MOVE	B,ZCHR
		 LSH	B,-4
		 MOVEI	A,.PRIOU
		 MOVEI	C,10.
		 NOUT
		  JFCL
		 MOVEI	B,"+
		 BOUT
		 MOVE	B,ZCHR
		 ANDI	B,17
		 NOUT
		  JFCL
		 MOVEI	B,")
		 BOUT
		 MOVEI	B,40
		 BOUT
		 POP	P,C
		 POP	P,B
		 POP	P,A
		 JRST	.+1]
	MOVEI	ZCHR,0
	POPJ	P,
	
MAKSTX:	PUSHJ	P,ENZASC
MAKSTE:	MOVEI	C,%FEOS		; strings end with EOS
	SKIPE	ZWDFLG
	 POPJ	P,
	MOVE	0,LSTFRM
	CAIE	0,%FFLG+%FESS
	 PUSHJ	P,OUTBYC
	POPJ	P,

OUTBYC:	EXCH	A,C		; output byte in c, saving a
	SKIPE	ZDEBUG
	 PUSHJ	P,PROUTB
	PUSHJ	P,OUTBYT
	MOVE	A,C
	SKIPN	PASS2
	 AOS	FSTRCT'
	POPJ	P,	

PROUTB:	PUSH	P,A
	PUSH	P,B
	PUSH	P,C
	MOVE	B,A
	MOVEI	A,.PRIOU
	MOVEI	C,10.
	NOUT
	 JFCL
	MOVEI	A,40
	PBOUT
	POP	P,C
	POP	P,B
	POP	P,A
	POPJ	P,

OUTFRM:	TRO	FRMT,%FFLG	; set the format bit
	MOVEM	FRMT,LSTFRM'
	SKIPE	ZDEBUG
	 JRST	[PUSH	P,A
		 MOVEI	A,"F
		 PBOUT
		 POP	P,A
		 JRST	.+1]
	MOVE	C,FRMT		; save A
	SKIPN	ZWDFLG
	 PUSHJ	P,OUTBYC	; output the format byte
	POPJ	P,

MAKFRM:	PUSH	P,A		; save text pointer
	ILDB	B,A		; get separator
	CAIN	B,".
	 JRST	MAKFPR
	CAIN	B,",		; check for comma
	 JRST	MAKFCM
	CAIN	B,40		; check for space
 	 JRST	MAKFSP
	CAIN	B,^M
	 JRST	MAKFEL
MAKFNR:	POP	P,A		; restore A to get separator into string 
	POPJ	P,

MAKFEL:	ILDB	B,A		; read LF
	MOVEI	C,%FEOL
	PUSHJ	P,OUTBYC
	MOVE	FRMT,FRMDFL
	JRST	POPPO

MAKFPR:	MOVE	C,A
	ILDB	B,C
	JUMPE	B,[MOVEI C,%FESS
		   JRST  MAKFPS]
	CAIE	B,40
	 JRST	MAKFNR
	MOVEI	C,%FESN
MAKFPS:	PUSHJ	P,OUTBYC
	MOVE	FRMT,FRMDFL
	MOVE	A,C
	JRST	POPPO

MAKFSP:	TRO	FRMT,%FSPC
POPPO:	POP	P,0
	POPJ	P,

MAKFCM:	TRO	FRMT,%FCOM	; set the comma next bit
	MOVE	C,A
	ILDB	B,C		; get next character
	CAIN	B,40		; is it a space?
	 JRST	[MOVE	A,C
		 TRO	FRMT,%FSPC
		 JRST	.+1]
	POP	P,0		; we're all set now with updated A
	POPJ	P,

;lookup word in word table
; a/ word
; +1: not found, loc to add in (A)
; +2: found, word is at (A)

WFREQ:	PUSH	P,B
	PUSH	P,F
	PUSH	P,G
	PUSH	P,H
      	SKIPL	G,WRDTAB
	 JRST	WFREQX
	HRRZ	G,G	;initial center point
	HRRZ	F,G	;initial low point
	MOVEI	H,WRDTND ;initial high point
;calculate test point
WFREQ1:	CAML	F,H	;not hit yet?
	 JRST	WFREQX
	SUB	G,F	;minus low point
	LSH	G,-1	;divide by two
	TRZ	G,1	;must be multiple of two (size of entries)
	ADD	G,F	;plus low
;test
	MOVE	B,1(G)	;get test
	PUSHJ	P,SFREQ
	 JRST	WFREQQ	;found it
	  SKIPA	H,G	;sample before
	MOVEI	F,2(G)	;sample after
	MOVE	G,H	;high point
	JRST	WFREQ1

WFREQQ:	AOS	-4(P)
	MOVE	C,(G)	;value
WFREQX:	POP	P,H
	POP	P,G
	POP	P,F
	POP	P,B
	POPJ	P,

;a/ sample
;b/ word from table
; +1: =
; +2: a>b
; +3: b>a

SFREQ:	PUSH	P,A
	PUSH	P,C
	SETZM	SFREQ1'
FREQN:	ILDB	C,B
	JUMPE	C,FREQQ
	ILDB	0,A
	SKIPN	SFREQ1
	 JRST	[CAIL	0,"A
	 	  CAILE	0,"Z
		   CAIA
		    ADDI 0,32.
		 JRST	.+1]
	SETOM	SFREQ1
	CAME	0,C
	 JRST	FREQD
	JRST	FREQN

FREQQ:	MOVE	C,A
	ILDB	B,C
	CAIN	B,"'
	 JRST	FREQD1
	PUSHJ	P,BALPHA
	 JRST	FREQQ1
	JRST	FREQD1
FREQQ1:	POP	P,C
	POP	P,0
	POPJ	P,

FREQD:	CAML	0,C
FREQD1:	 AOS	-2(P)
	AOS	-2(P)
	POP	P,C
	POP	P,A
	POPJ	P,

SUBTTL STRING ASSEMBLY DEBUGGING

;print zstring being assembled
;only called if CDEBUG is not 0
; a/ bptr to string
CSTRNG:	PUSH	P,A
	PUSH	P,B
	PUSH	P,C
	SKIPN	A,PDEBUG	;pick up script channel
	 MOVEI	A,.PRIOU	;or tty
	MOVEI	C,0
	HRROI	B,[ASCIZ /
"/]
	SOUT
	MOVE	B,-2(P)
	SOUT
	HRROI	B,[ASCIZ /"
/]
	SOUT
	JRST	POPCBA

;print character being produced for a zstring
;only called if CDEBUG is not 0
; b/ character
COUT:	PUSH	P,A
	PUSH	P,B
	PUSH	P,C
	MOVE	B,C
	SKIPN	A,PDEBUG	;pick up script channel
	 MOVEI	A,.PRIOU	;or tty if there is no script
	MOVEI	C,8		;radix 8
	HRLI	C,(NO%ZRO+NO%LFL)+2	;always print two column, pad with 0
	NOUT
	 JFCL
	MOVEI	B,40		;terminate with space 
	BOUT
POPCBA:	POP	P,C
	POP	P,B
	POP	P,A
	POPJ	P,

SUBTTL ROUTINE FOR PRINTING CURRENT ZFUNCTION NAME AND CURRENT PC

PFUNCT:	PUSH	P,A
	PUSH	P,B
	PUSH	P,C
        HRROI	A,[ASCIZ /  Len = /]
	PSOUT
	MOVEI	A,.PRIOU
	MOVE	B,ZPC
	SUB	B,ZPCLF'
	PUSH	P,B
	MOVEM	ZPC,ZPCLF
	MOVEI	C,10.
	NOUT
	 JFCL
	HRROI	A,[ASCIZ /  Str = /]
	PSOUT
	MOVEI	A,.PRIOU
	MOVE	B,FSTRCT
	ADDM	B,FSTRTT'
	MOVEI	C,10.
	NOUT
	 JFCL
	HRROI	A,[ASCIZ / (/]
	PSOUT
	POP	P,B
	MOVE	A,FSTRCT
	IMULI	A,100.
	IDIV	A,B
	MOVE	B,A
	MOVEI	A,.PRIOU
	MOVEI	C,10.
	NOUT
	 JFCL
	HRROI	A,[ASCIZ /%)/]
	PSOUT
	SETZM	FSTRCT
	MOVEI	A,^M
	PBOUT
	MOVEI	A,^J
	PBOUT
	MOVE	A,FUNCT
	PSOUT
	MOVEI	A,^I
	PBOUT
	MOVEI	A,.PRIOU
	MOVE	B,ZPC
	MOVEI	C,10.
	NOUT
	 JFCL
	POP	P,C
	POP	P,B
	POP	P,A
	POPJ	P,

SUBTTL WORD FREQUENCY PASS GOODIES GO HERE

FREQ:	MOVE	A,1(TP)
	CAIE	A,":
	 JRST	FREQ1
	NXTARG	1
	JRST	FREQ
FREQ1:	SKIPN	A,(TP)
	 SKIPE	1(TP)
	  SKIPA
	   POPJ	P,
	PUSHJ	P,LOOKUP
	 POPJ	P,
	JUMPL	B,FPSEUDO
	JRST	FOPER

FOPER:	TLNN	B,%STR
	 POPJ	P,
	NXTARG	1
	MOVE	D,(TP)
	PUSHJ	P,NEWWRD
	POPJ	P,

FPSEUD:	HRRZ	B,B
	SETZM	FPSVFL
	CAIE	B,ZINSER
	CAIN	B,ZENDI
	 JRST	(B)

	CAIN	B,ZZWORD
	 JRST	FPSEUV
	CAIE	B,ZSTRL
	 CAIN	B,ZSTR
	  JRST	FPSEU1
	CAIE	B,ZGSTR
	 POPJ	P,

FPSEUV:	SETOM	FPSVFL'
	JRST	FPSEU1

FPSEU2:	NXTARG	1
FPSEU1:	NXTARG	1
	SKIPN	D,(TP)
	 JRST	TFARG
	PUSHJ	P,NEWWRD
	POPJ	P,

;main entry to count frequency of words in a particular string
; called with string pointer in D

NEWWRD:	JUMPE	D,CPOPJ
	MOVE	E,[440700,,WRDBUF]
	MOVEI	J,0	;count of bytes
NXTWRD:	ILDB	A,D
	JUMPE	A,CPOPJ
	PUSHJ	P,ALPHA
	 JRST	NXTWRD
	CAIG  	A,"Z
	 CAIGE	A,"A
	  CAIA
	   ADDI	A,40
WRDLP:	IDPB	A,E
	ADDI	J,1
	MOVE	F,D	;save this pointer
	ILDB	A,D
	JUMPE	A,WRDEOS
	CAIG  	A,"Z
	 CAIGE	A,"A
	  CAIA
	   ADDI	A,40
	CAIN	A,"'
	 JRST	WRDLP
	PUSHJ	P,ALPHA
	 JRST	WRDEND	;not alphabetic
	JRST	WRDLP

WRDEOS:	MOVEI	D,0	;end of input string
	JRST	WRDEN2
WRDEND:	MOVE	D,F	;recover non-spaced bptr
WRDEN3:	MOVEI	A,0
WRDEN2:	IDPB	A,E
	MOVE	A,[440700,,WRDBUF]
	PUSHJ	P,WLOOK
	 JRST	WRDADD	;not there, go add it
	AOS	(G)	;add to its usage count
	JRST	NEWWRD

WRDADD:	SKIPN	WDEBUG
	 JRST	WRDAD1
	MSG	["]
	PSOUT
	MOVE	A,[440700,,WRDBUF]
	PSOUT
	MSG	["
]
	PSOUT

WRDAD1:	MOVE	A,TABPTR
	TLNN	A,400000
	 JRST	[HRLI A,440700
		 ADDI A,1
		 JRST .+1]
	MOVE	H,A
	MOVE	B,[440700,,WRDBUF]
	MOVEI	C,0
	SOUT		;copy string to buffer
	IDPB	C,A
	MOVEM	A,TABPTR
;update table pointer
	PUSH	P,G
	MOVE	G,WRDTAB
	SUB	G,[2,,2]
	MOVEM	G,WRDTAB
	POP	P,G
;make a slot for new entry
	HRRZ	A,WRDTAB
	HRLI	A,2(A)
	BLT	A,-1(G)
;put out new entry
	MOVEM	H,-1(G)	;string
	MOVEI	H,1
	HRL	H,J	;size of string in bytes
	MOVEM	H,-2(G)	;count
	JRST	NEWWRD

;here when all done
FILEND:	
	PUSHJ	P,BYTES
	PUSHJ	P,SORT


;here to output the data
	MOVE	A,[440700,,[ASCIZ /FREQ.ZAP/]]
	MOVE	B,OUTPTR
	ILDB	0,A
	IDPB	0,B
	JUMPN	0,.-2
	MOVSI	A,(GJ%SHT+GJ%FOU)
	HRROI	B,OUTFIL
	GTJFN
	 JRST	ERPRNT
	HRRZ	A,A
	MOVEM	A,OJFN
	MOVE	B,[070000,,OF%WR]
	OPENF
	 JRST	ERPRNT

;output the goodies
	MOVE	G,WRDTAB
	HRLI	G,-<2*%FWDCT>
	PUSHJ	P,PTAB
	
	MOVE	A,OJFN
	HRROI	B,[ASCIZ /

WORDS::	.TABLE/]
	MOVEI	C,0
	SOUT
	MOVE	G,[-%FWDCT,,1]
FWTBLL:	MOVE	A,OJFN
	HRROI	B,[ASCIZ /
	FSTR?/]
	MOVEI	C,0
	SOUT
	HRRZ	B,G
	MOVEI	C,10.
	NOUT
	 JFCL
	AOBJN	G,FWTBLL
	MOVE	A,OJFN
	HRROI	B,[ASCIZ /

	.ENDI
/]
	MOVEI	C,0
	SOUT
	CLOSF
	 JFCL
	HALTF

;calculate bytes saved
BYTES:	MOVE	A,WRDTAB
	SETZM	XTWRDS'
BYTES1:	HRRZ	B,(A)
	ADDM	B,XTWRDS
	HRLM	B,(A)
	ADD	A,[2,,2]
	JUMPL	A,BYTES1
	POPJ	P,

;sort word table by bytes saved
SORT:	MOVE	A,WRDTAB
;next slot of table
SORTM:	MOVE	B,A
	SETZB	C,D
	SETZ	E,
;next try for largest number
SORTN:	CAMLE	C,(B)
	 JRST	SORTL
;pick up new candidate
	MOVE	C,(B)
	MOVE	D,1(B)
	MOVE	E,B
SORTL:	ADD	B,[2,,2]
	JUMPL	B,SORTN
;end of pass
	JUMPE	C,SORTO
	EXCH	C,(A)
	MOVEM	C,(E)
	EXCH	D,1(A)
	MOVEM	D,1(E)
;move to next slot
SORTO:	MOVE	C,(A)
SORTP:	ADD	A,[2,,2]
	JUMPGE	A,CPOPJ
	CAMN	C,(A)
	 JRST	SORTP
	JRST	SORTM

NEXT31:	MOVE	A,WRDTAB
	ADD	A,[76,,76]
	MOVEM	A,WRDTAB
N31LUP:	HRRZ	B,(A)
	HLRZ	C,(A)
	IDIV	C,B
	SUBI	C,1
	HRLM	C,(A)
	ADD	A,[1,,1]
	AOBJN	A,N31LUP
	PUSHJ	P,BYTES
	PUSHJ	P,SORT
	POPJ	P,


PSAVED:	MSG	[31 words: ]
	PSOUT
	MOVEI	A,.PRIOU
	MOVE	B,D
	MOVEI	C,10.
	NOUT
	 JFCL
	MSG	[ zbytes saved, ]
	PSOUT
	MOVEI	A,.PRIOU
	MOVE	B,E
	NOUT
	 JFCL
	MSG	[ uses.

]
	PSOUT
	POPJ	P,

PTABS:	MOVEI	A,101
	MOVEM	A,OJFN
      	MOVE	G,WRDTAB
	HRLI	G,-76
	PUSHJ	P,PTAB
	PUSHJ	P,PSAVED
	PUSHJ	P,NEXT31
	MOVE	G,WRDTAB
	HRLI	G,-76
	PUSHJ	P,PTAB
	PUSHJ	P,PSAVED
	PUSHJ	P,NEXT31
	MOVE	G,WRDTAB
	HRLI	G,-76
	PUSHJ	P,PTAB
	PUSHJ	P,PSAVED
	POPJ	P,

PTABLE:	PUSH	P,G
	MOVE	G,WRDTAB
	PUSHJ	P,PTAB
	POP	P,G
	POPJ	P,

PTAB:	PUSH	P,A
	PUSH	P,B
	PUSH	P,C
	SETZB	D,E
	MOVEI	F,0
PTLOOP:	ADDI	F,1
	MOVE	A,OJFN
	HRROI	B,[ASCIZ /	.FSTR FSTR?/]
	MOVEI	C,0
	SOUT
	MOVE	B,F
	MOVEI	C,10.
	NOUT
	 JFCL
	MOVE	A,OJFN
	HRROI	B,[ASCIZ /,"/]
	MOVEI	C,0
	SOUT
	MOVE	B,1(G)
	SOUT
	HRROI	B,[ASCIZ /"	;/]
	SOUT
	MOVE	A,OJFN
	HLRZ	B,(G)
	ADD	D,B
	MOVEI	C,10.
	NOUT
	 JFCL
	MOVEI	B,15
	BOUT
	MOVEI	B,12
	BOUT
	ADD	G,[2,,2]
	JUMPL	G,PTLOOP
	PUSHJ	P,PT512
	POP	P,C
	POP	P,B
	POP	P,A
	POPJ	P,

PT512:	HRROI	B,[ASCIZ /

;	Top 512 Words: /]
 	MOVEI	C,0
	SOUT
	MOVE	A,OJFN
	MOVE	B,D
	MOVEI	C,10.
	NOUT
	 JFCL
	HRROI	B,[ASCIZ / uses (/]
	MOVEI	C,0
	SOUT
	MOVE	A,OJFN
	MOVE	B,D
	IMULI	B,100.
	IDIV	B,XTWRDS
	MOVEI	C,10.
	NOUT
	 JFCL
	HRROI	B,[ASCIZ /%)

/]
	MOVEI	C,0
	SOUT
	POPJ	P,

	

;lookup word in word table
; a/ word
; +1: not found, loc to add in (g)
; +2: found, word is at (g)

WLOOK:	SKIPL	G,WRDTAB
	 POPJ	P,
	HRRZ	G,G	;initial center point
	HRRZ	F,G	;initial low point
	MOVEI	H,WRDTND ;initial high point
;calculate test point
LOOK1:	CAML	F,H	;not hit yet?
	 POPJ	P,
	SUB	G,F	;minus low point
	LSH	G,-1	;divide by two
	TRZ	G,1	;must be multiple of two (size of entries)
	ADD	G,F	;plus low
;test
	MOVE	B,1(G)	;get test
	PUSHJ	P,SCOMP
	 JRST	LOOKEQ	;found it
	  SKIPA	H,G	;sample before
	MOVEI	F,2(G)	;sample after
	MOVE	G,H	;high point
	JRST	LOOK1

LOOKEQ:	AOS	(P)
	POPJ	P,

;a/ sample
;b/ word from table
; +1: =
; +2: a>b
; +3: b>a

SCOMP:	PUSH	P,A
	PUSH	P,C
COMPN:	ILDB	0,A
	ILDB	C,B
	CAME	0,C
	 JRST	COMPD
	JUMPE	0,COMPX
	JRST	COMPN
COMPX:	POP	P,C
	POP	P,A
	POPJ	P,

COMPD:	CAML	0,C
	 AOS	-2(P)
	AOS	-2(P)
	JRST	COMPX

ALPHA:	CAIL	A,"A
	 CAILE	A,"Z
	  SKIPA
	   JRST	ALPHA1
	CAIL	A,"a
	 CAILE	A,"z
	  POPJ	P,
ALPHA1:	AOS	(P)
	POPJ	P,


BALPHA:	CAIL	B,"A
	 CAILE	B,"Z
	  SKIPA
	   JRST	BALPH1
	CAIL	B,"a
	 CAILE	B,"z
	  POPJ	P,
BALPH1:	AOS	(P)
	POPJ	P,

PUNCT:	CAIE	A,",
	 CAIN	A,".
	  POPJ	P,
	CAIE	A,"!
	 CAIN	A,"?
	  POPJ	P,
	AOS	(P)
	POPJ	P,


SUBTTL VARIABLES AND BUFFERS

;debugging flags
SDEBUG:	0		;if non-0, print symbol table
PDEBUG:	0		;if non-0, print lines as they are read
TDEBUG:	0		;if non-0, print tokens after parsing them
ODEBUG:	0		;if non-0, print opers info
CDEBUG:	0		;if non-0, print strings in "zascii"
ZDEBUG:	-1
FDEBUG:	0		;if non-0, print functions as they are found
STOP:	0		;if non-0, location to halt at (for changing flags)
SYMFLG:	0		;if non-0, output symbol table

;flags for word frequency pass
DOFREQ:	0		;if non-0, this is word frequency run, not assy.
WDEBUG:	0		;if non-0, print new words during frequency pass

;i/o goodies

;gtjfn block for normal file opening
GTJFNB:	GJ%OLD		;flags
	.NULIO,,.NULIO	;jfns
	0		;device
	0		;dir
	-1,,[ASCIZ /ZIPTEST/]	;name
	-1,,[ASCIZ /ZAP/] ;ext
	0		;prot
	0		;acct
	0		;jfn

;gtjfn block for normal file opening
GTJFNX:	GJ%OLD		;flags
	.NULIO,,.NULIO	;jfns
	0		;device
	0		;dir
	-1,,[ASCIZ /ZIPTEST/]	;name
	-1,,[ASCIZ /XZAP/] ;ext
	0		;prot
	0		;acct
	0		;jfn

;gtjfn block for reading file name from tty
GTJFNT:	GJ%OLD+GJ%EXT	;flags
	.PRIIN,,.PRIOU	;jfns
	0		;device
	-1,,[ASCIZ /INFOCOM.ZORK/] ;dir
	-1,,[ASCIZ /ZIPTEST/]	;name
	-1,,[ASCIZ /ZAP/] ;ext
	0		;prot
	0		;acct
	0		;jfn
	0		;f2
	0		;input copy
	0		;
	-1,,[ASCIZ /File/]
	0
	0

;output gtjfn
OUTPTR:	440700,,OUTFIL
OUTFIL:	BLOCK 20

OJFN:	0		;old input jfn, for when .INSERT done
IJFN:	0		;input jfn
FILBUF:	BLOCK 20.
FILPTR:	0
JOBNAM:	ASCIZ	/MUDDLE/

PDL:	BLOCK 100	;stack

ZAPID:	3		;zap id number (assembly language version)

FLGWRD:	0		;1 if byte swapped (not implemented)
%BYTSWP==1		;flag word bit for byte-swapped mode
%TIMESL==2		;flag word bit for 'time' status line

RELEAS:	-1		;release number

;various assembler variables
SAVZPC:	0	;saved pc used mostly by debugging printers
SAVZ:	0	;saved output ptr ditto

TABLE:	0	;if in table, holds pc of table start
TABLEN:	0	;if in table, holds max length or -1 if none

GLBTOT:	0	;how many globals he made (limit is 255-20)
GLBCNT:	17	;current global (1-17 are really locals)

OBJTOT:	0	;how many objects he made (limit is 255)
OBJCNT:	0	;current object

FUNCT:	0	;non-zero during function assy.
FSYM:	0	;symbol value of last function

LSTSYM:	0	;last symbol defined

WRDBYT:	0	;-1 if assembling byte, 0 if word
JMPREF:	0	;-1 if assembling jump, 0 otherwise
SHRIMP:	0	;long jumps that were wasted
OSHRIM:	0	;saved count of wasted long jumps

;goodies for instruction assembly

NOREF:	0	;-1 if not to assemble references (as instruction operands
		;are moved into ARGBUF)

OPER:	0	;operator is saved here

ARGBUF:	BLOCK 14 ;args to operators, pairs of values and strings

SENSE:	0	;sense of predicate jump
PRED:	0	;value of predicate byte
	0	;ptr to string defining it
VAL:	0	;value of value byte
	0	;string defining it

LSTRWD:	0	;Z at last string word output saved here for stop bit addition

;junk for second pass over functions
TWOPAS:	-1	;-1 if two pass assembly
PASS2:	0	;-1 if doing second pass
FPOS:	0	;saved file pointer
FZ:	0	;saved z
FZPC:	0	;saved zpc
FSHORT:	0	;count of short jumps saved
ZCSET:	0	;char set of last character looked at

;parsing information of various sorts
BUFFER:	BLOCK 1000	;read in buffer

TOKEN:	BLOCK 1000	;buffer for parsed tokens
TOKPTR:	0		; ptr into same

TPDL:	-100.,,TOKENS-1	;stack for pairs of token/terminator
TOKENS:	BLOCK 100.	; points to here

;junk to unsuccessfully fool GC-READ (joel is a twit)
;this stuff is modified by OUTPUT
HEADER:	1305		;object plus type word
	1305
	1305
	122		; ??
	41		; ??
	51,,5374	;type,,length
	41000,,2006	;bptr to start

FOOTER:	40003,,0	;bytes
	1303,,3311	;length,,self

;get these out of the way
VARIAB
CONSTA

SUBTTL SYMBOL TABLES

SYMPTR:	SYMBUF+2	;ptr to symbol table buffer
FCNPTR:	FCNBUF		;ptr to function table buffer

SYMSIZ==3	;size of a symbol entry
SYMNAM==0	;offset of name slot
SYMVAL==1	;offset of value slot
SYMREF==2	;offset of references slot

BUCKN==201.	;how many buckets
BUCKL==25.*SYMSIZ ;how long buckets are

;local symbol goodies
LCLLST:	0		;list of local symbols
LCLPTR:	LCLBUF		;ptr to free space in local symbol buffer
LCLBUF:	BLOCK 10000	;local symbol pnames buffer

LCLOBL:	-<BUCKN*BUCKL>,,LCLTAB	;ptr to local symbol hash table
LCLTAB:	BLOCK BUCKN*BUCKL	;local symbol hash table
LCLEND:	0			;end of same

;global symbol goodies
GLBLST:	0		;list of global symbols
GLBPTR:	GLBBUF		;ptr to free space in global symbol buffer
GLBBUF:	BLOCK 40000	;global symbol pname buffer starts here

GLBOBL:	-<BUCKN*BUCKL>,,GLBTAB	;ptr to global symbol hash table
GLBTAB:	BLOCK BUCKN*BUCKL	;global symbol hash table
GLBEND:	0			;end of same

;word frequency hack stuff is here
FREQST:	0		;-1 when assembling string that can have fstrs
FSTRS:	-1		;count of .FSTRs seen
WRDBUF:	BLOCK	10.

WRDTLN==20000.
WRDTND==700000+WRDTLN-2

WRDTAB:	WRDTND
TABPTR:	440700,,.+1
	LOC .+1000

;output buffer

OUTBUF==<.+77777>&-100000	;lies at 100000*n

;symbol table hacks

FCNBUF==OUTBUF+200000		;function symbol tables made here
SYMBUF==FCNBUF+10000		;symbol tables made mapped here

	END	START
