汇编语言是一种最接近机器语言的编程语言。
汇编学习研究或者开发实践
选一个平台,比如是在给哪个目标平台上开发汇编程序,如x86,我们在开始学习的时候,通常将8086或8088作为学习的目标平台。老得掉渣的平台,不过我们还是可以买得到这种开发板的。这种开发板已经基本没什么实用了,但作为学习用已经够用了。
我们比较常见的是x86,所以选择x86开始是一个比较容易的途径。
另外是汇编编译器
常用汇编语言编译器,如nasm,as、gcc等, 以及windows平台下的MASM和armasm(Microsoft ARM assembler)。armasm是针对ARM架构的汇编开发。汇编语言也需要经过编译成obj文件,然后在链接成可执行文件。
nasm下载地址:
https://www.nasm.us/pub/nasm/releasebuilds/2.00/
as可以查看下帮助信息:
>as --help
其他的汇编器包括YASM等。
YASM下载地址:
http://yasm.tortall.net/Download.html
再一个就是微机原理、计算机体系结构、目标平台(如x86)的指令集等。
最后最好有个方便开发的系统,如windows,linux下。
这里以x86系列CPU处理器为例。
寄存器
寄存器大体可分为通用寄存器和专用寄存器两大类。很多寄存器都是可以通用的,而专用寄存器都是有它特定用处的,且通常不能直接对这种寄存器进行操作,如指令指针寄存器和标志寄存器。
通常我们根据其使用用途将寄存器分为通用寄存器,控制寄存器和段寄存器。
x86不同系列根据位数不同对各类寄存器进行了扩展,同时向后兼容,兼容之前的系列。
8086
8086总共有14个寄存器,且都是16位,同时兼容之前的8位用途。
通用寄存器
通用寄存器主要包括各类数据寄存器,如AX,BX,CX和DX,以及变址寄存器,栈指针寄存器和基址指针寄存器。
AX,BX,CX,DX
同时还可以将这些寄存器分成高8位和低8位,所以可以扩展为AH,AL,BH,BL,CH,CL,DH,DL 8个寄存器。
对于32位,还有EAX,EBX,ECX,EDX
如果支持64位的话,就是RAX,RBX,RCX,RDX.
AX表示累加器寄存器
BX表示基址寄存器
CX表示计数器寄存器
DX表示数据寄存器
变址寄存器
SI/ESI,DI/EDI
SI/ESI表示源变址寄存器
DI/EDI表示目的变址寄存器
栈指针寄存器
SP/ESP
基址指针寄存器
BP/EBP
段寄存器
CS,DS,SS,ES
CS表示代码段寄存器
DS表示数据段寄存器
SS表示堆栈段寄存器
ES表示附加段寄存器
控制寄存器主要包括指令指针寄存器和标志寄存器
指令指针寄存器
IP/EIP
标志寄存器
汇编语法风格
1、Intel汇编风格
2、AT&T汇编风格
不同的汇编语言编译器所采用的汇编风格不一样,包括在C/C++内嵌汇编时也需要注意所采用的汇编风格。在很多文档资料中,书写时所采用的风格也不一样。
这两种风格其实看着都差不多,基本上都是指令后面跟着零个或多个操作数,操作数之间用逗号或空格隔开,按操作数顺序依次称为操作数1,操作数2,。。。操作数n.也有叫源操作数和目的操作数的,不同的是,这两种汇编风格的源操作数和目的操作数正好是相反的,AT&T风格这种书写方式更符合我们的习惯,前面的操作数是源操作数,后面的操作数是目的操作数,而Intel这种书写风格正好相反,后面的那个操作数是源操作数,前面的那个操作数是目的操作数,看着有点不习惯。我们在看指令集文档时主要是采用的是Intel这种书写风格,这也是Intel的风格。
举个例子:将寄存器EBX的值存入EAX中
AT&T的写法:MOV EBX, EAX
而Intel的写法正好相反:MOV EAX, EBX
寻址
x86系列CPU处理器运行模式
DOS实模式
保护模式
Virtual 8086模式(虚拟8086模式,简称V86模式)
汇编源程序
采用不同的汇编编译器,在编写汇编程序是,汇编源代码的结构组织也有些不同,书写风格也有些差异。且不同编译器支持的特性也不同,如宏,伪指令等。
代码段
数据段
堆栈段
附加段
跳转
这里环境:
MASM32
主要工具包括:
ML:编译器
LINK:链接器,32位的。
LINK16:链接器,16位的。
ML
这里是6.14.8444的版本
>ml /?
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997. All rights reserved.
ML [ /options ] filelist [ /link linkoptions ]
/AT Enable tiny model (.COM file) /nologo Suppress copyright m
essage
/Bl<linker> Use alternate linker /Sa Maximize source listing
/c Assemble without linking /Sc Generate timings in list
ing
/Cp Preserve case of user identifiers /Sf Generate first pass list
ing
/Cu Map all identifiers to upper case /Sl<width> Set line width
/Cx Preserve case in publics, externs /Sn Suppress symbol-table li
sting
/coff generate COFF format object file /Sp<length> Set page length
/D<name>[=text] Define text macro /Ss<string> Set subtitle
/EP Output preprocessed listing to stdout /St<string> Set title
/F <hex> Set stack size (bytes) /Sx List false conditionals
/Fe<file> Name executable /Ta<file> Assemble non-.ASM
file
/Fl[file] Generate listing /w Same as /W0 /WX
/Fm[file] Generate map /WX Treat warnings as errors
/Fo<file> Name object file /W<number> Set warning level
/FPi Generate 80x87 emulator encoding /X Ignore INCLUDE environmen
t path
/Fr[file] Generate limited browser info /Zd Add line number debug in
fo
/FR[file] Generate full browser info /Zf Make all symbols public
/G<c|d|z> Use Pascal, C, or Stdcall calls /Zi Add symbolic debug info
/H<number> Set max external name length /Zm Enable MASM 5.10 compati
bility
/I<name> Add include path /Zp[n] Set structure alignme
nt
/link <linker options and libraries> /Zs Perform syntax check onl
y
ML默认会先编译再进行链接,并且在链接的时候默认使用的32位的链接器LINK工具进行链接,当然也可以通过/Bl指定链接器,另外ML可以指定/c只编译不链接。
也就是说默认情况下:
>ml test.asm
等价于:
>ml /Bllink test.asm
如果是编译链接16位程序的话,需要指定16位的链接器LINK16工具进行链接:
>ml /Bllink16 pci_test_1.asm
这里的link和link16就是指定的链接器,这里我因为将链接器加入PATH环境变量了,所以可以直接指定。如果没有将链接器加入PATH环境变量,需要指定绝对路径。如下:
>ml /Bld:\masm32\bin\link.exe test.asm
>ml /Bld:\masm32\bin\link16.exe test.asm
如果只编译不链接:
>ml /c test.asm
之后可以使用链接器LINK或LINK16工具进行链接。
LINK
LINK16
这里是5.60.339的版本
>link16 /?
Microsoft (R) Segmented Executable Linker Version 5.60.339 Dec 5 199
4
Copyright (C) Microsoft Corp 1984-1993. All rights reserved.
Usage:
LINK
LINK @<response file>
LINK <objs>,<exefile>,<mapfile>,<libs>,<deffile>
Valid options are:
/? /ALIGNMENT
/BATCH /CODEVIEW
/CPARMAXALLOC /DOSSEG
/DSALLOCATE /DYNAMIC
/EXEPACK /FARCALLTRANSLATION
/HELP /HIGH
/INFORMATION /LINENUMBERS
/MAP /NODEFAULTLIBRARYSEARCH
/NOEXTDICTIONARY /NOFARCALLTRANSLATION
/NOGROUPASSOCIATION /NOIGNORECASE
/NOLOGO /NONULLSDOSSEG
/NOPACKCODE /NOPACKFUNCTIONS
/NOFREEMEM /OLDOVERLAY
/ONERROR /OVERLAYINTERRUPT
/PACKCODE /PACKDATA
/PACKFUNCTIONS /PAUSE
/PCODE /PMTYPE
/QUICKLIBRARY /SEGMENTS
/STACK /TINY
/WARNFIXUP
链接的时候,最简单的方式就是:
>link16
Microsoft (R) Segmented Executable Linker Version 5.60.339 Dec 5 1994
Copyright (C) Microsoft Corp 1984-1993. All rights reserved.
Object Modules [.obj]: test.obj
Run File [test.exe]:
List File [nul.map]:
Libraries [.lib]:
Definitions File [nul.def]:
LINK : warning L4021: no stack segment
这里如果不需要链入lib库的话,至少需要指定需要链接的obj对象文件。当然也可以在命令行上直接指定需要链接的obj对象文件,如下:
>link16 test.obj
Microsoft (R) Segmented Executable Linker Version 5.60.339 Dec 5 1994
Copyright (C) Microsoft Corp 1984-1993. All rights reserved.
Run File [test.exe]:
List File [nul.map]:
Libraries [.lib]:
Definitions File [nul.def]:
LINK : warning L4021: no stack segment
上面两种方式都会提示指定相关信息:.exe输出文件,.map文件,.lib文件,.def文件。第一种还需要指定.obj文件。如果不需要这么麻烦的提示,可以这样:
link16 test.obj, pci_test_1.exe, , , ,
分别指定对象文件,EXE输出文件,.map文件,LIB库文件,.def文件,如果对象文件,LIB库文件有多个,可以通过+号分别将对象文件,LIB库文件连接起来。
JMP — Jump
EB cb | JMP rel8 | D | Valid | Valid | Jump short, RIP = RIP + 8-bit displacement sign extended to 64-bits |
E9 cw | JMP rel16 | D | N.S. | Valid | Jump near, relative, displacement relative to next instruction. Not supported in 64-bit mode. |
E9 cd | JMP rel32 | D | Valid | Valid | Jump near, relative, RIP = RIP + 32-bit displacement sign extended to 64-bits |
FF /4 | JMP r/m16 | M | N.S. | Valid | Jump near, absolute indirect, address = zero-extended r/m16. Not supported in 64-bit mode. |
FF /4 | JMP r/m32 | M | N.S. | Valid | Jump near, absolute indirect, address given in r/m32. Not supported in 64-bit mode. |
FF /4 | JMP r/m64 | M | Valid | N.E. | Jump near, absolute indirect, RIP = 64-Bit offset from register or memory |
EA cd | JMP ptr16:16 | D | Inv. | Valid | Jump far, absolute, address given in operand |
EA cp | JMP ptr16:32 | D | Inv. | Valid | Jump far, absolute, address given in operand |
FF /5 | JMP m16:16 | D | Valid | Valid | Jump far, absolute indirect, address given in m16:16 |
FF /5 | JMP m16:32 | D | Valid | Valid | Jump far, absolute indirect, address given in m16:32. |
REX.W FF /5 | JMP m16:64 | D | Valid | N.E. | Jump far, absolute indirect, address given in m16:64. |
LODS/LODSB/LODSW/LODSD/LODSQ — Load String
AC | LODS m8 | ZO | Valid | Valid | For legacy mode, Load byte at address DS:(E)SI into AL. For 64-bit mode load byte at address (R)SI into AL. |
AD | LODS m16 | ZO | Valid | Valid | For legacy mode, Load word at address DS:(E)SI into AX. For 64-bit mode load word at address (R)SI into AX. |
AD | LODS m32 | ZO | Valid | Valid | For legacy mode, Load dword at address DS:(E)SI into EAX. For 64-bit mode load dword at address (R)SI into EAX. |
REX.W + AD | LODS m64 | ZO | Valid | N.E. | Load qword at address (R)SI into RAX. |
AC | LODSB | ZO | Valid | Valid | For legacy mode, Load byte at address DS:(E)SI into AL. For 64-bit mode load byte at address (R)SI into AL. |
AD | LODSW | ZO | Valid | Valid | For legacy mode, Load word at address DS:(E)SI into AX. For 64-bit mode load word at address (R)SI into AX. |
AD | LODSD | ZO | Valid | Valid | For legacy mode, Load dword at address DS:(E)SI into EAX. For 64-bit mode load dword at address (R)SI into EAX. |
REX.W + AD | LODSQ | ZO | Valid | N.E. | Load qword at address (R)SI into RAX. |
LODS
mov ax, _data mov ds, ax mov si, offset message lods message
mov ax, _data mov ds, ax mov si, offset message lods byte ptr message
LODSB
mov ax, _data mov ds, ax mov si, offset message lodsb
过程
MASM汇编通过PROC伪指令定义过程。
参考下面的PROC过程定义。
PROC定义过程的例子:
_TEST PROC MOV DX, OFFSET MESSAGE ; 字符串首偏移地址放到DX中 MOV AH, 9 INT 21H ; 输出字符串 RET 0 _TEST ENDP
除了通过PROC伪指令定义过程,标号也可以定义一个过程。
标号定义过程的例子:
_TEST: MOV DX, OFFSET MESSAGE ; 字符串首偏移地址放到DX中 MOV AH, 9 INT 21H ; 输出字符串 RET 0
调用
通过CALL/RET指令来实现过程调用。
过程调用本质上就是在调用时跳转到被调用过程的第一条指令位置开始执行,执行到最后一条指令后再跳回到调用后的下一条指令继续执行。为了能跳回到调用后的下一条指令,调用者需要被调用者在执行完后的跳转返回地址。
CALL指令除了跳转到指定位置执行外,它还会将调用返回的地址压入栈中,这样被调用者在执行完后就能根据压入的返回地址返回继续执行。
对应RET指令,它除了能跳转之外,它还能根据压入的返回地址进行跳转。它会将压入的返回地址弹出栈,再进行跳转。RET指令还可以在跳转返回之前,将栈上的n个字节弹出释放。
段内调用
通过标号定义过程的例子:
_DATA SEGMENT MESSAGE DB 'Hello, MASM!', 0DH, 0AH, '$' _DATA ENDS _TEXT SEGMENT _TEST: MOV DX, OFFSET MESSAGE ; 字符串首偏移地址放到DX中 MOV AH, 9 INT 21H ; 输出字符串 RET 0 _START: MOV AX, _DATA MOV DS, AX CALL _TEST MOV AH,4CH INT 21H _TEXT ENDS END _START
通过PROC定义过程的例子:
_DATA SEGMENT MESSAGE DB 'Hello, MASM!', 0DH, 0AH, '$' _DATA ENDS _TEXT SEGMENT _TEST PROC MOV DX, OFFSET MESSAGE ; 字符串首偏移地址放到DX中 MOV AH, 9 INT 21H ; 输出字符串 RET 0 _TEST ENDP _START: MOV AX, _DATA MOV DS, AX CALL _TEST MOV AH,4CH INT 21H _TEXT ENDS END _START
段间调用
通过标号定义过程的例子:
_DATA SEGMENT MESSAGE DB 'Hello, MASM!', 0DH, 0AH, '$' _DATA ENDS _PROC SEGMENT _TEST: MOV DX, OFFSET MESSAGE ; 字符串首偏移地址放到DX中 MOV AH, 9 INT 21H ; 输出字符串 RETF 0 _PROC ENDS _TEXT SEGMENT _START: MOV AX, _DATA MOV DS, AX CALL FAR PTR _TEST MOV AH,4CH INT 21H _TEXT ENDS END _START通过PROC定义过程的例子:
_DATA SEGMENT MESSAGE DB 'Hello, MASM!', 0DH, 0AH, '$' _DATA ENDS _PROC SEGMENT _TEST PROC MOV DX, OFFSET MESSAGE ; 字符串首偏移地址放到DX中 MOV AH, 9 INT 21H ; 输出字符串 RETF 0 _TEST ENDP _PROC ENDS _TEXT SEGMENT _START: MOV AX, _DATA MOV DS, AX CALL FAR PTR _TEST MOV AH,4CH INT 21H _TEXT ENDS END _START除了通过段内调用和段间调用进行区分。还可以分为Near Call,Far Call,Inter-privilege-level far call, Task switch。
CALL — Call Procedure
E8 cw | CALL rel16 | D | N.S. | Valid | Call near, relative, displacement relative to next instruction. |
E8 cd | CALL rel32 | D | Valid | Valid | Call near, relative, displacement relative to next instruction. 32-bit displacement sign extended to 64-bits in 64-bit mode. |
FF /2 | CALL r/m16 | M | N.E. | Valid | Call near, absolute indirect, address given in r/m16. |
FF /2 | CALL r/m32 | M | N.E. | Valid | Call near, absolute indirect, address given in r/m32. |
FF /2 | CALL r/m64 | M | Valid | N.E. | Call near, absolute indirect, address given in r/m64. |
9A cd | CALLptr16:16 | D | Invalid | Valid | Call far, absolute, address given in operand. |
9A cp | CALLptr16:32 | D | Invalid | Valid | Call far, absolute, address given in operand. |
FF /3 | CALL m16:16 | M | Valid | Valid | Call far, absolute indirect address given in m16:16. In 32-bit mode: if selector points to a gate, then RIP = 32-bit zero extended displacement taken from gate; else RIP = zero extended 16-bit offset from far pointer referenced in the instruction. |
FF /3 | CALL m16:32 | M | Valid | Valid | In 64-bit mode: If selector points to a gate, then RIP = 64-bit displacement taken from gate; else RIP = zero extended 32-bit offset from far pointer referenced in the instruction. |
REX.W FF /3 | CALL m16:64 | M | Valid | N.E. | In 64-bit mode: If selector points to a gate, then RIP = 64-bit displacement taken from gate; else RIP = 64-bit offset from far pointer referenced in the instruction. |
E8 cw | CALL rel16 | D | N.S. | Valid | Call near, relative, displacement relative to next instruction. |
E8 cd | CALL rel32 | D | Valid | Valid | Call near, relative, displacement relative to next instruction. 32-bit displacement sign extended to 64-bits in 64-bit mode. |
FF /2 | CALL r/m16 | M | N.E. | Valid | Call near, absolute indirect, address given in r/m16. |
FF /2 | CALL r/m32 | M | N.E. | Valid | Call near, absolute indirect, address given in r/m32. |
FF /2 | CALL r/m64 | M | Valid | N.E. | Call near, absolute indirect, address given in r/m64. |
远距离调用,绝对地址调用
9A cd | CALLptr16:16 | D | Invalid | Valid | Call far, absolute, address given in operand. |
_DATA SEGMENT MESSAGE DB 'Hello, MASM!', 0DH, 0AH, '$' _DATA ENDS _PROC SEGMENT _TEST PROC MOV DX, OFFSET MESSAGE ; 字符串首偏移地址放到DX中 MOV AH, 9 INT 21H ; 输出字符串 RETF 0 _TEST ENDP _PROC ENDS _TEXT SEGMENT _START: MOV AX, _DATA MOV DS, AX CALL _PROC:_TEST MOV AH,4CH INT 21H _TEXT ENDS END _START
在上面的例子中,CALL _PROC:_TEST还可以写成以下形式:
CALL _PROC:[_TEST]
CALL SEG _PROC:_TEST
CALL SEG _PROC:[_TEST]
9A cp | CALLptr16:32 | D | Invalid | Valid | Call far, absolute, address given in operand. |
FF /3 | CALL m16:16 | M | Valid | Valid | Call far, absolute indirect address given in m16:16. In 32-bit mode: if selector points to a gate, then RIP = 32-bit zero extended displacement taken from gate; else RIP = zero extended 16-bit offset from far pointer referenced in the instruction. |
FF /3 | CALL m16:32 | M | Valid | Valid | In 64-bit mode: If selector points to a gate, then RIP = 64-bit displacement taken from gate; else RIP = zero extended 32-bit offset from far pointer referenced in the instruction. |
REX.W FF /3 | CALL m16:64 | M | Valid | N.E. | In 64-bit mode: If selector points to a gate, then RIP = 64-bit displacement taken from gate; else RIP = 64-bit offset from far pointer referenced in the instruction. |
也可以JMP来实现CALL的效果,通过JMP/RET指令来实现过程调用。在使用JMP/RET来实现过程调用的是个,对应RET指令,在JMP之前,应该将返回地址压入栈中,以便在RET的时候,根据压入的返回地址跳转返回继续执行。
所以这里其实有两次跳转,当然我们也可以使用两个JMP来实现,即JMP/JMP。
通过CALL指令实现过程调用
_DATA SEGMENT h_interrupt_process DB 0DH,0AH,'proc _test called.',0DH,0AH,'$' _DATA ENDS _TEXT SEGMENT _test PROC NEAR MOV AX, _DATA MOV DS, AX MOV DX, OFFSET h_interrupt_process ; 字符串首偏移地址放到DX中 MOV AH, 9 INT 21H ;输出字符串 ret 0 _test ENDP _TEXT ENDS _TEXT SEGMENT __START: call _test MOV AH,4CH INT 21H _TEXT ENDS END __START
通过JMP指令实现过程调用
_DATA SEGMENT h_interrupt_process DB 0DH,0AH,'proc _test called.',0DH,0AH,'$' _DATA ENDS _TEXT SEGMENT _test PROC NEAR MOV AX, _DATA MOV DS, AX MOV DX, OFFSET h_interrupt_process ; 字符串首偏移地址放到DX中 MOV AH, 9 INT 21H ;输出字符串 ret 0 _test ENDP _TEXT ENDS _TEXT SEGMENT __START: MOV BX, OFFSET __RET PUSH BX JMP _test __RET: MOV AH,4CH INT 21H _TEXT ENDS END __START
这里通过JMP指令还想到这样一种方式实现过程调用
_DATA SEGMENT h_interrupt_process DB 0DH,0AH,'proc _test called.',0DH,0AH,'$' _DATA ENDS _TEXT SEGMENT _test PROC NEAR MOV AX, _DATA MOV DS, AX MOV DX, OFFSET h_interrupt_process ; 字符串首偏移地址放到DX中 MOV AH, 9 INT 21H ; 输出字符串 ret 0 _test ENDP _TEXT ENDS _TEXT SEGMENT __START: MOV BX, OFFSET __RET ; 下面__RET定义了一个变量,分配了2个字节的空间, ADD BX, 2指令在__RET的位置上加2个字节 ; 表示jmp跳转(调用)到MOV AH,4CH指令的位置. ; ; 不加ADD BX, 2这条指令也可以,这样的话就表示jmp跳转(调用)到__RET DB 1 DUP(?), '$'指令的位置,、 ADD BX, 2 PUSH BX JMP _test __RET DB 1 DUP(?), '$' MOV AH, 4CH INT 21H _TEXT ENDS END __START
保存、恢复堆栈
通常我们在调用一个过程(函数)时,会为这个过程(函数)分配一块私有的栈空间。在调用过程(函数)的时候,会保留当前调用者的当前栈地址,进入调用函数后,就是调用函数的栈空间了,调用后需要恢复到调用者的栈空间。
保存、恢复堆栈这个是不必须的,这样的话,调用函数和被调用函数使用的是同一个栈空间。
只是有时候函数过大,需要的栈空间大,需要在栈上分配大量的栈空间,或者多个函数使用同一个栈空间也会使得在栈上分配大量的栈空间,导致栈溢出。所以最好设计为每个函数调用都使用自己的栈空间。这样就需要保存、恢复堆栈。
是否需要保存、恢复堆栈,或者说调用函数和被调用函数是否使用的是同一个栈空间,在汇编里边全凭自己权衡,维持堆栈平衡。
_sum PROC NEAR push ebp mov ebp, esp ... ... pop ebp ret 0 _sum ENDP
不需要保存、恢复堆栈
这里有个不需要保存、恢复堆栈的例子,如果加上保存、恢复堆栈的话还会报错。
TITLE .asm .386P .MODEL Flat,StdCall OPTION CaseMap:None include msvcrt.inc includelib msvcrt.lib _DATA SEGMENT _sum_result DB 'sum=%d',0DH,0AH _DATA ENDS _TEXT SEGMENT _sum PROC stdcall arg1:dword,arg2:dword ;push ebp ;mov ebp, esp mov eax, arg1 mov ecx, arg2 add eax, ecx ;pop ebp ret 8 _sum ENDP _TEXT ENDS _TEXT SEGMENT __main PROC NEAR ; 通过call指令调用 push 11 push 22 call _sum ; 通过invoke伪指令调用 ;invoke _sum, 11, 22 invoke crt_printf, addr _sum_result, eax ret __main ENDP _TEXT ENDS END
>ml /c /coff /ID:\masm32\include call_test_386p_1.asm
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997. All rights reserved.
Assembling: call_test_386p_1.asm
>link /SUBSYSTEM:CONSOLE /ENTRY:__main /LIBPATH:D:\masm32\lib call_test_386p_1.obj
Microsoft (R) Incremental Linker Version 5.12.8078
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.
>.\call_test_386p_1
sum=33
中断
MASM汇编语言
指令
这里指的是伪指令,这个是masm支持的不同于汇编指令的指令。是masm支持的directive而不是汇编中的指令集中的instruction。
操作符
符号
masm提供了一组内置符号(symbol)。它可能是一种操作符号。
汇编中我们定义的标号指的是label。
初始化内存模型
.MODEL memorymodel [[, langtype]] [[, stackoption]]
=
name = expression
这是一个伪指令, 将表达式的数值赋值给name。
例子:
U_DATA_SEG = 3000h
EQU
name EQU expression
将表达式的数值赋值给name。这和=伪指令是一样。
name EQU <text>
将字符串赋值给name
这是一个伪指令,
段的定义
name SEGMENT [[READONLY]] [[align]] [[combine]] [[use]] [[characteristics]] ALIAS(string) [['class']]
statements
name ENDS
例子:
CODE SEGMENT
... ...
CODE ENDS
CODE SEGMENT USE16
... ...
CODE ENDS
还有一种简化的段定义:
代码段
.CODE [[name]]
数据段
.DATA
未知数据段
.DATA?
常量数据段
.CONST
远距离数据段
.FARDATA [[name]]
未知远距离数据段
.FARDATA? [[name]]
堆栈段
.STACK [[size]]
过程定义
label PROC [[distance]] [[langtype]] [[visibility]] [[<prologuearg>]] [[USES reglist]] [[, parameter [[:tag]]]] ...
[[FRAME [[:ehandler-address]] ]]
statements
label ENDP
distance
可指定NEAR或FAR。如果指定的FAR,汇编编译器会将RET指令生成为RETF。
例子:
_testPROC NEAR
... ...
_testENDP
在下面的例子中,这里是一个段间调用。过程定义时distance指定为FAR,汇编编译器会将RET 0指令生成为RETF 0。如果不指定为FAR的话,这个段间调用在返回时通过RET 0是不能正常返回的。
_DATA SEGMENT MESSAGE DB 'Hello, MASM!', 0DH, 0AH, '$' _DATA ENDS _PROC SEGMENT _TEST PROC FAR MOV DX, OFFSET MESSAGE ; 字符串首偏移地址放到DX中 MOV AH, 9 INT 21H ; 输出字符串 RET 0 _TEST ENDP _PROC ENDS _TEXT SEGMENT _START: MOV AX, _DATA MOV DS, AX CALL FAR PTR _TEST MOV AH,4CH INT 21H _TEXT ENDS END _START
MASM中调用和命名规范(calling and naming convention)
在MASM中很多地方都有个“langtype” 的参数设定,用于指定调用和命名规范(calling and naming convention),这个东西不只是在MASM中有,在其他任何语言中其实都有这个,如C,C++,JAVA等。
下面是MASM中一些需要设定“langtype” 的参数的地方:
label PROC [[distance]] [[langtype]] [[visibility]] [[<prologuearg>]] [[USES reglist]] [[, parameter [[:tag]]]] ...
[[FRAME [[:ehandler-address]] ]]
statements
label ENDP
EXTERN [[langtype]] name [[ (altid) ]] : type [[, [[langtype]] name [[ (altid) ]] : type]] ...
EXTRN
EXTERNDEF [[langtype]] name:type [[, [[langtype]] name:type]]...
label PROTO [distance] [langtype] [, [parameter]:tag] ...
PUBLIC [[langtype]] name [[, [[langtype]] name]]...
MASM中调用和命名规范的支持:
32-bit FLAT model: C, STDCALL
16-bit models: C, BASIC, FORTRAN, PASCAL, SYSCALL, STDCALL
MASM中“langtype” 的参数的设定细节其实挺多的,设置的不一样, 反映的结果也不一样,另外,有些地方,不同的地方设置的一样,反映的结果也不一样,而且设定很灵活。
PROTO声明的例子:
_DATA SEGMENT MESSAGE2 DB 'Hello, This is MASM!', 0DH, 0AH, '$' _DATA ENDS _PROC SEGMENT ASSUME CS:_PROC, DS:_DATA _TEST_PRINTF PROC FAR PUSH BP MOV BP, SP MOV AX, _DATA MOV DS, AX MOV DX, OFFSET MESSAGE2 MOV AH, 9 INT 21H POP BP RET _TEST_PRINTF ENDP _PROC ENDS END
; IF PARAM langtype NOT SPECIFIED, DEFAULT "BASIC", THEN THE DEFINITION OF _TEST_PRINTF: ; ; _TEST_PRINTF PROC FAR ; ... ... ; ... ... ; _TEST_PRINTF ENDP _TEST_PRINTF PROTO FAR _DATA SEGMENT MESSAGE1 DB 'Hello, MASM!', 0DH, 0AH, '$' _DATA ENDS _TEXT SEGMENT ASSUME CS:_TEXT, DS:_DATA START: MOV AX, _DATA MOV DS, AX MOV DX, OFFSET MESSAGE1 MOV AH, 9 INT 21H CALL _TEST_PRINTF MOV AH,4CH INT 21H _TEXT ENDS END START
OFFSET
OFFSET expression
这个是操作符。返回指定表达式相对于段的偏移量
这里“相对于段的偏移量”有时候不太好理解。通常指的是当前段或当前所在的段,但理解成“当前段”或“当前所在的段”都不太正确。应该理解成当前限定的段的偏移量。
例如:
MOV SI, OFFSET MESSAGE
这里当前没有限定当前段是哪个,那就是指的是相对于当前段的偏移量。
jmp seg _proc:[offset _test]
这里假设这是_text段中的一条指令,但这里有限定段是_proc,所以这里指的是相对于_proc段的偏移量。
在16位处理器下,返回的是16位的偏移地址,在32位处理器下,返回的是32位的偏移地址。
$
返回当前地址。例子:
MESSAGE:
DB 'Hello, MASM!',0DH,0AH,'$'
MESSAGE_LENGTH:
DW $ - MESSAGE
SEG
SEG expression
返回指定表达式的段。例子:
SEG START
START是一个标号,返回这个标号对应的段。也就是段基地址。
MASM汇编源代码
MASM汇编源代码在书写时可以使用完整的段定义书写形式,还可以采用简化的段定义书写形式
完整的段定义书写形式:
.386P .MODEL Flat,StdCall OPTION CaseMap:None include msvcrt.inc includelib msvcrt.lib _DATA SEGMENT STR1 DB 'EAX=%d; ECX=%d; EDX=%d',0DH,0AH,'$' _DATA ENDS _TEXT SEGMENT ___main PROC NEAR push ebp mov ebp, esp mov eax, 11 mov ecx, 22 mov edx, 33 invoke crt_printf, addr STR1, eax, ecx, edx pop ebp ret 0 ___main ENDP _TEXT ENDS END
>ml /c /coff /ID:\masm32\include invoke_crt_printf_test_1.asm
>link /ENTRY:___main /SUBSYSTEM:CONSOLE /LIBPATH:D:\masm32\lib invoke_crt_printf_test_1.obj
>.\invoke_crt_printf_test_1.exe
MASM汇编源代码还可以采用简化的段定义书写形式
简化的段定义书写形式比较适合16位模式的汇编程序开发。采用这种简化的段定义书写形式必须要指定.MODEL
.386P ; MODEL: 伪指令 .MODEL Flat,StdCall OPTION CaseMap:None include msvcrt.inc includelib msvcrt.lib ; .DATA: 伪指令, 简化的数据段定义书写形式 .DATA STR1 DB 'EAX=%d; ECX=%d; EDX=%d',0DH,0AH,'$' ; .CODE: 伪指令, 简化的代码段定义书写形式 .CODE ___main PROC NEAR push ebp mov ebp, esp mov eax, 11 mov ecx, 22 mov edx, 33 invoke crt_printf, addr STR1, eax, ecx, edx pop ebp ret 0 ___main ENDP END
>ml /c /coff /ID:\masm32\include invoke_crt_printf_test_1_1.asm
>link /ENTRY:___main /SUBSYSTEM:CONSOLE /LIBPATH:D:\masm32\lib invoke_crt_printf_test_1_1.obj
>.\invoke_crt_printf_test_1_1.exe
MASM生成.COM可执行文件
CODE SEGMENT ORG 100H START: MOV AX,CS MOV DS,AX MOV DX,OFFSET MESSAGE ; 字符串首偏移地址放到DX中 MOV AH,9 INT 21H ;输出字符串 MOV AH,4CH INT 21H MESSAGE: DB 'HOW DU YOU DO?',0DH,0AH,'$' CODE ENDS END START
按照以下方式编译链接可生成.COM文件:
>ml /AT /c com_test.asm
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997. All rights reserved.
Assembling: com_test.asm
>link16 /TINY com_test.obj
Microsoft (R) Segmented Executable Linker Version 5.60.339 Dec 5 1994
Copyright (C) Microsoft Corp 1984-1993. All rights reserved.
Run File [com_test.com]:
List File [nul.map]:
Libraries [.lib]:
Definitions File [nul.def]:
LINK : warning L4045: name of output file is 'com_test.com'
>.\com_test.com
Hello, MASM!
键盘
键盘输入输出有中断和端口两种方式。
中断方式
int 21h
int 16h
端口方式
端口方式从端口读取键盘输入并显示
.model small .data message db 'Please type...', 0dh, 0ah, '$' ; 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 0a | 0b | 0c | 0d | 0e | 0f | scan_codes db ' ', ' ', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', ' ', ' ', ; 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 1a | 1b | 1c | 1d | 1e | 1f | db 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '[', ']', ' ', ' ', 'A', 'S', ; 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 2a | 2b | 2c | 2d | 2e | 2f | db 'D', 'F', 'G', 'H', 'J', 'K', 'L', ';', "'", '`', ' ', '\', 'Z', 'X', 'C', 'V', ; 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 3a | 3b | 3c | 3d | 3e | 3f | db 'B', 'N', 'M', ',', '.', '/', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ; 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 4a | 4b | 4c | 4d | 4e | 4f | db ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '-', ' ', ' ', ' ', '+', ' ', ; 50 | 51 | 52 | 53 | db ' ', ' ', ' ', ' ' end1 db '$' .code start: mov ax, _DATA mov ds, ax mov es, ax mov dx, offset message mov ah, 9 int 21h ;输出字符串 mov bx, offset scan_codes mov cx, 30 ; 缓存上次从60h读取的扫描码 last_buf: db 0 wait_enter: in al, 64h test al, 01h jz wait_enter read_loop: in al, 60h cmp al, byte ptr last_buf jz read_loop mov byte ptr last_buf, al xor ah, ah bt ax, 7 jc read_loop mov si, ax ;mov ah, 0eh ;mov al, es:[bx][si] ;int 10h mov ah, 02h mov dl, es:[bx][si] int 21h loop read_loop endl: mov ah, 4ch int 21h ;endl: ;hlt ;jmp endl end start
MASM汇编调用C程序
在这个例子中,只有2个程序文件,一个是汇编,一个是C程序,C程序中定义了一个函数,最后我们在汇编中调用C中定义的那个函数。
C程序-c_test_2.c
#include <stdio.h> void c_test_2() { printf("c_test_2 called.\n"); }
汇编程序-call_c_test_2.asm
.MODEL SMALL, C extrn c_test_2:near _DATA SEGMENT MESSAGE1 DB '[1] HELLO, INT 21H->09H!', 0DH, 0AH, '$' _DATA ENDS _TEXT SEGMENT MAIN PROC NEAR _START: MOV AX, _DATA MOV DS, AX MOV DX, OFFSET MESSAGE1 ; 字符串首偏移地址放到DX中 MOV AH, 9 INT 21H ; 输出字符串 CALL c_test_2 MOV AH, 4CH INT 21H MAIN ENDP _TEXT ENDS END
编译C程序:
tcc -c -n. -Ic:\turboc3\include -oc_test_2.obj c_test_2.c
编译汇编程序:
ml /c call_c_test_2.asm
编译完成后会生成两个对象文件:c_test_2.obj,call_c_test_2.obj
链接:
link16 C:\TURBOC3\LIB\C0S.OBJ+C_TEST_2.OBJ+call_c_test_2.obj,call_c_test_2.exe,,C:\TURBOC3\LIB\CS.LIB,,
因为c程序中调用了c的标准函数printf,用到了c的标准库,所以在链接的时候要指定c的标准库CS.LIB以及对应的对象文件。
MASM开发主引导程序
; CODE SEGMENT. 7C00H FOR BOOT: ORG 7C00H; 100H FOR .COM: : ORG 100H CODE_SEGMENT = 7C00H CODE SEGMENT ORG CODE_SEGMENT START: MOV AX, CS MOV DS, AX MOV SS, AX MOV ES, AX MOV SI, OFFSET MESSAGE PRINT_MESSAGE: MOV AL, [SI] CMP AL, '$' JE ENDL MOV AH, 0EH MOV BX, 0FH INT 10H INC SI JMP PRINT_MESSAGE ENDL: MOV AH,4CH INT 21H ;ENDL: ; HLT ; JMP ENDL MESSAGE: DB 'Hello, MASM!', 0DH, 0AH, '$' DB 510 - ($ - SEG START) DUP(0) DB 85, 170 ; 0X55, 0XAA CODE ENDS END START
NASM汇编语言
参考另一篇文章:https://lobin.iteye.com/blog/2041659
AS汇编语言
参考另一篇文章:https://www.iteye.com/blog/lobin-2038160
C/C++:内嵌汇编可参考另一篇文章:https://lobin.iteye.com/blog/1585647。
8086指令集:https://dwz.cn/DXsXNfmA?u=42fdac4f56abcfc3
Intel 80386:http://www.intel80386.com/
Bochs
这部分参考另一篇文章:https://www.iteye.com/blog/lobin-2041659
QEMU
这部分参考另一篇文章:https://www.iteye.com/blog/lobin-673570
相关推荐
本文将详细介绍masm615和masm5这两个汇编编译器,并探讨它们在汇编指令学习中的应用。 首先,让我们来看看masm615。MASM,全称Microsoft Macro Assembler,是微软公司开发的一个汇编器,主要用于生成IBM PC兼容机上...
**MASM,全称Microsoft Macro Assembler,是微软公司推出的一款宏汇编器,用于将汇编语言程序转换成机器代码。MASM6.11和MASM6.15是MASM的不同版本,这两个版本在汇编语言编程领域都有其特定的应用场景和特点。** *...
**MASM,全称Microsoft Macro Assembler,是微软公司推出的一款高级汇编语言编译器,主要用于编写基于x86和x64架构的程序。MASM 6.15是MASM的一个版本,它支持64位指令集,使得开发者能够创建针对64位操作系统的应用...
**MASM 6.15 完整版详解** **一、MASM介绍** MASM,全称为Microsoft Macro Assembler,是由微软公司开发的一款高级汇编语言编译器。MASM支持Intel架构下的x86指令集,广泛应用于编写低级系统程序、驱动程序以及对...
**MASM全称是Microsoft Macro Assembler,是微软公司推出的一种宏汇编器,用于将汇编语言程序转换成机器代码。它在个人计算机编程领域,尤其是在早期的DOS和Windows系统开发中扮演了重要角色。以下是关于MASM各版本...
**汇编语言编程工具MASM6.15详解** MASM(Microsoft Macro Assembler)是微软公司开发的一款宏汇编器,它为程序员提供了编写低级机器代码的能力,适用于IBM PC兼容机上的Intel x86架构。MASM6.15是MASM的一个版本,...
【标题】"masm5.0 win32" 指的是MicroAssember System (MASM) 5.0版本在Windows 32位操作系统上的应用。MASM是一款流行的x86汇编语言编译器,由Microsoft开发,用于编写针对Intel 8086及其后续处理器的低级代码。 ...
**MASM编译器 DOSBOX版本**是一种在现代操作系统中模拟DOS环境的解决方案,用于运行和调试使用MASM(Microsoft Assembler)编写的汇编语言程序。DOSBOX是一个开源的DOS模拟器,它允许用户在不支持DOS的操作系统上...
2. 接着,输入`cd <你的MASM 6.15安装目录>`,例如:`cd "C:\Program Files\MASM615"`,进入MASM 6.15的根目录。 3. 输入`masm`启动MASM 6.15。现在你可以像在DOS环境下一样,编写、编译和链接汇编语言程序了。 **...
**汇编语言MASM6.15详解** 汇编语言是一种低级编程语言,它与计算机硬件紧密相连,每一行代码直接对应机器指令。MASM(Microsoft Macro Assembler)是微软公司开发的一款流行的汇编器,用于编写针对x86架构的程序。...
汇编编辑器MASM6.15 MASM是微软公司开发的汇编开发环境,拥有可视化的开发界面,使开发人员不必再使用DOS环境进行汇编的开发,编译速度快,支持80x86汇编以及Win32Asm是Windows下开发汇编的利器。它与windows平台的...
**汇编语言与Masm6.15** 汇编语言是一种低级编程语言,它将计算机指令以人类可读的形式表示出来。每条汇编指令通常对应一个机器码,直接控制计算机硬件的操作,如移动数据、执行算术运算、逻辑操作以及控制程序流程...
**MASM2012.5:深入了解汇编语言的集成开发环境** MASM,全称为Microsoft Macro Assembler,是一款由微软公司推出的汇编语言编译器,它为程序员提供了编写、编译和调试汇编代码的强大工具。MASM2012.5是MASM系列的...
DOS汇编语言编译器最后版本MASM 5.10B MASM 5.00 MASM 5.00比4.00在速度上快了很多,并将段定义的伪指令简化为类似 .code与.data之类的定义方式,同时增加了对80386处理器指令的支持,对4.00版本的兼容性很好 MASM ...
标题中的“MASM50.MASM51.MASM60.MASM611.MASM611FULL.MASM614.MASM615工具”指的是Microsoft Macro Assembler(MASM)的不同版本。MASM是微软公司开发的一款汇编语言编译器,用于将汇编语言源代码转换为机器可执行...
**MASM5与MASM615:汇编语言编程工具的里程碑** MASM,全称为Microsoft Macro Assembler,是微软公司开发的一款汇编语言编译器,它为程序员提供了编写机器代码的能力,使得程序员可以直接对计算机硬件进行精确控制...
【汇编语言与MASM32开发环境】 汇编语言是一种低级编程语言,它将计算机指令与机器语言紧密相连,允许程序员直接控制硬件系统。在计算机科学领域,汇编语言被视为高级语言与机器语言之间的桥梁,对于理解计算机底层...
【汇编语言编程环境 MASM611的设置】 汇编语言是一种低级编程语言,它直接对应于计算机硬件的指令集。MASM(Microsoft Macro Assembler)是微软公司开发的一款汇编语言编译器,版本6.11是其在DOS和早期Windows系统...
在Windows系统中,Microsoft Macro Assembler(MASM)是一款广泛使用的汇编器,由微软公司开发,用于编写针对Intel架构处理器的汇编程序。 **MASM简介** MASM是微软提供的高级汇编器,它支持Intel的x86和x64指令集...
**汇编语言与MASM简介** 汇编语言是一种低级编程语言,它是计算机硬件可以直接理解和执行的语言。在软件开发中,尽管高级编程语言如C++、Java等更常见,但汇编语言因其对硬件的直接控制能力,在某些特定领域如系统...