对于一元运算,咱们如今指的还不是函数(完成函数会在之后讲到),而是通常语言中内置的一元运算,好比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.