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

;;;functional sources and destinations are stored at the end of the frame memory

(defconst %func-vma 0)
(defconst %func-vma-start-write 1)
(defconst %func-vma-start-read 2)
(defconst %func-md 3)
(defconst %func-return 4)
(defconst %func-instruction-counter 5)

(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)

(eh:def-ucode-format-error eh:sim-error "Simulator Microcode Error")

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

(defsubst read-register-as-pointer (name)
  (si:%make-pointer-unsigned (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* 2048.)
(defconst *number-of-frames* 256.)
(defconst *pages-of-frames* 16.)
(defconst *pages-of-opcs* 16.)

(defvar *main-memory-array* (si:make-wireable-array (* 4 *main-memory-pages*) 'art-32b nil))
(defvar *frames-array* (si:make-wireable-array (+ *pages-of-frames* 1) 'art-32b nil))
(defvar *free-list-array* (si:make-wireable-array (ceiling *number-of-frames* si:page-size) 'art-32b nil))
(defvar *hram-array* (si:make-wireable-array (ceiling (* 2 *number-of-frames*) si:page-size) 'art-32b nil))
(defvar *opc-array* (si:make-wireable-array *pages-of-opcs* 'art-32b nil))

(defun main-memory-size ()
  (floor (array-length *main-memory-array*) 4))

(defconst *memory-cost* 3)

;;; this should only reset the processor registers - not the memory system
(defun reset-registers ()
  (write-register 'micro:a-sim-main-memory (+ (si:%pointer-unsigned (%pointer *main-memory-array*))
					      (si:array-data-offset *main-memory-array*)))
  (write-register 'micro:a-sim-main-memory-size (main-memory-size))
  (write-register 'micro:a-sim-frames (+ (si:%pointer-unsigned (%pointer *frames-array*))
					 (si:array-data-offset *frames-array*)))
  (write-register 'micro:a-sim-free-list (+ (si:%pointer-unsigned (%pointer *free-list-array*))
					    (si:array-data-offset *free-list-array*)))
  (write-register 'micro:a-sim-hram (+ (si:%pointer-unsigned (%pointer *hram-array*))
				       (si:array-data-offset *hram-array*)))
  (write-register 'micro:a-sim-opc-ram (+ (si:%pointer-unsigned (%pointer *opc-array*))
					  (si:array-data-offset *opc-array*)))
  (write-register 'micro:a-sim-opc-ram-size (array-length *opc-array*))
  (write-register 'micro:a-sim-opc-ram-ptr 0)

  (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-sim-open-frame 0)
  (write-register 'micro:m-sim-active-frame 0)
  (write-register 'micro:m-sim-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)

  (write-register 'micro:a-sim-inst-counter 0)
  (write-register 'micro:a-sim-last-memory-op 0)
  (write-register 'micro:a-sim-src-is-func 0)
  (write-register 'micro:a-sim-memory-cost *memory-cost*)

  (write-register 'micro:a-sim-free-list-ptr 0)

  (let ((free-list-base (read-register-as-pointer 'micro:a-sim-free-list)))
    (dotimes (i *number-of-frames*)
      (setf (%p-contents-as-bignum (%pointer-plus free-list-base i)) i)))

  (let ((first-real-frame (+ *constants-frame-base*
			     (ceiling (array-length *constants*) 16.))))
    (write-register 'micro:m-sim-return-frame first-real-frame)
    (write-register 'micro:m-sim-active-frame (+ first-real-frame 1))
    (write-register 'micro:m-sim-open-frame (+ first-real-frame 1))
    (write-register 'micro:a-sim-free-list-ptr (+ first-real-frame 2)))
  )



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

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

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

(defun check-array (array data-ptr)
  (when (not (eq data-ptr
		 (%pointer-plus (%pointer array)
				(si:array-data-offset array))))
    (ferror nil "not the same array")))

(defmethod (ucode-sim :reset-and-clear) ()
  (reset-registers)
  (let ((main-memory-base (read-register-as-pointer 'micro:a-sim-main-memory)))
    (check-array *main-memory-array* main-memory-base)
    (setf (%p-contents-as-bignum main-memory-base) 0)
    (%blt main-memory-base (%pointer-plus main-memory-base 1) (array-length *main-memory-array*) 1))

  (let ((frames-base (read-register-as-pointer 'micro:a-sim-frames)))
    (check-array *frames-array* frames-base)
    (setf (%p-contents-as-bignum frames-base) 0)
    (%blt frames-base (%pointer-plus frames-base 1) (array-length *frames-array*) 1))

  (let ((free-list-base (read-register-as-pointer 'micro:a-sim-free-list)))
    (check-array *free-list-array* free-list-base)
    (setf (%p-contents-as-bignum free-list-base) 0)
    (%blt free-list-base (%pointer-plus free-list-base 1) (array-length *free-list-array*) 1))

  (let ((hram-base (read-register-as-pointer 'micro:a-sim-hram)))
    (check-array *hram-array* hram-base)
    (setf (%p-contents-as-bignum hram-base) 0)
    (%blt hram-base (%pointer-plus hram-base 1) (array-length *hram-array*) 1))

  (send *proc* :write-main-memory *free-pointer* #o1000)

  )


(defmethod (ucode-sim :read-main-memory) (adr)
  (when (or (< adr 0)
	    (>= adr (floor (array-length *main-memory-array*) 4)))
    (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-memory) (adr val)
  (when (or (< adr 0)
	    (>= adr (floor (array-length *main-memory-array*) 4)))
    (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-raw-hram-low) (index)
  (when (or (< index 0)
	    (> index 256.))
    (ferror nil "index out of range ~s" index))
  (let ((hram-base (read-register-as-pointer 'micro:a-sim-hram)))
    (%p-contents-as-bignum (%pointer-plus hram-base (* 2 index)))))

(defmethod (ucode-sim :read-raw-hram-hi) (index)
  (when (or (< index 0)
	    (> index 256.))
    (ferror nil "index out of range ~s" index))
  (let ((hram-base (read-register-as-pointer 'micro:a-sim-hram)))
    (%p-contents-as-bignum (%pointer-plus hram-base (1+ (* 2 index))))))


(defmethod (ucode-sim :read-h-active) (index)
  (ldb (byte 8 8) (send self :read-raw-hram-low index)))

(defmethod (ucode-sim :read-h-open) (index)
  (ldb (byte 8 0) (send self :read-raw-hram-low index)))

(defmethod (ucode-sim :read-h-pc) (index)
  (send self :read-raw-hram-hi index))

(defmethod (ucode-sim :previous-active) (frame)
  (send self :read-h-active frame))

(defmethod (ucode-sim :previous-open) (frame)
  (send self :read-h-open frame))

(defmethod (ucode-sim :frame-saved-pc) (frame)
  (send self :read-h-pc frame))



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

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

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

(defmethod (ucode-sim :write-open-frame) (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"))
  (send self :read-frames (dpb (send self :read-open-frame) (byte 8 4) adr)))

(defmethod (ucode-sim :write-open) (adr val)
  (when (or (< adr 0)
	    (>= adr 16.))
    (ferror nil "bad adr"))
  (send self :write-frames (dpb (send self :read-open-frame) (byte 8 4) adr) val))

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

(defmethod (ucode-sim :write-active-frame) (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"))
  (send self :read-frames (dpb (send self :read-active-frame) (byte 8 4) adr)))

(defmethod (ucode-sim :write-active) (adr val)
  (when (or (< adr 0)
	    (>= adr 16.))
    (ferror nil "bad adr"))
  (send self :write-frames (dpb (send self :read-active-frame) (byte 8 4) adr) val))

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

(defmethod (ucode-sim :write-return-frame) (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"))
  (send self :read-frames (dpb (send self :read-return-frame) (byte 8 4) adr)))

(defmethod (ucode-sim :write-return) (adr val)
  (when (or (< adr 0)
	    (>= adr 16.))
    (ferror nil "bad adr"))
  (send self :write-frames (dpb (send self :read-return-frame) (byte 8 4) 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))

(defmethod (ucode-sim :read-free-list-ptr) (ignore)
  (read-register 'micro:a-sim-free-list-ptr))

(defmethod (ucode-sim :read-free-list) (adr)
  (let ((base (read-register-as-pointer 'micro:a-sim-free-list)))
    (%p-contents-as-bignum (%pointer-plus base adr))))

(defmethod (ucode-sim :read-functional) (adr)
  (when (or (< adr 0)
	    (> adr 256.))
    (ferror nil "out of range"))
  (let ((frame-base (read-register-as-pointer 'micro:a-sim-frames)))
    (%p-contents-as-bignum (%pointer-plus frame-base (+ adr 4096.)))))


(defmethod (ucode-sim :read-vma) (ignore)
  (let ((frame-base (read-register-as-pointer 'micro:a-sim-frames)))
    (%p-contents-as-bignum (%pointer-plus frame-base (+ 4096. %func-vma)))))

(defmethod (ucode-sim :read-md) (ignore)
  (let ((frame-base (read-register-as-pointer 'micro:a-sim-frames)))
    (%p-contents-as-bignum (%pointer-plus frame-base (+ 4096. %func-md)))))
  
(defmethod (ucode-sim :read-opc) (adr)
  (let ((opc-base (read-register-as-pointer 'micro:a-sim-opc-ram))
	(opc-pointer (read-register 'micro:a-sim-opc-ram-ptr))
	(opc-size (read-register 'micro:a-sim-opc-ram-size)))
    (let ((offset (mod (+ opc-size opc-pointer (- adr)) opc-size)))
      (when (or (< offset 0)
		(>= offset opc-size))
	(ferror nil "bad opc pointer"))
      (%p-contents-as-bignum (%pointer-plus opc-base offset)))))

(defmethod (ucode-sim :check) ()
  (check-array *main-memory-array* (read-register-as-pointer 'micro:a-sim-main-memory))
  (when (not (= (read-register 'micro:a-sim-main-memory-size)
		(main-memory-size)))
    (ferror nil "main memory size bad"))
  (check-array *frames-array* (read-register-as-pointer 'micro:a-sim-frames))
  (check-array *free-list-array* (read-register-as-pointer 'micro:a-sim-free-list))
  (check-array *hram-array* (read-register-as-pointer 'micro:a-sim-hram))
  (check-array *opc-array* (read-register-as-pointer 'micro:a-sim-opc-ram))

  (mapcar #'(lambda (reg)
	      (let ((val (read-register (car reg))))
		(when (or (< val (eval (cadr reg)))
			  (>= val (eval (caddr reg))))
		  (ferror nil "reg out of range ~s" reg))))
	  '((micro:m-sim-src-1-ptr 0 (+ 256. 4096.))
	    (micro:m-sim-src-2-ptr 0 (+ 256. 4096.))
	    (micro:m-sim-dest-ptr 0 (+ 256. 4096.))
	    (micro:m-sim-open-frame 0 256.)
	    (micro:m-sim-active-frame 0 256.)
	    (micro:m-sim-return-frame 0 256.)
  
	    (micro:a-sim-pc 0 (array-length *main-memory-array*))
	    (micro:a-sim-next-pc 0 (array-length *main-memory-array*))

	    (micro:a-sim-opc-ram-ptr 0 (array-length *opc-array*))
	    ))
  t)

(defmethod (ucode-sim :single-step) ()
  (send self :check)
  (write-register 'micro:a-sim-inst-count 1)
  (micro:%sim-step)
  )

(defmethod (ucode-sim :run) (&optional check-for-kbd-input)
  (send self :check)
  (write-register 'micro:a-sim-inst-count -1)
  (do-forever
    (let ((val (micro:%sim-step)))
      (cond ((eq val t)
	     (when (and check-for-kbd-input
		      (read-char-no-hang))
	     (return nil)))
	    (t
	     (ecase val
	       (0 (return nil))			;saw halt bit
	       ))))))

(defvar *last-sim-load-functions* nil)
(defvar *last-sim-load-start* nil)
(defvar *last-sim-load-args* nil)
(defun sim-reload ()
  (format t "~&(SIM-LOAD ~:FUNCTIONS ~s~&~
			   :START ~s~&~
			   :ARGS ~s)~~&"
	  *last-sim-load-functions*
	  *last-sim-load-start*
	  *last-sim-load-args*)
  (sim-load :functions *last-sim-load-functions*
	    :start *last-sim-load-start*
	    :args *last-sim-load-args*))
		

(defun sim-test (func args &aux all-funcs jump-desc)
  (labels ((collector (function &aux x)
	     (push function all-funcs)
	     (cond ((setq x (get function 'sim-program))
		    (dolist (inst x)
		      (cond ((setq jump-desc (getf (cdr inst) '%%i-jump-adr))
			     ;; jump-desc is of the form (jump-target foobar)
			     (ecase (car jump-desc)
			       (local-jump-target)
			       (jump-target
				(when (not (memq (cadr jump-desc) all-funcs))
				  (collector (cadr jump-desc))))))
			    ((setq jump-desc (getf (cdr inst) 'immediate-data))
			     (when (and (eq (car jump-desc) 'function)
					(not (memq (cadr jump-desc) all-funcs)))
			       (collector (cadr jump-desc))))
			    )))
		   (t
		    (ferror nil "~s has no program" function)))))
    (collector func)
    (sim-load :functions all-funcs :start func :args args)))

(defun cold-reset ()
  (send *proc* :reset)
  (>invalidate-cons-cache)
  (send *proc* :write-main-memory *free-pointer* #o1000))

(defun sim-load (&key functions start args)
  (setq *last-sim-load-functions* functions)
  (setq *last-sim-load-start* start)
  (setq *last-sim-load-args* args)
  (send *proc* :reset)
  (clear-symbol-cache)
  (sim-colon-uab t)
  (clear-symbols)
  (>invalidate-cons-cache)
  (let ((*dont-cons* t))
    (let ((adr (send *proc* :read-main-memory *free-pointer*)))
      (dolist (f functions)
	(let ((n-inst (count-instructions f)))
	  (when (> n-inst 256.)
	    (ferror nil "don't know how to load big functions yet"))
	  (let ((words-to-go (- 256. (remainder adr 256.))))
	    (when (< words-to-go n-inst)
	      (dotimes (i words-to-go)
		(send *proc* :write-main-memory (+ adr i) 0))
	      (incf adr words-to-go)))
	  (add-symbol f adr n-inst :inst)
	  (incf adr n-inst)))
      (send *proc* :write-main-memory *free-pointer* adr)
      ))
  (dolist (f functions)
    (store-function-into-main-memory f (symbol-lookup f)))
  (install-constants)
  (let ((pc (symbol-lookup start)))
    (send *proc* :write-pc pc)
    (send *proc* :write-next-pc pc))
  (send *proc* :write-noop-next-bit 1)
  (do ((arg-number 0 (1+ arg-number))
       (arg args (cdr arg)))
      ((null arg))
    (send *proc* :write-active arg-number (car arg)))
  (write-register 'micro:a-sim-inst-counter 0)

  )

(defun read-inst-from-registers ()
  (+ (read-register 'micro:m-sim-inst-0)
     (ash (read-register 'micro:m-sim-inst-1) 32.)
     (ash (read-register 'micro:m-sim-inst-2) 64.)))
