Lisp 模拟 Assembler
通过Common Lisp,模拟assembler machine的运行。
参考SICP charpter5.
machine.lisp
;;; machine.lisp --- ;; ;; Filename: machine.lisp ;; Description: ;; Author: Liu Enze ;; Maintainer: ;; Created: Thu Dec 11 14:39:16 2014 (+0800) ;; Version: ;; Package-Requires: () ;; Last-Updated: Wed Feb 3 19:03:37 2016 (+0800) ;; By: Liu Enze ;; Update #: 55 ;; URL: ;; Doc URL: ;; Keywords: ;; Compatibility: ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;;; Commentary: ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;;; Change Log: ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; This program is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or (at ;; your option) any later version. ;; ;; This program is distributed in the hope that it will be useful, but ;; WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;; General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;;; Code: (defun make-machine (registers ops controller-text) (let ((machine (make-new-machine))) (mapcar #'(lambda (register-name) (funcall (funcall machine 'allocate-register) register-name)) registers) (funcall (funcall machine 'install-operations) ops) (funcall (funcall machine 'install-instruction-sequence) (e_assemble controller-text machine)) machine)) (defun make-register (name) (let ((contents '*unassigned*)) (lambda (message) (cond ((eq message 'get) contents) ((eq message 'set) (lambda (value) (setf contents value))) (t (error "~S :Unknown request -- REGISTER ~S" message name)))))) (defun get-contents (register) (funcall register 'get)) (defun set-contents (register value) (funcall (funcall register 'set) value)) (defun make-stack () (let ((s '())) (flet ((push-stack (x) (setf s (cons x s))) (pop-stack () (if (null s) (error "Empty stack --- POP") (let ((top (car s))) (setf s (cdr s)) top))) (initialize () (setf s '()) 'done)) (lambda (message) (cond ((eq message 'push) #'push-stack) ((eq message 'pop) (pop-stack)) ((eq message 'initialize) (initialize)) (t (error "~S :Unknown request --STACK" message))))))) (defun pop-stack (stack) (funcall stack 'pop)) (defun push-stack (stack value) (funcall (funcall stack 'push) value)) (defun instruction-execution-proc (inst) (cdr inst)) (defun make-new-machine () (let ((pc (make-register 'pc)) (flag (make-register 'flag)) (stack (make-stack)) (the-instruction-sequence '())) (let ((the-ops (list (list 'initialize-stack (lambda () (funcall stack 'initialize))))) (register-table (list (cons 'pc pc) (cons 'flag flag)))) (labels ((allocate-register (name) (if (assoc name register-table) (error "Multiply defined register: ~S" name) (setf register-table (cons (cons name (make-register name)) register-table))) 'register-allocated) (lookup-register (name) (let ((val (assoc name register-table))) (if val (cdr val) (error "Unknown register: ~S~%~S" name register-table)))) (execute () (let ((insts (get-contents pc))) (if (null insts) 'done (progn (funcall (instruction-execution-proc (car insts))) (execute)))))) (lambda (message) (cond ((eq message 'instructions) the-instruction-sequence) ((eq message 'start) (set-contents pc the-instruction-sequence) (execute)) ((eq message 'install-instruction-sequence) (lambda (seq) (setf the-instruction-sequence seq))) ((eq message 'allocate-register) #'allocate-register) ((eq message 'get-register) #'lookup-register) ((eq message 'install-operations) (lambda (ops) (setf the-ops (append the-ops ops)))) ((eq message 'stack) stack) ((eq message 'operations) the-ops) (t (error "Unknown request -- MACHINE: ~S" message)))))))) (defun start (machine) (funcall machine 'start)) (defun get-register (machine register-name) (funcall (funcall machine 'get-register) register-name)) (defun get-register-contents (machine register-name) (get-contents (get-register machine register-name))) (defun set-register-contents (machine register-name value) (set-contents (get-register machine register-name) value) 'done) (defun install-instruction (machine instructions) (funcall (funcall machine 'install-instruction-sequence) instructions)) (defun e_assemble (controller-text machine) (extract-labels controller-text (lambda (insts labels) (update-insts! insts labels machine)))) (defun extract-labels (text receive) (if (null text) (funcall receive '() '()) (extract-labels (cdr text) (lambda (insts labels) (let ((next-inst (car text))) (if (symbolp next-inst) (funcall receive insts (cons (make-label-entry next-inst insts) labels)) (funcall receive (cons (make-instruction next-inst) insts) labels))))))) (defun update-insts! (insts labels machine) (let ((pc (get-register machine 'pc)) (flag (get-register machine 'flag)) (stack (funcall machine 'stack)) (ops (funcall machine 'operations))) (loop for inst in insts collect (set-instruction-execution-proc! inst (make-execution-procedure (instruction-text inst) labels machine pc flag stack ops))))) (defun make-instruction (text) (cons text '())) (defun instruction-text (inst) (car inst)) (defun set-instruction-execution-proc! (inst proc) (setf (cdr inst) proc) inst) (defun make-label-entry (label-name insts) (cons label-name insts)) (defun lookup-label (all-labels label-name) (let ((val (assoc label-name all-labels))) (if val (cdr val) (error "Undefined label -- ASSEMBLE: ~S" label-name)))) (defun make-execution-procedure (inst labels machine pc flag stacks ops) (cond ((eq (car inst) 'assign) (make-assign inst machine labels ops pc)) ((eq (car inst) 'test) (make-test inst machine labels ops flag pc)) ((eq (car inst) 'branch) (make-branch inst machine labels flag pc)) ((eq (car inst) 'goto) (make-goto inst machine labels pc)) ((eq (car inst) 'save) (make-save inst machine stacks pc)) ((eq (car inst) 'restore) (make-restore inst machine stacks pc)) ((eq (car inst) 'perform) (make-perform inst machine labels ops pc)) (t (error "Unknown instruction type -- ASSEMBLE : ~S" inst)))) (defun make-assign (inst machine labels operations pc) (let ((target (get-register machine (assign-reg-name inst))) (value-exp (assign-value-exp inst))) (let ((value-proc (if (operation-exp value-exp) (make-operation-exp value-exp machine labels operations) (make-primitive-exp (car value-exp) machine labels)))) (lambda () (set-contents target (funcall value-proc)) (advance-pc pc))))) (defun assign-reg-name (assign-instruction) (cadr assign-instruction)) (defun assign-value-exp (assign-instruction) (cddr assign-instruction)) (defun advance-pc (pc) (set-contents pc (cdr (get-contents pc)))) (defun make-test (inst machine labels operations flag pc) (let ((condition (test-condition inst))) (if (operation-exp condition) (let ((condition-proc (make-operation-exp condition machine labels operations))) (lambda () (set-contents flag (funcall condition-proc)) (advance-pc pc))) (error "Bad Test instruction -- ASSEMBLE: ~S" inst)))) (defun test-condition (test-instruction) (cdr test-instruction)) (defun make-branch (inst machine labels flag pc) (let ((dest (branch-dest inst))) (if (label-exp dest) (let ((insts (lookup-label labels (label-exp-label dest)))) (lambda () (if (get-contents flag) (set-contents pc insts) (advance-pc pc)))) (error "Bad Branch instruction --ASSEMBLE: ~S" inst)))) (defun branch-dest (branch-instruction) (cadr branch-instruction)) (defun make-goto (inst machine labels pc) (let ((dest (goto-dest inst))) (cond ((label-exp dest) (let ((insts (lookup-label labels (label-exp-label dest)))) (lambda () (set-contents pc insts)))) ((register-exp dest) (let ((reg (get-register machine (register-exp-reg dest)))) (lambda () (set-contents pc (get-contents reg))))) (t (error "BAD GOTO instruction -- ASSEMBLE : ~S" inst))))) (defun goto-dest (goto-instruction) (cadr goto-instruction)) (defun make-save (inst machine stack pc) (let ((reg (get-register machine (stack-inst-reg-name inst)))) (lambda () (push-stack stack (get-contents reg)) (advance-pc pc)))) (defun make-restore (inst machine stack pc) (let ((reg (get-register machine (stack-inst-reg-name inst)))) (lambda () (set-contents reg (pop-stack stack)) (advance-pc pc)))) (defun stack-inst-reg-name (stack-instruction) (cadr stack-instruction)) (defun make-perform (inst machine labels operations pc) (let ((action (perform-action inst))) (if (operation-exp action) (let ((action-proc (make-operation-exp action machine labels operations))) (lambda () (funcall action-proc) (advance-pc pc)) ) (error "Bad PERFORM instruction -- ASSEMBLE: ~S" inst)))) (defun perform-action (inst) (cdr inst)) (defun make-primitive-exp (exp machine labels) (cond ((constant-exp exp) (let ((c (constant-exp-value exp))) (lambda () c))) ((label-exp exp) (let ((insts (lookup-label labels (label-exp-label exp)))) (lambda () insts))) ((register-exp exp) (let ((r (get-register machine (register-exp-reg exp)))) (lambda () (get-contents r)))) (t (error "Unknown expression type -- ASSEMBLE: ~S" exp)))) (defun register-exp (exp) (tagged-list exp 'reg)) (defun tagged-list (exp prefix) (eq (car exp) prefix)) (defun register-exp-reg (exp) (cadr exp)) (defun constant-exp (exp) (tagged-list exp 'const)) (defun constant-exp-value (exp) (cadr exp)) (defun label-exp (exp) (tagged-list exp 'label)) (defun label-exp-label (exp) (cadr exp)) (defun make-operation-exp (exp machine labels operations) (let ((op (lookup-prim (operation-exp-op exp) operations)) (aprocs (mapcar (lambda (e) (make-primitive-exp e machine labels)) (operation-exp-operands exp)))) (lambda () (apply op (mapcar (lambda (p) (funcall p)) aprocs))))) (defun operation-exp (exp) (and (consp exp) (tagged-list (car exp) 'op))) (defun operation-exp-op (operation-exp) (cadr (car operation-exp))) (defun operation-exp-operands (operation-exp) (cdr operation-exp)) (defun lookup-prim (symbol operations) (let ((val (assoc symbol operations))) (if val (cadr val) (error "Unknown operation -- ASSEMBLE: ~S" symbol)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; machine.lisp ends here
Usage
(load "./machine.lisp") (defun expt-machine () (make-machine '(b n val continue) `((= ,#'=) (- ,#'-) (* ,#'*)) '(controller (assign continue (label done)) expt-loop (test (op =) (reg n) (const 0)) (branch (label answer)) (save continue) (assign continue (label after-expt-n-1)) (save n) (assign n (op -) (reg n) (const 1)) (goto (label expt-loop)) after-expt-n-1 (restore n) (restore continue) (assign val (op *) (reg val) (reg b)) (goto (reg continue)) answer (assign val (const 1)) (goto (reg continue)) done))) (defparameter *m* (expt-machine)) (set-register-contents *m* 'b 2) ;;DONE (set-register-contents *m* 'n 2) ;;DONE (start *m*) ;;DONE (get-register-contents *m* 'val) ;;4