程序最美(寻路)

你还在坚持练习你的技术吗?运动员天天训练,音乐家也会演练更难的曲章。你呢?

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

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

         前面我们对一基于堆栈虚拟机进行了源码剖析《基于栈的虚拟机源码剖析》。之前我们也实现了一个简单的基于堆栈的虚拟机《实现一个堆栈虚拟机》。在《实现一个堆栈虚拟机》中,我们将虚拟机定义为一个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 << '\t' << 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解析器的实现),从中学习一些东西。

posted on 2013-10-11 00:32  unixfy  阅读(1181)  评论(0编辑  收藏  举报

导航