;;; -*- Mode:LISP; Package:SYSTEM-INTERNALS; Base:8; Readtable:ZL -*-

;(compiler:defmic %io-cmd-run 1150 (io-cmd) t)

(mapcar #'(lambda (x) (putprop x t 'special)) nupi-disk-rq-halfwords)
(assign-values nupi-disk-rq-halfwords)

(compiler:make-obsolete wire-page "use %wire-page with different conventions")
(compiler:make-obsolete unwire-page "use %unwire-page with different conventions")
(compiler:make-obsolete wire-words "use %wire-words with different conventions")
(compiler:make-obsolete unwire-words "use %unwire-words with different conventions")
(compiler:make-obsolete wire-array "use %wire-structure with different conventions")
(compiler:make-obsolete unwire-array "use %unwire-structure with different conventions")
(compiler:make-obsolete wire-structure "use %wire-structure with different conventions")
(compiler:make-obsolete unwire-structure "use %unwire-structure with different conventions")
(compiler:make-obsolete wire-area "use %wire-area")
(compiler:make-obsolete unwire-area "use %unwire-area")

;;Strategy:
;;Call %CHANGE-PAGE-STATUS to set the swap status to wired.  If the page
;;is swapped out, then this will return NIL.  If that happens, we have to
;;get the page into the PAGE-HASH-TABLE somehow.  If DONT-BOTHER-PAGING-IN
;;is set, we don't want to fault it in, so allocate a physical page, and
;;use %PAGE-IN to enter it into the PAGE-HASH-TABLE.  Otherwise, just 
;;touch it.
(defun %wire-page (address &optional (wire-p t) set-modified dont-bother-paging-in)
  (cond ((null wire-p)
	 (%unwire-page address))
	(t
	 (without-interrupts
	   (loop until (%change-page-status address %pht-swap-status-wired nil)
		 finally (if set-modified
			     (%p-dpb (%p-ldb (byte 1 0) address) (byte 1 0) address))
		 when dont-bother-paging-in
		 ;;"create" an uninitialized page
		   do (let* ((vpage (lsh address -8.))
			     (pfn (if (ldb-test %%processor-switch-fast-cache-mode
						(%processor-switches nil))
				      (%findcore-hexadec (logand #o17 vpage))
				    (%findcore))))
			(cond ((null (%page-in pfn vpage))
			       ;;it got paged in behind our back, just free the physical page
			       (%create-physical-page (lsh pfn 8)))))
		 else
		 ;;Touch page to get it in
		   do (%p-ldb (byte 1 0) (%pointer address)))))))


(defun %unwire-page (address)
  (%change-page-status address %pht-swap-status-normal nil))

(defun %wire-words (from size &optional (wire-p t) set-modified dont-bother-paging-in)
  (if (not (zerop (logand (%pointer from) (1- page-size))))
      (ferror nil "must wire from a page boundary"))
  (if (not (zerop (logand size (1- page-size))))
      (ferror nil "must wire a multiple of the page size"))

  (do ((adr (%pointer from) (%pointer-plus adr page-size))
       (pages-to-go (floor size page-size) (1- pages-to-go)))
      ((zerop pages-to-go))
    (%wire-page adr wire-p set-modified dont-bother-paging-in)))

(defun %unwire-words (from size)
  (%wire-words from size nil))

(defun %wire-structure (obj &optional set-modified dont-bother-paging-in)
  (setq obj (%find-structure-leader (follow-structure-forwarding obj)))
  (without-interrupts
    (%wire-words obj
		 (%structure-total-size obj)
		 t set-modified dont-bother-paging-in)))

(defun %unwire-structure (obj)
  (setq obj (%find-structure-leader (follow-structure-forwarding obj)))
  (without-interrupts
    (%unwire-words obj (%structure-total-size obj))))

;;; convert from explorer logical unit to physical unit
(defun convert-logical-unit-to-physical-unit (logical-unit)
  (dpb (ldb #o0103 logical-unit) #o0303 (ldb #o0001 logical-unit)))

(defstruct (io-cmd
	     (:type :array-leader)
	     )
  ;;these first four are the same as RQB's
  io-cmd-n-halfwords
  io-cmd-n-pages
  io-cmd-buffer
  io-cmd-8-bit-buffer

  io-cmd-command-block
  io-cmd-first-command-block-address
  io-cmd-first-data-address
  )

;** make sure not to create an io-cmd that has too many pages for the scatter list
(defun create-io-cmd (ignore n-pages)
  (let* ((n-half-words-for-user-data (* 2 n-pages page-size))
	 (sample-io-cmd (make-io-cmd))
	 (io-cmd-basic-size (%structure-total-size (%find-structure-leader sample-io-cmd)))
	 (n-half-words-left-on-first-page (* 2 (- page-size io-cmd-basic-size)))
	 io-cmd io-cmd-leader-pointer
	 )
    (cond ((> (+ n-half-words-for-user-data n-half-words-left-on-first-page)
	      %array-max-short-index-length)
	   (incf io-cmd-basic-size)
	   (decf n-half-words-left-on-first-page 2)
	   (cond ((<= (+ n-half-words-for-user-data n-half-words-left-on-first-page)
		      %array-max-short-index-length)
		  (ferror nil "pathalogical io-cmd size -- can't fit")))))
    (setq io-cmd (make-array (+ n-half-words-for-user-data
				n-half-words-left-on-first-page)
			     :type art-16b
			     :leader-length (array-leader-length sample-io-cmd)
			     :area disk-buffer-area
			     ))
    (setq io-cmd-leader-pointer (%find-structure-leader io-cmd))

    (cond ((not (zerop (logand (1- page-size) (%pointer io-cmd-leader-pointer))))
	   (ferror nil "io-cmd doesn't start at beginning of page")))
    (cond ((not (zerop (remainder (%structure-total-size io-cmd-leader-pointer) page-size)))
	   (ferror nil "io-cmd not multiple of page size")))

    (setf (io-cmd-n-pages io-cmd) n-pages)
    (setf (io-cmd-buffer io-cmd)
	  (make-array n-half-words-for-user-data
		      :type art-16b
		      :displaced-to io-cmd
		      :displaced-index-offset n-half-words-left-on-first-page
		      ))
    (setf (io-cmd-8-bit-buffer io-cmd)
	  (make-array (* 2 n-half-words-for-user-data)
		      :type art-string
		      :displaced-to io-cmd
		      :displaced-index-offset (* 2 n-half-words-left-on-first-page)
		      ))
    (setf (io-cmd-command-block io-cmd)
	  (make-array n-half-words-left-on-first-page
		      :type art-16b
		      :displaced-to io-cmd))

    (setf (io-cmd-first-command-block-address io-cmd)
	  (%pointer-plus io-cmd-leader-pointer io-cmd-basic-size))
    (setf (io-cmd-first-data-address io-cmd)
	  (%pointer-plus io-cmd-leader-pointer page-size))
	  
    io-cmd
    ))

(defresource io-cmd (n-pages)
  :constructor create-io-cmd
  :free-list-size 50.)

(defun get-io-cmd (&optional (n-pages 1))
  (let ((default-cons-area working-storage-area)) ;avoid lossage on consing in 
						  ; resource stuff. (specifically parametizer)
    (allocate-resource 'io-cmd n-pages)))

(defun return-io-cmd (io-cmd)
  (cond ((not (null io-cmd))
	 (%unwire-structure io-cmd)
	 (deallocate-resource 'io-cmd io-cmd)))
  nil)

(defun vadr-to-nubus-phys (vadr)
  (dpb (%nubus-physical-address (ldb (byte 22. 8) (%physical-address vadr)))
       (byte 22. 10.)
       (dpb (%pointer vadr)
	    (byte 8 2)
	    0)))

;;; +++ is this used? does the user want a logical of physical unit number?
;;; +++ 3/18/86 ptm
(defmacro nupi-unit (io-cmd)
  `(aref (io-cmd-command-block ,io-cmd) %nupi-disk-unit))

(defmacro nupi-command (io-cmd)
  `(aref (io-cmd-command-block ,io-cmd) %nupi-disk-command))

(defmacro nupi-disk-status (io-cmd)
  `(dpb (aref (io-cmd-command-block ,io-cmd) %nupi-disk-status-hi)
	(byte 16. 16.)
	(aref (io-cmd-command-block ,io-cmd) %nupi-disk-status-lo)))

(defmacro set-nupi-disk-status (io-cmd val)
  `(progn (aset (ldb (byte 16. 0) ,val) (io-cmd-command-block ,io-cmd) %nupi-disk-status-lo)
	  (aset (ldb (byte 16. 16.) ,val) (io-cmd-command-block ,io-cmd) %nupi-disk-status-hi)))

(defsetf nupi-disk-status set-nupi-disk-status)

(defmacro nupi-list-pointer (io-cmd)
  `(dpb (aref (io-cmd-command-block ,io-cmd) %nupi-disk-list-pointer-hi)
	(byte 16. 16.)
	(aref (io-cmd-command-block ,io-cmd) %nupi-disk-list-pointer-lo)))

(defmacro set-nupi-disk-list-pointer (io-cmd val)
  `(progn (aset (ldb (byte 16. 0) ,val) (io-cmd-command-block ,io-cmd) %nupi-disk-list-pointer-lo)
	  (aset (ldb (byte 16. 16.) ,val) (io-cmd-command-block ,io-cmd) %nupi-disk-list-pointer-hi)))

(defsetf nupi-disk-list-pointer set-nupi-disk-list-pointer)

(defmacro nupi-disk-transfer-count (io-cmd)
  `(dpb (aref (io-cmd-command-block ,io-cmd) %nupi-disk-transfer-count-hi)
	(byte 16. 16.)
	(aref (io-cmd-command-block ,io-cmd) %nupi-disk-transfer-count-lo)))

(defmacro set-nupi-disk-transfer-count (io-cmd val)
  `(progn (aset (ldb (byte 16. 0) ,val) (io-cmd-command-block ,io-cmd) %nupi-disk-transfer-count-lo)
	  (aset (ldb (byte 16. 16.) ,val) (io-cmd-command-block ,io-cmd) %nupi-disk-transfer-count-hi)))

(defsetf nupi-disk-transfer-count set-nupi-disk-transfer-count)

(defmacro nupi-disk-logical-block (io-cmd)
  `(dpb (aref (io-cmd-command-block ,io-cmd) %nupi-disk-logical-block-hi)
	(byte 16. 16.)
	(aref (io-cmd-command-block ,io-cmd) %nupi-disk-logical-block-lo)))

(defmacro set-nupi-disk-logical-block (io-cmd val)
  `(progn (aset (ldb (byte 16. 0) ,val) (io-cmd-command-block ,io-cmd) %nupi-disk-logical-block-lo)
	  (aset (ldb (byte 16. 16.) ,val) (io-cmd-command-block ,io-cmd) %nupi-disk-logical-block-hi)))

(defsetf nupi-disk-logical-block set-nupi-disk-logical-block)


(defmacro nupi-disk-interrupt-address (io-cmd)
  `(dpb (aref (io-cmd-command-block ,io-cmd) %nupi-disk-interrupt-address-hi)
	(byte 16. 16.)
	(aref (io-cmd-command-block ,io-cmd) %nupi-disk-interrupt-address-lo)))

(defmacro set-nupi-disk-interrupt-address (io-cmd val)
  `(progn (aset (ldb (byte 16. 0) ,val) (io-cmd-command-block ,io-cmd) %nupi-disk-interrupt-address-lo)
	  (aset (ldb (byte 16. 16.) ,val) (io-cmd-command-block ,io-cmd) %nupi-disk-interrupt-address-hi)))

(defsetf nupi-disk-interrupt-address set-nupi-disk-interrupt-address)

;io-cmd must be wired before calling this
(defun build-nupi-command (io-cmd phys-unit command byte-count disk-address
			   &optional
			   (scatter-p t)
			   (byte-offset 0)
			   )
  (let ((page-status (%page-status io-cmd)))
    (cond ((null page-status)
	   (ferror nil "io-cmd is swapped out!!"))
	  ((not (= (ldb %%pht1-swap-status-code page-status) %pht-swap-status-wired))
	   (ferror nil "io-cmd is not wired!!"))))

  (if (not (zerop (ldb (byte 10. 0) byte-count)))
      (ferror nil "byte-count must be an even number of pages"))
  (if (not (zerop (ldb (byte 10. 0) byte-offset)))
      (ferror nil "offset must be an even number of pages"))
  (if (> (+ byte-count byte-offset) (* 4 page-size (io-cmd-n-pages io-cmd)))
      (ferror nil "byte-count too big"))

  (array-initialize (io-cmd-command-block io-cmd) 0)

  (aset phys-unit (io-cmd-command-block io-cmd) %nupi-disk-unit)
  (cond ((null scatter-p)
	 (if (not (zerop byte-count))
	     (ferror nil "can't do non-scatter operations with non-zero byte-counts"))
	 (aset (dpb command (byte 8 8) 0) (io-cmd-command-block io-cmd) %nupi-disk-command)
	 (let ((data-phys-adr (vadr-to-nubus-phys (io-cmd-first-data-address io-cmd))))
	   (aset (ldb (byte 16. 0) data-phys-adr) (io-cmd-command-block io-cmd) %nupi-disk-ccw-list-pointer-lo)
	   (aset (ldb (byte 16. 16.) data-phys-adr) (io-cmd-command-block io-cmd) %nupi-disk-ccw-list-pointer-hi)))
	(t
	 (aset (dpb command (byte 8 8) #o100) (io-cmd-command-block io-cmd) %nupi-disk-command)
	 (let ((scatter-phys-adr (vadr-to-nubus-phys (+ (io-cmd-first-command-block-address io-cmd)
							(floor %nupi-disk-ccw-list 2)))))
	   (aset (ldb (byte 16. 0) scatter-phys-adr) (io-cmd-command-block io-cmd) %nupi-disk-ccw-list-pointer-lo)
	   (aset (ldb (byte 16. 16.) scatter-phys-adr) (io-cmd-command-block io-cmd) %nupi-disk-ccw-list-pointer-hi))))
  (aset (ldb (byte 16. 0) byte-count) (io-cmd-command-block io-cmd) %nupi-disk-transfer-count-lo)
  (aset (ldb (byte 16. 16.) byte-count) (io-cmd-command-block io-cmd) %nupi-disk-transfer-count-hi)
  (aset (ldb (byte 16. 0) disk-address) (io-cmd-command-block io-cmd) %nupi-disk-logical-block-lo)
  (aset (ldb (byte 16. 16.) disk-address) (io-cmd-command-block io-cmd) %nupi-disk-logical-block-hi)
  
  (when scatter-p
    (do ((vadr (%pointer-plus (io-cmd-first-data-address io-cmd)
			      (floor byte-offset 4))
	       (%pointer-plus vadr page-size))
	 (scatter-entry %nupi-disk-ccw-list (+ scatter-entry 4))
	 (pages-to-go (floor byte-count 1024.) (1- pages-to-go)))
	((zerop pages-to-go))
      (let ((padr (vadr-to-nubus-phys vadr)))
	(aset (ldb (byte 16. 0) padr) (io-cmd-command-block io-cmd) scatter-entry)
	(aset (ldb (byte 16. 16.) padr) (io-cmd-command-block io-cmd) (+ scatter-entry 1))
	(aset 1024. (io-cmd-command-block io-cmd) (+ scatter-entry 2))
	(aset 0 (io-cmd-command-block io-cmd) (+ scatter-entry 3)))))

  )

(defconst nupi-commands
	  '((#x00 "")
	    (#x41 "formatter set up")
	    (#x81 "NUPI set up")
	    (#x02 "Request device status")
	    (#x42 "Request Formatter Status")
	    (#x82 "Request NUPI Status")
	    (#x10 "Restore Device")
	    (#x11 "Seek")
	    (#x12 "Read")
	    (#x13 "Write")
	    (#x14 "Format")
	    (#x15 "Read to NUPI Buffer")
	    (#x16 "NUPI Buffer to Nubus Transfer")
	    (#x17 "Swap NUPI Buffer and Write")
	    (#x20 "Rewind (tape)")
	    (#x21 "Unload (tape)")
	    (#x22 "Erase (tape)")
	    (#x23 "Space forward (tape)")
	    (#x25 "Write Filemark (tape)")
	    (#x26 "Tape retension")
	    (#x27 "Space forward by filemarks")
	    (#x29 "Skip forward to end of data")
	    (#x2a "Load")
	    (#x2b "Load/unload cmd with bit params")
	    (#x30 "Device selftest")
	    (#x70 "Formatter selftest")
	    (#xb0 "NUPI selftest")
	    (#x71 "SCSI pass through read")
	    (#x72 "SCSI pass through write")
	    ))

(defconst nupi-error-classes '("no error"
			       "self test error"
			       "attention error"
			       "bus related error"
			       "command error"
			       "hardware error"
			       "media error"
			       "reserved error"))

(defconst nupi-controller-errors
	  '((#x61 "nubus time out")
	    (#x62 "nubus bus error")
	    (#x63 "scsi bus parity error")
	    (#x64 "formatter busy")
	    (#x65 "rate error")
	    (#x66 "bus error trap")
	    (#x81 "command aborted")
	    (#x82 "invalid command")
	    (#x83 "invalid parameter")
	    (#x84 "scsi command completed without data transfer")
	    (#x8c "attempted read-and-hold with one pending")
	    (#x8d "attempeted write-and-swap without valid buffer")
	    (#xa1 "illegal interrupt")
	    (#xa2 "scsi function complete without cause")
	    (#xa3 "timeout on NCR 5385 data register full wait")
	    (#xa4 "scsi invalid command interrupt")
	    (#xa6 "hardware error trap")
	    (#xa7 "queue overflow")
	    (#xa8 "address error trap")
	    (#xa9 "illegal instruction error trap")
	    (#xaa "nubus dma locked up")
	    ))

(defconst nupi-device-errors
	  '((#x41 "no selected unit")
	    (#x42 "media not loaded")
	    (#x43 "write protected")
	    (#x44 "offline device now online or media change")
	    (#x46 "temperature fault")
	    (#x47 "invalid media type")
	    (#x48 "status: scsi sense data available")
	    (#x49 "status: tape incorrect length indicate")
	    (#x4a "tape end of media")
	    (#x4b "status: tape incorrect length indicate and end of media")
	    (#x4c "status: tape file mark detected")
	    (#x4d "status: tape incorrect length indicate and file mark detected")
	    (#x4e "status: tape end of media and file mark detected")
	    (#x4f "status: tape incorrect length indicate, end of media, and file mark detected")
	    (#x50 "scsi bus hung needs hardware reset")
	    (#x61 "scsi bus parity error")
	    (#x62 "drive not ready")
	    (#x63 "rate error")
	    (#x64 "invalid scsi interrupt: selected")
	    (#x65 "device offline")
	    (#x66 "invalid scsi testability interrupt")
	    (#x67 "invalid scsi disconnect")
	    (#x68 "invalid mode for scsi status")
	    (#x69 "invalid mode for scsi command byte request")
	    (#x6a "sequence error: scsi completion address")
	    (#x6b "sequence error: scsi requested data")
	    (#x6c "sequence error: dma start/stop address")
	    (#x70 "unknown message received from formatter")
	    (#x71 "invalid mode on scsi message in")
	    (#x72 "excess scsi status")
	    (#x73 "excess scsi command bytes requested")
	    (#x74 "expected scsi restore message but note received")
	    (#x75 "reconnected to unit not waiting for it")
	    (#x76 "expected scsi cmd complete msg; did not receive it")
	    (#x77 "illegal scsi message for reconnected state")
	    (#x78 "reselected without valid scsi id")
	    (#x79 "invalid mode on scsi message out")
	    (#x7a "invalid mode on scsi data transfer")
	    (#x81 "command aborted")
	    (#x82 "invalid command")
	    (#x83 "invalid parameter")
	    (#x84 "illegal block address")
	    (#x85 "volume overflow")
	    (#x8a "formatter failed to connect to scsi bus")
	    (#x8e "unknown error code feturned from the formatter")
	    (#xa1 "missing index signal")
	    (#xa2 "no seek complete")
	    (#xa3 "write fault")
	    (#xa4 "track 0 not found")
	    (#xa5 "multiple units selected")
	    (#xa6 "seek error")
	    (#xa7 "formatter hardware error")
	    (#xc1 "id error")
	    (#xc2 "uncorrectable data error")
	    (#xc3 "id address mark not found")
	    (#xc4 "data address mark not found")
	    (#xc5 "block not found (sector address)")
	    (#xc6 "bad block not found")
	    (#xc7 "format error")
	    (#xc8 "corrrectable data check")
	    (#xc9 "interleave error")
	    (#xca "media error")))


(defun print-nupi-command (io-cmd)
  (let* ((command-word (dpb (aref io-cmd 1) (byte 16. 16.) (aref io-cmd 0)))
	 (status (dpb (aref io-cmd 3) (byte 16. 16.) (aref io-cmd 2)))
	 (parameter-list (dpb (aref io-cmd 5) (byte 16. 16.) (aref io-cmd 4)))
	 (transfer-count (dpb (aref io-cmd 7) (byte 16. 16.) (aref io-cmd 6)))
	 (device-address (dpb (aref io-cmd 9.) (byte 16. 16.) (aref io-cmd 8.)))
	 (interrupt-address (dpb (aref io-cmd 11.) (byte 16. 16.) (aref io-cmd 10.)))
	 (reserved-1 (dpb (aref io-cmd 13.) (byte 16. 16.) (aref io-cmd 12.)))
	 (reserved-2 (dpb (aref io-cmd 15.) (byte 16. 16.) (aref io-cmd 14.)))
	 (scatter-p (ldb-test (byte 1 22.) command-word))
	 )
    (format t "~&Physical Unit ~s" (ldb (byte 8 0) command-word))
    (format t "~&Spare ~s" (ldb (byte 8 8) command-word))
    (format t "~&Options ~s" (ldb (byte 8 16.) command-word))
    (if (ldb-test (byte 1 20.) command-word) (format t " Swap-partial-completion-interrupt"))
    (if (ldb-test (byte 1 21.) command-word) (format t " device-address-is-physical"))
    (if (ldb-test (byte 1 22.) command-word) (format t " SCATTER"))
    (if (ldb-test (byte 1 23.) command-word) (format t " Interrupt-enable"))
    (format t "~&Command #x~16r ~a" (ldb (byte 8 24.) command-word)
	    (cadr (assq (ldb (byte 8 24.) command-word) nupi-commands)))

    (format t "~&Status ~s" status)
    (format t "~&  Busy ~s" (ldb (byte 1 31.) status))
    (format t "~&  Complete ~s" (ldb (byte 1 30.) status))
    (format t "~&  Error ~s" (ldb (byte 1 29.) status))
    (format t "~&  Retries required ~s" (ldb (byte 1 28.) status))
    (format t "~&  Aux status available ~s" (ldb (byte 1 27.) status))
    (format t "~&  Paging partial completion ~s" (ldb (byte 1 26.) status))
    (format t "~&  spare ~s" (ldb (byte 2 24.) status))
    (let ((error (ldb (byte 8 16.) status)))
      (format t "~&  controller error ~s" error)
      (when (not (zerop error))
	(format t "  Class: /"~a/"" (nth (ldb (byte 3 21.) status) nupi-error-classes))
	(format t "  ~a" (cadr (assq error nupi-controller-errors)))
	))

    (let ((error (ldb (byte 8 8) status)))
      (format t "~&  device error ~s" error)
      (when (not (zerop error))
	(format t "  Class /"~a/"" (nth (ldb (byte 3 13.) status) nupi-error-classes))
	(format t "  ~a " (cadr (assq error nupi-device-errors)))
	))
    (format t "~&  spare ~s" (ldb (byte 3 5) status))
    (format t "~&  ECC applied ~s" (ldb (byte 1 4) status))
    (format t "~&  n-retries ~s" (ldb (byte 3 0) status))

    (format t "~&parameter-list #x~16r" parameter-list)

    (format t "~&Transfer count ~d." transfer-count)
    (format t "~&Device block address ~s" device-address)
    (format t "~&Interrupt address #x~16r" interrupt-address)
    (format t "~&Reserved ~s ~s" reserved-1 reserved-2)

    (when scatter-p
      (format t "~&Scatter list: ")
      (let ((cmd io-cmd))
	(do ((scatter-index %nupi-disk-ccw-list (+ scatter-index 4))
	     (pages-to-go (floor transfer-count 1024.) (1- pages-to-go)))
	    ((zerop pages-to-go))
	  (format t "~&#x~8x ~d."
		  (dpb (aref cmd (+ scatter-index 1))
		       (byte 16. 16.)
		       (aref cmd scatter-index))
		  (dpb (aref cmd (+ scatter-index 3))
		       (byte 16. 16.)
		       (aref cmd (+ scatter-index 2)))))))))


;;; Built on top of disk-error.
(DefSignal NuPI-Error eh:disk-error nil)

(defun nupi-disk-wait (io-cmd)
  (process-wait "Disk Wait"
		#'(lambda (io-cmd)
		    (ldb-test (byte 1 14.) (aref (io-cmd-command-block io-cmd) %nupi-disk-status-hi)))
		io-cmd)
  (When (ldb-test (byte 1 13.) (aref (io-cmd-command-block io-cmd) %nupi-disk-status-hi))
    ;;(ferror nil "disk error")
    ;; need some args to describe error...
    (Ferror 'NuPI-Error "NuPI request failed."))
  )

(defun nupi-disk-command (io-cmd command phys-unit disk-address byte-count &optional (scatter-p t))
  (unwind-protect
      (progn
	;;** someday, just wire the part we are going to use
	(%wire-structure (%find-structure-leader io-cmd) (eq command #x12))
	(build-nupi-command io-cmd phys-unit command byte-count disk-address scatter-p)
	(when scatter-p
	  (verify-nupi-command io-cmd))
	(%io-cmd-run io-cmd)
	(nupi-disk-wait io-cmd)
	)
    (%unwire-structure (%find-structure-leader io-cmd)))
  io-cmd)

(defun nupi-tape-command (io-cmd command phys-unit disk-address byte-count)
  (unwind-protect
      (progn
	(%wire-structure io-cmd)
	(do ((byte-offset 0 (+ byte-offset 1024.))
	     (block-number 0 (1+ block-number))
	     (end-block-number (floor byte-count 1024.))
	     )
	    ((= block-number end-block-number)
	     (setf (nupi-disk-transfer-count io-cmd) byte-count))
	  (build-nupi-command io-cmd phys-unit command 1024. disk-address t byte-offset)
	  (verify-nupi-command io-cmd)
	  (%io-cmd-run io-cmd)
	  (nupi-disk-wait io-cmd)
	  (when (not (zerop (ldb (byte 8 8) (nupi-disk-status io-cmd))))
	    (setf (nupi-disk-transfer-count io-cmd) (* block-number 1024.))
	    (return))
	  ))
    (%unwire-structure io-cmd))
  io-cmd)

(defun test-nupi-disk-command (io-cmd command phys-unit disk-address byte-count &optional (scatter-p t))
  (unwind-protect
      (progn
	(%wire-structure io-cmd)
	(build-nupi-command io-cmd phys-unit command byte-count disk-address scatter-p)
	(when scatter-p
	  (verify-nupi-command io-cmd))
	)
    (%unwire-structure io-cmd))
  io-cmd)

(defun verify-nupi-command (io-cmd)
  (let* ((command-block-adr (vadr-to-nubus-phys (io-cmd-first-command-block-address io-cmd)))
	 (command-block-slot (ldb (byte 8 24.) command-block-adr))
	 (command-block-offset (ldb (byte 24. 0) command-block-adr))
	 (parameter-list-adr (%nubus-read command-block-slot
					  (+ command-block-offset 8)))
	 (parameter-list-slot (ldb (byte 8 24.) parameter-list-adr))
	 (parameter-list-offset (ldb (byte 24. 0) parameter-list-adr))
	 )
    (if (not (= command-block-slot parameter-list-slot))
	(ferror nil "bad nupi command - command block and parameter list on different slots"))
    (if (not (= (+ command-block-offset (* 8. 4)) parameter-list-offset))
	(ferror nil "bad nupi command - parameter list doesn't follow command-block"))
    (do ((pages-to-go (floor (%nubus-read command-block-slot
					  (+ command-block-offset 12.))
			     1024.)
		      (1- pages-to-go))
	 (page-number 0 (1+ page-number))
	 )
	((zerop pages-to-go))
      (let ((adr (%nubus-read parameter-list-slot
			      (+ parameter-list-offset (* page-number 8))))
	    (size (%nubus-read parameter-list-slot
			       (+ parameter-list-offset (+ (* page-number 8) 4)))))
	(if (not (= size 1024.))
	    (ferror nil "bad nupi command: scatter list doesn't have size 1024."))
	(if (and (not (= (ldb (byte 8 24.) adr) #xf3))
		 (not (= (ldb (byte 8 24.) adr) #xf4)))
	    (ferror nil "bad nupi scatter address #x~x" adr))))))

(defun nupi-disk-read (io-cmd logical-unit disk-address n-pages)
  (nupi-disk-command io-cmd #x12 (convert-logical-unit-to-physical-unit logical-unit) disk-address (* n-pages 1024.))
  )

(defun nupi-disk-write (io-cmd logical-unit disk-address n-pages)
  (nupi-disk-command io-cmd #x13 (convert-logical-unit-to-physical-unit logical-unit) disk-address (* n-pages 1024.))
  )

;;; First, here are primitives for making arrays that can be wired, and for wiring them. 

(defvar wireable-structures-area)

(add-initialization "Make WIREABLE-STRUCTURES-AREA"
		    '(make-area :name 'wireable-structures-area
				:gc :static
;				:volatility 0
				)
		    '(once))

(defmacro with-consing-allowed-in-wireable-structures-area (&body body)
  `(unwind-protect
       (progn
	 (without-interrupts
	   (setf (%area-type wireable-structures-area) %region-space-static)
	   (for-every-region-in-area (r wireable-structures-area)
	     (setf (%region-type r) %region-space-static))
	   (%invalidate-area-mapping wireable-structures-area))
	 ,@body
	 )
     (without-interrupts
       (setf (%area-type wireable-structures-area) %region-space-fixed)
       (for-every-region-in-area (r wireable-structures-area)
	 (setf (%region-type r) %region-space-fixed))
       (%invalidate-area-mapping wireable-structures-area))
     ))

(defun make-wireable-array (n-pages type named-structure-symbol)
  (check-type n-pages integer)
  (check-type type (member art-1b art-2b art-4b art-8b art-16b art-32b art-string art-inum))
  (check-type named-structure-symbol symbol)
  
  (let* ((array-length (* n-pages page-size (cdr (assq type array-elements-per-q))))
	 (long-length (if (> array-length %array-max-short-index-length) 1 0)))
    (with-consing-allowed-in-wireable-structures-area
      (make-array array-length
		  :type type
		  :leader-length (max 2		;be sure named-structure-symbol is in leader
				      (- page-size
					 1	;for leader header
					 1	;for leader length q
					 1	;for array header
					 long-length	;for long length q
					 ))
		  :named-structure-symbol named-structure-symbol
		  :area wireable-structures-area
		  ))))

(defun wireable-array-p (array)
  (and (arrayp array)
       (not (array-displaced-p array))
       (eq (%area-number array) wireable-structures-area)
       (zerop (ldb (byte 8 0) (%pointer (%find-structure-leader array))))
       (zerop (ldb (byte 8 0) (%structure-total-size (%find-structure-leader array))))
       (zerop (ldb (byte 8 0) (%pointer-plus array (array-data-offset array))))))

(defun wire-wireable-array (array from-index to-index set-modified dont-bother-paging-in)
  from-index to-index
  ;;someday, just do specified part
  (if (not (wireable-array-p array))
      (ferror nil "Array ~s was not made with MAKE-WIREABLE-ARRAY." array))
  (%wire-structure array set-modified dont-bother-paging-in))

(defun unwire-wireable-array (array from-index to-index)
  from-index to-index
  (if (not (wireable-array-p array))
      (ferror nil "Array ~s was not made with MAKE-WIREABLE-ARRAY." array))
  (%unwire-structure array))

;;;
;;;  DMA-BUFFERS
;;;

;when we can wire subsections of the array, make the matcher
;accept any array that is at least as large as the requested size
(defstruct (dma-buffer-leader
	     (:type :array-leader)
	     )
  dma-buffer-ignore				;would be fill pointer
  dma-buffer-named-structure-symbol
  dma-buffer-16b
  dma-buffer-8b
  dma-buffer-string
  dma-buffer-size-in-pages
  dma-buffer-data-vadr
  )

(defresource dma-buffer (n-pages)
  :constructor make-dma-buffer
  :free-list-size 50.)

(defun make-dma-buffer (ignore n-pages)
  (let ((dma-buffer (make-wireable-array n-pages
					 'art-32b
					 'dma-buffer)))
    (setf (dma-buffer-16b dma-buffer)
	  (make-array (* n-pages page-size 2)
		      :type :art-16b
		      :displaced-to dma-buffer
		      :leader-length 2
		      :named-structure-symbol 'dma-buffer-16b))
    (setf (dma-buffer-8b dma-buffer)
	  (make-array (* n-pages page-size 4)
		      :type :art-8b
		      :displaced-to dma-buffer
		      :leader-length 2
		      :named-structure-symbol 'dma-buffer-8b))
    (setf (dma-buffer-string dma-buffer)
	  (make-array (* n-pages page-size 4)
		      :type :art-string
		      :displaced-to dma-buffer
		      :leader-length 2
		      :named-structure-symbol 'dma-buffer-string))
    ;;
    (setf (dma-buffer-size-in-pages dma-buffer) n-pages)
    (setf (dma-buffer-data-vadr dma-buffer)
	  (%pointer-plus dma-buffer (array-data-offset dma-buffer)))
    dma-buffer))

(defselect ((dma-buffer named-structure-invoke))
  (:print-self (array stream ignore ignore)
    (printing-random-object (array stream :typep)
      (format stream "Length ~d. page~:p" (dma-buffer-size-in-pages array))
      (when (eq (ldb %%pht1-swap-status-code (%page-status array))
		%pht-swap-status-wired)
	(format stream "; Wired, first page at #x~x"
		(vadr-to-nubus-phys (dma-buffer-data-vadr array))))))
  (:which-operations (ignore)
    '(:print-self :which-operations))
  )

(defun get-dma-buffer (n-pages)
  (let ((dma-buffer (allocate-resource 'dma-buffer n-pages)))
    ;;people are allowed to change this
    (setf (dma-buffer-named-structure-symbol dma-buffer) 'dma-buffer)
    (setf (array-leader (dma-buffer-16b dma-buffer) 1) 'dma-buffer-16b)
    (setf (array-leader (dma-buffer-8b dma-buffer) 1) 'dma-buffer-8b)
    (setf (array-leader (dma-buffer-string dma-buffer) 1) 'dma-buffer-string)
    dma-buffer))

(defun free-dma-buffer (dma-buffer)
  (setf (dma-buffer-named-structure-symbol dma-buffer) 'dma-buffer)
    (setf (array-leader (dma-buffer-16b dma-buffer) 1) 'dma-buffer-16b)
    (setf (array-leader (dma-buffer-8b dma-buffer) 1) 'dma-buffer-8b)
    (setf (array-leader (dma-buffer-string dma-buffer) 1) 'dma-buffer-string)
  (deallocate-resource 'dma-buffer dma-buffer))
