1. 内存单元的访问方式(这里的不全,之后会进一步补充):
1) 总共有四种方式:
i. [立即数],但是在这种情况下只能作为源操作数(即第二个操作数,比如mov ax, [15]等),此时访问的内存是(ds:立即数),但是不能作为目的操作数(即第一个操作数),如果作为第一个操作数则编译器会将看做是一个普通的立即数而报错,比如mov [15], ax就会看成mov 15, ax而报错!一定要注意这点!
以下所有方式既可以作为源操作数也可以作为目的操作数,没有任何限制!
ii. [bx],注意,必须是基地址寄存器bx,不能是其它寄存器,如ax等,都会报错的!此时访问的就是(ds:bx)单元;
iii. 任意段寄存器(包括cs):[bx],中括号中的偏移地址也必须是bx而不能是其它寄存器,这种表示法就是段前缀表示法,只不过不写段前缀默认ds作为段基,加了段前缀就能精确定位到某一个段上了!
2) 访问内存单元最重要的关键——确定内存单元的类型(是字节型的还是字型还是双字型的):一般类型是由另一个操作数来确定(即根据寄存器的类型推断出来),比如mov al, [bx]中因为al是字节型的,因此传送的就是8位内存单元,而mov ax, [bx]中ax是16位的,因此传送的是双字,而mov [bx], [bx + 2]由于无法参考出传送的是什么类型的会编译报错!同样mov [bx], 23H中也无法推断出传送数据的类型而编译报错!
!!注意:MASM不支持两个操作数都是内存单元的情况!
2. 自加指令inc:
1) 该命令只有一个参数,就是寄存器(也可以是内存单元,但是内存单元需要指定类型,后面会补充,这里先不讲),但可能不能作用于立即数;
2) 作用是让指定寄存器中的内容自加1,可能会越界,需要注意;
3) 除了段寄存器不能运用这条之另外其余所有寄存器(包括sp、bp、di、si等,不管是8位、16位还是32位)都可以运用该指令;
3. 使用loop指令构造循环结构:
1) 所有循环都需要有一个循环计数变量,loop指令也是一样,该计数变量存放在cx寄存器中;
2) 执行loop指令的过程:先将cx中的内容-1,然后再判断其是否为0,如果为0则推出循环(其实就是继续执行代码中位于loop后面的语句),如果不为0(不是大于0而是非0)则跳到loop指令中指定的那个标签的位置继续循环(其实就是将cs:ip指向标签处);
!汇编中的标签在编译后将会被替换成一个地址常量,定义方式和C语言中标签的定义方式相同,都是"标签名:"放在某个语句开头处;
3) 一般步骤:
mov cx, 循环次数 tag: 循环起始位置 循环体 loop tag
4. 示例:
1) 计算2^12:
assume cs:codesg codesg segment mov ax, 1 mov cx, 12 lop: add ax, ax loop lop mov ax, 4C00H int 21H codesg ends end注意!对于立即数,如果不加后缀H就表示十进制数,如果加后缀H就表示是16进制数;
2) 计算123×236:
assume cs:codesg codesg segment mov ax, 0 mov cx, 123 lop: add ax, 236 loop lop mov ax, 4C00H int 21H codesg ends end3) 将FFFF:0006字节中的值乘以3存入BX中
assume cs:codesg codesg segment mov ax, 0FFFFH mov ds, ax mov ax, 0 mov al, ds:[6] mov bx, 0 mov cx, 3 lop: add bx, ax loop lop mov ax, 4C00H int 21H codesg ends end!!注意:关于MASM中立即数表示问题的小结:
*1. 立即数字只能以数字打头而不能以字母打头,否则会编译报错;
*2. 因此像诸如AB、A00、FFFF等数据都将是非法的,而解决方法就是是在这些数前面加上一个0作为前缀;
*3. 以后缀辨别进制类型,十进制无后缀,二进制后缀为B,十六进制后缀为H,如果数字中有A~F而没有后缀H也会编译报错!
!关于书写规范问题的建议:汇编编译器是不区分大小写的,但是通常指令和标记都用小写,而立即数中的字母(A~F以及后缀都是用大写),这样使程序更加直观;
4) 求FFFF:0开始的13个字节内容之和(保存在DX寄存器中):
assume cs:codesg codesg segment mov ax, 0FFFFH mov ds, ax mov bx, 0 mov dx, 0 mov ah, 0 mov cx, 13 lop: mov al, [bx] add dx, ax inc bx loop lop mov ax, 4C00H int 21H codesg ends end注意!13个字节内容之和可能会超过8位,因此需要保存在16位寄存器,因此内存取出的字节要先放到ax中中转,然后再拿去和dx加!
6. 利用P命令和G命令调试循环程序:
1) 当循环次数非常大时单步调试明显不现实,此时可以使用P或G命令将剩余循环全部执行完(P是Proceed,即继续的意思,G就是Go的一次,但和C语言的goto语句不通,goto是直接跳跃,而G命令不仅跳跃到指定位置,并且将其中所有的代码都执行完);
3) G:
i. G命令的参数是偏移地址,不能含段基地址,而段基默认就是CS(且不能改),因此进入调试程序后,可以先用U命令反汇编即将执行的汇编指令,每条汇编指令的偏移地址都将被显示出来,然后再根据显示的地址选择想要到达的目的地偏移地址即可;
ii. 执行命令后就会从当前指令处一直连续执行到目的指令处,中间所有的代码都会被执行(包括循环);
iii. 目的指令地址必须大于当前指令地址否则调试将会异常终止而返回Debug程序;
可以看到G不是跳转而是一直连续执行到指定指令出,也就是说中间的所有指令都执行过了,可以从BX中的结果看出中间的循环都执行完了;
3) P:
i. 在遇到int指令(即中断指令)时使用P将会成功的结束程序回到Debug,这在之前已经运用过了;
ii. 当遇到loop指令使用P命令,将会将剩余的循环一次性全部走完,一直走到最后一次loop指令的后面一条指令处;
iii. 当遇到非上述两种指令的普通指令时,效果就跟T命令一样,仅仅就是“单步继续执行指令”的意思了;