3

ARM を実行しているマシンをシミュレートするために C で命令セット シミュレータを作成しようとしています。私は4GBのメモリを効率的に表現できるようにする必要があり、掘り下げた後、それぞれが最初の使用時に動的に割り当てられる4MBのブロックを指す1024個のポインターの配列を作成する解決策に達しました

#define MEMSIZE 1024    //1024 * 2Mb = 4Gb
#define PAGESIZE 4194304    //4 Mb
#define PAGEEXP 22      //2^PAGEEXP = PAGESIZE

uint32_t* mem[MEMSIZE];

私の質問は、メモリの特定のアドレスにアクセスするにはどうすればよいですか?

私が試したのは、以下のようにアドレスをインデックスとオフセットに分割することですが、これはインデックスとオフセットの両方に対して 0 しか返さないようです。(memAdd はアクセスしようとしているアドレスです)

memIdx = memAdd >> PAGEEXP;
memOfs = memAdd & PAGESIZE;

アドレスを取得したら、読み取り/書き込みに使用する関数を以下に示します。

void memWrite(uint32_t idx, uint32_t ofs, uint32_t val)
{
    if(mem[idx] == 0)
        mem[idx] = malloc(PAGESIZE);
    *(mem[idx] + ofs) = *(mem[idx] + ofs) & val;
}

uint32_t memRead(uint32_t idx, uint32_t ofs)
{
    if(mem[idx] == 0)
        return 0;
    else
        return *(mem[idx] + ofs);
}

これらは私の頭の中で正しいように見えますが、私はまだポインターに 100% 慣れていないので、これは間違っている可能性があります。

これがすでにどこかで議論されている場合は申し訳ありませんが、必要なものに関連するものを見つけることができませんでした(私のキーワードはかなり広いです)

4

4 に答える 4

4

ビットレベルではなく、論理的に見て始めましょう。

それぞれ4,194,304バイトのページがあります。

次に、算術的に、線形アドレスを(ページ、オフセット)ペアに変換するには、4,194,304で除算してページ番号を取得し、余りを取得してオフセットをページに取得します。

page = address / PAGESIZE;
offset = address % PAGESIZE;

これを効率的に実行する必要があり、これらは2の累乗であるため、PAGESIZEによる除算をPAGESIZEの2を底とする対数(22)による右シフトに置き換えることができます。

page = address >> PAGEEXP;

そのため、コードのその部分は正しいです。ただし、オフセットを取得するために実行したいのは、ページ番号からシフトアウトしたビット以外のすべてをマスクすることです。そのためには、ANDを使用する必要がありますPAGESIZE - 1

offset = address & (PAGESIZE - 1);

これは、バイナリでは、最初は次のような数値であるためです(pはページ番号ビット、oはオフセットビット)。

address = ppppppppppoooooooooooooooooooooo

ページ番号とオフセット番号を自分で取得したい。ページ番号を取得するには、明らかに22ビット右にシフトする必要があります。

page = addresss >> 22 = 0000000000000000000000pppppppppp

ただし、ページサイズ(バイナリでは00000000010000000000000000000000)とANDをとると、回答には最大で1ビットしか含まれず、ページ番号が奇数か偶数かがわかります。役に立たない。

ANDを使用する場合は、代わりに1ビット少なくなります。これはバイナリ000000000011111111111111111111111であるため、次のようになります。

  ppppppppppoooooooooooooooooooooo 
& 00000000001111111111111111111111
-----------------------------------
= 0000000000oooooooooooooooooooooo

これがオフセットを取得する方法です。

これは一般的な規則です。Nが2の整数乗の場合、Nによる除算は、log(N)/ log(2)による右シフトと同じであり、そのような除算の余りは、( N-1)。

于 2012-06-01T03:26:38.987 に答える
2

これはあなたが望むことをします。小さいサイズのものを使用しました。わかりやすくするために、エラーチェックを省略しました。インデクサー配列を使用するスキームを使用します。

#include <cstdlib>
#include <cstdio>
#include <stdint.h>

#define NUMPAGE 1024
#define NUMINTSPERPAGE 4

uint32_t* buf;
uint32_t* idx[NUMPAGE];

void InitBuf()
{
    buf = (uint32_t*) calloc(NUMPAGE, NUMINTSPERPAGE * sizeof uint32_t );
    for ( size_t i = 0; i < NUMPAGE; i++ )
    {
        idx[i] = &buf[i * NUMINTSPERPAGE * sizeof uint32_t];
    }
}

void memWrite(size_t i, size_t ofs, uint32_t val)
{
    idx[i][ofs] = val;
}
uint32_t memRead(size_t i, size_t ofs)
{
    return idx[i][ofs];
}
int main()
{
    InitBuf();
    uint32_t val = 1243;
    memWrite(1, 2, val);
    printf("difference = %ld", val - memRead(1, 2));
    getchar();
}
于 2012-06-01T03:32:32.320 に答える
2

が 2 の累乗の場合PAGESIZE、1 ビットのみが設定されます。したがって、それを別の値と AND 演算すると、結果には 0 または 1 ビットしかセットされません。2 つの可能な値。しかし、配列インデックスとして使用しています。

また、memWrite(uint32_t idx, uint32_t ofs, uint32_t val)関数は常に の値で AND しますval。したがって、たとえば、この関数を呼び出しても効果はvalありuint32_maxません。

最後に、失敗の結果をチェックしないだけでなくmalloc()、返されたメモリ ブロックを初期化しません。

このようなアプローチを試してみてください (残念ながら、私はそれをテストすることができませんでした。今のところ手元にコンパイラーがありません)。

enum { SIM_PAGE_BITS = 22 };   // 2^22 = 4MiB
enum { SIM_MEM_PAGES = 1024 }; // 1024 * 4MiB = 4GiB
enum { SIM_PAGE_SIZE = (1<<SIM_PAGE_BITS) };
enum { SIM_PAGE_MASK = SIM_PAGE_SIZE-1 };
enum { UNINITIALISED_MEMORY_CONTENT = 0 };
enum { WORD_BYTES = sizeof(uint32_t)/sizeof(unsigned char) };

#define PAGE_OFFSET(addr) (SIM_PAGE_MASK & (uint32_t)addr)
// cast to unsigned type to avoid sign extension surprises if addr<0
#define PAGE_NUM(addr) (((uint32_t)addr) >> SIM_PAGE_BITS)
#define IS_UNALIGNED(addr) (addr & (WORD_BYTES-1))

unsigned char* mem[MEMSIZE];

uint32_t memRead(uint32_t addr) {
    if (IS_UNALIGNED(addr)) return handle_unaligned_read(addr);
    const uint32_t page = PAGE_NUM(addr);
    if (mem[page]) {
        const unsigned char *p = mem[page] + PAGE_OFFSET(addr);
        return *(uint32_t*)p;
    } else {
        return UNINITIALISED_MEMORY_CONTENT;
    }
}

void memWrite(uint32_t addr, uint32_t val) {
    if (IS_UNALIGNED(addr)) return handle_unaligned_write(addr, val);
    const uint32_t page = PAGE_NUM(addr);
    if (!mem[page]) {
        if (val == UNINITIALISED_MEMORY_CONTENT) {
            return;
        }
        mem[page] = malloc(SIM_PAGE_SIZE);
        if (!mem[page]) {
            handle_out_of_memory();
        }
        // If UNINITIALISED_MEMORY_CONTENT is always 0 we can 
        // use calloc instead of malloc then memset.
        memset(mem[page], UNINITIALISED_MEMORY_CONTENT, SIM_PAGE_SIZE);
    } 
    const unsigned char *p = mem[page] + PAGE_OFFSET(addr);
    *(uint32_t*)p = val;
}
于 2012-06-01T03:35:14.697 に答える
0

memOfsの値が正しく計算されているとは思えません。たとえば、 で4194304表されるPAGESIZE10進数値0x400000は 16 進数です。つまり、ビットごとの AND 演算の後、下位 22 ビットではなく、元のアドレスのビット 22 のみが取得されます。その値を 4MB の page-array-pointer に追加すると、実際にはヒープ上の割り当てられた配列の末尾を超えて送信されます。オフセット計算用のマスクを に変更0x3FFFFFし、ページへの適切なオフセットを計算するために、元のメモリ アドレスとビットごとに AND します。たとえば、次のようになります。

memIdx = memAdd >> PAGEEXP;
memOfs = memAdd & 0x3FFFFF;  //value of memOfs will be between 0 and 4194303
于 2012-06-01T03:14:32.247 に答える