吴忠躺衫网络科技有限公司

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

一文了解多個段的相關程序

程序員cxuan ? 來源:程序員cxuan ? 2023-03-08 14:28 ? 次閱讀

這是 x86 匯編連載系列第七篇文章,前六篇文章見文末。

上回我們簡單認識了一下什么是段,段前綴和一段安全的段空間是哪里,但是程序中不會僅有一個段,復雜程序必然是包含多個段的,這篇文章我們就來了解下多個段的相關程序。

內存地址空間是由操作系統直接管理和分配的,一般操作系統分配空間有兩種方式:

程序由磁盤載入內存中時,會由操作系統直接為程序分配其運行所需要的內存空間。

程序在運行時可以動態向操作系統申請分配內存空間。

如果想要在程序被載入時獲得內存空間分配,我們就需要在源程序中對其進行聲明,通過定義多個段的方式來申請內存空間。這樣的好處是能夠保證段內的數據連續,而且對于我們來說也能夠清晰明白的看懂程序邏輯,所以我們一般采用定義多個段的方式編寫程序。

在代碼段 cs 中使用數據

現在考慮這樣一個問題,如何累加下面這幾個數據的和,并把它放在一個 ax 寄存器中呢?

0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h

我們當然可以通過一個數據接一個數據這樣進行累加,并把每次累加的結果都用 ax 進行存儲,簡單一點的方式是通過循環的方式累加,一共需要累加 8 次數據,用 ax 存儲。但是現在就有一個問題,這 8 個數據應該放在哪呢?我們之前學的累加做法都是把他們放在一組連續的內存單元,這樣我們就可以累加內存中的數據來把它們進行累加了,但是如何把這些數據放在一個連續的內容單元中呢?這段內存單元又是從哪找呢?

我們可以不用自己找內存單元,直接讓操作系統分配,我們只需要定義這些數據并把它們放在一個連續的內存單元即可,具體該怎么做呢?請看下面代碼

assume cs:code
code segment

 dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h

 mov ax,0
 mov bx,0

 mov cx,8

s: add ax,cs:[bx]
 add bx,2
 loop s

 mov ax,4c00h
 int 21h

code ends
end

上面匯編代碼中出現了之前我們沒有學過的 dw,dw 的含義是定義字型數據,dw 的全稱就是 define word,上面代碼使用 dw 定義了 8 個字型數據,它們所占用的空間為 16 個字節。

定義了數據之后,我們就需要對這 8 個數據進行累加,我們該如何找到這 8 個數據呢?

仔細觀察上面代碼,我們可以知道這 8 個數據在代碼段 cs 中,所以我們可以通過 cs 來找到這幾個數據,所以 cs 是段地址,而偏移地址是用 ip 表示的,也就是說這 8 個數據的偏移地址分別是 cs:0 cs:2 cs:4 cs:6 cs:8 cs:a cs:c cs:e 。

我們將上面這段代碼編寫、編譯和鏈接后,對其 exe 文件進行 debug :

d546cf78-ba6a-11ed-bfe3-dac502259ad0.png

??????等下,這個 and ax,[bx+di] 是什么東西?再看下 cs:ip 的地址,是初始地址沒錯啊,為啥沒看到程序中的指令呢?我們再用 debug -u 看看

d554b6ec-ba6a-11ed-bfe3-dac502259ad0.png

這前幾條指令都是什么東西?怎么 mov bx,0000 在偏移地址 0013 處?

從上圖中我們可以看到,程序被加載入內存之后,所占內存空間的前 16 個單元存放在源程序中用 dw 定義的數據,后面的單元存放源程序中匯編指令所對應的機器指令。

那么如何執行匯編指令所定義的機器指令呢?你可以直接 -t 慢慢的執行到 mov bx,0 處,也可以改變 ip 寄存器的值,也就是 ip = 10h,從而使 cs:ip 指向程序的第一條指令。

這兩種方式看起來哪個都有一定的局限性,那么還有沒有更簡潔一點的方式呢?

看下面這段代碼

assume cs:code
code segment

 dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h

start: mov ax,0
 mov bx,0

 mov cx,8
s: add ax,cs:[bx]
 add bx,2
 loop s

 mov ax,4c00h
 int 21h

code ends
end start

仔細看這段代碼,和上面那段代碼有什么區別?

只有兩個區別,一是在 mov ax,0 前面加了一個 start: 標志,并且在 end 后加了一個 start 標志,這兩個標志分別指向程序的開始處和結束處。

那么問題來了,為什么你加一個 start: 標志就說這是程序的開始處,我隨便加個比如 begin: ,能不能成為程序的開始處呢?

assume cs:code
code segment

 dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h

begin: ......

code ends
end begin

經過我的實驗驗證是可以的,為什么呢?

關鍵點并不在于你定義的是什么標志,而在于最后的 end,end 除了能夠告訴編譯器程序結束的位置外,還能夠告知編譯器程序是從哪里開始的,在上面代碼中我們用 start: 和 end start 告訴程序從 start 處開始,并執行到 end start 處結束,而 mov ax,0 是程序的第一條指令。

程序開始標志 start: 和 IP

我們知道,CS 和 IP 這兩個寄存器能夠指明程序的開始處和程序執行的偏移地址,而且 start: 標號指明了程序開始的地方,那么 start: 標號所指向的偏移地址是不是我們之前討論的 10h 處呢?

d563a828-ba6a-11ed-bfe3-dac502259ad0.png

我們編譯鏈接執行程序看一下。

d5806d8c-ba6a-11ed-bfe3-dac502259ad0.png

我們通過 -u 和 -r 分別執行了一下,可以看到,程序的起始地址都是 076A:0010 ,這也就是說,除了我們 dw 定義的幾條數據外,IP 指向的偏移地址 0010 就是程序的開始處。

所以有了這種方法,我們就可以這樣安排程序框架:

assume cs:code
code segment
...數據

start:
... 代碼

code ends
end start

在代碼段 cs 中使用棧

除了在 cs 代碼段中定義數據,還可以在 cs 代碼段中定義棧,比如下面這個需求:

利用棧將程序定義的數據逆序存放,源代碼如下

assume cs:codesg
codesg segment

 dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
 
 ......
 
codesg ends
end

如何實現將數據逆序存放呢?可以這么思考:

先定義一段和數據相同大小的棧空間,然后把數據入棧,然后再依次出棧就能夠實現逆序存放了,因為棧是后入先出的數據結構,最開始 push 進去的最后 pop 。

代碼如下:

assume cs:codesg
codesg segment

dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h

dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

start:mov ax,cs
mov ss,ax
mov sp,30h

mov bx,0
mov cx,8
s0:push cs:[bx]
add bx,2
loop s

mov bx,0
mov cx,8
s1:pop cs:[bx]
add bx,2
loop s1

mov ax,4c00h
int 21h

codesg ends
end start

解釋下上面這段代碼:

首先先定義了 16 個值為 0 的字型數據,程序被載入內存后,操作系統會為程序分配 16 個字型數據空間,用于存放 16 個數據,把這段空間當做棧來使用。

start 標號處開始程序,首先先要設置一段空間為棧段,由于我們只有一個 cs 段,所以程序是從 cs 段開始的,剛開始 dw 的 8 個數據所需的地址空間為 cs:0 ~ cs:f,后面 dw 定義的 16 個字所需的地址空間是 cs:10 ~ cs:2f ,由于 ss:sp 這兩個寄存器始終指向棧頂,目前還沒有數據入棧,所以棧頂必須為 2f + 1 ,也就是 30h 才可。

編譯鏈接執行后的棧段如下圖所示

d58b941e-ba6a-11ed-bfe3-dac502259ad0.png

這是程序被載入后的內存分配情況,可以看到,cs:0 ~ cs:f 存儲的是 8 個字型數據,cs:10 ~ cs:2f 存儲的是 16 個字型 0 數據。

程序執行完成后的內存分配圖如下,可以看到已經實現了數據的逆序存放。

d5982828-ba6a-11ed-bfe3-dac502259ad0.png

可見,我們定義這些數據的最終目的,是通過它們取得一定容量的內存空間,所以我們在描述 dw 的作用時,可以說用它來定義數據,也可以說用它來開辟一段內存空間。比如上面的 dw 0123h ... 0987h ,可以說定義了 8 個字型數據,也可以說開辟了 8 個內存空間,它們的效果是一樣的。

多個段的使用

上面討論的內容都是將數據和棧放入一個段中,這樣做雖然比較省事,但是程序邏輯不夠清晰,像個大雜燴一樣,而且都放入一個段中,這個段的內存有可能不夠用(8086 CPU 中一個段最大不能超過 64 KB)。

為了能夠清晰說明程序邏輯,并且能夠容納數據和棧,我們一般使用多個段的方式分別將數據、代碼和棧分別放入各自的段中。

比如我們通過多個段的方式將上面實現逆序存放的程序進行改寫,代碼如下

assume cs:codesg,ds:data,ss:stack

data segment
dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
data ends

stack segment
dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
stack ends

code segment
 
start: mov ax,stack
mov ss,ax
 mov sp,20h

 mov ax,data
 mov ds,ax

 mov bx,0
 mov cx,8
s0: push [bx]
 add bx,2
 loop s0

 mov bx,0
 mov cx,8
s1: pop [bx]
 add bx,2
 loop s1
 
 mov ax,4c00h
 int 21h

code ends
end start

如上代碼所示,在 assume 后面定義了三個段,這些都是偽指令,只是為了方便說明段的類型。然后分別在 data segment 和 stack segment 定義了相關數據,最后再 code segment 編寫的程序代碼

基本上代碼和在一個段中的定義是一樣的,值得說明是,同一個段中的 ss:sp 指向的是 ss:30 ,而在這段代碼中的 ss:sp 指向的是 ss:20 ,這個大家知道是怎么回事吧?由于數據會直接定義在 data segment 中,所以棧段的 16 個字型數據占用的空間就是 ss:0 ~ ss:1f,所以 ss:sp 指向 20h 處。

其實,代碼段、數據段、棧段完全是我們自己定義的,但是并不是我們分別定義了 cs:code,ds:data,ss,stack 之后,程序就會把它們分別當做程序段、數據段和棧段的。要知道這些和 assume 一樣都是偽指令,它們是由編譯器執行的,CPU 并不知道這些指令的存在,所以我們必須要在程序中告訴 CPU 哪個是棧段、哪個是數據段。

該如何告訴 CPU 呢?

我們看下棧段的設置指令

mov ax,stack
mov ss,ax
mov sp,20h

這樣就會將 ss 指向 stack ,ss:sp 用來指向 stack 棧段的棧頂地址,只有在這個指令執行后,CPU 才會把 stack 段當做棧段來使用。

相同的,CPU 如果想訪問 data 段中的數據,則可用 ds 指向 data 段,用其他寄存器比如 bx 來存放 data 段中的偏移地址即可。

所以,我們完全可以按照下面這種方式來定義程序

assume cs:a,ds:b,ss:c

b segment
dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
b ends

c segment
dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
c ends

a segment
 
d: mov ax,c
 mov ss,ax
 mov sp,20h

 mov ax,b
 mov ds,ax

 mov bx,0
 mov cx,8
s0: push [bx]
 add bx,2
 loop s0

 mov bx,0
 mov cx,8
s1: pop [bx]
 add bx,2
 loop s1
 
 mov ax,4c00h
 int 21h

code a
end d

最終程序的功能和上面的一模一樣。

審核編輯:湯梓紅
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 數據
    +關注

    關注

    8

    文章

    7139

    瀏覽量

    89576
  • 內存
    +關注

    關注

    8

    文章

    3055

    瀏覽量

    74327
  • 操作系統
    +關注

    關注

    37

    文章

    6892

    瀏覽量

    123742
  • 程序
    +關注

    關注

    117

    文章

    3795

    瀏覽量

    81406
  • 代碼
    +關注

    關注

    30

    文章

    4825

    瀏覽量

    69046

原文標題:多個段的程序

文章出處:【微信號:cxuangoodjob,微信公眾號:程序員cxuan】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    全面了解linux相關知識

    今天浩道跟大家分享linux實用場景相關的實例,助你全面了解linux相關知識。
    發表于 01-29 11:09 ?522次閱讀

    了解STM32啟動過程

    款芯片的啟動文件都值得去研究,因為它可是你的程序跑的最初一段路,不可以不知道。通過了解啟動文件,我們可以體會到處理器的架構、指令集、中斷向量安排等內容,是非常值得玩味的。
    發表于 05-08 09:44 ?3310次閱讀
    <b class='flag-5'>一</b><b class='flag-5'>文</b><b class='flag-5'>了解</b>STM32啟動過程

    詳解PLC子程序與子程序指令

    在編程時經常會遇到相同的程序需要多次執行的情況,如圖6-39所示,程序A要執行兩次,編程時要寫兩相同的
    的頭像 發表于 12-14 13:33 ?9202次閱讀
    <b class='flag-5'>一</b><b class='flag-5'>文</b>詳解PLC子<b class='flag-5'>程序</b>與子<b class='flag-5'>程序</b>指令

    帶你了解步進電機的相關知識

    帶你了解步進電機的相關知識:相、線、極性和步進方式2017-09-07 16:45這里不說步進電機的 “細分” 實驗,只說下有關步進電
    發表于 07-08 06:48

    了解BLDC與PMSM的區別

    參考文件:了解BLDC與PMSM的區別? ?????BLDC和PMSM電機區別???? ? STM32 FOC BLDC與PMSM的區別PS:總結語句用紅色標出,看紅色字體即可。現代電機與控制
    發表于 08-30 08:38

    了解LVGL的學習路線

    “本文大部分內容來自LVGL官方文檔,手翻版,如有錯誤歡迎指正。”系列文章目錄、LVGL系列(了解LVGL的學習路線輕松
    發表于 12-07 12:55

    了解通信技術的常用名詞解釋

    了解通信技術的常用名詞解釋
    的頭像 發表于 06-19 17:55 ?6108次閱讀

    了解氣象觀測站是什么?

    了解氣象觀測站是什么?
    的頭像 發表于 09-12 13:17 ?1420次閱讀

    了解pcb電路板加急打樣流程

    了解pcb電路板加急打樣流程
    的頭像 發表于 11-08 14:21 ?6467次閱讀

    了解 PCB 的有效導熱系數

    了解 PCB 的有效導熱系數
    的頭像 發表于 11-24 15:48 ?2115次閱讀
    <b class='flag-5'>一</b><b class='flag-5'>文</b><b class='flag-5'>了解</b> PCB 的有效導熱系數

    了解剛柔結合制造過程

    了解剛柔結合制造過程
    的頭像 發表于 12-04 16:22 ?847次閱讀

    了解單向晶閘管的結構及導電特性

    了解單向晶閘管的結構及導電特性
    的頭像 發表于 12-05 15:52 ?1503次閱讀
    <b class='flag-5'>一</b><b class='flag-5'>文</b><b class='flag-5'>了解</b>單向晶閘管的結構及導電特性

    了解相控陣天線中的真時延

    了解相控陣天線中的真時延
    的頭像 發表于 12-06 18:09 ?2194次閱讀

    帶你了解 DAC

    了解 DAC
    的頭像 發表于 12-07 15:10 ?1w次閱讀
    <b class='flag-5'>一</b><b class='flag-5'>文</b>帶你<b class='flag-5'>了解</b> DAC

    pcb應變測試有多重要?了解

    pcb應變測試有多重要?了解
    的頭像 發表于 02-24 16:26 ?1179次閱讀
    金矿百家乐的玩法技巧和规则| 百家乐规则博彩正网| 阴宅风水24山分金| 百家乐乐城皇冠| 百家乐网上赌博网| 贵宾百家乐的玩法技巧和规则| 大发888娱乐城游戏下载| 菲律宾新利国际| 百家乐官网必赢法软件| 玩百家乐官网的高手| 网上百家乐公式| 百家乐解析| 真人游戏下载| 灌云县| 什么是百家乐官网的大路| 百家乐双面数字筹码| 百家乐追号工具| 宝胜娱乐场| 百家乐官网自动投注| 百家乐官网赌博筹| 真博百家乐的玩法技巧和规则| 皇冠即时走地| 百家乐官网菲律宾| 百家乐怎样出千| 香港六合彩网址| 网站百家乐官网博彩| 缅甸百家乐龙虎斗| 娱网棋牌官方下载| 赌博百家乐官网下载| 百家乐庄闲| 棋牌室标语| 百家乐官网象棋玩法| 百家乐硬币打法| 大发888娱乐城欢迎lm0| 百家乐官网是片人的吗| 百家乐注册开户| 大发888最新信息| 百家乐官网赌博论谈| 澳门百家乐网上赌博| 彩票预测| 百家乐官网过滤工具|