zoukankan      html  css  js  c++  java
  • 另一版本的基于堆栈的虚拟机实现

    另一版本的基于堆栈的虚拟机实现

             前面我们对一基于堆栈虚拟机进行了源码剖析《基于栈的虚拟机源码剖析》。之前我们也实现了一个简单的基于堆栈的虚拟机《实现一个堆栈虚拟机》。在《实现一个堆栈虚拟机》中,我们将虚拟机定义为一个VirtualBox类,VirtualBox类中有成员变量:堆栈、指令内存、数据内存,另外还有成员函数:读取指令、执行指令。《基于栈的虚拟机源码剖析》中,是C语言实现的,没有设计成类的形式,但依然有堆栈、指令、数据、读取指令、执行指令等模块。

             这里,我们再次实现一个基于堆栈的虚拟机。先给出实现代码,然后再对代码进行解释。

    // 基于堆栈的虚拟机实现
    #include <iostream>
    #include <string>
    #include <vector>
    #include <stack>
    using namespace std;
    
    // 虚拟机的二进制指令集
    enum Command
    {
        HALT, IN, OUT, ADD, SUB, MUL, DIV,
        INMEMORY, /* 存放内存 */
        OUTMEMORY, /* 读取内存 */
        DUP,
        LD, ST, LDC, JLT, JLE, JGT, JGE, JEQ, JNE, JMP,
        INVALID
    };
    
    // 指令结构体
    struct Instruction
    {
        Command com;   // 指令码
        int     opd;   // 操作数
    };
    
    // 堆栈
    stack<int> stk;
    
    // 指令内存
    vector<Instruction> insMemory;
    
    // 数据内存
    vector<int> datMemory(101);
    
    // 状态码
    enum StateCode
    {
        scHALT, scOK,
        errDIVBYZERO, errDATMEMORY, errINSMEMORY,
        errSTACKOVERFLOW, errSTACKEMPTY, errPOP,
        errUNKNOWNOP
    };
    
    // 输出错误信息
    void Error(const string& err)
    {
        cerr << err << endl;
    }
    
    // 根据指令码返回指令码需要的操作数个数
    int GetOperandCount(Command com)
    {
        int ret = 0;
        switch (com)
        {
        case INMEMORY:
        case OUTMEMORY:
        case LDC:
        case JLT:
        case JLE:
        case JGT:
        case JGE:
        case JEQ:
        case JNE:
        case JMP:
            ret = 1;
            break;
    
        default:
            ret = 0;
            break;
        }
        return ret;
    }
    
    // 读取指令
    // 从字符串中读取指令码和操作数
    // 这里读取的是文本,而非二进制
    // 所以指令码占2位,操作数占4位
    void ReadInstruction(const string& strCodes)
    {
        int idx = 0;
        Instruction ins;
        Command com;
        int     opd;
        while (idx < strCodes.size())
        {
            string strCod = strCodes.substr(idx, 2);
            idx += 2;
            cout << strCod;
            com = static_cast<Command>(atoi(strCod.c_str())); // 字符转换为指令码,不检测com是否合法
            int cnt = GetOperandCount(com);
            if (cnt > 0)
            {
                string strOpd = strCodes.substr(idx, 4);
                idx += 4;
                opd = atoi(strOpd.c_str());
                cout << '	' << strOpd;
            }
            else
            {
                opd = 0;
            }
            cout << endl;
            ins.com = com;
            ins.opd = opd;
            insMemory.push_back(ins);
        }
    }
    
    // 执行指令
    void ExecuteInstructions()
    {
        for (auto idx = 0; idx < insMemory.size() && insMemory[idx].com != HALT; /*++idx*/)
        {
            bool idxChg  = false;
            int  idxJump = 0;
            switch (insMemory[idx].com)
            {
            case HALT: // 终止
                break;
    
            case IN:   // 输入
                {
                    int tmp;
                    cout << "输入:" << endl;
                    cin >> tmp;
                    stk.push(tmp);
                }
                break;
    
            case OUT:   // 输出
                {
                    int tmp = stk.top();
                    stk.pop();
                    cout << tmp << endl;
                }
                break;
    
            case ADD:   // 加法
                {
                    int a = stk.top();
                    stk.pop();
                    int b = stk.top();
                    stk.pop();
                    int c = b + a;
                    stk.push(c);
                }
                break;
    
            case SUB:   // 减法
                {
                    int a = stk.top();
                    stk.pop();
                    int b = stk.top();
                    stk.pop();
                    int c = b - a;
                    stk.push(c);
                }
                break;
    
            case MUL:    // 乘法
                {
                    int a = stk.top();
                    stk.pop();
                    int b = stk.top();
                    stk.pop();
                    int c = b * a;
                    stk.push(c);
                }
                break;
    
            case DIV:    // 除法
                {
                    int a = stk.top();
                    stk.pop();
                    int b = stk.top();
                    stk.pop();
                    if (a == 0)
                    {
                        Error("除数为0");
                        // 忽略
                    }
                    else
                    {
                        int c = b / a;
                        stk.push(c);
                    }
                }
                break;
    
            case INMEMORY:
                {
                    int addr = insMemory[idx].opd;
                    if (addr < 0 || addr >= datMemory.size())
                    {
                        Error("数据地址错误");
                        // 忽略处理
                    }
                    else
                    {
                        datMemory[addr] = stk.top();
                        stk.pop();
                    }
                }
                break;
    
            case OUTMEMORY:
                {
                    int addr = insMemory[idx].opd;
                    if (addr < 0 || addr >= datMemory.size())
                    {
                        Error("数据地址错误");
                        // 忽略处理
                    }
                    else
                    {
                        stk.push(datMemory[addr]);
                    }
                }
                break;
    
            case DUP:    // 将栈顶元素的值重复压栈
                {
                    stk.push(stk.top());
                }
                break;
    
            case LD:     // 弹出栈顶元素值,以值为地址,将该地址上的值压栈
                {
                    int addr = stk.top();
                    stk.pop();
    
                    if (addr < 0 || addr >= datMemory.size())
                    {
                        Error("地址错误");
                        // 忽略错误
                    }
                    else
                    {
                        stk.push(datMemory[addr]);
                    }
                }
                break;
    
            case ST:     // 弹出值,再弹出地址,将值赋予该地址
                {
                    int val = stk.top();
                    stk.pop();
                    int addr = stk.top();
                    stk.pop();
                    if (addr < 0 || addr >= datMemory.size())
                    {
                        Error("地址错误");
                        // 忽略错误
                    }
                    else
                    {
                        datMemory[addr] = val;
                    }
                }
                break;
    
            case LDC:    // 该指令有参数,将该参数压入栈中
                {
                    stk.push(insMemory[idx].opd);
                }
                break;
    
            case JLT:    // 该指令有参数,如果从栈中弹出的值小于0,则指令指针寄存器跳转到操作数
                {
                    int tmp = stk.top();
                    stk.pop();
                    if (tmp < 0)
                    {
                        idxChg  = true;
                        idxJump = insMemory[idx].opd;
                    }
                }
                break;
    
            case JLE:    // <=
                {
                    int tmp = stk.top();
                    stk.pop();
                    if (tmp <= 0)
                    {
                        idxChg  = true;
                        idxJump = insMemory[idx].opd;
                    }
                }
                break;
    
            case JGT:    // >
                {
                    int tmp = stk.top();
                    stk.pop();
                    if (tmp > 0)
                    {
                        idxChg  = true;
                        idxJump = insMemory[idx].opd;
                    }
                }
                break;
    
            case JGE:    // >=
                {
                    int tmp = stk.top();
                    stk.pop();
                    if (tmp >= 0)
                    {
                        idxChg  = true;
                        idxJump = insMemory[idx].opd;
                    }
                    break;
                }
    
            case JEQ:    // ==
                {
                    int tmp = stk.top();
                    stk.pop();
                    if (tmp == 0)
                    {
                        idxChg  = true;
                        idxJump = insMemory[idx].opd;
                    }
                }
                break;
    
            case JNE:    // !=
                {
                    int tmp = stk.top();
                    stk.pop();
                    if (tmp != 0)
                    {
                        idxChg  = true;
                        idxJump = insMemory[idx].opd;
                    }
                }
                break;
    
            case JMP:    // 无条件
                {
                    idxChg  = true;
                    idxJump = insMemory[idx].opd;
                }
                break;
    
            default:
                {
                    Error("未知指令码");
                }
                break;
            }
            if (idxChg)
            {
                idx = idxJump;
            }
            else
            {
                ++idx;
            }
        }
    }
    
    // 复位虚拟机
    void Reset()
    {
        insMemory.clear();
        datMemory.clear();
        datMemory.resize(101);
        while (!stk.empty())
        {
            stk.pop();
        }
    }
    
    int main()
    {
        // 测试虚拟机
        // 测试:
        // 输入两个数,并将其比较,将较大者输出
        //00 01      #输入第一个数                                          IN
        //01 01      #输入第二个数                                          IN
        //02 07 0001 #将数2存入到内存1中                                    INMEMORY
        //03 07 0000 #将数1存入到内存0中                                    INMEMORY
        //04 08 0000 #重新将内存0数入栈                                     OUTMEMORY
        //05 08 0001 #重新将内存1数入栈                                     OUTMEMORY
        //06 04      #将数1减去数2,数1和数2都弹栈,并将结果入栈            SUB
        //07 15 0011 #检测栈顶元素是否大于0,如果大于0进行跳转              JGT
        //08 08 0001 #如果不大于0,则将内存1输出,首先还是将内存1数入栈     OUTMEMORY
        //09 02      #将数1输出                                             OUT
        //10 19 0013 #无条件跳转到终止                                      JMP
        //11 08 0000 #如果大于0,则将内存0输出,首先还是将内存0数入栈       OUTMEMORY
        //12 02      #将数0输出                                             OUT
        //13 00      #终止                                                  HALT
    
        // 输入指令为:
        // 010107000107000008000008000104150011080001021900130800000200
        while (true)
        {
            string strCodes;
            cin >> strCodes;
            ReadInstruction(strCodes);
            ExecuteInstructions();
            Reset();
        }
    
        return 0;
    }

             上面基于堆栈的虚拟机主要包含以下几部分:

             1.虚拟机的二进制指令集定义

             2.指令结构体定义

             3.堆栈

             4.指令内存

             5.数据内存

             6.状态码的定义

             7.错误信息的处理

             8.根据指令码获取其操作数个数

             9.读取指令,这里我们是从文本中读取的指令码和操作数,而不是从二进制数据中读取。由于指令码的个数大于9,所以我们的指令码占2位,另外操作数占据4位,操作数可以是数据内存的地址,也可以是指令内存的地址,或者是具体的数值。

             10.执行指令

             11.复位

             12.测试指令:

             这里我们的测试样例是:输入两个数,将其比较输出其中较大的哪个数,指令如下:

    00 01      #输入第一个数                                         IN
    01 01      #输入第二个数                                         IN
    02 07 0001 #将数2存入到内存1中                                    INMEMORY
    03 07 0000 #将数1存入到内存0中                                    INMEMORY
    04 08 0000 #重新将内存0数入栈                                     OUTMEMORY
    05 08 0001 #重新将内存1数入栈                                     OUTMEMORY
    06 04      #将数1减去数2,数1和数2都弹栈,并将结果入栈                SUB
    07 15 0011 #检测栈顶元素是否大于0,如果大于0进行跳转                  JGT
    08 08 0001 #如果不大于0,则将内存1输出,首先还是将内存1数入栈          OUTMEMORY
    09 02      #将数1输出                                           OUT
    10 19 0013 #无条件跳转到终止                                      JMP
    11 08 0000 #如果大于0,则将内存0输出,首先还是将内存0数入栈            OUTMEMORY
    12 02      #将数0输出                                            OUT
    13 00      #终止                                                HALT
    
    输入指令为:
    010107000107000008000008000104150011080001021900130800000200

           

             另外,对于JLT、JLE、JGT、JGE、JEQ、JNE、JMP这几个跳转指令,其操作数是为下一个执行指令的地址,而非当前指令地址的增量。

             以上是我们根据《基于栈的虚拟机的实现》,相对于之前的《实现一个堆栈虚拟机》临摹的另一个版本的堆栈虚拟机。堆栈虚拟机支持更多的指令,比如算术指令、函数操作等有待我们进一步学习。另外,基于寄存器的虚拟机实现原理将在以后的学习中予以介绍。除此之外,还会研读一些别人的源码(比如XML解析器的实现),从中学习一些东西。

  • 相关阅读:
    RESTful API 设计原则
    c#的逆变和协变
    Java内部类之间的闭包和回调详解
    java的内部类
    抓包工具
    HashMap与HashTable的区别
    Java 语法清单
    Java面试问题列表
    bootstrap table api
    c# CacheManager 缓存管理
  • 原文地址:https://www.cnblogs.com/unixfy/p/3362542.html
Copyright © 2011-2022 走看看