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

实验内容

1.实验内容1

教材「实验9 根据材料编程」(P187-189) 编程:在屏幕中间分别显示绿色、绿底红色、白底蓝色的字符串'welcome to masm!'。

程序正确编写后,预期输出结果如下:

 

在内存地址空间中,B8000H~BFFFFH供32KB的空间,为80×25彩色字符模式的显示缓冲区。该显示缓冲区分为8页,每页4KB(≈4000B),每页有25行,一行可显示80个字符,一个字符占两个字节,共160个字节,低位字节存储字符的ASCII码,高位字节存储字符的颜色属性。

屏幕中间对应的显存位置的计算:以B800H作为段地址,要在屏幕中间显示三行字符串,这三行字符串应分别显示在第12、13、14行。要显示的字符串占16个字节,每个字符的颜色属性占16个字节,共32个字节,因为一行有160个字节,所以要让字符显示在屏幕中间,应从每行的第33列,即第64个字节开始存放字符及其颜色属性。第12行的开始处的偏移地址为1760,再加上64,等于1824,转换为十六进制就是720H。所以要显示的第一行字符串的起始位置偏移地址应为720H,加上160个字节就是第二行字符串起始位置的偏移地址,加上320个字节就是第3行字符串的起始位置的偏移地址。

字符串颜色属性的设置:根据属性字节的格式即可按位设置属性字节

黑底绿字:属性字节为00000010B,十六进制为02H;

绿底红字:属性字节为00100100B,十六进制为24H;

白底蓝字:属性字节为01110001B,十六进制为71H。

 代码:

assume  cs:code, ds:data

data segment
    db 'welcome to masm!'
data ends

code segment
start:    mov ax,data
    mov ds,ax
    mov ax,0b800h
    mov es,ax
    mov si,0
    mov bx,1824
    mov cx,16

s:    mov ah,2h;
    mov al,[si]
    mov es:[bx],ax
    mov ah,24h
    mov es:[bx+160],ax
    mov ah,71h
    mov es:[bx+320],ax
    inc si
    add bx,2
    loop s

    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
    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
    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改为:

1 str db 'Darth Vader wins!', 0

将line12改为:

1 mov  al,4

再次汇编连接,运行并观察结果:

 

 

line19-22, line36-39,这组对称使用的push、pop,这样用的目的是什么?

  bx、cx、si、di寄存器在子程序中需要使用,将会改变它们的值。为了保存现场,不使调用程序出现问题,故用栈保存这些寄存器的值,在开始操作前压入栈中,在返回前弹出。这样,这些寄存器的值在调用子程序之前和之后将保持不变。

  line31的功能是什么?

  line27将[si],即字符信息写入了cl寄存器当中。line30将al,即色彩信息写入了ch寄存器。此时,cx中的数据即为完整的显示单元(字符+颜色)。故line31将cx写入es:[di],即显存中。因此,line31的功能为将组合完毕的显示内容写入显存,显示在屏幕上。

3.实验任务三

1.子任务1

汇编、链接task3.asm,并使用debug进入调试。

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
        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
        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
code ends
end start

使用u命令进行反汇编,可见应执行至076C:000E。使用g E命令执行至076C:000E,如下图:

 

 使用-g 000e后:

 

 可以发现DI = 0006

使用-d 2 5 查看内存中的元素:

 

 可见,程序成功实现了将数字转换为字符串的功能。

2.子任务2

修改后源程序如下:

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
    mov di, offset str

    call num2str

    mov si, offset str
    mov al, 2
    call printStr

    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
    cmp al, 0
    jne s1
s2:
    pop dx
    or dl, 30h
    mov [di], dl
    inc di
    loop s2

    mov byte ptr [di], 0
    pop dx
    pop cx
    pop bx
    pop ax

    ret

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
    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

  其中,line16、line17的功能是为printStr子程序提供必要的输入信息。line46的功能为在转换完毕的字符串后添加0,构造为字符串。printStr子程序本身与任务2中一致。

  运行结果如下:

 

 改变数据,再次测试,如下图:

 

 可见,程序实现了所需的功能。

4.实验任务4

程序源代码如下:

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
        mov [si], al
        cmp al, '#'
        je next
        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实现的功能是?

答:循环读入从键盘输入的字符,遇到‘#’停止,最大长度不得超过80。

line21-27实现的功能是?

答:把si的值赋给cx,表明循环次数,然后把读入的字符再重新输出在屏幕上。

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);
}

反汇编的代码:

#include <stdio.h>
int sum(int, int);
int main() {
001717B0  push        ebp
001717B1  mov         ebp,esp
001717B3  sub         esp,0E4h
001717B9  push        ebx
001717BA  push        esi
001717BB  push        edi
001717BC  lea         edi,[ebp-0E4h]
001717C2  mov         ecx,39h
001717C7  mov         eax,0CCCCCCCCh
001717CC  rep stos    dword ptr es:[edi]
001717CE  mov         ecx,offset _XG17926B_demo@cpp (0FFC003h)
001717D3  call        @__CheckForDebuggerJustMyCode@4 (0FF130Ch)
    int a = 2, b = 7, c;
001717D8  mov         dword ptr [a],2   ;
001717DF  mov         dword ptr [b],7   ;
    c = sum(a, b);
001717E6  mov         eax,dword ptr [b] ;
001717E9  push        eax               ;
001717EA  mov         ecx,dword ptr [a] ;
001717ED  push        ecx               ;
001717EE  call        sum (017116Dh)    ;
001717F3  add         esp,8
001717F6  mov         dword ptr [c],eax ;
    return 0;
001717F9  xor         eax,eax  ;
}
001717FB  pop         edi
001717FC  pop         esi
001717FD  pop         ebx
001717FE  add         esp,0E4h
00171804  cmp         ebp,esp
00171806  call        __RTC_CheckEsp (0171235h)
0017180B  mov         esp,ebp
0017180D  pop         ebp
0017180E  ret

感悟:

汇编语言中,高级语言的参数传递是通过栈实现的。返回值则通过寄存器eax。

具有多个参数时,入栈顺序为自右向左入栈。函数调用栈使用EBP、ESP寄存器记录,入栈顺序为:实参N~1→主调函数返回地址→主调函数帧基指针EBP→被调函数局部变量1~N。

 二.实验总结

 

posted @ 2020-12-18 12:23  艾瑞克Aric  阅读(189)  评论(2)    收藏  举报