一个16位寄存器存储一个16位的数据,能保存的数据的最大值为 2 16 − 1 ( F F F F H ) 2^{16}-1(FFFFH) 216−1(FFFFH)
例:在AX中存储18D
12H
10010B
问题 :8086上一代CPU中的寄存器都是8位 的,如何保证程序的兼容性?
例1
看到老师演示windows自带计算器的程序员模式,感觉还挺好玩的
功能很强大
居然还有这个 BYTE WORD DWORD QWORD(四字)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
注意溢出
例2
看清楚 l 和 h ,注意溢出
最后那个158, 1要丢弃
8086CPU的解决方法
地址加法器合成物理地址的方法
物理地址=段地址×16+偏移地址
(段地址左移4位)
老师这里演示的动画非常棒!
哈哈哈,老师举的这个姚明的例子还挺好玩的
段地址×16 必然是 16的倍数,所以一个段的起始地址也一定是16的倍数
偏移地址为16位,16 位地址的寻址能力为 64K,所以一个段的长度最大为64K
例
起始地址(基础地址)为10000H,段地址为1000H,大小为100H
[
段地址取前16位
起始地址(基础地址)为10000H和10080H,段地址为1000H和1008H,大小均为80H
0203 里面已经举过例子
再举个例子 物理地址:21F60H
段地址 | 偏移地址 |
---|---|
2000H | 1F60H |
2100H | 0F60H |
21F0H | 0060H |
21F6H | 0000H |
1F00H | 2F60H |
下面两种说法等价
偏移地址16位,变化范围为0~FFFFH,用偏移地址最多寻址64KB
段地址非常重要
医学中的内窥镜
r
:查看寄存器内容r 寄存器名
(r和寄存器中可以没有空格):改变指定寄存器内容D
列出预设地址内存处的 128个字节的内容D 段地址:偏移地址
列出内存中指定地址处的内容
D 段地址:偏移地址 结尾偏移地址
列出内存中指定地址范 围内的内容
E 段地址:偏移地址 数据1 数据2 ...
E 段地址:偏移地址
逐个询问式修改
空格 - 接受,继续
回车 - 结束
例
有汇编指令
mov ax 0123H
mov bx, 0003H
mov ax, bx
add ax, bx
对应的机器码为
e 地址 数据
- 写入
d 地址
- 查看
u 地址
- 查看代码
神奇捏 可以看作指令 也可以看作数据
有汇编指令
mov ax, 0123H
mov bx, 0003H
mov ax, bx
add ax, bx
对应的机器码为
B8 23 01
BB 03 00
89 D8
01 D8
a 地址 - 写入汇编指令
写入cs:ip处
a 073F:0100
d 地址 - 查看数据
u 地址 - 查看代码
q - 退出Debug
8086CPU当前状态:CS中内容为2000H,IP中内容为0000H
内存20000H~20009H处存放着可执行的机器代码
https://www.bilibili.com/video/BV1pi4y1P76P?p=13&spm_id_from=pageDriver&vd_source=0e8431ba6fd78bb2215c36307a75ac1a
视频 P13 5min处 的演示动画 好棒!
上面那个图
先把CS改为2000, IP改为0
r cs
:2000
r ip
:0
r
用A命令把代码写入 , U命令查看代码
mov ax, 0123H
mov bx, 0003H
mov ax, bx
add ax, bx
t执行cs:ip处的代码
前面的问题, 当指令使用还是当数据使用?
d命令显示的是“狭义”的数据
u显示了“广义”的数据:数据和指令
执行何处的指令,取决于CS:IP
可以通过改变CS、IP中的内容,来控制CPU要执行的目标指令 :
那么如何改变CS、IP的值?
方法1:Debug 中的 R 命令可以改变寄存器的值——rcs, rip
方法2:用指令修改
可以同时修改cs、ip的内容
jmp 段地址:偏移地址
jmp 2AE3:3
jmp 3:0B16
功能:用指令中给出的段地址修改CS,偏移地址修改IP
也可以仅修改ip的内容
jmp某一合法寄存器
jmp ax
jmp bx
(类似于mov IP,ax,但是注意mov IP,ax是不允许的!)
从20000H开始,执行的指令序列是
mov ax,6622H
jmp 1000:3
mov ax,0000
mov bx,ax
jmp bx ;修改IP IP变为0
mov ax,0123H
;转到第(3)步mov ax,0000执行 开始循环
环境准备
执行指令
对8086CPU,16位作为一个字
例:20000D(4E20H)存放0、1两个单元,18D (0012H)存放在2、3两个单元
书中一般是最左边那个,
注意描述方式 0012H的起始地址是2 4E20H的起始地址是0
字单元:由两个地址连续的内存单元组成,存放一个字型数据(16位)
框出来的方式都可以组成一个字单元
原理:在一个字单元中,低地址单元存放低位字节,高地址单元存放高位字节 ; 在起始地址为0的单元中,存放的是4E20H ; 在起始地址为2的单元中,存放的是001
例1 将10000H(1000:0)中的数据读到al中
mov bx,1000H
mov ds,bx
mov al,[0] ;[0]这种方式给出,段地址默认在DS
例2 将al中的数据写到10000H(1000:0)中
mov bx,1000H
mov ds,bx
mov [0],al
cs:ip是执行,ds:[address] 是拿数据。
8086CPU可以一次性传送一个字(16位的数据)
例
mov bx, 1000H
mov ds, bx
mov ax, [0] ;1000:0处的字型数据送入ax
mov [0], cx ;cx中的16位数据送到1000
准备数据和指令
写入之后
执行指令
对于8086PC机,可以根据需要将一组内存单元定义为一个段
例:用123B0H~123B9H的空间来存放数据
段地址:123BH 起始偏移地址:0000H 长度:10字节
段地址:1230H 起始偏移地址:00B0H 长度:10字节
and so on …
将哪段内存当作数据段,段地址如何定,在编程时安排
处理方法:(DS)[address])
例:累加数据段中的前3个单元中的数据
mov ax, 123BH
mov ds, ax
mov al,0 ;al清零
add al,[0]
add al,[1]
add al,[2]
例:累加数据段中的前3个字型数据
mov ax, 123BH
mov ds, ax
mov ax, 0
add ax,[0]
add ax,[2]
add ax,[4]
指令形式 | 示例 |
---|---|
mov 寄存器,数据 | mov ax, 8 |
mov 寄存器,寄存器 | mov ax, bx |
mov 寄存器,内存单元 | mov ax, [0] |
mov 内存单元,寄存器 | mov [0], ax |
mov 段寄存器,寄存器 | mov ds, ax |
学习方法:大胆假设,小心求证
推测1~3 可以,推测4不行
段寄存器不能参与add运算
两个内存单元也不能直接相加
mov ax,1000H
mov ds,ax
mov ax,11316
mov [0],ax
mov bx,[0]
sub bx,[2]
mov [2],bx
字在内存中存储时,要用两个地址连续的内存单元来存放,字的低位字节存放在低地址单元中,高位字节存放在高地址单元中
用mov指令要访问内存单元,可以在mov指令中只给出单元的偏移地址,此时,段地址默认在DS寄存器中
[address]表示一个偏移地址为address的内存单元
在内存和寄存器之间传送字型数据时,高地址单元和高8位寄存器、低地址单元和低8位寄存器相对应
mov、add、sub是具有两个操作对象的指令,访问内存中的数据段
(对照:jmp是具有一个操作对象的指令,对应内存中的代码段)
可以根据自己的推测,在Debug中实验指令的新格式
视频里很棒的动画演示
没有问题,栈(地址)增长方向和内存单元地址增长方向相反,且8086为小端存储,低位放到低地址
However,CPU如何知道一段内存空间被当作栈使用的捏?
moreover,执行push和pop的时候,如何知道哪个单元是栈顶单元?
栈段寄存器SS:存放栈顶的段地址
栈顶指针寄存器SP:存放栈顶的偏移地址
任意时刻,SS:SP指向栈顶指针
push ax 时发生了啥
这里一开始有个疑惑,可能是来自于最早开始学栈的时候,拿一个框子举例子,先放进去一本书,再放进去一本书,书越堆越高,让我会有种误解,越往上去地址越高,
但是栈地址增加的方向和内存单元地址增长方向是相反滴! 记住这一点就ok啦
push 就是栈地址增加,那么就是内存地址减少 ,所以SP是减
pop ax时又发生了啥
pop是栈地址减少,那么就是内存地址增加,所以SP是加
实现了AX和BX的数据交换
push、pop 实质上就是一种内存传送指令,可以在寄存器和内存 之间传送数据,与mov指令不同的是,push和pop指令访问的内 存单元的地址不是在指令中给出的,而是由SS:SP指出的。
执行push和pop指令时,SP 中的内容自动改变。
8086CPU提供的栈操作机制:
编程时,可以根据需要将一组内存单元定义为一个段。
可以将起始地址为16的倍数,长度为 N(N ≤64K )的一组地址连续的内存单元,定义为一个段。
将一段内存定义为一个段,用一个段地址指示段,用偏移地址访问段内的单元——在程序中可以完全由程序员安排
mov bx,1000H
mov ds,bx
mov bx,1001H
mov ss,ax
mov sp,10H
mov ax,[0]
mov bx,[2]
push ax
push bx
pop ax
pop bx
mov [0],ax
mov [2],bx
mov bx,1000H
mov ds,bx
mov bx,1001H
mov ss,ax
mov sp,10H
mov ax,[0]
mov bx,[2]
push ax
push bx
pop ax
pop bx
mov [0],ax
mov [2],bx
写入指令
执行指令