(define (make-execution-procedure inst labels machine pc flag stack 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 stack pc)) ((eq? (car inst) 'restore) (make-restore inst machine stack pc)) ((eq? (car inst) 'perform) (make-perform inst machine labels ops pc)) (else (error "Unknown instruction type -- ASSEMBLE" inst))))
レジスタ計算機言語の命令の型ごとに, 適切な実行手続きを構築する生成プログラムがある. これらの手続きの細部が, レジスタ計算機言語での各命令の構文と意味を決める. 4.1.2節の評価器でやったように, レジスタ計算機の式の細い構文を一般の実行機構から分離するため, データ抽象を使い構文手続きで命令の部分を取り出し, 分類する.
(define (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 () ; assignの実行手続き (set-contents! target (value-proc)) (advance-pc pc)))))make-assignは選択子
(define (assign-reg-name assign-instruction) (cadr assign-instruction)) (define (assign-value-exp assign-instruction) (cddr assign-instruction))を使い, assign命令から目標のレジスタ名(命令の第二要素)と値の式(命令を形成する式のリストの残り)を取り出す. レジスタ名は, 目標のレジスタオブジェクトを作るべくget-registerで探す. 値の式は値が演算の結果ならmake- operation-expに, それ以外なら make-primitive-expに渡す. (次に示す)これらの手続きは, 値の式を構文解析し, その値の実行手続きを作る. これは value-procという引数なしの手続きで, シミュレーション中にレジスタに代入する実際の値を得るために評価される. レジスタ名の探索と, 値の式の構文解析は, 命令がシミュレートされる度ではなく, アセンブリ時に一回だけ行うことに注意しよう. この省力は, 実行手続きを使う理由であり, 4.1.7節の評価器で, プログラム解析を実行から分離して得た省力と直接に対応する.
make-assignが返す結果は, assign命令の実行手続きである. この手続きを(計算機モデルのexecute手続きで)呼び出すと, 目標のレジスタの内容を, value-procを評価して得られる結果に設定する. それから手続き
(define (advance-pc pc) (set-contents! pc (cdr (get-contents pc))))を走らせ, pcを次の命令へと進める. advance-pcはbranch とgotoを除き, すべての命令の通常の終了である.
(define (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 (condition-proc)) (advance-pc pc))) (error "Bad TEST instruction -- ASSEMBLE" inst)))) (define (test-condition test-instruction) (cdr test-instruction))
branch命令に対する実行手続きは, flagレジスタの内容を調べ, pcの内容を(分岐する時)分岐目的地へ設定するか(分岐しない時) pcを進めるかする. branch命令で指示される行き先はラベルでなければならず, make-branchはこれを強要することに注意しよう. またラベルはアセンブリ時に探すので, branch命令をシミュレートする度でないことにも注意しよう.
(define (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" inst)))) (define (branch-dest branch-instruction) (cadr branch-instruction))
goto命令は, 行き先がラベルとしてかレジスタとして指定出来る点と, 調べるべき条件がない---pcは常に新しい行き先へ設定する---点を除き, 分岐命令と似ている.
(define (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))))) (else (error "Bad GOTO instruction -- ASSEMBLE" inst))))) (define (goto-dest goto-instruction) (cadr goto-instruction))
(define (make-save inst machine stack pc) (let ((reg (get-register machine (stack-inst-reg-name inst)))) (lambda () (push stack (get-contents reg)) (advance-pc pc)))) (define (make-restore inst machine stack pc) (let ((reg (get-register machine (stack-inst-reg-name inst)))) (lambda () (set-contents! reg (pop stack)) (advance-pc pc)))) (define (stack-inst-reg-name stack-instruction) (cadr stack-instruction))
make-performが扱う命令の最後の型は, 実行すべき働きに対して実行手続きを生成する. シミュレーション時に働きの手続きが実行され, pcが進められる:
(define (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 () (action-proc) (advance-pc pc))) (error "Bad PERFORM instruction -- ASSEMBLE" inst)))) (define (perform-action inst) (cdr inst))
(define (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)))) (else (error "Unknown expression type -- ASSEMBLE" exp))))reg, labelおよびconst式の構文は
(define (register-exp? exp) (tagged-list? exp 'reg)) (define (register-exp-reg exp) (cadr exp)) (define (constant-exp? exp) (tagged-list? exp 'const)) (define (constant-exp-value exp) (cadr exp)) (define (label-exp? exp) (tagged-list? exp 'label)) (define (label-exp-label exp) (cadr exp))で決る.
assign, performおよびtest命令は, (regおよびconst式の規定する)いくつかの被演算子に対する(op式の規定する)機械演算の作用を含むかも知れない. 次の手続きは「演算式」---演算と被演算子の式を含むリスト---に対する実行手続きを命令から作る.
(define (make-operation-exp exp machine labels operations) (let ((op (lookup-prim (operation-exp-op exp) operations)) (aprocs (map (lambda (e) (make-primitive-exp e machine labels)) (operation-exp-operands exp)))) (lambda () (apply op (map (lambda (p) (p)) aprocs)))))演算式の構文は
(define (operation-exp? exp) (and (pair? exp) (tagged-list? (car exp) 'op))) (define (operation-exp-op operation-exp) (cadr (car operation-exp))) (define (operation-exp-operands operation-exp) (cdr operation-exp))で決る. 演算式の扱いは, 各被演算子について実行手続きを生成するという点で, 4.1.7節の評価器のanalyze- application手続きによる手続き作用の扱いと非常によく似ていることを見よう. シミュレーション時には, 被演算子手続きを呼び出し, その結果の値へ演算をシミュレートするScheme手続きを作用させる. シミュレーション手続きは, 計算機の演算表から演算名で探して見つける:
(define (lookup-prim symbol operations) (let ((val (assoc symbol operations))) (if val (cadr val) (error "Unknown operation -- ASSEMBLE" symbol))))
(save y) (save x) (restore y)のように最後に退避したのではないレジスタを回復しようとすると, 何が起きるかは規定しなかった. restoreの意味にはいくつかの正当な可能性がある.