DATA SEGMENT
AA1 DB 0C7H,24H
AA2 DB 0ACH,79H
BUF DW 2 DUP(?)
DATA ENDS
DATA SEGMENT
CODE SEGMENT ;代码段说明指令
ASSUME CS:CODE,DS:DATA
START: MOV AX,DATA
MOV DS,AX
LEA SI,AA1
LEA DI,AA2
MOV AL,[SI]
MOV BL,[DI]
CLC;将CF清零,因为下面的运算要用到CF的值
ADD AL,BL
INC SI
INC DI
MOV AH,[SI]
MOV BH,[DI]
ADC AH,BH
LEA DI,BUF
MOV [DI],AX
MOV AX,4C00H ;程序结束退出指令,不能少
INT 21H
CODE ENDS
END START
这程序就是为了实现两个为橡橡字数据的加法,激腊先将两个数的低八位取出相加,梁铅旁得到结果存入AL中,但这时要考虑到可能产生向高位产生进位,所以再进行加法前应该把CF清零,既加一句“CLD”,所以在第二次把两个数的高位取出相加时,要用ADC指令,既带进位加法指令,把低位向高位产生的进位加进去。
带注释的都是你给的程序中漏了的部分
代码+注释承上.386
.model flat,stdcall 这里我们用stdcall 就是函数参数 压栈的时候从最后一个开始压,和被调用函数负责清栈
option casemap:none区分大小写
includelib msvcrt.lib 这里是引入类库 相当于 #include<stdio.h>了
printf PROTO C:DWORD,:VARARG 这个就是声明一下我们要用的函数头,到时候 汇编程序会自动到msvcrt.lib里面找的了
:VARARG 表后面的参数不确定 因为C就是这样的printf(const char *, ...)
这样的函数要注意 不是被调用函数负责清栈 因为它本身不知道有多少个参数
而是有调用者负责清栈 下面会详细说明
.data
szTextFmt BYTE '%d',0这个是用来类型转换的,跟C的一样,字符用字节类型
a dword 1000 假设
b dword 2000 处理数值都用双字 没段山有int 跟long 的区别
//////////////////////////////////////迹肢///////////////////////////////////////////////////
.code
_test proc A:DWORD,B:DWORD
push ebp
mov ebp,esp
mov eax,dword ptr ss:[ebp+8]
add eax,1
mov edx,dword ptr ss:[ebp+0Ch]
add edx,100
add eax,edx
pop ebp
retn 8
_test endp
_main proc
push dword ptr ds:b 反汇编我们看到的b就不是b了而是一个[*****]数字 dword ptr 就是我们在ds(数据段)把[*****]
开始的一个双字长数值取出来
push dword ptr ds:a 跟她对应的还有 byte ptr ****就是取一个字节出来 比如这样 mov al,byte ptr ds:szTextFmt
就把 % 取出来 而不包括 d
call _test
push eax 假设push eax的地址是×××××
push offset szTextFmt
call printf
add esp,8
ret
_main endp
end _main
////////////////////////////////////////////////////////////// 下面介绍堆栈的变化
首先要明白的是 *** 作堆栈段 ss 只能用握州中 esp或ebp寄存器 其他的寄存器eax ebx edx等都不能够用 而 esp永远指向堆栈栈顶 ebp用来 在堆栈段
里面寻址
push 指令是压栈 ESP=ESP-4
pop 指令是出栈 ESP=ESP+4
我们假设main函数一开始堆栈定是 ESP=400
push dword ptr ds:b ESP-4=396 ->里面的值就是 2000 就是b的数值
push dword ptr ds:a ESP-4=392 ->里面的值就是 1000 就是a的数值
call test ESP-4=388->里面的数值是什么?这个太重要了 就是我们用来找游戏函数的原理所在。
里面的数值就是call test 指令下一条指令的地址->即push eax的地址×××××
到了test函数里面
push ebp ESP-4=384->里面保存了当前ebp的值 而不是把ebp清零
mov ebp,esp 这里ESP=384就没变化了,但是 ebp=esp=384,为什么要这样做呢 因为我们要用ebp到堆栈里面找参数
mov eax,dword ptr ss:[ebp+8] 反汇编是这样的 想想为什么a就是[ebp+8]呢
我们往上看看堆栈里地址392处就保存着a的值 这里ebp=384 加上8正好就是392了
这样就把传递过来的1000拿了出来eax=1000
add eax,1 相当于 a+1了 eax=1001
mov edx,dword ptr ss:[ebp+0Ch]0Ch=12 一样道理这里指向堆栈的地址是384+12=396 就是2000了 edx=2000
add edx,100 相当于 b+100 edx=2100
add eax,edx eax=eax+edx=1001+2100=3101 这里eax已经保存了最终的结果了
因为win32汇编一般用eax返回结果 所以如果最终结果不是在eax里面的话 还要把它放到eax
比如假设我的结果保存在变量nRet里面 最后还是要这样 mov eax,dword ptr nRet
pop ebp ESP=384+4=388 而保存在栈顶384的值 保存到 ebp中 即恢复ebp原来的值
因为一开始我们就把ebp的值压栈了,mov ebp,esp已经改变了ebp的值,这里恢复就是保证了堆栈平衡
retn 8ESP+8->396 这里retn是由系统调用的 我们不用管 系统会自动把EIP指针指向 原来的call的下一条指令
由于是系统自动恢复了call那里的压栈所以 真正返回到的时候ESP+4就是恢复了call压栈的堆栈
到了这个时候 ESP=400 就是函数调用开始的堆栈,就是说函数调用前跟函数调用后的堆栈是一样的
这就是堆栈平衡
由于我们用stdcall上面retn 8就是被调用者负责恢复堆栈的意思了,函数test是被调用者,所以负责把堆栈加8,call 那里是系统自动恢复的
push eaxESP-4=396->里面保存了eax的值3101
上面已经看到了eax保存着返回值,我们要把它传给printf也是通过堆栈传递
push offset szTextFmt ESP-4=392->里面保存了szTextFmt的地址 也就是C里面的指针 实际上没有什么把字符串传递的,我们传的都是地址
无论是在汇编或C 所以在汇编里没有什么字符串类型 用最多的就是DWORD。嘿嘿游戏里面传递参数 简单多了
call printf ESP-4=388->里面保存了下一条指令的地址
add esp,8 ESP+8=400 恢复了调用printf前的堆栈状态
上面说了由于printf后面参数是:VARARG 这样的类型是有调用者恢复堆栈的 所以printf里面没有retn 8之类的指令
这是由调用者负责清栈 main是调用者 所以下面一句就是 add esp,8 把堆栈恢复到调用printf之前
而call printf那里的压栈 是由系统做的 恢复的工作也是系统完成 我们不用理 只是知道里面保存是返回地址就够
了
ret main 函数返回 其他的事情是系统自动搞定 我们不用理 任务完成
我给你个辩野输入三个数,从小到大输出的:汇编:
inkeymacro nn
mov ah,1h
int 21h
sub al,30h
mov bl,nn
mul bl
add sum,ax
endm
data segment
sum dw 0
str1 dw 0
str2 dw 0
str3 dw 0
buff1db 0
bu2 db " 1 2 3 4 5 6 7 8 9 A", 0dh,0ah,"或斗$"
data ends
stacksegment para stack
db 100 dup(?)
stackends
code segment
assume cs:code,ds:data,es:data,ss:stack
sub1 proc far
start: push ds
mov ax,0
push ax
mov ax,data
mov ds,ax
mov es,ax
inkey 100 输入第一个数
inkey 10
inkey 1
mov ax,sum
mov str1,ax
mov sum,0
mov dl,"衫灶磨 "
mov ah,2h
int 21h
inkey 100 输入第二个数
inkey 10
inkey 1
mov ax,sum
mov str2,ax
mov sum,0
mov dl," "
mov ah,2h
int 21h
inkey 100 输入第三个数
inkey 10
inkey 1
mov ax,sum
mov str3,ax
mov dl,0dh
mov ah,2h
int 21h
mov dl,0ah
mov ah,2h
int 21h
mov ax,str1 三个数比较大小
cmp ax,str2
jae a7
jmp a8
a7: mov bx,str2
mov str2,ax
mov str1,bx
a8: mov ax,str2
cmp ax,str3
jae a9
jmp a11
a9: mov bx,str3
mov str3,ax
mov str2,bx
mov ax,str1
cmp ax,str2
jae a10
jmp a11
a10: mov bx,str2
mov str2,ax
mov str1,bx
a11: mov ax,str1
mov al,ah
call sub3
mov ax,str1
call sub3
mov dl,"H"
mov ah,2h
int 21h
mov dl," "
mov ah,2h
int 21h
xor ah,ah
mov ax,str2
mov al,ah
call sub3
mov ax,str2
call sub3
mov dl,"H"
mov ah,2h
int 21h
mov dl," "
mov ah,2h
int 21h
xor ah,ah
mov ax,str3
mov al,ah
call sub3
mov ax,str3
call sub3
mov dl,"H"
mov ah,2h
int 21h
ret
sub1 endp
SUB2 PROC
cmp al,9
ja g1
add al,"0"
jmp g2
g1: add al,37h
g2: mov dl,al
mov ah,2
int 21h
RET
SUB2 ENDP
SUB3 PROC
mov buff1,al
shr al,1
shr al,1
shr al,1
shr al,1
CALL SUB2
mov al,buff1
and al,0fh
CALL SUB2
RET
SUB3 ENDP
code ends
end start
高级(C语言):
#include<stdio.h>
void main()
{
int t
int a,b,c
printf("请输入三个整数:\n")
scanf("%d%d%d",&a,&b,&c)
if(a>b)
{
t=a
a=b
b=t
}//a,b交换
if(a>c)
{
t=a
a=c
c=t
}//a,c交换
if(b>c)
{
t=b
b=c
c=t
}//b,c交换
printf("从小到大输出:%d,%d,%d\n",a,b,c)
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)