汇编语言程序设计
汇编语言程序设计
第二章
1 通用数据寄存器--以AX为例
1.1 最大值:2^16-1
例:在AX中存储18D
18D
-12H
-10010B
1.2 8086为16位寄存器,如何保证程序兼容性
l 将通用寄存器分为两个独立的8位寄存器
l 细分:AX可以分为AH和AL
1.3 “字”在寄存器中的存储
8086 是16位CPU
l 8086的字长为16bit
l 一个字可以存在一个16位寄存器中
2 mov和add指令(汇编语言不区分大小写)
2.1 mov ax,18 将18送入AX中 ax=18
mov ah,78 将78送入AH中 ah=78
add ax,8 将寄存器AX的值加8 ax=ax+8
mov ax,bx 将bx值说送入ax中 ax=bx
add ax,bx 将ax,bx内容相加存入ax ax=ax+bx
2.2 在进行运算时无论是ah,al还是ax只要发生溢出,都会舍弃溢出值保留剩下的值
3 确定物理地址的方法
3.1 cpu访问内存单元时要给出内存单元的地址
3.2 所有的内存单元构成的存储空间是一个一维的线性空间
3.3 每一个内存单元在这个空间中都有唯一的地址,这个唯一的地址称为物理地址
3.4 事实8086有20位地址总线,可传送20位地址,寻址能力为1M(2^16)
3.5 8086是16位结构的CPU
3.5.1 运算器一次最多可以处理16位数据,寄存器的最大运算宽度为16位
3.5.2 在8086内部处理、传输、暂存的地址也是16位,寻址能力只是64K
3.6 解决方案:
3.6.1 用两个16位地址(段地址、偏移地址)和成一个20位的物理地址
3.6.2 地址加法器合成物理地址的方法:物理地址=段地址x16+偏移地址
3.6.3 本质意义
CPU在访问内存时,用一个基础地址(段地址x16)和一个相对于基础地址的偏移地址相加,给出内存单元的物理地址
4 内存分段表示
4.1 用分段的方式管理内存
4.1.1 8086CPU用“(段地址x16)+偏移地址=物理地址”的方式给出内存单元的物理地址
4.1.2 内存并没有分段,段的划分来自于CPU
4.1.3 段地址x16必然是16的倍数,所以一个段的起始地址也一定是16的倍数
4.1.4 偏移地址为16位,16位地址的寻址能力是64K,所以一个段地址的长度为64K。
4.1.5 偏移地址16位,变化范围为0~FFFFH,用偏移地址最大寻址64K。
4.1.6 例:数据在21F60H内存中,段地址是2000H,说法
4.1.6.1 数据存在内存2000:1F60单元中(这里的2000是段地址,1F60是偏移地址)。
4.1.6.2 数据存在内存中2000H段中的1F60H中。
4.1.6.3 段地址很重要
4.1.6.3.1 CS-代码段寄存器 DS-数据段寄存器
SS-栈段寄存器 ES-附加段寄存器
5 Debug调试工具
5.1.1 Debug是DOS系统中的著名的调试程序,也可以运行在windows系统模式下
5.1.2 使用Debug程序,可以观察CPU各种寄存器中内容,内存的情况,并且在机器指令级跟踪程序的运行。
5.1.3 R-查看、改变CPU寄存器的内容
5.1.3.1 R:查看寄存器的内容
5.1.3.2 R 寄存器名:改变指定寄存器内容(空格可加可不加)
5.1.4 T-单步调试,执行机器指令
5.1.4.1 T:执行CS:IP中的指令
5.1.5 D-查看内存中的内容
5.1.5.1 D:列出预设地址内存处的128个字节的内容
5.1.5.2 D 段地址:偏移地址:列出内存中指定地址处的内容
5.1.5.3 D 段地址:偏移地址 结尾偏移地址:列出内存中指定地址范围内的内容
5.1.6 E-改变内存中的内容
5.1.6.1 E 段地址:偏移地址:数据1数据2
5.1.6.2 E段地址:偏移地址
逐个访问式修改
空格:接受,继续
回车:结束
内存中的数据可以是数据,也可以是代码,完全取决于完美自己。
5.1.7 U-将机器指令翻 译成汇编指令
5.1.7.1 U 地址:查看代码
5.1.8 A-以汇编指令的形式在机器中写入机器指令
5.1.8.1 A 地址:写入汇编指令。(一般情况写在CS中,IP是偏移地址)出现错误继续输入就可以了。
5.1.9 Q:退出Debug
5.2 修改CS,IP指令
5.2.1 事实:执行指令取决于CS:IP
5.2.2 应用:可以通过改变CS:IP的内容,控制CPU主要执行的目标指令,但是现在是不通过debug修改的。
5.2.3 指令修改的方法
5.2.3.1 首先mov cs,2000h mov ip,0000H 是不行的。
5.2.3.2 唯一可以改变的是间接修改cs的值,且ip的值是无法间接修改的,也就是说,汇编中是不太支持,程序去修改内存地址的。
Mov ax,2000H 赋值
Mov cs,ax 可以运行
Mov ip,ax 错误
5.2.3.3 所以引用了跳转指令 jmp(转移指令)
5.2.3.3.1 Jmp 段地址:偏移地址
功能:用指令中给出的段地址修改CS,偏移地址修改IP
5.2.3.3.2 仅修改IP的内容
Jmp 某一合法寄存器
Jmp ax(ax中保存的是一个偏移地址,类似于mov ip,ax(只是有相同的意思,但是现实中确是没有的))
6 内存中字的存储
6.1 事实:对于8086CPU,16位为一个字
6.2 问题:16位字的储存在一个16位的寄存器中,如何存储
回答:高8位放在高字节,低8位放在低字节
6.3 问题:16位的字在内存中需要2个连续字节,怎么存放
回答:低字节存放在低8位,高字节存放在高8位
6.4 字单元
6.4.1 字单元:由两个地址连续的内存单元组成,存放一个字型数据(16位)
6.4.2 原理:在一个字单元中,低地址单元存放的是低位字节,高地址单元存放高位字节。
7 DS和[address]实现字节的传送
7.1 要求:CPU在读取一个内存单元的时候,必须先给出这个内存单元的地址
原理:内存地址由段地址和偏移地址组成
解决方案:DS和[address]
用DS寄存器存放要访问的数据的段地址
偏移地址用[…]形式直接访问
7.2 将段地址存入DS两种方式
7.2.1 Mov ds,1000h (错的)
7.2.2 Mov bx,1000h
Mov ds,bx (对的)
不支持直接将数据送入段地址中(硬件的设计问题)
套路:数据->一般寄存器->段寄存器
8 DS与数据段-一般情况下降数据存入DS段地址中
9 栈及栈操作的实现
9.1 栈是一种只能在一端进行插入或删除操作的数据结构
9.2 栈有两个基本操作:出栈和入栈。
入栈:将一个新的元素放到栈顶。
出栈:从栈顶取出一个元素。
9.3 栈顶的元素总是最后入栈,需要出栈时,又最是先被从栈中取出。
栈底就是第一个进栈的数据,栈顶就是最后一个进栈的数据。
9.4 栈的操作规则:LIFO(last in first out,后进先出)
9.5 PUSH(入栈)和POP(出栈)指令
push ax :将ax的数据送入栈中
pop ax :从栈顶取出数据送人ax
以字(16位)为单位对栈进行操作。
9.6 CPU是如何知道一段内存空间被当做栈来使用
栈段寄存器SS 存放栈顶的段地址
栈顶指针寄存器SP 存放栈顶的偏移地址
9.7 执行push和pop的时候,如何知道那个单元时栈顶单元
任意时刻,SS:SP指向栈顶元素
9.8 过程
push ax
(1) SP=SP-2;
(2) 将ax中的内容送入SS:SP指向的内存单元处,SS:SP此时指向新栈顶
pop ax
(1) 将SS:SP指向的内存单元的数据送入ax中
(2) SP=SP+2,SS:SP指向当前栈顶下面的单元,以当前栈顶下面的单元为新的栈顶。
(3) 栈越界的问题
如何能够保证在入栈和出栈时,栈顶不会超过出栈空间。