内存单元的描述
内存单元可以使用[数字]表示,当然也可以使用[寄存器]表示,如[bx]
,mov ax,[bx]
,mov al,[bx]
为了表示方便,使用()来表示一个内存单元或寄存器中的内容,如(ax)
,(20000H)
,或((dx)*16+(bx))
表示ds:bx中的内容,但不可写为(1000:0),((dx):0H)。而(X)中的内容由具体寄存器名或运算来决定。
我们使用idata来表示常量。所以以下语句可以这么写:mov ax,[idata]
mov ax,idata
。
loop指令
mov cx, loop_count
tag_name:
; code ...
loop tag_name
loop指令格式:loop 标号。
loop指令通常用来实现循环功能,当执行loop指令时,CPU进行两步操作:
- (cx)=(cx)-1
- (cx)不为零则跳至标号处执行程序。
所以CX中存放的是循环次数,一个简单的例子如下(计算2^12):
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
所以使用loop注意三点:
- 先设置cx的值 mov cx,循环次数
- 设置标号与执行循环的程序段 s:执行程序段
- 在程序段最后写
loop tag_name
注:在汇编语言中,数据不能以字母开头,所以大于9fffH的数据,要在开头加0,如0A000H
注:debug中G命令 g 0012表示CPU从当前CS:IP开始一直执行到0012处暂停。P命令可以将loop部分一次执行完毕,直到(CX)=0,或使用g loop执行下一条命令。
Debug和masm编译器对指令的不同处理
mov ax,[0]
这条指令在Debug和masm中有着不同的解释,Debug是将DS:0内存中的数据送给AX,而masm中则是mov ax,0
,即将0送入AX。
解决方法1:先将偏移地址送入BX,然后再使用mov ax,[bx]
解决方法2:直接显式给出地址,如mov al, ds:[0]
(相应的段寄存器还有CS,SS,ES这些在汇编语言中可以称为“段前缀”)当然,这种写法通过编译器之后会变成Debug中的mov al,[0]
段前缀的使用
在进行内存访问时,如指令mov ax,[bx]
内存单元的偏移地址由bx给出,而段地址默认在ds中。我们可以在访问内存单元的指令中显式地给出内存单元的段地址所在的段寄存器。如:mov ax,ds:[bx]
。这样显式地指明内存单元段地址的叫做段前缀。
将ffff:0-ffff:b中的数据转存入0:200b-0:20b中:
assume cs:code
code segment
mov ax,0ffffh
mov ds,ax
mov ax,0020h
mov es,ax
mov bx,0
mov cx,12
s:mov dl,[bx]
mov es:[bx],dl
inc bx
loop s
mov ax,4c00h
int 21h
code ends
end
[bx]直接使用的时候默认段前缀是ds,但要使用其他的段前缀,如es就要在前面加上。
安全的编程空间
在之前没有提到的一个问题,如果在写程序之前不看一眼要操作的内存,就直接开始使用的话,万一改写了内存中重要的系统数据,可能会引起系统崩溃。所以我们一般在一个安全的内存空间中操作。一般操作系统和合法程序都不会使用0:200~0:2ff这256字节的空间,所以我们可以在这里操作。
学习汇编语言的目的就是直接和硬件对话,而不理会操作系统,这在DOS(实模式)下是可以做到的,但在windows或Unix这种运行与CPU保护模式的操作系统上却是不可能的,因为这种操作系统已经将CPU全面严格的管理了。