マイペースなプログラミング日記

DTMやプログラミングにお熱なd-kamiがマイペースに書くブログ

超簡易x86エミュレータ

以下のアセンブリ言語のコードをアセンブラしたものを実行するエミュレータ(アセンブリ言語のコードの更に下)を載せておきます

    XOR AH, AH
    MOV AL, 0x03
    INT 0x10

    MOV SI, MESSAGE
    MOV AH, 0x0E

mloop:
    MOV AL, [SI]
    OR AL, AL
    JE fin

    INT 0x10
    ADD SI, 0x01
    JMP mloop

fin:
    JMP fin

MESSAGE:
    DB "Hello World!", 0x0D, 0x0A, 0x00 

    TIMES 510 - ($ - $$) DB 0
    DB 0x55, 0xAA
import java.io.FileInputStream;
import java.io.BufferedInputStream;
import java.io.IOException;

public class Emulator{
    private byte[] memory;    //プログラムを格納するメモリ
    private int[] registers;  //レジスタ郡
    private int eip;          //プログラムカウンタ

    private boolean zeroFlag; //ゼロフラグ

    private StringBuilder text;
    
    public static final int DEFAULT_MEMORY_SIZE = 1 * 1024 * 1024;
    public static final int AX = 0;
    public static final int CX = 1;
    public static final int DX = 2;
    public static final int BX = 3;
    public static final int SI = 6;
    
    public Emulator(int memorySize){
        memory = new byte[memorySize]; //プログラムを格納する領域の確保
        registers = new int[7];        //現在はレジスタ7つ目まで使う
        eip = 0;                       //プログラムカウンタ

        text = new StringBuilder();
    }
    
    public void read(String fileName) throws IOException{
        BufferedInputStream input = null;

        try{
            input = new BufferedInputStream(new FileInputStream(fileName));

            //プログラムを読み込む
            input.read(memory);
            input.close();
        }catch(IOException e){
            if(input != null){
                try{
                    input.close();
                }catch(IOException ioe){
                    throw ioe;
                }
            }
            
            throw e;
        }
    }
    
    public void execute(){
        //オペコードの取得
        int code = memory[eip] & 0xFF;
        
        //オペコードを表示する
        System.out.printf("code = %X\n", code);
        
        if(code == 0xB0){
            movALImm8();
        }else if(code == 0x04){
            addALImm8();
        }else if(code == 0xEB){
            jmpShort();
        }else if(code == 0x3C){
            cmpALImm8();
        }else if(code == 0x74){
            jzShort();
        }else if(code == 0x30){
            xorRM8R8();
        }else if(code == 0xCD){
            interrupt();
        }else if(code == 0xBE){
            movSIImm16();
        }else if(code == 0xB4){
            movAHImm8();
        }else if(code == 0x8A){
            movR8RM8();
        }else if(code == 0x08){
            orRM8R8();
        }else if(code == 0x83){
            addRM16Imm8();
        }else{
            throw new RuntimeException("Not Implemented 0x" + Integer.toHexString(code));
        }
    }
    
    private void movALImm8(){
        //代入を行う
        int value = memory[eip + 1] & 0xFF;
        System.out.printf("MOV AL, 0x%X\n", value);
       
        setRegister8Low(Emulator.AX, value);
        eip += 2;
    }
    
    private void addALImm8(){
        //足し算を行う
        int value = memory[eip + 1] & 0xFF;
        System.out.printf("ADD AL, 0x%X\n", value);
        
        int result = getRegister8Low(Emulator.AX)+ value;
        setRegister8Low(Emulator.AX, result);
        zeroFlag = result == 0 ? true : false;
        
        eip += 2;
    }
    
    private void jmpShort(){
        //ジャンプ命令を実行する
        int value = memory[eip + 1];
        System.out.printf("JMP 0x%X\n", value & 0xFF);
        
        eip += value;
        eip += 2;
    }
    
    private void cmpALImm8(){
        //比較を行う
        int value = memory[eip + 1] & 0xFF;
        System.out.printf("CMP AL, 0x%X\n", value);
        
        int result = getRegister8Low(Emulator.AX) - value;
        zeroFlag = result == 0 ? true : false;
        eip += 2;
    }
    
    private void jzShort(){
        //条件分岐
        int value = memory[eip + 1];
        System.out.printf("JZ 0x%X\n", value);
        
        if(zeroFlag){
            eip += value;
        }
        
        eip += 2;
    }

    private void xorRM8R8(){
        int modrm = memory[eip + 1] & 0xFF;
        
        if(modrm == 0xE4){
            System.out.println("XOR AH, AH");
            
            int ah = getRegister8High(Emulator.AX);
            int result = ah ^ ah;
            setRegister8High(Emulator.AX, result);
            
            zeroFlag = result == 0 ? true : false;
        }else{
            throw new RuntimeException("xorRM8R8 Not Implemented modrm = " + Integer.toHexString(modrm));
        }
        
        eip += 2;
    }
    
    private void interrupt(){
        int index = memory[eip + 1] & 0xFF;
        
        int al = getRegister8Low(Emulator.AX);
        int ah = getRegister8High(Emulator.AX);
        
        if(index == 0x10){
            System.out.println("INT 0x10");
            
            if(ah == 0 && al == 0x03){
                //このときは何もしない
            }else if(ah == 0x0E){
                if(al == '\n'){
                    System.out.println(text);
                    
                    throw new RuntimeException();
                }else{
                    System.out.println((char)al);
                    text.append((char)al);
                }

            }else{
                throw new RuntimeException("INT 0x10 実装されてない命令です");
            }
        }else{
            throw new RuntimeException("INT 0x" + Integer.toHexString(index) + " 実装されてない命令です");
        }
        
        eip += 2;
    }

    private void movAHImm8(){
        //代入を行う
        int value = memory[eip + 1] & 0xFF;
        System.out.printf("MOV AH, 0x%X\n", value);
       
        setRegister8High(Emulator.AX, value);
        eip += 2;
    }

    
    private void movSIImm16(){
        //代入を行う
        int value = (memory[eip + 1] & 0xFF) | (memory[eip + 2] & 0xFF) << 8;
        System.out.printf("MOV SI, 0x%X\n", value);
       
        registers[Emulator.SI] = value;
        eip += 3;
    }
    
    private void movR8RM8(){
        //代入を行う
        
        int modrm = memory[eip + 1] & 0xFF;
        
        if(modrm == 0x04){
            System.out.println("MOV AL, [SI]");
            
            setRegister8Low(Emulator.AX, memory[registers[Emulator.SI]] & 0xFF);
        }else{
            throw new RuntimeException("movR8RM8 Not Implemented modrm = " + Integer.toHexString(modrm));
        }
        
        eip += 2;
    }

    private void orRM8R8(){
        int modrm = memory[eip + 1] & 0xFF;
        
        if(modrm == 0xC0){
            System.out.println("OR AL, AL");
            
            int al = getRegister8Low(Emulator.AX);
            int result = al | al;
            setRegister8Low(Emulator.AX, result);
            
            zeroFlag = result == 0 ? true : false;
        }else{
            throw new RuntimeException("xorRM8R8 Not Implemented modrm = " + Integer.toHexString(modrm));
        }
        
        eip += 2;
    }
    
    private void addRM16Imm8(){
        int modrm = memory[eip + 1] & 0xFF;
        
        if(modrm == 0xC6){
            int value = memory[eip + 2] & 0xFF;
            int result = registers[Emulator.SI] + value;
            registers[Emulator.SI] = result;
            
            zeroFlag = result == 0 ? true : false;
        }
        
        eip += 3;
    }
    
    //16bitレジスタの下位8bitを書き換える
    private void setRegister8Low(int index, int data){
        registers[index] &= 0xFFFFFF00;
        registers[index] |= (data & 0xFF);
    }

    //16bitレジスタの上位8bitを書き換える
    private void setRegister8High(int index, int data){
        registers[index] &= 0xFFFF00FF;
        registers[index] |= (data & 0xFF) << 8;
    }

    //16bitレジスタの下位8bitを返す
    private int getRegister8Low(int index){
        return registers[index] & 0xFF;
    }

    //16bitレジスタの上位8bitを返す
    private int getRegister8High(int index){
        return (registers[index] >> 8) & 0xFF;
    }
    
    public void dumpRegisters(){
        //レジスタを全て出力する
        System.out.println();
        System.out.println("Registers Value");
        System.out.printf("AX = 0x%X\n", registers[Emulator.AX]);
        System.out.printf("CX = 0x%X\n", registers[Emulator.CX]);
        System.out.printf("DX = 0x%X\n", registers[Emulator.DX]);
        System.out.printf("BX = 0x%X\n", registers[Emulator.BX]);
        
        System.out.printf("EIP = 0x%X\n", eip);
        System.out.println("ZeroFlag = " + zeroFlag);
    }
    
    public static void main(String[] args){
        if(args.length == 0){
            System.out.println("引数で読み込むファイルを指定してください");
            System.exit(0);
        }

        Emulator emulator = new Emulator(Emulator.DEFAULT_MEMORY_SIZE);
        
        try{ 

            emulator.read(args[0]);
            
            while(true){
                emulator.execute();
            }
        }catch(IOException e){
            System.out.println("ファイルの読み込みに失敗しました。");
            e.printStackTrace();
        }catch(RuntimeException e){
            emulator.dumpRegisters();
            e.printStackTrace();
        }
    }
}