第三章 使用MASM
- 格式:doc
- 大小:41.00 KB
- 文档页数:6
主题:[07计应(3)班] | | 浏览数(1568) | 评论数(0) | 2008-12-16 1 实验目的⒈ 实验MASM 6.11的安装方法。
⒉ 实验MASM 6.11的启动方法。
⒊ 实验MASM 6.11的设置方法。
2 预习要求⒈认真阅读实验指导中实验1的内容。
⒉了解本次实验的各项要求。
⒊认真阅读教材中的附录。
3 实验内容⒈ MASM 6.11的安装⒉ MASM 6.11的启动⒊ MASM 6.11的设置4 实验环境⒈ 硬件:PC计算机。
⒉ 软件:Windows 98操作系统。
5 实验过程5.1 MASM6.11的安装5.1.1 安装前的准备工作1. 安装MASM 6.11的操作系统要求为Windows 98。
2. 在D∶\盘或别的硬盘上建一个文件夹。
今假设在D∶\盘上建立一个如下的文件夹:“D∶\xxxxxyyyyyy”,其中“xxxxx”是班级名,“yyyyyy”是本人姓名。
3. 从教师机上下载文件夹“MASM611SETUP”及其内容,并放入文件夹“D∶\xxxxxyyyyyy”。
4. 在文件夹“D∶\xxxxxyyyyyy”下建立一个如下的工作目录:“D∶\xxxxxyyyyyy \mymasm”,以备将来在工作的时候存放MASM 6.11汇编语言源文件、目标文件、可执行文件。
5.1.2 安装过程1. 打开MASM611SETUP文件夹,运行“Setup”。
2. 在出现“Microsoft MASM 6.11 Setup”屏幕之后,按“ENTER”键。
3. 在出现的“Microsoft MASM 6.11 Setup Main Menu”屏幕上,选中“Install the Microsoft Macro Assembler” ,按“ENTER”键。
4. 在出现的“Microsoft MASM 6.11 Setup System Configuration”屏幕上,选中“MS-DOS/Microsoft WINDOWS”,按“ENTER”键。
如何使用宏汇编程序MASM.EXE和连接程序LINK.EXE1、首先在硬盘上建立一个文件夹,例如:D:\MASM.2、下载MASM.EXE和LINK.EXE到D:\MASM文件夹里.3、经过以上两步,我们已经做好了准备工作,接下来书写程序。
我们的程序要求为,计算 y = a × b + c - 18,a、b、c 均为带符号的8位二进制数据,为了实验观看实验结果,我们假设 a = 34H、b = 56H、c = E7H。
程序原代码如下(分号后面内容为注释内容,可以不输入):CC EQU 18 ;伪指令定义CCSTACK SEGMENT ;堆栈段起始DW 256 DUP(?) ;在堆栈段中预留了256个字的内容TOP LABEL WORD ;TOP为栈顶偏移地址STACK ENDS ;堆栈段结束DATA SEGMENT ;数据段起始DAT1 DB 34H ;DAT1即为题中操作数a,为字节,存于数据段中DAT2 DB 56H ;DAT2即为题中操作数b,为字节,存于数据段中DAT3 DB 0E7H ;DAT3即为题中操作数c,为字节,存于数据段中DATY DW ? ;DATY即为题中计算结果的预留空间为字,存于数据段中DATA ENDS ;数据段结束CODE SEGMENT ;代码段起始ASSUME CS:CODE,DS:DATA,SS:STACK ;分配段地址对应关系START: MOV AX,DATA ;START为标号,MOV语句完成DATA数据段地址送入AXMOV DS,AX ;将AX内容送入DS,即赋予DS数据段DATA的段地址MOV AX,STACK ;将STACK堆栈段地址送入AXMOV SS,AX ;将AX内容送入SS,即赋予SS堆栈段STACK的段地址MOV SP,OFFSET TOP ;SP设初值MOV AL,DAT1 ;取a,直接寻址方式MOV BL,DAT2 ;取b,直接寻址方式IMUL BL ;AX <- AL * BL,即将a * b的值存入AXMOV BX,AX ;将AX内容保存至BXMOV AL,DAT3 ;取c,直接寻址方式CBW ;由于c是8位有符号数,所以做符号扩展至AHADD AX,BX ;AX <- AX + BX,即完成的操作为将a*b+c结果存入AXSUB AX,CC ;AX <- AX - 18MOV DATY,AX ;将a*b+c-18的内容存放至数据段中偏移地址DATY的内存中MOV AH,4CH ;将4CH存入AH,为了下句调用DOS 21号中断INT 21H ;调用DOS 21中断,由AH=4CH决定返回DOS操作系统CODE ENDS ;代码段结束END START ;程序结束在D:\MASM目录下新建文件名为“Sample.asm”(一定要注意扩展名必须为.asm或.ASM,大小写无所谓)的文件,将以上代码输入后保存。
初学者的MASM5.0使用方法(能保持命令界面的)Cyinsilence(一)首先下载masm5.0(简单,适合初学者),解压到某个盘里(如D:\masm);(二)在随意一个盘里(如F盘)新建一个文件夹(如masm8),把解压后的文件夹里的masm.exe和link.exe复制进masm8里(这里我们要求里面至少应包含masm.exe和link.exe,可能还包含其他的,不过我们只需要最基本的这两个)。
然后就在新建文件夹masm8中建立个文本文件(如test2.txt)写入个汇编程序,保存,将后缀名改为.asm。
(如test2.asm) 这里最好复制一份test2.txt做以后修改用。
这里有一个比较好的汇编程序:DATA SEGMENTMESSAGE DB 'This is asample program of keyboard and display:'DB 'Please strike thekey !',0DH,0AH,'$'DATA ENDSSTACK SEGMENT PARA STACK 'STACK'DB 50 DUP(?)STACK ENDSCODE SEGMENTASSUME CS:CODE,DS:DATA,SS:STACKSTART: MOV AX,DATAMOV DS,AXMOV DX,OFFSET MESSAGEMOV AH,9INT 21HAGAIN: MOV AH,1INT 21HCMP AL,1BHJE EXITCMP AL,61HJC NDCMP AL,7AHJA NDAND AL,11011111BND: MOV DL,ALMOV AH,2INT 21HJMP AGAINEXIT: MOV AH,4CHINT 21HCODE ENDSEND START(三)进入命令提示行(开始--运行--输入cmd)(四)进入程序所在文件夹,输入masm8所在盘让计算机找到masm8,刚刚我们假设在F盘建立masm8,就输入“F:”回车,键入“cd masm8”,回车,键入“masm test2”,回车,如果程序正确会在文件夹里生成test2.obj,并出现如下界面:之后根据提示输入就可以了,如下图:摁回车后会有如下界面:上述步骤是使计算机执行了MASM.EXE,接下来我们执行LINK.EXE。
第一部分汇编指令学习在教材第三章学习8088的指令系统(指令说明详见教材附录1),掌握指令是用汇编语言编程的基础,这要求我们熟练掌握80X86的每条指令。
学习汇编指令一方面要深刻理解指令的寻址方式,另一方面重在从以下四点来学习识记指令。
1.指令符号识记2.指令格式和操作数3.指令功能4.指令执行对外界的影响例如:加法指令助记符是:ADD指令格式是:ADD OPRD1 , OPRD2OPRD1是任一寄存器操作数或存储器操作数OPRD2是任一寄存器操作数或存储器操作数或是立即数指令影响:影响CF , SF , OF, PF, ZF , AF 标志位指令功能:完成无符号数或有符号数的加法运算,操作数可以是8位(字节)或16位(字)如果我们象上面所述那样熟悉汇编语言指令系统中的每一条常用指令的助记符、指令格式、指令的功能和执行结果对外界的影响,那么,我们应用汇编语言编程时就不存在工具带给我们的困难。
阅读下面两道程序/程序段,建议按上面所述来理解每条指令和程序功能。
程序1code segmentassume cs:codestart: mov ah , 1int 21hcmp al , '0'jb lab1cmp al , '9'ja lab2mov dl , 'n'jmp lab3lab2: cmp al , 'a'jb lab1cmp al , 'z'ja lab4mov dl , 'u'jmp lab3lab4: cmp al , 'a'jb lab1cmp al , 'z'ja lab1mov dl , 'l'jmp lab3lab1: mov dl , '*'lab3: mov ah , 2int 21hmov ah , 4chint 21hcode endsend start —————————————————————————————————程序2code segmentassume cs:codestart: mov cx , 26mov dl , 'a'l ab1: mov ah , 2int 21hadd dl , 1loop lab1mov ax , 4c00hint 21hcode endsend start第二部分汇编程序的建立、执行与调试一、汇编程序建立、调试的软件环境用汇编语言书写程序,从源程序的编辑、编译到调试,我们使用以下工具: 编辑程序一个编辑环境,用于建立源程序masm.exe 编译程序检查源程序的句、语法,生成OBJ文件link.exe 链接程序将OBJ文件转换成EXE文件 调试程序一个帮助检查程序中设计错误的工具将上述工具拷贝到包含DOS系统的软盘上,就在软盘上建立了编辑、汇编、连接和调试汇编程序的环境。
Win32汇编--使⽤MASM经过上⼀讲的准备⼯作,相信⼤家已经搭建好了 Win32 汇编的⼯作环境,并已经知道编译、链接⼀个程序的过程和原理了。
现在,我们让例⼦回归到经典:include <stdio.h>int main(void){Printf(“Hello, world\n”);} // 事实上想想,这不正是初⽣的婴⼉?!⿇雀虽⼩,五脏俱全。
刚刚那个C语⾔的”Hello, world”程序包含了C语⾔中的最基本的格式。
在C语⾔的源程序中,我们不需要为堆栈段、数据段和代码段的定义⽽烦恼,编译器会⾃⼰解决。
回顾⼀下,在DOS 下的汇编这段代码会变成什么样? Follow me!;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>; 堆栈段;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>stack segmentdb 100 dup (?) ;定义100个字节的内存存储单元空间,默认值为?stack ends ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>; 数据段;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>data segmentszHello db ‘Hello, world’,0dh,0ah,’$’data ends ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>; 代码段;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>code segmentassume cs:code,ds:data,ss:stackstart:mov ax,datamov ds,axmov ah,9mov dx,offset szHelloint 21hmov ah,4chint 21hcode endsend start在例⼦中我们看到,stack、data、code都找到了⾃⼰的⼩窝。
连接无论是单模块程序,还是多模块程序,虽然在对每一个模块汇编后都生成了相应的目标文件.OBJ,但它们却是不可运行的。
只有在将它们连接(LINK)后,才能生成一个可执行(运行)的.EXE文件或程序。
8086目标模块连接程序LINK,能够把一个或多个独立的目标文件.OBJ和定义于库文件.LIB中的子程序与变量,连接装配成一个可重定位的可执行文件.EXE。
在连接过程中,除了生成.EXE程序外,用户还可以根据需要,生成相应的内存映像文件.MAP。
LINK的操作在DOS环境下使用LINK,同使用MASM一样,也有提示应答和命令行两种使用法。
1.提示应答方式在DOS提示状态下,键入LINK命令后,便显示版权信息,接着依次显示目标模块文件名,可执行文件的文件名、映像文件的文件名和库文件名等提示信息。
用户只需根据提示信息,键入或选用相应的文件名就可以了。
例如:C:\MASM >LINKMicrosoft ® Corp. Overlay Linker Version 3.60Copyright © Microsoft Corp. 1983-1987,All rights reservedObject Modules[.OBJ]:EXAMPLE↙—连接EXAMPLE.OBJ目标模块Run File [EXAMPLE.EXE]:↙—生成的默认EXAMPLE.EXE文件List File[NUL.MAP]:↙—默认不生成.MAP文件Libraries[.LIB]: ↙—连接时不需要库文件如果需要连接多个目标模块,则在相邻模块文件名间用空格(Space)或“+”号隔开。
如果选用默认文件名,只需按回车键就可以了。
通常,汇编程序不需要任何库文件,这时也只需按回车键就行了。
假设有MODA.OBJ、MODB.OBJ和MODC.OBJ三个目标模块,程序中调用了MA TH.LIB 库中的某些子程序,且该库文件存放在B盘上的lib子目标中,希望连接时能生成MODA.EXE和MODA.MAP。
汇编学习笔记(14)-段定义(MASM)完整的段定义 格式 段名 SEGMENT [定位类型] [组合类型] ['类别'] XXXXXXXXX 段名 ENDS 说明 定位类型 定位类型就是这个段的起始位置的对齐⽅式,有如下⼏种 BYTE 没有对齐的要求,随便起始地址是哪⾥ WORD 起始地址的最后⼀位是0,即地址是 XXXX XXXX XXXX XXX0 DWORD 起始地址的最后⼆位是0,即地址是 XXXX XXXX XXXX XX00 PARA 起始地址的最后四位是0,即地址是 XXXX XXXX XXXX 0000 PAGE 起始地址的最后⼋位是0,即地址是 XXXX XXXX 0000 0000 组合类型 就是遇到名字和类型相同的段的时候他们直接怎么合并,有如下⼏种 PUBLIC 直接按照顺序拼接在⼀起,总长度就是所有段加起来的长度 COMMOM 这个就相当于C中的联合体 UNION, 互相重叠在⼀起,总长度就是最长的那个的长度 STACK 表明这是⼀个堆栈段,和PUBLIC采⽤相同的规则,拼接起来 类别 ⽤ ' ' 包起来的⼀个⾃定义的字符串,相当于在段名的基础上加了个⼩标,只有段名和类别都⼀致才认为是⼀个段 举例 CSEG SEGMENT BYTE PUBLIC 'DATA' XXXXXXXXX CSEG ENDS 其他说明 段组 格式 组名 GROUP 段名1,段名2 举例 DS1S2 GROUP DSG1, DSG2 说明 每个段寻址的时候都是使⽤段地址:偏移地址的⽅式寻址,如果存在多个段,那么在使⽤的时候就需要频繁的切换段地址可能是DS寄存器,也可能是ES寄存器,这就很⿇烦,使⽤段组就相当于给这些段起了个总的段地址, 所有段中的变量都可以使⽤这个总的段地址, 同时原来的段地址也还是可以正常使⽤的,不有有⼀下两点注意 1. ASSUME 的 DS必须设置为组名 ASSUM DS:DS1S2 2. 取地址的时候必须使⽤如下格式 OFFSET DS1S2 VAR1简化的段定义 前提 需要使⽤ .MODEL [类型] 的⽅式来告知汇编器程序的存储模型 .MODEL SMALE 程序和数据都不超过64K .MODEL MEDIUM 数据⼩于64K 代码不⼀定 .MODEL COMPACT 代码⼩于64K 数据数据不⼀定,但是单个数组肯定⼩于64K .MODEL LARGE 代码和数据都可能⼤于64K,但是单个数组肯定⼩于64K .MODEL HUGE 代码和数据都可能⼤于64K,⽽单个数组可能也⼤于64K .MODEL指令使⽤的时候相当于同时执⾏了如下伪指令 DGROUP GROUP DATA,CONST,BSS,STACK ASSUME CS:_TEXT,DS:DGROUP,SS:DGROUP 简化的段定义 .CODE 代码段的简化定义,遇到下⼀个段的定义或者遇到END表⽰该段结束 .STACK [⼤⼩] 堆栈段的简化定义,遇到下⼀个段的定义表⽰该段结束, ⼤⼩表明这个段有多⼤ .DATA 数据段的简化定义,遇到下⼀个段的定义表⽰该段结束 .DATA? 未初始化的数据段的简化定义,遇到下⼀个段的定义表⽰该段结束 .CONST 常数段的简化定义,遇到下⼀个段的定义表⽰该段结束 .FARDATA [名字] 其他⾃定义段的简化定义,名字表⽰段的名称 例⼦ .MODEL SMALL .STACK 1024 .DATA MESS: DB 'HELLO',0DH,0AH .CODE START : MOV AX, DGROUP MOV DX,AX MOV DX,OFFSET MESS MOV AH,9 INT 21H MOV AX, 4C00H INT 21H ASSUME DS:@CODE ;可以⼿动重设段地址,简化的段名使⽤@符号引⽤ END START 函数的导出与引⽤ 说明 汇编程序可以分别汇编多段程序,然后使⽤LINK程序将它们链接成⼀个完成程序,它们之后需要使⽤CALL来互相调⽤,那么在别写的时候就需要知道其他模块的函数,⽽本⾝给别⼈调⽤的时候也要告诉别⼈⾃⼰的函数名 PUBLIC 公开⾃⼰的函数或者变量 格式 PUBLIC A,B,C 说明 A,B,C 可以是函数名,也可以是变量名 EXTRN 告诉汇编器,这些函数或者变量是在别的模块中定义的 格式 EXTREN 名称:类型 举例 EXTREN PROC1:NEAR,PROC2:FAR EXTREN VAR1:WORD,VAR2:BYTE 说明 由于链接的时候很能确定两段代码时候能在同⼀个段中,所以函数的声明最好永远是使⽤FAR调⽤的 程序例⼦ 模块1 .MODEL SMALL PUBLIC VAR1,VAR2 PUBLIC DELAY .DATA VAR1 DW ? VAR2 DB ? VAR3 DB 5 DUP (0) .CODE DELAY PROC LAB1: RET DELAY ENDP END 模块2 .MODEL SMALL EXTRN VAR1:WORD,VAR2:BYTE EXTRN DELAY:FAR .CODE START: MOV AX, VAR1 MOV AL, VAR2 CALL FAR PTR DELAY END SATRT。
实验一汇编程序MASM的使用Microsoft公司提供了两种汇编程序版本,一种是全型版本宏汇编MASM;一种是小型版本ASM。
ASM的功能是MASM功能的一个子集,它不支持宏汇编、条件汇编等。
所以我们主要讲解MASM的使用。
一、实验环境1.为了对包括Pentium指令在内的汇编语言的源程序文件进行汇编,应有一台Pentium以上的计算机。
操作系统使用DOS5.0以上版本或windows操作系统命令提示符。
2.编辑程序可用EDIT、WPS、WORD等。
3.MASM 5.0以上宏汇编系统,包括:(1)MASM:汇编程序(2)LINK:连接程序(3)CREF:索引程序(4)LIB:库管理程序4.DEBUG调试程序说明:后面的内容假定在E盘上建立了MASM子目录,且上述程序都在该目录中或建立了相应路径。
二、汇编语言源程序上机操作过程用汇编语言编制程序,一般要经历如下步骤:1.编辑源文件用编辑程序EDIT或其它编辑程序建立源程序。
源程序文件必须以.ASM位扩展名存盘。
2.汇编源程序。
由编辑程序所建立的.ASM文件,只是个文本文件,计算机无法执行。
需要经过汇编程序的汇编,使其转为目标代码文件。
它是通过调用宏汇编MASM 程序完成这一汇编操作的汇编程序还将对原文件进行检查,如发现有错,将给出错误原因。
程序设计者可根据它分析和修改源程序,并再经过汇编,直到无误为止,此时得到的是一个目标文件(.OBJ)。
汇编源程序过程如下:先启动DOS,然后才进入汇编环境。
假设MASM系统(以MASM5.0为例)已预先装在硬盘的MASM子目录中。
下面均以E盘上操作为例进行说明。
假设事先用EDIT编辑程序编辑的源程序为EXAM1.ASM,则汇编过程如下:E:\MASM\masmMicrosoft (R) Macro Assembler Version 5.00Copyright (C) Microsoft Corp 1981-1985,1987.All rights reserved.Source filename[.ASM]:EXAM1.ASMSource listing [NUL.LST]:Cross-reference [NUL.CRF]:51698+417454 Bytes symbol space freeO Warning ErrorsO Severe Errors宏汇编程序被调入以后,先显示版本号。
实验二汇编编译工具MASM的使用2.1. 实验目的1、学习如何安装MASM;2、学习如何使用和使用编译和链接汇编语言程序。
2.2. 基础知识2、DOS汇编与WIN32汇编在DOS下编写汇编程序,程序员可以管理系统的所有资源,访问和改动系统中所有的内存,修改内存控制块,修改中断向量表截获中断,直接对I/O端口进行读写。
DOS是个单任务操作系统,只有一个运行级别,任何进程和DOS操作系统都是同等级别的。
因此,DOS系统中各个进程会互相影响,如果某个进程死机的话,整个系统都会垮掉。
DOS工作在实模式下,程序员可以寻址1M的内存,通过段寄存器来指定段的初始地址,每个段的大小为64K。
超过1M的部分,就只能把他作为XMS使用。
1M以上的内存空间只能用作数据存放而无法在其中执行程序。
Win32是指32位的Windows操作系统,进程有多种运行级别,操作系统工作在最高级别——0级,而应用程序工作在3级。
在第三级别,进程无法直接访问I/O端口,无法访问其它进程运行的内存,连向自己的运行代码写入数据都是非法的。
只有对于最高级别的进程,系统才是全开放的。
Windows工作在保护模式下,所有的资源对进程来说都是被“保护”的。
在内存方面,Windows使用了处理器的分页机制,程序员不必用一个段寄存器去指定段的地址,因为在保护模式下,段寄存器的含义是不同的(参见80386手册方面的书籍)。
程序员可以直接指定一个32位的地址来寻址4GB的内存。
在程序结构方面,Windows程序也有很大的不同,它是“基于消息”的。
在上面列举的汇编工具中,一些是DOS汇编工具,一些是Win32汇编工具。
初学者可以从DOS汇编入手,先掌握汇编编程的思路、基本语法和编程技巧,待入门后再学习Win32汇编。
当然,学习DOS汇编并不一定需要安装和使用DOS操作系统。
程序员可以在Win32环境中安装MASM 6.0或其它16位汇编工具,在命令提示符状态下编译和链接DOS汇编程序。
标题:【原创】windows下32位汇编语言学习笔记第三章使用MASM作者:jasonnbfan时间: 2009-05-03,02:48:43链接: /showthread.php?t=87752windows下32位汇编语言学习笔记第三章使用MASM本章讲述的是masm 汇编的程序结构,基本语法,定义等,本章这些内容只是汇编指令里比较常用的,在下面的章节将要用到的指令。
实际上汇编指令远不止这些。
感兴趣可以参照其他的汇编书籍了解一下。
不过对于本书下面的章节来说,这些指令基本上够用了。
Win32汇编程序的基本结构从例子可以看出来,Win32汇编的结构很简单,下面简单分析下。
模式定义.386.model falt,stdcalloption casemap:none这个地方书上已经将的很清楚了。
关于.386 .486 .586 .686 之类的指令集,我没找到资料,试验了一下写成.686也没什么问题。
include includelib语句include windows.incincludelib kernel32.lib这里的include 和C语言里的include 头文件一个道理,都是导入预先声明好的函数,包括定义好的各种结构。
includelib 就是指定连接的时候告诉连接器从那个lib里找你通过include引入并使用的函数,win32API都是以动态链接库的形式提供的,所以这里就需要对你使用的winAPI包含在那个dll里做到心中有数,不知道的就查msdn,每个API说明后面都有这个API包含在那个头文件中,比如:Header: Declared in Winuser.h; include Windows.h.winAPI是C语言写的,所以头文件都是.h的,汇编的头文件声明是.inc的,打开kernel32.inc 找找Exitprocess 的申明 ExitProcess PROTO :DWORD你也可以不用预定义的.inc头文件,自己定义。
标题:【原创】windows下32位汇编语言学习笔记第三章使用MASM作者:jasonnbfan时间: 2009-05-03,02:48:43链接: /showthread.php?t=87752windows下32位汇编语言学习笔记第三章使用MASM本章讲述的是masm 汇编的程序结构,基本语法,定义等,本章这些内容只是汇编指令里比较常用的,在下面的章节将要用到的指令。
实际上汇编指令远不止这些。
感兴趣可以参照其他的汇编书籍了解一下。
不过对于本书下面的章节来说,这些指令基本上够用了。
Win32汇编程序的基本结构从例子可以看出来,Win32汇编的结构很简单,下面简单分析下。
模式定义.386.model falt,stdcalloption casemap:none这个地方书上已经将的很清楚了。
关于.386 .486 .586 .686 之类的指令集,我没找到资料,试验了一下写成.686也没什么问题。
include includelib语句include windows.incincludelib kernel32.lib这里的include 和C语言里的include 头文件一个道理,都是导入预先声明好的函数,包括定义好的各种结构。
includelib 就是指定连接的时候告诉连接器从那个lib里找你通过include引入并使用的函数,win32API都是以动态链接库的形式提供的,所以这里就需要对你使用的winAPI包含在那个dll里做到心中有数,不知道的就查msdn,每个API说明后面都有这个API包含在那个头文件中,比如:Header: Declared in Winuser.h; include Windows.h.winAPI是C语言写的,所以头文件都是.h的,汇编的头文件声明是.inc的,打开kernel32.inc 找找Exitprocess 的申明 ExitProcess PROTO :DWORD你也可以不用预定义的.inc头文件,自己定义。
如果你使用了函数确没有包含对应的.lib,比如使用了ExitProcess函数,没有includelib kernel32.lib,连接时就会报错:error LNK2001: 无法解析的外部符号 __imp__ExitProcess@4这个外部符号名就是你要调用的函数,名字很诡异吧,这里先有个了解,讲到调用约定的时候再详细说明。
段定义,程序结束和入口..data ;全局变量段szTest db '消息窗内容',0szCaption db '消息窗标题',0.code 代码段start:invoke MessageBox,NULL,offset szText,offset szCaption,MB_OKend start这里的段就是PE格式里的Section (区块),一个PE文件(可执行)最少包含2个区块,代码块和数据块,区块的名只是方面记忆,对于系统来说是无关紧要的。
区块是按内存页对齐的(0x1000 4K)。
区块的类型很多,比如.idata包含导入表,.rsrc,包含资源文件等等。
但是,任何时候不要通过区块名字来定位区块,从PE结构的IMAGE_SECTION_HEADER来定位区块才是正确的做法,因为区块名字是可以任意的。
关于PE结构的说明,我见过的最详细的就是“加密与解密第三版”第10章的介绍,大家可以去看看。
编译本章的hello.asm 用OD打开exe可以看见有3个区段,.text .rdata 和 .data ,.text 就是代码里的.code 段,.data就是代码里的.data段,.rdata没人定义怎么自己冒出来了,其实这就是hello.exe的导入表,因为程序里用到了2个外部dll函数,MessageBox,ExitProcess。
这个段就是编译器自动生成的。
至于为什么叫.rdata,刚才说了,名字不是重要的,只是帮助记忆,导入表区段有的名字可能就是.idata。
另外还需要注意,程序的入口必须自己指定,汇编里没有Main这样的程序执行起点,这点别忘了。
变量名,变量,数据结构这个地方没啥好说的,多看,多写,慢慢就习惯了,值得注意的地方就是,变量的命名方式一定要按照后面代码风格所说,按照匈牙利表示法来命名,从一开始就养成一个好习惯。
子程序,函数的定义和使用调用约定和名称修饰符除了书上将的_cdecl,_stdcall等,还有一种c++builder里常用的_fastcall调用,__fastcall调用也是被调用的函数负责清栈,参数的传递规则是,从左边开始不大于4字节的参数分别用edx,ecx传递,其他参数遵循从又右到左的顺序通过堆栈传递。
c c++在内部是通过函数修饰符来识别函数的,由编译器在编译时生成函数名称修饰符,而且,不同的调用约定不同的语言生成的修饰符定义名称不同,所以有必要了解一下函数的名称修饰符。
例子函数:int max(int,int);对于C语言_cdecl调用名称修饰符是在函数前加一个下划线:_max_fastcall调用名称修饰符在函数前加一个@后面加一个@紧跟参数字节数:@max@8_stdcall调用名称修饰符在函数前加一个_后面加一个@紧跟参数字节数:_max@8对于C++语言,不管任何调用约定,描述符都以?开头后边更函数名,然后是根据参数表查出的返回值类型,然后是参数类型,最有以@Z结束?+函数名+调用规则名+返回类型+参数类型(从左到右)+@Z其中调用规则名表:_cdecl:@@YA,_stdcall:@@YG,_fastcall:YI标示符:参数类型X:void,D:char,E:unsigned char,F:short,H:int,I:unsigned int,J:long,K:unsigned long,M:float,N:double,_N:bool,U:St ruct指针:PA,const指针:PB对于max函数修饰名称就是:?max@@Y?HHH@Z。
这里给了个问号,意思就是不同的调用规则就更具调用规则表变化,其他不变。
很明显,C++的修饰更为详细。
现在回过头看看刚才的错误提示:error LNK2001: 无法解析的外部符号 __imp__ExitProcess@4__imp_ 这个是代表函数ExitProcess是从外部导入的,后面的_ExitProcess@4很明显参数是四字节的和ExitProcess(UNIT uExitCode)相符实际上对于C++的类成员函数,描述符的规则又有不同,但是,如果你写的DLL动态链接库使用自定义类,估计没人会用的,使用类了就不能通用了。
MASM的优化都知道汇编效率高,但是MASM编译出的EXE真的就是最佳优化的么?让我们看看本章中的hello.exe 用OD反汇编看看是不是这样。
反汇编内容:00011000 >/$ 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL00011002 |. 68 00300100 PUSH Hello.00013000 ; |Title = "A MessageBox !"00011007 |. 68 0F300100 PUSH Hello.0001300F ; |Text = "Hello, World !"0001100C |. 6A 00 PUSH 0 ; |hOwner = NULL0001100E |. E8 07000000 CALL <JMP.&user32.MessageBoxA> ; \MessageBoxA00011013 |. 6A 00 PUSH 0 ; /ExitCode = 000011015 \. E8 06000000 CALL <JMP.&kernel32.ExitProcess> ; \ExitProcess0001101A $- FF25 08200100 JMP NEAR DWORD PTR DS:[<&user32.Mess>; user32.MessageBoxA 00011020 .- FF25 00200100 JMP NEAR DWORD PTR DS:[<&kernel32.Ex>; kernel32.ExitProcess看看那2个CALL,一个调用MessageBoxA,一个调用ExitProcess,这个JMP产生了额外的代码,并且增加执行时间,产生这样的代码是因为编译器不知道你调用的函数是从外部导入的。
如果编译器预先知道这个函数是从外部引入的,编译器就会把CALL后面的地址直接指向,PE文件的IA T(import_address_table)输入表中的函数地址,当程序运行时由系统加载器更新IAT表(如果需要的话),这样就调用了函数在DLL中的正确地址,避免了这种低效能的调用方式。
高级语言,比如C语言在引入外部DLL函数时,再dll头文件里对于每一个函数都有一个描述 __declspec(dllimport),这就是告诉编译器,这个函数是从外部引入的,从而提高空间和时间效率。
看看C写的,功能呢个同样的代码,编译后的反汇编内容:00401000 /$ 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL 00401002 |. 68 00304000 PUSH HelloMsg.00403000 ; |Title = "HelloMsg"00401007 |. 68 0C304000 PUSH HelloMsg.0040300C ; |Text = "Hello, Windows 98!" 0040100C |. 6A 00 PUSH 0 ; |hOwner = NULL0040100E |. FF15 AC204000 CALL NEAR DWORD PTR DS:[<&USER32.MessageBoxA>] ; \MessageB oxA00401014 |. 33C0 XOR EAX, EAX00401016 \. C2 1000 RETN 10这个MessageBoxA的CALL才是效率最高的call!但是悲剧的是在masm里我们无法用任何描述告诉编译器,当前使用的函数是从外部引入的。
结果就是使用效率最高的语言确产生了效率最低的外部函数调用...有没有办法解决,确实有,我google了一下,发现了一段代码。