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

;; to-do:
;;   fix P_R and M_R defs
;;   funcs that return node-names lose when name is multiply defined
;; for every node: check that chip-loc and chip-type are consistent

;;;;;;;;;;;;;;;;

;; parse daisy wirelist into list structure

; SIGNAL_NAME   PHYLOC - PIN attrib  NAME   of PART_NM         PATH/PAGE
; AD10            UN69 - 7   pin in  A10    of CY7C171_25      @MEM_BD/NUBUS/7   
; VCC             UA7  - (1) pin bi  B2     of SIP_1K          @PROC_BD/ALU/1   
; VCC             UA29 - B14 supply  VCC    of WTL2264         @PROC_BD/ALU/2   
; 01234567890123456789012345678901234567890123456789012345678901234567890123456789
;           1         2         3         4         5         6         7

; node is (name (connections ...))


;; final merged list of (node-name connection-list)
(defvar wire-list)

;; temporary parsed wire-lists for each board
(defvar proc-wire-list)
(defvar mem-wire-list)

;; structure of each element of connection-list
(defstruct (conn (:type :list))
  chip-loc
  pin-number
  pin-type
  pin-name
  chip-type
  path)

;; list of pal defstructs
(defvar pals)

;; alist of (chip-loc pal-name)
(defvar pal-locs)

;; structure of an element of pals
(defstruct (pal (:type :list))
  name
  type
  comment
  pin-types	;;(pin-number pin-type)
  locations)

;; sorted list of all chip-locations
(defvar all-chips)

;; alist of (chip-loc (x-coord y-coord))
(defvar grid-locs)

;; alist of length and node-name sorted by decreasing length
(defvar lengths)

;;;;

(defvar forms-to-save '(wire-list pals pal-locs grid-locs lengths))

(defun save-all (&optional (dir-path "lm:bobp.k;"))
  (loop for q in forms-to-save
	when (symeval q)
	do (let ((file (string-append dir-path (symbol-name q))))
	     (format t "~&~s:" file)
	     (compiler:fasd-symbol-value file q))))

(defun restore-all (&optional (dir-path "lm:bobp.k;"))
  (loop for q in forms-to-save
	do (let ((file (string-append dir-path (symbol-name q))))
	     (load file))))

;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;

; stuff to parse ascii wire-list

(defun parse-wire-list (s)
  (loop for q = (parse-one-node s)
	until (null q)
	collect q))

(defun parse-one-node (s &aux node-name)
  (and (find-next s)
       (let ((node-list
	       (loop for l = (read-line s nil)
		     until (or (null l) (string= l ""))
		     for (name conn) = (parse-wire l)
		     do (setq node-name name)
		     collect conn)))
	 (format t "~a " node-name)
	 (list
	   node-name
	   node-list))))

(defun parse-one-node (s &aux node-name)
  (and (find-next s)
       (let ((node-list
	       (loop for l = (read-line s nil)
		     until (or (null l) (string= l ""))
		     collect (multiple-value-bind (name conn)
				 (parse-wire l)
			       (setq node-name name)
			       conn))))
	 (format t "~a " node-name)
	 (list
	   node-name
	   node-list))))

(defun find-next (s)
  (loop for l = (read-line s nil)
	unless l
	  (return nil)
	when (string= l "SIGNAL_NAME" :end1 11)
	  (return t)))

(defun parse-wire (l)
  (let ((conn (make-conn))
	(name (intern (get-field l 0 14))))	;name
    (setf (conn-chip-loc conn)
	  (intern (get-field l 16 20)))		;phys-loc
    (setf (conn-pin-number conn)
	  (let ((p (read-from-string (get-field l 23 27))))
	    (if (listp p)			;pin-number
		(car p)
	      p)))
    (setf (conn-pin-type conn)
	  (read-from-string (string-subst-char #\- #\space (get-field l 27 34) nil)))	;pin-type
    (setf (conn-pin-name conn)
	  (intern (get-field l 35 40)))		;pin-name
    (setf (conn-chip-type conn)
	  (intern (get-field l 45 58)))		;chip-type
    (setf (conn-path conn)
	  (intern (string-subst-char #\- #\/ (get-field l 61) nil)))	;path
    (list name conn)))

(defun get-field (l from &optional to)
  (string-trim '(#\space) (substring l from to)))

;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;

; stuff to print from internal format

(defun print-list (&optional (wl wire-list))
  (loop for node in wl
	(print-node node)))

(defun print-by-lengths (lengths)
  (loop for (len name) in lengths
	(print-node-name name)))

(defun print-node-name (n &optional (wl wire-list))
  (cond
    ((symbolp n)
     (loop for node in (find-nodes n wl)
	   (print-node node)))
    (t
     (print-node n))))

(defun print-node (node)
  (format t "~&~a: ~,1,-1f\"" (car node) (compute-length node))
  (loop for conn in (cadr node)
	(format t "~&~4tpin ~d of ~a at ~a; ~a, ~a, ~a"
		      ;;pin 3 of 74F00 at UN42; input, A10, path
		(conn-pin-number conn)
		(conn-chip-type conn)
		(conn-chip-loc conn)
		(conn-pin-type conn)
		(conn-pin-name conn)
		(conn-path conn))
	(let ((pal-loc (assoc (conn-chip-loc conn) pal-locs)))
	  (when pal-loc
	    (format t " (~s)" (cadr pal-loc))))
	(let ((grid-loc (cadr (assoc (conn-chip-loc conn) grid-locs))))
	  (when grid-loc
	    (format t " (~d,~d)" (car grid-loc) (cadr grid-loc))))
	)
  (format t "~&"))

(defun node-names (&optional (wl wire-list))
  (loop for l in wl
	collect (car l)))

(defun name-hack (&optional (wl wire-list))
  (sort
    (loop for (name conn) in wl
	  for n = (substring name 2)
	  unless (string-equal n "xsig" :end1 4)
	  collect (intern n))
    'string-lessp))

(defun name-rev (l)
  (sortcar
    (loop for x in l
	  collect (list (intern (string-reverse (symbol-name x)))
			x))
    'string-lessp))


(defun find-by-length (wl len &optional (pred '=))
  (loop for l in wl
	when (funcall pred (length (cadr l)) len)
	collect l))

(defun chart-length (&optional (wl wire-list))
  (loop for (conns num) in (count-lengths wl)
	(format t "~&~d node~:P with ~d connection~:P" num conns)))

(defun count-lengths (&optional (wl wire-list))
  (sortcar
    (let (ll)
      (loop for (name conn) in wl
	    for len = (length conn)
	    for lp = (assoc len ll)
	    when lp (incf (cadr lp))
	    else (push (list len 1) ll))
      ll)
    #'<
    ))

(defun find-nodes (nodename &optional (wl wire-list))
  "return a list of nodes with this name"
  (loop for q in wl
	when (eq nodename (car q))
	collect q))

(defun find-node-names-for-chip (chip &optional (wl wire-list))
  (loop for node in wl
	append
	(loop for conn in (cadr node)
	      when (eq chip (conn-chip-loc conn))
	      collect (car node))))

(defun find-nodes-for-chip (chip &optional (wl wire-list))
  (loop for node in wl
	append
	(loop for conn in (cadr node)
	      when (eq chip (conn-chip-loc conn))
	      collect node)))

(defun find-node-substring (s &optional (wl wire-list))
  (loop for (name conn) in wl
	when (string-search s name)
	collect name))

(defun flat-conn-list (&optional (wl wire-list))
  (loop for node in wl
	append
	(loop for conn in (cadr node)
	      collect (list (car node) conn))))

(defun all-chips (&optional (wl wire-list))
  (setq all-chips (uniq-sort
		    (loop for node in (flat-conn-list wl)
			  collect (conn-chip-loc (cadr node))))))

;;;;;;;;;;;;;;;;

(defun find-pin-type (pt &optional (wl wire-list))
  (loop for node in wl
	(loop for w in (cadr node)
	      when (eq (conn-pin-type w) pt)
	      (format t "~&~s" w))))

(defun fix-pin-types (&optional (wl wire-list))
  (dolist (node wl)
    (dolist (conn (cadr node))
      (fix-pin-type conn))))

(defvar out-that-should-be-bi '(74ALS534))
(defvar bi-that-should-be-in '(RED_LED))

(defun fix-pin-type (conn)
  (let ((chip-type (conn-chip-type conn))
	(pin-type (conn-pin-type conn)))
    (cond-every
      ((eq chip-type 'conn96)
       (setf (conn-pin-type conn) 'pin-conn))
      ((eq chip-type 'res_10)
       (setf (conn-pin-type conn) 'pin-res))
      ((and (member chip-type out-that-should-be-bi)
	    (eq pin-type 'pin-out))
       (setf (conn-pin-type conn) 'pin-bi))
      ((and (member chip-type bi-that-should-be-in)
	    (eq pin-type 'pin-bi))
       (setf (conn-pin-type conn) 'pin-in))
      )))

;;;;;;;;;;;;;;;;

(defun check-list (&optional (wl wire-list))
  (loop for node in wl
	for err = (check-node node)
	when err
	do
          (format t "~2&~a " err)
	  (print-node node)))

(defvar random-pin-types '(pin-foo pin-led pin-res))

; error if none of output, bi or tri
; error if output and any of output, bi or tri

(defun check-node (node)
  (loop for w in (cadr node)
	for pt = (conn-pin-type w)
	count (eq pt 'pin-in) into pin-in
	count (eq pt 'pin-out) into pin-out
	count (or (eq pt 'pin-tri) (eq pt 'pin-bi)) into pin-bi
	count (eq pt 'supply) into supply
	count (member pt random-pin-types) into pin-foo
	finally
	(when (or (zerop (+ pin-in pin-bi pin-foo supply))
		  (<= (length (cadr node)) 1))
	  (return "No inputs using"))
	(when (and (zerop pin-foo)
		   (zerop pin-out)
		   (zerop pin-bi))
	  (return "No outputs driving"))
	(when (and (zerop pin-foo)
		   (or (> pin-out 2)
		       (and (plusp pin-out)
			    (not (zerop pin-bi)))))
	  (return "Multiple outputs driving"))))

;;;;;;;;;;;;;;;;

(defun uniq (l)
  (loop for q in l
	and for last = nil then q
	when (neq q last)
	collect q))

(defun uniq-sort (l)
  (uniq (sort (copy-list l) 'string-lessp)))

(defun dup (l &optional (acc #'(lambda (x) x)))
  (loop for q in l
	and for last = nil then q
	when (eq (funcall acc q) (funcall acc last))
	  collect (funcall acc q)))

;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;

(defun compare-lists (w1 w2)
  ;;find duplicate signal names in style of merge-sort
  (loop until (or (null w1) (null w2))
	(let ((x (string-compare (caar w1) (caar w2))))
	  (cond
	    ((zerop x)				;w1 = w2
	     (format t "~&~s" (caar w1))
	     (pop w1)
	     (pop w2))
	    ((minusp x)				;w1 < w2
	     (pop w1))
	    (t					;w1 > w2
	     (pop w2))))
	))

(defun hack-names (wl)
  (loop for node in wl
	collect (list (intern (substring (symbol-name (car node)) 2))
		      (car node))))

;;;;;;;;;;;;;;;;

(defun prepend-names (wl prefix)
  (loop for node in wl
	(setf (car node) (intern (string-append prefix (symbol-name (car node)))))))

(defun prepend-locs (wl prefix)
  (loop for (name conn) in (flat-conn-list wl)
	(setf (conn-chip-loc conn)
	      (intern (string-append prefix (symbol-name (conn-chip-loc conn)))))))
	

; splice together a pair of nodes
;   add second node's connections to first node
;   delete second node from list
;   first node retains its name

(defun splice-nodes (n1 n2)
  (rplacd (last (cadr n1)) (cadr n2))
  (setf (cadr n2) nil)
  ;;for now, leave empty n2 in place
  )

(defun flush-empty-nodes (wl)
  (loop for node in wl
	when (cadr node)
	collect node))

; splice together both wirelists
;   find duplicate signal names between wirelists
;   add "p_" or "m_" to start of every chip location (and signal name?)
;   concatenate wirelists
;   splice across connectors

;;;;;;;;;;;;;;;;

; build list of nodes that include connectors p4 and p5
; sort by connector and pin number
; compare node names for each side
; step lists for both boards and splice nodes

(defun make-loc-list (wl loc)
  (sortcar
    (loop for node in wl
	  append
	  (loop for conn in (cadr node)
		when (eq loc (conn-chip-loc conn))
		collect (list (conn-pin-number conn)
			      node)))
    '<))

(defun make-loc-name-list (wl loc)
  (sortcar
    (loop for node in wl
	  append
	  (loop for conn in (cadr node)
		when (eq loc (conn-chip-loc conn))
		collect (list (conn-pin-number conn)
			      (car node))))
    '<))

(defun merge-foo ()
  (loop for q in '(p4a p4b p4c p5a p5b p5c)
	(let ((p (intern (string-append "P_" (symbol-name q))))
	      (q (intern (string-append "M_" (symbol-name q)))))
	  (merge-connectors
	    (make-loc-list proc-wire-list p)
	    (make-loc-list mem-wire-list q)))))

(defun merge-connectors (l1 l2)
  (loop for p1 in l1
	for p2 = (assoc (car p1) l2)
	when p2
	(splice-nodes (cadr p1) (cadr p2))))

;;;;;;;;;;;;;;;;

;; wire-list now has whole wire list;
;; z has (hack-names), that is list of names with prefixes stripped;
;; (dup z) is conflicts.

;; fix conflicts and strip prefixes;
;; then delete conn96 refs from p4, p5 nodes

;; compute max line lengths based on perimeters of min-rectangle for node.

(defun print-connectors (&optional (wire-list wire-list))
 (let ((p "P_P00"))
  (loop for b in '(#\P #\M)
    (loop for i from #\1 to #\5
	(loop for j from #\A to #\C
		(setf (aref p 0) b)
		(setf (aref p 3) i)
		(setf (aref p 4) j)
		(let ((ps (intern p)))
		  (format t "~&~s: ~s" ps (make-loc-name-list wire-list ps))))))))

(defvar wl-ptr nil)

(defun print-inc (wl)
  (loop for ptr on wl
	do
	(print-node (car ptr))
	(setq wl-ptr ptr)))

;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;

; parse PAL pin types

; PAL16R4 B                                           PAL DESIGN SPECIFICATION
; CH1_00                                              KENT HOULT  
; CALL HARDWARE CONTROL PAL #1                        9/09/86
; 
; C_PROC IR48 IR49 IR50 /TRAP1 RD5 RD6 /FRDEST /WOAR GND
; /OE /HP_DEC /HP_CNT H_WE /PREV_INC /PREV_DEC NC17 /RF_CE RF_SEL VCC
; 
; IF(VCC) HP_DEC = /TRAP1 * /IR50 * /IR49 *  IR48     ; OPEN
;                +  TRAP1 * PREV_INC

(defun parse-pal (s)
  (let ((pal (make-pal)))
    (let ((l (read-line s)))			;pal type is first field of first line
      (setf (pal-type pal) (read-from-string l)))
    
    (let ((l (read-line s)))			;pal name is first field of second line
      (setf (pal-name pal) (read-from-string l)))
    
    (setf (pal-comment pal) (read-line s))	;comment is third line
    
    (read-line s)				;discard fourth line
    
    (let* ((l1 (read-line s))			;get pin names from next two lines
	   (l2 (read-line s))
	   (pin-names (parse-pal-pin-names (string-append l1 " " l2 " ")))
	   (out-list (parse-output-pins s)))	;read equations

      (loop for (pin-num pin-name) in pin-names	;make pin-types list
	    (push
	      (list pin-num (if (memq pin-name out-list) 'pin-out 'pin-in))
	      (pal-pin-types pal))))
    pal))

;; make a list of output pins
(defun parse-output-pins (s)
  (loop for l = (read-line s nil)
	while l
	when (string-search-char #\= (substring l 0 (string-search-char #\; l)))
	collect (let ((li (substring l 0 (string-search-char #\= l))))
		  (setq li (substring li (or (string-search-char #\) li) 0)))
		  (intern (string-trim '(#\space #\) #\/ #\: #\=) li)))))

;; make a list of (pin-number pin-name)
(defun parse-pal-pin-names (line)
  (loop for pin from 1
	for sp = (string-search-char #\space line)
	while sp
	collect (let ((field (string-trim '(#\space #\/) (substring line 0 sp))))
		  (list pin (intern field)))
	do (setq line (string-trim '(#\space) (substring line (1+ sp))))))

;;;;;;;;;;;;;;;;

;; make a list of pal locations for each pal name

(defun parse-pal-directory (&optional (dir-path "lm:bobp.k.pals;*.*#>"))
  (setq pals
	(loop for (f) in (cdr (fs:directory-list dir-path))
	      collect (with-open-file (s f)
			(format t "~&~s" (send f :string-for-printing))
			(parse-pal s))))
  (update-pal-locations pals pal-locs)
  nil)

(defun update-pal-locations (&optional (pals pals) (pal-locs pal-locs))
  (loop for (loc name) in pal-locs
	(push loc (pal-locations (assoc name pals)))))

(defun find-pal (name &optional (pals pals))
  (let ((l (loop for pal in pals
		 when (string-search name (pal-name pal))
		 collect pal)))
    (if (= 1 (length l))
	(car l)
      l)))

(defun pals-by-loc (&optional (pals pals))
  (loop for pal in pals
	append
	(loop for loc in (pal-locations pal)
	      collect (list loc pal))))

(defun pal-names-by-loc (&optional (pals pals))
  (loop for pal in pals
	append
	(loop for loc in (pal-locations pal)
	      collect (list loc (pal-name pal)))))

(defun fix-pal-pins (&optional (wl wire-list) (pals pals))
  (let ((pal-locations (pals-by-loc pals)))
    (loop for (name conns) in wl
	  sum
	  (loop for conn in conns
		for (loc pal) = (assoc (conn-chip-loc conn) pal-locations)
		count loc
		when loc
		do
		(let ((new-type (cadr (assoc (conn-pin-number conn) (pal-pin-types pal)))))
;		  (format t "~&~s: changing pin ~d of ~s (~s) at ~s from ~s to ~s"
;			  name (conn-pin-number conn) (pal-name pal) (pal-type pal)
;			  loc (conn-pin-type conn) new-type)
		  (setf (conn-pin-type conn) new-type)
		  )))))

;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;

;next:
; wirelist tracer ...
;  pick a signal name
;    display all connections for that node
;    click on a connection to get menu of other signals connected to that chip
;  pick a chip and display all connections

;;;;;;;;;;;;;;;;

;trace-length measurer
;  for each chip location
;    compute a grid position for the boards hinged by the P4-P5 connectors
;  for each node
;    compute the minimum rectangle that contains all points

(defun make-m-rows ()
  (with-output-to-string (*standard-output*)
    (loop for i from 16 by 14
	  for c from #\a to #\j
	  (format t "(m_~c ~d) " c i))))

(defvar p-row-locs '((p_a 16) (p_b 26) (p_c 36) (p_d 46) (p_e 58) (p_f 70) (p_g 84)
		     (p_h 96) (p_i 106) (p_j 120) (p_k 134) (p_l 148)))

(defvar m-row-locs-raw '((m_a 16) (m_b 30) (m_c 44) (m_d 58) (m_e 72) (m_f 86)
			 (m_g 100) (m_h 114) (m_i 128) (m_j 142)))

(defvar m-row-base (+ 154 148))

(defvar m-row-locs (loop for (name x) in m-row-locs-raw
			 collect (list name (- m-row-base x))))

(defun make-loc-grid ()
  (setq grid-locs
	(append
	  ;; P_A1 .. P_L33
	  (make-grid p-row-locs 1 33)
	  
	  ;; M_A1 .. M_J33
	  (make-grid m-row-locs 1 33)
	  
	  ;; M_B27A M_B27B M_B27C M_F16A P_H13A
	  (make-grid-aux (assoc 'm_b m-row-locs) 27 #\A #\C)
	  (make-grid-aux (assoc 'm_f m-row-locs) 16 #\A #\A)
	  (make-grid-aux (assoc 'p_h p-row-locs) 13 #\A #\A)

	  ;; M_SIM0 .. M_SIMF
	  (make-sim 'm_sim 0 15)
	  
	  ;; P_R1, M_R1..M_R6
	  (make-simple 'p_r 1 1 nil nil)
	  (make-simple 'm_r 1 6 nil nil)
	  
	  ;; M_LED1 .. M_LED4
	  (make-simple 'm_led 1 4 (- m-row-base 140) 130)
	  
	  ;; P_P1A..C etc.
	  (make-conn 'p_p5 154 30)
	  (make-conn 'p_p4 154 100)
	  (make-conn 'p_p1 0 110)
	  (make-conn 'm_p5 (- m-row-base 146) 30)
	  (make-conn 'm_p4 (- m-row-base 146) 100)
	  (make-conn 'm_p1 m-row-base 110)
	  ))
  nil)

(defun make-loc (name x y)
  `(,(intern name) (,x ,y)))

(defun make-grid (row-list start-col end-col)
  (loop for (row-name x) in row-list
	append
	(loop for col from start-col to end-col
	      collect (make-loc (format nil "~a~d" row-name col)
				x
				(* (1- col) 4)))))

(defun make-grid-aux (name-x col from-c to-c)
  (loop for c from from-c to to-c
	collect (make-loc (format nil "~a~d~c" (car name-x) col c)
			  (cadr name-x)
			  (* (1- col) 4))))

(defun make-conn (name x y)
  (loop for row from #\A to #\C
	collect (make-loc (format nil "~a~c" name row)
			  x
			  y)))

(defun make-simple (name first last x y)
  (loop for n from first to last
	collect (make-loc (format nil "~a~d" name n)
			  x
			  y)))

(defun make-sim (name first last)
  (loop for sim from first to last
	collect (make-loc (format nil "~a~x" name sim)
			  (- m-row-base 3 (* sim 3))
			  32)))

;;;;;;;;;;;;;;;;

(defun compute-length (node &optional (verbose-p nil))
  (multiple-value-bind (x-min y-min x-max y-max)
      (loop for conn in (cadr node)
	    for (loc (x y)) = (assoc (conn-chip-loc conn) grid-locs)
	    when x
	      minimize x into x-min
	    when y
	      minimize y into y-min
	    when x
	      maximize x into x-max
	    when y
	      maximize y into y-max
	    unless loc
	      (format t "~&~s not defined for ~a" (conn-chip-loc conn) (car node))
	    finally
	      (return (values x-min y-min x-max y-max)))
    (when verbose-p
      (format t "~&~s: x: ~d-~d, y: ~d-~d" (car node) x-min x-max y-min y-max))
    (+ (abs (- x-min x-max))
       (abs (- y-min y-max)))))

(defun len-errs ()
  (loop for node in wire-list
	append (length-error node)))

(defun length-error (node)
  (loop for conn in (cadr node)
	unless (assoc (conn-chip-loc conn) grid-locs)
	collect node))

(defun compute-lengths (&optional (wl wire-list))
  (setq lengths
	(sortcar
	  (loop for node in wl
		collect (list (compute-length node) (car node)))
	  '>))
  nil)

(defun print-len-greater ()
  (loop for i from 0 to 33
	(format t "~&~d longer than ~f" (length (len-compare #'> (* i 10))) i)))

(defun len-compare (func lim)
  (loop for (len name) in lengths
	when (funcall func len lim)
	collect name))

(defun find-by-loc (loc)
  (uniq
    (sort
      (loop for (name conns) in wire-list
	    append
	    (loop for conn in conns
		  when (eq loc (conn-chip-loc conn))
		  collect name))
      'string-lessp)))

(defun find-undef (undef-list)
  (loop for (name conns) in wire-list
	append
	(loop for conn in conns
	      when (memq (conn-chip-loc conn) undef-list)
	      collect name)))

(defun change-loc (old new)
  (loop for (name conns) in wire-list
	(loop for conn in conns
	      when (eq old (conn-chip-loc conn))
	      do
	      (format t "~&~s:" name)
	      (setf (conn-chip-loc conn) new))))

