本文已参与「新人创作礼」活动,一起开启掘金创作之路。
前言
学习教材:《汇编语言(第4版)》王爽著 此笔记是书中内容+自我总结,方便查阅和复习 请支持原著
约定[bx]符号、(bx)符号、idata符号
前面提到,[0]可以表示某段偏移地址为0的那个内存单元,它有自适应功能:
mov ax,[0] //表示将字单元[0]和[1]传入ax
mov al,[0] //表示将字节单元[0]传入al
因此,类似于上面的用法:[bx]表示某段偏移地址为bx值的那个内存单元,此时偏移地址存在bx内
注意:只有bx、bp、si、di四个寄存器可以用[...]的形式寻址
为了区分且使描述方便,定义(bx)表示bx存储的内容,可以是寄存器(ax)、段寄存器+偏移地址((ds)*16+(bx))和内存单元(10000H)
(2000:0)、((ds):1000H)等是错误用法,必须前后统一
idata表示任意常量,可以是1,2,3,...
@[toc]
一、loop
loop指令表示循环,循环次数存储在寄存器cx里,格式为loop 标号,在循环体的第一行添加标号以表明循环入口。这是一个两次自增的循环:
assume cs:cal
cal segment
;初始化
mov ax,1
mov cx,2
;循环体
s:add ax,ax
loop s
;返回值
mov ax,4c00h
int 21h
test ends
end
这里的s是一个标号,可以任取不限定于s
CPU执行Loop指令时分作两步
- (cx) -= 1
- 判断cx是否为0,不为0则转至运行标号处代码,否则向下执行
实验:加法循环计算123*236
累加123个236即可得到答案
assume cs:cal
cal segment
mov ax,0
mov cx,123
s:add ax,236
loop s
mov ax,4c00h
int 21h
cal ends
end
二、遍历连续的内存单元
假设需要遍历FFFF:0~FFFF:B这十二个内存单元,累加它们的值放到dx,传统方法是写十二个add,这里我们可以用循环
inc指令表示将某寄存器的值加一,如inc bx
需要注意的是:
-
内存单元是一字节,而ax是十六位寄存器,存储元件的数据长度不一样不能直接赋值,因此需要分别对ah赋值0,对al赋值(FFFF:[bx]), 其他方法可能会引起进位丢失等情况
-
汇编源程序中,数据不能以字母开头,FFFFH要写作
0FFFFHassume cs:ffff0_12 ffff0_12 segment mov ax,0FFFFH mov ds,ax mov bx,0 mov cx,12 mov dx,0 s:mov al,[bx] mov ah,0 add dx,ax inc bx loop s mov ax,4c00h int 21 ffff0_12 ends end
三、Debug.exe和MASM的不同处理
对于mov ax,[0],希望表示将ds:0内存单元的值送入ax,然而:
- MASM会将其当作
mov ax,0 - Debug.exe会将其当作
mov ax,ds:0
为了避免歧义,建议使用以下形式:
mov al,ds:[0],(al)=((ds)*16+0)mov al,[bx],其中bx=0,(al)=((ds)*16+(bx))
四、段前缀
由于上方提到的差异处理,建议对偏移地址均添加对应的段前缀,避免歧义且便于理解
mov ax,ds:[bx/0]mov ax,cs:[bx/0]mov ax,ss:[bx/0]mov ax,es:[bx/0]
五、安全的内存
随意向任意内存写入内容是危险的!
在一般的PC机中,DOS模式下安全的空闲的空间是00200H~002FFH,总长256B,可以供我们操作
谨慎起见,使用前先Debug查询,只有全是0才能安全使用
六、Debug调试loop
g (偏移地址)用于跳转到指定行运行,该段代码之前的命令会先运行,断点设置在指定行p用于一步执行完loop循环,使用前先跳转到loop行
后记
- 编程中发现segment不能命名为test(?存疑)
- loop的原理是先减再判定再执行
- cx记录循环次数
- 良好的习惯:段前缀
- 不要随意写入
- 内存单元到寄存器的数据送入需要分两段送入,前者大小1B,后者大小2B