2012-06-01 39 views
3

我想在C中編寫一個指令集模擬器來模擬運行ARM的機器。 我需要能夠高效地表示4GB存儲器和一些挖掘之後都來創建的1024個指針每個指向的數組,其動態地在其第一次使用分配4MB的塊的溶液分別代表內存c

#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); 
} 

這些不過我似乎是正確的我的頭仍然不能百分之百地使用指針,所以這可能是錯誤的。

很抱歉,如果這已經討論的地方,但我不能找到任何培訓相關到什麼,我需要(我的關鍵字是非常寬泛的)

+0

檢查malloc的失敗 - 如果(MEM [IDX] == NULL)... – djechlin

+0

*評論刪除* –

回答

2

如果PAGESIZE是2的冪,它只有1個比特組。因此將它與另一個值進行AND運算只能在結果中設置零或一位。兩個可能的值。但是你將它用作數組索引。

此外,您的memWrite(uint32_t idx, uint32_t ofs, uint32_t val)函數始終ANDs的值爲val。因此,例如,如果valuint32_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; 
} 
0

我不相信的memOfs值被正確計算。例如,由PAGESIZE代表的十進制值4194304是十六進制的0x400000,這意味着在按位「與」操作之後,您只獲取原始地址的位22,而不是較低的22位。將該值添加到4MB頁面數組指針實際上會將您發送到堆中分配數組的末尾。將偏移計算的掩碼更改爲0x3FFFFF,然後按位與原始內存地址進行AND運算,以便計算頁面中的正確偏移量。例如:

memIdx = memAdd >> PAGEEXP; 
memOfs = memAdd & 0x3FFFFF; //value of memOfs will be between 0 and 4194303 
+0

感謝您的答案。不能相信我犯了一個簡單的錯誤,比如嘗試和一點點。也對如何構建我的程序提供了一些很好的見解! – Scott

4

從邏輯上開始看,而不是在位級別。

您每頁有4,194,304個字節。

然後,在算術上將線性地址轉換爲(頁面,偏移量)對,除以4,194,304以獲取頁面編號,並將剩餘部分轉換爲頁面中的偏移量。

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

既然要有效地做到這一點,這是2的冪,則可以通過PAGESIZE與PAGESIZE的2爲底的對數,這是22右移代替除法:

page = address >> PAGEEXP; 

這樣你的部分代碼是正確的。但是,想要獲得偏移量的方法是屏蔽除剛剛移出頁碼外的所有位。要做到這一點,你必須與PAGESIZE - 1

offset = address & (PAGESIZE - 1); 

這是因爲在二進制,你剛開始使用是一個數字,看起來像這樣(其中p爲頁號位和O是偏移位):

address = ppppppppppoooooooooooooooooooooo 

你想要自己獲取頁碼和偏移號碼。你顯然希望通過22位右移,以獲得頁碼:

page = addresss >> 22 = 0000000000000000000000pppppppppp 

但是,如果你和頁面大小的(00000000010000000000000000000000二進制),你只有一個在答案最多一個1位,它只會告訴你頁碼是奇數還是偶數。沒有用。

你想以怎樣的反而比少一位,這是二進制00000000001111111111111111111111,即:

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

這是你如何得到補償。

這是一條通用規則:如果N是2的整數次冪,那麼除以N與通過log(N)/ log(2)右移相同,並且給出這種除法的餘下部分通過與(N-1)進行AND運算。

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(); 
}