;;; -*- Mode:LISP; Package:FILE-SYSTEM; Base:8; Readtable:ZL; Fonts:(CPTFONT) -*-
;;
;; Copyright LISP Machine, Inc. 1986
;;   See filename "Copyright" for
;; licensing and release information.


;;; Explorer Parallel Device Stream
;;;
;;;
;;; The parallel port has two registers:
;;;   Parallel Control Register (PCR)
;;;   Parallel Data Register (PDR)

(DefConstant PDR-Addr #xF10000)
(DefConstant PCR-Addr #xF10004)

(DefConstant Parallel-Strobe-Active #x05)
(DefConstant Parallel-Strobe-Inactive #x07)
(DefConstant Parallel-Reset #x02)

(DefConstant Parallel-Port-Not-Busy-Mask #b1100)

(Defun Parallel-Read-Status ()
  (%nubus-read-8 TV:TV-QUAD-SLOT PCR-Addr))

(Defun Parallel-Write-Control (value)
  (%nubus-write-8 TV:TV-QUAD-SLOT PCR-Addr value))

(Defun Parallel-Write-Data (value)
  (%nubus-write-8 TV:TV-QUAD-SLOT PDR-Addr value))


;;; Port not busy or an error has occurred.
(Defun Parallel-Port-Not-Busy ()
  (Let ((control-bits (Parallel-Read-Status)))
    (or (not (ldb-test #o0001 control-bits))
	(ldb-test #o0101 control-bits)
	(not (ldb-test #o0201 control-bits))
;;;	(ldb-test #o0301 control-bits)       ; ignore since it seems to be bogus
	)))

(Defun Parallel-Port-Help (stream ignore ignore)
  (format stream "~&During printing the problem noted above occurred.~
                  ~&Resolve the problem and press any key to continue."))

(Defun Parallel-Port-Exception (exception-print-string)
  (tv:notify nil "Printer Exception: ~s, go to Tiger Operator Window."
	     exception-print-string)
  (fquery '(:type :tyi
	    :choices (:any)
	    :timeout 3600.
	    :help-function parallel-port-help)
	  exception-print-string)
  )

(Defun Parallel-Port-Check-Status ()
  (Let ((control-bits (Parallel-Read-Status)))
    (Cond ((ldb-test #o0101 control-bits)
	   (Parallel-Port-Exception "Printer out of paper "))
	  ((Not (ldb-test #o0201 control-bits))
	   (Parallel-Port-Exception "Printer offline "))
;;;	  ((Not (ldb-test #o0301 control-bits))     ; ignore since it seems to be bogus
;;;	   (Parallel-Port-Exception "Printer fault "))
	   ))
  )



(DefParameter Parallel-Port-Buffer-Size 512.)


(DefFlavor exp-parallel-stream-mixin
	((buffer (make-array Parallel-Port-Buffer-Size :type :art-string)))
	(si:buffered-output-stream)
  (:required-flavors si:output-stream si:character-stream
		     si:basic-buffered-output-stream)
  (:initable-instance-variables)
  (:settable-instance-variables))


(DefMethod (exp-parallel-stream-mixin :new-output-buffer) ()
  (Unless (<= Parallel-Port-Buffer-Size (Array-Length buffer))
    (setq buffer (make-array Parallel-Port-Buffer-Size :type :art-string)))
  (values buffer 0 Parallel-Port-Buffer-Size))


(DefMethod (exp-parallel-stream-mixin :send-output-buffer) (output-buffer new-index)
  (dotimes (idx new-index)
    (do ()
	((= (ldb #o0004 (Parallel-Read-Status))
	    Parallel-Port-Not-Busy-Mask))
      (Process-Wait "Parallel Out" #'Parallel-Port-Not-Busy)
      (Parallel-Port-Check-Status))
    (Parallel-Write-Data (aref output-buffer idx))
    (Parallel-Write-Control Parallel-Strobe-Active)
    (Parallel-Write-Control Parallel-Strobe-Inactive))
  )


(DefMethod (exp-parallel-stream-mixin :discard-output-buffer) (ignore)
  nil)



(DefFlavor exp-parallel-stream () (exp-parallel-stream-mixin
				   si:output-stream
				   si:character-stream
				   si:buffered-output-stream)
  (:documentation :combination "Explorer Parallel Output Stream, no character-set translation"))

(Compile-Flavor-Methods exp-parallel-stream)




;;; Explorer Serial Device Stream
;;;
;;;
;;; The serial port has many (il)logical registers,
;;; but only two physical registers per port:
;;;   Serial Control/Status Register (SCSR)
;;;   Serial Data Register (SDR)

(DefConstant SCSR-A-Addr #xFB0004)
(DefConstant SDR-A-Addr  #xFB000C)
(DefConstant SCSR-B-Addr #xFB0000)
(DefConstant SDR-B-Addr  #xFB0010)

;;; Functions for  referencing (il)logical registers

(Defun Write-Serial-Port-A-Register (reg value)
  (If (zerop reg)
      (%nubus-write-8 tv:tv-quad-slot SCSR-A-Addr value)
    (%nubus-write-8 tv:tv-quad-slot SCSR-A-Addr reg)
    (%nubus-write-8 tv:tv-quad-slot SCSR-A-Addr value)))

(Defun Read-Serial-Port-A-Register (reg)
  (If (zerop reg)
      (%nubus-read-8 tv:tv-quad-slot SCSR-A-Addr)
    (%nubus-write-8 tv:tv-quad-slot SCSR-A-Addr reg)
    (%nubus-read-8 tv:tv-quad-slot SCSR-A-Addr)))

(Defun Write-Serial-Port-B-Register (reg value)
  (If (zerop reg)
      (%nubus-write-8 tv:tv-quad-slot SCSR-B-Addr value)
    (%nubus-write-8 tv:tv-quad-slot SCSR-B-Addr reg)
    (%nubus-write-8 tv:tv-quad-slot SCSR-B-Addr value)))

(Defun Write-Serial-Port-AB-Register (reg value)
  (Write-Serial-Port-A-Register reg value)
  (Unless (or (= reg 2) (= reg 9)) ; unless shared
    (Write-Serial-Port-B-Register reg value)))

(Defun Serial-Port-Error-Bits ()
  (ldb #o0403 (Read-Serial-Port-A-Register 1)))

(Defun Serial-Port-Not-Busy ()	   ; port not busy or an error has occurred
  (ldb-test #o0201 (%Nubus-Read-8 tv:tv-quad-slot SCSR-A-Addr)))

(Defun Serial-Port-Send-Char (char)
  (%nubus-write-8 TV:TV-QUAD-SLOT SDR-A-Addr char))

(Defun Serial-Port-Char-Ready ()
  (ldb-test #o0001 (%Nubus-Read-8 tv:tv-quad-slot SCSR-A-Addr)))

(Defun Serial-Port-Receive-Char ()
  (Let ((error-bits (Serial-Port-Error-Bits)))
    (If (Not (= 0 error-bits))
	(Ferror nil "Error bits on receive: ~16r" error-bits)
      (%nubus-read-8 tv:tv-quad-slot SDR-A-addr)))
  )



(DefParameter Serial-Port-Buffer-Size 512.)

(DefFlavor exp-serial-stream-mixin
	((buffer (make-array Serial-Port-Buffer-Size :type :art-string)))
	(si:buffered-stream)
  (:required-flavors si:output-stream si:character-stream
		     si:basic-buffered-output-stream)
  (:initable-instance-variables)
  (:settable-instance-variables))

(DefMethod (exp-serial-stream-mixin :after :init) (ignore)
  (Write-Serial-Port-AB-Register  9. #xC0)	   ; reset the Z8530
  (Write-Serial-Port-AB-Register  4. #x04)	   ; async 1 stop bit
  (Write-Serial-Port-AB-Register  1. #x12)	   ; enable internal interrupts
  (Write-Serial-Port-AB-Register 11. #x50)	   ; enable internal baud rate clock
  (Write-Serial-Port-AB-Register 12. #x7E)	   ; set baud rate to 9600 (low byte)
  (Write-Serial-Port-AB-Register 13. #x00)	   ; set baud rate to 9600 (high byte)
  (Write-Serial-Port-AB-Register 14. #x63)	   ; disable sync comm and enable baud rate generator
  (Write-Serial-Port-AB-Register 15. #x00)	   ; disable external interrupts
  (Write-Serial-Port-AB-Register  3. #xC1)	   ; enable receiver at 8 bits
  (Write-Serial-Port-AB-Register  5. #xEA)	   ; enable modem control bits
  )


(DefMethod (exp-serial-stream-mixin :new-output-buffer) ()
  (Unless (<= Serial-Port-Buffer-Size (Array-Length buffer))
    (setq buffer (make-array Serial-Port-Buffer-Size :type :art-string)))
  (values buffer 0 Serial-Port-Buffer-Size))

(DefMethod (exp-serial-stream-mixin :send-output-buffer) (output-buffer new-index)
  (dotimes (idx new-index)
    (Process-Wait "Serial Out" #'Serial-Port-Not-Busy)
    (Serial-Port-Send-Char (aref output-buffer idx)))
  )

(DefMethod (exp-serial-stream-mixin :discard-output-buffer) (ignore)
  nil)

(DefMethod (exp-serial-stream-mixin :setup-next-input-buffer) (&optional no-hang-p)
  (Unless (<= Serial-Port-Buffer-Size (Array-Length stream-input-buffer))
    (setq stream-input-buffer (make-array Serial-Port-Buffer-Size :type :art-string)))
  (setq stream-input-index 0)
  (setq stream-input-limit 0)
  (funcall-self :next-input-buffer no-hang-p)
  )

(DefMethod (exp-serial-stream-mixin :next-input-buffer) (&optional no-hang-p)
  (DoTimes (idx (array-length stream-input-buffer))
    (Cond ((Serial-Port-Char-Ready)
	   (aset (Serial-Port-Receive-Char) stream-input-buffer stream-input-limit)
	   (incf stream-input-limit))
	  (no-hang-p (return))
	  (t
	   (Process-Wait "Serial In" #'Serial-Port-Char-Ready)
	   (aset (Serial-Port-Receive-Char) stream-input-buffer stream-input-limit)
	   (incf stream-input-limit))))
  (Values stream-input-buffer stream-input-index stream-input-limit)
  )

(DefMethod (exp-serial-stream-mixin :discard-current-input-buffer) ()
  nil)

(DefMethod (exp-serial-stream-mixin :discard-input-buffer) ()
  NIL)

(DefFlavor exp-serial-stream () (exp-serial-stream-mixin
				 si:bidirectional-stream
				 si:character-stream
				 si:unbuffered-line-input-stream
				 si:buffered-output-stream)
  (:documentation :combination
		  "Explorer Serial Input/Output Stream, no character-set translation"))



(DefMethod (exp-serial-stream :tyi-no-hang) (&optional ignore)
  (Send self :tyi t))


(DefMethod (exp-serial-stream :tyi) (&optional no-hang-p (whostate "Serial In"))
  (Cond ((Serial-Port-Char-Ready)
	 (Serial-Port-Receive-Char))
	(no-hang-p nil)
	(t
	 (Process-Wait whostate #'Serial-Port-Char-Ready)
	 (Serial-Port-Receive-Char)))
  )


(DefFlavor exp-serial-xon-xoff-stream () (exp-serial-stream)
  (:documentation :combination
		  "Explorer Serial Input/Output Stream with XON/XOFF, no character-set translation"))

;;; +++ For some reason the TI-855 printer XON/XOFF characters are many times received
;;; +++ garbled.  If you just pretend to see the character you expected it seems to work.
;;; +++ The *serial-error-list* records these occurances.
(DefVar *serial-error-list* nil)

(DefConstant XOFF #x13)
(DefConstant XON  #x11)


(Defun Serial-Port-Find-XOFF (stream)
  (Let ((char (send stream :tyi-no-hang)))
    (Unless (Null char)
      (Unless (= char XOFF)
	(push (format nil "Serial Stream: XOFF (#x13) expected, received ~16r." char)
	      *serial-error-list*))
      t)
    )
  )

(Defun Serial-Port-Find-XON (stream)
  (Let ((char (send stream :tyi nil "XON")))
    (Unless (= char XON)
      (push (format nil "Serial Stream: XON (#x11) expected, received ~16r." char)
	    *serial-error-list*)))
  )


(DefMethod (exp-serial-xon-xoff-stream :send-output-buffer) (output-buffer new-index)
  (DoTimes (idx new-index)
    (Process-Wait "Serial Out" #'Serial-Port-Not-Busy)
    (When (Serial-Port-Find-XOFF self)
      (Serial-Port-Find-XON self))
    (Serial-Port-Send-Char (aref output-buffer idx)))
  )

(Compile-Flavor-Methods exp-serial-xon-xoff-stream)


(DEFFLAVOR EXP-PRINTER-HOST
	 (DEVICE-NAME)
	 (SI:BASIC-HOST)
  (:GETTABLE-INSTANCE-VARIABLES DEVICE-NAME)
  (:INITABLE-INSTANCE-VARIABLES DEVICE-NAME))

(DEFMETHOD (EXP-PRINTER-HOST :NAME) ()
  DEVICE-NAME)

(DEFMETHOD (EXP-PRINTER-HOST :NAME-AS-FILE-COMPUTER) ()
  DEVICE-NAME)

(DEFMETHOD (EXP-PRINTER-HOST :PATHNAME-HOST-NAMEP) (NAME)
  (STRING-EQUAL NAME DEVICE-NAME))

(DEFMETHOD (EXP-PRINTER-HOST :PATHNAME-FLAVOR) ()
  (VALUES 'EXP-PRINTER-PATHNAME NIL))

(DEFFLAVOR EXP-PRINTER-PATHNAME
	 ()
	 (PATHNAME))

(DEFMETHOD (EXP-PRINTER-PATHNAME :PARSE-NAMESTRING)
	   (HOST-SPECIFIED STRING &OPTIONAL (START 0) END)
  HOST-SPECIFIED
  (VALUES :NO-INTERN (MAKE-INSTANCE 'EXP-PRINTER-FILEHANDLE
				     :NAMESTRING (SUBSTRING STRING START END))))

;NAMESTRING is just for pseudo debugging purposes; Magtape files dont really have names.
(DEFFLAVOR EXP-PRINTER-FILEHANDLE
	 (NAMESTRING
	  (last-instance nil))
	 ()
  (:INITABLE-INSTANCE-VARIABLES NAMESTRING))

(DEFMETHOD (EXP-PRINTER-FILEHANDLE :STRING-FOR-PRINTING) ()
  NAMESTRING)

(DEFMETHOD (EXP-PRINTER-FILEHANDLE :PRINT-SELF) (STREAM PRINDEPTH SLASHIFY-P) PRINDEPTH
  (COND (SLASHIFY-P
	 (SEND STREAM :STRING-OUT "#<")
	 (PRIN1 'EXP-PRINTER-FILEHANDLE STREAM)
	 (FORMAT STREAM " ~S ~O>" NAMESTRING (%POINTER SELF)))
	(T
	 (SEND STREAM :STRING-OUT NAMESTRING))))

;This is a kludge to make the copy-patch-files-of-system work.
(DEFMETHOD (EXP-PRINTER-FILEHANDLE :PATCH-FILE-PATHNAME) (&REST IGNORE)
  "EXP-PRINTER:")

(defmethod (EXP-PRINTER-FILEHANDLE :OPEN) (pathname &key flavor-and-init-options)
  (cond ((null flavor-and-init-options)
	 (setq flavor-and-init-options default-flavor-and-init-options)))
  (let ((flavor (car flavor-and-init-options))
	(init-options (cdr flavor-and-init-options)))
    (cond ((null flavor-and-init-options)
	   pathname)
	  ((eq (car flavor-and-init-options) (type-of last-instance))
	   last-instance)
	  (t
	   (setq last-instance (apply 'make-instance flavor init-options))))))

(DEFUN ADD-EXP-PRINTER-HOST (&OPTIONAL (NAME "EXP-PRINTER"))
  (COND ((NULL (GET-PATHNAME-HOST NAME T))
	 (LET ((HOST (MAKE-INSTANCE 'EXP-PRINTER-HOST :DEVICE-NAME NAME)))
	   (PUSH HOST *PATHNAME-HOST-LIST*)))))

(COMPILE-FLAVOR-METHODS EXP-PRINTER-HOST EXP-PRINTER-PATHNAME EXP-PRINTER-FILEHANDLE)

(ADD-EXP-PRINTER-HOST)
