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

(defconst %%quantum-byte (byte 11. 14.))
(defvar quantum-map-area-number nil "the area number of the quantum-map")

(defun find-quantum-map ()
  "find the area number of the quantum map"
  (or quantum-map-area-number
      (setq quantum-map-area-number
	    (do ((i 0 (1+ i)))
		((>= i si:working-storage-area) nil)
	      (when (string-equal (symbol-name (area-name i))
				  "QUANTUM-MAP")
		(return i))))))

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

(defun quantum-map-index (quantum-number)
  "compute the virtual address of the entry in the quantum map corresponding to QUANTUM-NUMBER"
  (+ (si:%region-origin (find-quantum-map))
     (* 2 quantum-number)))

(defun read-quantum-map (quantum-number)
  "read a quantum map entry.
Values are one of
	NOT-VALID
	MEMORY copy-first page-offset boot-pages partition-number
	A-MEMORY
	DEVICE nubus-words l2-control l2-page"
  (let ((address (quantum-map-index quantum-number)))
    (if (zerop (%p-ldb si:%%pq1-quantum-is-valid address))
	'not-valid
      (if (zerop (%p-ldb si:%%pq1-quantum-is-device address))
	  (values 'memory
		  (if (zerop (%p-ldb si:%%pq1m-page-out-copy-first address))
		      'no-need-to-copy
		    'copy-first)
		  (%p-ldb si:%%pq1m-page-offset address)
		  (%p-ldb si:%%pq2m-boot-pages-allocated (1+ address))
		  (%p-ldb si:%%pq2m-partition-number (1+ address)))
	(if (zerop (%p-ldb si:%%pq1d-quantum-is-special-a-memory address))
	    (values 'device
		    (%p-ldb si:%%pq1d-quantum-nubus-words address)
		    (%p-ldb si:%%pq2d-quantum-l2mc-except-meta-bits (1+ address))
		    (%p-ldb si:%%pq2d-quantum-l2mpp (1+ address)))
	  (values 'a-memory))))))

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

(defun %enable-quantum-map-on-next-boot (p)
  "Enables (T) or dissables (NIL) use of quantum map for the next time this processor is booted."
  (setf (%processor-conf-starting-processor-switches *my-proc-conf*)
	(dpb (if p 1 0) %%processor-switch-fast-boot-enable
	     (%processor-conf-starting-processor-switches *my-proc-conf*))))

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

(defun print-quantum-map (&optional (first 0) (n 1_11.))
  (dotimes (idx n)
    (format t "~A " (read-quantum-map (+ first idx)))
    (if (zerop (logand idx #o7)) (terpri))))

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

(defvar device-quantum-area nil
  "This is the area in which arrays which refer to quantum-mapped NuBus space are consed")

(defun initialize-device-quantum-area ()
  "Creates the area where quantum mapped device arrays are allocated and sets device-quantum-area to its area number."
  (when (null device-quantum-area)
    (setq device-quantum-area
	  (make-area :name 'device-quantum-area
		     :gc :static))))

(defun make-device-array (nubus-page nubus-words array-type write-enable)
  "Make an array of the type specified whose data is mapped to the area of nubus space described by NUBUS-PAGE
and NUBUS-WORDS.  If WRITE-ENABLE is nil then the array will be read-only."
  (let* ((words-per-quantum (* 64. page-size))
	 (number-of-quanta (ceiling nubus-words words-per-quantum))
	 (waste-array (make-array (* words-per-quantum (1+ number-of-quanta))
				  :type art-32b :area device-quantum-area))
	 (device-quantum-start
	   (ldb %%quantum-byte
		(%pointer-plus (%pointer-plus waste-array
					      (si:array-data-offset waste-array))
			       (1- words-per-quantum))))
	 device-array
	 (boxed-words-per-element (assq array-type array-boxed-words-per-element))
	 elements-per-word)
    (cond ((null boxed-words-per-element)
	   (ferror nil "~s is not a known array type" array-type))
	  ((not (zerop (cdr boxed-words-per-element)))
	   (ferror nil "Device arrays must have unboxed data"))
	  (t (setq elements-per-word (cdr (assq array-type array-elements-per-q)))
	     (setq device-array (make-array (if (< elements-per-word 0)
						(ceiling nubus-words elements-per-word)
					      (* nubus-words elements-per-word))
					    :type array-type
					    :displaced-to (* words-per-quantum device-quantum-start)))
	     (let ((nb-page nubus-page)
		   (words-remaining nubus-words))
	       (dotimes (i number-of-quanta)
		 (test-map-device-quantum (+ device-quantum-start i)
					       nb-page
					       (min words-remaining words-per-quantum)
					       (if write-enable #o14 #o10))
		 (incf nb-page 64.)
		 (decf words-remaining words-per-quantum)))	;only drops below 0 when we're done
	     device-array))))

;;; tell the compiler what's going on
; compiler:(defmic %map-device-quantum #o1164 (quantum-number nubus-page nubus-words l2-control) t)

(defun test-map-device-quantum (a b c d)
  (format t "~&quantum number ~o~&nubus-page ~o ~x~&words ~d~&l2c ~o"
	  a b b c d)
  (terpri)
  (compiler:%map-device-quantum a b c d))

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

;;; some useful functions

(defun nubus-address-to-nubus-word (adr)
  (ash adr -2))

(defun nubus-word-to-nubus-page (w-adr)
  (ash w-adr -8))

(defun nubus-address-to-nubus-page (adr)
  (nubus-word-to-nubus-page
    (nubus-address-to-nubus-word adr)))

(defun read-nubus-word (array offset)
  (let ((w0 (aref array (* 2 offset)))
	(w1 (aref array (1+ (* 2 offset)))))
    (dpb w1 #o2020 w0)))

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

;;; some code for testing device mapping

#|
(defun nils-nubus-address ()
  (si:%processor-conf-memory-base-0 si:*my-proc-conf*))

(setq foo
      (si:make-device-array (si:nubus-address-to-nubus-page (si:nils-nubus-address))
			    20. 'art-16b nil))

;;; these should give you symbol pointers to NIL and T respecively:
(read-nubus-word foo 1)				;NIL's value cell, pointer to NIL
(read-nubus-word foo 6)				;T's value cell, pointer to T
|#
