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

(defun new-proc (frame-size n-frames main-memory-size)
  (let ((proc (make-proc)))
    (setf (proc-frame-size proc) frame-size)
    (setf (proc-n-frames proc) n-frames)

    (setf (proc-frame-free-list proc) (make-array (proc-n-frames proc)))

    (setf (proc-h-active proc) (make-array (proc-n-frames proc)))
    (setf (proc-h-open proc) (make-array (proc-n-frames proc)))
    (setf (proc-h-pc proc) (make-array (proc-n-frames proc)))

    (setf (proc-frames proc) (make-array (* (proc-n-frames proc)
					    (proc-frame-size proc))))
    (setf (proc-main-memory proc) (make-array main-memory-size))

    (setf (proc-l1-map proc) (make-array 4096.))
    (setf (proc-l2-map-control proc) (make-array 4096.))
    (setf (proc-l2-map-physical-page proc) (make-array 4096.))

    (reset-proc proc)
    proc))

(defun reset-proc-all (&optional (proc *proc*))

  (array-initialize (proc-frames proc) 0)

  (array-initialize (proc-main-memory proc) 0)

  (array-initialize (proc-l1-map proc) 0)
  (array-initialize (proc-l2-map-control proc) 0)
  (array-initialize (proc-l2-map-physical-page proc) 0)

  (reset-proc proc)
  )

(defun reset-proc (&optional (proc *proc*))
  (dotimes (i (proc-n-frames proc))
    (setf (aref (proc-frame-free-list proc) i) i))
  (setf (proc-frame-free-list-ptr proc) 0)
    
  (array-initialize (proc-h-active proc) 0)
  (array-initialize (proc-h-open proc) 0)
  (array-initialize (proc-h-pc proc) 0)

  (setf (proc-return-frame proc) (free-frame-pop))
  (let ((frame (free-frame-pop)))
    (setf (proc-active-frame proc) frame)
    (setf (proc-open-frame proc) frame))

  (setf (proc-pc proc) 256.)
  (setf (proc-next-pc proc) (1+ (proc-pc proc)))

  (setf (proc-zero-bit proc) 0)
  (setf (proc-carry-bit proc) 0)
  (setf (proc-sign-bit proc) 0)
  (setf (proc-overflow-bit proc) 0)

  (setf (proc-noop-next-bit proc) 0)

  (setq *need-to-find-register-offsets* t)
  )

(defun run (&optional (check-for-type-in t))
  (do ()
      ((or (and check-for-type-in (read-char-no-hang))
	   (eq (single-step) :halt)))))


(defvar *need-to-find-register-offsets* t)

(defvar *number-of-instructions* 0)

(defun single-step ()
  (incf *number-of-instructions*)
  (let ((inst (aref (proc-main-memory *proc*)
		    (proc-pc *proc*)))
	(noop-this (proc-noop-next-bit *proc*)))
    (when *need-to-find-register-offsets*
      (find-next-register-offsets))
    (setf (proc-pc *proc*) (proc-next-pc *proc*))
    (setq *need-to-find-register-offsets* t)
    (incf (proc-next-pc *proc*))
    (setf (proc-noop-next-bit *proc*) 0)
    (when (zerop noop-this)
      (execute-inst inst))))


(defun execute-inst (inst)
  (select (ldb %%i-opcode inst)
    (%i-op-alu
     (execute-alu-op inst))
    (%i-op-jump
     (execute-jump-op inst))
    (%i-op-sim
     (execute-sim-op inst))
    (%i-op-open
     (execute-open-op inst))
    (%i-op-tail-recursive-open
     (execute-tail-recursive-open-op inst))
    (%i-op-call
     (execute-call-op inst))
    (%i-op-tail-recursive-call
     (execute-tail-recursive-call-op inst))
    (%i-op-return 
     (execute-return-op inst))
    (t
     (ferror nil "unknown op"))))

(defvar *src-1-offset*)
(defvar *src-2-offset*)
(defvar *dest-offset*)

(defun find-next-register-offsets (&aux inst)
  (setq inst (aref (proc-main-memory *proc*) (proc-pc *proc*)))
  (labels ((get-base (base-code)
		     (select base-code
		       (%i-base-open (proc-open-frame *proc*))
		       (%i-base-active (proc-active-frame *proc*))
		       (%i-base-return (proc-return-frame *proc*))
		       (%i-base-global (ldb %%i-immediate inst))
		       (t (ferror nil "unknown base")))))
    (setq *src-1-offset* (+ (* (get-base (ldb %%i-src-1-base inst)) (proc-frame-size *proc*))
			    (ldb %%i-src-1-offset inst)))
    (setq *src-2-offset* (+ (* (get-base (ldb %%i-src-2-base inst)) (proc-frame-size *proc*))
			    (ldb %%i-src-2-offset inst)))
    (setq *dest-offset* (+ (* (get-base (ldb %%i-dest-base inst)) (proc-frame-size *proc*))
			    (ldb %%i-dest-offset inst))))
  (setq *need-to-find-register-offsets* nil))

(defun execute-alu-op (inst)
  (let ((s1 (aref (proc-frames *proc*) *src-1-offset*))
	(s2 (aref (proc-frames *proc*) *src-2-offset*))
	result)
    (select (ldb %%i-aluf inst)
      (%i-aluf-setz
       (setq result 0))
      (%i-aluf-add
       (setq result (+ s1 s2)))
      (%i-aluf-set1 (setq result s1))
      (%i-aluf-sub
       (setq s2 (logxor #xffffffff s2))
       (incf s2)
       (setq result (+ s1 s2)))
      (%i-aluf-src1-minus-1
       (setq result (+ s1 #xffffffff)))
      (t
       (ferror nil "unknown aluf")))
    (setf (proc-carry-bit *proc*) (ldb (byte 1 32.) result))
    (setq result (logand #xffffffff result))
    (setf (proc-zero-bit *proc*) (if (zerop result) 1 0))
    (setf (proc-sign-bit *proc*) (ldb (byte 1 31.) result))
    (setf (proc-overflow-bit *proc*) (logxor (ldb (byte 1 31.) result)
					     (ldb (byte 1 32.) result)))
    (setf (aref (proc-frames *proc*) *dest-offset*) result)
    ))

(defun execute-jump-op (inst)
  (let ((jump-adr (ldb-big %%i-jump-adr inst))
	(n (ldb %%i-jump-n inst))
	(cond (ldb %%i-jump-cond inst))
	(s1 (aref (proc-frames *proc*) *src-1-offset*))
	(s2 (aref (proc-frames *proc*) *src-2-offset*))
	jump-p
	)
    s1 s2
    (select cond
      (%i-jump-cond-unc
       (setq jump-p t))
      (%i-jump-cond-less-than
       (setq jump-p (not (zerop (logior (proc-sign-bit *proc*)
					(proc-overflow-bit *proc*))))))
      (t
       (ferror nil "unknown jump cond")))
    (when jump-p
      (setf (proc-next-pc *proc*) jump-adr)
      (setf (proc-noop-next-bit *proc*) n))))

(defvar *sim-ops* (make-array 10. :fill-pointer 0))

(defun get-sim-op-index (name)
  (dotimes (i (array-active-length *sim-ops*)
	      (progn (array-push-extend *sim-ops* name)
		     (1- (array-active-length *sim-ops*))))
    (when (eq (aref *sim-ops* i) name)
      (return i))))

(defun execute-sim-op (inst)
  (let ((index (ldb %%i-immediate inst)))
    (when (>= index (array-length *sim-ops*))
      (ferror nil "bad sim op - index too big"))
    (when (null (aref *sim-ops* index))
      (ferror nil "unimplemented sim op ~s" index))
    (funcall (aref *sim-ops* index) inst)))

(defun sim-halt (ignore)
  :halt)

(defun next-free-frame ()
  (aref (proc-frame-free-list *proc*) (proc-frame-free-list-ptr *proc*)))

(defun free-frame-pop ()
  (when (> (proc-frame-free-list-ptr *proc*) (proc-n-frames *proc*))
    (ferror nil "out of free frames"))
  (let ((frame (next-free-frame)))
    (incf (proc-frame-free-list-ptr *proc*))
    frame))

(defun free-frame-push (frame)
  (do ((i (proc-frame-free-list-ptr *proc*) (1+ i)))
      ((= i (proc-n-frames *proc*)))
    (when (= frame (aref (proc-frame-free-list *proc*) i))
      (ferror nil "freeing a free frame")))
  
  (let ((new-ptr (decf (proc-frame-free-list-ptr *proc*))))
    (when (< new-ptr 0)
      (ferror nil "free frame list overflow"))
    (setf (aref (proc-frame-free-list *proc*) new-ptr) frame)))
		
    
    
(defun execute-open-op (inst)
  inst
  (psetf (aref (proc-h-active *proc*) (proc-frame-free-list-ptr *proc*)) (proc-active-frame *proc*)
	 (aref (proc-h-open *proc*) (proc-frame-free-list-ptr *proc*)) (proc-open-frame *proc*))
  (psetf (proc-open-frame *proc*) (free-frame-pop)))

(defun execute-tail-recursive-open-op (inst)
  inst
  (let ((open (aref (proc-h-open *proc*) (proc-active-frame *proc*)))
	(active-temp (aref (proc-h-active *proc*) (proc-active-frame *proc*)))
	(pc-temp (aref (proc-h-pc *proc*) (proc-active-frame *proc*))))
    (let ((frame (free-frame-pop)))
      (psetf (aref (proc-h-open *proc*) frame) open
	     (aref (proc-h-active *proc*) frame) active-temp
	     (aref (proc-h-pc *proc*) frame) pc-temp
	     (proc-open-frame *proc*) frame))))

(defvar *number-of-calls* 0)

(defun execute-call-op (inst)
  inst
  (incf *number-of-calls*)
  (when (zerop (mod *number-of-calls* 100.))
    (format t "<~d,~d;~7f>" *number-of-calls* *number-of-instructions*
	    (/ (float *number-of-instructions*) *number-of-calls*)))
  (find-next-register-offsets)
  (setf (aref (proc-h-pc *proc*) (proc-open-frame *proc*)) (proc-pc *proc*))
  (setf (proc-active-frame *proc*) (proc-open-frame *proc*))
  (setf (proc-next-pc *proc*) (ldb-big %%i-jump-adr inst))
  (setf (proc-noop-next-bit *proc*) (ldb %%i-jump-n inst)))

(defun execute-tail-recursive-call-op (inst)
  inst
  (incf *number-of-calls*)
  (when (zerop (mod *number-of-calls* 100.))
    (format t "<~d,~d;~7f>" *number-of-calls* *number-of-instructions*
	    (/ (float *number-of-instructions*) *number-of-calls*)))
  (find-next-register-offsets)
  (let ((old (proc-active-frame *proc*)))
    (setf (proc-active-frame *proc*) (proc-open-frame *proc*))
    (free-frame-push old))
  (setf (proc-next-pc *proc*) (ldb-big %%i-jump-adr inst))
  (setf (proc-noop-next-bit *proc*) (ldb %%i-jump-n inst)))

(defun execute-return-op (inst)
  inst
  (find-next-register-offsets)
  (let ((active (proc-active-frame *proc*)))
    (setf (proc-return-frame *proc*) active)
    (free-frame-push active)
    (psetf (proc-active-frame *proc*) (aref (proc-h-active *proc*) active)
	   (proc-open-frame *proc*) (aref (proc-h-open *proc*) active)
	   (proc-next-pc *proc*) (aref (proc-h-pc *proc*) active))
    (setf (proc-noop-next-bit *proc*) (ldb %%i-jump-n inst))))

(defun execute-recall (inst)
  inst
  (let ((frame (free-frame-pop)))
    (psetf (proc-active-frame *proc*) frame
	   (proc-open-frame *proc*) frame
	   (proc-h-pc *proc*) (proc-next-pc *proc*))
    (setf (proc-pc *proc*) 0)
    (setf (proc-noop-next-bit *proc*) 1)))

	
	   