4,887,235 93 94 ;;; -*- Mode:Lisp; Package:Micro; Base:8; Lowercase:t -*- ;;; (c) Copyright 1982, Symbolics, Inc. ; Simulator of L-machine microcode ; This file contains the framework needed to run everything else ; This part gets loaded before the architecture definitions, SIMX is loaded later ;Kludges #M (declare (muzzled t)) #M (eval-when (load) (putprop 'loop-collect-init (get 'loop 'autoload) 'autoload)) ;Memories (defconst *main-memory-size* 40000) ;16K should be enough for anyone! (defvar *main-memory* (make-array *main-memory-size*)) (defconst *a-memory-size* 10000) ;Possibly only half of this will exist (defvar *a-memory* (make-array *a-memory-size*)) (defvar *b-memory* (make-array 400)) (defconst *page-size* 400) (defconst *quantum-size* *page-size*) ;small for now. And no virtual mapping. (defvar *address-space-map* (make-array 2000)) ;by 5 (defconst *a-memory-virtual-address* (lsh 1 16.)) ;arbitrarily chosen (defvar *opcode-table* (make-array 2000)) ;Registers (defvar *vma*) ;Virtual memory address (defvar *pma*) ;Physical memory address (defvar *mem*) ;Data to and from memory (defvar *pc*) ;Macroprogram next-instruction pointer (in halfwords) (defvar *instruction*) ;Current instruction ;Base registers ;These contain 28-bit addresses that also point at the internal memory (defvar *frame-pointer*) (defvar *stack-pointer*) ;can count up and down (defconst *base-register-list* '(*frame-pointer* *stack-pointer*)) ;These registers control address mapping when internal memory ;is addressed via *frame-pointer* or *stack-pointer* (defvar *stack-buffer-address* 0) ;Must be multiple of 400 (defvar *stack-buffer-mask* 1777) ;Low 8 bits must be 1's ;Because I can't read long strings of 7s ;This has to use sub1 and expt so I can get a 36-bit mask in Maclisp ;Note that the argument must be a number (eval-when (compile load eval) (defun (mask macro) (x) (let #Q ((default-cons-area working-storage-area)) (sub1 (expt 2 (cadr x)))))) ;Basic Word Formats ;(comment ;comes from SYSDEF now (eval-when (compile eval load) (defconst *data-types* '( ;somewhat preliminary! ;Low 16 types dtp-null dtp-nil dtp-symbol dtp-extended-number dtp-external-value-cell-pointer dtp-locative dtp-list dtp-compiled-function dtp-array dtp-closure dtp-entity dtp-lexical-closure dtp-select-method dtp-instance dtp-header-p dtp-header-i ;Fixnum uses up 16 types dtp-fix dtp-fix dtp-fix dtp-fix dtp-fix dtp-fix dtp-fix dtp-fix dtp-fix dtp-fix dtp-fix dtp-fix dtp-fix dtp-fix dtp-fix dtp-fix ;Flonum uses up 16 types dtp-float dtp-float dtp-float dtp-float dtp-float dtp-float dtp-float dtp-float dtp-float dtp-float dtp-float dtp-float dtp-float dtp-float dtp-float dtp-float ;High 16 types (note: dtp-even-pc dtp-odd-pc must be 0 and 10 ; in this group of 16) 4,887,235 95 96 dtp-even-pc dtp-gc-forward dtp-one-q-forward dtp-header-forward dtp-body-forward dtp-66 dtp-66 dtp-67 dtp-odd-pc dtp-71 dtp-72 dtp-73 dtp-74 dtp-75 dtp-76 dtp-77)) (defconst *cdr-codes* '(cdr-next cdr-nil cdr-normal cdr-spare)) );eval-when ;);comment (declare (special *data-types* *cdr-codes*)) ;in SYSDEF (defmacro pointer-field (q) `(logand (mask 28.) ,q)) (defmacro fixnum-field (q) `(logand (mask 32.) ,q)) (defmacro high-type-field (q) `(ldb 4002 ,q)) (defmacro type-field (q) `(ldb 3406 ,q)) (defmacro cdr-field (q) `(ldb 4202 ,q)) (defmacro set-cdr (value cdr) (let ((cdr-code (if (numberp cdr) cdr (find-position-in-list cdr *cdr-codes*)))) (or cdr-code (ferror nil "~S undefined cdr code" cdr)) `(dpb ,cdr-code 4202 ,value))) (defmacro set-type (ptr dtp) (let ((dtp-code (find-position-in-list dtp *data-types*))) (or dtp-code (ferror nil "~S undefined data type" dtp)) (if (memq dtp '(dtp-fix dtp-float)) `(dpb ,(lsh dtp-code -4) 4002 (logand (mask 32.) ,ptr)) `(dpb ,dtp-code 3406 (logand (mask 28.) ,ptr))))) ;Number fields (fixnum, only for now) (defun unbox-fixnum (q) (- (logxor (fixnum-field q) 1_31.) 1_31.)) ;;; -*- Mode:Lisp; Package:Micro; Base:8; Lowercase:t -*- ;;; (c) Copyright 1982, Symbolics, Inc. ; Simulator of L-machine microcode ; This file gets loaded after the architecture definitions #M (declare (load 'sim)) #M (declare (*lexpr address-add) (fixnum (even-instruction fixnum) (odd-instruction fixnum) (instruction-opcode) (instruction-unsigned-immediate) (instruction-signed-immediate) (pc-add fixnum fixnum) (instructon-baseno) (instruction-offset) (stack-address fixnum) (address-add notype fixnum))) ;Accessor macros for named memory slots (defmacro top-of-stack () '(aref *b-memory* 360)) (defmacro stack-limit () '(aref *b-memory* 344)) (comment (defmacro temp-1 () '(aref *b-memory* 361)) (defmacro temp-2 () '(aref *b-memory* 362)) (defmacro temp-3 () '(aref *b-memory* 363)) (defmacro temp-4 () '(aref *b-memory* 364)) (defmacro temp-5 () '(aref *b-memory* 365)) (defmacro trans-temp () '(aref *b-memory* 366)) ) ;comment (defmacro stack-low () '(aref *a-memory* 2403)) (defmacro a-stack-overflow () '(aref *a-memory* 2404)) ;Accessor macros for fields of the VMA (defmacro vma-quantum () '(// (pointer-field *vma*) *quantum-size*)) (defmacro vma-page () '(// (pointer-field *vma*) *page-size*)) (defmacro vma-within-page () `(logand *vma* ,(1- *page-size*))) ;Accessors for instructions as fetched from memory (defun even-instruction (mem) (dpb (ldb 4201 mem) 2001 (ldb 0020 mem))) (defun odd-instruction (mem) (dpb (ldb 4301 mem) 2001 (ldb 2020 mem))) ;Accessors for fields of the instruction (defun instruction-opcode () (ldb 1011 *instruction*)) (defun instruction-no-operand-opcode () (+ (ldb 0011 *instruction*) 1000)) (defun instructon-unsigned-immediate () (ldb 0010 *instruction*)) (defun instruction-signed-immediate () (- (logxor 200 (instruction-unsigned-immediate)) 200)) (defun instruction-baseno () (ldb 0701 *instruction*)) 4,887,235 97 98 (defun instruction-offset () (ldb 0007 *instruction*)) ;Address arithmetic for internal memory (defun address-add (baseno offset &optional (macrocode nil)) (let ((base-reg (if (numberp baseno) (nth baseno *base-register-list*) baseno))) (and macrocode (eq base-reg '*stack-pointer*) (setq offset (1+ (logior offset 7600)))) (let ((addr (logand (+ (symeval base-reg) offset) (1- *a-memory-size*)))) (stack-address addr)))) (defun stack-address (addr) (+ (logand addr *stack-buffer-mask*) *stack-buffer-address*)) (defmacro local-operand () '(aref *a-memory* (address-add (instruction-baseno) (instruction-offset) t))) ;Accessor macros for the current frame ;The currently executing function (defmacro frame-function () '(aref *a-memory* (address-add '*frame-pointer* -1))) ;A fixnum full of various fields (defmacro frame-misc-data () '(aref *a-memory* (address-add '*frame-pointer* -2))) ;Caller's return PC (defmacro frame-return-pc () '(aref *a-memory* (address-add '*frame-pointer* -3))) ;Top of previous frame - value to restore to (stack-pointer) ;The cdr code of this word is the value disposition (defmacro frame-previous-top () '(aref *a-memory* (address-add '*frame-pointer* -4))) ;Base of previous frame - value to restore to (arg-pointer) (defmacro frame-previous-frame () '(aref *a-memory* (address-add '*frame-pointer* -5))) ;Fields in frame-misc-data (defmacro frame-number-of-args () '(ldb 0006 (frame-misc-data))) (defmacro frame-cleanup-bits () '(ldb 0605 (frame-misc-data))) (defmacro frame-buffer-underflow-bit () '(ldb 0601 (frame-misc-data))) ;PC manipulation (defun pc-add (pc offset) (let ((word (+ (pointer-field pc) (ash offset -1))) (halfword (logxor (ldb 3701 pc) offset (if (minusp offset) 1 0)))) (if (oddp halfword) (set-type word dtp-odd-pc) (set-type word dtp-even-pc)))) (defun pc-plus-number (pc offset) (let ((word (pointer-field pc)) (halfword (+ (ldb 3701 pc) offset))) (setq word (+ word (if (minusp halfword) (1- (// halfword 2)) (// halfword 2)))) (if (oddp halfword) (set-type word dtp-odd-pc) (set-type word dtp-even-pc)))) (defun pc-oddp (pc) (not (zerop (ldb 3701 pc)))) ;Comparisons ; these are all assumed to exist in the real machine (defun equal-pointer (x y) ;28-bit (= (pointer-field x) (pointer-field y))) (defun equal-fixnum (x y) ;32-bit (= (fixnum-field x) (fixnum-field y))) (defun equal-typed-pointer (x y) ;34-bit (= (logand (mask 34.) x) (logand (mask 34.) y))) 4,887,235 99 100 (defun equal-word (x y) (= x y)) ;36-bit (defun greater-pointer (x y) ;28-bit (> (pointer-field x) (pointer-field y))) (defun lesser-pointer (x y) ;28-bit (< (pointer-field x) (pointer-field y))) (defun greater-fixnum (x y) ;32-bit (> (unbox-fixnum x) (unbox-fixnum y))) (defun lesser-fixnum (x y) ;32-bit (< (unbox-fixnum x) (unbox-fixnum y))) (defun lesser-fixnum-unsigned (x y) ;32-bit unsigned (< (fixnum-field x) (fixnum-field y))) (defmacro data-type? (word &rest types) (consify 'or (loop for type in types collect (selectq type (dtp-fix `(= (high-type-field ,word) 1)) (dtp-float `(= (high-type-field word) 2)) (otherwise `(= (type-field ,word) ,(find-position-in-list type *data-types*))))))) (defmacro cdr-code? (word &rest cdrs) (consify 'or (loop for cdr in cdrs collect `(= (cdr-field ,word) ,(cond ((numberp cdr) cdr) ((find-position-in-list cdr *cdr-codes*)) (t (ferror nil "~S illegal cdr code" cdr))))))) ;NIL and T constants (defvar *nil* (set-type 0 dtp-nil)) (defvar *t* (set-type 525252 dtp-symbol)) (eval-when (compile load eval) (defun consify (head list) (cond ((nul list) (ferror nil "something is missing")) ((null (cdr list)) (car list)) (t (cons head list)))) ); eval-when ;In real machine this comes out of the ALU. This routine is a crock. ;Return T if bite 31 and 32 of the alu output differ. (defun overflow-p (alu-output) (not (zerop (logand (ash alu-output -31.) (ash alu-output -32.) 1)))) (comment ;not used any more ;Stands for AND of deciding to trap and the arithmetic trap-address PLA (defun encode-arithmetic-trap-condition (abus-type-mismatch bbus-type-mismatch overflow abus bbus) (and (or abus-type-mismatch bbus-type-mismatch overflow) (cond ((data-type? abus dtp-fix) (cond ((data-type? bbus dtp-fix) 'fixnum-fixnum) ((data-type? bbus dtp-float) 'fixnum-flonum) ((data-type? bbus dtp-extended-number) 'fixnum-extnum) (t 'error))) ((data-type? abus dtp-float) (cond ((data-type? bbus dtp-fix) 'flonum-fixnum) ((data-type? bbus dtp-float) 'flonum-flonum) ((data-type? bbus dtp-extended-number) 'extnum-extnum) (t 'error))) ((data-type? abus dtp-extended-number) (cond ((data-type? bbus dtp-fix) 'extnum-fixnum) ((data-type? bbus dtp-float) 'extnum-extnum) ((data-type? bbus dtp-extended-number) 'extnum-extnum) (t 'error))) (t 'error)))) );comment ;Internal memory (A memory) address conversions ;The A memory can be addressed either directly or by a ;base register plus an offset. The two base registers are the ;frame pointer and the stack pointer; the latter is an up/down ;counter. These two base registers are 28-bit registers that ;read and write from the main data path. The offset that can ;be added can be the low 8 bits of a macro-instruction with ;sign-extension controlled jointly by the microcode and the 8th bit, ;or a microcode constant. ;When the stack pointer is used as a base, the high bits of the ;offset and the carry-in are set to 1 to cause, in effect, ;a subtraction (this only happens when the offset comes from ;a macroinstruction) ;The mapping from the result of the addition of base and offset ;to an internal memory address is as follows: the low 8 bits ;go straight through. The high 2 bits come from a special register. ;The middle 2 bits are selectable between the output of the 4,887,235 101 102 ;adder and the special register. The special register and mode ;control are changed when iwitching between the main and auxiliary ;stack buffers. ;For function calling to work efficiently with this, the main ;data path has to be able to add or subtract a small microcode ;constant from either of the base registers, plug in a data type, ;and put the result on the output bus whence it can be written ;into internal memory or into a base register. The address adder ;cannot be used for this since it has to be a 28-bit add. The ;necessary microcode constants are stored in B memory. ;This function sets up a stack at virtual addresses 32000-37777. puts the ;first 1K of it into the stack buffer in the first 1K of A memory, and sets ;up the frame pointers to give a frame for the specified function and ;arguments. Also sets the PC to the function's starting address. This only ;works for functions that use the fast-arg sequence. (defun initialize-sg (function &rest args) ;;Map locations 32000-33777 into A memory 0-1777 ;--- no map yet --- ;;Set pointers to initial frame (setq *frame-pointer* 32005) ;;Build the frame header (setf (frame-misc-data) (set-type (length args) dtp-fix)) (setf (frame-buffer-underflow-bit) 1) (setf (frame-function) function) ;Note that the return PC is given valid data type so that a data ;type check does not go off prematurely before the frame cleanup ;check when returning out the top of a stack group. (setf (frame-return-pc) (set-type 0 dtp-even-pc)) ;no caller (setf (frame-previous-top) (set-cdr (set-type 31777 dtp-locative) 1)) ;empty pdl, for Value (setf (frame-previous-frame) *nil*) ;no caller ;Depends on pointer-field of frame-previous-frame being zero! ;;Store the arguments (setq *stack-pointer* 32004) (loop for arg in args do (pushval arg)) ;;Set up the stack-buffer limit allowing for 100 words of overhead ;;i.e. space for frame header of overflowing frame, for executing ;;trap routines, etc. 100 is hopefully much too high. (setf (stack-limit) (set-type (- 33777 100) dtp-locative)) (setf (stack-low) (set-type 32000 dtp-locative)) (setf (a-stack-overflow) (set-type (- 37777 100) dtp-locative)) ;;Set the PC (setq *pc* (set-type function dtp-odd-pc))) (declare (*lexpr micro-main-loop)) (defun run-sg (function &rest args) (lexpr-funcall #'initialize-sg function args) (micro-main-loop)) ;Debug I/O routines ;Print a word (defun pq (q) (princ (nth (cdr-field q) *cdr-codes*)) (tyo #\sp) (let ((type (nth (type-field q) *data-types*)) (base 8)) (princ type) (tyo #\sp) (selectq type (dtp-fix (prin1 (unbox-fixnum q))) (dtp-float (prin1 (fixnum-field q))) ;--- temporary (otherwise (prin1 (pointer-field q))))) (princ '| |) ;For people who mapcar this #Q (values)) ;Print the pdl (defun pp () (loop for i from *frame-pointer* to *stack-pointers* as ii = (stack-address i) do (format t "~&~O: ~O " ii (aref *a-memory* ii)) (pq (aref *a-memory* ii))) (cond ((not (= (top-of-stack) 4,887,235 103 104 (aref *a-memory* (stack-address *stack-pointer*)))) (format t "~&TOS-register: ~O " (top-of-stack)) (pq (top-of-stack)))) #Q (values)) ;Print the current frame (or any frame) (defun pf (&optional (ap *frame-pointer*)) (loop for i from (- ap 5) below ap for label in '(previous-frame previous-top return-pc misc-data function) as ii = (stack-address i) do (format t "~&~O(~A):~22T~O " ii label (aref *a-memory* ii)) (pq (aref *a-memory* ii))) #Q (values)) ;Print contents of one or more memory locations (defun pm (from &optional (to from)) (loop for addr from (pointer-field from) to (pointer-field to) as data = (raw-mem-read addr) do (format t "~&~O// ~O " addr data) (pq data)) #Q (values)) ;Print contents of one or more internal memory locations (defun pim (from &optional (to from)) (loop for addr from from to to as data = (aref *a-memory* addr) do (format t "~&~O// ~O " addr data) (pq data)) #Q (values)) ;Memory referencing without transport ;This does just enough page mapping to make things work. ;Virtual addresses from stack-low through stack-pointer are mapped ;into the low 1K of internal memory. (defun set-pma-from-vma () (setq *pma* (if (and (<= (pointer-field (stack-low)) *vma*) (<= *vma* (pointer-field *stack-pointer*))) (+ *a-memory-virtual-address* (logand 1777 *vma*)) *vma*))) (defun raw-mem-read (address) (setq *vma* address) (setq *pma* (pointer-field *vma*)) (pma-mem-read)) (defun pma-mem-read () (cond ((>= *pma* *a-memory-virtual-address*) (let ((tem (- *pma* *a-memory-virtual-address*))) (or (< tem 10000) (ferror nil "reading garbage address ~S" *pma*)) (setq *mem* (aref *a-memory* tem)))) ((>= *pma* *main-memory-size*) (ferror nil "reading garbage address ~S" *pma*)) (t (setq *mem* (aref *main-memory* *pma*))))) (defun raw-mem-write (address data) (setq *vma* address *mem* data) (setq *pma* (pointer-field *vma*)) (pma-mem-write data)) (defun pma-mem-write (data) (cond ((>= *pma* *a-memory-virtual-address*) (let ((tem (- *pma* *a-memory-virtual-address*))) (or (< tem 10000) (ferror nil "writing garbage address ~S" *pma*)) (aset data *a-memory* tem))) ((>= *pma* *main-memory-size*) (ferror nil "writing garbage address ~S" *pma*)) (t (aset data *main-memory* *pma*)))) (defun simulate-transporter (transport-type) (loop doing (pma-mem-read) until (selectq (nth (type-field *mem*) *data-types*) ((dtp-nil dtp-symbol dtp-extended-number dtp-locative dtp-list dtp-compiled-function dtp-array dtp-closure dtp-entity dtp-lexical-closure dtp-instance dtp-fix dtp-float dtp-even-pc dtp-odd-pc) t) ;Good types ((dtp-null) (or (memq transport-type '(write bind)) (terror nil "unbound variable//definition"))) 4,887,235 105 106 ((dtp-header-p dtp-header-i) (or (eq transport-type 'header) (ferror nil "bad data type encountered"))) ((dtp-external-value-cell-pointer) (memq transport-type '(bind no-evcp))) ((dtp-one-q-forward dtp-header-forward) (setq *vma* *mem*) nil) ((dtp-body-forward) (setf (trans-temp) *vma*) (raw-mem-read *mem*) (or (data-type? *mem* dtp-header-forward) (terror nil "body forward doesn't point to header fwd")) (setq *vma* (dbp (+ (pointer-field *mem*) (- (pointer-field (trans-temp)) (pointer-field *vma*))) 0034 (trans-temp))) nil) (otherwise (ferror nil "bad data type encountered"))) do (setq *pma* (setq *vma* (pointer-field *vma*))))) (defun mem-read (address &optional (transport-type 'data)) (transport-address address transport-type) *mem*) (defun mem-write (address data &optional (transport-type 'data)) (transport-address address transport-type) (raw-mem-write *vma* data)) ;Actually. doesn't repeat mapping phase );end comment (defun initialize-main-memory (&optional (n-words *main-memory-size*)) (dotimes (i n-words) (aset (set-type i dtp-null) *main-memory* i))) ;Instruction emulation (comment (defvar *next-free-opcode* 0) (defmacro definstruction (name format &body emulator) `(progn 'compile (add-instruction ',name ',format) (defun (,name executor) () . ,emulator))) (defun add-instruction (name format) (let ((opcode (or (car (get name 'instruction-data)) (if (eq format '10-bit-immediate) ;Have to assign group of 4 opcodes ;For simulator these actually have to be aligned (let ((opcode (logand (+ *next-free-opcode* 3) -4))) (if (> (setq *next-free-opcode* (+ opcode 4)) 1000) (error "out of opcodes" name 'fail-act)) opcode) (prog1 *next-free-opcode* (if (> (setq *next-free-opcode* (1+ *next-free-opcode*)) 1000) (error "out of opcodes" name 'fail-act))))))) (putprop name (list opcode format) 'instruction-data) (if (eq format '10-bit-immediate) (loop for i from 1 to 3 do (aset name *opcode-table* (+ opcode i)))) (aset name *opcode-table* opcode))) ) ;comment (defvar *single-step* nil) (comment ;Run using emulator written with def instruction (defun main-loop (&optional (starting-pc *pc*)) (setq *pc* (if (< starting-pc (mask 28.)) (set-type starting-pc dtp-even-pc) ;number = word address starting-pc)) (*catch 'halt (do ((opcode)) (nil) ;;Instruction fetch (raw-mem-read *pc*) (setq *instruction* (if (pc-oddp *pc*) (odd-instruction *mem*) (even-instruction *mem*))) ;; Instruction decode (setq opcode (aref *opcode-table* (instruction-opcode))) ;; Possible debug break (cord ((or *single-step* (null opcode)) (lm-disassemble *pc* 1) (break single-step t))) ;; Increment PC and execute instruction (setq *pc* (pc-plus-number *pc* 1)) (*catch 'pclsr (funcall (get opcode 'executor)))))) 4,887,235 107 108 );comment ;Run using actual microcode emulator (defun micro-main-loop (&optional (starting-pc *pcz)) (setq *pc* (if (< starting-pc (mask 28.)) (set-type starting-pc dtp-even-pc) ;number = word address starting-pc)) (*catch 'halt (do ((opcode) (executor)) (nil) ;;Instruction fetch (raw-mem-read *pc*) (setq *instruction* (if (pc-oddp *pc*) (odd-instruction *mem*) (even-instruction *mem*))) ;;Instruction decode (setq opcode (instruction-opcode)) (if (> opcode 375) (setq opcode (instruction-no-operand-opcode))) (setq opcode (aref *opcode-table* opcode)) ;;Possible debug break (cond ((or *single-step* (null opcode)) (lm-disassemble *pc* 1) (break single-step))) (cond ((null (setq executor (get opcode 'micro-executor))) (lm-disassemble *pc* 1) (terpri) (princ "No micro-executor found. $p to use SIM executor.") (break missing-executor) (setq executor (get opcode 'executor)))) ;;Increment PC and execute instruction (setq *pc* (pc-plus-number *pc* 1)) (aset *pc* *a-memory* 2500) ;Kludge for temporary memory control (*catch 'pclsr (funcall executor)) (setq *pc* (aref *a-memory* 2500))))) ;.. ;Excessively simple assembler (defmacro defmacrocode (pcvar starting-word &body code) `(progn (setq ,pcvar (set-type ,starting-word dtp-even-pc)) . ,(loop for addr upfrom (* 2 starting-word) for inst in code collect `(lm-assemble ,addr ',inst)))) (defmacro defunction (fcnvar starting-word (min-nargs max-nargs rest-arg) constant-list &body code) (or max-nargs (setq max-nargs min-nargs)) ;defaults to no optionals ;--- What to do about this? No encoding in entry instruction for ;--- a function with no constants! (or constant-list (setq constant-list (list *nil*))) `(progn 'compile ;The pointer to the object points at the entry instruction (setq ,fcnvar (set-type ,(+ starting-word (length constant-list) 2) dtp-compiled-function)) ;dtp-header-i, type=compiled-code, lengths of both parts, interp info (aset (set-cdr (set-type ,(+ (1- (length constant-list)) ;Length-3 of Q part (ash (// (+ (length code) 2) 2) ;Length of non-Q part 8)) dtp-header-i) 0) *main-memory* ,starting-word) ;list of function name and debug info (aset *nil* *main-memory* ,(+ starting-word 1)) ;constants/value-function cell references in reverse order ;--- For now, we assume cell references are just numbers! ,@(loop for addr downfrom (+ starting-word 1 (length constant-list)) for const in constant-list do (if (zerop (type-field const)) (setq const (set-type const dtp-locative))) collect `(aset ,const *main-memory* ,addr)) ;entry instruction (aset ,(make-entry-instruction min-nargs max-nargs rest-arg (1- (length constant-list))) *main-memory* ,(+ starting-word 2 (length constant-list))) ;The code . ,(loop for addr upfrom (1+ (* 2 (+ starting-word 2 (length constant-list)))) for inst in code collect `(lm-assemble ,addr ',inst)))) (defun make-entry-instruction (min-nargs max-nargs rest-arg header-offset) (if (> min-nargs max-nargs) (ferror nil "min-nargs ~D > max-nargs ~D ?" min-nargs max-nargs)) (+ header-offset (lsh (if (or rest-arg (> max-nargs 4)) 0 (- (nth max-nargs '(1 3 6 10. 15.)) (- max-nargs min-nargs))) 8))) ;Not called assemble because ncormplr has a global symbol by that name (defun lm-assemble (halfword-addr code) (let ((op (car code)) (arg (cadr code))) 4,887,235 109 110 (let ((opcode (car (get op 'instruction-data))) (format (cadr (get op 'instruction-data))) (inst)) (and opcode (setq inst (if (< opcode 1000) (lsh opcode 8) (+ 377_9 (- opcode 1000))))) (selectq format (no-operand) ((unsigned-immediate-operand signed-immediate-operand constant-operand indirect-operand) (setq inst (dpb arg 0010 inst))) ((signed-pc-relative unsigned-pc-relative) (setq inst (dpb (convert-branch-length halfword-addr arg) 0010 inst))) (10-bit-immediate-operand (setq inst (dpb arg 0010 (+ inst (logand 3_8 arg))))) (address-operand (setq inst (+ inst (lsh (or (find-position-in-list (cadr code) '(arg stack)) (ferror nil "~S illegal base pntr" code)) 7) (logand (if (eq (cadr code) 'stack) (+ (caddr code) 177) (caddr code)) 177)))) (nil (ferror nil "~S undefined Instruction" op)) (otherwise (ferror nil "~S instruction in bad format ~S" op format))) (aset (dpb 1 4002 ;fixnum data type (dpb (ldb 2001 inst) (if (oddp halfword-addr) 4301 4201) (dpb inst (if (oddp halfword-addr) 2020 0020) (aref *main-memory* (// halfword-addr 2))))) *main-memory* (// halfword-addr 2))))) ;;; Convert branch length to hardware format. ;;; The hardware takes the branch offset, rotates it right one bit, and ;;; adds it to the PC. Thus there is a carry from the word offset into ;;; the halfword offset, rather than the reverse as you might expect. ;;; This function really is a case where you want to divide by 2 with ASH, not with // !! (defun convert-branch-length (address length) (let* ((word-offset (+ (ash length -1) (if (and (oddp length) (evenp address)) 1 0))) (halfword-offset (logxor (logand 1 length) (if (minusp word-offset) 1 0)))) (+ (ash word-offset 1) halfword-offset))) (defun lm-disassemble (pc n-insts) (loop repeat n-insts as inst = (if (pc-oddp pc) (odd-instruction (aref *main-memory* (pointer-field pc))) (even-instruction (aref *main-memory* (pointer-field pc)))) as op = (aref *opcode-table* (if (= (ldb 1110 inst) 377) (+ (ldb 0011 inst) 1000) (ldb 1011 inst))) as fmt = (second (get op 'instruction-data)) as imm = (logand (mask 8) inst) do (format t "~&~O(~O) ~O ~A " (pointer-field pc) (if (pc-oddp pc) 1 0) inst op) (selectq fmt ((unsigned-immediate-operand unsigned-pc-relative) (prin1 imm)) ((signed-immediate-operand signed-pc-relative) (prin1 (- (logxor 200 imm) 200))) (10-bit-immediate-operand (prin1 (logand (mask 10.) inst))) (address-operand (prin1 (nth (lsh imm -7) '(arg stack))) (tyo #/1) (prin1 (if (< imm 200) imm (- (logand 177 imm) 177)))) ((constant-operand constant-pc-relative indirect-operand) (format t "~A ~O" fmt imm))) (setq pc (pc-plus-number pc 1)))) (defun inc-pc () (setq *ps* (if (data-type? *pc* dtp-even-pc) (set-type *pc* dtp-odd-pc) (set-type (1+ *pc*) dtp-even-pc)))) ;Support routines for instructions ;These would be open-coded and go in one cycle (defun pushval (val) (setq val (set-cdr val cdr-next)) (aset val *a-memory* (address-add *stack-pointer* 1)) (setf (top-of-stack) val) (incf *stack-pointer*)) (comment (defun popval () (prog1 (top-of-stack) (setf (top-of-stack) (aref *a-memory* (address-add '*stack-pointer* -1))) (decf *stack-pointer*))) (defun newtop (val) 4,887,235 111 112 (setq val (set-cdr val cdr-next)) (aset val *a-memory* (address-add *stack-pointer* 0)) (setf (top-of-stack) val)) (defun next-on-stack () (aref *a-memory* (address-add '*stack-pointer* -1))) ;This is like doing two popval;s and then a pushval (defun pop2push (val) (setq val (set-cdr val cdr-next)) (aset val *a-memory* (address-add '*stack-pointer* -1)) (setf (top-of-stack) val) (decf *stack-pointer*)) (defun pushval-with-cdr (val) (aset val *a-memory* (address-add '*stack-pointer* 1)) (setf (top-of-stack) val) (incf *stack-pointer*)) ;Helper functions for arithmetic ;These do arithmetic but trap to overflow-bignum-create if the ;result doesn't fit in a fixnum. ;In the simulator this thinks a lot, in the real machine it ;needs to be built in (conditional branch on ALU 32-bit overflow flag). (defun plus-check-overflow (op1 op2 stack-adjustment) (let ((res (+ op1 op2))) (or (and (<= -1_31. res) (< res 1_31.)) (overflow-bignum-create res stack-adjustment)) res)) (defun minus-check-overflow (op1 op2 stack-adjustment) (let ((res (- op1 op2))) (or (and (<= -1_31. res) (< res 1_31.)) (overflow-bignum-create res stack-adjustment)) res)) ;Some simple instructions (definstruction halt no-operand (*throw 'halt 'halt)) (definstruction push-immed signed-immediate-operand (pushval (set-type (instruction-signed-immediate) dtp-fix))) (definstruction push-local address-operand (pushval (local-operand))) (definstruction pop-local address-operand (setf (local-operand) (popval))) (definstruction movem-local address-operand (setf (local-operand) (top-of-stack))) (definstruction add-immed signed-immediate-operand (or (data-type? (top-of-stack) dtp-fix) (take-arithmetic-trap 'add 'signed-immed)) (newtop (set-type (plus-check-overflow (unbox-fixnum (top-of-stack)) (instruction-signed-immediate) 0) dtp-fix))) (definstruction add-local address-operand (or (and (data-type? (top-of-stack) dtp-fix) (data-type? (local-operand) dtp-fix)) (take-arithmetic-trap 'add 'local)) (newtop (set-type (plus-check-overflow (unbox-fixnum (top-of-stack)) (unbox-fixnum (local-operand)) 0) dtp-fix))) ;This will be format-3 when I bother simulating those (definstruction add-stack no-operand (or (and (data-type? (top-of-stack) dtp-fix) (data-type? (next-on-stack) dtp-fix)) (take-arithmetic-trap 'add 'stack)) (pop2push (set-type (plus-check-overflow (unbox-fixnum (top-of-stack)) (unbox-fixnum (next-on-stack)) -1) dtp-fix))) (definstruction push-constant constant-operand (pushval (mem-read (- (frame-function) (instruction-unsigned-immediate) 1)))) (definstruction push-specvar indirect-operand (pushval (mem-read (mem-read (- (frame-function) (instruction-unsigned-immediate) 1) 'no-evcp))))