WIN32汇编语言教程:第03章 使用MASM · 3.1 Win32汇编源程序的结构(1)

经过第02章的准备工作,相信大家都已经建好了Win32汇编的工作环境,并已经可以编译程序了,现在让我们来编译所带光盘的Chapter03\HelloWorld目录下的Hello World程序。这是一个相当小的程序,就和DOS时代下经典的Hello World程序一样,并没有涉及系统中很多的东西,甚至连Windows系统中基本的消息驱动机制也没有看到,它只是简单地弹出一个消息框,在上面显示了一句“Hello,World!”,并在文字的下面显示了一个“确定”按钮,就停在那里了,当用户按下“确定”按钮,程序就退出了,同时消息框消失。这个程序运行的结果如图3.1所示。

但这样一个小程序从结构来看,却“麻雀虽小,五脏俱全”,用它来举例说明Win32汇编源程序的框架是最合适不过的了,本章将从这个程序出发,探讨MASM在Win32汇编中的用法,由于篇幅所限,内容只涉及MASM在Win32编程中常用的部分。

3.1  Win32汇编源程序的结构

任何种类的语言,总是有基本的源程序结构规范,在讨论C语言的书中,大家都会记得这个非常经典的Hello World程序:

#include <stdio.h>
main()
{
        printf("Hello, world\n");
}

像这样一个程序,就说明了C语言中最基本的格式,main()中的括号和下面的花括号说明了一个函数的定义方法,printf语句说明了一个函数的调用方法,调用函数语句后面的分号也是基本的格式。C是一种高级语言,在C源程序中,不必为堆栈段、数据段和代码段的定义而担心,编译器会把程序中的字符串和语句代码分别放到它们该去的地方,程序开始执行的时候也会自己找到main()函数。而汇编是低级语言,必须为所有的东西找到它们该去的地方,所以在DOS的汇编中,Hello World又长成了这样一副模样:

;#############################################################
; 堆栈段
;#############################################################
stack             segment stack
                  db       100 dup (?)
stack             ends
;#############################################################
; 数据段
;#############################################################
data              segment
szHello           db       'Hello, world',0dh,0ah,'$'
data              ends
;#############################################################
; 代码段
;#############################################################
code              segment
                  assume   cs:code,ds:data,ss:stack
start:
                  mov      ax,data
                  mov      ds,ax
 
                  mov      ah,9
                  mov      dx,offset szHello
                  int      21h
 
                  mov      ah,4ch
                  int      21h
code              ends
                  end      start

在这个源程序中,stack段为堆栈找了个家,hello world字符串则跑到数据段中去了,代码则放在代码段中,程序的开始语句必须由最后一句end start来说明应该从start这个标号开始执行,整个程序在使用过DOS汇编的程序员眼里是非常的熟悉。

到了Win32汇编的时候,程序的基本结构还是如此,先来看一看这个看起来很新鲜的Win32的Hello world程序:

               .386
               .model flat,stdcall
               option casemap:none
;####################################################################
; Include 文件定义
;####################################################################
include        windows.inc
include        user32.inc
includelib     user32.lib
include        kernel32.inc
includelib     kernel32.lib
;####################################################################
; 数据段
;####################################################################
               .data
 
szCaption      db     'A MessageBox !',0
szText         db     'Hello, World !',0
;####################################################################
; 代码段
;####################################################################
               .code
start:
               invoke MessageBox,NULL,offset szText,offset szCaption,MB_OK
               invoke ExitProcess,NULL
;####################################################################
               end    start

怎么样,看来和上面的C以及DOS汇编又不同了吧!但从include,.data和.code等语句“顾名思义”也能看出一点苗头来,include应该就是包含别的文件,.data想必是数据段,.code应该就是代码段了吧!接下来通过这个例了程序逐段介绍Win32汇编程序的结构。

3.1.1 模式定义

程序的第一部分是模式和源程序格式的定义语句:

   .386
   .model flat,stdcall
   option casemap:none

这些指令定义了程序使用的指令集、工作模式和格式。

1. 指定使用的指令集

.386语句是汇编语言的伪指令,它在低版本的宏汇编中就已经存在,类似的指令还有:.8086,.186,.286,.386/.386p,.486/.486p和 .586/.586p等,用于告诉编译器在本程序中使用的指令集。在DOS的汇编中默认使用的是8086指令集,那时候如果在源程序中写入80386所特有的指令或使用32位的寄存器就会报错,为了在DOS环境下进行保护模式编程或仅为了使用32位寄存器,常在DOS的汇编中使用 .386来定义。Win32环境工作在80386及以上的处理器中,所以这一句 .386是必不可少的。

后面带p的伪指令则表示程序中可以使用特权指令,如:

   mov cr0,eax

这一类指令必须在特权级0上运行,如果只指定 .386,那么使用普通的指令是可以的,编译时到这一句就会报错,如果我们要写的程序是VxD等驱动程序,中间要用到特权指令,那么必须定义 .386p,在应用程序级别的Win32编程中,程序都是运行在优先级3上,不会用到特权指令,只需定义 .386就够了。80486和Pentium处理器指令是80386处理器指令的超集,同样道理,如果程序中要用80486处理器或Pentium处理器的指令,则必须定义 .486或 .586。另外,Intel公司的80x86系列处理器从Pentium MMX开始增加了MMX指令集,为了使用MMX指令,除了定义 .586之外,还要加上一句 .mmx伪指令:

   .586
   .mmx

上页:第02章 准备编程环境 · 2.6 构建编程环境 下页:第03章 使用MASM · 3.1 Win32汇编源程序的结构(2)

第03章 使用MASM

版权所有 © 中山市飞娥软件工作室 证书:粤ICP备09170368号