	TITLE	'ASM OPERAND SCAN MODULE'
;	OPERAND SCAN MODULE
	org	0
base	equ	$

	ORG	1860H
;
;	EXTERNALS
IOMOD	EQU	base+200H	;I/O MODULE
SCMOD	EQU	base+1100H	;SCANNER MODULE
SYMOD	EQU	base+1340H	;SYMBOL TABLE MODULE
BMOD	EQU	base+15A0H	;BINARY SEARCH MODULE
;
;
PERR	EQU	IOMOD+18H
SCAN	EQU	SCMOD+6H	;SCANNER ENTRY POINT
CR	EQU	0DH	;CARRIAGE RETURN
;
LOOKUP	EQU	SYMOD+6H	;LOOKUP
FOUND	EQU	LOOKUP+3	;FOUND SYMBOL IF ZERO FLAG NOT SET
ENTER	EQU	FOUND+3		;ENTER SYMBOL
SETTY	EQU	ENTER+3		;SET TYPE FIELD
GETTY	EQU	SETTY+3		;SET TYPE FIELD
SETVAL	EQU	GETTY+3		;SET VALUE FIELD
GETVAL	EQU	SETVAL+3	;GET VALUE FIELD
;
BSEAR	EQU	BMOD+3	;BINARY SEARCH ROUTINE
BGET	EQU	BSEAR+3	;GET VALUES WITH SEARCH
;
;	COMMON EQUATES
PBMAX	EQU	90	;MAX PRINT SIZE
PBUFF	EQU	base+10CH	;PRINT BUFFER
PBP	EQU	PBUFF+PBMAX	;PRINT BUFFER POINTER
;
TOKEN	EQU	PBP+1	;CURRENT TOKEN UDER SCAN
VALUE	EQU	TOKEN+1	;VALUE OF NUMBER IN BINARY
ACCLEN	EQU	VALUE+2	;ACCUMULATOR LENGTH
ACMAX	EQU	64	;MAX ACCUMULATOR LENGTH
ACCUM	EQU	ACCLEN+1
;
EVALUE	EQU	ACCUM+ACMAX	;VALUE FROM EXPRESSION ANALYSIS
;
SYTOP	EQU	EVALUE+2	;CURRENT SYMBOL TOP
SYMAX	EQU	SYTOP+2		;MAX ADDRESS+1
;
PASS	EQU	SYMAX+2	;CURRENT PASS NUMBER
FPC	EQU	PASS+1	;FILL ADDRESS FOR NEXT HEX BYTE
ASPC	EQU	FPC+2	;ASSEMBLER'S PSEUDO PC
;
;	GLOBAL EQUATES
IDEN	EQU	1	;IDENTIFIER
NUMB	EQU	2	;NUMBER
STRNG	EQU	3	;STRING
SPECL	EQU	4	;SPECIAL CHARACTER
;
PLABT	EQU	0001B	;PROGRAM LABEL
DLABT	EQU	0010B	;DATA LABEL
EQUT	EQU	0100B	;EQUATE
SETT	EQU	0101B	;SET
MACT	EQU	0110B	;MACRO
;
EXTT	EQU	1000B	;EXTERNAL
REFT	EQU	1011B	;REFER
GLBT	EQU	1100B	;GLOBAL
;
;
;	TABLE DEFINITIONS
XBASE	EQU	0	;START OF OPERATORS
OPER	EQU	15	;LAST OPERATOR
RT	EQU	16
PT	EQU	RT+1	;RT IS REGISTER TYPE, PT IS PSEUDO OPERATION
OBASE	EQU	PT+1
;
PLUS	EQU	5
MINUS	EQU	6
NOTF	EQU	8	;NOT
LPAR	EQU	12
RPAR	EQU	13
OSMAX	EQU	10
VSMAX	EQU	8*2
;
;
;	BEGINNING OF MODULE
	JMP	ENDMOD	;PAST THIS MODULE
	JMP	OPAND	;SCAN OPERAND FIELD
	JMP	MULF	;MULTIPLY FUNCTION
	JMP	DIVE	;DIVIDE FUNCTION
UNARY:	DS	1	;TRUE IF NEXT OPERATOR IS UNARY
OPERV:	DS	OSMAX	;OPERATOR STACK
HIERV:	DS	OSMAX	;OPERATOR PRIORITY
VSTACK:	DS	VSMAX	;VALUE STACK
OSP:	DS	1	;OPERATOR STACK POINTER
VSP:	DS	1	;VALUE STACK POINTER
;
;
;
STKV:	;PLACE CURRENT H,L VALUE AT TOP OF VSTACK
	XCHG	;HOLD VALUE IN D,E
	LXI	H,VSP
	MOV	A,M
	CPI	VSMAX
	JC	STKV0
	CALL	ERREX	;OVERFLOW IN EXPRESSION
	MVI	M,0	;VSP=0
STKV0:	MOV	A,M	;GET VSP
	INR	M	;VSP=VSP+1
	INR	M	;VSP=VSP+2
	MOV	C,A	;SAVE VSP
	MVI	B,0	;DOUBLE VSP
	LXI	H,VSTACK
	DAD	B
	MOV	M,E	;LOW BYTE
	INX	H
	MOV	M,D	;HIGH BYTE
	RET
;
STKO:	;STACK OPERATOR (REG-A) AND PRIORITY (REG-B)
	PUSH	PSW	;SAVE IT
	LXI	H,OSP
	MOV	A,M
	CPI	OSMAX
	JC	STKO1
	MVI	M,0
	CALL	ERREX	;OPERATOR STACK OVERFLOW
STKO1:	MOV	E,M	;GET OSP
	MVI	D,0
	INR	M	;OSP=OSP+1
	POP	PSW	;RECALL OPERATOR
	LXI	H,OPERV
	DAD	D	;OPERV(OSP)
	MOV	M,A	;OPERV(OSP)=OPERATOR
	LXI	H,HIERV
	DAD	D
	MOV	M,B	;HIERV(OSP)=PRIORITY
	RET
;
LODV1:	;LOAD TOP ELEMENT FROM VSTACK TO H,L
	LXI	H,VSP
	MOV	A,M
	ORA	A
	JNZ	LODOK
	CALL	ERREX	;UNDERFLOW
	LXI	H,0
	RET
;
LODOK:	DCR	M
	DCR	M	;VSP=VSP-2
	MOV	C,M	;LOW BYTE
	MVI	B,0
	LXI	H,VSTACK
	DAD	B	;VSTACK(VSP)
	MOV	C,M	;GET LOW BYTE
	INX	H
	MOV	H,M
	MOV	L,C
	RET
;
LODV2:	;LOAD TOP TWO ELEMENTS DE HOLDS TOP, HL HOLDS TOP-1
	CALL	LODV1
	XCHG
	CALL	LODV1
	RET
;
APPLY:	;APPLY OPERATOR IN REG-A TO TOP OF STACK
	MOV	L,A
	MVI	H,0
	DAD	H	;OPERATOR NUMBER*2
	LXI	D,OPTAB
	DAD	D	;INDEXED OPTAB
	MOV	E,M	;LOW ADDRESS
	INX	H
	MOV	H,M	;HIGH ADDRESS
	MOV	L,E
	PCHL		;SET PC AND GO TO SUBROUTINE
;
OPTAB:	DW	MULOP
	DW	DIVOP
	DW	MODOP
	DW	SHLOP
	DW	SHROP
	DW	ADDOP
	DW	SUBOP
	DW	NEGOP
	DW	NOTOP
	DW	ANDOP
	DW	OROP
	DW	XOROP
	DW	ERREX	;(
;
;	SPECIFIC HANDLERS FOLLOW
SHFT:	;SET UP OPERANDS FOR SHIFT L AND R
	CALL	LODV2
	MOV	A,D	;ENSURE 0-15
	ORA	A
	JNZ	SHERR
	MOV	A,E
	CPI	17
	RC		;RETURN IF 0-16 SHIFT
SHERR:	CALL	ERREX
	MVI	A,16
	RET
;
NEGF:	;COMPUTE 0-H,L TO H,L
	XRA	A
	SUB	L
	MOV	L,A
	MVI	A,0
	SBB	H
	MOV	H,A
	RET
;
DIVF:	CALL	LODV2
DIVE:	;(EXTERNAL ENTRY FROM MAIN PROGRAM)
	XCHG		;SWAP D,E WITH H,L FOR DIVIDE FUNCTION
;	COMPUTE X/Y WHERE X IS IN D,E AND Y IS IN H,L
;	THE VALUE OF X/Y APPEARS IN D,E AND X MOD Y IS IN H,L
;
	SHLD	DTEMP	;SAVE X IN TEMPORARY
	LXI	H,BNUM	;STORE BIT COUNT
	MVI	M,11H
	LXI	B,0	;INTIALIZE RESULT
	PUSH	B
	XRA	A	;CLEAR FLAGS
DLOOP:
	MOV	A,E	;GET LOW Y BYTE
	RAL
	MOV	E,A
	MOV	A,D
	RAL
	MOV	D,A
	DCR	M	;DECREMENT BIT COUNT
	POP	H	;RESTORE TEMP RESULT
	RZ		;ZERO BIT COUNT MEANS ALL DONE
	MVI	A,0	;ADD IN CARRY
	ACI	0	;CARRY
	DAD	H	;SHIFT TEMP RESULT LEFT ONE BIT
	MOV	B,H	;COPY HA AND L TO A A ND C
	ADD	L
	LHLD	DTEMP	;GET ADDRESS OF X
	SUB	L	;SUBTRACT FROM TEMPORARY RESULT
	MOV	C,A
	MOV	A,B
	SBB	H
	MOV	B,A
	PUSH	B	;SAVE TEMP RESULT IN STACK
	JNC	DSKIP	;NO BORROW FROM SUBTRACT
	DAD	B	;ADD X BACK IN
	XTHL	;REPLACE TEMP RESULT ON STACK
DSKIP:	LXI	H,BNUM	;RESTORE H,L
	CMC
	JMP	DLOOP	;REPEAT LOOP STEPS
;
DTEMP:	DS	2
BNUM:	DS	1
;
MULF:	;MULTIPLY D,E BY H,L AND REPLACE H,L WITH RESULT
	MOV	B,H
	MOV	C,L	;COPY OF 1ST VALUE TO B,C FOR SHIFT AND ADD
	LXI	H,0	;H,L IS THE ACCUMULATOR
MUL0:	XRA	A
	MOV	A,B	;CARRY IS CLEARED
	RAR
	MOV	B,A
	MOV	A,C
	RAR
	MOV	C,A
	JC	MUL1	;SKIP THIS ADD IF LSB IS ZERO
	ORA	B
	RZ		;RETURN WITH H,L
	JMP	MUL2	;SKIP ADD
MUL1:	DAD	D	;ADD CURRENT VALUE OF D
MUL2:	XCHG	;READY FOR *2
	DAD	H
	XCHG
	JMP	MUL0
;
MULOP:	;MULTIPLY D,E BY H,L
	CALL	LODV2
	CALL	MULF
	JMP	ENDOP
;
DIVOP:	;DIVIDE H,L BY D,E
	CALL	DIVF
	XCHG		;RESULT TO H,L
	JMP	ENDOP
;
MODOP:	CALL	DIVF
	JMP	ENDOP
;
SHLOP:	CALL	SHFT	;CHECK VALUES
SHL0:	ORA	A	;DONE?
	JZ	ENDOP
	DAD	H	;HL=HL*2
	DCR	A
	JMP	SHL0
;
SHROP:	CALL	SHFT
SHR0:	ORA	A	;DONE?
	JZ	ENDOP
	PUSH	PSW	;SAVE CURRENT COUNT
	XRA	A
	MOV	A,H
	RAR
	MOV	H,A
	MOV	A,L
	RAR
	MOV	L,A
	POP	PSW
	DCR	A
	JMP	SHR0
;
ADDOP:	CALL	LODV2
ADD0:	DAD	D
	JMP	ENDOP
;
SUBOP:	CALL	LODV2
	XCHG		;TREAT AS HL+(-DE)
	CALL	NEGF	;0-HL
	JMP	ADD0
;
NEGOP:	CALL	LODV1
NEG0:	CALL	NEGF	;COMPUTE 0-HL
	JMP	ENDOP
;
NOTOP:	CALL	LODV1
	INX	H	;65536-HL = 65535-(HL+1)
	JMP	NEG0
;
ANDOP:	CALL	LODV2
	MOV	A,D
	ANA	H
	MOV	H,A
	MOV	A,E
	ANA	L
	MOV	L,A
	JMP	ENDOP
;
OROP:	CALL	LODV2
	MOV	A,D
	ORA	H
	MOV	H,A
	MOV	A,E
	ORA	L
	MOV	L,A
	JMP	ENDOP
;
XOROP:	CALL	LODV2
	MOV	A,D
	XRA	H
	MOV	H,A
	MOV	A,E
	XRA	L
	MOV	L,A
;
ENDOP:	JMP	STKV
;
;
;
ENDEXP:	;RETURNS ZERO FLAG IF SYMBOL IS CR, ;, OR ,
	LDA	TOKEN
	CPI	SPECL
	RNZ		;NOT END IF NOT SPECIAL
;
	LDA	ACCUM
	CPI	CR
	RZ
	CPI	';'
	RZ
	CPI	','
	RZ
	CPI	'!'
	RET
;
OPAND:	;SCAN THE OPERAND FIELD OF AN INSTRUCTION
;	(NOT A DB WITH FIRST TOKEN STRING > 2 OR 0)
	XRA	A
	STA	OSP	;ZERO OPERATOR STACK POINTER
	STA	VSP
	DCR	A	;255
	STA	UNARY
	LXI	H,0
	SHLD	EVALUE
;
OP0:	;ARRIVE HERE WITH NEXT ITEM ALREADY SCANNED
	CALL	ENDEXP	;DONE?
	JNZ	OP1
;	EMPTY THE OPERATOR STACK
EMPOP:	LXI	H,OSP
	MOV	A,M	;GET THE OSP AND CHECK FOR EMPTY
	ORA	A
	JZ	CHKVAL	;JUMP IF EMPTY
	DCR	M	;POP ELEMENT
	MOV	E,A	;COPY FOR DOUBLE ADD
	DCR	E
	MVI	D,0
	LXI	H,OPERV
	DAD	D	;INDEXED - OPERV(OSP)
	MOV	A,M	;GET OPERATOR
	CALL	APPLY	;APPLY OPERATOR
	JMP	EMPOP
;
CHKVAL:
	LDA	VSP	;MUST HAVE ONE ELEMENT IT THE STACK
	CPI	2
	CNZ	ERREX
	LDA	PBUFF
	CPI	' '
	RNZ		;EVALUE REMAINS AT ZERO
	LHLD	VSTACK	;GET DOUBLE BYTE IN STACK
	SHLD	EVALUE
	RET
;
OP1:	;MORE TO SCAN
	LDA	PBUFF
	CPI	' '
	JNZ	GETOP
	LDA	TOKEN
	CPI	STRNG	;IS THIS A STRING?
	JNZ	OP3
;
;	STRING - CONVERT TO DOUBLE PRECISION
	LDA	ACCLEN
	ORA	A
	CZ	ERREX	;ERROR IF LENGTH=0
	CPI	3
	CNC	ERREX	;ERROR IF LENGTH>2
	MVI	D,0
	LXI	H,ACCUM
	MOV	E,M	;LSBYTE
	INX	H
	DCR	A	;A HAS THE LENGTH
	JZ	OP2	;ONE OR TWO BYTES
	MOV	D,M	;FILL HIGH ORDER
OP2:	XCHG		;VALUE TO H,L
	JMP	STNUM	;STORE TO STACK
;
OP3:	;NOT A STRING, CHECK FOR NUMBER
	CPI	NUMB
	JNZ	OP4
	LHLD	VALUE	;NUMERIC VALUE
	JMP	STNUM
;
OP4:	;NOT STRING OR NUMBER, MUST BE ID OR SPECL
	CALL	BGET	;BINARY SEARCH, GET ATTRIBUTES
	JNZ	OP6	;MATCH?
;	YES, MAY BE OPERATOR
	CPI	OPER+1
	JNC	OP5
;	OPERATOR ENCOUNTERED MS NIBBLE OF B IS PRIORITY NUMBER LS NIBBLE
;	IS THE OPERATOR
;	ACC HAS THE OPERATOR NUMBER, B HAS PRIORITY
	CPI	LPAR	;(?
	MOV	C,A	;SAVE COPY OF OPERATOR NUMBER
	LDA	UNARY
	JNZ	OPER1	;JUMP IF NOT A (
;	( ENCOUNTERED, UNARY MUST BE TRUE
	ORA	A
	CZ	ERREX
	MVI	A,0FFH
	STA	UNARY	;UNARY IS SET TRUE
	MOV	A,C	;RECOVER OPERATOR
	JMP	OPER4	;CALLS STKO AND SETS UNARY TO TRUE
;
;
OPER1:	;NOT A LEFT PAREN
	ORA	A
	JNZ	OPER6	;MUST BE + OR - SINCE UNARY IS SET
;
;	UNARY NOT SET, MUST BE BINARY OPERATOR
OPER2:	;COMPARE HIERARCHY OF TOS
	PUSH	B	;SAVE PRIORITY AND OPERATOR NUMBER
	LDA	OSP
	ORA	A
	JZ	OPER3	;NO MORE OPERATORS IN STACK
	MOV	E,A	;OSP TO E
	DCR	E	;OSP-1
	MVI	D,0
	LXI	H,HIERV
	DAD	D	;HL ADDRESSES TOP OF OPERATOR STACK
	MOV	A,M	;PRIORITY OF TOP OPERATOR
	CMP	B	;CURRENT GREATER?
	JC	OPER3	;JUMP IF SO
;	APPLY TOP OPERATOR TO VALUE STACK
	LXI	H,OSP
	MOV	M,E	;OSP=OSP-1
	LXI	H,OPERV
	DAD	D
	MOV	A,M	;OPERATOR NUMBER TO ACC
	CALL	APPLY
	POP	B	;RESTORE OPERATOR NUMBER AND PRIORITY
	JMP	OPER2	;FOR ANOTHER TEST
;
OPER3:	;ARRIVE HERE WHEN OPERATOR IS STACKED
;	CHECK FOR RIGHT PAREN BALANCE
	POP	B	;OPERATOR NUMBER IN C, PRIORITY IN B
	MOV	A,C
	CPI	RPAR
	JNZ	OPER4	;JUMP IF NOT A RIGHT PAREN
;
;	RIGHT PAREN FOUND, STACK MUST CONTAIN LEFT PAREN TO DELETE
	LXI	H,OSP
	MOV	A,M
	ORA	A	;ZERO?
	JZ	LPERR	;PAREN ERROR IF SO
	DCR	A	;OSP-1
	MOV	M,A	;STORED TO MEMORY
	MOV	E,A
	MVI	D,0
	LXI	H,OPERV
	DAD	D
	MOV	A,M	;TOP OPERATOR IN REG-A
	CPI	LPAR
	JZ	NLERR	;JMP IF NO ERROR - PARENS BALANCE
LPERR:	CALL	ERREX
NLERR:	;ERROR REPORTING COMPLETE
	XRA	A
	JMP	OPER5	;TO CLEAR UNARY FLAG
;
OPER4:	;ORDINARY OPERATOR
	CALL	STKO
	MVI	A,0FFH	;TO SET UNARY FLAG
OPER5:	STA	UNARY
	JMP	GETOP	;FOR ANOTHER ELEMENT
;
OPER6:	;UNARY SET, MUST BE + OR -
	MOV	A,C	;RECALL OPERATOR
	CPI	PLUS
	JZ	GETOP	;IGNORE UNARY PLUS
	CPI	MINUS
	JNZ	CHKNOT
	INR	A	;CHANGE TO UNARY MINUS
	MOV	C,A
	JMP	OPER2
CHKNOT:	;UNARY NOT SYMBOL?
	CPI	NOTF
	CNZ	ERREX
	JMP	OPER2
;
;
OP5:	;ELEMENT FOUND IN TABLE, NOT AN OPERATOR
	CPI	PT	;PSEUDO OPERATOR?
	CZ	ERREX	;ERROR IF SO
	MOV	L,B	;GET LOW VALUE TO L
	MVI	H,0	;ZERO HIGH ORDER BYTE
	JMP	STNUM	;STORE IT
;
OP6:	;NOT FOUND IN TABLE SCAN, $?
	LDA	TOKEN
	CPI	SPECL
	JNZ	OP7
	LDA	ACCUM
	CPI	'$'
	JZ	CURPC	;USE CURRENT PC
	CALL	ERREX
	LXI	H,0
	JMP	STNUM
CURPC:	LHLD	ASPC	;GET CURRENT PC
	JMP	STNUM
;
OP7:	;NOT $, LOOK IT UP
	CALL	LOOKUP
	CALL	FOUND
	JNZ	FIDENT
;	NOT FOUND IN SYMBOL TABLE, ENTER IF PASS 1
	MVI	A,'P'
	CALL	PERR
	CALL	ENTER	;ENTER SYMBOL WITH ZERO TYPE FIELD
	JMP	FIDE0
FIDENT:	CALL	GETTY	;TYPE TO H,L
	ANI	111B
	MVI	A,'U'
	CZ	PERR
;
FIDE0:
	CALL	GETVAL	;VALUE TO H,L
;
STNUM:	;STORE H,L TO VALUE STACK
	LDA	UNARY
	ORA	A	;UNARY OPERATION SET
	CZ	ERREX	;OPERAND ENCOUNTERED WITH UNARY OFF
	XRA	A
	STA	UNARY	;SET TO OFF
	CALL	STKV	;STACK THE VALUE
;
GETOP:	CALL	SCAN
	JMP	OP0
;
ERREX:	;PUT 'E' ERROR IN OUTPUT BUFFER
	PUSH	H
	MVI	A,'E'
	CALL	PERR
	POP	H
	RET
;
ENDMOD	EQU	($ AND 0FFE0H) + 20H	;NEXT HALF PAGE
	END
