;;; -*- Mode:LISP; Package:SIM; Readtable:CL; Base:10 -*-

(defsubst read-register (name)
  (micro:%read-a-mem (get name 'micro:a-mem-adr)))

(defsubst write-register (name val)
  (let ((adr (get name 'micro:a-mem-adr)))
    (when (not (fixp adr))
      (ferror nil "bad address ~s" name))
    (micro:%write-a-mem adr val)))

(defsetf read-register write-register)

(defconst *main-memory-pages* 1024.)
(defvar *main-memory-end*)
(defconst *pages-of-frames* 16.)
(defvar *frames-end*)

(defvar *main-memory-array* (si:make-wireable-array (+ *pages-of-frames* 1) 'art-32b nil))
(defvar *frames-array* (si:make-wireable-array (* 4 *main-memory-pages*) 'art-32b nil))

(defun make-structures ()
  ;;frames + functional registers
  (write-register 'micro:a-sim-frames (+ (si:%pointer-unsigned (%pointer *frames-array*))
					 (si:array-data-offset *frames-array*)))
  (setq *frames-end* (+ (si:%pointer-unsigned (%pointer *frames-array*))
			(si:array-data-offset *frames-array*)
			(array-length *frames-array*)))
  (write-register 'micro:a-sim-main-memory (+ (si:%pointer-unsigned (%pointer *main-memory-array*))
					      (si:array-data-offset *main-memory-array*)))
  (setq *main-memory-end* (+ (si:%pointer-unsigned (%pointer *main-memory-array*))
			     (si:array-data-offset *main-memory-array*)
			     (array-length *main-memory-array*)))
  )

(defun reset-registers ()
  (write-register 'micro:m-sim-src-1-ptr 0)
  (write-register 'micro:m-sim-src-2-ptr 0)
  (write-register 'micro:m-sim-dest-ptr 0)
  (write-register 'micro:m-sim-last-src-1 0)
  (write-register 'micro:m-sim-last-aluf 0)
  (write-register 'micro:m-sim-inst-0 0)
  (write-register 'micro:m-sim-inst-1 0)
  (write-register 'micro:m-sim-inst-2 0)
  (write-register 'micro:m-open-frame 0)
  (write-register 'micro:m-active-frame 0)
  (write-register 'micro:m-return-frame 0)

 (write-register 'micro:a-sim-pc 0)
 (write-register 'micro:a-sim-next-pc 0)
 (write-register 'micro:a-sim-noop-next-bit 0)
 (write-register 'micro:a-sim-last-src-2 0)
 (write-register 'micro:a-sim-last-result 0)

 (write-register 'micro:a-sim-src-ptrs-valid 0)
 )

(defun %p-contents-as-bignum (adr)
  (check-type adr integer)
  (dpb (%p-ldb (byte 16. 16.) adr)
       (byte 16. 16.)
       (%p-ldb (byte 16. 0) adr)))

(defun %p-store-contents-as-bignum (adr val)
  (check-type adr integer)
  (%p-dpb (ldb (byte 16. 0) val) (byte 16. 0) adr)
  (%p-dpb (ldb (byte 16. 16.) val) (byte 16. 16.) adr)
  val)

(defsetf %p-contents-as-bignum %p-store-contents-as-bignum)

(defflavor ucode-sim
	 (
	  )
	 ()
  :settable-instance-variables)

(defmethod (ucode-sim :after :init) (ignore)
  (make-structures)
  (send self :reset))

(defmethod (ucode-sim :reset) ()
  (reset-registers))

(defmethod (ucode-sim :reset-and-clear) ()
  (reset-registers)
  (let ((main-memory-base (read-register 'micro:a-sim-main-memory))
	(frames-base (read-register 'micro:a-sim-frames)))
    (setf (%p-contents-as-bignum main-memory-base) 0)
    (%blt main-memory-base (1+ main-memory-base) (- *main-memory-end* main-memory-base 1) 1)
    (setf (%p-contents-as-bignum frames-base) 0)
    (%blt frames-base (1+ frames-base) (- *frames-end* frames-base 1) 1)))

(defmethod (ucode-sim :read-main-mem) (adr)
  (when (or (< adr 0)
	    (>= adr *main-memory-end*))
    (ferror nil "bad adr"))
  (let* ((real-adr (si:%make-pointer-unsigned (+ (* 4 adr) (read-register 'micro:a-sim-main-memory))))
	 (real-adr+1 (+ real-adr 1))
	 (real-adr+2 (+ real-adr 2)))
    (+ (%p-ldb (byte 16. 0) real-adr)
       (dpb (%p-ldb (byte 16. 16.) real-adr) (byte 16. 16.) 0)
       (dpb (%p-ldb (byte 16. 0) real-adr+1) (byte 16. 32.) 0)
       (dpb (%p-ldb (byte 16. 16.) real-adr+1) (byte 16. 48.) 0)
       (dpb (%p-ldb (byte 16. 0) real-adr+2) (byte 16. 64.) 0)
       (dpb (%p-ldb (byte 16. 16.) real-adr+2) (byte 16. 80.) 0))))

(defmethod (ucode-sim :write-main-mem) (adr val)
  (when (or (< adr 0)
	    (>= adr *main-memory-end*))
    (ferror nil "bad adr"))
  (let* ((real-adr (si:%make-pointer-unsigned (+ (* 4 adr) (read-register 'micro:a-sim-main-memory))))
	 (real-adr+1 (+ real-adr 1))
	 (real-adr+2 (+ real-adr 2)))
    (%p-dpb (ldb (byte 16. 0) val) (byte 16. 0) real-adr)
    (%p-dpb (ldb (byte 16. 16.) val) (byte 16. 16.) real-adr)
    (%p-dpb (ldb (byte 16. 32.) val) (byte 16. 0) real-adr+1)
    (%p-dpb (ldb (byte 16. 48.) val) (byte 16. 16.) real-adr+1)
    (%p-dpb (ldb (byte 16. 64.) val) (byte 16. 0) real-adr+2)
    (%p-dpb (ldb (byte 16. 80.) val) (byte 16. 16.) real-adr+2))
  val)

(defmethod (ucode-sim :read-frame-mem) (adr)
  (when (or (< adr 0)
	    (>= adr (* 256. 16.)))
    (ferror nil "bad adr"))
  (let ((frame-base (read-register 'micro:a-sim-frames)))
    (%p-contents-as-bignum (+ frame-base adr))))

(defmethod (ucode-sim :write-frame-mem) (adr val)
  (when (or (< adr 0)
	    (>= adr (* 256. 16.)))
    (ferror nil "bad adr"))
  (let ((frame-base (read-register 'micro:a-sim-frames)))
    (setf (%p-contents-as-bignum (+ frame-base adr)) val))
  val)

(defmethod (ucode-sim :read-open-base) ()
  (read-register 'micro:m-sim-open-frame))

(defmethod (ucode-sim :write-open-base) (val)
  (write-register 'micro:m-sim-open-frame val))

(defmethod (ucode-sim :read-open) (adr)
  (when (or (< adr 0)
	    (>= adr 16.))
    (ferror nil "bad adr"))
  (%p-contents-as-bignum (+ (send self :read-open-base) adr)))

(defmethod (ucode-sim :read-open) (adr val)
  (when (or (< adr 0)
	    (>= adr 16.))
    (ferror nil "bad adr"))
  (setf (%p-contents-as-bignum (+ (send self :read-open-base) adr)) val))

(defmethod (ucode-sim :read-active-base) ()
  (read-register 'micro:m-sim-active-frame))

(defmethod (ucode-sim :write-active-base) (val)
  (write-register 'micro:m-sim-active-frame val))

(defmethod (ucode-sim :read-active) (adr)
  (when (or (< adr 0)
	    (>= adr 16.))
    (ferror nil "bad adr"))
  (%p-contents-as-bignum (+ (send self :read-active-base) adr)))

(defmethod (ucode-sim :read-active) (adr val)
  (when (or (< adr 0)
	    (>= adr 16.))
    (ferror nil "bad adr"))
  (setf (%p-contents-as-bignum (+ (send self :read-active-base) adr)) val))

(defmethod (ucode-sim :read-return-base) ()
  (read-register 'micro:m-sim-return-frame))

(defmethod (ucode-sim :write-return-base) (val)
  (write-register 'micro:m-sim-return-frame val))

(defmethod (ucode-sim :read-return) (adr)
  (when (or (< adr 0)
	    (>= adr 16.))
    (ferror nil "bad adr"))
  (%p-contents-as-bignum (+ (send self :read-return-base) adr)))

(defmethod (ucode-sim :read-return) (adr val)
  (when (or (< adr 0)
	    (>= adr 16.))
    (ferror nil "bad adr"))
  (setf (%p-contents-as-bignum (+ (send self :read-return-base) adr)) val))

(defmethod (ucode-sim :read-pc) ()
  (read-register 'micro:a-sim-pc))

(defmethod (ucode-sim :write-pc) (val)
  (setf (read-register 'micro:a-sim-pc) val))

(defmethod (ucode-sim :read-next-pc) ()
  (read-register 'micro:a-sim-next-pc))

(defmethod (ucode-sim :write-next-pc) (val)
  (setf (read-register 'micro:a-sim-next-pc) val))

(defmethod (ucode-sim :read-noop-next-bit) ()
  (read-register 'micro:a-sim-noop-next-bit))

(defmethod (ucode-sim :write-noop-next-bit) (val)
  (setf (read-register 'micro:a-sim-noop-next-bit) val))

