0 前言
你是否因為匯編指令繁雜的規則而苦惱呢?作者本人也很煩,因為往往教材中只告訴我們規則,卻不告訴我們為什么,沒有原因就直接記憶,負擔太大,后期靈活運用也增添阻力,因此,我經過自己的思考去為你解釋為什么有些規則是內樣的,本文并不是深入硬件原理,只在匯編語言層級,從邏輯上說明原因。
x86指令集的核心原則:操作數最多只能有一個是內存單元,另外的可以是立即數或者寄存器
1 寄存器的分類
我先從其他博客為你找來了8086CPU的全部寄存器及其功能
原文鏈接在此
通用寄存器:
ax——accumulate register——累加器
bx——based register——基地址寄存器
cx——count register——計數器
dx——data registered——數據寄存器
段寄存器:
cs——code segment——代碼段
ds——data segment——數據段
ss——stack segment——棧段寄存器
es——extra segment——附加段寄存器
特殊功能寄存器:
ip——instructor point——指令指針寄存器
sp——stack point——堆棧指針寄存器
bp——base point——基礎指針
si——source index——源變址寄存器
di——destination index——目的變址寄存器
psw——program state word——程序狀態字
Psw的常用標志:
OF(11位-overflow flag-溢出標志位)——OV(overflow-溢出)——NV(not overflow-沒溢出)
DF(10位-direction flag-方向標志位)——DN(down-下方)——UP(up-上方)
IF(9位-interrupt flag-中斷標志位)——EI(enable interrupt-允許中斷)——DI(disabled interrupt-不允許中斷)
TF(8位-trap flag-陷阱標志位)
SF(7位-sign flag-負號標志位)——NG(negative-負)——PL(plus-正)
ZF(6位-zero flag-零值標志位)——ZR(zero-為零)——NZ(not zero-不為0)
AF(4位-auxiliary carray flag-輔助進位標志位)——AC(auxiliary carry-有輔助進位)NA(not auxiliary carry-沒有輔助進位)
PF(2位-parity flag-奇偶標志位)——PE(parity even-偶)——PO(parity odd-奇)
CF(0位-carry flag-進位標志位)——CY(carried-有進位)——NC(not carried-沒進位)
不過我想說的是,這些內容看看就好,平時做對應內容學習的時候,作為參考資料就可以。
我根據匯編指令的規則,將其重新進行了分類。
1.1 煩人的匯編指令規則
我們知道,通用寄存器,用于存放一般的數據。
那么:
- 一般的數據到底是什么?
- 通用寄存器到底用來存什么數據的?
- 它能夠存放來自哪些地方的數據?
- 它存放的數據又能夠流向那些地方?
不知道你有沒有想過這些問題,不知道你有沒有因為8086CPU基本指令的一些規則而煩惱,例如
mov 段寄存器,通用寄存器
允許:mov ds,ax
允許,但是mov cs,ax
又不允許了mov 段寄存器,通用寄存器
允許,但是add 段寄存器,寄存器
又不允許了mov 通用寄存器,立即數
允許,但是mov 段寄存器,立即數
又不允許了- IP不能作為操作數,而
jmp
指令又可以修改CS:IP的值 - ……
不知道你煩不煩,反正我是煩了,這與高級語言不同,因為他們都是寄存器啊,憑什么有的行,有的就不行了?
但是仔細想想,高級語言,其實是將不同操作的內容,明確分類了!,并且將一些容易出錯的操作,直接封裝了。
順著這個思路,我們是不是也可以將寄存器,按照其指令規則進行分類呢?我們可以做出嘗試!
其實這些規則,主要是因為硬件不允許,沒有建立數據通路,又或者是因為邏輯不合理而禁止某些規則。
邏輯不允許我們還能理解,但是硬件數據通路的建立,這個傷腦筋了,我們力求,通過邏輯理解規則,從而掌握規則。
1.2 通用寄存器
什么叫通用!!就是它能夠干好多事情,它受到的制約比較小!
有些其他寄存器不能直接與內存交流的,它可以來幫忙傳信。
它自己也有功能的劃分,但是不是很明顯,但是我仍然建議你編輯匯編語言的時候,按照其功能使用,除非不夠用,其他的也可以來幫忙。
- AX:累加寄存器,可以用了存儲由運算器得到的數據
- BX:基地址寄存器,可以用來存放基地址
- CX:計數器,載入程序的時候存放機器碼的大小,它也可以干別的事
- DX:數據寄存器,用來存放數據
他們的區別不明顯,允許相互混用,通常,通用寄存器,存儲的是數據,可以代表數值或者符號。
- 數值:1,2,3……
- 符號:中文,韓文……
1.3 段寄存器
段寄存器,就是用來存放段地址的,因此不能隨意改變,根據不同的功能,權限也不一樣。
這里補充一個概念
1.3.1 段寄存器數據的含義
段寄存器中的數據含義:
它是數據,它代表的是某個段的段地址,它并不是代表某一個數值或者符號,因此它不能與通用寄存器進行數值運算
因此你就理解,為什么add sub
指令的操作數不能是段寄存器。
而對于mov
指令,并不是全部的段寄存器都不能作為操作數,因為mov
做的是數據傳送工作,并不是運算工作,因此部分操作允許數據傳送到段寄存器中,以完成某個段的段地址的指定。
我們接下來以CS和DS為例,帶你理解一些指令的規則,更加細節的規則,可以根據本文的思路自行完成。
1.3.2 CS(code segment 代碼段寄存器)
CS是非常重要的,程序的指令執行依靠CS:IP指向的位置,因此CS和IP不能隨意更改。
- IP不能作為任何指令的操作數,它通常是CPU自動修改的,
jmp
指令是專門修改CS:IP的指令,以實現指令的跳轉 (jmp指令的用法請自行查閱資料) - CS的要求相對放寬一點,在
mov
指令中,它可以作為源操作數,也就是可以把它的數據傳出去,但是,不能作為目標操作數,除了專用的jmp
等一類轉移指令(先不用管,知道他們可以修改CS和IP就可以了),別的指令不能修改CS和IP的值。
下面的指令是不允許的
mov cs,ax
下面的指令是允許的
mov ax,cs
想要修改CS:IP需要以下指令
jmp 2000H:0
jmp,全稱jump,有跳轉的意思,也就是指令的跳轉,執行之后:CS = 2000H,IP = 0
總結一下
- cs不能做運算,
add sub
運算等指令中,所有段寄存器都不能作為操作數 - cs可以mov到其他寄存器
mov ax,cs
,也可以mov到內存單元mov [0],cs
,此時,把CS中的內容,當成一般數據,傳到別的地方。 jmp
等轉移指令,可以修改CS:IP
的值,實現指令的跳轉
1.3.3 DS(date segment 數據段寄存器)
DS用來存儲數據段的段地址。
我們知道,在內存單元中找到數據,需要DS:EA
指向它,因此,涉及到存儲器尋址的指令,都需要修改DS的值,并且將EA(有效地址,又叫偏移地址)告訴指令。
下面用debug模式中的指令舉例(而不是以標號代替地址的匯編源程序指令)
; 修改DS的值
mov ax,1000H
mov ds,ax
; 指定EA
mov bx,[0]
以上程序,DS:EA = 1000:0
,bx將會被賦予這個地址指向的內存單元的字型數據。
對于DS,你應該已經很清楚它的功能的,它是幫助CPU,在存儲器中找到數據段內的數據的。
既然它是擅長找東西的,那么修改它的數據也就自然合理,以下mov指令都是允許的
- mov ds,通用寄存器
- mov ds,內存單元
- mov 內存單元,ds (ds當作一般性數據)
舉例如下
mov ds,ax
mov ds,[0]
mov [0],ds
這里,ax可以是其他的通用寄存器,[0]可以是存儲器尋址方式中的其他類型
重點強調,如果想要用立即數修改DS,指令mov ds,1234H
是不允許的,這與8086CPU的硬件實現有關,你可以理解為,沒有相關數據通路能夠立即數直接進入DS中。
需要使用兩條指令來完成mov ds,1234H
想要做的事情
mov ax,1234H
mov ds,ax
這兩條指令會非常常見,就相當于是:直走走不通,只能繞路而行。
其他的內容,以后會逐步更新