【问题标题】:Unsafe pointer manipulation不安全的指针操作
【发布时间】:2013-03-03 07:34:35
【问题描述】:

我正在尝试用 C# 编写一个 CPU 模拟器。机器的对象是这样的:

class Machine 
{
    short a,b,c,d; //these are registers.

    short[] ram=new short[0x10000];  //RAM organised as 65536 16-bit words

    public void tick() { ... } //one instruction is processed
}

当我执行一条指令时,我有一个 switch 语句,它决定指令的结果将存储在什么中(寄存器或 RAM 的一个字)

我希望能够做到这一点:

short* resultContainer;

if (destination == register)
{
    switch (resultSymbol) //this is really an opcode, made a char for clarity
    {
       case 'a': resultContainer=&a;
       case 'b': resultContainer=&b;
       //etc
    }
}
else
{
    //must be a place in RAM
    resultContainer = &RAM[location];
}

然后,当我执行完指令后,我可以简单地将结果存储为:

*resultContainer = result;

我一直在试图弄清楚如何在不破坏 C# 的情况下做到这一点。

我如何使用unsafe{}fixed(){ } 以及其他我不知道的东西来实现这一点?

【问题讨论】:

  • bregister?你的意思是resultContainer
  • 很好,谢谢!
  • 酷,DCPU / 0x10c :)
  • 是的 :) DCPU 是唯一具有非奇怪寄存器名称的架构吗?

标签: c# .net pointers unsafe fixed-point


【解决方案1】:

这就是我将如何实现它:

class Machine
{
    short a, b, c, d;
    short[] ram = new short[0x10000];

    enum AddressType
    {
        Register,
        DirectMemory,
    }

    // Gives the address for an operand or for the result.
    // `addressType`and `addrCode` are extracted from instruction opcode
    // `regPointers` and `ramPointer` are fixed pointers to registers and RAM.
    private unsafe short* GetAddress(AddressType addressType, short addrCode, short*[] regPointers, short* ramPointer)
    {
        switch (addressType)
        {
            case AddressType.Register:
                return regPointers[addrCode]; //just an example implementation
            case AddressType.DirectMemory:
                return ramPointer + addrCode; //just an example implementation
            default:
                return null;
        }
    }

    public unsafe void tick()
    {
        fixed (short* ap = &a, bp = &b, cp = &c, dp = &d, ramPointer = ram)
        {
            short*[] regPointers = new short*[] { ap, bp, cp, dp };

            short* pOperand1, pOperand2, pResult;
            AddressType operand1AddrType, operand2AddrType, resultAddrType;
            short operand1AddrCode, operand2AddrCode, resultAddrCode;

            // ... decipher the instruction and extract the address types and codes

            pOperand1 = GetAddress(operand1AddrType, operand1AddrCode, regPointers, ramPointer);
            pOperand2 = GetAddress(operand2AddrType, operand2AddrCode, regPointers, ramPointer);
            pResult = GetAddress(resultAddrType, resultAddrCode, regPointers, ramPointer);

            // execute the instruction, using `*pOperand1` and `*pOperand2`, storing the result in `*pResult`.

        }
    }
}

要获取寄存器和 RAM 数组的地址,您必须使用 fixed 语句。此外,您只能在 fixed 块中使用获取的指针。所以你必须传递指针。

【讨论】:

  • 这似乎是最好的方法,但它仍然很丑:(
【解决方案2】:

如果我们换个角度看问题呢?执行指令并将结果存储在变量 (result) 中,然后决定将结果放在哪里。不行吗?

【讨论】:

  • 这是个好主意,但我认为它不会很好用,因为大多数指令都像:add a,b,所以我必须将ab放入一个变量,添加它们,并将结果放入a。我需要能够在计算之前访问a
【解决方案3】:

一个不错的选择是生成您的仿真代码,我曾为 DevKit 计划但尚未实施。这是一个久经考验、久经考验的解决方案。对于每个操作码/寄存器组合,或一些有效的子集,分别为 ADD A、B 和 SUB X、Y 生成代码。您可以使用位掩码来获取您正在寻找的特定情况并简单地执行正确的代码。编译器应该能够在后台使用跳转表,并且没有查找,没有条件,应该非常有效。

【讨论】:

    猜你喜欢
    • 2017-06-27
    • 1970-01-01
    • 2018-07-29
    • 2014-08-30
    • 1970-01-01
    • 2013-08-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多