成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

來看三段程序,你學會了什么?

開發 前端
debug 對我們來說非常重要,有很多代碼細節和問題通過肉眼是觀察出來的,我們肉眼可能能夠判斷一些簡單的程序問題,但是對于很多隱藏較深的問題,還是要依據 debug 才能發現。

學習任何一門語言都不能少的了 debug ,匯編也是。

debug 程序執行過程

下面我們就依據這幾個功能來跟蹤一下程序的執行過程。

debug 對我們來說非常重要,有很多代碼細節和問題通過肉眼是觀察出來的,我們肉眼可能能夠判斷一些簡單的程序問題,但是對于很多隱藏較深的問題,還是要依據 debug 才能發現。

下面是一段匯編代碼,這段匯編代碼我之前的文章中也給大家寫過。

assume cs:codesg
codesg segment

mov ax,0123h
mov bx,0456h
add ax,bx
add ax,ax

mov ax,4c00h
int 21h

codesg ends
end

新建文本文件,把代碼 cv 過去,然后右鍵保存,使用 dosbox 將其編譯為 1.obj 文件,鏈接為 1.exe 文件后,我們使用 ??debug 1.exe?? 命令來分析一下這段程序,并用 -r 命令來看一下初始的寄存器情況。

圖片

程序初始狀態下,可以看到 CX 中的數據為 000F,這也表示著程序的長度是 000F,1.exe 中共有 15 個字節,CX 中的內容為 000FH。

好,現在我們已經知道程序被成功的載入內存并運行起來了,但是我們現在先不妨想一下,被鏈接成為 EXE 的程序會被裝入內存的哪個地方的呢?我們怎么知道程序被裝入在哪里呢?

程序裝載的過程分下面幾步:

  1. 首先程序會從內存中找到一塊區域,記為初始地址 SA,此時的偏移地址為 0 的這樣一塊足夠容量的內存區域。

圖片

  1. 在這段區域內的頭 256 個字節中,會創建一塊稱為程序段前綴(Program Segment Prefix ,PSP)的區域,這塊區域被 DOS 用來和被加載的程序進行通信。

圖片

  1. 從這塊程序的 256 個字節開始處,也就是在 PSP 程序段前綴的后面,程序會被加載到這里,此時程序的初始地址是 SA + 10H,偏移地址為 0 。也就是 SA + 10H : 0,所以程序的初始地址就是 CS = 076AH ,IP = 0000H。

圖片

程序被裝入內存后,由 DS 段寄存器存放著內存區的段地址,此時內存區域的偏移量為 0 ,所以此時的物理地址為 SA * 16:0,我們并不用知道真實的 DS 是多少,反正都是由操作系統和 DOS 分配的。

然后這個內存區域的前 256 個字節被用于存放 PSP ,所以程序的物理地址為 SA * 16 + 256 : 0 。

SA * 16 + 256 = SA * 16 + 16 * 16 = (SA  + 16) * 16 ,轉換為 16 進制就是 SA + 10H,所以物理地址就是 SA + 10H : 0。

我們上面 debug 1.exe 之后可以看到,DS 段寄存器的值為 076AH ,而 CS 段寄存器的值為 076BH ,正好符合 076A * 16 + 10 = 076BH (注意這里的 * 16 就是左移 4 位的意思,之前文章中也解釋過原因。)

我們使用 -u 指令可以看到完整的匯編源代碼。

圖片

上圖中用紅框圈出來的就是我們這段匯編程序的源代碼,可以看到這是一個程序段,程序段的段地址始終為 076A,偏移地址在不斷變化。

我們使用 -t 命令來單步執行以下這段程序,如下圖所示。

圖片

(為了連續的觀察一下程序的執行結果,我索性直接把主要的程序步驟執行完了。)

這段程序就是 mov 和 add 的基本使用,將 0123 送入 AX 寄存器,將 0456 送入 BX 寄存器,對 AX 寄存器執行 AX = AX + BX ,再對 AX 執行 AX = AX + AX。

程序繼續向下執行,當執行到 int 21H 處,程序執行完畢,此時要使用 -p 命令結束程序的執行,如下圖所示。

圖片

當顯示 Program terminated normally 時,表示程序正常結束,這里大家先不用考慮為什么執行到 int 21 處才執行 -p 命令,也不用關心 mov ax,4c00 和 int 21 是什么意思,大家先記住就行。

由于程序裝載的過程是 command 將程序裝載進入內存,然后 debug 程序對 exe 程序其進行跟蹤,所以程序退出后也是先從 exe 程序退出到 debug 程序中,由 debug 程序再退回到 command 程序中。

下面再分析一段程序,匯編原代碼

assume cs:codesg

codesg segment

mov ax,2000H
mov ss,ax
mov sp,0
add sp,10
pop ax
pop bx
push ax
push bx
pop ax
pop bx

mov ax,4c00H
int 21H

codesg ends

end

仍然是將其保存為 test.txt,然后執行編譯和鏈接操作,將其生成可執行文件 test.exe,觀察其執行過程。

我們先使用 -r 查看一下初始寄存器的內容。

圖片

主要觀察一下 CX 、DS 、CS 和 IP 的值,是否和我們上面描述的一致,CX 存放程序長度,DS 存放程序段地址,CS 存放程序初始地址,IP 存放程序偏移地址。

再使用 -u 看一下 exe 程序的源代碼,這個 exe 程序是經過編譯和鏈接之后的程序。

圖片

我們來分析一下這段,這是一段棧段的入棧和出棧的程序,首先

mov ax,2000H
mov ss,ax
mov sp,0

是設置棧段的棧頂指令,執行完成后會設置棧頂的物理地址為 20000 H ,即 SS:SP = 2000:0000。

圖片

我們執行這個程序的過程中,發現 mov sp,0 這個指令為什么沒有出現呢?難道是我們漏寫了?查看了一下,源代碼確實是有這條指令的,難道是沒有執行?

為了驗證這個假設,我們重新 debug 一下這段程序,然后先把 SP 的值進行修改,如下圖所示。

圖片

剛開始,我們使用 -r 把 sp 的值改成 0002,然后單步執行,在執行到 mov ss,ax 之后,發現 SP 的值變為 0000,這也就是說 mov sp,0 這條指令其實是執行了的,只是 debug 模式下沒有顯示而已。

程序繼續向下執行,下面是兩個 pop 出棧操作。

圖片

pop ax 和 pop bx 做了兩件事:把寄存器清空;棧頂位置 + 2 ,所以 ax 和 bx 寄存器的內容為 0 ,并且 SP = SP + 2 ,執行后 SP = 000E。

之后是兩個 push 操作,把出棧的兩個寄存器再進行入棧,如下圖所示。

圖片

push 操作也做了兩件事情,將寄存器入棧,SP = SP - 2,由于 ax 和 bx 已經 pop 出棧了,所以寄存器內容為 0 ,最后再進行 pop 操作,然后再結束程序的執行過程。

圖片

我們再來看一下 PSP 的情況,由于程序被裝入的時候前 256 個字節是 PSP 所占用的,此時 DS(SA)處就是 PSP 的起始地址,而 CS = SA + 10H ,也就是 CS = 076AH。

debug 循環程序

下面我們來 debug 一下循環程序,看看有哪些有意思的細節。

現在有這樣一道問題,計算 ffff:0006 單元中的數乘 3 ,讓結果存儲在 dx 中。

針對這個問題,有幾個點需要思考:

  • 我們知道 ,8086 匯編語言中單個存儲單元所能存儲的最大值是 8 位,一個字節長度,范圍是 0 - 255 之間,而一個寄存器 dx 中可容納的最大值是 16 位,兩個字節長度,范圍是 0 - 65535,即使 255 * 3 也小于 65535,很顯然乘以 3 之后,dx 中能夠存放的下。
  • 數乘 3 相當于是循環做 add 自身操作 3 次,所以需要用加法來實現乘法,可以直接使用 dx 進行累加,不過需要一個 ax 來進行中轉。
  • ffff:6 內存單元是一個字節單元,而 ax 寄存器能容納的是一個字單元,無法直接賦值,該如何做呢?因為 ax 可以看做 al 和 ah ,而 al 和 ah 又是兩個單獨的寄存器,它們之間不會發生值溢出,所以讓 ah = 0 ,al = 內存單元的值即可。

所以這段匯編程序的代碼如下

assume cs:codesg

codesg segment

mov ax,0ffffh
mov ds,ax

mov ah,0
mov al,[6]

mov cx,3
s: add dx,ax
loop s

mov ax,4c00h
int 21h

codesg ends
end

編寫完畢,編譯鏈接成 exe 程序后,對其進行 debug xxx.exe 操作。

我們來看下程序的執行過程。

圖片

前兩段沒毛病,設置 DS 段寄存器的值為 FFFF 。然后繼續向下執行

圖片

執行到 mov al,[6] 的時候我發現,怎么 AX 寄存器中的內容變成 0006 了?我不是想要把 06 放入 ax 中啊,我是想把 ffff:06 內存單元中的值放入 ax 中啊,我突然意識到編譯器是個傻子。

經過我認真仔細細心耐心用心的排查了一番問題之后,我方才大悟,原來我是個傻子!不知道各位小伙伴們看出來我代碼的問題了嗎?

我怎么敢在源程序中把立即數當做內存偏移地址來用呢?必須要用 bx 中轉啊!

這也就是說,編譯器編譯完源代碼之后,會把 06 當做立即數使用,如果想要使 06 表示內存地址,必須要用 bx 進行中轉,修改之后的源代碼如下:

assume cs:codesg

codesg segment

mov ax,0ffffh
mov ds,ax
mov bx,6

mov ah,0
mov al,[bx] # 必須要用 bx 進行中轉,才能表示內存地址
mov dx,0 # 累加寄存器清 0

mov cx,3
s: add dx,ax
loop s

mov ax,4c00h
int 21h

codesg ends
end

然后再重新鏈接成為 exe 程序之后,我們一步一步 debug 看一下。

圖片

執行到 mov al,[bx] 的時候,我們發現,此時右側有個 ds:0006 = 31,這段代碼表示的是 ds:0006 處內存單元的值是 31,這才表明我們的程序是正確的。

繼續向下執行程序。

圖片

前兩條指令執行完成后,(dx) = 0 ,(cx) = 3,完成對累加寄存器的清空和循環計數器的賦值操作。最后一條指令是第一次循環操作指令,此時 CS:IP 指向 076A:0012 ,繼續向下執行。

圖片

可以看到,第一次 add dx,ax 執行完成后 IP = 0014H ,此時指向的指令是 LOOP 0012,這條指令的意思是讓程序再執行一次 (IP) = 0012H 處的指令,也就是再執行一次 add dx,ax,可以看到 cx 的值變成了 0002,因為循環指令執行后 (cx) = (cx) - 2 ,然后再向下執行,發現后面的循環指令還是 LOOP 0012 ,再執行一次 add dx,ax,一直到 (cx) = 0 后結束程序執行,如下圖所示

圖片

可以發現,整個程序一共循環三次,最終 dx 中的值是 93 ,程序執行到 int 21H 處,使用 -p 命令結束程序的執行。

責任編輯:武曉燕 來源: 程序員cxuan
相關推薦

2023-07-26 13:14:13

業務項目技術

2024-07-22 09:52:42

2023-05-19 07:31:48

2023-06-28 11:01:08

2024-07-12 09:21:38

負載均衡HTTP網絡

2024-01-19 08:25:38

死鎖Java通信

2024-02-04 00:00:00

Effect數據組件

2023-07-26 13:11:21

ChatGPT平臺工具

2023-01-10 08:43:15

定義DDD架構

2023-12-11 08:03:01

Java線程線程組

2025-01-16 00:17:44

2022-11-18 12:03:01

2023-10-10 11:04:11

Rust難點內存

2024-05-06 00:00:00

InnoDBView隔離

2024-07-31 08:39:45

Git命令暫存區

2023-01-30 09:01:54

圖表指南圖形化

2024-08-06 09:47:57

2022-07-08 09:27:48

CSSIFC模型

2023-12-12 08:02:10

2022-03-08 08:39:22

gRPC協議云原生
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 亚洲激情专区 | 国产成人精品a视频一区www | 91久久精 | 男女视频网站 | 中文字幕在线观看视频一区 | 亚洲一区二区精品视频在线观看 | 看毛片的网站 | 99一区二区| 日韩精品一二三 | 中文在线一区 | 精品免费视频 | 精品一区二区三区91 | 波多野结衣一二三区 | 日韩一区二区三区视频在线观看 | 亚洲精品一区在线观看 | 伊人精品 | 91av在线免费观看 | 久久久网| 精品国产黄a∨片高清在线 www.一级片 国产欧美日韩综合精品一区二区 | 色资源在线 | 精品综合视频 | 在线三级电影 | 色综合国产 | 国产日韩欧美一区 | 国产精品亚洲一区 | 久久久九九九九 | 人人玩人人添人人澡欧美 | 中文字幕视频三区 | 91精品国产91久久久久久吃药 | 久久久精品久 | 日韩一区二区福利 | 久久久久久久av麻豆果冻 | 国产日韩一区二区三区 | 嫩呦国产一区二区三区av | 国产精品久久久久久婷婷天堂 | 国产 日韩 欧美 制服 另类 | 日日干夜夜操 | 一区二区免费视频 | 好姑娘高清在线观看电影 | 国产欧美一区二区三区久久 | 久久精品aaa |