实验4 [bx]和loop的使用
1.编程:向内存0:200H~0:23fH依次传送数据0~63(3FH)
程序分析:
【1】内存0:200H~0:23fH空间与0020:0-0020:3f内存空间是一样的,(这个不会?oh!My God!,物理地址是唯一的,但逻辑地址组合是多种的。)
【2】因为偏移地址是连续内存单元;我们可以把偏移地址做下文章。bx寄存器存储偏移地址(通过偏移地址的间接访问内存单元,这主要是写入的内存单元)。dx寄存器作为存储中间变量的容器(源数据,常量0-63)来向内存写入。
汇编代码如下:
assume cs:code
code segment
mov ax,0020H
mov ds,ax ;内存单元的段地址写入ds寄存器
mov bx,0 ;bx寄存器存放偏移地址,初始化为0
mov dx,0 ;dx寄存器存储常量数值0~63
mov cx,40H ;这里40H==64,cx寄存器存放循环次数。也可以为64;
s: mov [bx],dx ;向[bx]内存单元写入dx值
inc bx ;累加bx
inc dx ;累加dx
loop s
mov ax,4c00H
int 21H
code ends
end
2. 向内存0:200H~0:23fH依次传送数据0~63(3FH),9条命令的程序的简化版本(不包括伪代码):
程序分析:
【1】内存0:200H~0:23fH空间与0020:0-0020:3f内存空间是一样的,(这个不会?oh!My God!,物理地址是唯一的,但逻辑地址组合是多种的。)为什么这样?数据0-63是64个连续的数字,0-3fH也是连续的64个编号。我们可以使用一个bx变量就把偏移地址和数字的递增都搞定了!
修改后的汇编代码如下:
assume cs:code
code segment
mov ax,0020H
mov ds,ax ;ds指向0020内存段
mov bx,0 ;bx寄存器存放偏移地址,初始化为0,也当做源数据:常量数值
mov cx,64 ;循环次数64
s: mov [bx],bx ; 向[bx]内存单元写入bx数值
inc bx
loop s
int 21H
code ends
end
实验体会:
1. bx寄存器一般用作偏移地址的存储,[bx]也就代表了段地址与[bx]组合后指向的内存单元。 为什么这样,还有一个原因就是在源程序中[xxxx]在masm编译器中当做了常量xxxx,而[bx]则没有这个问题,编译器当做是一个内存地址段的偏移地址。
2.cx寄存器在循环语句中,常作为计数器使用,loop循环语句把cx寄存器中的值作为判断是否循环的依据。
CX寄存器在debug调试一个可执行程序时,CX的初始值为该程序的字节尺寸大小。
3.ds寄存器存放的是指向的内存单元的段地址。
4.在debug模式下,注意源程序中loop s语句的体现,直接转移到了一个地址(偏移地址)了。
5.结束debug程序,键入q,退回到dos或命令提示符状态。
6.在debug状态下,如果想要直接执行的到某个语句,可使用g命令,例如:
g XXXX(偏移地址) 在此之前的命令都执行了。
7.在汇编源程序中,数据不能以字母开头,前面可以加0,例如:0ff02H。
8.在debug下调试程序,遇到int 21H命令,键入p命令结束程序。
9.使用p命令可以结束loop的循环(当你遇到了loop语句的时候)。
10.在写汇编程序时,十进制和十六进制(用h、H标注)可以混用。编译器自动给你转换了。
程序运行后结果:
-d ds:0
0020:0000 00 01 02 03 04 05 06 07-08 09 0A 0B 0C 0D 0E 0F ................
0020:0010 10 11 12 13 14 15 16 17-18 19 1A 1B 1C 1D 1E 1F ................
0020:0020 20 21 22 23 24 25 26 27-28 29 2A 2B 2C 2D 2E 2F !"#$%&'()*+,-./
0020:0030 30 31 32 33 34 35 36 37-38 39 3A 3B 3C 3D 3E 3F 0123456789:;<=>?
3.下面程序的功能是将mov ax,4c00H之前的指令复制到内存0:200处,补全程序,上机调试,跟踪运行结果:
程序分析:
【1】使用debug调试一个EXE文件时候,使用r命令查看寄存器状态,其中cx寄存器的值(初始值)就是该程序代码的大小(按照字节数)。我们可以通过运行debug程序来调试生成的EXE文件,前提你先将CX寄存器赋个值。
侧面验证CX寄存器的另一个作用。
【2】cs段寄存器中存储的是指向程序代码段的段地址。此实验是将程序的代码(按字节)复制,故将cs寄存器中的指向代码的段地址赋值给ax,再通过ax寄存器赋值给ds段寄存器。(为什么不能支持从段寄存器cs直接赋值给段寄存器ds呢?回忆下,在8086CPU中,ds、ss、cs、es四个段寄存器存放的都是段地址,在CPU和我们来看。其他的寄存器一般存放的都是数据。
这4个段寄存器支持从其他寄存器中赋值,但不允许立即数直接赋值给段寄存器。)
【3】[bx]作为偏移地址为bx的内存单元,它支持的段地址默认是存储在ds段寄存器中的。 本例中ds:[bx]指向的是存储代码段的内存单元(源内存段)。由于ds被占用了,故被写入的内存单元的段地址就没有存储的段寄存器了,es寄存器上场了,es存储了地址为0020H的段地址(目标内存段),那么同样使用[bx]偏移地址的话,必须明确的指出它的前缀,故es:[bx]就指向了内存是0200H的内存单元地址段。
实验步骤如下:
(1)首先使用debug调试该程序:假如这个可执行程序(经编译、连接无误后的)为test5.exe
debug test5.exe
(2)使用r命令显示寄存器状态,显示整个程序代码所占字节数。
-r
AX=0000 BX=0000 CX=001C DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B55 ES=0B55 SS=0B65 CS=0B65 IP=0000 NV UP EI PL NZ NA PO NC
0B65:0000 8CC8 MOV AX,CS
这里我们发现CX= 001CH。
(3)使用u命令显示汇编指令,求出需要复制的机器码字节数。
-u cs:0000
0B65:0000 8CC8 MOV AX,CS
0B65:0002 8ED8 MOV DS,AX
0B65:0004 B82000 MOV AX,0020
0B65:0007 8EC0 MOV ES,AX
0B65:0009 BB0000 MOV BX,0000
0B65:000C B90300 MOV CX,0003
0B65:000F 8A07 MOV AL,[BX]
0B65:0011 26 ES:
0B65:0012 8807 MOV [BX],AL
0B65:0014 43 INC BX
0B65:0015 E2F8 LOOP 000F
0B65:0017 B8004C MOV AX,4C00
0B65:001A CD21 INT 21
我们发现mov ax,4cooH/int 21H它们共占用了5个字节。所以在本实验中我们需要复制的代码字节数是001CH-0005H=0017H==23个字节,故cx计数寄存器赋值为23或17H。
(4)完整的汇编代码如下:
assume cs:code
code segment
mov ax,cs ;将cs段地址赋值给ax
mov ds,ax ;用cs寄存器中的值初始化ds段寄存器,
mov ax,0020H
mov es,ax ;es指向0020H内存段
mov bx,0 ;偏移地址寄存器清零
mov cx,17H ;此处是循环次数:程序机器码的字节数,存储在CX中
s: mov al,[bx] ;将[bx]按照字节单元传送给al
mov es:[bx],al ;复制到es段内存中
inc bx
loop s
mov ax,4c00H
int 21H
code ends
end
实验结果测试:
-d 20:0
0020:0000 8C C8 8E D8 B8 20 00 8E-C0 BB 00 00 B9 17 00 8A ..... ..........
0020:0010 07 26 88 07 43 E2 F8 00-00 00 00 00 00 00 00 00 .&..C...........
……
我们发现偏移地址从0000H~0017H存储了程序的执行代码。与程序执行代码存储的内存单元比较,我们发现一样的。
-d cs:0
0B65:0000 8C C8 8E D8 B8 20 00 8E-C0 BB 00 00 B9 17 00 8A ..... ..........
0B65:0010 07 26 88 07 43 E2 F8 B8-00 4C CD 21 FF 06 48 91 .&..C....L.!..H.
原创:筑基2017