	ALWAYS	2FEB84	ARITH	<ARITHMETIC/LOGICAL OPS ON VIRTUAL MEMORY>
	.MCALL	DIR$,CALLR
;************************************************************************
;*									*
;*	MODULE: ARITH							*
;*									*
;*	FUNCTION: Perform given arithmetic/logical operation on each	*
;*	byte in the range FROM-THRU.					*
;*									*
;*	FUNCTIONS and syntaxes:						*
;*		AND range WITH pp					*
;*		COMPLEMENT range					*
;*		DECREMENT range	BY pp					*
;*		DIVIDE {SIGNED} range BY pp				*
;*		FILL range WITH pp					*
;*		INCREMENT range { BY pp }				*
;*		MULTIPLY range BY pp					*
;*		NEGATE range						*
;*		OR range WITH pp					*
;*		REMAINDER {SIGNED} range BY pp				*
;*		ROTATE LEFT/RIGHT range { BY pp }			*
;*		SHIFT LEFT/RIGHT {SIGNED} range { BY pp }		*
;*		XOR range WITH pp					*
;*									*
;*	pp may be a 2-digit hex number or ASCII character ('char etc).	*
;*									*
;*	INPUT PARAMETERS:						*
;*									*
;*	R0 POINTS TO THE COMMAND LINE IN PROCESS			*
;*									*
;*	OUTPUT PARAMETERS:						*
;*									*
;*	R0 POINTS JUST BEYOND COMMAND LINE				*
;*									*
;*	DESTROYS: R1,R2,R3,R4,R5					*
;*									*
;*	Based on and replacing FILL, COMPLEMENT, and NEGATE functions	*
;*	in Aug 82 version, by:						*
;*									*
;*	AUTHOR: KEVIN ANGLEY						*
;*									*
;*	DATE: 27-JUL-82							*
;*									*
;*	REWRITTEN BY: Chris Doran, Sira Ltd				*
;*									*
;*	DATE: 30-Jan-84							*
;*									*
;************************************************************************

; Local macro:
.MACRO	FUNCTION	FUNCTN	COMMON
FUNCTN::
	MOV	#P'FUNCTN,%5	; Load required routine address
	BR	COMMON		; Fetch SIGNED/operand as required
.ENDM	FUNCTION

FUNCTION	NEGATE		GETRANGE
FUNCTION	COMPLEMENT	GETRANGE
FUNCTION	INCREMENT	GETRANGE
FUNCTION	DECREMENT	GETRANGE
FUNCTION	OR		GETRANGE
FUNCTION	XOR		GETRANGE
FUNCTION	FILL		GETRANGE
FUNCTION	AND		GETRANGE
FUNCTION	MULTIPLY	GETRANGE

FUNCTION	DIVIDE		SIGNED
FUNCTION	REMAINDER	SIGNED

; SHIFT and ROTATE require a LEFT or RIGHT qualifier. Set %4 to 0 for LEFT,
; <>0 for RIGHT.
	.ENABL	LSB
FUNCTION	ROTATE		10$

SHIFT::
	MOV	#PSHIFT,%5	; Load address of shift routine
10$:	SETNZ	%4		; Set left/right flag
	GETKEY	RIGHT		; See if RIGHT
	BEQ	20$		; Yes, continue with flag set
	CLR	%4		; No, see if LEFT
	GETKEY	LEFT
	BNE	MISSINGKEY	; No, "Missing keyword"
20$:	CMP	%5,#PROTATE	; Got LEFT/RIGHT. Is this ROTATE?
	BEQ	GETRANGE	; No SIGNED option if so
;	BR	SIGNED		; Else can be SIGNED
	.DSABL	LSB

; Possible SIGNED option.
SIGNED:	CLR	EXTMSK		; Signed requires no bits cleared
	GETKEY	SIGNED		; See if it is SIGNED
	BEQ	GETRANGE	; Yes, get operand etc
	COMB	EXTMSK+1	; Default or explicit unsigned
;	BR	GETRANGE	; Go get range and operand

; Get range.
GETRANGE:
	CALL	FROMTH		; GET FROM ADDR IN R1, THRU ADDR IN R2,
				;  COUNT IN R3, and step in STEP
	BCS	ERREXIT		;  CS: NOT SUCCESSFUL, TAKE ERROR EXIT
	MOV	R1,R2		; SAVE FROM ADDRESS IN R2 - DON'T NEED THRU

	CMP	%5,#NOOPERAND	; NEGATE and COMPLEMENT
	BLO	START		;  have no operand to fetch.

; Get operand. WITH and BY keywords are synonomous, but one must be present.
	GETKEY	BY		; Try BY
	BEQ	10$		; Yes, OK
	GETKEY	WITH		; No, try WITH
	BEQ	10$		; Go get value if given
	MOV	#1,%1		; No operand. Maybe it should default to 01
	CMP	%5,#DEFAULT1	; Should it?
	BHIS	20$		; Yes, continue with 1 (no need to check AND)
	BR	MISSINGKEY	; No -- "Missing keyword"
10$:
	CLR	%1		; Clear hi byte
	CALL	GETHAS		; Get hex or ASCII value
	BCS	ERREXIT		; Error if none
	CLRB	EXTMSK		; Never clear lo byte
	MOVB	%1,%1		; on sign extend, but clear hi
	BIC	EXTMSK,%1	; unless unsigned (or it doesn't matter)

; Perform any special processing of the operand required by certain functions:

; AND complements operand for BIC operation.
	CMP	%5,#PAND	; Is it AND?
	BNE	20$		; No, branch
	COMB	%1		; Yes, complement for BIC = NAND
	BR	START		; Join common code

; ROTATEs > 7 can be reduced modulus 8, and rotate left n (which can't be done
; by EIS ASHC) is the same as rotate right 8-n = (n-8)&7.
20$:	CMP	%5,#PROTATE	; Is it ROTATE?
	BNE	30$		; No, branch
	BIC	#^C7,%1		; Rotates > 7 bits are redundant
.IF NDF M$$EIS
	BEQ	NOREXIT		; 0 bit rotate does nothing (exit cc from CMP)
.IFTF
	TST	%4		; Test left/right flag
.IFF
	BNE	25$		; NE: right, make sign -ve for ASH
	SUB	#8.,%1		; EQ: left, change it to right equivalent
	BR	START
25$:	NEG	%1		; Negate for ASHC right shift = rotate
.IFT
	BNE	START		; NE: right, keep +ve value
	SUB	#8.,%1		; EQ: left, change it to right equivalent
	NEG	%1		; also +ve
.ENDC
	BR	START		; Join common code

; SHIFT also requires a LEFT/RIGHT flag, which sets the shift count sign
; (+ve = left, -ve = right). Shifts of > 7 bits are redundant.
30$:	CMP	%5,#PSHIFT	; SHIFT operation?
	BNE	START		; No, branch
.IF NDF M$$EIS
	TST	%1		; Test for shift of 0 which does nothing
	BEQ	NOREXIT		; (Saves PSHIFT testing each time)
.ENDC
	CMP	%1,#7		; Shifts > 7 bits are redundant
	BLOS	35$		; If higher, make it 7 to
	MOV	#7,%1		; reduce work by PSHIFT/make ASH sign-extend
35$:	TST	%4		; Yes, should it be right?
	BEQ	START		;   EQ: no, left
	NEG	%1		;   NE: yes, change sign
;	BR	START		; Join common code

START:
	MOV	%1,%4		; Copy operand to %4
	CLRB	EXTMSK		; Never clear lo byte
	PUSH	%0		; Save command line pointer
30$:
	MOVB	MEMORY(R2),%1	; Fetch virtual memory byte (with sign extend)
	BIC	EXTMSK,%1	; Clear sign extend if required
	CALL	@%5		; Call required arithmetic routine
	MOVB	%1,MEMORY(%2)	; Put byte back
40$:	ADD	STEP,R2		; Move to next virtual memory location
	SOB	R3,30$		; USE COUNT TO COUNT LOCATIONS TO NEGATE
	POP	%0		; Restore line pointer
NOREXIT:			; NORMAL EXIT
ERREXIT:
	CALLR	EXTRA		; PURGE COMMAND LINE OF SUPERFLUOUS JUNK
				;  AND RETURN FROM THERE

; Missing keyword (LEFT/RIGHT or WITH/BY).
MISSINGKEY:
	OUTPUT	MSK		; Print message
	SEC			; Set error flag
	BR	ERREXIT		; Take error exit
	.PAGE

; Operation subroutines. Called with value from memory in %1, operand (if
; any) in %4, both sign extended, or sign cleared, as required by SIGNED
; option (if appropriate). Return with result of operation in %1 lo,
; all other registers preserved.
;
; Note that the order of these routines is important -- see NOOPERAND and
; DEFAULT1 positions.

PNEGATE:
	NEGB	%1		; Negate -- monadic
	RETURN

PCOMPLEMENT:
	COMB	%1
	RETURN

; Routines before this have no operand.
NOOPERAND=.

PFILL:	MOVB	%4,%1		; Fill -- straight copy
	RETURN

PAND:
	BIC	%4,%1		; AND = NOT [NAND=BIC]
	RETURN

POR:
	BIS	%4,%1		; BIS = logical OR
	RETURN

PXOR:
	XOR	%4,%1		; Exclusive OR
	RETURN

PMULTIPLY:
.IF DF M$$EIS
	MUL	%4,%1		; Multiply
	RETURN
.IFF
	MOV	%4,%0		; Copy multiplier
	CALLR	$MUL		; Multiply and return
.IFTF

PDIVIDE:
.IFT
	SXT	%0		; Sign extend source if required
	DIV	%4,%0		; Do the divide
.IFF
	MOV	%1,%0		; Load dividend
	MOV	%4,%1		; and divisor
	CALL	$DIV		; Divide
.IFTF
	MOV	%0,%1		; Copy result
	RETURN

PREMAINDER:
.IFT
	SXT	%0		; Sign extend source if required
	DIV	%4,%0		; Do the divide, remainder to %1
.IFF
	MOV	%1,%0		; Load dividend
	MOV	%4,%1		; and divisor
	CALL	$DIV		; Divide, remainder to %1
.IFTF
	RETURN

; NOTE: All routines beyond this point may take a default operand value of 01.
;	Any which have a mandatory BY/WITH/operand must be before this.
DEFAULT1=.

.IFT
; Rotate (always right, since ASHC only works that way).
PROTATE:
	PUSH	%1		; Push value
	MOVB	%1,1(SP)	; to copy bits 0-7 into 8-15
	POP	%1
	ASHC	%4,%1		; So we can do a 16-bit rotate
.IFF
; Rotate (always left, never 0).
PROTATE:
	PUSH	%4		; Save counter
10$:	ASLB	%1		; Shift left into carry, clearing bit 0
	ADCB	%1		; Put carry from bit 7 back into bit 0
15$:	DECB	@SP		; Decrement count
	BNE	10$		; until zero
20$:	POP			; Purge stack
.IFTF
	RETURN			; Return

PSHIFT:
.IFT
	ASH	%4,%1		; Shift signed/unsigned (extend already done)
	RETURN
.IFF
	PUSH	%4		; Save counter (0 checked above)
	BMI	10$		; -ve is a right shift
				; +ve is left:
5$:	ASL	%1		; Shift whole word, including sign extend if reqd
	DECB	@SP		; Decrement counter
	BNE	5$		; Repeat until zero
	BR	20$		; when return

10$:	ASRB	%1		; Shift right (always loses sign)
	DECB	@SP		; Decrement count
	BNE	10$		; until zero
20$:	POP			; Purge stack
	RETURN			; and return
	.DSABL	LSB
.ENDC

PINCREMENT:
	ADD	%4,%1		; Add operand
	RETURN

PDECREMENT:
	SUB	%4,%1		; Subtract operand
	RETURN
.PAGE
	.PSECT	PURE	RO,D

	KEY	LEFT
	KEY	RIGHT
	KEY	SIGNED
	KEY	BY		; (WITH is in HEX, also used by INIT)
	.EVEN

	.PSECT	DATA	RW,D

EXTMSK:	.WORD	0		; Sign extend clear mask, 0 = signed
				;			  177600 = unsigned
	.END
