(;; SCCS   : @(#)ARITH.LSP	3.14 2/4/86
;;; File   : $xerox/arith.lsp
;;; Author : Richard A. O'Keefe
;;; Purpose: Define the arithmetic instructions.
PROGN
;;; ------------------------------------------------------------------------
;;
;;	WARNING: This material is CONFIDENTIAL and proprietary to Quintus
;;	Computer Systems Inc.  This notice is protection against inadvertent
;;	disclosure and does not constitute publication or reflect any intent
;;	to publish.
;;
;;	Copyright (C) 1985, by Quintus Computer Systems, Inc.
;;	All rights reserved.
;;
;;	CAVEAT LECTOR: This software is under development.  No warrantee is
;;	made that it is any use for anything at all.
;;
;;; ------------------------------------------------------------------------


;;; ------------------------------------------------------------------------
;;
;;	The D-machine version of the arithmetic instructions differs from
;;	the VAX and Sun emulators.  Instead of holding unboxed numbers, T0
;;	and T1 hold ordinary Lisp pointers to SMALLPs, FIXPs, FLOATPs, or
;;	whatever.  There are several reasons for this.  One is that it is
;;	easiest to do it this way.  Another important one is that in JAZZ
;;	I am told there will be BIGNUMs, so that the two-fold distinction
;;	which that VAX and Sun emulators assume will not be adequate.  As
;;	a consequence of this change, there is no distinction between the
;;	two modes here as there is in the VAX/Sun versions: we always stay
;;	in whatever mode we happened to be in when we started.
;;
;;	A difference between the Lisp emulator and the existing emulators
;;	is the way that division by zero is handled.  The existing
;;	emulators check for zero and simply fail.  Arithmetic overflow
;;	is handling by taking whatever the hardware gives you for integers,
;;	and infinities for floating point numbers.  Allowing infinities
;;	was a glaringly stupid mistake, we cannot possibly make that work
;;	on the VAX nor on the 1108, and we may change the SUN version to
;;	trap floating point overflows as well.  The Koto version of Lisp
;;	supports bignums, so integer overflow isn't a problem.  But we
;;	want to do something sensible with floating pointer overflow and
;;	all kinds of integer division as well, so we run with

	(OVERFLOW T)

;;	enabled.  Floating overflow is supposed to be caught; for some
;;	reason zero divide is slipping through.


;;  (raw.float)
;;  Takes the floating point number in T0 and turns it into a FIXP
;;  with the same bits.  T0 is first floated, if necessary.

(def.both.mode raw.float (extend)
    (LET ((Ptr (FLOAT (get.nb T0)) ))
	(put.nb T0 (\MAKENUMBER (\GETBASE Ptr 0) (\GETBASE Ptr 1)))
    )
    (continue 0)
)

;;  (float)
;;  ensures that T0 is a floating point number.

(def.both.mode float (extend)
    (if (NOT (FLOATP (get.nb T0))) then
	(put.nb T0 (FLOAT (get.nb T0)) ))
    (continue 0)
)

;;  (fix)
;;  ensures that T0 is a fixed point number.
;;  Note that this can cause an overflow trap.

(def.both.mode fix (extend)
    (if (FLOATP (get.nb T0)) then
	(put.nb T0 (FIX (get.nb T0)) ))
    (continue 0)
)


;;  (minus)
;;  negates a number.
;;  does (IMINUS -2↑31) cause an integer overflow trap?  It should.

(def.both.mode minus (extend)
    (put.nb T0 (MINUS (get.nb T0)))
    (continue 0)
)


;;  (boolean.not)
;;  takes the complement of a number.

(def.both.mode boolean.not (extend)
    (put.nb T0 (LOGNOT (get.nb T0)))
    (continue 0)
)


;;  All the two-argument arithmetic operations follow a common pattern.
;;  They just apply a Lisp function to T0 and T1, and put the result in
;;  T0.  So, to save my effort, they are defined via a macro.

(def.macro def.binop (Op Fn)
   `(def.both.mode ,Op (extend)
	(put.nb T0 (,Fn (get.nb T1) (get.nb T0)) )
	(continue 0)
    )
)

(def.binop add			PLUS)
(def.binop subtract		DIFFERENCE)
(def.binop multiply		TIMES)
(def.binop divide		FQUOTIENT)
(def.binop integer.divide	IQUOTIENT)
(def.binop modulus		REMAINDER)

(def.binop boolean.and		LOGAND)
(def.binop boolean.or		LOGOR)
(def.binop boolean.xor		LOGXOR)
(def.binop left.shift		LSH)	; *NOT* LLSH
(def.binop right.shift		RSH)	; *NOT* LRSH


;;  There are two versions of each arithmetic relation.
;;  <foo> succeeds if the relation holds, fails otherwise.
;;  <foo.else L> falls through if the relation holds, or
;;  jumps to L if the relation fails to hold.  For convenience,
;;  the jump instructions are like other jumps, [opcode:8|addr:24]
;;  and the other ones are [extend:8|opcode:8].  As a matter of
;;  fact, these instructions can only appear at the end of a
;;  block of arithmetic instructions, so if we arranged for load
;;  instructions to set read mode, we could double up the other
;;  arithmetic instructions, particularly the jumps and stores.

(def.macro def.relop (Fn Op1 Op2)
   `(PROGN
	(def.both.mode ,Op1 (extend)
	    (if (,Fn (get.nb T1) (get.nb T0)) then
		(continue 0)
	    else
		(fast.fail)
	    )
	)
	(def.both.mode ,Op2 (address)
	    (if (,Fn (get.nb T1) (get.nb T0)) then
		(continue 1)
	    else
		(continue.at (address.operand))
	    )
	)
    )
)

(def.relop	EQP	    equal.to		equal.to.else)
(def.relop	(LAMBDA (X Y) (NOT (EQP X Y)))
			    not.equal.to	not.equal.to.else)
(def.relop	LESSP	    less.than		less.than.else)
(def.relop	GEQ	    not.less.than	not.less.than.else)
(def.relop	GREATERP    greater.than	greater.than.else)
(def.relop	LEQ	    not.greater.than	not.greater.than.else)



;;  (store.variable.Xn n)
;;  stores the number in T0 in the previously uninitialised variable Xn.
;;  We set the tag of T0, but it is safe to continue in arithmetic mode,
;;  because the arithmetic operations ignore that tag.  We have to use
;;  tag.other here rather than tag.number because, thanks to the new
;;  improved choice point code, T0 might be a Prolog choice point handle.

(def.macro store.variable (store)
   `(LET ((Ptr (get.nb T0)))
	(if (AND (NOT (SMALLP Ptr)) (NUMBERP Ptr)) then
	    (QP.ADD.REF Ptr)	; FIXP, BIGNUM, FLOATP
	)
	(,store N (tag.other Ptr))
    )
)

(def.both.mode store.variable.Xn (areg)
    (store.variable put.Aval)
    (continue 0)
)

;;  (store.variable.XnM n)
;;  is for the X registers stored in memory.

(def.both.mode store.variable.XnM (amem)
    (store.variable put.Amem)
    (continue 0)
)

;;  (store.variable.Yn n)
;;  is much the same.

(def.both.mode store.variable.Yn (yreg)
    (store.variable put.Yval)
    (continue 0)
)



;;  (store.value.Xn n) and
;;  (store.value.Yn n)
;;  are really a form of unification.  For the moment, the simplest
;;  thing to do is to set up T0 and T1 and go ahead with a call to
;;  the general unifier.  We could save some time by making a special
;;  case unify.constant routine, but it wouldn't save all that much
;;  effort, as we still have to check for boxed numbers.   Because
;;  maintaining Lisp reference counts is so expensive, we want to
;;  avoid calling QP.ADD.REF if we possibly can.  The same criteria
;;  apply to all flavours of store value, even store.value.Xn, as
;;  Xn might hold an unbound variable.  So the store.value macro
;;  packages up the reference count maintenance and tag repair for
;;  all flavours of store.value.  We use tag.other because gettopval
;;  is sometimes used to load procedure record addresses!

(def.open store.value ()
    (LET ((Ptr (get.nb T0)))
	(if (AND (EQ (tag.of T1) ref.tag.8)	; might store into memory
		 (NOT (SMALLP Ptr))		; might need a ref count
		 (NUMBERP Ptr)) then		; filter out BLOCKDATAPs
	    (QP.ADD.REF Ptr)
	)
	(put.32 T0 (tag.other Ptr))
    )
    (unify.and.continue 0)
)

(def.both.mode store.value.Xn (areg)
    (put.32 T1 (get.Aval N))
    (store.value)
)

(def.both.mode store.value.XnM (amem)
    (put.32 T1 (get.Amem N))
    (store.value)
)

(def.both.mode store.value.Yn (yreg)
    (put.32 T1 (get.Yval N))
    (store.value)
)



;;  (store.integer cell) and
;;  (store.float cell)
;;  could be done the same way, by just going off to the general unifier.
;;  Indeed, the whole of the "store" family could be handled by just the
;;  store.variable.An instruction, followed by unification against that
;;  A register.  store.integer can check immediately for a FIXP, and
;;  store.float can check immediately for a FLOATP; this is no real gain
;;  as these instructions are vanishingly rare.

(def.both.mode store.integer (extend cell)
    (put.32 T1 (cell.operand))
    (if (AND (FIXP (get.nb T0)) (EQP (get.nb T0) (get.nb T1)) ) then
	(continue 2)
    else
	(fast.fail)
    )
)

(def.both.mode store.float (extend cell)
    (put.32 T1 (cell.operand))
    (if (AND (FLOATP (get.nb T0)) (EQP (get.nb T0) (get.nb T1)) ) then
	(continue 2)
    else
	(fast.fail)
    )
)




;;  In an earlier version of this file, the load instructions used to
;;  demand that an integer or float be yielded.  But now that BIGNUMS
;;  are available, this is no longer right, and more importantly, the
;;  pseudo-Prolog code that maintains dynamic clauses expects X =\= 0
;;  to work correctly for any "address" X.  So now we accept any kind
;;  of constant.   If we do arithmetic on a non-numeric value, Inter-
;;  LOSS is perfectly capable of spotting the error and reporting it.
;;  As we don't check, "Y = a, X is Y" will succeed in compiled code,
;;  and it isn't supposed to (and won't in interpreted code).  We can
;;  live with that.  When we find a constant, we leave the tag in T0.
;;  It doesn't matter, nothing is going to look at it.  The final (?)
;;  idea is to reject atoms but accept "others".

(def.subr load.value.error ()			; print an error message
    ;;  Check whether we can change (MKATOM &) -> &
    (QP.PUT.TOKEN.SYMBOL QP.CURRENT.OUTPUT (MKATOM "%
[Warning: Arithmetic predicate has failed - tried to evaluate a non number]%
[         Use the interpreter and debugger to locate this error]%
"   ) 0)
    (fast.fail)
)

(def.open load.value ()
    (PROG (Tag)
	(while (EQ (SETQ Tag (tag.of T0)) ref.tag.8)
	    ;;  variable.  Dereference, see if unbound.
	    (put.24 R  (untag.ref T0))
	    (put.32 T0 (get.cell R 0))
	    (if (EQ (get.nb T0) (get.24 R)) then
		(load.value.error)	; unbound variable, never ok
	    )
	)
	(if (ILESSP Tag immed.tag.8) then
	    (load.value.error)		; not a constant
	)
    )
    ;;  T0 is a smallp, fixp, floatp, or "other"
    (continue 0)
)


(def.both.mode load.value.Xn (areg)
    (put.32 T1 (get.32 T0))		; "push" T0
    (put.32 T0 (get.Aval N))
    (load.value)
)


(def.both.mode load.value.XnM (amem)
    (put.32 T1 (get.32 T0))		; "push" T0
    (put.32 T0 (get.Amem N))		; #TAIL
    (load.value)
)


(def.both.mode load.value.Yn (yreg)
    (put.32 T1 (get.32 T0))		; "push" T0
    (put.32 T0 (get.Yval N))
    (load.value)
)



;;  When it comes to loading a constant, we don't need to have separate
;;  load.integer and load.float instructions.  The Sun/VAX emulators use
;;  them so that (1) they can work out fast what mode they are supposed
;;  to be in, and (2) they can put 32-bit boxed numbers in line in the
;;  code.  We don't use the mode that way, and there isn't any advantage
;;  to having in-line constants.  Indeed, it'd cost us more boxing.
;;  So there is only the
;;  (load.constant cell)
;;  instruction, which copies the pointer part of the cell to T0.  (The
;;  tag is copied too, but nobody cares about that.)

(def.both.mode load.constant (extend cell)
    (put.32 T1 (get.32 T0))		; "push" T0
    (put.32 T0 (cell.operand))
    (continue 2)
)


;;  There are four instructions which take 16-bit integer arguments.
;;  load.{pos/neg}.word are there for compactness; they are one word
;;  shorter than load.cell.  All things considered, I wonder if I am
;;  wise to keep them?

(def.both.mode load.pos.word (extend word)
    (put.32 T1 (get.32 T0))
    (put.nb T0 (get.code P 0))
    (continue 1)
)

(def.both.mode load.neg.word (extend word)
    (put.32 T1 (get.32 T0))
    (put.nb T0 (\VAG2 (CONSTANT (\HILOC -1)) (get.code P 0)))
    (continue 1)
)

;;  add.{pos/neg}.word add a 17-bit integer to T0.  They have to do
;;  this without disturbing T1, so we use R as a scratch register.
;;  This really needs some thought.  add.constant would be one word
;;  longer, and would not be all that much slower.

(def.both.mode add.pos.word (extend word)
    (put.nb T0 (PLUS (get.nb T0) (get.code P 0)))
    (continue 1)
)

(def.both.mode add.neg.word (extend word)
    (put.nb T0 (PLUS (get.nb T0)
	(\VAG2 (CONSTANT (\HILOC -1)) (get.code P 0))))
    (continue 1)
)


) STOP