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


(defvar *instructions* '())

(defconstant *tag-package* (or (find-package 'tag)
			       (make-package 'tag)))

(defun get-tag (node)
  (let ((var (cond ((lambda-node? node)
		    (lambda-self-var node))
		   ((reference-node? node)
		    (reference-variable node))
		   (t (bug "Bad tag")))))		 
    (intern (variable-unique-name var) *tag-package*)))

(defun emit-template (n1 n2)
  (let ((tag (get-tag n1)))
    (debug :emit (format t "~&~a" tag))
    (push tag *instructions*)))

(defun emit-tag (tag)
  (setq tag (get-tag tag))
  (debug :emit (format t "~&~a" tag))
  (push tag *instructions*))

(defun emit-comment (string)
  (push `(COMMENT ,(format nil "~&;~a" string)) *instructions*))

(defun emit-jump (node)
  (emit 'K:JUMP (get-tag node)))


(defconstant *inverse-conditions*
	     `((K:BR-EQUAL . K:BR-NOT-EQUAL)
	       (K:BR-NOT-EQUAL . K:BR-EQUAL)))

(defun inverse-cond (cond)
  (cdr (assoc cond *inverse-conditions*)))

(defun emit-inverse-test (cond)
  (emit 'K:TEST (inverse-cond cond)))

(defun emit-test (cond)
  (emit 'K:TEST cond))

(defun emit-branch (node)
  (emit 'K:BRANCH (get-tag node)))

(defun emit-alu (op dest left right)
  (emit 'K:ALU op dest left right))

;;; should be K: 
(defconstant active-regs '(K:A0 K:A1 K:A2 K:A3 K:A4 K:A5 K:A6 K:A7 K:A8
			   K:A9 K:A10 K:A11 K:A12 K:A13 K:A14 K:A15))
(defconstant open-regs '(K:O0 K:O1 K:O2 K:O3 K:O4 K:O5 K:O6 K:O7 K:O8
 			 K:O9 K:O10 K:O11 K:O12 K:O13 K:O14 K:O15))
(defconstant return-regs '(K:R0 K:R1 K:R2 K:R3 K:R4 K:R5 K:R6 K:R7 K:R8
			   K:R9 K:R10 K:R11 K:R12 K:R13 K:R14 K:R15))
(defconstant *all-regs* (append active-regs open-regs return-regs '(K:RETURN K:P)))

(defun hack-operands (ops)
  (let ((new-ops '()))
    (dolist (op ops)
      (push
	(cond ((integerp op)
	       (nth op *all-regs*))
	      ((and (consp op) (integerp (car op)) (integerp (cdr op)))
	       (cons (nth (car op) *all-regs*) (cdr op)))
	      ((literal-node? op)
	       (list 'quote (leaf-value op)))
	      ((reference-node? op)
	       (variable-name (leaf-value op)))
	      ((lambda-node? op)
	       (variable-unique-name (lambda-self-var op)))
	      (t op))
	new-ops))
    (nreverse new-ops)))

;(defun emit (op &rest operands)
;  (format t "~&  ~a" (cons op (hack-operands operands))))


(defun ncp (form)
  (setq *instructions* '())
  (nc form)
  (let ((insts (post-process *instructions*)))
    (debug :post (print-instructions insts))
    insts))

(defun comp (form)
  (case (car form)
    (DEFUN `(sim:defkfun ,(cadr form) ,(caddr form)
	      . ,(cddr (ncp form))))
    (t (error "KCOMP doesn't grok ~a" (car form)))))

(defun kcomp (form)
  (eval (comp form))
  (compile (cadr form)))

(defun emit (op &rest operands)
  (let ((i (cons op (hack-operands operands))))
    (debug :emit (format t "~&  ~a" i))
    (push  i *instructions*)))

(defun i-type (i)
  (if (consp i)
      (car i)
    'LABEL))

(defun post-process (instructions)
  (do ((instructions (cdr instructions) (cdr instructions))
       (i (car instructions) (car instructions))
       (new-instructions '()))
      ((null instructions) (push i new-instructions))
    (let* ((i-type (i-type i))
	   (prev-i (car instructions))
	   (prev-i-type (i-type prev-i)))
      (push (case i-type
	      (K:KCALL (case prev-i-type
		      (K:MOVE (pop instructions)
			    (case (i-type (car instructions))
			      (K:KOPEN
			       (pop instructions)
			       (case (i-type (car instructions))
;				 (K:KOPEN
;				  (pop instructions)
;				  `(K:OPEN-CALL ,(second i) ,(third i) ,(list 'K:NEW-OPEN (fourth i)) ,(cdr prev-i)
;						. ,(cddddr i)))
				 (t `(K:OPEN-CALL ,(second i) ,(third i) ,(fourth i) ,(cdr prev-i) . ,(cddddr i)))))
			      (t 
			       `(K:KCALL ,(second i) ,(third i) ,(fourth i) ,(cdr prev-i) . ,(cddddr i)))))
		      (K:KOPEN (pop instructions)
			    `(K:OPEN-CALL ,(second i) ,(third i) ,(fourth i) () . ,(cddddr i)))
		      (t `(K:KCALL ,(second i) ,(third i) ,(fourth i) () . ,(cddddr i)))))
	      ;; gack
	      (K:TAIL-CALL (case prev-i-type
		      (K:MOVE (pop instructions)
			    (case (i-type (car instructions))
			      (K:TAIL-OPEN
			       (pop instructions)
			       (case (i-type (car instructions))
;				 (K:KOPEN
;				  (pop instructions)
;				  `(K:OPEN-TAIL-CALL ,prev-i (K:NEW-OPEN ,(cadr i)) . ,(cddr i)))
				 (t `(K:OPEN-TAIL-CALL ,(second i) ,(third i) ,(cdr prev-i) . ,(cddddr i)))))
			      (t 
			       `(K:TAIL-CALL ,(second i) ,(third i) ,(cdr prev-i) . ,(cddddr i)))))
		      (K:TAIL-OPEN (pop instructions)
			    `(K:OPEN-TAIL-CALL () ,(cdr i)))
		      (t `(K:TAIL-CALL . ,(cdr i)))))
	      (K:RETURN (case prev-i-type
			(K:MOVE (pop instructions)
			      (append prev-i (list 'K:CH-RETURN)))))
	      ((K:MOVE K:ALU)
	       (case prev-i-type
		 (K:KOPEN (pop instructions)
		       (append i (list 'K:CH-OPEN)))
		 (K:TAIL-OPEN (pop instructions)
		       (append i (list 'K:CH-TAIL-OPEN)))
		 (t i)))
	      (K:KOPEN (case prev-i-type
;		      (K:KOPEN (pop instructions)
;			    ;; find the call for the second open
;			    (let ((call (car (member-if #'(lambda (i-type) (or (eq i-type 'K:KCALL)
;									       (eq i-type 'K:OPEN-CALL)))
;							new-instructions :key #'car))))
;			      ;; munge it to do a new-open
;			      (rplaca (cddr call) (list 'K:NEW-OPEN (caddr call)))
;			      i))
;		      (K:TAIL-OPEN (pop instructions)
;			    ;; find the call for the second open
;			    (let ((call (car (member-if #'(lambda (i-type) (or (eq i-type 'K:KCALL)
;									       (eq i-type 'K:OPEN-CALL)))
;							new-instructions :key #'car))))
;			      ;; munge it to do a new-open
;			      (rplaca (cddr call) (list 'K:NEW-TAIL-OPEN (caddr call)))
;			      i))
		      (t i)))
	      (t i))
	    new-instructions))))


(defun print-instructions (insts)
  (dolist (i insts)
    (cond ((consp i)
	   (format t "~&   ~a" i))
	  (t (format t "~&~a" i)))))


