4,887,235 153 154 and pos upfrom (\ pos skip-step) by skip-step do (store-into-block succ1 succ pos))))))) ;Second pass -- find blocks with npc-predecessor mics that are not in blocks ; and consequently weren't seen in the first pass. Also find blocks with ; component mics with npc-predecessor mics. In either case we create a new ; block and make it the pedecessor of the found block. In the first case ; this completes the data structure that tells us what size and shape hole ; we need to find in control memory; in the second case it avoids unnecessarily ; making two copies of a mic. ; However, if the alternative to making two copies of a mic is to create a chain ; of 5 skip blocks in a row, which cannot be located when we have 8K control ; memory, then we would rather duplicatc the mic. (defun determine-other-successors () ;; This loop repeats until no new address blocks are created (loop for already-done = nil then previous-address-block-list as previous-address-block-list = *address-block-list* until (eq *address-block-list* already-done) do ;; This loop does each address block that was not done before (loop for lst = *address-block-list* then (cdr lst) until (eq 1st already-done) as block = (car lst) as chain-length = (loop as b = block then (address-block-successor b) while b while (eq (address-block-kind b) 'skip) count t) as block-predecessors = (loop for mic in (address-block-mic-predecessors block) when (and (null (micabs-blocks mic)) (symbolp (mic-address-constraints mic))) ;NIL or UNIQUE unless (memq mic res) collect mic into res finally (return res)) as other-predecessors = (loop for mic being the array-elements of block unless (null mic) nconc (loop for mic in (micabs-predecessors mic) when (symbolp (mic-address-constraints mic)) when (< (+ (max-predecessor-chain-length mic) chain-length) 5) unless (memq mic res) collect mic) into res finally (return res)) as predb = (address-block-predecessor block) with slot do ;;--- I'm fairly sure that I don't need to worry about aliases here ;; What we want to do is first store all the block-predecessors then ;; fill in the available gaps with other-predecessors. However the ;; other-predecessors have stronger address requirements. So we will ;; first do the block-predecessors, which may leave one location left ;; over for other-predecessors. After that, fill in any available holes ;; with other-predecessors, or create a new predecessor block. (loop for mic in block-predecessors do ;; Find a place to put this predecessor, by force if necessary (loop doing (multiple-value (predb block) (make-address-block-predecessor block predb)) until (loop for pos from 0 below (array-length predb) when (null (aref predb pos)) return (setq slot pos)) do (setq predb nil)) ;This predb used up, make new one (store-into-block mic predb slot)) ;; If a predecessor exists, and free slots fortuitously exist in the right ;; places, fill them with the other-predecessors. If no predecessor exists, ;; and there are other-predecessors, it can't hurt (much!) [sic] to make one. (cond (other-predecessors (multiple-value (predb block) (make-address-block-predecessor block predb)) (loop for mic in other-predecessors as target = (mic-npc-successor mic) when (loop for succ being the array-elements of block using (index pos) thereis (and (eq succ target) (null (aref predb (setq slot pos))))) do (store-into-block mic predb slot))))))) ;Make a block to preceed the given block, if necessary. ;If the second argument is non-NIL (we already have a predecessor available), ;then don't make a new one, except if this block is already located, in which ;case we make a copy of it and a predecessor of the copy. This is necessary ;when the block's predecessor is a mic at a fixed address. ;lf the second argument is NIL, then make a predecessor. If the block already ;has a predecessor, make a copy of the block so that a second predecessor can exist. ;Two values: the preceding and succeeding blocks (defun make-address-block-predecessor (block predb) (prog () (if (if (null predb) (address-block-predecessor block) (or (address-block-locations block) (return predb block))) (setq block (copy-address-block block))) (let ((predb (make-address-block (address-block-kind block)))) (setf (address-block-successor predb) block) 4,887,235 155 156 (setf (address-block-predecessor block) predb) (return (values predb block))))) ;Copy a block (and its successors) when space preceding the block is overcrowded (defun copy-address-block (block &aux new) (setq new (make-address-block (address-block-kind block))) (push (list new 0) (address-block-aliases block)) (loop for mic being the array-elements of block using (index pool) do (store-into-block mic new pos)) (cond ((address-block-successor block) (setq block (copy-address-block (address-block-successor block))) (setf (address-block-predecessor block) new) (setf (address-block-successor new) block))) new) (defun max-predecessor-chain-length (mic) (let ((preds (micabs-predecessors mic))) (if (null preds) 1 ;This test is unnecessary in the old loop by coincidence (1+ (loop for mic in preds ;and superfluous in the new maximize (max-predecessor-chain-length mic)))))) ;;;; Microinstruction linker -- address assignment (defun assign-fixed-addresses () (setq *undefined-opcode-standin* (make-micabs tag '*undefined-opcode-standin*)) (store-field *undefined-opcode-standin* 'spec 'halt) ;; Store halts in the dispatch locations for all undefined opcodes ;; and all defined but unimplemented cpcodes (loop with ucode-alist = (cdr (assp *machine-version* *ucode-alist-alist*)) for i from 0 to 1777 ;Opcode dispatch unless (and (= i 376) (eq *machine-version* 'proto)) ;no-operand-subdispatch unless (assq (aref *opcode-table* i) ucode-alist) do (aset *undefined-opcode-standin* *microinstruction-memory* (lsh i 2))) ;; Store any microinstructions that have no freedom of location at all (loop for bucket being the array-elements of *microinstruction-hash-table* do (loop for mic in bucket as con = (mic-address-constraints mic) do (cond ((numberp con) (locate-inst mic con)) ((listp con) (dolist (loc con) (locate-inst mic loc)))))) ;; Now go fill in any unused reserved locations with a halt instruction ;; so that no floating instructions will float into them (selectq *machine-version* (proto (store-default-inst 10000 *undefined-opcode-standin*) ;Transport trap (loop for i from 10010 to 10015 ;Type trap (4 locs), map miss (2 locs) do (store-default-inst i *undefined-opcode-standin*)) (loop for i from 10020 to 10022 ;IFU exceptions? do (store-default-inst i *undefined-opcode-standin*))) ((tmc tmc5) (loop for mem-state from 0 to 30 by 10 do (loop for i in '(0 1 4 5 6 7) do (store-default-inst (logior 10000 mem-state 1) *undefined-opcode-standin*))) (store-default-inst 14000 *undefined-opcode-standin*) ;IFU traps (store-default-inst 16000 *undefined-opcode-standin*)) (otherwise (ferror nil "What are the trap addresses for ~S?" *machine-version*)))) (defun assign-floating-addresses (&aux (freep 0)) ;; Now pack the address blocks into available free spaces (assign-address-blocks) ;; Now pack npc-chains of instructions not involving any blocks (assign-npc-chains) ;; Now assign any remaining instructions arbitrarily (loop for bucket being the array-elements of *microinstruction-hash-table* do (loop for mic in bucket do (setq freep (assign-floating-mic mic freep)))) (if *unresolved-symbolic-references* (setq freep (assign-floating-mic *undefined-tag-standin* freep)))) (defun assign-floating-mic (mic freep) (or (micabs-addresses mic) (locate-inst mic (loop until (null (aref *microinstruction-memory* freep)) do (incf freep) (if (>= freep *microinstruction-memory-size*) (ferror nil "Gleep! Microinstruction memory overflows")) finally (return freep)))) freep) (defun locate-inst (mic bc &aux tem) (cond ((null (setq tem (aref *microinstruction-memory* loc))) (aset mic *microinstruction-memory* loc) (push loc (micams-addresses mic)) ;If this is somebody's predecessor, he is now absolutely constrained. (let ((succ (mic-npc-successor mic))) (cond ((typep succ 'micabs) (locate-inst succ (npc-next-loc loc))) ((typep succ 'address-block) (locate-address-block succ (logand (npc-next-loc loc) (lognot (address-block-bit-mask succ)))))))) 4,887,235 157 158 ((neq tem mic) (ferror nil "Two different microinstructions trying to go in same location;~e ~S and ~S" (mic-tag mic) (mic-tag tem))))) ;Note that this does not remember the location nor link to successors ;Use this only for "fake" mic's (defun store-default-inst (loc mic) (or (aref *microinstruction-memory* loc) (aset mic *microinstruction-memory* loc))) (defun npc-next-loc (loc) (+ (* (//I loc *npc-modulus*) *npc-modulus*) (\ (+ loc *npc-increment*) *npc-modulus*))) ;I don't really want to solve the general bin-packing problem, so I guess I will ;just assign the largest blocks first, and assign down from the top of memory, ;and hope for the best. Doesn't fill holes in big blocks with little blocks! ;--- I'm fairly sure this is going to have to done over in a cleverer way ; Well. it seems to work, doesn't it.... (defun assign-address-blocks () ;; Largest blocks first. But only blocks without predecessors, and not ;; unnecessary duplicate aliases, need be located, (loop for block in (sort (loop for block in *address-block-list* when (and (null (address-block-predecessor block)) (null (address-block-aliases block)) (null (address-block-locations block))) collect block) #'(lambda (b1 b2) (> (address-block-size b1) (address-block-size b2)))) with disp-freep = (- *microinstruction-memory-size* (* 17 *dispatch-increment*)) with skip-freep = (- *microinstruction-memory-size* *skip-increment*) when (eq (address-block-kind block) 'skip) do (setq skip-freep (find-space-for-block block skip-freep)) else do (setq disp-freep (find-space-for-block block disp-freep)))) (defun address-block-size (block) (if (address-block-successor block) (+ (array-length block) (address-block-size (address-block-successor block))) (array-length block))) (defun find-space-for-block (block freep) (do ((b block (address-block-successor b)) (bits 0 (logior (address-block-bit-mask b) bits)) (width 0 (max (array-length b) width)) (length 0 (1+ length))) ((null b) (decf freep length) (loop when (minusp freep) do (error 'microinstruction-memory-overflow ':msg (format nil "Cannot locate chain of ~D blocks" length) ':chain-head block) until (loop repeat length for pos upfrom freep always (loop for pos upfrom pos by (logand bits (- bits)) repeat width ;skip/dspatch bits are adjacent! always (null (aref *microinstruction-memory* pos)))) do (decf freep)) (locate-address-block block freep) freep))) ;Locate all of the instructions in this address block, based on bc ;Note that an address-block can get located twice, if it is an npc-successor ;of two mic's both with fixed address constraints. (defun locate-address-block (block loc) (push loc (address-block-locations block)) (loop for mic being the array-elements of block as pos upfrom loc by (if (eq (address-block-kind block) 'skip) *skip-increment* *dispatch-increment*) unless (null mic) do (locate-inst mic pos)) (if (address-block-successor block) (locate-address-block (address-block-successor block) (npc-next-loc loc)))) ;Find all microinstruction chains that must be in consecutive addresses ;and are not already located (none of them are in blocks and the head of ;the chain is not assigned to a fixed address). Find places in memory ;to stuff them. (defun assign-npc-chains () :; This loop iterates over all unlocated chain hoads, longest chains first (loop for (length . mic) in (sortcar (loop for bucket being the array-elements of *microinstruction-hash-table* nconc (loop for mic in bucket when (and (null (micabs-addresses mic)) (null (micabs-predecessors mic)) (typep (mic-npc-successor mic) 'micabs)) collect (cons (mic-npc-chain-length mic) mic))) #'>) with freep = 0 do (locate-inst mic (setq freep (find-space-for-chain freep length mic))) (incf freep length))) 4,887,235 159 160 (defun mic-npc-chain-length (mic) (loop for mic = mic then (mic-npc-successor mic) until (null mic) count t)) (defun find-space-for-chain (freep length mic) (loop with block-start = nil for freep upfrom freep by 1 when (>= freep *microinstruction-memory-size*) do (error 'microinstruction-memory-overflow ':msg (format nil "Can't locate ~D-entry NPC chain of microinstructions" length) ':chain-head mic) when (null (aref *microinstruction-memory* freep)) do (cond ((null block-start) (setq block-start freep)) ((zerop (logand 377 freep)) (setq block-start freep)) ((= (- (1++ freep) block-start) length) (return block-start))) else do (setq block-start nil))) ;A debugging function (defun print-chain (mic-or-block) ;or nil (typecase mic-or-block (micabs (format t "~&MIC: ~A" (mic-tag mic-or-block)) (print-chain (mic-npc-successor mic-or-block))) (address-block (format t "~&~A-BLOCK[~O]: " (address-block-kind mic-or-block) (array-length mic-or-block)) (format:print-list standard-output "~A" (loop for mic being the array-elements of mic-or-block collect (if mic (mic-tag mic) "-"))) (print-chain (address-block-successor mic-or-block))))) (defflavor microinstruction-memory-overflow (msg chain-head) (error) :initable-instance-variables) (defmethod (microinstruction-memory-overflow :report) (stream) (format stream "Gleep! Microinstruction memory overflow~%~A~%The chain is:~%" msg) (let ((standard-output stream) (prinlength nil)) (print-chain chain-head))) (compile-flavor-methods microinstruction-memory-overflow) ;;;; Microinstruction linker -- plug in successor addresses (defun plug-in-successors () (loop for loc from 0 below *microinstruction-memory-size* with succ as mic = (aref *microinstruction-memory* loc) unless (null mic) do (if (setq succ (mic-naf-successor mic)) (store-number mic (get-mic-or-block-address succ) u-naf)) (if (setq succ (mic-npc-successor mic)) (cond ((typep succ 'micabs) (or (eq (aref *microinstruction-memory* (npc-next-loc loc)) succ) (ferror nil "~S's npc-successor isn't there!" (mic-tag mic)))) ((typep succ 'address-block) (or (address-block-effectively-at succ (logand (npc-next-loc loc) (lognot (address-block-bit-mask succ)))) (ferror nil "~S's npc-successor isn't there!" (mic-tag mic)))))))) (defun get-mic-or-block-address (x) (cond ((typep x 'micabs) (car (micabs-addresses x))) ((typep x 'address-block) (or (car (address-block-locations x)) (let ((alias (caar (address-block-aliases x)))) (+ (get-mic-or-block-address alias) (* (cadar (address-block-aliases x)) (logand (address-block-bit-mask alias) (- (address-block-bit-mask alias)))))))))) (defun address-block-effectively-at (block loc) (or (memq loc (address-block-locations block)) (loop for (b offset) in (address-block-aliases block) thereis (address-block-effectively-at b (+ (* offset (logand (address-block-bit-mask b) (- (address-block-bit-mask b)))) lo))))) (defun resolve-constants () (setq *a-constant-list* (resolve-constants1 *a-constant-hash-table*)) (setq *b-constant-list* (resolve-constants1 *b-constant-hash-table*))) (defun resolve-constants1 (hash-table) (local-declare ((special constants)) (let ((constants nil)) (maphash-equal #'(lambda (val loc) (push (cons loc 4,887,235 161 162 (cond ((numberp val) val) ((and (listp val) (eq (car val) 'build-task-state)) (resolve-task-state (cdr val))) (t (ferror "~S illegal constant" val)))) constants)) hash-table) constants))) (defun resolve-task-state (options) (let ((cpc nil) (npc nil) (csp 17)) (loop for (opt val) on options by 'cddr do (selectq opt (cpc (setq cpc (resolve-cues-location val))) (npc (setq npc (resolve-cmem-location val))) (csp (setq cop val)) (otherwise (ferror "~S illegal in BUILD-TASK-STATE" opt)))) (or cpc (ferror "CPC not specified in ~S" (cons 'build-task-state options))) (or npc (setq npc (dpb (1+ cpc) 0010 cpc))) (dpb csp 3404 (dpb npc 1616 cpc)))) (defun resolve-cmem-location (loc &aux mic) (cond ((symbolp loc) (if (setq mic (cdr (assq loc *microinstruction-tag-alist*))) (car (micabs-addresses mic)) (format error-output "~&WARNING: ~S not found for build-task-state~%" loc) 0)) ((numberp loc) loc) ((and (listp loc) (eq (car loc) 'npc-successor)) (setq loc (resolve-cmem-location (cadr loc))) (dpb (1+ loc) 0010 loc)) (t (ferror "~S illegal cmem-location for build-task-state" loc)))) ;;;; File interface (defun new-microcode-version () (let ((si::*system-being-made* (si:find-system-named "MICROCODE")) (si::silent-p: nil)) (si:increment-compiled-version-1) (si:increment-loaded-version-1))) ;--- Someday these might be a MAKE-SYSTEM transformation (defun compile-the-microcode (*machine-version*) (write-the-microcode *machine-version* t)) (defun write-the-microcode (*machine-version* &optional (link-p nil) (name (string-append *machine-version* "-MIC")) (version (si:get-system-version "MICROCODE"))) (or (boundp 'lcold:*most-negative-immediate-number*) (icold:setup-crucial-variables nil)) (let ((patnname (fs:make-pathname ':host "SYS" ':directory "L-UCODE" ':name name ':version version))) (with-open-file (log (funcall pathname ':new-type "LOG") '(:print)) (let ((standard-output (make-broadcast-stream log standard-output))) (if link-p (link-the-microcode *machine-version*)) ;; Write out various files (write-mic-file (funcall pathname ':new-type "MIC") name version) (write-sym-file (funcall pathname ':new-type "SYM") name version) (write-err-file (funcall pathname ':new-type "ERR") name version))))) (defun write-mic-file (pathname name version) (with-open-file (stream pathname '(:out :fixnum)) (let* ((length (min (string-length name) 32.)) (name16 (make-array (// (1+ length) 2) ':type 'art-16b ':displaced-to name))) (funcall stream ':tyo length) (funcall stream ':string-out name16)) (funcall stream ':tyo version) ;; Type map (let ((ntypes (lsh (length *type-maps*) 6))) (format t "~&Type map - ~O locations" ntypes) (funcall stream ':tyo 1) (funcall stream ':tyo 0) (funcall stream ':tyo ntypes) (funcall stream ':tyo 1) (loop for i from 0 below ntypes do (funcall stream ':tyo (aref *type-maps* i)))) ;; A and B memories (write-a-b-memory stream 2 *a-memory-values* *a-constant-list* "A") (write-a-b-memory stream 3 *b-memory-values* *b-constant-list* "B") ;; Control memory (loop with length = (array-active-length *microinstruction-memory*) with total = 0 with patches for start from 0 below length as mic = (aref *microinstruction-memory* start) do (cond ((null mic)) ((null (setq patches (mic-load-time-patches mic))) 4,887,235 163 164 (let ((count (loop for address from start below length as mic = (aref *microinstruction-memory* address) while (not (null mic)) while (null (mic-load-time-patches mic)) sum 1))) (incf total count) (funcall stream ':tyo 4) (funcall stream ':tyo start) (funcall stream ':tyo count) (funcall stream ':tyo 7) (loop repeat count for address from start as mic = (aref *microinstruction-memory* address) when (not (null mic)) do (loop with val = (mic-code mic) repeat 7 for ppss from 0020 by 2000 do (funcall stream ':tyo (ldb ppss val)))) (incf start (1- count)))) (t ;; Write cmem location that needs to be patched: ;; 104
,