简单虚拟机

时间:2016-09-05 23:52:15

标签: c++

前段时间我创建了一个简单的模拟计算机。它有外围设备,可以渲染为OpenGL纹理的屏幕缓冲区,以及其他一些简洁的功能。它运作良好,效果很好,总的来说我很满意。

除此之外,我作弊。

底层数据类型是整数,浮点和指令类型的并集(拆分为位字段)。

对于任何正确的(模拟的)程序,union总是安全使用,只读取写入的最后一个union成员。但是,一个格式错误的程序(例如从模拟硬盘驱动器加载)可能无法访问成员的可能性可能使我暴露于与工会滥用相关的常见问题:

  • 编译时可以优化写入的可能性 - 编译器可能没有足够的信息来尝试这种优化
  • 从联盟读取的值可能是垃圾 - 这对我来说是完全可以接受的行为。
  • 以这种方式读取的浮点数可能是信号 - NaN /陷阱值 - 这是一个真正的问题 - 模拟计算机崩溃很好,但崩溃真实程序是一场灾难。
  • 这是技术上未定义的行为,所以虽然它可能会赢,但它可能会使计算机着火,擦除我的硬盘或召唤Cthulhu。

考虑的解决方案:

  • 坚持联盟 - 也许它适用于所有现实世界的平台?也许有办法消毒sNaNs?
  • 标记联盟 - 将有效地减少一半的记忆津贴
  • 分开存储的高效打包标签阵列 - 稍微繁殖标签,但在某种程度上可行。
  • char数组 - 看起来很简单,但安全地执行此操作的成本,允许从与写入的类型不同的类型读取,确实加起来。
  • 整数类型 - 如上面的浮点数和指令,区别在于整数是微不足道的。
  • char数组加上单独的整数和浮点寄存器 - 在很多方面都很理想,但需要我编写一个可以有效使用这些寄存器的编译器。

我认为这是许多SO用户曾经尝试过的那种项目,所以特别欢迎特定问题的体验。

1 个答案:

答案 0 :(得分:3)

如果您的编译器支持它,您可以使用C ++ 17 std::variant(基于boost::variant)。

编辑:为了最大限度地节省空间,选择加入类型安全,你可以做点什么

union Word { int32_t i; float f; Instruction inst; };

namespace MemAccess
{
        static std::bitset<MEM_SIZE> int32_whitelist,
                                     float_whitelist,
                                     inst_whitelist;
        static std::array<Word, MEM_SIZE> memory;
        // set or reinterpret as int32
        int32_t &
        int32_at(const size_t at)
        {
                int32_whitelist[at] = 1;
                float_whitelist[at] = inst_whitelist[at] = 0;

                return memory[at].i;
        }
        // interpret as int32 only if whitelisted
        int32_t &
        int32_checked(const size_t at)
        {
                if (int32_whitelist[at])
                {
                        return memory[at].i;
                }
                else
                {
                        throw;
                }
        }
        // equivalent functions for floats and instructions
}

编辑2:发生在我身上也可以用一个bitset完成。

static std::array<Word, MEM_SIZE> memory;
static std::bitset<MEM_SIZE * 2> whitelist;

float &
float_at(const size_t at)
{       // None = 00, Inst = 10, Int32 = 11
        whitelist[at * 2]     = 0;
        whitelist[at * 2 + 1] = 1;

        return memory[at].f;
}

float &
float_checked(const size_t at)
{
        if (!whitelist[at * 2] && whitelist[at * 2 + 1])
        {
                return memory[at].f;
        }

        throw;
}