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

;;; 4-Jun-86 21:34:44 DRIVER FOR THE AUSCOM MODEL 8600
;;;                   MULTIBUS CHANNEL INTERFACE
;;; - GEORGE CARRETTE


(DEFUN RESET (&OPTIONAL NOHACK)
  (WHEN (NOT *USER-MULTIBUS*)
    (MESSAGE "SETUP LAMBDA MULTIBUS INTERFACE")
    (SETQ *XPNT* NIL)
    (SETQ *SBUFF* NIL)
    (SETUP-USER-MULTIBUS))
  (OR *XPNT* (SETQ *XPNT* (ALLOCATE-USER-MULTIBUS-DMA-BUFFER (* 2 *WRLEN*))))
  (OR *SBUFF* (SETQ *SBUFF* (allocate-user-multibus-dma-buffer (* 2 *slen*))))
  (MESSAGE "RESET MASTER CONTROL")
  (WRITE MC 0)
  (MESSAGE "CLEARING REGISTER ARRAY")
  (WRITE RA_B[0] 0) (WRITE RA_B[1] 0) (WRITE RA_B[2] 0) (WRITE RA_B[3] 0)
  (WRITE RA_B[4] 0) (WRITE RA_B[5] 0) (WRITE RA_B[6] 0) (WRITE RA_B[7] 0)
  (WRITE RA_B[8] 0) (WRITE RA_B[9] 0) (WRITE RA_B[10] 0) (WRITE RA_B[11] 0)
  (WRITE RA_B[12] 0) (WRITE RA_B[13] 0) (WRITE RA_B[14] 0) (WRITE RA_B[15] 0)
  (UNLESS NOHACK
    (MESSAGE "WRITE MINIT")
    (MODIFY MINIT
	    HSP (BOOLEAN *HSP*)
	    SNGL (BOOLEAN *SNGL*)
	    SWP (BOOLEAN *SWP*)
	    )
    (SETF (ELT *E38BBT* 0) *SUBCH*)		; set subchannel in burst/byte table
    (SETF (ELT *E38NRT* 0) *SUBCH*)		; set subchannel in not ready table
    
    (FILL *VTABLE* :INVALID)
    (FILL *CHSTTABLE* 0) ;; appears to be the only reference to this in the code!
    (MESSAGE "init8259")
    ;; i am not sure what these do. documentation obscure on this point.
    ;; sounds like multibus/8086 considerations we dont care about.
;    (WRITE PIC1 #x13)				; edge trig, intvl=8, single, icw4
;    (WRITE PIC2 #x00)				; 5 bit int vector, bit0-2 don't care
;    (WRITE PIC2 0)
;    (WRITE PIC2 #x0D)				; 8086 set
    
;    (WRITE PIC2 #xFF)				; mask off all 8259 interrupts
    )
  )



(DEFUN MAIN (&OPTIONAL SET-OPTIONS)
  (WHEN SET-OPTIONS
    (OPTIONS))
  (DOLIST (O *OPTIONS*)
    (FORMAT T "~&~10S = ~10S ~A~%"
	    (CAR o) (symeval (car o)) (CADR o)))
    
  (RESET)
  (OR (KLOAD2 *CSBUFF* *BUFFSIZE*)
      (DEVICE-ERROR "ERROR IN DOWNLOAD"))
  (MESSAGE "STARTING MICROPROCESSOR")
  ;(OR (NOT *VERBOSE*) (Y-OR-N-P "START UCODE?") (RETURN-FROM MAIN NIL))
  (MODIFY MC 8XRUN 1)
  (WAIT 1000)
  (MESSAGE "8X305 EXECUTING")
  ;;/* make sure we are offline by issuing an offline command */
  (MODIFY ADDCOM OFLN 1)
  (MODIFY MULCOM GO 1)
  (WAIT 5000)
  (MESSAGE "Initializing valid address RAM")
  (SETF (AREF *VTABLE* *SUBCH*) :VALID)
  (INITVADDR *vTable*)
  (MESSAGE "Zero 256x1 and Status RAM")
  (ZRAMS)
  (MESSAGE "Initializing 256x1 status RAM")
  (init256 *e38cmt* *VLCM* :set)
  (init256 *e38ict* *IMMC* :set)
  (COND (*BRSTFLG*
	 (init256 *e38bbt* *BBR* :set))
	('ELSE
	 (init256 *e38bbt* *BBR* :clear)))
  (init256 *e38nrt* *NTRD* :clear)
  (MESSAGE "Initialization complete.")
  (MAIN-1))


(DEFUN MAIN-1 ()
  (GO-ON)
;  (write pic2 0)       ;;  /* clear interrupt mask */
;  (write pic1 #x0A)
;  (do ()
;      ((zerop (read pic1)))
    ;; issue eoi
;    (write pic1 #x10))
  
  ;; put the 8600 in the idle loop
  (when *as_de*
    (write comsta *de*)
    (write chadd *subch*)
    (modify mulcom endg 1))
  (RESET-MULTIBUS-INTERRUPT-5)
  (modify mulcom go 1)
  (message "Entering interrupt poll loop")
  (interrupt-poll-loop))
  

(defun interrupt-poll-loop ()
  (do-forever
    ;; instead of interrupt, wait for redy.
    ;;(PROCESS-WAIT-MULTIBUS-INTERRUPT-5)
    ;;    (RESET-MULTIBUS-INTERRUPT-5)
    (process-wait "auscom"
		  #'(lambda ()
		      (boolean (read rtnreg redy))))
    ;;(KIH)
    (chint)))

(defun KIH (&aux pint)
  "KIWI Interrupt handler"
  ;; see what kind of interrupt is requested
  (write pic1 #x0C) ;; set poll bit
  (setq pint (read pic1))
  (do ()
      ((NOT (= (LOGAND pint #x80) #x80)))
    (ecase (logand pint #x7)
      (0
       (write pic1 #x0a)
       (write pic1 #x10) ;; issue eoi
       (chint))
      (1
       (message "reset switch")
       (write pic1 #x0a)
       (write pic1 #x10) ;; issue eoi
       (device-error "reset switch"))
      ((2 3 4 5 6 7)
       (write pic1 #x20)
       (message "Unrecognized int from 8600.")
       (write pic1 #x0A)
       (message "Pic IR = ~X" (read pic1))))
    (write pic1 #x0C) ;; set poll bit
    (setq pint (read pic1))))

(defun chint ()
  ;; this is the real thing, find out what caused the irq
  (WHEN *VERBOSE* ;; **** DEBUGGING
    (DE RTNREG))  ;; **** DEBUGGING
  (if (not (boolean (read rtnreg redy)))
      (device-error "Ready bit not set after interrupt occurred."))
  (if (boolean (read rtnreg errb))
      (IF (EQ (eproc) :LOOP) (RETURN-FROM CHINT NIL)))
  (cond ((boolean (read rtnreg wrcp))
	 (message "write complete")
	 ;; get buffer size,  odd/even
	 (setq *xlen* (- *WRLEN* (dpb (read bfszu) (byte 8 8) (read bfszl))))
	 (cond ((boolean (read err1 odby))
		(setq *xodd* t)
		(message "length = ~D bytes" (1- (* *xlen* 2))))
	       ('else
		(setq *xodd* nil)
		(message "length = ~D bytes" (* *xlen* 2))))
	 (PROCESS-DATA (MBU:USER-MULTIBUS-DMA-BUFFER.ARRAY *XPNT*)
		       (- (* *XLEN* 2)
			  (IF *XODD* 1 0)))
	 (COND ((READ RTNREG ENDP)		; ****
		(MESSAGE "ALSO ENDING PRESENTED")))	; ****
	 (COND ((READ RTNREG CRCV)		; ****
		(MESSAGE "ALSO COMMAND RECEIVED")	; ****
		(PCOM))				; ****
	       ('ELSE				; ****
		;; **** DONT set up DE status
		;; **** (write comsta *de*)
		;; **** (write chadd *subch*)
		;; **** (modify mulcom go 1 endg 1)
		(MODIFY MULCOM GO 1))))		; ****
	((boolean (read rtnreg rdcp))
	 (cond ((boolean (read rtnreg endp))
		(message "Read complete and ending presented"))
	       ('else
		(message "Read complete")))
	 (message "length = ~D bytes" (* 2 (dpb (read bfszu) (byte 8 8) (read bfszl))))
	 (cond ((boolean (read rtnreg crcv))	; ****
		(message "also command received")	; ****
		(pcom))				; ****
	       ('else				; ****
		;; CE, DE already presented on read, just issue GO
		(modify mulcom go 1))))		; ****
	((boolean (read rtnreg endp))
	 (cond ((boolean (read rtnreg crcv))
		(message "ending presented and command received")
		(pcom))
	       ('else
		(message "Ending presented")
		;; just issue GO
		(modify mulcom go 1))))
	((boolean (read rtnreg crcv))
	 ;; command received,
	 (pcom))
	('else
	 ;; Command not valid or immediate, error
	 (message "Not a valid or immediate command.")
	 (pra)
	 (modify mulcom go 1))))



(DEFUN PRA ()
  (WHEN *VERBOSE*
    (DE)))


(DEFUN PCOM ()
  "process a received command"
  (COND ((= (READ COMSTA) *WRITE*)
	 (MESSAGE "Write command received")
	 ;; set up for a channel write
	 (SETQ *XLEN* *WRLEN*)
	 (SETQ *XODD* NIL)
	 (SDMA *XPNT* *XLEN* *XODD*)
	 ;; issue GO,WRITE plus ending=CE  **** AND ALSO DE
	 (WRITE WDCNT *WCOUNT*)
	 (WRITE COMSTA (LOGIOR *CE* *DE*))			; ****
	 (MODIFY MULCOM GO 1 WRIT 1 ENDG 1))
	((= (READ COMSTA) *READ*)
	 (MESSAGE "Read command received")
	 (CHREAD))
	((= (READ COMSTA) *SENSE*)
	 (MESSAGE "Sense command received")
	 ;; set up for channel read, 24 bytes = 0
	 (SDMA *SBUFF* *SLEN* *SODD*)
	 ;; issue GO,READ plus ending=CE,DE
	 (WRITE WDCNT *WCOUNT*)
	 (WRITE COMSTA (LOGIOR *DE* *CE*))
	 (MODIFY MULCOM GO 1 READ 1 ENDG 1))
	((= (READ COMSTA) *DAER*)
	 (MESSAGE "Read Backward command received")
	 ;; do the same as a read
	 (CHREAD))
	((= (READ COMSTA) *NOP*)
	 ;; shouldn't be here
	 (MESSAGE "Farmer and Cam said we wouldn't get a NOP command, but we did"))
	((MEMQ (READ COMSTA) *E38ICT*)
	 ;; look up command in immediate command table
	 (MESSAGE "Immediate command received ~2X" (READ COMSTA))
	 ;; found immediate command
	 ;; issue GO plus ending=DE
	 (WRITE COMSTA *DE*)
	 (MODIFY MULCOM GO 1 ENDG 1))
	('ELSE
	 (DEVICE-ERROR "Unknown command received ~2X" (READ COMSTA)))))

(DEFVAR *CHREAD-DATA-FUNCTION* NIL "if NIL then loop back data from write, otherwise this returns a string and length")

(defun chread ()
  (when *chread-data-function*
    (multiple-value-bind (string length)
	(funcall *chread-data-function*)
      (multiple-value-bind (q r)
	  (floor length 2)
	(setq *xlen* q)
	(setq *xodd* (if (zerop r) nil t)))
      (copy-array-portion string
			  0 (min length (* 2 *wrlen*))
			  (mbu:user-multibus-dma-buffer.array *xpnt*)
			  0
			  (min length (* 2 *wrlen*)))))
  ;; set up for a channel read
  (sDma *xpnt* *xlen* *xodd*)
  ;; issue GO,READ plus ending = CD,DE
  (write wdcnt *wcount*)
  (write comsta (logior *de* *ce*))
  (modify mulcom go 1 read 1 endg 1))


(defun SDMA (buff len odd)
  ;; odd byte transsfer, or
  ;; even byte transsfer
  (modify minit odd (boolean odd))
  (write bfszl (ldb (byte 8 0) len))
  (write bfszu (ldb (byte 8 8) len))
  (let ((addr (USER-MULTIBUS-DMA-BUFFER.MULTIBUS-ADDRESS BUFF)))
    (write bfadl (ldb (byte 8 0) addr))
    (write bfadm (ldb (byte 8 8) addr))
    (write bfadu (ldb (byte 8 16) addr))))




(defun eproc ()
  (message "         ****error****")
  (when *verbose*
    (describe-errors))
  (cond ((boolean (read err0 chre))
	 (go-on)
	 (MODIFY MULCOM GO 1) ;; THIS ADDED TO CLEAR READY BIT
;	 (write pic1 #x0A)
;	 (write pic1 #x10) ;; issue eoi
	 :LOOP
	 )
	((boolean (read err0 offl))
	 (go-on)
	 (MODIFY MULCOM GO 1) ;; THIS ADDED TO CLEAR READY BIT
;	 (write pic1 #x0A)
;	 (write pic1 #x10) ;; issue eoi
	 :LOOP
	 )
	('else
	 (ferror nil "unrecoverable device error"))))
  

(DEFUN KLOAD2 (addr size &AUX CSADDR P CNT ERROR LSB MSB)
  ;; WRITE THE CONTROL STORE. THEN READ IT BACK TO DOUBLE CHECK.
  (WRITE MC 0) ;RESET 8X305
  (MODIFY MC   ;SET UP FOR DOWNLOAD
	  8XRUN 1
	  DWNLD 1
	  CSRWE 1)
  (SETQ CSADDR 0)
  (SETQ CNT 0)
  (SETQ P ADDR)
  (SETQ LSB (BYTE 8 0)
	MSB (BYTE 8 8))
  (MESSAGE "Loading KIWI control store")
  (DO ()
      ((= CNT SIZE))
    (WHEN (< (CAR P) CSADDR)
      (SETQ CSADDR 0)
      (WRITE MC 0)
      (MODIFY MC 8XRUN 1 DWNLD 1 CSRWE 1))
    (DO ()
	((= (CAR P) CSADDR))
      (INCF CSADDR)
      (MODIFY-MC-STEP))
    (POP P)
    (WRITE DOWNLOAD-REGISTER-LSB (LDB LSB (CAR P)))
    (WRITE DOWNLOAD-REGISTER-MSB (LDB MSB (CAR P)))
    (POP P)
    (INCF CNT 2)
    (INCF CSADDR) 
    (MODIFY-MC-STEP))
  (WRITE MC 0)
  (MODIFY MC DWNLD 1 CSRWE 1)
  (MODIFY MC 8XRUN 1)
  (SETQ CSADDR 0)
  (SETQ CNT 0)
  (SETQ P ADDR)
  (SETQ ERROR NIL)
  (MESSAGE "VERIFY KIWI CONTROL STORE")
  (BLOCK ERROR
    (DO ((VALUE))
	((= CNT SIZE))
      (DO ()
	  ((= (CAR P) CSADDR))
	(MODIFY-MC-STEP)
	(INCF CSADDR))
      (POP P)
      (SETQ VALUE (DPB (READ DOWNLOAD-REGISTER-MSB)
		       MSB
		       (DPB (READ DOWNLOAD-REGISTER-LSB)
			    LSB 0)))
      (COND ((= VALUE (CAR P))
	     (INCF CNT 2)
	     (INCF CSADDR)
	     (POP P)
	     (MODIFY-MC-STEP))
	    ('ELSE
	     (MESSAGE "AT CSADDR = ~D EXPECT ~X GOT ~X" CSADDR (CAR P) VALUE)
	     (RETURN-FROM ERROR (SETQ ERROR T))))))
  (WRITE MC 0)
  (NOT ERROR))


(DEFUN MODIFY-MC-STEP ()
  (MODIFY MC STEP 1)
  ;; the "C" code didnt have this process-wait.
  ;; but the documentation indicates that if the STEP bit reads as 1
  ;; then the load operation has not completed.
  ;; Appears that the operation is fast enough so that
  ;; we always read this as zero. So this is just an error check.
  (PROCESS-WAIT "uload" #'(lambda () (not (boolean (read mc step)))))
  )


(DEFUN WAIT (time)
  (DO ((I TIME (1- I)))
      ((BOOLEAN (READ RTNREG REDY)))
    (WHEN (ZEROP I)
      (DEVICE-ERROR "Timed out waiting for 8x305 to acknowledge"))))

(DEFUN INITVADDR (TABLE)
 "initialize the valid address RAM - table is a list of 256
subchannel address Valid/Invalid values"
 (DOTIMES (COUNT 256)
   (WRITE OFFLINE-BUS-OUT COUNT)
   (ECASE (ELT TABLE COUNT)
     (:VALID
      (MODIFY ADDCOM WRVL 1)
      (MODIFY MULCOM GO 1))
     (:INVALID
      (MODIFY ADDCOM WRIN 1)
      (MODIFY MULCOM GO 1)))
   (WAIT 100))
 ;; there appears to be an error in the C-coded version of this
 ;; function. 	"while (count <= 256)" would have the clause
 ;; executed for count=256, which would extend beyond the end
 ;; of the table. Since I'm not having much luck getting
 ;;  CHSEL to work later, perhaps this clause will fix things:
 ;;(WRITE OFFLINE-BUS-OUT 256)
 ;;(MODIFY ADDCOM WRVL 1)
 ;;(MODIFY MULCOM GO 1)
 ;;(WAIT 100)
 )

(DEFUN ZRAMS ()
  (WRITE COMSTA 0)
  (DOTIMES (COUNT 256)
    (WRITE CHADD COUNT)
    (MODIFY MULCOM GO 1 C256 1)
    (WAIT 100)
    (MODIFY MULCOM GO 1 CSTA 1)
    (WAIT 100)))


(defun init256 (table mask flip)
  (DOLIST (*table table)
    (write chadd *table)
    (WRITED addcom a256 1)
    (WRITED mulcom go 1)
    (wait 100)
    (ecase flip
      (:set
       (write comsta (logior mask (read comsta))))
      (:clear
       (write comsta (logand (lognot mask) (read comsta)))))
    (modify mulcom go 1 c256 1)
    (wait 100)))


(defun GO-ON ()
  (modify addcom onln 1)
  (modify mulcom go 1)
  (wait 8000)
  (do-forever
    (cond ((boolean (read rtnreg onl))
	   (return (message "8600 online")))
	  ('else
	   (DEVICE-ERROR-R "wait forever for online"
			   "8600 failed to go online. RTNREG = #x~X"
			   (read rtnreg))
	   (process-wait "go online" #'(lambda (x)
					 (or (boolean (read rtnreg onl))
					     (send x :listen))))
	   (send terminal-io :tyi-no-hang)))))


(defun describe-errors ()
  (when (boolean (read rtnreg errb))
    (format t "~&RTNREG has ERRB set~%"))
  (dolist (bit (*de 'err0 nil))
    (when (boolean (cadr bit))
      (format t "~&ERR0: ~A~%" (or (get (car bit) 'err0) (car bit)))))
  (dolist (bit (*de 'err1 nil))
    (when (boolean (cadr bit))
      (format t "~&ERR1: ~A~%" (or (get (car bit) 'err1) (car bit))))))


(defprop device-error t :error-reporter)

(defun device-error (message &rest args)
  (if *verbose* (describe-errors))
  (apply 'ferror nil message args))

(defprop device-error-r t :error-reporter)

(defun device-error-r (resume message &rest args)
  (if *verbose* (describe-errors))
  (apply 'cerror resume message args))



(DEFUN MESSAGE (STRING &REST L)
  (WHEN *VERBOSE*
    (FORMAT T "~&")
    (APPLY 'FORMAT T STRING L)
    (FORMAT T "~%")))


(DEFUN OPTIONS ()
  (TV:CHOOSE-VARIABLE-VALUES *OPTIONS*
			     :LABEL "            Choose Device/Diagnostic Options"))


(defvar *chwrite-data-function* nil "A function to receive a STRING and LENGTH")

(DEFUN PROCESS-DATA (ARRAY LENGTH)
  (cond (*chwrite-data-function*
	 (funcall *chwrite-data-function* array length))
	('else
	 (let ((s (SUBSTRING ARRAY 0 LENGTH)))
	   (format t "~&DATA = ~S~%" s)
	   (string-translate-in-place *ebcdic->lispm* s)
	   (FORMAT T "~&EBCDIC-> ~S~%" s)))))
