;
;THE ASSEMBLER STARTS HERE
;
;ASPC IS THE PROGRAM COUNTER (GOES ON LISTING), AND
;BBUF+2 IS THE STORAGE COUNTER (WHERE OBJECT CODE GOES)
;
ASSM:	CALL	VCHK		;AT LEAST 1 PARM. REQD
	LDA	ABUF+7		;2ND PARM. (OFFSET) GIVEN ?
	ORA	A		;SET FLAGS
	JNZ	ASM4		;USE 2ND PARM. IF GIVEN
	LHLD	BBUF		;ELSE GET 1ST PARM
	SHLD	BBUF+2		;USE 1ST PARM. AS STORAGE CNTR
ASM4:	LDA	IBUF+4		;GET LETTER AFTER ASSM OR ASSI
	CPI	'E'		;'E' - OMIT NON-ERROR LINES ?
	JZ	LEAD6		;SKIP CHECKING FOR 'S' OR 'X'
	CPI	'S'		;'S' - LIST SYMBOL TABLE ?
	JZ	LEAD6		;SKIP CHECKING FOR 'X'
	CPI	'X'		;'X' - CROSS REFERENCE ?
	JZ	LEAD6		;HAVE PRINT FLAG
	CPI	' '		;MUST BE SPACE, E, S, OR X
	JNZ	WHAT		;WHOOPS - ILLEAGLE CHAR.
LEAD6:	STA	SYMX		;SAVE SYMBOL/PRINT FLAG
	LDA	IBUF+3		;GET ASSEMBLY TYPE (ASSM/ASSI)
	STA	ASMTY		;SAVE AS ASSEMBLY TYPE
	XRA	A		;GET A ZERO
	LHLD	SYMADD		;POINT TO SYMBOL TABLE START
	MOV	M,A		;PUT IN END OF TABLE MARK
	STA	ALST		;ASSUME LIST TURNED 'ON'
	STA	PASI		;SET PASS INDICATOR TO PASS 1
LEAEA:	LHLD	BBUF		;GET PGM. ORG
	SHLD	ASPC		;USE IT TO INIT. PROG. COUNTER
	LHLD	BOFP		;GET START OF SOURCE
	SHLD	APNT		;USE IT TO INIT. ASS. POINTER
;
;THIS IS THE START OF THE MAIN ASSEMBLER PROCESSING LOOP
;
LEAF6:	LXI	SP,SMODE
	LXI	H,OBUF		;POINT TO OUTPUT BUFFER
	MVI	A,IBUF AND 0FFH	;LOW BYTE OF END OF OBUF
	CALL	CLER		;CLEAR OBUF TO SPACES
	LDA	ASMTY		;GET ASSM/ASSI FLAG
	CPI	'I'		;USING EXTERNAL INPUT ?
	JZ	LEB12		;YES - CALL IT
	CALL	MOVEL		;ELSE MOVEL PUTS LINE IN IBUF
	JZ	LEE39		;IF END OF FILE DETECTED
	JMP	LEB15		;SKIP CALL TO EXT. INPUT
LEB12:	CALL	IN8		;CALL ASSI DRIVER FOR EXT INPUT
LEB15:	LDA	PASI		;GET PASS INDICATOR
	ORA	A		;SET FLAGS
	JNZ	ASM2		;DO THE 2ND PASS
	CALL	PAS1		;ELSE DO PASS 1
	JMP	LEAF6		;THEN BACK TO TOP OF LOOP
ASM2:	CALL	PAS2		;PROCESS PASS 2 LINE
	LXI	H,OBUF		;POINT TO OUTPUT BUFFER
	CALL	AOUT		;PRINT LINE
	JMP	LEAF6		;THEN BACK TO TOP OF LOOP
;
;END OF MAIN LOOP
;
;FOLLOWING ROUTINE PRINTS LINE DURING 2ND PASS
;
AOUT:	LDA	OBUF+18		;GET ERROR TYPE IN OUTPUT LINE
	CPI	' '		;WAS THERE AN ERROR ?
	JNZ	LEB41		;YES - PRINT LINE
	LDA	AERR		;GET SYMBOL/ERROR PRINT FLAG
	CPI	'E'		;ERRORS-ONLY LISTING ?
	RZ			;YES - DONE WITH THIS LINE
	LDA	ALST		;GET ASSEMBLER LIST FLAG
	ORA	A		;SET FLAGS
	RNZ			;DONE IF LISTING TURNED OFF
LEB41:	LDA	PASI		;GET PASS INDICATOR
	CPI	2		;GREATER THAN 1 (E.G. 3RD PASS)
	RNC			;DONE THEN
	LDA	LFMT		;GET FORMAT/NO FORMAT FLAG
	ORA	A		;SET FLAGS
	CNZ	LF361		;FORMAT LINE IF NECESSARY
	LXI	H,OBUF		;POINT TO LINE TO OUTPUT
	CALL	CRLF		;DO CARRIAGE RET FROM PREV LINE
	JMP	SCRN		;PRINT LINE & RET.
;
;PASS 1 OF THE ASSEMBLER USED TO FORM SYMBOL TABLE
;
PAS1:	CALL	ZBUF		;CLEAR OUT THE ASCII BUFFER
	STA	PASI		;PUT PASS INDICATOR BACK
	LXI	H,IBUF		;POINT TO 1ST CHAR OF LINE
	SHLD	PNTR		;SAVE AS POINTER INTO LINE
	MOV	A,M		;GET 1ST CHAR
	CPI	' '		;BLANK ?
	JZ	OPC		;NO LABEL THEN
	CPI	COMCHR		;COMMENT CHARACTER ?
	RZ			;DONE IF COMMENT LINE
;
;PROCESS LABEL POINTED TO IN LINE BY H,L
;
	CALL	SLAB		;SEE IF LABEL ALREADY IN TABLE
	JC	LF0E0		;ERROR IN LABEL
	JZ	ERRD		;DUPLICATE LABEL
	CALL	LCHK		;CHECK CHAR AFTER LABEL
	JC	LF0E0		;IF NOT SPACE OR COLON
	CALL	LEBA5		;TO MOVE LABEL INTO TABLE
	XCHG			;SYM. TABLE ADDR TO H,L
	SHLD	TABA		;SAVE AS SYMBOL TABLE PNTR
	LDA	ASPC+1		;PUTTING P.C. IN SYMBOL TABLE
	MOV	M,A		;PUT 1ST BYTE IN TABLE
	INX	H		;INR. TABLE POINTER
	LDA	ASPC		;GET 2ND BYTE
	MOV	M,A		;PUT IT IN TABLE
	INX	H		;POINT PAST VALUE
	MVI	M,0		;END OF TABLE MARK
;
;PROCESS OPCODE
;
OPC:	CALL	ZBUF		;BLANK OUT ABUF
	CALL	SBLK		;SCAN TO OPCODE
	JC	OERR		;IF FOUND C/R INSTEAD
	CALL	ALPS		;MOVE OPCODE INTO ABUF
	CPI	' '		;CHECK FOR SPACE AFTER OPCODE
	JC	OPCD		;OPCODE FOLLOWED BY C/R
	JNZ	OERR		;OPCODE FOLLOWED BY CHAR.
	JMP	OPCD		;TO CONTINUE OPCODE PROC.
;
;ROUTINE TO MOVE LABEL INTO SYMBOL TABLE
;
LEBA5:	MVI	C,LLAB		;LENGTH OF LABEL
	LXI	H,ABUF		;SOURCE
MLAB:	MOV	A,M		;GET CHAR FROM LABEL
	STAX	D		;PUT IT IN TABLE
	INX	D		;DEST. POINTER
	INX	H		;SOURCE POINTER
	DCR	C		;LENGTH POINTER
	JNZ	MLAB		;IF MORE CHARS TO MOVE
	RET			;ELSE DONE
;
;CHECK LABELS FOR VALID TERMINATOR
;
LCHK:	LHLD	PNTR		;GET POINTER
	MOV	A,M		;GET CHAR
	CPI	' '		;SPACE ?
	RZ			;OK THEN, RETURN
	CPI	':'		;COLON ?
	RNZ			;IF NOT, RETURN ZERO FLAG OFF
	INX	H		;ELSE OK, INR. PAST NON-SPACE
	SHLD	PNTR		;AND UPDATE POINTER
	RET			;THEN RETURN
;
;PROCESS PSEUDO OPS IN PASS 1
;H,L POINTS INTO LINE, D,E POINTS INTO OPCODE TABLE
;
PSU1:	CALL	SBLK		;SCAN TO OPERAND
	LDAX	D		;FETCH TABLE VALUE
	ORA	A		;SET FLAGS
	JZ	ORG1		;ORG PSEUDO OP
	JM	DAT2A		;DB PSEUDO OP
	CPI	2		;
	JC	EQU1		;EQU PSEUDO OP
	JZ	RESI		;DS PSEUDO OP
	CPI	8		;
	JZ	LEC26		;ASC PSEUDO OP
	CPI	5		;
	RNC			;PSEUDO-OPS NOT USED 1ST PASS
	JPE	LEE39		;END PSEUDO OP
;
;DO DW PSEUDO OP
;
ACO1:	MVI	C,2		;DW IS A 2-BYTE INSTR.
	XRA	A		;CLEAR CARRY & FLAGS
	JMP	OCN1		;TO INCREMENT PGM. COUNTER
;
;DO ORG PSEUDO OP
;
ORG1:	CALL	ASCN		;GET OPERAND TO H,L
	LDA	OBUF+18		;GET ERROR INDICATOR
	CPI	' '		;ANY ERRORS IN THIS LINE ?
	RNZ			;IF SO, DON'T PROCESS
	SHLD	ASPC		;ELSE PUT VALUE IN PGM. CTR.
	LDA	IBUF		;GET 1ST CHAR OF LINE
	CPI	' '		;SPACE OR LABEL CHAR ?
	RZ			;DONE IF SPACE
	JMP	EQUS		;ELSE CHANGE LABEL VALUE
;
;DO EQU PSEUDO OP
;
EQU1:	CALL	ASCN		;GET VALUE TO H,L
	LDA	IBUF		;POINT AT 1ST CHAR OF LINE
	CPI	' '		;SPACE OR LABLE CHAR ?
	JZ	ERRM		;EQU REQUIRES A LABEL ALWAYS
EQUS:	XCHG			;VALUE TO D,E
	LHLD	TABA		;TABLE ADDR TO H,L
	MOV	M,D		;STORE LABEL VALUE IN S. TABLE
	INX	H		;2ND BYTE
	MOV	M,E		;
	LDA	OBUF+18		;GET ERROR FLAG
	CPI	' '		;LINE O.K. ?
	RZ			;DONE THEN
	JMP	LEB41		;ELSE PRINT THE LINE
;
;DO DS PSEUDO OP
;
RESI:	CALL	ASCN		;GET OPERAND VALUE TO H,L
	MOV	B,H		;MOVING TO B,C
	MOV	C,L		;
	LDA	OBUF+18		;GET ERROR INDICATOR
	CPI	' '		;LINE O.K. ?
	JNZ	LEB41		;PRINT IT IF NOT
	JMP	RES21		;ELSE ADD B,C TO PGM. CTR.
;
;DO ASC PSEUDO OP
;
LEC26:	MOV	A,M		;GET 1ST NON-SPACE CHAR
	CPI	0DH		;CARRIAGE RETN ?
	RZ			;DONE
	MOV	D,A		;SAVE DELIMITER IN D
	LXI	B,0		;INITIALIZE CHAR COUNT TO 0
LEC2E:	INX	H		;POINT TO NEXT CHAR
	MOV	A,M		;GET IT
	CPI	0DH		;END OF LINE ?
	JZ	RES21		;DONE WITH STRING
	CMP	D		;2ND DELIMETER ?
	JZ	RES21		;DONE IF SO
	INX	B		;ELSE INR. CHAR COUNT
	JMP	LEC2E		;AND KEEP ON COUNTING
;
;PERFORM PASS 2 OF THE ASSEMBLER
;
PAS2:	LXI	H,OBUF		;SET OUTPUT BUFFER ADDRESS
	LDA	ASPC+1		;GET HIGH BYTE OF PGM. CTR.
	CALL	BINH+3		;CONVERT TO ASCII & STORE
	INX	H		;POINT PAST BYTE
	LDA	ASPC		;GET LOW ORDER BYTE
	CALL	BINH+3		;PUT IT IN ADDR FIELD, TOO
	SHLD	OIND		;SAVE H,L AS OUTPUT POINTER
	CALL	ZBUF		;CLEAR ABUFF
	LXI	H,IBUF		;POINT TO LINE TO ASSEMBLE
PABL:	SHLD	PNTR		;SAVE AS POINTER
	MOV	A,M		;GET 1ST CHAR OF LINE
	CPI	' '		;SPACE ?
	JZ	OPC		;YES, NO LABEL, PROC. OPCODE
	CPI	COMCHR		;IS IT A COMMENT LINE ?
	RZ			;DONE THEN
	CALL	SLAB		;LOOK UP LABEL IN TABLE
	JC	LF0E0		;LABEL ERROR
	CALL	LCHK		;CHECK TERM. CHAR
	JNZ	LF0E0		;NOT FOLLOWED BY '$' OR ':'
	JMP	OPC		;PROCESS THE OPCODE
;
;PROCESS PSEUDO OPS FOR THE 2ND PASS
;
PSU2:	LDAX	D		;GET TABLE VALUE
	ORA	A		;SET FLAGS
	JZ	ORG2		;ORG PSEUDO OP
	JM	DAT2		;DB PSEUDO OP
	CPI	2
	RC			;EQU WAS PROCESSED ON 1ST PASS
	JZ	RES2		;DS PSEUDO OP
	CPI	8
	JZ	LED01		;ASC PSEUDO OP
	CPI	5
	JZ	LECD3		;COM PSEUDO OP
	JNC	LECCD		;BOTH LST AND NLST
	JPE	LEE39		;END PSEUDO OP
;
;DO DW PSEUDO OP
;
ACO2:	CALL	TYS6		;GET VALUE & PUT IN MEM
	JMP	ACO1		;INR P.C. & RET
;
;DO DS PSEUDO OP IN PASS 2
;
RES2:	CALL	ASBL		;GET VALUE IN H,L
	MOV	B,H		;MOVING TO B,C
	MOV	C,L
	LHLD	BBUF+2		;GET STORAGE COUNTER
	DAD	B		;ADD NO BYTES IN DS STMNT
	SHLD	BBUF+2		;UPDATE STORAGE COUNTER
RES21:	XRA	A		;CLEAR ZERO FLAG
	JMP	OCN2		;TO UPDATE PGM. COUNTER
;
;DO PASS 2 DB PSEUDO OP
;
DAT2:	CALL	TYS5		;GET OPERNAD
DAT2A:	XRA	A		;MAKE ACC. ZERO
	MVI	C,1		;BYTE COUNT
	JMP	OCN1		;TO INR. PGM. COUNTER
;
;DO ORG PSEUDO OP
;
;P.T. SCREWED THIS ONE UP, AS THEY STORE THE NEW ORG NOT ONLY
;AS THE ASSEMBLERS PROGRAM COUNTER, BUT ALSO AS THE STORAGE
;COUNTER, WHICH POINTS TO WHERE THEY ARE STORING THE OBJECT
;CODE. THUS IF YOU WERE USING AN OFFSET (E.G. ASSM XXXX YYYY)
;BEFORE THE ORG STATEMENT, YOU WON'T BE AFTERWARDS.  WHAT'S
;SURPRISING IS THAT IMSAI'S SCS-1, WRITTEN BY THE SAME CO.
;THAT WROTE ALS-8 FOR P.T. (MICRO-TEC OF SUNNYVALE CALIF.),
;HAS THIS FIXED, EVEN THOUGH IT WAS AN EARLIER PRODUCT.
;
ORG2:	CALL	ASBL		;GET ORG VALUE
	LDA	OBUF+18		;FETCH ERROR FLAG
	CPI	' '		;LINE O.K. ?
	RNZ			;IGNORA IF NOT
	XCHG			;VALUE TO D,E
	LHLD	ASPC		;GET CUR. PGM COUNTER
	XCHG			;NEW ORG H,L - PC IN D,E
	SHLD	ASPC		;MAKE ORG NEW P.C.
	SHLD	BBUF+2		;AND MAKE IT STORAGE CNTR ALSO
	RET			;DONE, AND YOU ARE SCREWED
;
;PROCESS THE $ SYMBOL (CUR. VALUE OF PGM. COUNTER)
;
LECC3:	INX	H		;INCREMENT POINTER
	SHLD	PNTR		;SAVE POINTER
	LHLD	ASPC		;GET CUR. PGM. COUNTER
	JMP	AVAL		;TO FINISH EXPR. EVALUATION
;
;PROCESS NLST AND LST PSEUDO OPS.
;
LECCD:	SUI	6		;CONVERT LST/NLST TO 0 OR 1
	STA	ALST		;SAVE AP LIST FLAG
	RET			;DONE
;
;PROCESS COM PSEUDO OP
;
LECD3:	CALL	ZBUF		;CLEAR ABUF
	CALL	SBLK		;SCAN TO LABEL TO ENTER
	CALL	SLAB		;LOOKUP LABEL, VALUE IN H,L
	JC	ERRA		;IF ERROR IN LABEL
	JNZ	ERRU		;IF LABEL NOT FOUND
LECE2:	LXI	D,SYSYM		;SYSTEM SYMBOL TABLE
LECE5:	SHLD	OPRD		;SAVE VALUE IN OPRD
	MVI	B,LLAB		;LENGTH OF LABELS
	CALL	COMS		;SEE IF ALREADY IN SYSTEM TABLE
	JZ	LECF9		;ALREADY IN TABLE, CHG VALUE
	CALL	LEBA5		;NOT IN TABLE, ADD IT
	INX	D		;POINT PAST (UNDEFINED) VALUE
	INX	D		;2ND BYTE
	XRA	A		;GET BINARY ZERO
	STAX	D		;TABLE'S NEW END MARK
	DCX	D		;POINT TO 2ND BYTE OF VALUE
	INR	A		;TURN ZERO FLAG OFF
LECF9:	LHLD	OPRD		;GET SYMBOL'S VALUE
	XCHG			;TO D,E
	MOV	M,E		;STORE 1 BYTE
	DCX	H		;POINT FROM 2ND TO 1ST BYTE
	MOV	M,D		;STORE IT, TOO.
	RET			;DONE
;
;DO ASC PSEUDO OP
;
LED01:	CALL	SBLK		;SCAN TO 1ST NON BLANK CHAR
	JC	ERRA		;IF C/R FOUND
	MOV	C,A		;SAVE AS DELIM.
	INX	H		;POINT TO 1ST CHR OF STRING
	MOV	A,M		;GET IT
	CPI	0DH		;END OF LINE ?
	JZ	ERRA		;ERROR THEN
	CMP	C		;SECOND DELIMITER ?
	JZ	ERRA		;ERROR THEN, TOO
	MVI	B,0F9H		;******MAY BE ADDRESS DEPENDANT
LED15:	MOV	A,M		;GET THE CHAR FROM STRING
	CPI	0DH		;END OF LINE ?
	JZ	LED57		;DONE WITH STRING THEN
	CMP	C		;2ND DELIMITER ?
	JZ	LED57		;DONE WITH STRING THEN
	SHLD	PNTR		;SAVE STRING POINTER
	LHLD	ASPC		;GET PROGRAM COUNTER
	INX	H		;THIS USED UP 1 BYTE
	SHLD	ASPC		;PUT UPDATED POINTER BACK
	INR	B		;
	PUSH	B		;SAVE B,C
	CALL	ASTO		;PUT BYTE IN MEMORY & LISTING
	MOV	A,L		;GET LOW BYTE OF LISTING PTR
	CPI	(OBUF+15) AND 0FFH
	JNZ	LED4F		;NO, DO NEXT BYTE
	CALL	AOUT		;YES, PRINT THE LINE
	POP	B		;GET BACK B,C
	MVI	B,0		;RESET FLAG
	PUSH	B		;SAVE B,C AGAIN
	LXI	H,2020H		;TWO SPACES
	SHLD	OBUF		;BLANK ADDR FIELD BYTES 1 & 2
	SHLD	OBUF+2		;3RD & 4TH BYTES
	MVI	A,0DH		;GET C/R
	STA	OBUF+16		;LINE ENDS WITH OBJECT CODE
	LXI	H,OBUF+3	;LAST BYTE OF ADDR FIELD
	SHLD	OIND		;OUTPUT POINTER FOR NXT LINE
LED4F:	LHLD	PNTR		;GET STRING POINTER
	POP	B		;RESTORE B,C
	INX	H		;INCREMENT STRING POINTER
	JMP	LED15		;DO NEXT BYTE OF STRING
;
;GET HERE WHEN HAVE REACHED 2ND DELIMITER OR C/R
;
LED57:	MOV	A,B		;GET 1ST LINE FLAG TO A
	ORA	A		;SET FLAGS
	RM			;ON 1ST LINE, DONE
	JZ	LEAF6		;FOR S.T./XREF, MAIN LOOP
	LHLD	OIND		;GET OUTPUT LINE POINTER
	INX	H		;POINT PAST LAST BYTE
	MVI	M,0DH		;TERMINATE LINE
	RET			;DONE
;
;PROCESS STAX AND LDAX INST.
;
TYP2:	CALL	ASBL		;FETCH OPERAND
	CNZ	ERRR		;ILLEGAL REGISTER
	MOV	A,L		;GET LOW ORDER OPERAND
	ORA	A		;SET FLAGS
	JZ	TY31		;OPERAND = 0
	CPI	2		;OPERAND = 2
	CNZ	ERRR		;ILLEGAL REGISTER
	JMP	TY31
;
;PROCESS PUSH, POP, INX, DCX, DAD INSTRUCTIONS
;
TYP3:	CALL	ASBL		;FETCH OPERAND
	CNZ	ERRR		;ILLEGAL REGISTER
	MOV	A,L		;GET LOW ORDER OPERAND
	RRC			;CHECK LOW ORDER BIT
	CC	ERRR		;ILLEGAL REGISTER
	RAL			;RESTORE
	CPI	8		;
	CNC	ERRR		;ILLEGAL REGISTER
TY31:	RLC			;SHIFT LEFT 3 TIMES
	RAL			;
	RAL			;
TY32:	MOV	B,A		;SAVE REGISTER
	LDAX	D		;FETCH OPCODE BASE
	ADD	B		;FORM OPCODE W/REGISTER
	CPI	76H		;CHECK FOR MOV M,M
	CZ	ERRR		;ILLEGAL REGISTER
	JMP	ASTO		;PUT IN MEM & INR P.C.
;
;PROCESS ACCUMULATOR, INR, DCR, MOV, RST INSTRUCTIONS
;
TYP4:	CALL	ASBL		;FETCH OPERAND
	CNZ	ERRR		;ILLEGAL REGISTER
	MOV	A,L		;GET LOW ORDER OPERAND
	CPI	8		;
	CNC	ERRR		;ILLEGAL REGISTER
	LDAX	D		;FETCH OPCODE BASE
	CPI	40H		;CHECK FOR MOV INST.
	JZ	TY41		;PROCESS THEM SEPARATELY
	CPI	0C7H		;
	MOV	A,L		;OPERAND IN ACCUM.
	JZ	TY31		;RST INSTRUCTION
	JM	TY32		;ACCUMULATOR INST.
	JMP	TY31		;INR, DCR INSTRUCTIONS
;
;PROCESS MOV INSTRUCTIONS
;
TY41:	DAD	H		;MULT OPRND BY 8 (3 LFT SHIFTS)
	DAD	H		;
	DAD	H		;
	ADD	L		;FORM OPCODE
	STAX	D		;SAVE OPCODE
	CALL	MPNT		;
	CALL	ASCN		;
	CNZ	ERRR		;
	MOV	A,L		;FETCH LOW ORDER OPERAND
	CPI	8		;
	CNC	ERRR		;ILLEGAL REGISTERS
	JMP	TY32		;
;
;PROCESS IMMEDIATE INSTRUCTIONS
;IMMEDIATE BYTE CAN BE -256 TO +255
;MVI INSTRUCTION IS A SPECIAL CASE AND HAS 2 OPERANDS
;
TYP5:	CPI	6		;CHECK FOR MVI INST
	CZ	TY56		;SPECIAL CASE
	CALL	ASTO		;PUT OBJ. IN MEM & ON LISTING
TYS5:	CALL	ASBL		;GET IMMEDIATE ARG.
	INR	A		;
	CPI	2		;CHECK OPERAND FOR RANGE
	CNC	ERRV		;OPERAND OUT OF RANGE
	MOV	A,L		;
	JMP	ASTO		;PUT OBJECT IN MEM & ON LISTING
;
;FETCH 1ST ARGUMENT FOR MVI AND LXI INSTRUCTIONS
;
TY56:	CALL	ASBL		;FETCH ARGUMENT
	CNZ	ERRR		;ILLEGAL REGISTER
	MOV	A,L		;GET LOW ORDER ARGUMENT
	CPI	8		;
	CNC	ERRR		;ILLEGAL REGISTER
	DAD	H		;MULT BY 8 (3 LEFT SHIFTS)
	DAD	H		;
	DAD	H		;
	LDAX	D		;FETCH OPCODE BASE
	ADD	L		;FORM OPCODE
	MOV	E,A		;SAVE OBJECT BYTE
MPNT:	LHLD	PNTR		;FETCH POINTER
	MOV	A,M		;FETCH CHARACTER
	CPI	','		;CHECK FOR COMMA
	INX	H		;INCREMENT POINTER
	SHLD	PNTR		;AND SAVE IT
	JNZ	ERRS		;SYNTAX ERROR IF NO COMMA
	MOV	A,E		;
	RET			;
;
;PROCESS 3 BYTE INSTRUCTIONS
;LXI INSTRUCTION IS A SPECIAL CASE
;
TYP6:	CPI	1		;CHECK FOR LXI INSTRUCTION
	JNZ	TY6		;JUMP IF NOT LXI
	CALL	TY56		;GET REGISTER
	ANI	8		;CHECK FOR ILLEGAL REGISTER
	CNZ	ERRR		;REGISTER ERROR
	MOV	A,E		;GET OPCODE
	ANI	0F7H		;CLEAR BIT IN ERROR
TY6:	CALL	ASTO		;STORE OBJECT BYTE
TYS6:	CALL	ASBL		;FETCH OPERAND
	MOV	A,L		;TO ACCUM
	MOV	D,H		;
	CALL	ASTO		;PUT 2ND BYTE IN MEMORY
	MOV	A,D		;
;
;THIS ROUTINE IS USED TO STORE OBJECT CODE PRODUCED BY
;THE ASSEMBLER DURING THE 2ND PASS INTO MEMORY
;
ASTO:	LHLD	BBUF+2		;FETCH STORAGE COUNTER
	MOV	B,A		;SAVE OBJECT BYTE
	LDA	PASI		;GET PASS INDICATOR
	CPI	2		;3RD PASS ? (S.T./XREF)
	RNC			;SKIP OBJECT STORAGE THEN
	MOV	A,B		;ELSE RESTORE OBJECT BYTE
	MOV	M,A		;AND PUT IT IN MEMORY
	INX	H		;INCREMENT STORAGE POINTER
	SHLD	BBUF+2		;AND PUT IT BACK
	LHLD	OIND		;GET OUTPUT ADDRESS
	INX	H		;INCR. TO POINT PAST LAST BYTE
	INX	H		;AGAIN, PUT SPACE BETWEEN BYTES
	CALL	BINH+3		;CONVERT OBJECT BYTE TO ASCII
	SHLD	OIND		;AND SAVE UPDATED POINTER
	RET			;OBJECT IN MEM & ON LISTING
;
;PROCESS THE END PSEUDO OP
;
LEE39:	LXI	H,PASI		;POINT TO THE PASS INDICATOR
	MOV	A,M		;GET IT
	CPI	1		;JUST FINISHED 2ND PASS ?
	JZ	LEE4C		;YES - DONE OR START 3RD PASS
	JNC	LEE9D		;IF 3RD PASS COMPLETE
	CALL	CRLF		;DO CRLF AT START OF EA. PASS
	INR	M		;INCREMENT PASS COUNTER
	JMP	LEAEA		;START NEXT PASS
;
;GET HERE AT END OF 2ND PASS
;
LEE4C:	LDA	SYMX		;GET S.T./XREF FLAG
	CPI	'E'+1		;'S' OR 'X' OPTION REQUESTED ?
	JC	EORMS		;NO - ASSY DONE, BACK TO ALS8
	INR	M		;YES - INCREMENT PASS IND.
	CALL	CRLF		;CRLF AFTER EA. PASS
LEE58:	CALL	CRLF		;AGAIN BEFORE S.T./XREF
	XRA	A		;GET A ZERO
LEE5C:	STA	SCNT		;SAVE AS SYMBOL COUNT
	LHLD	SYMADD		;GET ADDR OF SYMBOL TABLE
	MOV	D,H		;DUPLICATE IN D,E
	MOV	E,L		;
;
;TEST NAMES POINTED TO BY D,E AND H,L
;LEAVE NAME WHICH COMES 1ST IN ALPHABETICAL ORDER
;POINTED TO BY D,E
;
LEE64:	LXI	B,LLAB-1	;OFFSET TO LAST CHAR OF NAME
	MOV	A,M		;GET 1ST CHAR OF NAME
	ORA	A		;ZERO ?
	JZ	LEE82		;REACHED END OF SYMBOL TABLE
	DAD	B		;POINT TO LAST CHAR
	XCHG			;
	DAD	B		;POINT TO LAST CHAR
	XCHG			;
	INR	C		;C = LENGTH OF NAME
	CALL	COM1		;COMPARE NAMES
	INX	H		;POINT BACK AT 1ST CHAR
	INX	D		;POINT BACK AT 1ST CHAR
	LXI	B,LLAB+2	;LENGTH OF TABLE ENTRY
	JC	LEE7E		;ALREADY IN CORRECT ORDER
	MOV	D,H		;MAKE D,E POINT TO "1ST" NAME
	MOV	E,L		;
LEE7E:	DAD	B		;H,L POINT TO NEXT NAME
	JMP	LEE64		;TST NAMES POINTED TO BY DE, HL
;
;GET HERE WHEN PASS COMPLETED THRU SYMBOL TABLE
;D,E POINT TO NAME WHICH COMES "1ST" IN ALPH. ORDER
;NOTE THAT THE SYMBOLS ARE NOT MOVED IN THE TABLE, BUT
;THAT THEIR 1ST CHAR IS REPLACED WITH 0FFH AS THE ARE PRINTED
;
LEE82:	XCHG			;"1ST" NAME TO D,E
	MOV	A,M		;GET 1ST CHAR OF "LOWEST" NAME
	DCR	A		;IS IT 0FFH ? (IT WASN'T ZERO)
	JM	EOR		;IF SO, RETURN TO ALS-8
	INR	A		;ELSE RESTORE
	SHLD	SYMSV		;AND SAVE IT'S ADDRESS
	CALL	LEEA8		;PRINT IT AND IT'S VALUE
	LDA	SYMX		;GET THE S.T./XREF FLAG
	CPI	'X'		;XREF REQUESTED ?
	JZ	LEAEA		;DO THE CROSS REFERENCE SEARCH
	LDA	SCNT		;ELSE GET SYMBOL COUNT
	INR	A		;INCREMENT IT
	CPI	4		;MAX NO. SYMBOLS PRINTED/LINE
LEE9D:	LHLD	SYMSV		;GET SYMBOL POINTER
	MVI	M,0FFH		;MAKE SYMBOL LAST
	JC	LEE5C		;DO MORE SYMBOLS THIS LINE
	JMP	LEE58		;TO START A NEW LINE
;
;THIS ROUTINE PRINTS NAMES AND THEIR VALUES.
;IT IS USED FOR BOTH THE S.T./XREF AND FOR THE FILES/IODR
;COMMANDS TO LIST THE DIRECTORY CONTENTS
;
LEEA8:	MVI	C,NMLEN		;CAREFUL IF LLAB <> NMLEN !
LEEAA:	MOV	B,A		;SAVE CHAR IN B
	ORA	A		;SET FLAGS
	JNZ	LEEB1		;IF CHAR IS NOT HEX 00
	MVI	B,' '		;PRINT SPACE TO PAD SHORT NAMES
LEEB1:	CALL	OUT8		;PRINT IT
	INX	H		;INCREMENT POINTER
	MOV	A,M		;GET CHAR TO PRINT
	DCR	C		;DCR COUNT OF CHARS LEFT
	JNZ	LEEAA		;DO REMAINING CHARS
	MVI	C,3		;NO OF SPACES TO FOLLOW NAME
	CALL	BLKO		;PRINT THEM
	MOV	D,M		;GET SYMBOL VALUE IN D,E
	INX	H		;2ND BYTE
	MOV	E,M		;IN D,E
	PUSH	H		;SAVE POINTER
	CALL	ADOUT		;PRINT VALUE
	POP	D		;RESTORE POINTER TO D,E
	MVI	C,5		;NO OF SPACES AFTER VALUE
	JMP	BLKO		;PRINT THEM & RETURN
;
;ROUTINE TO SCAN THROUGH SPACES UNTIL NON-BLANK CHAR FOUND
;
SBLK:	LHLD	PNTR		;GET POINTER
SBL1:	MOV	A,M		;GET CHAR AT POINTER
	CPI	' '		;SPACE ?
	RNZ			;NO - DONE
SBL2:	INX	H		;YES - INCREMENT POINTER
	SHLD	PNTR		;AND UPDATE IT
	JMP	SBL1		;THEN CHECK NEW CHAR
;
;THIS ROUTINE IS USED TO CHECK THE CONDITION CODE
;MNEMONICS FOR CONDITIONAL JUMPS, CALLS AND RETURNS.
;
COND:	LXI	H,ABUF+1	;POINT TO 2ND CHAR
	SHLD	ADDS		;SAVE IT'S ADDR.
	MVI	B,2		;MAX. LENGTH OF CONDITION
	JMP	COPC		;TO LOOKUP IN TABLE
;
;HERE IS THE ASSEMBLER OPCODE TABLE
;
;PSEUDO OPS WITH LENGTH OF 4 (OR PADDED TO 4 WITH HEX 0'S)
;COME FIRST
;
OTAB:	DB	'ORG'
	DW	0
	DB	'EQU'
	DW	0100H
	DB	'DB'
	DW	0
	DB	0FFH
	DB	'DS'
	DW	0
	DB	2
	DB	'DW'
	DW	0
	DB	3
	DB	'END'
	DW	0400H
	DB	'COM'
	DW	0500H
	DB	'LST'
	DW	0600H
	DB	'NLST'
	DB	7
	DB	'ASC'
	DW	0800H
	DB	0
;
;THESE ARE FOLLOWED BY 3 CHAR OPCODES (OR 2 CHAR PADDED TO 3)
;
	DB	'HLT'
	DB	118
	DB	'RLC'
	DB	7
	DB	'RRC'
	DB	15
	DB	'RAL'
	DB	23
	DB	'RAR'
	DB	31
	DB	'RET'
	DB	201
	DB	'CMA'
	DB	47
	DB	'STC'
	DB	55
	DB	'DAA'
	DB	39
	DB	'CMC'
	DB	63
	DB	'EI'
	DW	0FB00H
	DB	'DI'
	DW	0F300H
	DB	'NOP'
	DW	0
	DB	'XCHG'
	DB	235
	DB	'XTHL'
	DB	227
	DB	'SPHL'
	DB	249
	DB	'PCHL'
	DW	00E9H
	DB	'STAX'
	DB	2
	DB	'LDAX'
	DW	000AH
	DB	'PUSH'
	DB	197
	DB	'POP'
	DW	0C100H
	DB	'INX'
	DW	0300H
	DB	'DCX'
	DW	0B00H
	DB	'DAD'
	DW	0900H
	DB	0
	DB	'INR'
	DB	4
	DB	'DCR'
	DB	5
	DB	'MOV'
	DB	64
	DB	'ADD'
	DB	128
	DB	'ADC'
	DB	136
	DB	'SUB'
	DB	144
	DB	'SBB'
	DB	152
	DB	'ANA'
	DB	160
	DB	'XRA'
	DB	168
	DB	'ORA'
	DB	176
	DB	'CMP'
	DB	184
	DB	'RST'
	DW	00C7H
	DB	'ADI'
	DB	198
	DB	'ACI'
	DB	206
	DB	'SUI'
	DB	214
	DB	'SBI'
	DB	222
	DB	'ANI'
	DB	230
	DB	'XRI'
	DB	238
	DB	'ORI'
	DB	246
	DB	'CPI'
	DB	254
	DB	'IN'
	DW	0DB00H
	DB	'OUT'
	DB	211
	DB	'MVI'
	DW	0006H
;
;WHICH ARE FOLLOWED BY 4 CHAR OPCODES (OR LESS PADDED TO 4)
;
	DB	'JMP'
	DW	0C300H
	DB	'CALL'
	DB	205
	DB	'LXI'
	DW	0100H
	DB	'LDA'
	DW	3A00H
	DB	'STA'
	DW	3200H
	DB	'SHLD'
	DB	34
	DB	'LHLD'
	DW	002AH
;
;WHICH IS FINALLY FOLLOWED BY THE 2 CHAR CNDX CODE TABLE
;
	DB	'NZ'
	DB	0
	DB	'Z'
	DW	0800H
	DB	'NC'
	DB	16
	DB	'C'
	DW	1800H
	DB	'PO'
	DB	32
	DB	'PE'
	DB	40
	DB	'P'
	DW	3000H
	DB	'M'
	DW	3800H
	DB	0
;
;THIS ROUTINE IS USED TO CHECK A GIVEN OPCODE
;AGAINST THE LEGAL OPCODES IN THE OPCODE TABLE
;
COPC:	LHLD	ADDS		;GET POINTER
	LDAX	D		;FETCH CHARACTER
	ORA	A		;SET FLAGS
	JZ	COP1		;END OF TABLE
	MOV	C,B		;LENGTH OF STRING
	CALL	SEAR		;SEARCH THE TABLE
	LDAX	D		;
	RZ			;RETURN IF MATCH
	INX	D		;NEXT STRING
	JMP	COPC		;CONTINUE SEARCH
COP1:	INR	A		;CLEAR ZERO FLAG
	INX	D		;INCREMENT ADDRESS
	RET			;NO MATCH
;
;THIS ROUTINE CHECKS THE LEGAL OPCODES IN BOTH PASS 1
;AND PASS 2.  IN PASS 1 THE PROGRAM COUNTER IS INCREMENTED
;BY THE CORRECT NUMBER OF BYTES. AN ADDRESS IS
;ALSO SET SO THAT AN INDEXED JUMP CAN BE MADE TO
;PROCESS THE OPCODE FOR PASS 2.
;
OPCD:	LXI	H,ABUF		;GET ADDRESS
	SHLD	ADDS		;SAVE IT
	LXI	D,OTAB		;OPCODE TABLE ADDR.
	MVI	B,4		;LENGTH OF PSEUDO-OP ENTRIES
	CALL	COPC		;LOOK IT UP IN TABLE
	JZ	PSEU		;PSEUDO-OP IF FOUND
	DCR	B		;ELSE TRY 3-CHAR OPCODES
	CALL	COPC		;LOOK IT UP
	JZ	OP1		;IF FOUND IN THIS GROUP
	INR	B		;THEN TRY FOUR CHAR OPCODES
	CALL	COPC		;LOOK IT UP
OP1:	LXI	H,ASTO		;TYPE 1 INSTRUCTIONS
OP2:	MVI	C,1		;1 BYTE INST.
	JZ	OCNT		;IF FOUND
;
OPC2:	CALL	COPC		;CHECK FOR STAX, LDAX
	LXI	H,TYP2		;
	JZ	OP2		;
	CALL	COPC		;CHK FOR PUSH, POP, INX, DCX, DAD
;
	LXI	H,TYP3		;
	JZ	OP2		;
	DCR	B		;3 CHAR OPCODES
	CALL	COPC		;ACC INST: INR, DCR, MOV, RST
;
	LXI	H,TYP4		;
	JZ	OP2		;
;
OPC3:	CALL	COPC		;IMMEDIATE INSTRUCTIONS
	LXI	H,TYP5
	MVI	C,2		;2 BYTE INSTRUCTIONS
	JZ	OCNT		;
	INR	B		;4 CHAR OPCODES
	CALL	COPC		;JMP, CALL, LXI, LDA, STA,
;				;LHLD, SHLD
	JZ	OP4		;
	CALL	COND		;CONDITIONAL INST.
	JNZ	OERR		;ILLEGAL OPCODE
	ADI	192		;ADD BASE VALUE TO RETURN
	MOV	D,A		;
	MVI	B,3		;3 CHARACTER OPCODES
	LDA	ABUF		;FETCH 1ST CHAR
	MOV	C,A		;SAVE IT
	CPI	'R'		;CONDITIONAL RETURN
	MOV	A,D		;
	JZ	OP1		;
	MOV	A,C		;
	INR	D		;FORM CONDITIONAL JUMP
	INR	D		;
	CPI	'J'		;CONDITIONAL JUMP
	JZ	OPAD		;
	CPI	'C'		;CONDITIONAL CALL
	JNZ	OERR		;ILLEGAL OPCODE
	INR	D		;FORM CONDITIONAL CALL
	INR	D		;
OPAD:	MOV	A,D		;GET OPCODE
OP4:	LXI	H,TYP6		;
OP5:	MVI	C,3		;3 BYTE INSTRUCTIONS
OCNT:	STA	TEMP		;SAVE OPCODE
;
;CHECK FOR OPCODE ONLY CONTAINING CORRECT NO. OF CHARS.
;THUS ADDQ, SAY, WOULD GIVE AN ERROR
;
	MVI	A,ABUF AND 0FFH	;LOAD BUFFER ADDRESS
	ADD	B		;LENGTH OF OPCODE
	MOV	E,A		;
	MVI	A,ABUF/256	;
	ACI	0		;GET HIGH ORDER ADDRESS
	MOV	D,A		;
	LDAX	D		;FETCH CHAR AFTER OPCODE
	ORA	A		;IT SHOULD BE ZERO
	JNZ	OERR		;OPCODE ERROR
	LDA	PASI		;FETCH PASS INDICATOR
OCN1:	MVI	B,0		;
	XCHG			;
OCN2:	LHLD	ASPC		;FETCH PROGRAM COUNTER
	DAD	B		;ADD IN BYTE COUNT
	SHLD	ASPC		;STORE PC
	ORA	A		;WHICH PASS
	RZ			;RETURN IF PASS 1
	LDA	TEMP		;FETCH OPCODE
	XCHG			;
	PCHL			;
;
OERR:	LXI	H,ERRO		;SET ERROR ADDRESS
LF0DB:	MVI	C,3		;LEAVE 3 BYTES FOR PATCH
	JMP	OCN1-3		;
;
LF0E0	LXI	H,ERRL
	JMP	LF0DB
;
PSEU:	LXI	H,ABUF+4	;SET BUFFER ADDRESS
	MOV	A,M		;FETCH CHAR AFTER OPCODE
	ORA	A		;IT SHOULD BE ZERO
	JNZ	OERR		;IF NOT
	LDA	PASI		;GET PASS INDICATOR
	ORA	A		;PASS 1 ?
	JZ	PSU1		;YES - JUMP
	JMP	PSU2		;NO - DO 2ND PASS OPERATIONS
;
;THIS ROUTINE IS USED TO PROCESS LABELS.
;IT CHECKS TO SEE IF A LABEL IS IN THE SYMBOL TABLE
;OR NOT.  ON RETURN, Z=1 INDICATES A MATCH WAS FOUND
;AND H,L CONTAIN THE VALUE ASSOCIATED WITH THE LABEL.
;THE REGISTER NAMES A, B, C, D, E, H, L, AND M ARE
;PRE-DEFINED AND NEED NOT BE ENTERED BY THE USER.
;ON RETURN, C=1 INDICATES A LABEL ERROR.
;
SLAB:	CPI	'A'		;CHECK FOR LEGAL CHAR
	RC			;ILLEGAL CHAR
	CPI	'Z'+1		;CHECK FOR LEAGAL CHAR
	CMC			;SETTING CARRY FOR PROPER RET
	RC			;ILLEGAL CHAR
	CALL	ALPS		;PLACE SYMBOL IN ABUF
	LXI	H,ABUF		;POINT TO SYMBOL
	LDA	PASI		;GET PASS INDICATOR
	CPI	2		;DOING XREF/S.T. PASS ?
	INR	A		;BUMP PASS INDICATOR IN ACCUM.
	RNC			;RETURN IF ON XREF/S.T. PASS
	SHLD	ADDS		;SAVE ADDRESS OF SYMBOL
	DCR	B		;CHECK IF ONLY 1 CHAR SYMBOL
	JNZ	SLA1		;LOOK THEM UP IN REG. TABLE
	INR	B		;SET B=1
	LXI	D,RTAB		;POINT TO REGISTER TABLE
	CALL	COPC		;LOOK FOR MATCH
	JNZ	SLA1		;NOT FOUND, TRY SYMBOL TABLE
	MOV	L,A		;FOUND, VALUE TO L
	MVI	H,0		;AND MAKE H ZERO
	STC			;SET CARRY
	CMC			;COMPLEMENT CARRY
	RET			;DONE
SLA1:	LHLD	SYMADD		;GET SYMBOL TABLE ADDR
	XCHG			;TO D,E
	MVI	B,LLAB		;LENGTH OF LABELS
	CALL	COMS		;CHECK TABLE
	RET			;WITH FLAGS SET
;
;HERE IS THE INTERNAL REGISTER TABLE
;
RTAB:	DB	'A'
	DB	7
	DB	'B'
	DB	0
	DB	'C'
	DB	1
	DB	'D'
	DB	2
	DB	'E'
	DB	3
	DB	'H'
	DB	4
	DB	'L'
	DB	5
	DB	'M'
	DB	6
	DB	0
;
;THIS ROUTINE SCANS THE INPUT LINE AND PLACES THE
;OPCODES AND LABELS IN THE ABUF BUFFER.  THE SCAN TERMINATES
;WHEN A NON ALPHANUMERIC CHARACTER (A-Z, 0-9) IS FOUND.
;
ALPS:	MVI	B,0		;SET COUNT
ALP1:	STAX	D		;STORE CHAR IN BUFFER
	INR	B		;INCREMENT COUNT
	MOV	A,B		;FETCH COUNT
	CPI	15		;MAX BUFFER SIZE
	RNC			;RETURN IF BUFFER FILLED
	INX	D		;INCREMENT ABUF POINTER
	INX	H		;INCREMENT INPUT LINE POINTER
	SHLD	PNTR		;SAVE INPUT POINTER
	MOV	A,M		;FETCH CHARACTER
	CPI	'0'		;CHECK FOR LEGAL CHAR
	RC			;ILLEGAL CHAR
	CPI	'9'+1		;AGAIN
	JC	ALP1		;NUMERIC = O.K.
LF154:	CPI	'A'		;TEST FOR LETTER
	RC			;ILLEGAL CHAR
	CPI	'Z'+1		;
	JC	ALP1		;VALID LETTER
	RET			;W/CARRY SET, ILLEGAL CHAR
;
;THIS ROUTINE IS USED TO SCAN THE INPUT LINE AND DETERMINE
;THE VALUE OF AN OPERAND FIELD.  ON RETURN, VALUE IS IN H,L.
;
ASBL:	CALL	SBLK		;GET THE FIRST ARGUMENT
ASCN:	LHLD	PNTR		;FETCH SCAN POINTER
	MOV	A,M		;FETCH CHARACTER
	LXI	H,0		;GET A ZERO
	SHLD	OPRD		;INITIALIZE OPERAND
	INR	L		;L = 1 ==> BECOMES OPRI
	MOV	H,A		;CHAR TO H, BECOMES GTLT
	SHLD	OPRI		;SAVE OPRI AND GTLT
NXT1:	LHLD	PNTR		;GET SCAN POINTER
	DCX	H		;INIT. FOR LOOP BELOW
	CALL	ZBUF		;CLEAR OUT ABUFF
	STA	SIGN		;ZERO SIGN INDICATOR
NXT2:	INX	H		;INCREMENT POINTER
	MOV	A,M		;GET CHAR
	CPI	'<'		;HIGH ORDER BYTE SIGN ?
	JZ	NXT2		;ALREADY TAKEN CARE OF (GTLT)
	CPI	'>'		;LOW ORDER BYTE SIGN ?
	JZ	NXT2		;ALREADY TAKEN CARE OF (GTLT)
	CPI	' '+1		;CAR. RETURN OR SPACE ?
	JC	SEND		;END OF OPERAND FIELD THEN
	CPI	','		;FIELD SEPARATOR ?
	JZ	SEND		;ALSO END OF FIELD
;
;CHECK FOR OPERATORS
;
	CPI	'+'		;CHECK FOR PLUS
	JZ	LF1A6		;STORE 0 AS SIGN
	INR	B		;MAKE B 01
	CPI	'-'		;MINUS SIGN ?
	JZ	LF1A6		;SAVE NON-ZERO SIGN
	INR	B		;MAKE B 02
	CPI	'*'		;CHECK FOR MULTIPLY
	JZ	LF1A6		;STORE B AS 02
	INR	B		;MAKE B 03
	CPI	'/'		;CHECK FOR DIVIDE
	JNZ	ASC2		;IF NOT SIGN, CHK FOR OPERAND
LF1A6:	MOV	A,B		;MOVE B TO A
	STA	SIGN		;SET SIGN BYTE
ASC1:	LDA	OPRI		;FETCH OPERAND INDICATOR
	CPI	2		;CHECK FOR 2 OPERATORS TOGETHER
	JZ	ERRS		;SYNTAX ERROR
	MVI	A,2		;NOW SET UP OPRI AS 2
	STA	OPRI		;AND STORE IT
	JMP	NXT2		;ANOTHER WILL NOT BE ALLOWED
;
;CHECK FOR OPERANDS
;
ASC2:	MOV	C,A		;SAVE CHAR
	LDA	OPRI		;GET OPERATOR
	ORA	A		;CHECK FOR TWO OPERANDS
	JZ	ERRS		;TWO IN A ROW NOT ALLOWED
	MOV	A,C		;RESTORE CHAR
	CPI	'$'		;PGM. COUNTER ?
	JZ	LECC3		;GET IT & CONTINUE
;
;CHECK FOR ASCII CHARACTERS
;
ASC3:	CPI	27H		;QUOTE MARK ?
	JNZ	ASC5		;NO - MUST BE SOMETHING ELSE
	LXI	D,0		;INITIALIZE CHAR. VALUE
	MVI	C,3		;MAX CHAR COUNT + 1
ASC4:	INX	H		;POINT TO NEXT CHAR
	SHLD	PNTR		;SAVE POINTER
	MOV	A,M		;GET CHAR
	CPI	0DH		;CARRIAGE RETURN ?
	JZ	ERRA		;ARGUMENT ERROR THEN
	CPI	27H		;2ND QUOTE MARK ?
	JNZ	SSTR		;NO - PROCESS CHAR
	INX	H		;POINT PAST 2ND QUOTE
	SHLD	PNTR		;UPDATE POINTER
	MOV	A,M		;FETCH NEXT CHAR
	CPI	27H		;CHECK FOR TWO IN A ROW
	JNZ	AVAL+1		;WAS TERMINAL QUOTE
SSTR:	DCR	C		;CHECK COUNT
	JZ	ERRA		;TOO MANY CHARS
	MOV	D,E		;1ST CHAR (IF ANY) IN D
	MOV	E,A		;THIS CHAR IN E
	JMP	ASC4		;GET NEXT CHAR
ASC5:	CPI	'0'		;SEE IF NUMERIC
	JC	ERRA		;NO CHARS < 30H LEGAL
	CPI	'9'+1		;TRY HIGH END
	JNC	ALAB		;ALPHABETIC - TRY LABEL
	CALL	NUMS		;NUMERIC - CONVERT
	JC	ERRA		;ARGUMENT ERROR
AVAL:	XCHG			;OPERAND VALUE IN H,L
	LHLD	OPRD		;FETCH OPERAND VALUE
	XRA	A		;GET A ZERO
	STA	OPRI		;AND CLEAR OPERAND INDICATOR
	LDA	SIGN		;GET SIGN
	CPI	1		;MINUS SIGN ?
	JZ	ASUB		;THEN DO A SUBTACTION
	JNC	LF227		;MULTIPLY OR DIVIDE
	DAD	D		;ADD NEW OPERAND DE TO OLD HL
ASC7:	SHLD	OPRD		;AND UPDATE THE RESULT
	JMP	NXT1		;NOW SEE IF MORE
ASUB:	MOV	A,L		;GET OLD OPRAND IN H,L
	SUB	E		;SUBTRACT NEW OPRAND IN D,E
	MOV	L,A		;RESTORE RESULT TO H,L
	MOV	A,H		;NOW DO HIGH ORDER BYTE
	SBB	D		;
	MOV	H,A		;PUT IT BACK
	JMP	ASC7		;AND FINISH UP
;
;DO MULTIPLY DIVIDE
;
LF227:	MOV	B,D		;MOVE CURRENT OPRD IN DE TO BC
	MOV	C,E		;
	XCHG			;AND OLD OPRND TO D,E
	LXI	H,0		;INITIALIZE ANSWER
	CPI	3		;DOING DIVISION ?
	JZ	LF247		;YES
	CALL	LF238		;NO, MULITPLICATION - DO IT
	JMP	ASC7		;THEN FINISH UP
;
;MULTIPLICATION ROUTINE
;
;THIS ALGORITHM IS INTERESTING BUT DIFFICULT TO UNDERSTAND
;UNTIL STUDIED FOR A WHILE. BUT IT DOES WORK
;
LF238:	MVI	A,16		;16 BITS
LF23A:	DAD	H		;MULTIPLY PARTIAL ANS. BY 2
	XCHG			;CHANGE REGISTERS
	DAD	H		;MULT. MULTIPLIER BY 2
	XCHG			;RESTORE ORIG. REGISTERS
	JNC	LF242		;IF 2X MULTIPLIER ==> NO CARRY
	DAD	B		;ADD MULTIPLICAND TO INTER ANS
LF242:	DCR	A		;DCR NO OF BITS LEFT TO CHECK
	JNZ	LF23A		;DO IT OVER IF NOT DONE
	RET			;ELSE FINAL ANS. IS IN H,L
;
;DIVISION ROUTINE
;
;FIRST CHECK FOR DIVIDE BY ZERO
;
LF247:	MOV	A,B		;GET BYTE OF DIVISOR
	ORA	C		;OR IT WITH OTHER BYTE
	JZ	ASC7		;CAN'T DIVIDE BY ZERO
	CALL	LF253		;DO ACTUALLY DO THE DIVISION
	XCHG			;ANSWER TO H,L
	JMP	ASC7		;TO FINISH UP
;
;DIVISION ROUTINE FOLLOWS. IT IS SIMILAR IN PRINCIPLE TO
;THE MULTIPLICATION ROUTINE
;
LF253:	MVI	A,16		;16 BITS
LF255:	STA	SCNT		;SAVE AS COUNT
	DAD	H		;SHIFT LEFT
	XCHG			;SWITCH REGISTERS
	DAD	H		;SHIFT LEFT
	XCHG			;RESTORE REGISTERS
	JNC	LF260		;IF NO CARRY
	INX	H		;ELSE INCREMENT H,L
LF260:	MOV	A,L		;AND SUBTRACT DIVISOR FROM IT
	SUB	C		;
	MOV	L,A		;
	MOV	A,H		;
	SBB	B		;
	MOV	H,A		;
	INX	D		;INCREMENT PARTIAL ANSWER
	JNC	LF26C		;OK IF NO CARRY
	DAD	B		;ADD DIVISOR TO H,L
	DCX	D		;AND DECREMENT ANSWER
LF26C:	LDA	SCNT		;GET COUNT
	DCR	A		;DECREMENT IT
	JNZ	LF255		;IF NOT DONE, DO MORE
	RET			;ELSE DONE, ANS IN D,E
;
;CHECK FOR SYMBOLS FROM SYMBOL TABLE
;
ALAB:	CALL	SLAB		;SEE IF LABEL IN TABLE
	JZ	AVAL		;IF FOUND
	JC	ERRA		;IF LABEL ERROR
	LDA	PASI		;NOT FOUND, GET PASS IND.
	CPI	2		;DOING S.T./XREF PASS ?
	JNC	LF291		;YES - PROCESS
	LXI	D,SYSYM		;ELSE TRY SYSTEM SYMBOL TABLE
	CALL	COMS		;SEARCH IT
	JZ	AVAL		;IF FOUND
	JMP	ERRU		;ELSE UNDEFINED SYMBOL ERROR
;
;THE FOLLOWING ROUTINE IS USED TO HANDLE SYMBOLS DURING A
;SYMBOL TABLE/XREF PASS (3RD PASS, PASI = 2).
;DOES CROSS REFERENCE
;
LF291:	XCHG			;D,E ==> SYMBOL NAME IN ABUF
	LHLD	SYMSV		;SYMBOL DOING XREF PASS FOR
	MVI	C,LLAB		;LENGTH OF SYMBOL
	CALL	SEAR		;SAME ?
	JNZ	LF2C2		;NO
	LXI	H,SCNT		;POINT TO SYMBOL COUNT
	INR	M		;INCREMENT IT
	MOV	A,M		;GET IT
	CPI	12		;MAX. NO ON ONE LINE ?
	JC	LF2B1		;NO - DO MORE THIS LINE
	MVI	M,1		;YES - RESET COUNT
	CALL	CRLF		;DO CAR RET/LINE FEED
	MVI	C,17		;AND PRINT 17 SPACES
	CALL	BLKO		;DO IT
LF2B1:	LXI	H,IBUF-5	;POINT TO LINE NO.
	MVI	C,4		;LENGTH OF LINE NO
LF2B6:	MOV	B,M		;GET A CHAR
	CALL	OUT8		;PRINT IT
	INX	H		;POINT TO NEXT CHAR
	DCR	C		;DECR CHAR COUNT
	JNZ	LF2B6		;DO NEXT CHAR IF NOT DONE
	CALL	LE35C		;PUT OUT A SPACE AFTER LINE NO
LF2C2:	XRA	A		;CLEAR FLAGS
	MVI	H,0		;AND H,L
	JMP	AVAL		;AND CONTINUE SEARCH
;
;ROUTINE TO CHECK FOR TERMINATING CHARACTERS IN LABEL FILEDS
;
SEND:	LDA	OPRI		;FETCH OPERAND INDICATOR
	ORA	A		;SET FLAGS
	JNZ	ERRS		;SYNTAX ERROR
	LHLD	OPRD		;GET OPERAND VALUE
	LDA	GTLT		;GET SHIFT INDICATOR
	CPI	'<'		;USING HIGH HALF ONLY ?
	JNZ	LF2DE		;NO - OK AS IS
	MOV	L,H		;YES - MOVE HIGH HALF TO L
	JMP	LF2E3		;AND CLEAR HIGH HALF
LF2DE:	CPI	'>'		;USING LOW HALF ONLY ?
	JNZ	SEN1		;NO - OK AS IS THEN
LF2E3:	MVI	H,0		;ELSE CLEAR HIGH HALF
SEN1:	MOV	A,H		;GET HIGH ORDER BYTE
	LXI	D,TEMP		;GET ADDRESS
	ORA	A		;SET FLAGS
	RET			;DONE
;
;GET A NUMERIC VALUE, HEX, OCTAL, OR DECIMAL
;
NUMS:	CALL	ALPS		;PUT CHARS IN ABUFF
	DCX	D		;POINT TO LAST CHAR
	LDAX	D		;GET LAST CHAR
	LXI	B,ABUF		;B,C POINT TO NUMBER
	CPI	'H'		;HEX ?
	JZ	NUM2		;YES - PROCESS
	CPI	'Q'		;OCTAL ?
	JZ	NUM3		;YES - PROCESS
	CPI	'D'		;DECIMAL ?
	JNZ	ADEC		;PROCESS DECIMAL BY DEFAULT
NUM1:	XRA	A		;GET BINARY ZERO
	STAX	D		;CLEAR THE TYPE LETTER
	JMP	ADEC		;PROCESS DECIMAL
NUM2:	XRA	A		;GET A BINARY ZERO
	STAX	D		;CLEAR THE TRAILING H
	JMP	AHEX		;PROCESS HEX
NUM3:	XRA	A		;GET A BINARY ZERO
	STAX	D		;CLEAR THE TRAILING Q
	JMP	AOCT		;PROCESS OCTAL
;
;FOLLOWING ARE THE ERROR REPORTING ROUTINES
;
ERRR:	MVI	A,'R'		;REGISTER ERROR
	LXI	H,0		;CLEAR H,L
	STA	OBUF+18		;WHERE THE ERROR LETTER GOES
	RET			;DONE
ERRS:	MVI	A,'S'		;SYNTAX ERROR
	STA	OBUF+18		;WHERE THE ERROR LETTER GOES
	LXI	H,0		;CLEAR H,L
	JMP	SEN1		;FINISH UP & GO HOME
ERRU:	MVI	A,'U'		;UNDEFINED SYMBOL
	JMP	ERRS+2
ERRV:	MVI	A,'V'		;VALUE ERROR
	JMP	ERRR+2
ERRM:	MVI	A,'M'		;MISSING LABEL
	STA	OBUF+18		;WHERE THE ERROR LETTER GOES
	JMP	LEB41		;FINISH STATEMENT PROCESSING
ERRA:	MVI	A,'A'		;ARGUMENT ERROR
	JMP	ERRS+2
ERRO:	MVI	A,'O'		;OPCODE ERROR
	STA	OBUF+18		;WHERE ERROR LETTER GOES
	LDA	PASI		;GET PASS INDICATOR
	ORA	A		;SET FLAGS
	RZ			;RET IF ON 1ST PASS
	MVI	C,3		;ELSE SET UP TO LEAVE 3 BYTES
ERO1:	XRA	A		;GET A BINARY ZERO
	CALL	ASTO		;PUT IT IN MEMORY & LISTING
	DCR	C		;DECR COUNT
	JNZ	ERO1		;DO MORE IF NOT DONE
	RET			;DONE - ROOM LEFT FOR PATCH
ERRL:	MVI	A,'L'		;LABEL ERROR
	JMP	ERRO+2
ERRD:	MVI	A,'D'		;DUPLICATE LABEL
	STA	OBUF+18		;WHERE ERROR LETTER GOES
	CALL	LEB41		;DISPLAY THE ERROR
	JMP	OPC		;PROCESS OPCODE
;
;FOLLOWING IS THE LINE FORMATING ROUTINE
;
;IT IS BEING LEFT UN-COMMENTED SO THAT YOU MAY TRY TO
;COMMENT IT YOURSELF & THEREFORE HAVE A BETTER APPRECIATION
;OF THE WORK I HAVE GONE TO - NOTE THAT IT IS LESS THAN TWO
;PAGES LONG, AND WE ARE NOW OVER 50 PAGES INTO THIS MONSTER.
;HAVE FUN.  (PS - I HAVE GONE TO THE TROUBLE OF PUTING
;IN SYMBOLIC REFERENCES TO MAKE THE CODE RE-ASSEMBLEABLE)
;
LF361:	MVI	C,0
	LXI	H,IBUF
	MOV	A,M
	CPI	COMCHR
	RZ
	MVI	B,2
	CALL	LF37A
	RC
	INR	B
	CALL	LF37A
	RC
	MVI	D,2
	JMP	LF37C
LF37A:	MVI	D,1
LF37C:	MOV	A,B
	ADD	D
	MOV	B,A
	LDA	TERMW
	SUI	(IBUF+81) AND 0FFH
	JZ	LF392
	JNC	LF38E
	DCR	B
	JMP	LF392
LF38E:	MOV	A,D
	RAL
	ADD	B
	MOV	B,A
LF392:	MOV	A,B
	ADI	5
	MOV	B,A
LF396:	MOV	E,D
LF397:	INR	C
	MOV	A,M
	CPI	20H
	RC
	INX	H
	JNZ	LF396
	DCR	E
	JNZ	LF397
LF3A4:	INR	C
	MOV	A,M
	CPI	20H
	RC
	INX	H
	JZ	LF3A4
	MOV	A,C
	SUB	B
	RNC
	DCX	H
	DCX	H
	MVI	M,0
	MOV	C,A
	DCR	D
	JNZ	LF3C5
	LHLD	PNTR
	CMA
	INR	A
	MOV	E,A
	MVI	D,0
	DAD	D
	SHLD	PNTR
LF3C5:	MOV	L,C
	MVI	H,0FFH
	MVI	C,0
	LXI	D,IBUF+82
	DAD	D
	XCHG
	CALL	RMOV
	XCHG
LF3D3:	INX	H
	MVI	M,20H
	MOV	A,L
	CMP	E
	JNZ	LF3D3
	INX	H
	INX	H
	MOV	C,B
	RET
LF3DF:	LDA	TERMW
	INR	A
	RET
;
;FOLLOWING IS THE CODE TO SET THE TERMINAL WIDTH
;NOTE THAT WHAT IS ACTUALLY STORED IS THE LOW ORDER BYTE
;OF THE 1ST ADDR BEYOND THE BUFFER END, NOT THE WIDTH ITSELF.
;
TERM:	LXI	B,ABUF		;POINT TO KEYED IN VALUE
	CALL	ADEC		;CONVERT TO BINARY
	MOV	A,L		;VALUE TO A
	SUI	(-(IBUF+1)) AND 0FFH
	CPI	(IBUF+120) AND 0FFH
	JNC	WHAT		;IF TERMW > 120 SPECIFIED
	STA	TERMW		;SAVE IT
	JMP	EORMS		;DONE
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
