`
lobin
  • 浏览: 432047 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

MASM

 
阅读更多

 

 

汇编语言是一种最接近机器语言的编程语言。

 

汇编学习研究或者开发实践

 

选一个平台,比如是在给哪个目标平台上开发汇编程序,如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

Opcode Instruction Op/En 64-Bit Mode Compat/Leg Mode Description
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

Opcode Instruction Op/En 64-Bit Mode Compat/Leg Mode Description
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指令调用实际上还有多种形式,可以把他们分为近距离调用(near),远距离调用(far),相对地址调用(relative),绝对地址调用(absolute),间接绝对地址调用(absolute indirect)

 

CALL — Call Procedure

Opcode Instruction Op/En 64-bit Mode Compat/Leg Mode Description
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

 

 

 

中断

参考另一篇文章:https://www.iteye.com/blog/lobin-2038516

 

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

 

 

1、http://www.ref.x86asm.net/

2、https://www.felixcloutier.com/x86/

 

 

 

 

 

 

 

 

 
 
 
 

0
1
分享到:
评论

相关推荐

    masm615和masm5

    本文将详细介绍masm615和masm5这两个汇编编译器,并探讨它们在汇编指令学习中的应用。 首先,让我们来看看masm615。MASM,全称Microsoft Macro Assembler,是微软公司开发的一个汇编器,主要用于生成IBM PC兼容机上...

    masm6.11与masm6.15安装包

    **MASM,全称Microsoft Macro Assembler,是微软公司推出的一款宏汇编器,用于将汇编语言程序转换成机器代码。MASM6.11和MASM6.15是MASM的不同版本,这两个版本在汇编语言编程领域都有其特定的应用场景和特点。** *...

    masm masm6.15 64位

    **MASM,全称Microsoft Macro Assembler,是微软公司推出的一款高级汇编语言编译器,主要用于编写基于x86和x64架构的程序。MASM 6.15是MASM的一个版本,它支持64位指令集,使得开发者能够创建针对64位操作系统的应用...

    MASM 6.15完整版

    **MASM 6.15 完整版详解** **一、MASM介绍** MASM,全称为Microsoft Macro Assembler,是由微软公司开发的一款高级汇编语言编译器。MASM支持Intel架构下的x86指令集,广泛应用于编写低级系统程序、驱动程序以及对...

    MASM各版本大全

    **MASM全称是Microsoft Macro Assembler,是微软公司推出的一种宏汇编器,用于将汇编语言程序转换成机器代码。它在个人计算机编程领域,尤其是在早期的DOS和Windows系统开发中扮演了重要角色。以下是关于MASM各版本...

    Masm6.15 完整版 full

    **汇编语言编程工具MASM6.15详解** MASM(Microsoft Macro Assembler)是微软公司开发的一款宏汇编器,它为程序员提供了编写低级机器代码的能力,适用于IBM PC兼容机上的Intel x86架构。MASM6.15是MASM的一个版本,...

    masm5.0 win32

    【标题】"masm5.0 win32" 指的是MicroAssember System (MASM) 5.0版本在Windows 32位操作系统上的应用。MASM是一款流行的x86汇编语言编译器,由Microsoft开发,用于编写针对Intel 8086及其后续处理器的低级代码。 ...

    MASM编译器 DOSBOX版本

    **MASM编译器 DOSBOX版本**是一种在现代操作系统中模拟DOS环境的解决方案,用于运行和调试使用MASM(Microsoft Assembler)编写的汇编语言程序。DOSBOX是一个开源的DOS模拟器,它允许用户在不支持DOS的操作系统上...

    内含MASM 6.15完整版和DOSBOX(WIN7 64位下使用MASM)

    2. 接着,输入`cd &lt;你的MASM 6.15安装目录&gt;`,例如:`cd "C:\Program Files\MASM615"`,进入MASM 6.15的根目录。 3. 输入`masm`启动MASM 6.15。现在你可以像在DOS环境下一样,编写、编译和链接汇编语言程序了。 **...

    汇编MASM6.15官方原版

    **汇编语言MASM6.15详解** 汇编语言是一种低级编程语言,它与计算机硬件紧密相连,每一行代码直接对应机器指令。MASM(Microsoft Macro Assembler)是微软公司开发的一款流行的汇编器,用于编写针对x86架构的程序。...

    汇编编辑器MASM6.15

    汇编编辑器MASM6.15 MASM是微软公司开发的汇编开发环境,拥有可视化的开发界面,使开发人员不必再使用DOS环境进行汇编的开发,编译速度快,支持80x86汇编以及Win32Asm是Windows下开发汇编的利器。它与windows平台的...

    Masm6.15.zip

    **汇编语言与Masm6.15** 汇编语言是一种低级编程语言,它将计算机指令以人类可读的形式表示出来。每条汇编指令通常对应一个机器码,直接控制计算机硬件的操作,如移动数据、执行算术运算、逻辑操作以及控制程序流程...

    MASM2012.5

    **MASM2012.5:深入了解汇编语言的集成开发环境** MASM,全称为Microsoft Macro Assembler,是一款由微软公司推出的汇编语言编译器,它为程序员提供了编写、编译和调试汇编代码的强大工具。MASM2012.5是MASM系列的...

    最后的纯dos版本MASM 5.10B

    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工具

    标题中的“MASM50.MASM51.MASM60.MASM611.MASM611FULL.MASM614.MASM615工具”指的是Microsoft Macro Assembler(MASM)的不同版本。MASM是微软公司开发的一款汇编语言编译器,用于将汇编语言源代码转换为机器可执行...

    MASM5+MASM615

    **MASM5与MASM615:汇编语言编程工具的里程碑** MASM,全称为Microsoft Macro Assembler,是微软公司开发的一款汇编语言编译器,它为程序员提供了编写机器代码的能力,使得程序员可以直接对计算机硬件进行精确控制...

    masm32v11r免安装文件

    【汇编语言与MASM32开发环境】 汇编语言是一种低级编程语言,它将计算机指令与机器语言紧密相连,允许程序员直接控制硬件系统。在计算机科学领域,汇编语言被视为高级语言与机器语言之间的桥梁,对于理解计算机底层...

    汇编语言编程环境 MASM611的设置

    【汇编语言编程环境 MASM611的设置】 汇编语言是一种低级编程语言,它直接对应于计算机硬件的指令集。MASM(Microsoft Macro Assembler)是微软公司开发的一款汇编语言编译器,版本6.11是其在DOS和早期Windows系统...

    微软官方MASM教学 & Intel手册

    在Windows系统中,Microsoft Macro Assembler(MASM)是一款广泛使用的汇编器,由微软公司开发,用于编写针对Intel架构处理器的汇编程序。 **MASM简介** MASM是微软提供的高级汇编器,它支持Intel的x86和x64指令集...

    masm5和masm615

    **汇编语言与MASM简介** 汇编语言是一种低级编程语言,它是计算机硬件可以直接理解和执行的语言。在软件开发中,尽管高级编程语言如C++、Java等更常见,但汇编语言因其对硬件的直接控制能力,在某些特定领域如系统...

Global site tag (gtag.js) - Google Analytics