文章目录
课程地址
确定物理地址的方法
对8086而言
物理地址 = 段地址*16+偏移地址
*16
=在二进制下左移4位
=在十六进制下左移一位。
1230 + 00C8 = 123C8 ⇒ \Rightarrow ⇒地址总线参与寻址。
用两个16位的地址相加得到20位的地址。
用一个基础地址和一个相对于基础地址的偏移地址相加,给出物理单元的内存地址。
内存的分段表示法
内存本身不分段,段的划分来自于CPU
1.一个段的起始地址一定是16的倍数
2.偏移地址为16位,16位的寻址能力为64K,故一个段的最大长度为64K
16位:
2
16
2^{16}
216
1K = 1024 =
2
10
2^{10}
210
16位 =
2
6
2^6
26K = 64K
综上,16位可以表示64K这么多的内存位置。
如果一块内存单元的大小用1B表示,则16位可表示64KB。
例如:数据在21F60内存单元中,段地址为2000H
(a)数据存在2000:1F60单元中
(b)数据存在内存的2000段的1F60H单元中
CPU有专门的寄存器存储段地址:
CS 代码段
DS 数据段
SS 栈段
ES 附加段
CS和IP与代码段
CS:代码段
IP:指令指针
CS:IP把指向的内存单元中的内容看作指令。
jmp指令
Debug中的rcs和rip可以改变cs和ip的值。但这是调试手段,而非程序方式。
mov cs 2000H
mov ip 0000H
上面这种方式是不正确的。不能给CS直接赋值立即数。
mov ax 2000
mov cs ax
mov ip ax
mov cs ax
可能不会报错,但不符合要求。而对ip的更改会直接报错。
jmp 2AE3:3
用指令里给出的地址对IP和CS进行修改。
jmp ax
上面的语句仅仅修改IP的值。
内存中字的存储
16位的字存储在一个16的寄存器
高8位高字节,低8位低字节。
低位字节放在低地址单元,高位字节放在高地址单元。
注意字节型和字型数据的区别。
CPU数据传送
mov bx,1000H
mov ds,bx
mov al,[0]
段地址送入DS中
1.mov ds,1000H
2.mov bx,1000H
mov ds,bx
第一种方式错误,第二种正确。
数据
⇒
\Rightarrow
⇒一般寄存器
⇒
\Rightarrow
⇒段寄存器
move ax,[0]以字型数据传送:ax是16位的,2个字节,是字型数据。
上面的代码可以实现寄存器和内存的双向传送。
DS和数据段
mov 段寄存器,寄存器
上面的指令是合法的
mov ds ax
推测4不正确,其余均正确。不能直接把数据赋值为段寄存器。
add 内存单元 内存单元
上面的指令不合法。
栈和栈操作
注意AX的值被改变。
SS和SP:
SS存栈顶段地址
SP存放栈顶偏移地址
SS:SP指向栈顶元素
这里AX和BX的数据发生了交换。
push和pop的执行过程如下:
关于段的总结
三种段的定义:
[…]和(…)的区别
(ax) = 0010H ax中的内容为0010H。
(21000) = 0010H 括号中只能用物理内存地址。
执行mov ax,[bx]时,ax = 00BE
inc指令即为bx++
上面的指令最终情况:
注意21005,21006改变为BE
Loop指令
CX是默认使用的寄存器。
注意mov ah,0
这样可以避免al中原来存在的数据产生的影响,同时合成ax
注意0fffh
8086指令中要求不能以字母开头,所以加上0
段前缀
改进:引入附加段地址寄存器
在代码段中使用数据
定义code segment:在代码段中定义数据
dw-define word 定义字型数据
上面这个程序仍有问题!
IP过去是0,但现在第一行是数据,而不是代码。
让数据从CS:0000开始,让代码从CS:0010开始。
代码改进:加入start
start即为指令开始的地方。
代码段中使用栈
注意pop cs:[bx]
会把栈顶数据弹入cs:[bx]
上面的方法是设置0来开辟栈空间。定义SP等即可。
数据、代码、栈放入不同段
assume cs:code,ds:data,ss:stack
data segment
data ends
stack segment
stack ends
code segment
start
初始化各段寄存器
入栈
出栈
CS不用用寄存器赋值,程序会自动将其赋值为code
DS和SS要用寄存器赋值。
处理字符问题
‘x’,会转化为ASCII码值。
小写字母比大写字母大20H
db 定义字节的方式。
用&&(与运算)来处理大小写问题。
mov al,[bx]这里取出的是字节。
一个是and运算,一个是or运算。
[bx+idata]的含义
注意下面的几种写法:
关键之处在于mov al,[5+bx]
变址寄存器
和BX功能相似。也有不同。
SI和DI不能够分成两个8位的寄存器来用。
SI 源地址
DI 目标地址
[bx+si]和[bx+di]方式
即基址变址寻址方式
[bx+si+idata]和[bx+di+idata]方式
不同寻址方式总结
举例1:[bx_idata]寄存器相对寻址
举例2:用[bx+si]方式-基址变址寻址。
上面的程序对CX在内层更改,外层会被破坏。
使用div指令
8位:高位放余数,低位放商。
16位:AX和DX
如果除数是16位的,那就要用两个寄存器凑出32位的寄存器。
流程转移与子程序
jmp可以控制CPU执行内存中某处代码的指令。
可以修改IP,或同时修改CS和IP的指令。
段内转移和段间转移。
操作符offset
取得标号的偏移地址。
jmp指令
机器代码里包含偏移地址。
下一条指令是05,加上偏移地址03,得08;
变化方式如上。
注意是IP先加1后,再+05
近转移和远转移。
注意来两种方式:给出目的地址/给出位移
、
内存中的jmp:
注意dword是因为要给出两个字。
注意高地址是段地址,低地址是偏移地址。
小结:
debug可以直接针对内存进行调试。
其他转移指令
注意保存的是位移。
04+C = 10
mul指令
示例:
注意8位乘法被乘数放在AL中。
mul ?
=>AX
标志寄存器
ZF:判断运算后结果是否为0(Zero)
SF:判断运算后结果是否为负数
CF:进位标志
OF:溢出标志
带符号加法(ADC和SBB)
SBB指令
先做低位,再做高位。
cmp指令
下面的ppt中的小于等于改为小于,ax,bx改为ah,bh;
如果OF=1,说明发生了溢出,则实际情况与当前SF所指示的情况相反。(注意OF1,SF1时是大于,因为有溢出说明不可能为0)
跳转指令:
DF和串传送指令
示例:
使用rep指令