3

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(){ }そしておそらく私が気付いていない他のことを達成できますか?

4

3 に答える 3

1

問題を別の観点から見た場合はどうなりますか?命令を実行し、結果を変数(result)に格納してから、結果を配置する場所を決定します。動作しませんか?

于 2013-03-03T07:50:19.430 に答える
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また、ブロック内で取得したポインタのみを使用できます。したがって、ポインタを渡す必要があります。

于 2013-03-03T09:07:56.587 に答える
0

私がDevKitで計画したがまだ実装されていない良いオプションは、エミュレーションコードを生成することです。これは、古くからの、試行錯誤されたソリューションです。すべてのオペコード/レジスタの組み合わせ、またはいくつかの効率的なサブセットについて、ADD A、BおよびSUB X、Yのコードを個別に生成します。ビットマスクを使用して、探している特定のケースを取得し、正しいコードを実行することができます。コンパイラーは内部でジャンプテーブルを使用できる必要があり、ルックアップや条件文がないため、かなり効率的である必要があります。

于 2013-03-03T09:15:49.867 に答える