對於一元運算,咱們如今指的還不是函數(完成函數會在之後講到),而是通常語言中內置的一元運算,好比Scheme中的add1,sub1,integer->char, char->integer, fixnum? ,boolean? ,null? ,char? 等的運算.函數
實現這種一元運算的原理和關鍵就在於用匯編語言完成這些函數的運算後,咱們能夠使用%eax寄存器將運算的結果進行返回.spa
咱們要將編譯程序調整爲適應一元運算rest
(define (compile-program expr) (emit-head) (emit-expr expr) (emit-return))
從咱們以前的經驗大概能夠看出來,彙編語句中只有一些是於咱們所要編寫的程序相關的,因此咱們能夠把編譯主程序改寫爲由head,expr,return三部分組成的程序.code
(define (emit-head) (emit " .text") (emit " .global _scheme_entry") (emit " .def _scheme_entry; .scl 2; .type 32; .endef") (emit "_scheme_entry:") (emit "LFB0:") (emit " .cfi_startproc") (emit " pushl %ebp") (emit " .cfi_def_cfa_offset 8") (emit " .cfi_offset 5, -8") (emit " movl %esp, %ebp") (emit " .cfi_def_cfa_register 5"))
expr:blog
(define (emit-expr expr) (cond ((immediate-value? expr) (emit-immediate expr)) ((primcall? expr) (emit-primcall expr)) (else (error 'emit-expr))))
(define (emit-return) (emit " popl %ebp") (emit " .cfi_restore 5") (emit " .cfi_def_cfa 4, 4") (emit " ret") (emit " .cfi_endproc") (emit "LFE0:"))
爲了方便咱們編寫一元運算的彙編代碼,咱們能夠寫一個宏:it
(define-syntax define-primitive (syntax-rules () ((_ (op arg* ...) b b* ...) (begin (putprop 'op '*is-prim* #t) (putprop 'op '*arg-length* (length '(arg* ...))) (putprop 'op '*emitter* (lambda (arg* ...) b b* …))))))
(define-primitive (add1 arg) (emit-expr arg) (emit " addl $~a, %eax" (immediate-value-rep 1)))
(define-primitive (sub1 arg) (emit-expr arg) (emit " subl $~a, %eax" (immediate-value-rep 1)))
(define-primitive (char->integer arg) (emit-expr arg) (emit " shll $~s, %eax" (- charshift fxshift)))
字符轉換爲數字只須要向右移動6位便可,io
由於字符的低8位爲00001111, 數字 的低8位爲xxxxxx00編譯
(define-primitive (integer->char arg) (emit-expr arg) (emit " shll $~s, %eax" (- charshift fxshift)))
(define-primitive (fixnum? arg) (emit-expr arg) (emit " and $~s, %al" fxmask) (emit-cmp fxtag))
(define-primitive (null? arg) (emit-expr arg) (emit-cmp list_nil))
(define-primitive (boolean? arg) (emit-expr arg) (emit " and $~s, %al" bool_mask) (emit-cmp bool_f))
判斷是不是布爾值是利用bool_mask(0xbf),將#t轉變爲#f,而後比較是否等於#fclass
(define-primitive (char? arg) (emit-expr arg) (emit " and $~s, %al" charmask) (emit-cmp chartag))
最後是判斷是否相等的彙編代碼:原理
(define (emit-cmp tag) (emit " cmp $~s, %al" tag) (emit " sete %al") (emit " movzbl %al, %eax") (emit " sal $~s, %al" bool_bit) (emit " or $~s, %al" bool_f))
經過sete咱們能夠將比較結果放置在%eax中,若是相等,爲1,不然爲0,
將1左移6位後,與bool_f或運算爲bool_t,若爲0,或運算後仍爲bool_t.