5.2.1 机器的模型
由make-machine生成的机器模型被表示成一个使用在第三章中开发的
消息传递技术的具有本地状态的程序。为了构建这个模型,make-machine开始于
调用 make-new-machine 程序来组装对于所有的寄存器机器共用的机器模型的各个部分。
这个基本的被make-machine组装的机器模型是一个有一些寄存器和一个栈,加上一个逐条
执行控制器指令的执行机制的容器。
make-machine使用发消息的方式扩展了这个基本的模型,包含了寄存器,操作和
被特定的机器定义的控制器。首先它在新机器上,为给定的每个寄存器名称,
分配一个寄存器,在机器上安装目标操作。然后它使用汇编器(这个汇编器在如下
的5.2.2部分中描述)把控制器列表转换成新机器的指令,并且作为机器的
指令序列安装它们。make-machine返回已修改的机器模型的值。
(define (make-machine register-names ops controller-text)
(let ((machine (make-new-machine)))
(for-each (lambda (register-name)
((machine 'allocate-register) register-name))
register-names)
((machine 'install-operations) ops)
((machine 'install-instruction-sequence)
(assemble controller-text machine))
machine
)
)
*寄存器
正如在第三章中的那样,我们用一个有局部状态的程序来表示一个寄存器。
程序make-register 创建一个寄存器,这个寄存器有一个能被读取与修改的值。
(define (make-register name)
(let ((contents '*unassigned*))
(define (dispatch message)
(cond ((eq? message 'get) contents)
((eq? message 'set)
(lambda (value) (set! contents value)))
(else (error "" message))
)
)
dispatch
)
)
以下的程序被用来读取寄存器。
(define (get-contents register)
(register 'get)
)
(define (set-contents! register value)
((register 'set) value)
)
*栈
我们也用一个有局部状态的程序来表示一个栈。程序make-stack创建一个栈,
栈的局部状态是由栈上的项的一个列表组成的。一个栈接受压栈,弹栈,返回值,
和把栈初始化为空的请求。
(define (make-stack)
(let ((s '()))
(define (push x) (set! s (cons x s)))
(define (pop)
(if (null? s) (error "Empty stack --POP")
(let ((top (car s)))
(set! s (cdr s))
top)))
(define (initialize)
(set! s '())
'done)
(define (dispatch message)
(cond ((eq? message 'push) push)
((eq? message 'pop) pop)
((eq? message 'initialize) (initialize))
(else (error "Unknown request --STACK" message))))
dispatch
)
)
以下的程序被用来读取栈。
(define (pop stack)
(stack 'pop))
(define (push stack value)
((stack 'push) value))
*基本机器
在图5.13中显示的程序make-new-machine,由一个有局部状态的对象组成,
局部状态包括了一个栈,一个初始化的空的指令序列,和一些操作的列表还
有一个寄存器的表格。这些操作初始化时包括了一个对栈进行初始化的操作。
寄存器的表格初始化时包括了两个寄存器,一个叫做flag(标志),一个叫做pc
(程序计数器)。内部的程序allocate-register向寄存器的表格中添加新的记录项。
内部程序lookup-register在表格中查找寄存器。
在模拟的机器中标志寄存器被用来控制分支过程。测试指令根据测试结果设置标志
寄存器的值。分支指令根据检查的标志寄存器的值来决定是否进入分支部分。
程序计数器的寄存器确定着机器运行的指令的序列。通过内部程序execute实现了这个序列。
在模拟的模型中,任何的机器指令都是一个数据结构,它包括了一个没有参数的程序,
叫做指令执行程序,这样,调用这个程序就模拟了执行指令。作为模拟执行,程序计数器指向
指令序列中下一条要被执行的指令开始的地方。指令执行程序execute得到了指令,通过调用
指令执行程序执行它,并且重复这个循环,直到没有更多的指令可以执行了为止。也就是程序计数
器指到了指令序列的末尾了。
(define (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 () (stack 'initialize)))))
(register-table (list (list 'pc pc) (list 'flag flag))))
(define (allocate-register name)
(if (assoc name register-table)
(error "Multiply defined register:" name)
(set! register-table (cons (list name (make-register name))
register-table)))
'register-allocated
)
(define (lookup-register name)
(let ((val (assoc name register-table)))
(if val (cadr val)
(error "Unknown register:" name)))
)
(define (execute)
(let ((insts (get-contents pc)))
(if (null? insts)
'done
(begin
(instruction-execution-proc (car insts))
(execute))))
)
(define (dispatch message)
(cond ((eq? message 'start) (set-contents! pc the-instruction-sequence))
((eq? message 'install-instruction-sequence)
(lambda (seq) (set! the-instruction-sequence seq)))
((eq? message 'allocate-register) allocate-register)
((eq? message 'get-register) lookup-register)
((eq? message 'install-operations)
(lambda (ops) (set! the-ops (append the-ops ops))))
((eq? message 'stack) stack)
((eq? message 'operations) the-ops)
(else (error "Unknown request --MACHINE" message))
))
dispatch
)
)
)
图5.13 程序make-new-machine,实现了基本的机器模型
作为它的操作的一部分,任何一个指令执行程序都修改了程序计数器,来显示下一条要执行的指令。
分支与去哪的指令修改程序计数器让它指向新的目标地址。所有的其它的指令简化了程序计数器,让它
指向序列中的下一条指令。注意的是任何对execute的调用,也调用了execute,但是这没有生成一个
无限的循环,因为运行的指令执行程序修改了程序计数器的内容。
make-new-machine返回了程序dispatch,程序dispatch实现了消息传递来读取内部的状态。
注意的是通过设置程序计数器到指令序列的开头处和调用执行程序,来完成启动机器。
为了方便,我们提供了一个可选的程序化接口,这个接口是对机器的启动操作的,正如
设置和检查寄存器内容的程序,它被指定在5.2部分的开头处:
(define (start machine)
(machine 'start))
(define (get-register-contents machine register-name)
(get-contents (get-register machine register-name)))
(define (set-register-contents! machine register-name value)
(set-contents! (get-register machine register-name) value) 'done)
这些程序(和许多在5.2.2和 5.2.3部分中的程序)使用如下的在一个给定的机器中,
以一个给定的名称,来查找寄存器的程序:
(define (get-register machine reg-name)
((machine 'get-register) reg-name))