关于MASM汇编的⼀点点备忘
起因
前段时间帮我的⼩狗我的⼥朋友写汇编作业,很多东西没有和她解释清楚主要是当时我也不怎么清楚导致验收的时候发⽣了⼀些不愉快的事情,所以整理了这篇随笔,梳理了⼀遍x86汇编的流程和基础⽤法,于我⽽⾔也作备忘之⽤。
题⽬要求
编写⼀个程序,在⽆符号数组中查从键盘输⼊的⽆符号数,若存在则输出该数在数组中的位置和该数,若不存在则输出错误信息。
例:数组array=[1,23,4,5,6,7,8,9]
输⼊:4
输出:3 : 4
先上源代码吧
.
386
.model flat, stdcall
.stack 4096
ExitProcess PROTO, dwexitcode:Dword
INCLUDE Irvine32.inc
INCLUDE macros.inc
includelib Irvine32.lib
.data
array dword 88, 5, 1, 443, 21, 310, 4, 75, 27, 0
inputNum dword ?
msg byte "The input number is not in the array!", 0
promptBad byte "Invalid input, please enter again", 0
split byte " : ", 0
.code
main PROC
mov edi, 0
mov ecx, lengthof array ;lengthof 得到数组元素个数
read:
call ReadDec
jnc goodInput
mov edx, OFFSET promptBad
call WriteString;
jmp read
goodInput:
mov inputNum, eax ;store input value
L1 :
mov ebx, [array + edi] ;[]意为解析操作,取出该地址⾥的值
cmp ebx, inputNum
je Find
add edi, type array ;type得到元素长度(字节),加进edi,得到下⼀个元素地址
loop L1
jmp notFind
Find:
mov eax, edi
cdq
mov ebx, type array
div ebx
inc eax
call WriteDec ;打印eax寄存器⾥的值(数字位置)(数字 : 数字)
mov eax, [array + edi]
mov edx, offset split
call WriteString
call WriteDec
jmp ed
notFind:
mov edx, offset msg
call WriteString
jmp ed
ed:
exit
main ENDP
END main
masm汇编需要注意的⼀点是,它是⼤⼩写不敏感的,也就是说END和end是⼀个意思,并且汇编语⾔注释是;开始,⼀⾏代码没有结束符号。前7⾏是32位汇编语⾔起⼿式:
.386表⽰这是个32位程序,能访问32位寄存器和地址,.model flat, stdcall选择了程序的运⾏模式(flat),并确定了⼦程序的调⽤规范(stdcall),.stack 4096声明了该程序运⾏时堆栈的存储空间,ExitProcess P
ROTO, dwexitcode:Dword声明了ExitProcess函数原型,当程序运⾏结束时,程序就调⽤该函数,向操作系统返回⼀个整数表⽰该程序运⾏良好(相当于C语⾔return 0),后⾯三⾏引⼊了Irvine32库,⾥⾯封装了⼀些很⽅便的操作,如下⽂的WriteString(相当于C语⾔中#include <stdio.h>)
.data:
表⽰接下来是变量声明区,汇编的变量声明格式为:变量数据类型初始值,在本程序中,只⽤到了dword,byte两个数据类型,dword的d 为double之意,意为“双字”,word代表⽆符号16位整数,那么dword就是⽆符号32位整数,byte意为字节,即是⽆符号8位整数,在汇编⾥,数据类型的长度都是确定的,不会出现如C语⾔中int在32位,64位机器上是32位,16位机器上是16位的情况。
当暂时不需要设置初始值时,可以将初始值设为?,初始值可以⼀个,也可以若⼲个(构成数组),像上⽂程序⾥的array就声明了若⼲个初始值,也就是说,变量名仅仅表⽰第⼀个初始值的地址,⽐如在上⽂程序⾥,array表⽰数字88的地址,88后⾯的数字的地址紧随88地址之后,⼀个接⼀个,构成了数组。既然这样,那么字符串也可以像这样表⽰,例如msg byte "The input number is not in the array!", 0因为⼀个英⽂字符占1个字节,所以⽤byte类型即可,msg就表⽰后⾯的字符串中第⼀个字符"T"的地址,为什么最后有个0呢,记得C语⾔⾥字符串以'\0'结尾吗,这⾥的0,就是字符串的结束标志。
.code:
表⽰接下来是代码区,main PROC表⽰接下来是main函数,⼀直到main ENDP结束(类似C语⾔⾥的main函数),接下来程序⼀⾏⼀⾏向下运⾏。
解释⼀下⼏个关键寄存器在代码段⾥的作⽤:
edi:表⽰遍历时,遍历到的数组元素的地址
ecx:记录数组元素的个数(循环时控制循环次数⽤)
PS. 不同名字寄存器在计算机中有不同的位置和擅长领域,如eax是“累加器”,ebx是“基地址寄存器”等等,但在汇编代码编写时,我们也可平等视之,像本⽂代码的edi寄存器的选择,并没有特殊的考虑。
解释⼀下⼏个Irvine库⾥的函数:
ReadDec读取⼀个10进制数进eax
WriteDec将eax⾥的数以10进制形式输出到控制台
WriteString输出字符串到控制台,此字符串的偏移量(有效地址)需要事先存进edx寄存器中
接下来需要着重说明⼀下的是汇编语⾔的条件跳转操作和除法操作
x86指令集中没有明确的⾼级逻辑结构,但是可以⽤⽐较和跳转的组合实现他们:
第⼀步:⽤cmp,add,sub等操作修改CPU状态标志位;
第⼆步:使⽤合适的条件跳转指令来测试标志位,然后产⽣⼀个新地址的分⽀
例如,在上⾯的代码中,有cmp ebx, inputNum je Find这两句指令,je为相等跳转,也叫为零跳转(CPU零标志位置1),当cmp的两个操作数相等时(也就是ebx与inputNum相等时)发⽣跳转,跳转到下⽂的的标志Find
除法操作:在32位模式下,div指令执⾏8,16,32位⽆符号除法,idiv执⾏有符号除法
下⾯是被除数,除数,商和余数之间的关系:
被除数除数商余数
ax reg/mem8al ah
dx:ax reg/mem16ax dx
edx:eax reg/mem32eax edx
例如在上⽂的代码中,需要⽤元素在数组中的偏移量 / 每个元素所占字节数得到该元素在数组中的位置
mov eax, edi
mov eax, edi
cdq
mov ebx, type array
div ebx
先将edi⾥的总字节数存⼊eax中,再⽤cdq将eax扩展到edx
接着⽤ebx存储⼀个元素所占字节数字符串数组怎么转成byte
最后经过div ebx操作,eax⾥存的就是该元素在数组中的位置了(例如数组[88, 5, 1, 443, 21, 310, 4, 75, 27, 0],443在数组中的位置就是3(从0开始))
The END
⼤致内容就是这些,本⼈能⼒有限,如有谬误,欢迎在评论区批评指正
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论