8086汇编学习之[BX],CX寄存器与loop指令,ES寄存器等

来源:互联网 发布:死神vs火影月改优化版 编辑:程序博客网 时间:2024/05/18 04:54

同类学习笔记总结:
(一)、8086汇编学习之基础知识、通用寄存器、CS/IP寄存器与Debug的使用
(二)、8086汇编学习之DS寄存器、SS/SP寄存器

一、汇编程序的基本格式:

1、基本格式与解析:

assume cs:codeseg   //assume假设CS寄存器与codeseg段有关联,codeseg段本就是代码段codeseg segment //段开始,codeseg为段名,可随意命名只要不和伪指令、指令等冲突即可    mov ax,4C00H    int 21Hcodeseg ends    //段结束end //程序结束标志

以上格式中assume、segment、ends、end均为伪指令,由编译器识别翻译。assume的关联并非必须。

    mov ax,4C00H    int 21H

而这两条指令的功能就是实现程序返回,在DOS中如果是command将可执行程序加载入内存,command就设置CPU的CS:IP指向程序的第一条指令,及程序的入口,当程序从入口开始执行完毕以后,“mov ax,4C00H”、“int 21H”看来那个条指令执行后返回到command中,CPU则继续执行command,如果是Debug将可执行程序加载进内存中,则返回后回到Debug(类似于在Shell中运行的子进程(”main(){return 0;})运行完毕会返回shell,shell则继续运行。只不过DOS是单进程,而Linux的Shell是时间片轮转多进程)。

2、注意问题:

(1)不允许操作数以字母开头:
在Debug中一行一行指令输入时,我们可以以字母开头,但是在编辑器编辑、MASM编译、LINK连接时,就不能以字母开头,否则会报未定义错误,如果遇到以字母开头的,在其前面加一个0即可。
eg:

mov ax,B810H    //(×)mov ax,47120    //(√),B810H的十进制mov ax,0B810H   //(√),十六进制修改(修正)mov al,E7H  //(×)mov al,0E7H //(√)

关于以字母开头会提示错误的测试如下:
这里写图片描述

(2)-g cs:ip、-g ip 快速执行多行
-g ip 省略cs,默认去ds寄存器中找。

//eg:    这是编译过程中生成的“.lst”文件中的一部分                            assume cs:codeseg        0000                 codeseg segment        037F:0000  B8 1000           mov ax,1000H        037F:0003  8E D8             mov ds,ax        037F:0005  BB 0000           mov bx,0        037F:0008  8B 07             mov ax,ds:[bx]        037F:000A  BB 0002           mov bx,2        037F:000D  8B 07             mov ax,ds:[bx]        037F:000F  BB 0004           mov bx,4        037F:0012  8B 07             mov ax,ds:[bx]        037F:0014  BB 0006           mov bx,6        037F:0017  8B 07             mov ax,ds:[bx]        037F:0019  BB 0008           mov bx,8        037F:001C  8B 07             mov ax,ds:[bx]        037F:001E  BB 000A           mov bx,10        037F:0021  8B 07             mov ax,ds:[bx]        037F:0023  B8 4C00           mov ax,4C00H        037F:0026  CD 21             int 21H        037F:0028                codeseg ends                             end //debug dabx.exe//直接-g 000F//将000F之前的运行完停到"mov bx,4"这一行等待运行

上面这段代码编译运行前:

;-d 2000:1000;00 00 00 00 00 00 00 00--00 00 00 00 00 00 00 00

编译->连接->装载->运行后,结果为:

;-d 2000:1000;BE 00 EE 00 EE EE EE 00--00 00 00 00 00 00 00 00

(3)、用[bx]代替[n]:
在上面我们已经用到[bx]了。至于为什么要用[bx]代替[n],同样是因为在Debug中会自动识别为ds:[n]段地址+偏移地址的格式,但是在编辑器中编辑并通过masm编译后,这种[n]形式的都会被编译器解析为值,eg:mov ax,[2]会被解析为mov ax,2,但是并不是说必须用[bx]的形式,只是[n]的形式必须配合”段地址寄存器:[n]”的形式一起使用(“这里的不能字母开头”、“[bx]代替[n]”等问题都是Debug与MASM对指令的不同处理方式引起的)。

mov ax,[0]  //对MASM来说错误mov ax,ds:[0]   //正确mov ax,ds:[bx]  //正确mov ax,[bx] //正确

3、程序加载:

编译->连接->装载(加载)->运行的过程,我们说说程序在内存中的装载情况:
这里写图片描述
,测试如下:
这里写图片描述

二、loop指令、CX寄存器等:

1、简介:

在C语言中,我们使用过goto语句:

loop_one:    int x = 5;    ...    goto loop_one;

而在汇编中的循环(loop)与goto语句及其相似,其格式为:

loop_one:    mov ax,5    ...    loop loop_one

我们C中goto循环何时结束的条件一般在if..else判断中,而汇编则是将循环结束条件放在了CX寄存器中。给CX一个初始值(循环次数),当loop指令中标号(如:loop_one)所标识的地址被再次访问,CX的值减去1(即loop执行一次,CX值自减1)。当CX的值为0时结束循环,执行loop指令的下一条指令。loop_one其实就是个地址,loop loop_one就是跳跃到loop_one所标识的地址去(IP寄存器修改为loop_one的地址),从该地址继续解析指令并执行。loop指令的循环形式像do{}while();而不像while(){},因为loop至少会执行一次。那么CX初始化为0时,先减到FFFF再判断,而不是直接判断等于0退出循环。并且这是倒计时式的循环,不是计数式的循环。
(知识点:在用Debug的-p调试时可以不进入循环/call调中去逐句执行。-p:特点是当遇到int中断程序时,会一次性将调用的中断程序执行完;而-t:的特点是当遇到int中断程序时会追踪到调用的中断程序里面去。像loop、call、int 21H等都属于int中断,所以通常会用到-p而不是-t)

2、应用:

例一:计算FFFF:0~FFFF:F的字节型数据的和,结果保存到DX中:

assume cs:codesegcodeseg segment        ;设置ds寄存器为FFFFH        mov bx,0        mov ax,0FFFFH        mov ds,ax        mov dx,0        ;初始偏移地址为0        mov cx,16       ;FFFF:0~FFFF:F共16个字节,循环16次        mov ax,0        ;字节型数据,需要8位寄存器,但是8位可能累加时溢出,必须用16位,所以高位必须清零,之后向AL存数据,所以只需清零一次    addNumber:        mov al,ds:[bx]  ;字节型数据,需要8位寄存器        add dx,ax       ;累加        inc bx          ;inc指令将bx的值自加1,即偏移地址增大一个字节        loop addNumber  ;条件(CX)减1        mov ax,4C00H        int 21Hcodeseg endsend

测试结果:
这里写图片描述

2、将FFFF:0~FFFF:F的数据复制到0:200~020F中:

assume cs:codesegcodeseg segment        mov cx,16        mov bx,0    ;由于只用一个ds数据段地址寄存器,而需要在两段数据段之间循环操作,就需要用中间寄存器先保存被复制的对象,然后修改ds寄存器,最后将中间寄存器中的一个字节mov到目标地址中    copyNumber:        mov ax,0FFFFH        mov ds,ax        mov al,ds:[bx]  ;保存到中间寄存器        mov ax,0020H    ;修改ds寄存器,0020*10:0==0:200        mov ds,ax        mov ds:[bx],al  ;中间寄存器转移        inc bx  ;操作下一个字节        loop copyNumber        mov ax,4C00H        int 21Hcodeseg endsend

三、ES寄存器:

由于上例题中,用一个ds寄存器在两段地之间来回修改,使得指令数随着循环次数增大而成倍增加,那么我们是否可以借用其它段地址寄存器(cs:[bx]、ss:[bx]、es:[bx]来优化)呢?但是cs和ss都已有他用,只有ES空闲,ES寄存器也是一个段地址寄存器,我们可以用ES段地址寄存器对上例进行优化:
DS:数据从哪里来
ES:数据到哪里去
当然ES和DS在下例中可以替换位置,但是注意:ds可以不写成ds:[bx]的形式,因为[bx]默认是ds:[bx],而ES就必须写成es:[bx],否协会被解析为ds:[bx]。

assume cs:codesegcodeseg segment        mov cx,16        mov bx,0        mov ax,0FFFFH  ;ds寄存器存储被复制者段地址        mov ds,ax        mov ax,0020H  ;es存放目标段地址        mov es,ax    ;用ES寄存器优化:    copyNumber:        mov dl,ds:[bx]        mov es:[bx],dl          inc bx        loop copyNumber        mov ax,4C00H        int 21Hcodeseg endsend

测试结果:
这里写图片描述
还有一点问题:由于汇编是直接对物理内存进行操作,所以不安全的内存操作很可能发生,为了避免使用已经有数据的内存,我们要尽量在0:200~0:2FF的256个字节中进行编程(因为DOS以及其他合法程序一般会使用该段空间)。

0 0
原创粉丝点击