iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 3
1
Software Development

8086下16位元DOS組合語言學習筆記系列 第 3

[Day03] 記憶體地址空間和第一次反組譯

上一篇結尾講到尋址能力,尋址就是定位記憶體的地址空間,相信很多人知道 XP 作業系統有記憶體 4G 的限制,因為 XP 是 32 位元的作業系統,所以最多只能尋址 2 的 32 次方字節大小的空間,剛好等於 4GB。

2 ^ 32 = 4 x 1024 x 1024 x 1024 = 4 GB

記憶體種類

  • RAM:

RAM 的全名是隨機存取記憶體,用於程式執行中提供 CPU 暫存資料的地方,RAM 並不能永久保存資料,電腦關機後儲存在上面的資料就會消失。

  • ROM:

ROM 的全名是唯讀記憶體,顧名思義只能讀取不能寫入,程式燒入後就不能更改,儲存在上面的資料關閉電源後也不會消失,最常聽到的就是用於主機板的 BIOS 開機程式,還有一些外接卡例如顯示卡、網路卡等也會用到。

記憶體地址空間

記憶體地址空間不單指主記憶體,還包括顯卡、網路卡、BIOS、等等,雖然這些裝置物理上是獨立的,但對於 CPU 而言他們是一個整體,都是透過總線相連,可用相同的方式進行控制,所以可以把所有的存儲器當成一的大的邏輯存儲器,這個連續的邏輯空間就稱為記憶體地址空間。

邏輯存儲器示意圖:

https://ithelp.ithome.com.tw/upload/images/20181018/20106865dQftBLu8Ep.jpg

8086 CPU 的記憶體分配大致如下:

  • 主記憶體: 0000 ~ 9FFFF
  • 顯卡記憶體: A0000 ~ BFFFF
  • 其他 ROM: C0000 ~ FFFFF

總結一下

CPU 將所有存儲器當成一個大的邏輯存儲器,這個連續的空間就稱為記憶體地址空間,而它的大小受限於 CPU 的尋址能力。


第一次反組譯

接著來看個有趣的例子,有一段 C 程式如下。

#include<stdio.h>
#include<stdlib.h>

int main()
{
    int a, b;
    int x = 0;
    int y = 0;

    a = (x++)+(x++);
    b = (++y)+(++y);

    printf("a=%d, b=%d\n", a, b);   //a=1, b=4

    return 0;
}

看起來 a 沒問題 0 + 1 = 1,
但 b 應該是 1 + 2 = 3,怎麼結果是 4 呢?

這其實是個 未定義行為,C++ 標準並沒有規範這種行為編譯器該如何實作,因此不同編譯器可能有不同的結果,我們將程式反組譯來分析一下 (使用 Visual Studio)。

	int a, b;
	int x = 0;
011516BE  mov         dword ptr [x],0  
	int y = 0;
011516C5  mov         dword ptr [y],0  

011516CC  mov         eax,dword ptr [y]   ;將 y 變數地址放進 eax
011516CF  add         eax,1               ;將 eax 加一 y=1
011516D2  mov         dword ptr [y],eax   ;將 eax 放回 y 變數
011516D5  mov         ecx,dword ptr [y]   ;將 y 變數地址放進 ecx
011516D8  add         ecx,1               ;將 ecx 加一 y=2
011516DB  mov         dword ptr [y],ecx   ;將 ecx 放回 y 變數
011516DE  mov         edx,dword ptr [y]   ;將 y 變數地址放進 edx
011516E1  add         edx,dword ptr [y]   ;將 edx + y 等於 2 + 2
011516E4  mov         dword ptr [b],edx   ;將 edx 放進 b 變數,結果為 4
	b = (++y) + (++y);

可以看到編譯器應該是為了優化,先將 y 變數 +1 了兩次再相加,所以結果才會等於 4,透過組合語言可以清楚看到程式是如何執行,後面有機會再找幾個常用的語法分享給大家,昨天暐翰大說期待反組譯,結果只寫這樣希望不要被打。
/images/emoticon/emoticon16.gif

補充: Visual Studio C++ 專案在 Debug 模式時,按右鍵可以看到反組譯選項,會將組合語言穿插在 C++ 程式碼中,非常好用。

結語

下一篇要開始介紹組合語言的核心,令人非常頭痛的 暫存器,今天就到這裡摟,感謝大家觀看。


上一篇
[Day02] CPU 如何和外部溝通 - 存儲器和總線
下一篇
[Day04] 通用暫存器
系列文
8086下16位元DOS組合語言學習筆記12

2 則留言

0
johnqq
iT邦新手 5 級 ‧ 2021-09-02 16:40:11

針對b = (++y) + (++y);
請問實際上正確是4還是3?

就語意上來說我要的是(1+0)+(1+1)

結論是要改code修正語意嗎?

可以參考這篇,講的很詳細
https://www.cnblogs.com/jiayouwyhit/p/3147557.html

b = (++y) + (++y);

在同個表達式中,y 被改變了多次
這種語法屬於未定義行為,使用不同編譯器可能產生不同結果
所以建議工程師不要使用

結論是要改code修正語意嗎?

恩,將程式碼拆開來寫即可

johnqq iT邦新手 5 級 ‧ 2021-09-03 07:45:51 檢舉

我學程式那麼久才知道有這種眉角
感激不盡

/images/emoticon/emoticon12.gif

0
Troy2000
iT邦新手 5 級 ‧ 2021-09-27 14:49:24

ROM的介紹那邊484打錯了
///
RAM 的全名是唯讀記憶體,顧名思義只能讀取不能寫入,程式燒入後就不能更改,儲存在上面的資料關閉電源後也不會消失,最常聽到的就是用於主機板的 BIOS 開機程式,還有一些外接卡例如顯示卡、網路卡等也會用到。
///
應該是ROM吧

真的,謝謝提醒!

我要留言

立即登入留言