masm32汇编

masm32下载


〇、环境配置

  1. 下载安装:

    • 注 Win10 下可能安装失败。(兼容打开,或关闭防火墙)
  2. 环境配置:

变量名 变量值
include D:\masm32\include
lib D:\masm32\lib
PATH D:\masm32\bin
  1. 执行:

    • 编译:ml /c /coff eg.asm(源程序:*.asm,包含文件:*.inc(列表文件:*.lst))

    • 链接:link /subsystem:console eg.obj [eg.lib...]

    • 指明库文件编译:link /OUT:eg.lib eg.obj(目标模块文件:*.obj,库文件:*.lib

事先设置好后使用:MAKE32.BAT 1.asm
设置如下
@echo off
REM make32.bat,for assembling and linking 32-bit Console programs(.exe)
ml /c /coff /Fl /Zi %1.asm
if errorlevel 1 goto terminate
link /subsystem:console /debug %1.obj
if errorlevel 1 goto terminate
:terminate
@echo on

一、masm32汇编

1 基础汇编

1.1 寄存器

text::基础汇编

text::计算机组成原理

扩展寄存器:EAX,EBX,ECX,EDX,ESI,EDI,EBP,ESP,EIP

未扩展寄存器:CS,DS,ES,FS,GS

1.2 头文件

伪指令 功能 伪指令 功能
.8086 仅接受8086指令(缺省状态) .387 接受80387数学协处理器指令
.186 接受80186指令 .No87 取消使用协处理器指令
.286 接受除特权指令外的80286指令 .586 接受除特权指令外的Pentium指令
.286P 接受全部80286指令,包括特权指令 .586P 接受全部的Pentium指令
.386 接受除特权指令外的80386指令 .686 接受除特权指令外的Pentium Pro指令
.386P 接受全部80386指令,包括特权指令 .686P 接受全部Pentium Pro指令
.486 接受除特权指令外的80486指令,包括浮点指令 .MMX 接受MMX指令
.486P 接受全部80486指令,包括特权指令和浮点指令 .K3D 接受AMD处理器的3D指令
.8087 接受8087数学协处理器指令 .XMM 接受SSE,SSE2和SSE3指令
.287 接受80287数学协处理器指令

1.3 io32.inc中的输入输出

C语言格式符 子程序名 参数 常用功能
printf(“%s”,a) dispmsg 入口:EAX=字符串地址 显示字符串(以0结尾)
printf(“%c”,a) dispc 入口:AL=ASCII码 显示一个字符
printf(“\n”) dispcrlf 光标回车换行,到下一行首位置
disprd 显示8个32为通用寄存器内容(十六进制)
disprf 显示6个状态标志的状态
print(“%X”,a) disphd 入口:EAX=32b数据 以16进制显示8位数据
print(“%u”,a) dispuid 入口:EAX=32b数据 显示无符号十进制数
print(“%d”,a) dispsid 入口:EAX=32b数据 显示有符号十进制数
scanf(“%s”,&a) readmsg 入口:EAX=缓冲区地址。出口:EAX=实际输入的字符个数(不含结尾0),字符串以0结尾 输入一个字符串(回车结束)
scanf(“%c”,&a) readc 出口:AL=ASCII码 输入一个字符(回显)
scanf(“%X”,&a) readhd 出口:EAX=32b数据 输入8为十六进制数
scanf(“%u”,&a) readuid 出口:EAX=32b数据 输入无符号十进制数
scanf(“%d”,&a) readsid 出口:EAX=32b数据 输入有符号十进制数

2 数据表示与寻址

2.1 伪指令

;等价EQU,等号=
;EQU:给符号名定义一个数值或另一个字符串,甚至可以是处理器指令
;EQU用于数值等价时不能重复定义符号名,但是'='可以
符号名 EQU 数值表达式
符号名 EQU <字符串>
符号名 = 数值表达式

2.2 变量定义

定义

  • 变量 变量定义伪指令 处置表
  • 变量定义伪指令 重复次数 dup(重复参数)

除了如下定义汇编还支持:Structure(结构),Record(记录),Union(联合)

老版 新版 作用
db byte 1B
dw word 2B
dd dword 4B
df fword 6B,多用于16b段选择器和32b偏移地址的48b指针地址
dq qword 8B
dt tbyte 10B,多用于BCD码,浮点运算

2.3 变量指令

属性 操作符 作用
地址 [] 将括起的表达式作为存储器地址指针
$ 返回当前偏移地址
offset 变量名 返回变量名所在段的偏移地址
seg 变量名 返回段基地址(实地址存储模型)
类型 类型名 ptr 变量名 将变量名按照指定的类型使用
type 变量名 返回一个字量数值,表明变量名的类型
lengthof 变量名 返回整个变量的数据项数(即元素数)
sizeof 变量名 返回整个变量占用的字节数
;伪指令,将参数表达的偏移地址作为当前偏移地址
org para

;伪指令,对齐,N是对齐数(2的平方)
align N
;伪指令,偶对齐(相当于align 2)
even

3 通用数据处理指令

3.1 数据操作指令

;数据交换
xchg si,di

;获得偏移地址(offset在编译阶段就获得了,但是lea可以动态获得)
lea esi,label

;换码指令([AL]=[EBX+AL]),用于字典
xlat

;求补指令([rem]=0-[rem])
neg rem

;有符号乘法
imul reg/mem
;双/三操作数乘法
imul reg,reg/mem/imm
imul reg,reg/mem,imm

;有符号除法
idiv reg/mem

;零扩展位(后者一定小于前者,前面补0)
movzx r16,r8
movzx r32,r16
;符号扩展位(带符号扩展,负数前面补全1,整数补全0)
movsx r16,r8
movsx r32,r16
;扩展指令集(i8086只有CBW和CWD,其他包括movzx都没有)
CBW ;al符号扩展ax
CWD ;ax符号扩展dx和ax
CWDE ;ax符号扩展eax
CDQ ;eax符号扩展edx和eax

;非
not reg/mem
;异或
xor reg,mem
;测试,逻辑与(只改变状态标志)
test reg,mem

;算术移位(负数右移时前面补1)
sal reg
sar reg

;循环移位
rol reg,cl ;不带进位左移
ror reg,cl ;不带进位右移
rcl reg,cl ;带进位左移
rcr reg,cl ;带进位右移

3.2 标志位操作

指令 功能 指令 功能
clc 复位进位标志:CF=0 lahf 标志寄存器低字节内容传送到AH中
stc 置位进位标志:CF=1 sahf AH内容传送到标志寄存器最低位
cmc 求反进位标志:0变1,1变0 pushf 标志寄存器地16位入栈
cld 复位方向标志:DF=0,串操作后地址增大 popf 堆栈顶部一个字量数据弹出到标志寄存器低16位
std 复位方向标志:DF=1,串操作后地址减小 pushfd 32位标志寄存器内容全部压入堆栈
cli 复位中断标志:IF=0,禁止可屏蔽中断 popfd 当前堆栈顶部一个双字数据弹出到标志寄存器
sti 置位中断标志:IF=1,允许可屏蔽中断
;取ZF标志位值存入寄存器中(用于 == )
sete mem
setz mem
;取ZF标志位值再取反存入寄存器中(用于 != )
setne mem
setnz mem

;当ZF == 0 && SF == 0 && OF == 1时 mem = 1(用于 > )
setg mem
;当sf == 1 || OF == 1时 mem = 1(用于 < )
setl mem
;当SF == 0F时 mem = 1(用于 >= )
setge mem
;当ZF == 1 || SF != OF时 mem = 1(用于 <= )
setle mem

4 程序结构

4.1 跳转

助记符 标志位 英文含义 中文说明
JZ / JE ZF=1 Jump if Zero / Equal 等于零/相等
JNZ / JNE ZF=0 Jump if Not Zero / Not Equal 不等于零/不相等
JS SF=1 Jump if Sign 符号为负
JNS SF=0 Jump if Not Sign 符号为正
JP / JPE PF=1 Jump if Parity / Parity Even 1的个数为偶
JNP / JPO PF=0 Jump if Not Parity / Parity Odd 1的个数为奇
JO OF=1 Jump if Overflow 溢出
JNO OF=0 Jump if Not Overflow 无溢出
JC / JB / JNAE CF=1 Jump if Carry / Below / Not Above or Equal 进位/低于/不高于等于
JNC / JNB / JAE CF=0 Jump if Not Carry / Not Below / Above or Equal 无进位/不低于/高于等于
JBE / JNA CF=1 or ZF=1 Jump if Below or Equal / Not Above 低于等于/不高于
JNBE / JA CF=0 or ZF=0 Jump if Not Below or Equal / Above 不低于等于/高于
JL / JNGE SF != OF Jump if Less / Not Greater or Equal 小于/不大于等于
JNL / JGE SF=OF Jump if Not Less / Greater or Equal 不小于/大于等于
JLE / JNG SF != OF or ZF=1 Jump if Less or Equal / Not Greater 小于等于/不大于
JNLE / JG SF=OF and ZF=0 Jump if Not Less or Equal / Greater 不小于等于/大于

4.2 分支循环

;ecx=0跳转,否则向下
jcxz/jecxz label

4.3 结构体

;定义结构体
Snake struct
len dword ?
x dword ?
y dword ?
Snake ends
;初始化
sn1 Snake <5,2,3>
;预留空间
Snake 100 dup(<>)
;字段引用
mov sn1.len,100
;匿名寻址
mov [ebx].Snake.x,eax

5 模块化编程

5.1 源程序框架

Win32汇编基本编程框架

; 源程序框架
.686 ;编译模式
.model flat,stdcall ;默认模式
option casemap:none ;编译器大小写不敏感

include io32.inc ;头文件

.stack [堆栈段大小]
.data
msg dword 1 ;初始化变量
.data?
msg dword ? ;未初始化变量
.const
...;常量定义
.code
... ;普通代码
start:
... ;执行代码
[exit 0] ;退出代码
end start

5.2 函数传参

ret      ;无参返回:出栈返回地址
ret i16 ;带参返回:出栈返回地址,ESP=ESP+i16

;传参方式
;1.寄存器传参,返回可用寄存器返回(一般是EAX)
;2.堆栈传参,返回一般不用堆栈
;3.内存传参(指针)
;4.多参数传参:将参数地址存入堆栈,依次取得堆栈数据

5.3 头文件

;引用头文件
include *.inc
;引用库文件
includelib *.lib

public 标识符[,标识符...] ;可被外部程序使用
extern 标识符:类型[,标识符:类型...] ;数据来自外部程序
;案例
;主程序
public write
extern temp:dword
;子程序
extern rdhd:near
public temp

5.4 宏定义

;;宏
;定义宏
宏名 macro[形参表]
...
endm
;使用宏
宏名[实参表]
;在汇编时,宏指令被汇编程序用宏定义的代码序列替代。

;宏的局部标号伪指令(必须是marcro语句后的第一条语句,两者间不能有分号或注释)
local 标号列表

;删除宏名表(逗号隔开宏名)
purge 宏名表
;立刻停止后面部分的宏展开
extim

;;重复汇编
;参数值重复
repeat 重复次数
...
endm

;参数个数重复
for 形参,<实参表>
...
endm
;案例
for regard,<eax,ebx,ecx,edx>
push regad
endm
;=>
push eax
push ebx
push ecx
push edx

;按字符个数
forc 形参,字符串
...
endm
;案例(结果同理上,pop edx...)
forc regad,dcba
pop e&regad&x
endm

;条件汇编(按条件汇编指定语句)
ifxx 表达式
分支语句体1
[else
分支语句体2]
endif
宏操作符 作用
& 替换操作符,用于将参数与其他字符分开。如果参数紧接在其他字符之前或之后,或者参数出现在带引号的字符串中,就必须使用该伪操作符
<> 字符串传递操作符,用于括起字符串。在宏调用中,如果传递的字符串实参数含有逗号、空格等间隔符号、则必须用这对操作符,以保证字符串的完整
! 转义操作符,用于指示其后的一个字符作为一般字符,而不含特殊意义
% 表达式操作符,用在宏调用中,表示将后跟的一个表达式的值作为实参,而不是将表达式本身作为参数
;; 宏注释符,用于表示在宏定义中的注释。采用这个符号的注释,在宏展开时不出现
: reg 说明宏定义设定的参数在调用时不可缺少
: =默认值 设定参数默认值
格式 功能说明
if 表达式 汇编程序求出表达式的值,不为0满足条件
ife 表达式 汇编程序求出表达式的值,为0满足条件
ifdef 符号 符号已定义(内部定义或声明外部定义),则满足条件
ifndef 符号 符号未定义,则满足条件
ifb <形参> 用在宏定义体内。如果宏调用没有用实参替代该形参,则满足条件
ifnb <形参> 用在宏定义体内。如果宏调用用实参替代该形参,则满足条件
ifidn <字符串1>,<字符串2> 字符串1与字符串2相同则条件满足;区分大小写
ifdif <字符串1>,<字符串2> 字符串1与字符串2不相同则条件满足;区分大小写
ifidni <字符串1>,<字符串2> 字符串1与字符串2相同则条件满足;不区分大小写
ifdifi <字符串1>,<字符串2> 字符串1与字符串2不相同则条件满足;不区分大小写

5.5 简单案例

;弹窗显示hello world
.386
.model flat, stdcall
option casemap:none

include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc

includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib

.data
msg db 'Hello, world!',0

.code
start:
invoke MessageBox,NULL,offset msg,offset msg,MB_OK ;用于弹窗
invoke ExitProcess,0 ;用于弹窗
end start

二、Windows编程

1 库调用

连接:

  • 静态连接(.lib):连接程序会自动从库文件中抽取需要的子程序插入到可执行代码中。

  • 动态连接(.dll):每次需要执行指定代码时才会去动态连接库中找到对应的代码执行。

库:

  • kernel32.dll:系统服务函数,主要处理内存管理和进度调度。

  • user32.dll:用户接口函数,主要控制用户界面。

  • gdi32.dll:图形设备函数,主要负责图形方面的操作。

;过程声明
;过程名:proc定义的过程名、API函数名、高级语言函数名
;调用距离:near,far。省略由存储模型自己决定
;语言类型:STDCALL(系统API调用规范)、C(C语言调用规范)等。如果该过程使用的语言类型与存储模型model伪指令定义的相同,这里可以省略,否则必须说明
过程名 proto [调用距离][语言类型][[,参数]:类型]...

;过程调用
invoke 过程名[,参数,...]

;案例:程序退出函数
;kernel32.dll中,用于结束一个进程及其所有线程
void ExitProcess(UINT uExitCode);
;汇编中定义
ExitProcess proto,:dword
;汇编中使用
invoke ExitProcess,0
;将退出做成宏(该宏存在于io32.inc中)
exit marco dwexitcode
invoke ExitProcess,dwexitcode
endm
;使用
exit 0

2 控制台程序

句柄:32位无符号整数,用来唯一确定一个对象,如输入设备、输出设备或一个文件、图形等。

API 函数返回值保存在 EAX 中。

输出函数:

  • Windows 95/98 操作系统不支持以W结尾的函数。

  • Windows NT/2000/XP 操作系统的内置字符集是 Unicode,在这些操作系统中如果调用以A结尾的函数,操作系统会首先将ANSI字符转换成Unicode字符,然后再调用以W结尾的对应函数。

  • 在微软MSDN文档中,函数名尾部的字母 A 或 W 被省略,汇编语言使用时需要重新声明。

;控制台函数都存在kernel32.dll中,所以要导入kernel32.lib
includelib kernel32.lib

STD_INPUT_HANDLE = -10 ;标准输入句柄,值为-10
STD_OUTPUT_HANDLE = -11 ;标准输出句柄,值为-11
STD_ERROR_HANDLE = -12 ;标准错误句柄,值为-12

;获得句柄函数
HANDLE GetStdHandle(dword nStdHandle);
;汇编使用
GetStdHandle proto,nStdHandle:dword
invoke GetStdHandle,STD_OUTPUT_HANDLE

;输出函数声明(ASCII:WriteConsoleA,Unicode:WriteConsoleW)
WriteConsole equ<WriteConsoleA> ;重新声明
;输出函数
Bool WriteConsole(
HANDLE HConsoleOutput,
CONST VOID *lpBuffer,
DWORD nNumberOfCharsToWrite,
LPDWORD lpNumberOfCharsWritten,
LPVOID lpReserved
)
;汇编使用
WriteConsoleA proto,
handle:dword, ;输出句柄
pbuffer:dword, ;输出缓冲区指针
bufsize:dword, ;输出缓冲区大小
pcount:dword, ;实际输出字符数量的指针
lpreserved:dword ;保留(必须为0)
invoke GetStdHandle,STD_OUTPUT_HANDLE
invoke WriteConsole,eax,addr msg,sizeof msg,addr outsize(.data中dword ?),0

;输入函数
BOOL ReadConsole(
HANDLE hConsoleInput,
LPVOID lpBuffer
DOWRD nNumberOfCharsToRead,
LPDWORD lpNumberOfCharsRead,
LPVOID lpReversed
)
;汇编使用
ReadConsoleA proto,
handle:dword, ;输入句柄
pbuffer:dword, ;输入缓冲区指针
maxsize:dword, ;要读取字符的最大数量
pBytesRead:dword, ;实际读取字符数量的指针
notUsed:dword ;保留(必须为0)
ReadConsole equ<ReadConsoleA>
invoke GetStdHandle,STD_INPUT_HANDLE
invoke ReadConsole,eax,addr _inbuffer(.data中byte 255 dup(0)),255,addr _insize(.data中dword ?),0

;获得控制台模式
GetConsoleMode proto,hConsoleHandle:dword,lpMode:dword
;设置控制台模式(dwMode=0:单字符模式(可以读退格回车tab等,不能读ESC,F1-F12,ctrl,alt,shift,home,end等))
SetConsoleMode proto,hConsoleHandle:dword,dwMode:dword

3 图形化界面

;消息窗口。(在USER32.dll中)创建失败返回0
int MessageBox(
HWND hWnd, ;父窗口(0表示没有)
LPCTSTR lpText, ;内容字符串首地址(0结尾)
LPCTSTR lpCaption, ;标题字符串首地址(0结尾)
UINT uType ;窗口选项:MB_OK(0):OK,MB_OKCANCEL(1):OK&&Cancel
);

;获取时间函数
void GetLocalTime(LPSYSTEMTIME lpSystemTime);
;汇编语言
GetLocalTime proto,:dword
SYSTEMTIME struct(年月星期日时分秒毫秒,word型)
mytime SYSTEMTIME <>
invoke GetLocalTime,addr mytime

;图形化界面

4 高级语言特性

;定义子程序
label_name proc [调用距离][语言类型][作用范围][<起始参数>][USES寄存器列表][形参:类型,...]
local 参数表
...;
label_name endp
;语言类型:SRDCALL,C等
;作用范围:public,private,export(隐含了public和far)

;伪指令条件if
.IF expr
statement
[.ELSEIF expr
statement ]
[.ELSE
statement ]
.ENDIF

;伪指令while
.WHILE expr
...
.ENDW

;伪指令重复
.REPEAT
...
.UNTIL expr
;另一种形式
.REPEAT
...
.UNTILCXZ [expr] ;ECX-=1,直到ECX=0或者条件为真
操作符(大于小于与或非同C语言) 功能
& 位测试
CARRY? CF=1?
OVERFLOW? OF=1?
PARITY? PF=1?
SIGH? SF=1?
ZERO? ZF=1?

三、与 C++ 混合编程

1 VS配置

  1. 项目——生成依赖项——生成自定义——masm
  2. Debug模式要是x86
  3. x64配置方式

2 嵌入汇编

#include <iostream>
using namespace std;
int main()
{
int a;
__asm ;多条嵌入
{
lea eax,a
mov eax,15
mov a,eax
}
cout << a << endl;
return 0;
}
;单条嵌入多行
__asm mov eax,01H
__asm mov dx,0D007H
__asm out dx,eax
;单条嵌入单行(空格隔开)
__asm mov eax,01H __asm mov dx,0D007H __asm out dx,eax

四、DOS环境程序设计

text::基础汇编

1 DOS编程

2 串操作

3 输入输出

4 中断


五、扩展

1 浮点指令

1.1 浮点数

浮点数:

  • 单精度:1位符号位,8位指数,23位有效数字。(D31-D0)

  • 双精度:1位符号位,11位指数,52位有效数字。(D63-D0)

浮点寄存器:

  • x87FPU浮点寄存器:80位数据寄存器,2位标记寄存器。分别是FPR0、tag0,FPR1、tag1…FPR7、tag7。
  • x87FPU的8个数据寄存器存取是栈原则且首尾循环。
  • 在OD中显示的是st0-st7。

标记寄存器标记:

  • 00——对应数据寄存器存有有效数据。
  • 01——对应数据寄存器数据为0。
  • 10——对应数据寄存器数据是特殊数据,如NaN,无限大或非规格化数据。
  • 11——对应数据寄存器内无数据。

浮点状态寄存器:

1.2 浮点指令

;push [] -> st
fld []
;st0 = (double)[],整数入栈
fild []

;pop st -> []
fst []
;出栈并清空栈顶
fstp []
;浮点转有符号整数
cvttsd2si r32,st0/m32

;st0 += []
fadd []
;st0 -= []
fsub []
;st0 *= []
fmul
;st0 /= []
fdiv

2 多媒体指令

寄存器是MMX。使用的寄存器也是st0-7,但是名称为MM0-MM7。

3 64位指令


六、编程技巧

;清空ebx
xor ebx,ebx

;预留空间
a dword ?

;windows换行
endl equ <0DH,0AH>