4,887,235
	433	434
  (start-memory write physical %disk-command-address)
  (assign memory-data disk-command-stop))
;; Switch disk over to write operation, then feed first word without dismissing
(parallel
 (start-memory write physical %disk-command-address)
 (assign memory-data disk-command-val2)
 (jump disk-write-startup)))

;For first DnA transfer when writing, must not check state machine aliveness since
;it hasn’t sent us any wakeups yet. Must decide whethmr this is last (and first) DAP.
(defucode disk-write-startup
  ;; Increment MA, start memory to fetch first word of write data
  (parallel
   (start-memory read physical disk-memory-address dma iob 3)
   (assign disk-memory-address (1+ disk-memory-address))
   (dismiss)
   (if (minus-fixnum obus)
       (goto disk-write-loop-1)
       (goto disk-write-loop-last-1))))

;DMA transfer boo, not last DAP
(defucode disk-write-loop
  ;; First cycle: increment MA, start memory
  (parallel
   (start-memory read physical disk-memory-address dma iob 3)
   (assign disk-memory-address (1+ disk-memory-address))
   (dismiss)
   (if lbus-dev-cond
       (terminate-disk-dma %disk-micro-status-disk-error)
     (goto disk-write-loop-1))))

(defucode disk-write-loop-1
  ;; Second cycle: count down LIC.
  (parallel
   (assign disk-word-count (1- disk-word-count))
   (if (minus-fixnum obus)
       (parallel
	;; Transfer last word and fetch new DAP
	(start-memory read physical disk-memory-address dma iob 1)
	(assign disk-memory-address (1+ disk-memory-address))
	(call-and-return-skip disk-new-dap disk-write-loop-last disk-write-loop))
     (goto disk-write-loop))))

;DMA transfer loop, last DAP
(defucode disk-write-loop-last
  ;; First cycle: increment MA, start memory
  (parallel
   (start-memory read physical disk-memory-address dma iob 3)
   (assign disk-memory-address (1+ disk-memory-address))
   (dismiss)
   (if lbus-dev-cond
       (terminate-disk-dma %disk-micro-status-disk-error)
     (goto disk-write-loop-last-1))))

(defucode disk-write-loop-last-1
  ;; Second cycle: count down WC.
  (parallel
   (assign disk-word-count (1- disk-word-count))
   (if (minus-fixnum obus)
       (goto disk-write-drain)
       (goto disk-write-loop-last))))

;Transfer last two words in sector with end flag
(defucode disk-write-drain
  (parallel (start-memory read physical disk-memory-address dma iob 7)
	    (assign disk-memory-address (1+ disk-memory-address))
	    (dismiss)
	    (if	lbus-dev-cond
		(terminate-disk-dma %disk-micro-status-disk-error)
	      (drop-through)))
  (nop)
  (parallel (start-memory read physical disk-memory-address dma iob 7)
	    (assign disk-memory-address (1+ disk-memory-address))
	    (dismiss)
	    (if lbus-dev-cond
		(terminate-disk-dma %disk-micro-status-disk-error)
	      (drop-through)))
  (nop)
  ;; Wake up here when state machine has swallowed last word
  (dismiss-disk-task)
  (nop)
  ;; Wake up here when state machine stops, after writing ECC
  (terminate-disk-dma %disk-micro-status-end-write))

;Read-compare routine. Use this for both 32-bit and 36-bit reads.
;This is a hybrid of read and write
(define-disk-search-ucode disk-read-compare
  ;; Stop the disk state machine
  (parallel
   (start-memory write physical %disk-command-address)
   (assign memory-data disk-command-stop))
  ;; Switch disk over to read/compare operation, then feed first word without dismissing
  (parallel
4,887,235
	435	436
    (start-memory write physical %disk-command-address)
    (assign memory-data disk-command-val2)
    (jump disk-read-compare-startup)))

;For first DMA transfer, must not check state machine aliveness since
;it hasn’t sent us any wakeups yet. riust decide whether this is last (and first) DAP.
(defucode disk-read-compare-startup
  ;; Increment MA, start memory to fetch first word of data
  (parallel
   (start-memory read physical disk-memory-address dma iob 3)
   (assign disk-memory-address (1+ disk-memory-address))
   (dismiss)
   (if (minus-fixnum obus)
       (goto disk-read-compare-loop-1)
       (goto disk-read-compare-loop-last-1))))

;DMA transfer loop, not last DAP
(defucode disk-read-compare-loop
  ;; First cycle: increment MA, start memory
  (parallel
   (start-memory read physical disk-memory-address dma iob 3)
   (assion disk-memory-address (1+ disk-memory-address))
   (dismiss)
   (if lbus-dev-cond
       (terminate-disk-dma %disk-micro-status-disk-error)
     (goto disk-read-compare-loop-1))))

(defucode disk-read-compare-loop-1
  ;; Second cycle: count down WC.
  (parallel
   (assign disk-word-count (1- disk-word-count))
   (if (minus-fixnum obus)
       (parallel
	;; Transfer last word and fetch new DAP
	(start-memory read physical disk-memory-address dma iob 1)
	(assign disk-memory-address (1+ disk-memory-address))
	(call-and-return-skip disk-new-dap
			      disk-read-compare-loop-last disk-read-compare-loop))
     (goto disk-read-compare-loop))))

;DMA transfer loop, last DAP
(defucode disk-read-compare-loop-last
  ;; First cycle: increment MA. start memory
  (parallel
   (start-memory read physical disk-memory-address dma iob 3)
   (assign disk-memory-address (1+ disk-memory-address))
   (dismiss)
   (if lbus-dev-cond
       (terminate-disk-dma %disk-micro-status-disk-error)
       (goto disk-read-compare-loop-last-1))))

(defucode disk-read-compare-loop-last-1
  ;; Second cycle: count down WC.
  (parallel
   (assign disk-word-count (1- disk-word-count))
   (if (minus-fixnum obus)
       (goto disk-read-compare-drain)
       (goto disk-read-compare-loop-last))))

;Transfer last two words in sector with end flag
(defucode disk-read-compare-drain	
  (parallel
   (start-memory read physical disk-memory-address dma iob 7)
   (assign disk-memory-address (1+ disk-memory-address))
   (dismiss)
   (if lbus-dev-cond
       (terminate-disk-dma %disk-micro-status-disk-error)
     (drop-through)))
  (nop)
  (parallel
   (start-memory read physical disk-memory-address dma iob 7)
   (assign disk-memory-address (1+ disk-memory-address))
   (dismiss)
   (if lbus-dev-cond
       (terminate-disk-dma %disk-micro-status-disk-error)
     (drop-through)))
  (nop)
  ;; Wake up here when state machine has swallowed last word
  (dismiss-disk-task)
  (nop)
  ;; Wake up here when state machine stops, after reading ECC
  (terminate-disk-dma %disk-micro-status-end-read-compare))

;Write-all command. Wait for’ an index pulse then go start writing.
(defucode disk-write-a;;
  (call-and-return-to start-read-or-write-all disk-write-startup))

;Read-all command. Wait for an index pulse then start reading.
(defucode disk-read-all
  (call start-read-or-write-all)
  (dismiss-disk-task)
  (if (minus-fixnum disk-memory-address)
4,887,235
	438
      (goto disk-read-loop)
      (goto disk-read-loop-last)))

;Since index pulse is narrow, we actually loop in this high-priority task
(defucode start-read-or-write-all
  ;; Loop until Index is true
  (read-disk-status-to-val1)
  (if (field-bit disk-command-val1 %%dsr-index)
      (drop-through)
      (goto start-read-or-write-all))
  :; Start up the disk state machine. By the time it gets going we should
  ;; be near the trailing edge of Index.
  (parallel
   (start-memory write physical %disk-command-address)
   (assign memory-data disk-command-val2)
   (return)))


;Sector-wait command (used for seek-wait)
;Service task starts hardware in Sector Wait command. We wakeup immediately
;and then again at the beginning of the next sector
(defucode disk-sector-wait
  (dismiss-disk-task)
  (nop)
  ;; Wake up here when state machine sees sector pulse
  (terminate-disk-dma %disk-micro-status-end-sector-wait))

;Read-header command
;Service task starts hardware in Read command, we awaken immediately
;and then again when sector header found
;--- This is pretty much guaranteed to cause an overrun...what to do?
(defucode disk-read-header
  (dismiss-disk-task)
  (nop)
  ;; Do a DMA write of the header into the DCW list (in the immediate arg of the read-header)
  (start-memory write physical disk-dap-address dma iob 1)
  (terminate-disk-dma %disk-micro-status-end-write))

;;; Service task
;--- for now, only serves the disk. Add the network later.
(defucode device-service-loop
  ;; Scan requests for service
  (if (bit %%service-disk)
      ;; Disk service (DMA task not running now)
      (dispatch-after-this disk-micro-status
			   (assign %%service-disk (b-constant 0))
	((%disk-micro-status-idle %disk-micro-status-in-sector %disk-micro-status-stop)
	 (jump device-service-end)) ;Use jump rather than goto to save space
	((%disk-micro-status-search-error %disk-micro-status-disk-error
					  %disk-micro-status-ecc-done)
	 (parallel (wakeup-driver)
		   (jump device-service-end)))
	((%disk-micro-status-start)
	 (goto fetch-disk-dcw))
	((%disk-micro-status-end-sector-wait)
	 (jump disk-seek-wait)) ;Use jump rather than goto to save space
	((%disk-micro-status-end-write)
	 (call-and-return-to check-disk-status next-disk-dcw))
	((%disk-micro-status-end-read)
	 (call check-disk-status)
	 (parallel
	  (trap-if (not (field-bit disk-command-val1 %%dsr-ecc-ok)) disk-error-detected)
	  (jump next-disk-dcw)))
	((%disk-micro-status-end-read-compare)
	 (call check-disk-status)
	 (trap-if (field-bit disk-command-val1 %%dsr-compare-error) disk-error-detected)
	 (parallel
	  (trap-if (not (field-bit disk-command-val1 %%dsr-ecc-ok)) disk-error-detected)
	  (jump next-disk-dcw)))
	(otherwise
	 (goto device-service-end)))	;Ignore any garbage status
    ;; No requests for service
    (goto net-service-loop)))

;; If no requests, dismiss. If more requests have come in, go do them without
;; dismissing. Check must be in same cycle as dismiss to avoid hazard.
(defucode device-service-end
  (parallel
   (trap-if (not-zero-fixnum service-task-requests)
	    device-service-loop)
   (dismiss))
  (nop)					;Wait two cycles for dismiss
  (jump device-service-loop))

;Read disk status register. Die if error, and return status in disk-command-vali.
;Note that the control stack can remain pushed spuriously if an error is detected.
;This is not a problem since there are no magic locations in this task's control stack.
(defucode check-disk-status
  (read-disk-status-to-val1)
  (parallel
4,887,235
	439	440
    (trap-if (bit-test (a-constant (get '%dsr-error-mask 'sysconstant)) disk-command-val1)
	     disk-error-detected)
    (return)))

(defucode disk-error-detected
  (assign %disk-micro-status (set-type %disk-micro-status-disk-error dtp-fix))
  (parallel (wakeup-driver)
	    (jump device-service-end)))

;; Do next DCW after the one we just did
(defucode next-disk-dcw
  (parallel
   (assign %disk-dcw-address (+ %disk-dcw-address (ldb-field current-disk-dcw %%dcw-length)))
   (jump fetch-disk-dcw)))

;; Do DCLI whose address has been set up
(defucode fetch-disk-dcw
  ;; Start fetch of first word
  (parallel (start-memory read physical %disk-dcw-address)
	    (assign disk-dap-address (1+ %disk-dcw-address)))
  ;; Start fetch of second word
  (parallel (start-memory read physical disk-dap-address)
	    (assign disk-dap-address (1+ disk-dap-address)))
  ;; Store the DCW away. Cannot be overlapped with dispatch due to damnable field conflicts
  (assign current-disk-dcw memory-data)
  (assign current-disk-dcw2 memory-data)
  ;; Decode the DCW
  (dispatch-after-this (ldb-field current-disk-dcw %%dcw-micro-command)
	;; Initialize micro status
	(assign %disk-micro-status (set-type %disk-micro-status-in-sector dtp-fix))
	((%dcw-u-nop)
	 (goto next-disk-dcw))
	((%dcw-u-stop)
	 (sequential
	  (assign %disk-micro-status (set-type %disk-micro-status-stop dtp-fix))
	  (parallel (wakeup-driver)
		    (jump device-service-end))))
	((%dcw-u-wakeup)
	 (parallel (wakeup-driver)
		   (jump next-disk-dcw)))
	((%dcw-u-goto)
	 (parallel
	  (assign %disk-dcw-address current-disk-dcw2)
	  (jump fetch-disk-dcw)))
	((%dcw-u-head)
	 (goto disk-head-select))
	((%dcw-u-seek-wait)
	 (goto disk-seek-wait))
	((%dcw-u-read-header)
	 (start-disk-dma disk-read-header)
	 (parallel (start-memory write physical %disk-command-address)
		   (assign memory-data current-disk-dcw2)
		   (jump device-service-end)))
	((%dcw-u-read)
	 (parallel (start-disk-dma disk-read)
		   (jump start-disk-transfer)))
	((%dcw-u-write)
	 (parallel (start-disk-dma disk-write)
		   (jump start-disk-transfer)))
	((%dcw-u-read-compare)
	 (parallel (start-disk-dma disk-read-compare)
		   (jump start-disk-transfer)))
	((%dcw-u-read-all)
	 (parallel (start-disk-dma disk-read-all)
		   (jump start-disk-transfer)))
	((%dcw-u-write-all)
	 (parallel (start-disk-dma disk-write-all)
		   (jump start-disk-transfer)))
	((%dcw-u-ecc)
	 (start-disk-dma disk-ecc)
	 (parallel (start-memory write physical %disk-command-address)
		   (assign memory-data current-disk-dcw2)
		   (jump device-service-end)))
	(otherwise 		;Die if garbage seen
	 (sequential
	  (assign %disk-micro-status (set-type %disk-micro-status-stop dtp-fix))
	  (parallel (wakeup-driver)
		    (jump device-service-end))))))

;Transfer DCWs come here. The state of the DMA task has been set.
(defucode start-disk-transfer
  ;; Start fetch of third word (command)
  (parallel (start-memory read physical disk-dap-address)
	    (assign disk-dap-address (1+ disk-dap-address)))
  ;; Start fetch of first DAP
  (parallel (start-memory read physical disk-dap-address)
	    (assign disk-dap-address (1+ disk-dap-address)))
  ;; Stash command
  (assign disk-command-val1 memory-data)
  ;; Complete fetch cf first DAP
  (assign disk-word-count memory-data)
4,887,235
	441	442
  (parallel (start-memory read physical disk-dap-address)
	    (assign disk-dap-address (1+ disk-dap-address)))
  (assign disk-command-stop (logand disk-command-val1
				    (a-constant (lognot (field-mask %%dcr-busy)))))
  (assign disk-memory-address memory-data)
  ;; Screw around for 2 extra cycles because of conflicts for AMWA
  (assign disk-sector-tries (ldb-field current-disk-dcw %%dcw-dcr-command))
  (assign disk-sector-tries (dpb-field disk-sector-tries %%dcr-command disk-command-val1))
  (assign disk-command-val2 disk-secter-tries)

  (assign disk-sector-tries %disk-sector-max-tries)
  (parallel (start-memory write physical %disk-command-address)
	    (assign memory-data disk-command-stop)	;Wake up and go to sleep
	    (jump device-service-end)))

;Check whether seek has completed immediately and at every sector pulse thereafter
(defucode disk-seek-wait
  (parallel (assign %disk-micro-status (set-type %disk-micro-status-in-sector dtp-fix))
	    (call check-disk-status))
  (if (field-bit disk-command-val1 %%dsr-on-cylinder)
      (goto next-disk-dcw)
    (drop-through))
  (start-disk-dma disk-sector-wait)
  (parallel (start-memory write physical %disk-command-address)
	    (assign memory-data current-disk-dcw2)
	    (jump device-service-end)))

;Head select -- need to twiddle tog bit up end down
(defucode disk-head-select
  ;; Write bus, with tag bit turned off
  (parallel (start-memory write physical %disk-command-address)
	    (assign memory-data current-disk-dcw2))
  ;; Write again, with tag bit turned on
  (assign disk-command-val1 (logior (a-constant (field-mask %%dcr-head-tag))
				    current-disk-dcw2))
  (parallel (start-memory write physical %disk-command-address)
	    (assign memory-data disk-command-val1)
	    ;; Delay a microsecond or so by checking for error status
	    (call check-disk-status))
  ;; Clear tag bit, leaving same value on bus
  (parallel (start-memory write physical %disk-command-address)
	    (assign memory-data current-disk-dcw2)
	    (jump next-disk-dcw)))

;Error correction computation. We have to do the word counting here.
;Do it in %disk-memory-add’ess so when we’re done the macrocode can read it.
;First, have to take 335-72 wakeups to rec~cle the ecc code (335 is the
;ecc code field size of 42987 divided by 128. 72 is the sector size
;divided by 128). The state machine takes care of the extra bits for
;the remainder of 42987/128, minus the 32 bits already clocked when the
;ecc was read at the end of the sector and the 64 bits already clocked
;when the prefix was read.
;The -4 is because if %disk-memory-address starts out negative the state
;machine will still process 3 128-bit chunks before it sees the end flag.
(defucode disk-ecc
  (parallel (assign %disk-memory-address (set-type (b-constant (- 335. 72. 4)) dtp-fix))
	    (dismiss-disk-task)
	    (jump disk-ecc-loop-1)))

(defucode disk-ecc-loop-1
  (if (minus-fixnum %disk-memory-address)
      ;; Finished recycling code, start counting words of data field
      ;; Start at -3 because we will wake up twice while two more 128-bit
      ;; chunks are passed over, and if we stop after the first word, that
      ;; is word 0
      (parallel (assign %disk-memory-address (set-type (b-constant -3) dtp-fix))
		(dismiss-disk-task-and-ack end-flag)
		(jump disk-ecc-loop-2))
    (drop-through))
  ;; Wakes up here
  (parallel (assign %disk-memory-address (set-type (1- %disk-memory-address) dtp-fix))
	    (dismiss-disk-task-and-ack)
	    (jump disk-ecc-loop-1)))

;Now run and count words until state machine stops or full sector size has been scanned.
(defucode disk-ecc-loop-2
  (if (greater-or-equal-fixnum %disk-memory-address (b-constant 290.))
      (terminate-disk-dma %disk-micro-status-ecc-done) ;Uncorrectable error
    (drop-through))
  ;; Wakes up here
  (parallel (assign %disk-memory-address (set-type (1+ %disk-memory-address) dtp-fix))
	    (dismiss-disk-task-and-ack)
	    (if lbus-dev-cond		;Was this a complete word, or did at mach stop?
		(terminate-disk-dma %disk-micro-status-ecc-done)	;Correctable error
	      (goto disk-ecc-loop-2))))

;;; Initialization--maybe some day the microcode loader can take care of this?
;;; In the meantime the startup microcode should call this subroutine
(defucode disk-initialize
  (parallel
   (write-task-state %device-service-task
;XXXbrad - missing...
)))

4,887,235
	443	444
;;; -*- Mode:Lisp; Package:Micro; Base:8; Lowercase:yes -*-
;;; (c) Copyright 1982, Symbolics, Inc.


;;; Microcode for master control

;Get defmicro and all his hosts
#M
(declare (cond ((not (status feature lmucode))
		(load 'udcls))))

(define-sysconstant main-stack-buffer-address)
(define-sysconstant auxiliary-stack-buffer-address)
(reserve-scratchpad-memory 2514 2520)

(defareg current-dp-control)		;Copy of dp-control register (can’t read back)
(defareg a-page-fault-address)		;VMA of last page fault (for debugging)
(defareg a-page-fault-micro-pc)		;Micro-PC of last page fault (for debugging)

;If this register is non-zero and we pclsr, save-bitblt-buffer must be
;called after restoring the stack pointer.
(defareg bitblt-buffer-active 0)

;Start the machine here
(defucode-at-loc start 1		;105 FOOBAR
  (assign b-quote-t quote-t)		;These are needed on the B side
  (parallel
    (assign b-quote-nil quote-nil)
    (call disk-initialize))		;Initialize other tasks
  ;; Initialize virtual address map
  (parallel (assign vma (a-constant 0))
	    (call clear-map-cache))
  ;; Initialize flags
  (assign a-pclsr-top-of-stack (set-type (b-constant 0) dtp-null))
  (assign bitblt-buffer-active (b-constant 0))
  (assign stack-load-started (b-constant 0))
  (assign current-dp-control (b-constant 0))
  (assign a-stack-group-lock quote-nil)
  (assign b-cached-mapping-table quote-nil)
  (assign %stack-buffer-low (set-type (b-constant 0) dtp-fix)) ;do this in macrocode later...
  (assign %stack-buffer-limit (set-type (b-constant 0) dtp-fix))



  (call switch-to-auxiliary-stack-buffer)
  (parallel (pushval function-system-startup)	;Call this function to start up
	    (call funcall-0-ignore)) 		;Build frame header, set PC
  ;; Mark this frame as the bottom frame so we trap if it tries to return
  (parallel
    (assign frame-misc-data (logior frame-misc-data	;Cause trap on return
				    (b-constant (+ (byte-mask frame-buffer-underflow-bit)
						   (byte-mask frame-bottom-bit)))))
    (call initialize-net))	;Initialize the network
  (parallel
   (assign frame-previous-frame quote-nil)	;No back-pointer in this frame
   (jump pclsr)))		;Adjust CSP and take instruction dispatch

;;; Pclsr

;Come here with new PC (to escape to) loaded. Clear the micro stack, reset the
;main stack, and return to the main loop (eventually the IFU dispatch address)
(defucode pclsr-restore-stack
  (call-and-return-to restore-stack-pointer pclsr))

(defucode pclsr
  ;; Pop stack until clear. If not in emulator task. halt.
  ;; Don’t pop control-stack simultaneous with test, it would cause SQ NEXT INST
  ;; to come on spuriously if stack uas already clear.
  (if (not-zero-fixnum (read-cur-task))
      ;; Not in emulator task
      (halt pclsr-in-io-task)
      ;; In emulator task, check csp left In b-temp as bi-product
      (if (equal-fixnum (ldb b-temp 4 16.) (a-constant 17))
	  ;; Stack is empty, exit
	  (parallel (assign a-pclsr-top-of-stack (set-type (a-constant 0) dtp-null))
		    (jump pclsr-done))		;Must make sure above flag is clear
	;; Stack not empty, pop and try again
	(parallel
	  (for-effect (pop-control-stack))
	  (jump pclsr)))))

(defucode pclsr-done
  (if (not-zero-fixnum bitblt-buffer-active)
      (goto save-bitblt-buffer)
      (next-instruction)))

;Restore stack-pointer to its value at the start of thio macroinstruction,
;clobbering top-of-stack (but no temporaries!)
(defucode restore-stack-pointer
  (assign top-of-stack (logior (a-constant -1_4) stack-adjustment))
  (if (ldb-bit-test top-of-stack 3)
4,887,235
	445	446
      (assign stack-pointer (- stack-pointer top-of-stack))
      (assign stack-pointer (- stack-pointer (ldb top-of-stack 3 0))))
  (if (not (data-type? a-pclsr-top-of-stack dtp-null))
      (parallel (assign top-of-stack-a a-pclsr-top-of-stack)
		(return))
    (return)))

;;; Multiple stack-buffer primitives

;Discard the state of the auxiliary stack buffer and resume the saved state
;of the main stack buffer. If %sequence-break-pending is set, trap imeadiately.
(definst %resume-main-stack-buffer no-operand
  (error-if (not (equal-pointer %current-stack-buffer auxiliary-stack-buffer-address))
	    illegal-instruction)
  (if (not-data-type? %sequence-break-pending dtp-nil)
      (parallel (assign %sequence-break-pending quote-nil)
		(call set-sequence-break))
    (drop-through))
  (assign %control-stack-low %other-control-stack-low)
  (assign %control-stack-limit %other-control-stack-limit)
  (assign %binding-stack-low %other-binding-stack-low)
  (assign %binding-stack-limit %other-binding-stack-limit)
  (assign %binding-stack-pointer %other-binding-stack-pointer)
  (assign %catch-block-list %other-catch-block-list)
  (assign %current-stack-group-status-bits %other-stack-group-status-bits)
  (assign pc %other-pc)		;No instruction fetch since page fault muat be deferred
  (assign frame-pointer %other-frame-pointer)
  (assign stack-pointer %other-stack-pointer)
  (parallel
   (assign %current-stack-buffer (set-type main-stack-buffer-address dtp-fix))
   (assign b-temp obus)
   (call set-stack-buffer))
  (parallel
   (assign top-of-stack top-of-stack-a)
   (jump set-stack-buffer-limit)))

;Explicit switch to aux sb.
;Stack contains function, args, count of args. All popped upon return, no values
;returned unless they are pushed "by hand" before resuming,
(definst %funcall-in-auxiliary-stack-buffer (no-operand needs-stack)
  ;; Perform context switch and pop our arguments
  (assign a-temp (- stack-pointer top-of-stack 1))	;Address of the function
  (parallel
   (assign vma (ldb a-temp 10. 0 main-stack-buffer-address)) ;Translate to physical address
   (decrement-stack-pointer)
   (call switch-to-auxiliary-stack-buffer))
  (parallel
   (assign %other-stack-pointer (- %other-stack-pointer top-of-stack 1))
   (jump %funcall-in-auxiliary-stack-buffer1)))

(defucode %funcall-in-auxiliary-stack-buffer1
  ;; Copy function, args, count into new stack, then perform a function call
  (parallel
    (start-memory read block)
    (assign top-of-stack (1- top-of-stack)))
  (if (greater-fixnum top-of-stack (a-constant -2))
      (sequential
       (parallel
	(assign (amem (stack-pointer 1)) memory-data)
	(increment-stack-pointer))
       (parallel
	(assign vma (ldb vma 10. 0 main-stack-buffer-address))
	(jump %funcall-in-auxiliary-stack-buffer1)))
    (parallel
     (assign (amem (stack-pointer 1)) memory-data)
     (increment-stack-pointer)
     (assign top-of-stack memory-data)
     (call funcall-n-ignore)))
  ;; Mark this frame as the bottom frame so we trap if it tries to return
  (assign frame-misc-data (logior frame-misc-data	;Cause trap on return
				  (b-constant (+ (byte-mask frame-buffer-underflow-bit)
						 (byte-mask frame-bottom-bit)
						 (byte-mask frame-trace-bit)))))
  (parallel
   (assign frame-previous-frame quote-nil)	;No back-pointer in this frame
   (next-instruction)))

;Subroutine to save the main stack buffer’s context and select the auxiliary buffer,
;giving it a freshly-created small control stack, and no binding stack
;This control stack resides in virtual-physical space.
(defucode switch-to-auxiliary-stack-buffer
  ;; State save
  (assign %other-pc pc)
  (assign %other-frame-pointer frame-pointer)
  (assign %other-stack-pointer stack-pointer)
  (assign %other-control-stack-low %control-stack-low)
  (assign %other-control-stack-limit %control-stack-limit)
  (assign %other-binding-stack-low %binding-stack-low)
  (assign %other-binding-stack-limit %binding-stack-limit)
  (assign %other-binding-stack-pointer %binding-stack-pointer)
  (assign %other-catch-block-list %catch-block-list)
4,887,235
	447	448
  (assign %other-stack-group-status-bits %current-stack-group-status-bits)
  ;; Setup new state
  (assign %control-stack-low (set-type auxiliary-stack-buffer-address dtp-locative))
  (assign %control-stack-limit (set-type (+ %control-stack-low (b-constant 1400))
					 dtp-locative))
  (assign %binding-stack-low (set-type (b-constant 0) dtp-locative))
  (assign %binding-stack-limit %binding-stack-low)

  (assign %binding-stack-pointer %binding-stack-low)
  (assign %catch-block-list quote-nil)
  (assign %current-stack-group-status-bits
	  (set-type (a-constant (field-mask sg-halt-on-error)) dtp-fix))
  (assign frame-pointer (set-type (b-constant 0) dtp-null))	;I guess...
  (assign stack-pointer (1- %control-stack-low))
  (assign stack-limit %control-stack-limit)
  (parallel
   (assign %current-stack-buffer (set-type auxiliary-stack-buffer-address dtp-fix))
   (assign b-temp obus)
   (jump set-stack-buffer)))

;Tell the hardware to use the stack buffer whose address is in b-temp
(defucode set-stack-buffer
  (parallel
   (write-dp-control (ldb b-temp 2 10. current-dp-control))
   (assign current-dp-control obus)
   (return)))

;;; Sequence Break

;Set the sequence break flag in the hardware. This is usually called in an I/O task.
(defucode set-sequence-break
  (parallel
   (write-dp-control (dpb (b-constant 1) 1 2 current-dp-control))
   (assign current-dp-control obus)
   (return)))

;Sequence break is deferred if we are already in the auxiliary stack buffer.
;Otherwise switch stack buffers and call the function SEQUENCE-BREAK with no args.
;There is guaranteed always to be enough extra room in the main stack buffer
;to do the necessary pushes for this. We don’t use an escape function because
;there are no pclsring issues, we want to store the real pc in %other-pc,
;and it would save at most one control-memory location.
;Note that the harduare ensures that the EPC is not incremented past the
;instruction that would have been executed next were it not for the sequence break.
;In the TMC5 the DPC gets incremented, however.
(defucode-at-loc sequence-break 16000
  ;; Clear the flag in the hardware
  (parallel
   (write-dp-control (dpb (b-constant 0) 1 2 current-dp-control))
   (assign current-dp-control obus))
  ;; Defer if already on aux buffer
  (if (equal-pointer %current-stack-buffer auxiliary-stack-buffer-address)
      (parallel (assign %sequence-break-pending quote-t)
		(jump ifu-empty-trap))		;Recycle fake IFU by loading PC
    (drop-through))
  ;; Go call the sequence-break handler
  (machine-version-case
   ((tmc5 ifu)					;Function call will advance the return PC
    (assign pc (pc-plus-number pc (b-constant -1)))) ;so decrement it to cancel that cut
   (otherwise nil))
  (pushval function-sequence-break)
  (parallel
   (pushval (set-type (a-constant 0) dtp-fix))	;No arguments
   (jump %funcall-in-auxiliary-stack-buffer)))

;;;; Page fault trap-out

;Come here if there is a page fault, with the referencing address in VI’IA,
;and the fault tyom (%page-pht-miss or %page-write-fault) in a-temp.
;We will do a "take-pre-trap restore-stack" then call PAGE-FAULT with two
;arguments, on the auxiliary stack buffer, whether or not we were already there.
;The macrocode is in charge of figuring our whether this was a "recursive" page fault.
;There is guaranteed always to be enough extra room in the main stack buffer
;to do the necessary pushes for this.


(defucode page-fault
;; Save debugging information. Storing micro-pc takes two cycles because of
;; AMWA conflict and also because valid NPC needed for following call.
  (assign b-temp (logand (pop-control-stack) (b-constant 37777)))
  (assign a-page-fault-micro-pc (set-type b-temp dtp-fix))
  (parallel
   (assign a-page-fault-address vma)
   ;; Restore sp to its state at the start of the instruction
   (call restore-stack-pointer))
  ;; Push funcall block for entering the page-fault macrocode
  (pushval function-page-fault)
  (pushval (set-type vma dtp-fix))
  (pushval (set-type a-temp dtp-fix))
  (pushval (set-type (a-constant 2) dtp-fix))	;2 args
  ;; Restore pc to its state at start of instruction (now that vma is saved)
  (machine-version-case
   ((ifu tmc5) nil)	;Hardware takes care of it
4,887,235
	449	450
   ((tmc)
    (if (equal-pointer a-page-fault-micro-pc
		       (b-constant '(build-task-state cpc ifu-empty-trap-1 npc 0 csp 0)))
	(drop-through)		;Kludge: don’t back up PC if fault on inst fetch
        (assign pc (pc-plus-number pc (b-constant -1))))))
  ;; Call the function, switching to auxiliary stack buffer if not already there
  (call-select-and-return-to
   (equal-pointer %current-stack-buffer auxiliary-stack-buffer-address)
   funcall-n-ignore %funcall-in-auxiliary-stack-buffer
   pclsr))

  ;Temporary for debugging. If you see this, it isn’t here.
  (definst %hack no-operand
    (nop)
    (nop)
    (nop)
    (next-instruction))

F:>lmach>ucode>CATCH.LISP.10

;;; -*- Mode:Lisp; Package:Micro; Base:8; Lowercase:yes -*-
;;; (c) Copyright 1982, Symbolics, Inc.

; Microcode for catch/throw/unwind-protect instructions

;Get defmicro and all his hosts
#M
(declare (cond ((not (status feature lmucode))
		(load 'udcls))))

;Initialize %catch-block-list to nil (temporary kludge)
; This is now done by >lmach>sysdfl
;(defareg %catch-block-list *nil*)

;Temporaries
(reserve-scratchpad-memory 2424 2430)

(defareg a-catch-pc)
(defareg a-catch-nwords)

;:PUSHVAL without setting the top-of-stack register
(defmicro pushval1 (val)
  `(parallel (assign (amem (stack-pointer 1)) (set-cdr ,val cdr-next))
	     (increment-stack-pointer)))

;This micro writes the code for the catch-block-creation instructions
(defmicro catch-open (value-disposition &optional unwind-protect-hair)
  (setq value-disposition (find-position-in-list value-disposition
						 '(ignore stack return multiple)))
  `(sequential
    ;; The tag is already in the stack. Now push the PC, BSP, and thread.
    (if (not-zero-fixnum macro-unsigned-immediate)
	(pushval1 (pc-add pc macro-unsigned-immediate))
      ;; Offset of zero means pop offset off the stack, and push PC back on.
      ,(if (not unwind-protect-hair)
	   `(newtop (pc-add pc top-of-stack))
	 ;; hairy case for unwind-protect, twiddle the stack
	 `(sequential
	   (parallel
	    (assign b-temp next-on-stack)
	    (assign next-on-stack top-of-stack))
	   (newtop (pc-add pc b-temp)))))
    (pushval %binding-stack-pointer)
    (pushval-with-cdr (set-ccr %catch-block-list ,value-disposition))
    ;; Now link up to the list and set the flag bit in the frame
    (assign %catch-block-list
	    (set-type (- stack-pointer (b-constant 3)) dtp-locative))
    (parallel (assign frame-catch-bit (b-constant 1))
	      (next-instruction))))

(definst catch-open-ignore (unsigned-pc-relative needs-stack)
  (catch-open ignore))

(definst catch-open-stack (unsigned-pc-relative needs-stack)
  (catch-open stack))

(definst catch-open-return (unsigned-pc-relative needs-stack)
  (catch-open return))

(definst catch-open-multiple (unsigned-pc-relative needs-stack)
  (catch-open multiple))

;---This uses T as the magic tag meaning unwind-protect, This is temporary.
(definst unwind-protect-open unsigned-pc-relative
  (sequential (pushval quote-t)
	      (catch-open ignore t)))


;Closing off the current catch block. We are given a number of words at
;the top of the stack to be preserved. Everything between them and the
;base of the catch block is recoved from the stack, the binding stack
4,887,235
	451	452
;is unwound if necessary, the block is unthreaded, bits in the frame
;header are cleared as necessary. Now if the catch block was an unwind-protect,
;the cleanup handler is pushj’ed to; otherwise the instruction simply returns.

(definst catch-close unsigned-immediate-operand
  (parallel (assign a-catch-nwords macro-unsigned-immediate)
	    (jump catch-close-1)))

(definst catch-close-multiple no-operand
  (parallel (check-arg-type top-of-stack top-of-stack-a dtp-fix)
;;XXXbrad (1+?
	    (assign b-catch-nwords (1- top-of-stack-a))
	    (jump catch-close-1)))

;a-catch-nwords has the number of words to be preserved at the top of the stack
(defucode catch-close-1
  ;; Make the catch block addressable. Assume it resides in the current frame.
  (assign xbas %catch-block-list)
  ;; --- First we should fcol around with unsafe pointer-s to the stack
  ;; Pop tho binding stack since that can pclsr
  (assign b-temp (amem (xbas 2)))
  (if (not-equal-pointer b-temp %binding-stack-pointer)
      (call pop-binding-stack-to-b-temp)
		;restore xbas?
    (drop-through))
  ;; Copy Out the parts of the catch block that we will need
  (assign b-temp (amem (xbas 0)))		;Catch tag
  (if (equal-typed-pointer b-temp quote-t)	;unwind-protect
      (sequential
       (parallel
	(assign a-catch-pc (amem (xbas 1)))	;Cleanup handler address
	(call catch-close-2))
       (pushval pc)				;Now pushj to cleanup handler
       (assign pc a-catch-pc)			;Don’t use set-pc. We must not pclsr
       (nop)					;and try to close the catch over again.
	(next-instruction))			;Set the PC first. then take any page foult.
	(goto catch-close-2)))

;Blt down the stack (cannot pclsr after this point)
(defucode catch-close-2
  (assign b-temp frame-pointer)			;Save FP used as a temporary
  (assign b-temp-2 stack-pointer)		;Last word to save
  (assign frame-pointer (- b-temp-2 a-catch-nwords))	;First word to save-1
  (assign stack-pointer (1- %catch-block-list))	;Flush stack down to base of block
  (parallel
    (assign %catch-block-list (amem (xbas 3))) ;Unthread this catch block
    (call blt-stack))
  (parallel
    (assign frame-pointer b-temp)		;Restore FP
    (if (data-type? %catch-block-list dtp-locative)
	(if (greater-or-equal-pointer %catch-block-list b-temp)
	    (return)				;Still some catch blocks in this frame
	    (drop-through))
      (drop-through)))
  (parallel
   (assign frame-catch-bit (b-constant 0))		;No more blocks this frame, clear bit
   (return)))
F:>lmach>ucode>

; -*- Mode:Lisp; Base:8; Lowercase:yes -*-

; Bogus Microcode for testing that various things are possible
; Not all of this will work in the simulator

;Get defmicro and all his hosts
(declare (cond ((not (status feature lmucode))
		(load 'udcls))))

;Micro for the first cycle of a trap handler.
;Finishes the state save by calling for a PUSHJ. which saves
;the original CPC (now in NPC) onto the stack. The original NPC
;is already on the stack.
(defmicro trap-save ()
  '(microinstruction control-stack pushj))

;Micro for the last two cycles of a trap handler.
;Takes arguments of what else to do in those cycles, that
;seeming clear-er- than throwino a parallel around the sequence.
;We restore the NPC and the CPC by twice popping the control
;stack into NPC. In the second cycle we also use NPC as
;as the source for CPC. Thus the push order is NPC, CPC and
;the pop order is CPC, NPC.
(defmicro trap-restore (cycle-1 cycle-2)
  `(sequential
    (parallel
     ,cycle-1
     (microinstruction control-stack popj npc ctos))
    (parallel
     ,cycle-2
     (microinstruction control-stack popj npc ctos cpc npc))))
