1.[bx], 和 内存单元的描述
- 完整的描述一个内存单元需要两种信息:
(1)内存单元地址
(2)内存单元的长度(类型)
[0] 表示内存单元,它的偏移地址是 0, 段地址在 ds 中,单元的长度(类型)可以由具体指令中的其他操作对象(比如说寄存器)指出
[bx] 也表示内存单元,它的偏移地址在 bx 中,段地址在 ds 中,单元的长度(类型)可以由具体指令中的其他操作对象(比如说寄存器)指出
loop
循环指令描述性的符号 “()”
(ax)表示ax中的内容
(al)表示al中的内容
(20000H)表示内存20000H单元的内容,其中20000H为内存单元的物理地址
- “()” 中的元素可以由3中类型:
(1)寄存器名
(2)段寄存器名
(3)内存单元的物理地址(一个20位数据)
(ax),(ds),(al),(cx),(2000H),((ds)*16 + (bx))等都是正确的用法。
(2000:0),((ds):1000H) 是不正确的用法。
(X)的应用:
(1)ax 中的内容为 0010H => (ax)=0010H
(2)2000:1000H 中的内容为 0010H => (21000H)=0010H
(3)mov ax,[2] => (ax) = ((ds)*16+2)
(4)mov [2],(ax) => ((ds)*16+2) = (ax)
(5)add ax,2 => (ax)=(ax)+2
(6)add ax,bx => (ax)=(ax)+(bx)
(7)push ax =>
(sp)=(sp)-2
((ss)*16+(sp))=(ax)
(8)pop ax =>
(ax)=((ss)*16+(sp))
(sp)=(sp)+2
(9)“(x)” 所表示的数据有两种各类型: 字节, 字。
哪种类型有寄存器名或具体的运算决定。比如:
(al)(bl)(cl)等得到的数据为字节型
(ax)(bx)(cx)等得到的数据为字节型
(al)=(20000H),则(20000H)得到的数据为字节型
(ax)=(20000H),则(20000H)得到的数据为字型
5.1 [BX]
mov ax,[bx] => (ax) = ((ds)*16+(bx))
mov [bx],ax => ((ds)*16+(bx)) = (ax)
- inc bx 的含义是 bx的内容 加1
mov bx,1
inc bx
// 执行后,(bx) = 2
5.2 Loop 指令
(1)格式: loop 标号
CPU在执行loop 的时候要进行两部操作:
(1)(cx)=(cx)-1
(2)判断 cx 中的值,不为0则转至 标号 处执行程序,如果为 0 则乡下执行。
注意:
cx 中的值影响着 loop 指令的执行结果,通常(注意,说的是通常)我们用loop指令来实现循环功能,cx中存放循环次数。
- example1: 编程计算 2^11,结果存在 ax 中
assume cs:code
code segment
mov ax,2
.... //重复累计做11次 add ax,ax
mov ax,4c00h
int 21h
code ends
end
assume cs:code
code segment
mov ax,2
mov cx,11
s: add ax,ax
loop s
mov ax,4c00h
int 21h
code ends
end
/**
* (1) 标号: 汇编语言中,标号代表一个地址,程序中有一个标号 s, 它实际上标识了一个地址,这个地址处有一条指令:add ax,ax
* (2) loop s : CPU 执行 loop s 的时候,需要进行两步操作:
第一步: (cx)=(cx)-1
第二步:判断 cx 中的值,不为 0,则转至标号 s 所标识的地址处执行(程序中的指令是 add ax,ax),如果为 0 则执行下一条指令(下一条指令是 mov ax,4c00h)
*/
- cx 和 loop 指令相配合实现循环功能的 3 个要点:
(1)在 cx 中存放循环次数
(2)loop 指令中的标号所标识的地址要在前面
(3)要循环执行的程序段,要写在 标号 和 loop 指令的中间
- cx 和 loop 指令相配合实现循环功能的 程序框架:
mov cx,循环次数
s:
循环执行的程序段
loop s
5.3 DEBUG中 跟踪 loop 指令实现循环程序
- 汇编程序中,数据不能以字母开头。
所以前面要加 0 。 比如 mov ax,0FFFFH - g 0012 => 表示 CS:0012 前的程序被执行。
- 遇到 loop 指令时,使用 p 命令执行, Debug 就会自动重复执行循环中的指令,知道 (cx)= 0 为止。
5.4 Debug 和 汇编编译器 masm 对指令的不同处理
- Debug 和 编译器 对 “ mov al,[0] ” 的解释不同,debug 中 认为 [0] -> ((ds)*16 + 0) 即 内存单元, 编译器则解释为 常量 0
汇编源程序中一下指令的含义:
mov al,[0] => (al)=0 ,将常量0送入al中 等同于 mov al,0
mov al,ds:[0] => (al)=((ds)*16+0) ,将内存单元中的数据送入al中
mov al,[bx] => (al)=((ds)*16+bx) ,将内存单元中的数据送入al中
mov al,ds:[bx] => 等同于 mov al,[bx]
- 在汇编源程序中, 如果用指令访问一个内存单元,则在指令中必须用“ [...] ” 来表示内存单元, 如果 “ [] ” 中用的是一个常量 idata,直接给成内存单元地址,就要在 “ [] ” 的前面显式的给出 段地址所在的段寄存器。 比如
mov al,ds:[0] - 如果 “ [] ” 里用寄存器 如 mov al,[bx], 间接给出内存单元的偏移地址,则段地址默认在ds中。当然,也可以显式的给出段地址所在的段寄存器。
5.5 loop 和 [bx] 的联合应用
计算 ffff:0~ffff:b 单元中数据的和,结果存储在 dx 中
assume cs:code
code segment
mov ax,0ffffh
mov ds,ax
mov bx,0
mov dx,0
mov cx,12
s: mov al,[bx]
mov ah,0
add dx,ax
inc bx
loop s
mov ax,4c00h
int 21h
code ends
end
5.6 段前缀 : 出现在访问内存单元的指令中,用于显式的指明内存单元的段地址。
(1)mov ax,ds:[bx] //将一个内存单元的内容送入 ax,这个内存单元的长度为 2字节(字单元),存放一个字, 偏移地址在 bx 中,段地址在 ds 中。
(2)mov ax,cs:[bx] //将一个内存单元的内容送入 ax,这个内存单元的长度为 2字节(字单元),存放一个字, 偏移地址在 bx 中,段地址在 cs 中。
(3)mov ax,ss:[bx] //将一个内存单元的内容送入 ax,这个内存单元的长度为 2字节(字单元),存放一个字, 偏移地址在 bx 中,段地址在 ss 中。
(4)mov ax,es:[bx] //将一个内存单元的内容送入 ax,这个内存单元的长度为 2字节(字单元),存放一个字, 偏移地址在 bx 中,段地址在 es 中。
(5)mov ax,ss:[0] //将一个内存单元的内容送入 ax,这个内存单元的长度为 2字节(字单元),存放一个字, 偏移地址 0,段地址在 ss 中。
(6)mov ax,cs:[0] //将一个内存单元的内容送入 ax,这个内存单元的长度为 2字节(字单元),存放一个字, 偏移地址 0,段地址在 cs 中。
5.7 一段安全的空间:
0:200 ~ 0:2FF
5.7 段前缀的使用:
将 内存 ffff:0 ~ ffff:b 单元中的数据复制到 0:200 ~ 0:20B 单元中
assume cs:code
code segment
mov bx,0
mov cx,12
s: mov ax,0ffffh
mov ds,ax
mov dl,[bx]
mov ax,0020h
mov ds,ax
mov [bx],dl
inc bx
loops
mov ax,4c00h
int 21h
code ends
end
- 练习
(1)编程, 向内存 0:200~0:23F 一次传送数据 0~63?
assume cs:code
code segment
mov ax,0020h
mov ds,ax
mov bx,0
mov cx,64
s: mov [bx],bx
inc bx
loop s
mov ax,4c00h
int 21h
code ends
end
(2)下面的程序的功能是将“mov ax,4c00h”之前的指令复制到内存0:200处,补全程序。上机调试,跟踪运行结果。
assume cs:code
code segment
mov ax,code
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