一週幹掉彙編語言 #Day2 #第一個程序 #[BX]和loop #包含多個段的程序


基礎不會,啥都白費;基礎不牢,地動山搖。
彙編在基礎中的地位舉足輕重,學習彙編,可以幫助我們從CPU角度出發,理解程序,寫出更好的高級語言 程序。可以幫助我們理解程序的運行機制,知道原理,解決一些隱蔽的BUG。
學習步驟: 看視頻,看書,做筆記,理解爲主。習題獨立完成,全對,才能進入下一章的學習。
視頻: 小甲魚
教材: 《彙編語言(王爽)》(三版)
日期: 2020-07-01

進度:31/77

四、第一個程序:22/77

編寫
編譯連接
執行
  1. 編寫源程序文件
  2. 編譯鏈接可執行文件
  3. 執行可執行文件中的程序

可執行文件:

  1. 程序(彙編指令翻譯成的機器碼) + 數據(源程序中定義的數據)
  2. 描述信息(如,程序有多大、要佔用多少內存空間)

1. 源程序結構

源程序: 源程序文件中的所有程序,皆稱爲源程序
程序: 源程序中最終 由計算機執行,處理的指令或數據。


彙編源程序由僞指令彙編指令 構成


彙編指令: 用來翻譯成機器碼
僞指令: 給編譯器執行的,讓編譯器執行相關編譯工作。
在這裏插入圖片描述

;在彙編中表示註釋


  • 段名 segment段名 ends是一對,定義一個段,這個段用來存放代碼。(ends後面的s表示的是segment而不是複數的意思)

標號: XXX segment裏的XXX就是標號。它是一個段的名稱 ,最終會被編譯,連接成爲一個段的段地址(就類似C中的指針)

  • end用來標識程序的結束
  • assume假設寄存器和程序中的某一個XXX segment ... XXX ends段相關聯,很像給寄存器取別名

注意: 至少要有一個段(代碼段)


下圖展示了彙編指令可執行文件 的過程
在這裏插入圖片描述

2. 程序返回

DOS是一個單任務系統:

  • 一個程序p2想要運行,必須得有一個正在運行的p1,把它從可執行文件加載入內存,並將CPU的控制權交給p2,p2才能運行。
  • p2運行時,p1暫停運行。p2運行完畢,p1繼續運行。

比如仿DOS程序CMD,執行p2.exe時,CMD.exe停止執行。等到p2.exe執行完畢後,CMD.exe再次執行。

一個程序結束,然後返回調用的程序的行爲,叫做程序的返回 ret


要讓程序返回,需要以下兩行代碼
在這裏插入圖片描述
int 21H是中斷的意思,中斷機制是DOS的發明。在Win中,變成了消息機制,從而單任務系統變爲多任務系統。

注意: 上面兩行代碼是固定的,原理後面再講。

3. 程序錯誤

程序錯誤分爲:語法錯誤邏輯錯誤

  • 語法錯誤: 編譯器報錯
  • 邏輯錯誤: 運行報錯

很類似JAVA中的運行期異常編譯期異常 。但是 錯誤異常 一定要區分開來。錯誤是不可避免的,異常是可以避免的。所以,語法錯誤叫成 語法異常 更準確。邏輯錯誤,可能是由於代碼不規範,那就應該列爲 邏輯異常 ;如果是由於溢出之類導致內存出錯之類的,應該列爲 邏輯錯誤


源文件得不到目標文件的兩類錯誤:

  • severe errors有億些錯誤
  • 找不到給出的源文件程序

4. 第一個程序

  1. 編寫源程序,源程序以.asm爲擴展名
    在這裏插入圖片描述
  2. 利用masm.exe編譯1.asm生成目標文件1.obj。直接輸入masm,然後如圖操作
    在這裏插入圖片描述
    在這裏插入圖片描述
  3. 利用link.exe編譯1.obj生成可執行文件1.exe。直接輸入link,然後如圖操作
    在這裏插入圖片描述

鏈接時報了個錯誤:no stack segment沒有棧段
原因是: 在連接過程中,並未因爲有“stacksg segment”,和assume了“ss:stacksg”就認爲設置了堆棧段。
解決辦法: 在代碼段開頭添加stack關鍵字。但是! 這樣處理後,文件不能停止了,所以這個錯誤直接忽略吧在這裏插入圖片描述

  1. 直接1.exe運行。(輸入1運行也可以哦)
    在這裏插入圖片描述

有簡化方式:

  1. masm 文件名.asm;可以直接默認編譯,不用輸入參數。link也同樣有這個功能。關鍵在於;
  2. ml 文件名.asm,編譯同時鏈接

5. 程序執行過程

Shell殼:

  1. 操作系統是一個龐大的、複雜的軟件系統
  2. 通用的操作系統,需要提供一個Shell程序,讓用戶能操作計算機系統
  3. DOS中的Shell,叫做command.com命令解釋器。
  4. DOS啓動先初始化環境,然後運行command.com,等待用戶輸入(命令也是一個可執行文件)
  5. 用戶輸入可執行文件路徑,command.com會將這個程序加載入內存,設置CS/IP指向程序入口。然後command.com暫停,把CPU控制權交出。程序結束後,將控制權交給command.com,等待用戶輸入

下面這個過程,要爛熟於心:

1.asm
1.obj
1.exe
內存中的程序
編程:Edit
編譯:masm
連接:link
加載:command
運行:CPU

6. Debug單步執行

之前的程序是沒有入口的,我們編寫一個有入口的(不一定用start,只要和end後面的標號相同即可,不要忘記冒號)
在這裏插入圖片描述


用Debug單步執行: 用t單步執行,到了程序返回的代碼int 21用p在這裏插入圖片描述在這裏插入圖片描述

補充:Debug程序運行時,cx默認記錄的是程序的長度(2.EXE爲c,表示12字節大小)

程序返回是返回上一級,2.asm結束返回到debug.exedebug.exe使用q命令返回到command.com


程序被加載後,CS應該和DS指向同一個地址。看上圖,會發現CSDS=10HCS - DS = 10H,中間那一段是什麼?
在這裏插入圖片描述

  1. 在內存中找一塊地兒(特徵是,偏移地址爲0)。段地址爲SA,同時也是DS的值。
  2. 弄一個程序前綴數據區PSP,來進行程序和DOS的通信(和操作系統通信的一個接口)。這個區域佔256字節,也就是10H。
  3. 所以:DS=SADS = SACS=SA+10H=DS+10HCS = SA + 10H = DS + 10H

查看PSP部分內存:
在這裏插入圖片描述
查看CS指向部分內存:
在這裏插入圖片描述

實驗3

  1. 編寫(答案在註釋裏)在這裏插入圖片描述
  2. 編譯鏈接在這裏插入圖片描述
  3. 單步執行在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述

PSP的頭兩個字節確實是CD,通過查看彙編,我們可以知道,PSP的第一條語句是int 20,又是中斷機制,以後再學
在這裏插入圖片描述

五、[BX]和loop指令:28/77

1. debug運行程序和可執行文件寫代碼的不同

問題: Debug中有R命令,可以直接修改段寄存器的值。而mov只能通過通用寄存器間接修改。這兩者修改有什麼區別?
答: debug是程序,而mov是彙編語句,兩者沒關係!彙編中,只能通過jmp來跳轉程序。


  1. 先來一段程序: 在這裏插入圖片描述
  2. 查看內存:
    在這裏插入圖片描述
  3. 怎麼回事? 會發現中括號沒被識別在這裏插入圖片描述
  4. 查看源代碼: 果然沒識別在這裏插入圖片描述

2. 內存單元&描述性符號

要完整描述一個內存單元,需要兩種信息:

  1. 內存單元地址 DS:[偏移地址]
  2. 內存單元長度(類型)
  • 補充: mov ax, [0],傳遞的是字。mov al, [0],傳遞的是字節。
  • 重要:五.1所示,[0]這個不會被識別爲內存單元,需要使用:
    mov bx, 0
    mov ax, [BX]
    也就是拿bx中轉

描述性符號1: ()用來描述一個地址存放的內容。段寄存器:偏移地址就相當於一個指針,它指向的內存地址的值也可以用()描述。

注意: ax之類通用寄存器,本身保存的內容可以說是一個值,也可以說是一個地址。用()描述會得到這個寄存器本身保存的內容。寄存器更像是一個普通變量。
段寄存器雖然名義上是保存地址的,實際上保存的還是一個值。DS:0000可以描述一個內存單元,(DS):0000也可以描述一個內存單元。
上面的地址描述不規範,應當爲(DS)*16 + 0000

  • 可以簡單粗暴的理解爲()就是取值的意思

描述性符號2: idata表示常量

3. Loop指令

格式: loop 標號
執行loop會進行兩步操作:

  1. (cx) -= 1
  2. 如果(cx)==0,跳到標號處執行;否則向下執行

3.1 用循環計算10次方

通常,我們用loop進行循環,cx中存放循環次數
在這裏插入圖片描述
在這裏插入圖片描述


循環結構:

mov cx, 循環次數
s: 循環程序段
	loop s

注意: 寫程序前,要小心數據溢出,要事先估算數據範圍

3.2 值得注意的點&調試

5.3節代碼:
在這裏插入圖片描述

注意: 彙編程序中,程序不能以字母開頭


一個問題: 一次t只能走一步,如果循環的次數多,那是不是要一直搞t?有沒有簡便方法?有的,g命令(會將指定偏移地址前的代碼執行完畢)
在這裏插入圖片描述
p命令,可以後臺靜默執行循環:
在這裏插入圖片描述

4. Debug和彙編編譯器masm對指令的不同處理

  1. Debug默認數據16進制;masm默認數據10進制
  2. Debug用[0]來取內存單元的值;masm用[bx]來取內存單元的值,或DS:[0]來取

段前綴: 利用DS:[0]來取內存單元的值,這個DS叫做段前綴。也可以使用es/ss/es等其他方式指定段前綴。不寫的話,默認段前綴爲DS。

5. 安全的內存空間

內存空間不止我們寫的程序在用,還有包括操作系統在內的程序在使用。所以,我們在操作一個空間時,需要先知道它是安全的

  • 我們在面臨一個選擇:在操作系統中安全、規矩的編程。還是利用匯編,直接去探查硬件真相?(我選後者,直接乾硬件!)

補充: 在純DOS方式(實模式)下,可以不理會DOS,直接用匯編去操作硬件


一般情況下,0:200~0:2ff這個空間不被其他程序使用,是安全的。
在這裏插入圖片描述
安全的空間不夠了咋辦: 只要是合法的請求,系統會給的。具體怎麼給,日後再說。

6. 複製內存內容

在這裏插入圖片描述在這裏插入圖片描述

實驗4答案: 0020H, 40H
看寄存器是多少字節的。

六、包含多個段的程序:31/77

爲什麼要使用多個段: 爲了日後的封裝

1. 程序的入口

我們先來看一個程序:
在這裏插入圖片描述

  • dwdefine word,定義字型數據(db定義字節數據)

  • 段地址: 這八個數據存在哪兒呢?因爲是放在cs代碼段中,所以數據存在cs代碼段中

  • 數據的偏移地址: 從0開始,0、2、4…
    在這裏插入圖片描述

  • 代碼的偏移地址: 從16開始
    在這裏插入圖片描述

程序如何知道從16開始執行呢?可以注意到我們把程序的入口定義在了第一條程序語句前,就是這個入口。
最關鍵的不是讀錯行,而是讀錯指令。可以試下,當沒有指定程序入口時,大概率也不會讀錯。8086很聰明,它知道從第16條開始執行。但是!因爲實際讀取的是機器碼,從而導致語句錯誤(直觀就是,用u查看我們所寫代碼對應的內存,會發現這些指令和我們寫的不一樣。原因就是被CPU把機器碼錯誤組合導致的。當然,有概率不會出錯。因爲本人寫的沒出錯,所以就沒截圖了。)總結:不一定報錯,但是結果一定錯


程序怎麼知道程序的入口: 直接去找endend後面的標號是一個地址,程序直接去找這個地址執行

2. 程序中使用棧

需求: 把數據存入棧,然後逆序排列

  • 代碼: 注意閱讀註釋在這裏插入圖片描述

補充: 可以看出,彙編語言並沒有區域註釋。這一點上和C如出一轍。

  • 查看內存: 在這裏插入圖片描述
  • 程序執行完後,查看內存:
    在這裏插入圖片描述

補充: 我們可以說,定義了八個字形數據。也可以說,開闢了八個字的內存空間,然後在裏面存放了數據。兩種角度,表達不同,理解不同,效果一樣

檢測點6.1

  1. mov CS:[bx], ax。題目的意思是,內存的數據在0:0 15,程序的數據在CS:0 15中,需要用內存的數據把程序的數據換了。
  2. CS
    0020H(當然,也可以寫32)
    pop CS:[bx](有的地方會說這裏的答案是SS:[bx]。注意,雖然SS的值等於CS,但是從邏輯上不符合題意。題意是用棧作中介,改變程序的數據。當然了,合理利用棧溢出是一種靈活性,所以我才說是注意,沒說是錯誤)

3. 在程序中使用不同的段

前面我們在程序中放入了數據、棧、代碼,我們需要時刻注意內存空間是數據、棧、還是段

  1. 這樣的程序未免過於混亂
  2. 以8086爲例,如果 數據段+棧段+代碼段>64K ,那麼一個段就放不下了
  3. 程序耦合性高。如果需要把8個字數據,變成9個字數據呢?是整體右移?還是在內存中再開闢一塊,然後利用代碼進行邏輯上的聯結?顯然,兩種方法都可行,但是牽一髮而動全身

代碼: 注意閱讀註釋
在這裏插入圖片描述

補充1: 可以看出,每個部分佔用一個段。(默認是連續的三個段)
在這裏插入圖片描述原因: 如果一個數據 佔用N個字節,程序加載後,該段實際佔有的空間是16*(N/16 + 1)

補充2: 一個段的時候,DS比CS小10H,大的部分用來放PSP了。多個段的時候,DS只比CS小1H,那麼PSP在哪呢?經過本人試驗,發現PSPDS - 10H處。

注意: CPU默認把機器碼當指令而不是數據(數據就直接讀寫,指令需要CPU運算)

補充3: CPU是從上往下讀的。先定義數據段,DS就小;先定義代碼段,CS就小。

補充4: 如果不指定程序入口,CPU會從上到下,把碰到的機器碼都當成指令

實驗5

  1. 沒什麼好說的
  2. 如果一個數據佔用N個字節,程序加載後,該段實際佔有的空間是16*(N/16 + 1)
  3. CPU是從上往下讀的。先定義數據段,DS就小;先定義代碼段,CS就小。
  4. 如果不指定程序入口,CPU會從上到下,把碰到的機器碼都當成指令
  5. 可以利用多個段寄存器,如圖
    在這裏插入圖片描述
  6. 沒什麼好說的
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章