汇编学习--体验软件编程下硬件工作(二)

语言类的学习最好的办法就是放到在功能代码中理解,文字分析特殊情况。

体验软件编程下硬件工作(二)

◆在DOS的DEBUG下,可以通过指令跟踪和修改各个寄存器以及内存中的数据,具体的指令如下:

R查看和改变寄存器内容

D查看内存中内容

E改写内存中的内容

U将内存中的机器指令翻译成汇编指令

T执行一条机器指令

A以汇编指令的格式在内存中写入一条机器指令

这些指令在DEBUG中多用就会熟悉。

◆第一个汇编程序:

[plain] view plaincopy
  1. assume cs:codesg  
  2. codesg segment  
  3. start: mov ax,0123h      
  4.        mov bx,0456h  
  5.        add ax,bx  
  6.        add ax,ax  
  7.          
  8.        mov ax,4c00h  
  9.        int21h  
  10. codesg ends  
  11. end  
⑴assume----伪指令,意为假设将段寄存器和某一段连接起来。

⑵XXX  segment

    ......

   XXX  ends----成对使用的伪指令,定义一个段,分别表示段开始和段结束。

⑶end----伪指令,汇编语言的结束标志。

以上这三条是通过伪指令实现的汇编结构,它们只对编译器有作用。

mov指令:传送指令,将第二个数据送到第一个位置处。

add指令:加法指令,将第一个位置和第二个位置的数据相加,并存到第一个位置处。

mov ax,4c00h

int 21h:程序返回的指令。我们的程序最先以湖边指令的形式存在源程序中,经编译、连接后编程机器码,存储在可执行的文件中,那么怎么得到运行呢?

一个程序A在可执行的文件中,则必须有一个正在运行的程序B,将A从可执行文件中加载在内存后,将CPU的控制权交给A,A才能得到运行。A开始运行后,B暂停运行。而当A运行完毕后,应该将CPU的控制权交还给使它得以运行的程序B,B继续运行。

用我们的系统解释就是,我们的这个可执行程序被commond运行并加载到内存中,系统将CPU的控制权交给我们的程序,等我们的程序执行完后,我们要将CPU的控制权还给commond,所以以上的两条指令就是将控制权返回的指令。

下面进行源程序的编译和连接:

想要进行编译连接工作,我们需要一个编译器,目前汇编语言的编译器有很多,推荐两种,masm和masm32

一个是DOS下的编译器,一个是可视化的编译器(使用方法网上很多,安装好了别忘了设置环境变量,否则会报错)

下图是编译连接的过程过程源文件转换成的机器码:


整个过程可以通过debug  *.exe来跟踪。

在DOS系统中EXE文件中的程序加载过程:


◆第二段代码

[plain] view plaincopy
  1. assume  cs:code,ds:data,ss:stack  
  2. data segment  
  3.     dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h  
  4. data ends  
  5. stack segment  
  6.     dw 0,0,0,0,0,0,0,0  
  7. stack ends  
  8. code segment  
  9.      start:mov ax,stack  
  10.              mov ss,ax  
  11.              mov sp,16  
  12.              mov ax,data  
  13.              mov ds,ax  
  14.              mov bx,0  
  15.              mov cx,8  
  16.            s:push [bx]  
  17.               add bx,2  
  18.               loop s  
  19.               mov bx,0  
  20.               mov cx,8  
  21.           s0:pop [bx]  
  22.                add bx,2  
  23.                loop s0  
  24.                mov ax,4c00h  
  25.                int 21h  
  26. code ends  
  27. end start  
之前的一段代码只有一个代码段,而这个代码采用了3个不同的段,用以区分存储数据,栈和代码,这样的利用方便连续的读取数据和访问栈

我们逐行来分析代码:

第1行:可能有人会问,是不是通过assume  cs:code,ds:data,ss:stack就可以让CPU的cs指向code段,ds指向data段,ss指向stack段了?如果你这样想的话就大错特错了,assume只是一种伪指令,它只能作用于编译器,并不能作用于CPU。这样做的意义不需要知道,只要知道在程序前一定要将各个段和各个段寄存器对应assume就好。

第2~4行:数据段,在这里储存数据。

第5~7行:栈段,这里定义的多少个0是为了说明定义多大的栈空间。

第8~26行:代码段,实现功能代码。

              mov ax,stack----stack代表的是栈段的段地址,通过

             mov ax,stack
             mov ss,ax
             mov sp,16  ----     
将栈指针指向栈底。

             mov ax,data
             mov ds,ax
             mov bx,0   ----      定位数据段的位置。

             mov cx,8   ----      cx中存放的是loop(循环)的次数

            s:push [bx]  ----    s:是标记,在使用跳转命令时这种标记十分有用。

                                        push是将其后数据推向栈顶的意思,与其相反的是pop,将栈顶的数据弹出

             loop s        ----   条件跳转,只要cx中不为0就跳转。

◆第三个程序(灵活的寻址方式)

定义一个数据段:

datasg segment

       db  'BaSic'

       db  'iNfOrMaTiOn'

datasg ends

要将datasg中的第一个字符串转化为大写,第二个字符串转化为小写。

程序如下:

[plain] view plaincopy
  1. datasg  segment  
  2.      db 'BaSiC'  
  3.      db 'iNfOrMaTiOn'  
  4. datasg ends  
  5.   
  6. codesg segment   
  7.       start: mov ax,datasg  
  8.                mov ds,ax  
  9.                mov  bx,0  
  10.                mov cx,5  
  11.             s:mov al,[bx]  
  12.                and al,11011111b  
  13.                mov[bx],al  
  14.                inc bx  
  15.                loop s  
  16.                mov bx,5  
  17.                mov cx,11  
  18.            s0:mov al,[bx]  
  19.                 or al,00100000b  
  20.                 mov [bx],al  
  21.                 inc bx  
  22.                 loop s0  
  23.                
  24.                 mov ax,4c00h  
  25.                 int 21h  
  26. codesg ends  
  27. end start  
           and al,11011111b   ----这句是将小写转换成大写(不明白可以对比一下大写和小写字母的ascii码)
           or  al,00100000b    ---- 这句是将大写转换成小写

           inc  bx                   ----加1的意思

在高级语言中,遇到'BaSiC'这种字符串我们经常会将它存储在数组中,以便进行相关的操作,那么作为一种底层的语言,让我们看看“数组”是怎么实现的:

datasg segment

       db  'BaSic'

       db  'iNfOrMaTiOn'

datasg ends中的第一个字符传转化为大写字母,第二个字符串转换成小写字母

实现程序如下:

[plain] view plaincopy
  1. assume cs:codesg,ds:datasg  
  2. datasg  segment  
  3.      db 'BaSiC'  
  4.      db 'iNfOrMaTiOn'  
  5. datasg ends  
  6.   
  7. codesg segment   
  8.       start: mov ax,datasg  
  9.                mov ds,ax  
  10.                mov  bx,0  
  11.                mov cx,5  
  12.             s:mov al,[bx]  
  13.                and al,11011111b  
  14.                mov[bx],al  
  15.                mov al,<span style="color:#ff0000;">[5+bx]       </span>;可以写成5[bx]  
  16.                 or al,00100000b  
  17.                 mov [bx],al  
  18.                 mov <span style="color:#ff0000;">[5+bx]</span>,al        
  19.                 inc bx  
  20.                 loop s  
  21.   
  22.                 mov ax,4c00h  
  23.                 int 21h  
  24. codesg ends  
  25. end start     
这里采用了一种[bx+idata]的结构,可以通过这种结构来寻找到每个字符串的开端,非常的方便。

在8086CPU中还有两种寄存器,它们的作用和bx很相似,它们就是si和di(它们不能拆成两个8字节寄存器来用)

我们可以利用它们使用[bx+si]、[bx+di]、[bx+si+idata]和[bx+di+idata]的结构,实现高级语言的多重循环。

代码如下:

[plain] view plaincopy
  1. assume cs:codesg,ds:datasg,ss:stacksg  
  2. datasg segment  
  3.      db 'ibm              '  
  4.      db 'ibm              '  
  5.      db 'ibm              '  
  6.      db 'ibm              '  
  7. datasg ends  
  8. stacksg segment  
  9.     dw 0,0,0,0,0,0,0,0  
  10. stacksg ends  
  11.    
  12. codesg segment  
  13.      start:mov ax,stacksg  
  14.              mov ss,ax  
  15.              mov sp,16  
  16.              mov ax,datasg  
  17.              mov bx,0  
  18.              mov cx,4  
  19.          s0:push cx  
  20.              mov  si,0  
  21.              mov cx,3  
  22.            s:mov al,[bx+si]  
  23.               and al,11011111b  
  24.               mov [bx+si],al  
  25.               inc si  
  26.               loop s  
  27.               add  bx,16  
  28.               pop cx  
  29.               loop s0  
  30.                 
  31.               mov ax,4c00h  
  32.               inc 21h  
  33. codesg ends  
  34. end start  
以上代码的功能是将数据段中的字母都转换成大写字母。

在上述的代码中用到了push cx,在汇编编程中可以将需要暂时存储的数据存放到栈中,这和高级语言中函数中的局部变量是一个道理。

◆指令要处理的数据长度

⑴通过寄存器来指明要处理的数据的长度。

例如:

      mov ax,1----字长度

      mov al,1----字节长度

⑵在没有寄存器名存在的情况下,用操作符X ptr指明内存单元的长度,X在汇编指令中可以是word或者byte

例如:

      mov word ptr ds:[0],1    -----访问的内存单元是一个字单元

      mov byte ptr ds:[0],1     -----访问的内存单元是一个字节单元

◆div指令(除法指令)

⑴除数:有8位和16位两种,在一个寄存器或内存单元中

⑵被除数:默认的放在AX或DX和AX中,如果除数是8位,除数则是16位,默认在AX中存放;如果除数为16位,则被除数位32位,在DX和AX中存放,DX存放高16位,AX存放底16位

⑶结果:如果除数为8位,则AL存储触发操作的商,AL存储除法操作的余数;如果除数是16位,则AX存储除法操作的商,DX中存储除法操作的余数。

◆几个伪指令:

⑴db定义字节型数据,dw定义字型数据,dd定义双字型数据

⑵dup表示重复的数据

例如:db 3 dup(0)表示 db 0,0,0


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章