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