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

(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 (near-jump emu)
  (set! (ref emu 'eip) (+ (ref emu 'eip) (get-sign-code32 emu 1) 5)))

(define (get-instructions code)
  (cond [(and (<= #xb8 code) (<= code (+ #xb8 7)))
	 mov-r32-imm32]
	[(= code #xeb) short-jump]
	[(= code #xe9) near-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-code32-1 num)
  (if (not (= (logand (expt 2 31) num) 0))
    (- (+ (logxor num (- (expt 2 32) 1) ) 1))
    num))

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

(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 #x7c00 (+ #x7c00 511)))

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