0%

《汇编语言》(第四版) 实验 4

《汇编语言(第四版)》 . 王爽著 . 清华大学出版社 . 2019

实验 4 [bx] 和 loop 的使用

考虑一个问题,将内存 ffff:0 ~ ffff:b 单元中的数据复制到 0:200 ~ 0:20b 单元中

注意到,0:200 ~ 0:20b 单元等同于 0020:0 ~ 0020:b 单元,它们描述的是同一段内存空间,这样的等同可以使目标单元的偏移地址和原始单元的偏移地址从同一数值 0 开始

思路是,将偏移地址作为循环变量,在每一次循环中将段地址为 ffff 的内存单元中的数据先送入寄存器,再转移到段地址为 0020 的内存单元中(这里需要寄存器作为中转)

注意,在汇编源程序中,数据不能以字母开头,mov ax,0ffffh 不能省去 0ffffh 前面的 0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
assume cs:code

code segment

mov bx,0 ;(bx)=0, 偏移地址从 0 开始
mov cx,12 ;(cx)=12, 循环 12 次

s: mov ax,0ffffh
mov ds,ax ;(ds)=0ffffh
mov dl,[bx] ;(dl)=((ds)*16+(bx)), 将 ffff:bx 中的数据送入 dl

mov ax,0020h
mov ds,ax ;(ds)=0020h
mov [bx],dl ;((ds)*16+(bx))=(dl), 将 dl 中的数据送入 0020:bx

inc bx ;(bx)=(bx)+1
loop s

mov ax,4c00h
int 21h

code ends

end

可以看到,上面的程序在循环中要设置两次段寄存器 ds,但其实并不需要这样做,我们还有 es,16位的扩展段寄存器,可以用 es 存放目标空间的段地址,用 ds 存放原始空间的段地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
assume cs:code

code segment

mov ax,0ffffH
mov ds,ax ;(ds)=0ffffh

mov ax,0020H
mov es,ax ;(es)=0020h

mov bx,0 ;(bx)=0, 此时 ds:bx 指向 ffff:0, es:bx 指向 0020:0

mov cx,12 ;(cx)=12, 循环 12 次

s: mov dl,[bx] ;(dl)=((ds)*16+(bx)), 将 ffff 中的数据送入 dl
mov es:[bx],dl ;((es)*16+(bx))=(dl), 将 dl 中的数据送入 0020:bx
inc bx ;(bx)=(bx)+1
loop s

mov ax,4c00H
int 21H

code ends

end

注意,mov dl,[bx] 是隐式地使用 ds 寄存器中的数据作为段地址,而 mov es:[bx],dl 是显式地用段前缀 es: 给出单元的段地址

实验任务(1)(2)

编程,向内存 0:200 ~ 0:23F 依次传送数据 0 ~ 63(3FH),程序中只能使用 9 条指令,9 条指令中包括 mov ax,4c00hint 21h

思路很简单,只需将要传送的数据当作循环变量,在循环中把寄存器的值送入内存,然后自增即可

编辑程序

程序运行前内存状态

程序运行后内存状态

实验任务(3)

补全程序,其功能为,将 mov ax,4c00h 之前的指令复制到内存 0:200

指令在哪儿呢,我们好像不知道,但我们知道,在运行程序时,CPU 将 CS:IP 指向的内容当作指令执行,换一个说法,程序中 code 段(code segment)的段地址其实可以在 cs 中取到(这里未标明程序入口 start:,程序顺序地运行),我们可以猜测,程序第一条指令的地址应该就是 cs:0

那么循环的次数如何得知呢,即,mov ax,4c00h 之前的指令的长度是多少,这个当然可以用 Debug 程序测试出来,也可以通过我们编写程序的经验推算出来

补全后的程序如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
assume cs:code

code segment

mov ax,cs
mov ds,ax

mov ax,0020h
mov es,ax

mov bx,0
mov cx,17h

s: mov al,[bx]
mov es:[bx],al
inc bx
loop s

mov ax,4c00h
int 21h

code ends

end

用 Debug 调试工具跟踪程序的运行,使用 R 命令找到 CS:IP 指向的地址,再使用 U 命令查看从该地址开始的机器码转换成的汇编指令,可以看到,CS:IP 指向 code segment 中的第一条指令 mov ax,cs,并且得知从该指令到 mov ax,4c00h 之前的指令占 23(0x17h) 个字节,即 cx 应设为 17h


程序运行后的结果,成功地将指令复制到了内存 0:200


这里可以补充一点 8086 指令集编码的知识

参考:
8086指令的机器码编码格式
8086 汇编和机器码的对应表
8086指令的机器码表示
探索机器码(8086)

8086 指令编码的特点是为每种基本指令类型给出一个编码格式,对照格式填上不同数字表示不同的寻址方式与数据类型,便可得出每条指令的机器码(也就是说,每一条指令对应唯一的机器码,这唯一的机器码应该指明该指令的所有有效信息)

8086 指令采用变长指令,每条指令可由 1~6 个字节组成

以程序的第一条指令为例,这条指令很简单,是将段寄存器 cs 中的值送入数据寄存器 ax 中去

8CC8      mov ax,cs

对应机器码:1000 1100 1100 1000

因为 MOVsr(sr 代表段寄存器 segment register) 对应的机器码为 100011d0,其中 d 这一位表示的是数据传送的方向,那么 8C(d0) 即代表以一个段寄存器为源操作数,一个数据寄存器为目标操作数,传送一个字的数据

后一个字节 C8 中,前两位代表 mod(寻址方式),11 即说明该指令采用寄存器寻址,没有位移量,后六位分别指 cs 段寄存器(001)与 ax 数据寄存器(000

这里有一个问题,即 cx 数据寄存器的机器码与 cs 段寄存器的机器码都是 001,难道不会产生歧义吗?答案当然是不会的,因为在解析前面的机器码时,就已经知道这后面几位代表的是数据寄存器还是段寄存器了

如果指令为 mov ax,cx
那么对应的机器码应为 89C8:1000 1001 1100 1000

附 MOV 指令的 8086CPU 手册指令集图示,仅供参考


汇编语言实验合集

汇编语言实验合集

实验 1 查看 CPU 和内存,用机器指令和汇编指令编程
实验 2 用机器指令和汇编指令编程
实验 3 编程、编译、连接、跟踪
实验 4 [bx] 和 loop 的使用
实验 5 编写、调试具有多个段的程序
实验 6 实践课程中的程序
实验 7 寻址方式在结构化数据访问中的应用
实验 8 分析一个奇怪的程序
实验 9 根据材料编程
实验 10 编写子程序
课程设计 1
实验 11 编写子程序
实验 12 编写 0 号中断的处理程序
实验 13 编写、应用中断例程
实验 14 访问 CMOS RAM
实验 15 安装新的 int9 中断例程
实验 16 编写包含多个功能子程序的中断例程
实验 17 编写包含多个功能子程序的中断例程
课程设计 2