自作エミュレータで学ぶx86アーキテクチャ 2.3

自作エミュレータで学ぶx86アーキテクチャを読んでます。
この本ではあらかじめc言語で書かれているエミュレータのプログラムを元に
解説していくのですが、理解を深めるためにgaucheエミュレータを作成しています。

(use gauche.uvector)
(define-class <registers> ()
  ((eax :init-keyword :eax :init-value 0)
   (ecx :init-keyword :ecx :init-value 0)
   (edx :init-keyword :edx :init-value 0)
   (ebx :init-keyword :ebx :init-value 0)
   (esp :init-keyword :esp :init-value 0)
   (ebp :init-keyword :ebp :init-value 0)
   (esi :init-keyword :esi :init-value 0)
   (edi :init-keyword :edi :init-value 0))
  )
(define register-name '(eax ecx edx ebx esp ebp esi edi))
(define memory-size (* 1024 1024))

(define-class <emulator> ()
  ((memory :init-keyword :memory)
   (registers :init-keyword :registers)
   (eflags :init-keyword :eflags)
   (eip :init-keyword :eip))
  )

(define-method dump-registers ((reg <registers>))
	(for-each (lambda (s)
		    (format #t "~a = ~8,'0x\n" s (ref reg s))) register-name))

(define-method dump-registers ((emu <emulator>))
	       (dump-registers (ref emu 'registers))
	       (format #t "~a = ~8,'0x\n" 'eip (ref emu 'eip)))


(define (mov-r32-imm32 emu)
  (let1 reg (- (get-code8 emu 0) #xb8)
    (set! (ref (ref emu 'registers) (list-ref register-name reg)) (get-code32 emu 1))
    (set! (ref emu 'eip) (+ (ref emu 'eip) 5))))

(define (short-jump emu)
  (set! (ref emu 'eip) (+ (ref emu 'eip) (get-sign-code8 emu 1) 2)))

(define (get-instructions code)
  (cond [(and (<= #xb8 code) (<= code (+ #xb8 7)))
	 mov-r32-imm32]
	[(= code #xeb) short-jump]
	[else '()]))

(define (create-emu size eip esp)
  (make <emulator> :memory (make-u8vector size 0)
	           :registers (make <registers> :esp esp)
		   :eip eip))

(define (get-code8 emu index)
  (let ((memory (ref emu 'memory))
	(eip (ref emu 'eip)))
    (u8vector-ref memory (+ index eip))))


(define (get-sign-code8-1 num)
  (if (not (= (logand #b10000000 num) 0))
    (- (+ (logxor num #b11111111) 1))
    num))

(define (get-sign-code8 emu index)
  (get-sign-code8-1 (get-code8 emu index)))

(define (get-code32 emu index)
  (let loop ((i 0) (ret 0))
    (if (< i 4)
      (loop (+ i 1) (logior ret 
			   (ash (get-code8 emu (+ index i)) 
				(* i 8))))  
      ret)))

(define (run1 emu code)
  (let1 inst (get-instructions code)
	(format #t "eip = ~x, code = ~2,'0x\n" (ref emu 'eip) code)
	(cond [(null? inst) 
	       (format #t "\n\nNot Implemented: ~x\n" code)
	       '()]
	      [else (inst emu)
		    #t])))

(define (run-1 emu)
  (cond [(= (ref emu 'eip) #x00)
	 (display "\n\nend of program.\n\n")]  
        [(< (ref emu 'eip) memory-size)
	 (if (run1 emu (get-code8 emu 0))
	   (run-1 emu))]))

(define (run emu)
  (if (run1 emu (get-code8 emu 0))
    (run-1 emu)))

      

(define (memory-load emu p)
  (read-block! (ref emu 'memory) p 0 511))

(define (emu-run file)
  (let [(emu (create-emu memory-size 0 #x7c00))]
    (call-with-input-file file
      (lambda (p)
	(memory-load emu p)
	(run emu)
	(dump-registers emu)))))

(define test-emu (create-emu memory-size 0 0))

emu-runにhelloworld.binのパスを渡せば、c言語版と同じ結果が表示されます。

gosh> (emu-run "./helloworld.bin")
eip = 0, code = b8
eip = 5, code = eb


end of program.

eax = 00000029
ecx = 00000000
edx = 00000000
ebx = 00000000
esp = 00007c00
ebp = 00000000
esi = 00000000
edi = 00000000
eip = 00000000
#<undef>
gosh>