各位大哥帮小弟做个汇编小程序拉,拜托拉,最好有流程图..

各位大哥帮小弟做个汇编小程序拉,拜托拉,最好有流程图..,第1张

DSEG SEGMENT

msgi db "please input a string:$"显示信息,提示输入一串字符

buf db 100,0,100 dup (?)输入字符串缓冲区

digit db 100 dup (?)数字字符缓冲区

leter db 100 dup (?)字母字符缓冲区

other db 100 dup (?)其它字符缓冲区

dn db ?数字字符个数

ln db ?字母字符个数

on db ?其它字符个数

msgd db 0dh,0ah,"digits are:$"显示信息,提示数字字符显示

msgl db 0dh,0ah,"leters are:$"显示信息,提示字母字符显示

msgo db 0dh,0ah,"others are:$"显示信息,提示其它字符显示

msgdn db 0dh,0ah,"digits:$"显示信息,提示数字字符个数显示

msgln db 0dh,0ah,"leter :$"显示信息,提示字母字符个数显示

msgon db 0dh,0ah,"other:$"显示信息,提示其它字符个数显示

DSEG ENDS

CSEG SEGMENT

assume cs:CSEG, ds:DSEG

MAIN PROC FAR 主程序入口

mov ax, dseg

mov ds, ax

lea dx,msgi显示信息,提示输入一串字符

mov ah,9

int 21h

lea dx,buf输入字符串

mov ah,0ah

int 21h

lea si,buf

mov cl,[si+1]输入字符实际个数存入CX中

mov ch,0

add si,2输入字符起始地址存入SI

lea bx,digit数字字符起始地址存入BX

lea di,leter字母字符起始地址存入DI

lea bp,other其它字符起始地址存入BP

fenlei:

mov al,[si]取出一个字符

cmp al,30h判断该字符是否为数字

jb oth

cmp al,39h

ja ulet

mov [bx],al若为数字字符,则存入相应缓冲区

inc bx

inc dn并将数字字符个数加1

jmp next

ulet:

cmp al,41h判断该字符是否为大写字母

jb oth

cmp al,5ah

ja llet

mov [di],al若为大写字母字符,则存入相应缓冲区

inc di

inc ln并将字母字符个数加1

jmp next

llet:

cmp al,61h判断该字符是否为小写字母

jb oth

cmp al,7ah

ja oth

mov [di],al若为小写字母字符,则存入相应缓冲区

inc di

inc ln并将字母字符个数加1

jmp next

oth:

mov ds:[bp],al为其它字符,则存入相应缓冲区

inc bp

inc on并将其它字符个数加1

next:

inc si调整地址,指向下一个字符

loop fenlei循环次数为实际输入字符个数

mov byte ptr [bx],'$'在数字字符串末尾加'$',目的用9号中断显示该串

mov byte ptr [di],'$'在字母字符串末尾加'$',目的用9号中断显示该串

mov byte ptr ds:[bp],'$'在其它字符串末尾加'$',目的用9号中断显示该串

lea dx,msgd显示数字字符

mov ah,9

int 21h

lea dx,digit

mov ah,9

int 21h

lea dx,msgl显示字母字符

mov ah,9

int 21h

lea dx,leter

mov ah,9

int 21h

lea dx,msgo显示其它字符

mov ah,9

int 21h

lea dx,other

mov ah,9

int 21h

lea dx,msgln显示数字字符个数

mov ah,9

int 21h

mov bl,dn

call disp以十进制形式显示个数

lea dx,msgln显示字母字符个数

mov ah,9

int 21h

mov bl,ln

call disp

lea dx,msgon显示其它字符个数

mov ah,9

int 21h

mov bl,on

call disp

mov ah,1按任意键退出

int 21h

mov ax, 4c00h 程序结束,返回到 *** 作系统系统

int 21h

MAIN ENDP

disp proc near

mov ch,2

rotate:

mov cl,4

rol bl,cl

mov al,bl

and al,0fh

add al,30h

cmp al,3ah

jl printit

add al,7h

printit:

mov dl,al

mov ah,2

int 21h

dec ch

jnz rotate

ret

disp endp

CSEG ENDS

END MAIN

代码+注释承上

.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 函数返回 其他的事情是系统自动搞定 我们不用理 任务完成

微信小程序金山表单多人填写后汇总步骤。

1、打开多人填写的金山表单。

2、点击数据工具,找到下拉菜单中的合并数据。

3、再找到多个工作簿合并成一个工作簿的选项。

4、点击即可完成。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/12133819.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-21
下一篇 2023-05-21

发表评论

登录后才能评论

评论列表(0条)

保存