32位汇编的代码的语法

本文详细介绍了32位汇编语言的关键概念,包括寄存器变化、分区概念、伪指令与硬指令的区别,以及如何使用伪指令进行函数声明和调用。同时,提供了32位汇编语言的基本语法示例,如结构体定义、条件判断和循环,并展示了如何通过调用win32api实现弹框功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

32位汇编与16位汇编的改变

1寄存器的变化

32位寄存器兼容16位寄存器,例如EAX 的低16位变为AX了,其余的寄存器同理
注意:现在32位中的段寄存器给操作系统使用,所以有了权限一说

EAX  EBX ECX EDX ESI EDI ESP EBP    ;八个寄存器,都是32位寄存器,占4个字节
EIP EFLAGES                         ;特殊寄存器
CS ES SS DS GS FS           ;其中GS FS是新增加的寄存器,这些段寄存器,并不是4个字节(32位的)还是以前16位的

注意:32位汇编内存是4G,而32位汇编寻址能力也是0-FFFFFFFF(2的32次方等于4G),所以32位没有分段的概念,但是有分区的概念,分区与分段不同就是多了保护属性

2分区

1常量区
2 数据区(初始化数据区和未初始化数据区)
3代码区 (没有栈区,栈区由编译器维护,编译器分配)

32位汇编的语法

32位汇编有伪指令的概念
硬指令:硬指令是在程序运行期间由计算机来执行的,如push ,mov等指令
伪指令:伪指令(伪操作)是在汇编程序对源程序进行汇编时处理的操作,完成诸如数据定义、存储器分配、指示程序开始结束等功能 如.model .code等

1伪指令

.386    ;定义指令集
.model FLAT,STDCALL ;内存平坦模式(表示内存是连续的,因为不能分段了) ,设置了调用约定以后声明函数就不需要设置调用约定了
option casemap:none		;告诉编译器程序大小写敏感,因为win32API大小写敏感

;定义常量区
.const
;定义数据区
.data 
.data?;?号表示未初始化
    g_data dw ? ;数据的申请必须是? 也就是未初始化的
    ;两者的区别
  ;初始化的数据,不过你定义数据的时候,是否给? 都会写的EXE(PE文件中)
  ;未初始化的数据, 定义数据的时候只能给? 不在PE文件中保存
;定义代码区
.code

;使用伪指令proto函数声明
MyAdd PROTO stdcall n1:dword,n2:dword
;函数调用
invoke MyAdd,1,2
;函数实现,参数是n1,n2,指明的大小是DWORD(4个字节的),这里没有写调用约定,上面写了默认的调用约定了,也可以加调用约定
MyAdd PROC n1:dword,n2:dword 
mov eax,n1
add eax,n2
ret
MyAdd endp  ;函数定义的结束标志

语法

列出一些基本语法,具体的语法自行百度

;定义结构体类型
Date STRUCT 
  year  dd ?
  month dd ?
  day   dd ?
Date ends
;数据区
.data
g_data dw 0	;定义全局变量
;代码区
.code
START proc	;函数入口点
	local @date:Date	;定义结构体局部变量
  	mov  @date.year, 1	;初始化结构体
  	mov  @date.month, 2
  	mov  @date.day, 3
	
	;条件
	.if n1>n2 && n1>n3
		代码
	.endif
	
	;循环
	.while TRUE
	.endw


START endp
end START

32位汇编弹框例子

写32位汇编和写c差不多,只要有库和头文件就可以调用,我是配置好了lib,所以直接包含库就可以调用win32 api

.386                 ;386指令集
.model FLAT,STDCALL  ;内存平坦, 
OPTION CASEMAP:none  ;区分大小写

;包含库
includelib user32.lib
includelib kernel32.lib

MB_OK equ 0
NULL  equ 0

MessageBoxA proto stdcall  ,:dword,:dword,:dword,:dword     ;函数声明
ExitProcess proto stdcall , :dword

;常量区
.const
  TITLE1  db "Win32", 0
  MSG1    db "Hello World!", 0
;代码区
.code
START proc
	invoke MessageBoxA, NULL, offset MSG1, offset TITLE1, MB_OK
  	call ExitProcess
START endp
end START

代 码 风 格(1) 随着程序功能的增加和版本的提高,程序越来越复杂,源文件也越来越多,风格规范的源程序会对软件的升级、修改和维护带来极大的方便,要想开发一个成熟的软件产品,必须在编写源程序的时候就有条不紊,细致严谨。 在编程中,在程序排版、注释、命名和可读性等问题上都有一定的规范,虽然编写可读性良好的代码并不是必然的要求(世界上还有难懂代码比赛,看谁的代码最不好读懂!),但好的代码风格实际上是为自己将来维护和使用这些代码节省时间。本节就是对汇编语言代码风格的建议。 变量和函数的命名 1. 匈牙利表示法 匈牙利表示法主要用在变量和子程序的命名,这是现在大部分程序员都在使用的命名约定。“匈牙利表示法”这个奇怪的名字是为了纪念匈牙利籍的Microsoft程序员Charles Simonyi,他首先使用了这种命名方法。 匈牙利表示法用连在一起的几个部分来命名一个变量,格式是类型前缀加上变量说明,类型用小写字母表示,如用h表示句柄,用dw表示double word,用sz表示以0结尾的字符串等,说明则用首字母大写的几个英文单词组成,如TimeCounter,NextPoint等,可以令人一眼看出变量的含义来,在汇编语言中常用的类型前缀有: b 表示byte w 表示word dw 表示dword h 表示句柄 lp 表示指针 sz 表示以0结尾的字符串 lpsz 表示指向0结尾的字符串的指针 f 表示浮点数 st 表示一个数据结构 这样一来,变量的意思就很好理解: hWinMain 主窗口的句柄 dwTimeCount 时间计数器,以双字定义 szWelcome 欢迎信息字符串,以0结尾 lpBuffer 指向缓冲区的指针 stWndClass WNDCLASS结构 … 很明显,这些变量名比count1,abc,commandlinebuffer和FILEFLAG之类的命名要易于理解。由于匈牙利表示法既描述了变量的类型,又描述了变量的作用,所以能帮助程序员及早发现变量的使用错误,如把一个数值当指针来使用引发的内存页错误等。 对于函数名,由于不会返回多种类型的数值,所以命名时一般不再用类型开头,但名称还是用表示用途的单词组成,每个单词的首字母大写。Windows API是这种命名方式的绝好例子,当人们看到ShowWindow,GetWindowText,DeleteFile和GetCommandLine之类的API函数名称时,恐怕不用查手册,就能知道它们是做什么用的。比起int 21h/09h和int 13h/02h之类的中断调用,好处是不必多讲的。 2. 对匈牙利表示法的补充 使用匈牙利表示法已经基本上解决了命名的可读性问题,但相对于其他高级语言,汇编语言有语法上的特殊性,考虑下面这些汇编语言特有的问题: ● 对局部变量的地址引用要用lea指令或用addr伪操作,全局变量要用offset;对局部变量的使用要特别注意初始化问题。如何在定义中区分全局变量、局部变量和参数? ● 汇编的源代码占用的行数比较多,代码行数很容易膨胀,程序规模大了如何分清一个函数是系统的API还是本程序内部的子程序? 实际上上面的这些问题都可以归纳为区分作用域的问题。为了分清变量的作用域,命名中对全局变量、局部变量和参数应该有所区别,所以我们需要对匈牙利表示法做一些补充,以适应Win32汇编的特殊情况,下面的补充方法是笔者提出的,读者可以参考使用: ● 全局变量的定义使用标准的匈牙利表示法,在参数的前面加下划线,在局部变量的前面加@符号,这样引用的时候就能随时注意到变量的作用域。 ● 在内部子程序的名称前面加下划线,以便和系统API区别。 如下面是一个求复数模的子程序,子程序名前面加下划线表示这是本程序内部模块,两个参数——复数的实部和虚部用_dwX和_dwY表示,中间用到的局部变量@dwResult则用@号开头: _Calc proc _dwX,_dwY local @dwResult finit fild _dwX fld st(0) fmul ;i * i fild _dwY fld st(0) fmul ;j * j fadd ;i * i + j * j fsqrt ;sqrt(i * i + j * j) fistp @dwResult ;put result mov eax,@dwResult ret _Calc endp 本书中所有的示范源代码采用的都是这样的命名约定。 代码的书写格式 1. 排版方式 程序的排版风格应该遵循以下规则。 首先是大小写的问题,汇编程序中对于指令和寄存器的书写是不分大小写的,但小写代码比大写代码便于阅读,所以程序中的指令和寄存器等要采用小写字母,而用equ伪操作符定义的常量则使用大写,变量和标号使用匈牙利表示法,大小写混合。 其次是使用Tab的问题。汇编源程序中Tab的宽度一般设置为8个字符。在语法上,指令和操作数之间至少有一个空格就可以了,但指令的助记符长度是不等长的,用Tab隔开指令和操作数可以使格式对齐,便于阅读。如: xor eax,eax fistp dwNumber xchg eax,ebx 上述代码的写法就不如下面的写法整齐: xor eax,eax fistp dwNumber xchg eax, ebx 还有就是缩进格式的问题。程序中的各部分采用不同的缩进,一般变量和标号的定义不缩进,指令用两个Tab缩进,遇到分支或循环伪指令再缩进一格,如: .data dwFlag dd ? .code start: mov eax,dwFlag .if dwFlag == 1 call _Function1 .else call _Function2 .endif … 合适的缩进格式可以明显地表现出程序的流程结构,也很容易发现嵌套错误,当缩进过多的时候,可以意识到嵌套过深,该改进程序结构了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值