0 写在前面
为了更深入的了解程序的实现原理,近期我学习了IBM-PC相关原理,并手工编写了一些x86汇编程序。
在2017年的计算机组成原理中,曾对MIPS体系结构及其汇编语言有过一定的了解,考虑到x86体系结构在目前的广泛应用,我通过两个月左右的时间对x86的相关内容进行了学习。
在《x86汇编语言实践》系列中(包括本篇、x86汇编语言实践(1)、x86汇编语言实践(2)、x86汇编语言实践(3)以及x86汇编语言实践(4)),我通过几个具体案例对x86汇编语言进行实践操作,并记录了自己再编写汇编代码中遇到的困难和心得体会,与各位学习x86汇编的朋友共同分享。
我将我编写的一些汇编代码放到了github上,感兴趣的朋友可以点击屏幕左上角的小猫咪进入我的github,或请点击这里下载源代码。
这是《x86汇编语言实践》系列的最后一篇文章,后天就要迎来x86汇编的期末考试了,希望所有朋友们以及先先能够考试顺利!
1 基础知识
1.Intel 8086/8088PC机的CPU字长为16位,16位的信息称为1个字,内存的基本单元为1个字节,但任何相邻两个单元都可以组成1个字。Intel 8086/8088PC机共有20根地址线,其寻址范围为00000H~FFFFFH。
2.用于间接寻址的寄存器有BX,SP,BP,SI,DI,其中,BX一般用于存放基址;在采用基址变址寻址时,采用SI寄存器,基址寻址默认的段是DS段(DS:[SI]);采用DI寄存器,基址寻址默认的段是ES段(ES:[DI]);采用BP寄存器,基址寻址默认的段是SS段(堆栈的位置和大小是由SP和SS共同决定的)。
3.串操作指令如MOVSB,STOSB,LODSB,SCASB,CMPSB,MOVSW,LODSW,STOSW,SCASW,CMPSW等,源操作数对应的地址是DS:[SI],目的操作数对应的地址是ES:[DI]。
4.Intel8086/8088CPU共有9个1为的标志寄存器(标志位),为了便于CPU的加工,他们被组合在一起形成一个16位的程序状态字寄存器PSW中。几个比较重要的标志位有:
- ZF:当运算结果为0时,ZF=1,否则ZF=0
- SF:运算结果为负时,SF=1,否则SF=0
- CF:算术运算最高位产生进位,CF=1.否则CF=0;还用于移位指令保存最高位左移或最低位右移移出的代码。
- DF:DF=1时每次串操作SI和DI减1,DF=0时每次串操作SI和DI加1。使用CLD可以将DF清零,即规定为正向操作字符串。
- TF:TF=1时执行完一条产生单步中断,中断处理程序将TF置0。TF标志用于调试。
- PF,AF,IF,OF这里我斗胆预测一啵,不考(因为真的没有使用过)。
5.STD是将DF置1的指令,与CLD将DF清零的效果相反。使用STD后,串指令对应的DI,SI寄存器每次操作后根据是SB还是SW操作自动减少1或2
6.逻辑地址向物理地址的转化(书上P24)。地址转化的动机:20位物理地址无法直接在16位字长的机器中直接运算,因此可以采用Intel的分段方法将其划分为16位段地址和16位段内地址(也称为偏移地址)逻辑地址的基本形式为0000H:0000H,该逻辑地址表示物理地址的00000H。
那么逻辑地址向物理地址的转换方式可以表述为以下公式:段地址x10H + 偏移地址 = 物理地址
举例说明:逻辑地址1234H:5678H转换为物理地址为:1234Hx10H + 5678H = 12340H + 5678H = 179B8H。再如:1234H:2001H = 12340H + 2001H = 14341H
7.几个重要的数据传送指令PUSH,POP,PUSHF,POPF
- PUSH SRC:先SP = SP - 2 再 SS:[SP] <- SRC
- PUSHF :先SP = SP - 2 再 SS:[SP] <- PSW
- POP SRC:先SP = SP + 2 再 SRC <- SS:[SP]
- POPF :先SP = SP + 2 再 PSW <- SS:[SP]
总结而言,PUSH和POP是将操作数压(弹)栈,POPF和PUSHF是将PSW标志寄存器压(弹)栈
2 寻址方式
2-1 六种与数据有关的寻址方式
2-1-1 立即寻址
直接将立即数写到指令中的寻址方式。注意不得超出寄存器的字节范围:AL8位,AX16位。
【例】
- AND AX,0FFFEH
- MOV AL,100H
- MOV AL,00000101B
- MOV AX,512
【不能使用】
- MOV AL,100H
- MOV AX,10000H (超出了字节范围)
2-1-2 寄存器寻址
使用寄存器的寻址方式。可以显示使用,也可以隐式使用。也可以使用段寄存器CS,DS,SS,ES。
【例】
- MOV DS,AX
- PUSH DS
- PUSHF (隐式操作PSW)
- STD (隐式操作PSW)
2-1-3 直接寻址
直接使用操作数的偏移地址进行寻址的方式,偏移地址用[立即数]的形式表示,或者直接用数据段中定义的变量名表示,或用数据段中定义的变量名+立即数的形式表示。
【例】
- AND AX,[0FFFEH]
- MOV AX,X ;其中X为数据段中定义好的数据
- MOV AX,STR+1 ;其中STR为数据段中定义好的数据,STR+1直接寻址到STR下一个字节单元的内容
2-1-4 寄存器间接寻址
使用寄存器中存储的偏移地址进行寻址。注意只能使用寻址寄存器BX,BP,SI,DI进行寻址,而不能用DX等进行寻址。此外,寻址的地址必须为16位,即不能使用BL等进行寻址。
【例】
- MOV AX.[BX]
- MOV BH,[BP]
- MOV CX,[SI]
- MOV DL,[DI]
以上四条指令等价于
- MOV AX.DS:[BX]
- MOV BH,SS:[BP]
- MOV CX,DS:[SI]
- MOV DL,ES:[DI]
但是在每条指令前加上一个段超越的段名,既麻烦又没必要,因此通常都默认缺省为上述隐含段规则。
【不能使用】
- MOV AX,[DX] (不能用DX)
- MOV DL,[BL] (必须为16位寻址)
2-1-5 寄存器相对寻址
在寄存器间接寻址的基础上,再增加一个常偏移量。形式多变,大致有如下几种
【例】
- MOV AX,[BX+100]
- MOV AX,[SI+10H] <==> MOV AX,10H[SI]
- MOV AX,ARRAY[SI]
- MOV TABLE[DI],AL
- MOV TABLE[DI+1],AL 3~5展示了立即数也可以是数据段中定义好的变量名
最终在debug下所有的寻址有效地址会被计算成[DI+XXXX]的形式,XXXX是一个十六进制数。
2-1-6 基址变址寻址
即基址加变址寻址方式,基址采用BX,BP寻址,变址采用DI,SI寻址,寻址规则相对固定。
【例】
- MOV AX,[BX][SI]
- MOV AX,[BX+SI]
- MOV ES:[BX+SI],AL
- MOV [BP+DI],AX
- MOV AX,[BX+SI+200]
- MOV ARRAY[BP+SI],AX
其中,段取决于基址寄存器,如BX的段就默认为DS;BP缺省为SS。当然,有指定段的情况除外。也可以在两个寄存器加和的基础上再增加一个立即数。
【不能使用】
- MOV [BX+CX],AX (CX不能做变址寄存器)
- MOV [BX+BP],AX (BP不能做变址寄存器)
- MOV [BX+DI],ARRAY (两个全在内存中的操作数,不符合语法)