实验4 汇编应用编程和c语言程序反汇编分析

一、实验目的

1. 理解80×25彩色字符模式显示原理。
2. 理解转移指令jmp, loop, jcxz的跳转原理,掌握使用其实现分支和循环的用法。
3. 理解转移指令call, ret, retf的跳转原理,掌握组合使用call和ret/retf编写汇编子程序的方法,掌握
参数传递方式。
4. 理解标志寄存器的作用。
5. 理解条件转移指令je, jz, ja, jb, jg, jl等的跳转原理,掌握组合使用汇编指令cmp和条件转移指令实
现分支和循环的用法。
6. 了解在visual studio/Xcode等环境或利用gcc命令行参数反汇编c语言程序的方法,理解编译器生成
的反汇编代码。
7. 综合应用寻址方式和汇编指令完成应用编程。

二、实验准备

实验前,请复习/学习教材以下内容:
第9章 转移指令的原理
第10章 call和ret指令
第11章 标志寄存器

三、实验内容

1. 实验任务1

教材「实验9 根据材料编程」(P187-189)

编程:在屏幕中间分别显示绿色、绿底红色、白底蓝色的字符串'welcome to masm!'。

codesg ends
end start

assume cs:code, ds:data,ss:stack
data segment
        db 'Welcome to masm!'
        db 00000010b,00100100b,01110001b
data ends
stack segment
        db 16 dup(0)
stack ends
code segment
start:
        mov ax,stack
        mov ss,ax
        mov ax,16
        mov sp,ax
        mov ax,0b800H 
        mov ds,ax 
        mov ax,data
        mov es,ax

        mov bx,1830     //第一行W所在的偏移地址
        mov di,0        //记录这是第几行
        mov cx,3        //外层循环次数

s0:    push cx
        mov si,0        
        mov cx,16       //内存循环次数,16为字符串长度
        
s:     
        mov al,es:[si]    //取出一个字母,放在低位
        mov ah,es:[di+16]  //取出该i行的颜色信息,放在高位
        mov ds:[bx],ax     //存入对应位置的显存
        
        inc  si
        add bx,2        
        loop s

        pop cx
        add bx,0080h      //一行是160个,按照道理应该是加上00a0h,但是在上面的循环里面bx增加了32,所以这里只用增加0080h
        inc di
        loop s0

        mov ah, 4ch
        int 21h
code ends
end start

结果:

2. 实验任务2

编写子程序printStr,实现以指定颜色在屏幕上输出字符串。调用它,完成字符串输出。

assume cs:code, ds:data
data segment
    str db 'try', 0
data ends

code segment
start:  
    mov ax, data
    mov ds, ax

    mov si, offset str //用offset找到str标记的偏移地址
    mov al, 2
    call printStr      //调用子程序

    mov ah, 4ch
    int 21h

printStr:
    push bx
    push cx
    push si
    push di            //保存寄存器

    mov bx, 0b800H     //显存的段地址
    mov es, bx
    mov di, 0
s:  mov cl, [si]
    mov ch, 0
    jcxz over          //如果cl为0,就表示字符串读取结束,那么cx为0,用jcxz跳出循环
    mov ch, al         //赋值颜色
    mov es:[di], cx
    inc si
    add di, 2
    jmp s

over:    pop di
    pop si
    pop cx
    pop bx
    ret

code ends
end start

汇编,运行代码,得到结果:

把line3改为:
str db 'another try', 0

这里只是将字符串改变,但是使用jcxz是当判断后面出现0才结束写入的循环,所以改变了显示的个数。

把line12改为:
mov al, 4

4为00000100b,所以是黑底红色,结果如下

line19-22, line36-39,这组对称使用的push、pop,这样用的目的是什么?
用于保护和恢复寄存器的值,更好的利用寄存器
line30的功能是什么?
将高位是颜色信息,低位是字符ASCLL信息的cx,存入显存。

3. 实验任务3

使用任意文本编辑器,录入汇编源程序task3.asm

assume cs:code, ds:data
data segment
        x dw 1984
        str db 16 dup(0)
data ends

code segment
start:  
        mov ax, data
        mov ds, ax
        mov ax, x           //将1984存入ax当被除数  07C0H=1984
        mov di, offset str
        call num2str

        mov ah, 4ch
        int 21h

num2str:
        push ax
        push bx
        push cx
        push dx         //保存寄存器
        
        mov cx, 0
        mov bl, 10      //赋值除数
s1:      
        div bl          //除法运算
        inc cx
        mov dl, ah      //取出余数
        push dx         //余数进栈
        mov ah, 0       //余数赋值0    因为这之前已经除过一次10了,而al能表示的最大数为255,所以这个代码能表示的范围是0-2599
        cmp al, 0       //如果商为0,表示你将数字全部单个取出入栈了,那么jne判断不成功就会执行下面的代码,而不会继续s1的循环了
        jne s1
s2:        
        pop dx
        or dl, 30h      //将数字转化成字符
        mov [di], dl    //存入数据(字符)
        inc di
        loop s2
        
        pop dx          //还原寄存器
        pop cx
        pop bx
        pop ax

        ret
code ends
end start
对task3.asm进行汇编、链接,得到可执行程序后,在debug中使用u命令反汇编,使用g命令执行到line15(程序退出之前),使用d命令查看数据段内容

查看出来的结果确实是1984的ASCLL值

对task3.asm源代码进行修改、完善,把task2.asm中用于输出以0结尾的字符串的子程序加进来,实现对转换后的字符串进行输出。
assume cs:code, ds:data
data segment
        x dw 2020
        str db 16 dup(0)
data ends

code segment
start:  
        mov ax, data
        mov ds, ax
        mov ax, x
        mov di, offset str
        call num2str
        mov si, offset str
        call  shuchu

        mov ah, 4ch
        int 21h

num2str:
        push ax
        push bx
        push cx
        push dx
        
        mov bx,0b800h
        mov es,bx
        mov cx, 0
        mov bl, 10
s1:      
        div bl
        inc cx
        mov dl, ah
        push dx
        mov ah, 0
        cmp al, 0
        jne s1
s2:        
        pop dx
        or dl, 30h
        mov [di], dl
        inc di
        loop s2
        
        pop dx
        pop cx
        pop bx
        pop ax

        ret
shuchu :
        push ax
        push bx
        push cx
        push dx
        
        mov bx, 0b800H
        mov es, bx
        mov di, 0
        mov al,2        //指定输出的是绿色黑底
s:     
        mov cl, [si]
        mov ch, 0
        jcxz over
        mov ch, al
        mov es:[di], cx
        inc si
        add di, 2
        jmp s

over:        
        pop di
        pop si
        pop cx
        pop bx
        ret
code ends
end start
结果:

 

上面是我指定了al为2的情况
如果不指定,在调用了转换成字符的子程序后,pop ax,将ax赋值成所输入的数字,那么此时的字符的颜色会随着输入的数字的变化而变化。

4. 实验任务4

使用任意文本编辑器,录入汇编源程序task4.asm。

assume cs:code, ds:data
data segment
        str db 80 dup(?)
data ends

code segment
start:  
        mov ax, data
        mov ds, ax
        mov si, 0

s1:        
        mov ah, 1
        int 21h         //读取一个字符,放到ah中
        mov [si], al
        cmp al, '#'      //如果输入的是#,那么停止输入,转跳到下面的循环输出语句,如果不是#,那么跳回s1进行输入
        je next          //使用的是ZF寄存器
        inc si
        jmp s1
next:
        mov cx, si
        mov si, 0
s2:     mov ah, 2
        mov dl, [si]
        int 21h      //输出
        inc si
        loop s2

        mov ah, 4ch
        int 21h
code ends
end start
汇编、链接、运行程序,输入一个字符串并以#结束
line12-19实现的功能是?
首先利用int 21h 的1号子功能读取一个字符,判断输入的字符是否为#,如果是,就用中断转移指令直接专挑到next,如果不是就循环回去读取字符并将其保存在数据段里.因为中断转移在inc si上面,所以接下来的循环不会输出#.
line21-27实现的功能是?
根据上面得到的 si(字符的长度)设置循环次数,然后利用int 21h 的2号子功能依次输出保存的字符.

5. 实验任务5

visual studio集成环境中,编写一个简单的包含有函数调用的c程序。代码如下:

#include <stdio.h>
int sum(int, int); 
int main() 
{ 
    int a = 2, b = 7, c;
    c = sum(a, b); 
    return 0; 
}
int sum(int x, int y)
{ 
    return (x + y); 
}

反汇编得到的结果:

首先进行初始化操作,进行变量声明,反汇编就会为其生成一条 mov指令为其赋值。调用函数时,实参参数自右向左,先把数据存入寄存器,在把他们进栈(先将参数b 的值存入寄存器eax,再压入堆栈,再将参数a 的值存入寄存器ecx,再压入堆栈。实现了实参赋值给形参,通过 call 指令调用函数sum。sum函数内同样先初始化并分配栈空间,执行得到的结果保存在eax寄存器内,调用ret指令返回。执行完毕,返回主函数main,再将eax值赋值给参数c。主调函数根据压入堆栈的参数的数目 2 和参数大小,利用指令 add esp,8 将参数全部弹出。此时堆栈就恢复到其调用前的状态。

  

 

  

 

  

  

 

  

  

posted @ 2020-12-17 22:16  Defot  阅读(150)  评论(0)    收藏  举报