`
insertyou
  • 浏览: 905801 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

【我所認知的BIOS】->汇编语言之宏汇编1

阅读更多

【我所認知的BIOS】->汇编语言之宏汇编1

By LightSeed

2010-2-2

其实早就想写点关于汇编语言的文章了,但是最近感觉比较累,自己也比较懒今天才动手写。哎。。。真是身心俱疲,房价涨了,小菜也涨了,妹儿的要求也涨了,但是哥的工资还是不涨。这社会我快混不下去了。但是想想做男人嘛,有什么大不了的。做人,最重要的是心态要好,要稳。

1、我体会到的C和汇编

目前我还做的legacyBIOS,里面全是汇编语言。老实说,最近在study C语言的东西,也在研究用C生成ASM的文件,细细想来C确实很强大,汇编语言的效率确实也高。从C生成的汇编语言来看的话,如果是组织的好,实际的汇编语言的效率应该是比C高的。关于汇编语言的基础性的东西我就不多赘述了。在这里我想谈谈我在仿写ADU那个debug tool的时候用的一些汇编语言的里的高级语句。(当然先说清楚,这也是参考了BIOS code的东西,让我自己写我想我是写不出来的啦。),话有说回来,对于程序员来说,如果让我在C和汇编语言之间选择,当然还是比较喜欢用C了。(最近我在study ACPI的东西的时候,就试图用C来写一个ACPI dump的工具,发觉用C写真的好方便哦,两天就能搞出来一个功能了,但是如果用汇编的话,就算知道算法,估计写起来也比较费神。)

在组织数据段中的数据的时候,按照结构体的形式来存储有关联的数据是一种明智的选择。但是由于这个结构体需要输入的东西比较多,那么每次都手动去输入相关数据的话,估计烦也烦死了。所以宏汇编在这里就其了很大的作用。

2、宏汇编

(以下这段话是引用自网上的,对于这些定义放在这里纯粹是为了这篇文章的完整性。)

----------------------------引用开始----------------------

在程序设计中,为了简化程序的设计,将多次重复使用的程序段用宏指令代替。宏指令是指程序员事先定义的特定的指令,这种指令是一组重复出现的程序指令块的缩写和替代。宏指令定义后,凡在宏指令出现的地方,宏汇编程序总是自动地把它们替换成对应的程序指令块。宏指令有时也称为宏,包含宏定义和宏调用。

使用宏指令的好处是:简化源程序的编写,传递参数灵活,功能更强。

2.1宏指令的定义

宏是源程序中的一段具有独立功能的程序代码。它只要在源程序中定义一次,就可以多次调用,调用时只要使用一个宏指令语句就可以了。宏指令定义由开始伪指令MACRO、宏指令体、宏指令定义结束伪指令ENDM组成。格式如下:

宏指令名 MACRO

[形式参数1,形式参数2 形式参数N]

;宏指令体(宏体)

ENDM

其中,宏指令名是宏定义为宏体程序指令块规定的名称,可以是任一合法的名字,也可以是系统保留字(如指令助记符、伪指令操作符等),当宏指令名是某个系统保留字时,则该系统保留字就被赋予新的含义,从而失去原有的意义。MACRO语句到ENDM语句之间的所有汇编语句构成宏指令体,简称宏体,宏体中使用的形式参数必须在MACRO语句中列出。

形式参数是出现在宏体内某些位置上可以变化的符号,也可以是任一合法的名字,甚至是寄存器名(笔者:我不建议你这样使用)。如果形式参数中使用某些寄存器名,那么在宏汇编展开时,将不认为这些寄存器名是寄存器本身,而是形式参数,并被实际参数所代替。形式参数可以缺省,也可以有一个或多个。当形式参数多于一个时,形式参数之间用逗号隔开。定义宏指令时,每行的字符数不能多于132个。宏指令定义一般放在源程序的开头,以避免产生不应发生的错误。

宏指令必须先定义后调用(引用)。宏指令可以重新定义,也可以嵌套定义。嵌套定义是指在宏指令体内还可以再定义宏指令或调用另一宏指令。(笔者:其实这和C中的方式也比较相似。扩展一下,汇编语言中的结构体也是这也的。)

2.2宏指令的调用

宏指令一旦定义后,就可以用宏指令名字(宏名)来引用(或调用),这个引用过程即为宏调用。宏调用的格式为

宏指令名 实际参数1,实际参数2,实际参数N

其中,实际参数的类型和顺序要与形式参数的类型和顺序保持一致,宏调用时将一一对应地替换宏指令体中的形式参数。当有两个以上参数时,中间用逗号、空格或制表符隔开。宏指令调用时,实际参数的数目并不一定要和形式参数的数目一致。当实参个数多于形参的个数时,忽略多余的实参;当实参个数少于形参个数时,多余的形参用空串代替。

;---------------------------------引用结束------------------------

哈哈,我们应该向前辈看齐的嘛,多学习前辈的东西,总是不会错的。

3、条件汇编

IF/IFE伪指令中的表达式可以采用第3章学习的关系运算符EQ(相等)、NE(不相等)、GT(大于)、LT(小于)、GE(大于等于)、LE(小于等于)。关系表达式用0ffffh(非0)表示真,用0表示假。

条件汇编伪指令

格式

功能说明

IF 表达式

汇编程序求出表达式的值,此值不为0则条件满足

IFE 表达式

汇编程序求出表达式的值,此值为0则条件满足

IFDEF 符号

符号已定义(内部定义或声明外部定义),则条件满足

IFNDEF 符号

符号未定义,则条件满足

IFB <形参>

用在宏定义体。如果宏调用没有用实参替代该形参,则条件满足

IFNB <形参>

用在宏定义体。如果宏调用用实参替代该形参,则条件满足

IFIDN <字符串1>,<字符串2>

字符串1与字符串2相同则条件满足;区别大小写

IFIDNI <字符串1>,<字符串2>

字符串1与字符串2相同则条件满足;不区别大小写

IFDIF <字符串1>,<字符串2>

字符串1与字符串2不相同则条件满足;区别大小写

IFDIFI <字符串1>,<字符串2>

字符串1与字符串2不相同则条件满足;不区别大小写

对于他们的定义我就不详细例举了,只是在后面的叙述中要用到这些语句。所以先列在这里,如果需要您可以去查更过的资料。

4、实例

上面对文字性的东西定义了太多了,我们来直接看实例吧,这样比较直观。(本想在这里先引用一个简单的宏汇编的例子的,可是后来又想想算了,如果有人对这篇文章敢兴趣的话,我想简单的宏汇编也应该难不到你。)下面的例子主要是当时在study 宏汇编的时候见到网上的例子,在这里就直接引用过来,(虽说是从CSDN上的一个老兄blog里面看到的,但是我感觉是小木偶前辈的例子,所以我一不知道究竟哪位是原作者,您可以看看这里:

http://blog.csdn.net/benny5609/archive/2008/06/12/2541246.aspx

范例:通用的推入堆栈宏

8086 指令的 PUSH 只能把十六位的寄存器或十六位变量推入堆栈,不能把十六位立即值 (常数) 或八位的寄存器推入堆栈,而底下这个宏范例,push_op,也能使立即值或八位的寄存器推入堆栈。底下是 push_op 源代码:

page ,132 ;01

push_op MACRO arg ;;03

reg16 = 0 ;;05

reg08 = 0 ;;06

addr = 0 ;;07

;;09 检查输入参数是否为 16 位的寄存器

IRP reg,<AX,BX,CX,DX,CS,DS,ES,SS,SI,DI,BP,SP,ax,bx,cx,dx,cs,ds,es,ss,si,di,bp,sp>

IFIDN <reg>,<arg>

push arg ;;12 如果相等的话,推入堆栈

reg16 = 0ffffh ;;13 数定虚拟变量为真

exitm ;;14 跳出 IRP

ENDIF

ENDM

IF reg16 ;;17 reg16 为真

exitm ;;18 则跳出 push_op

ENDIF

;;21 检查输入参数是否为 16 位的寄存器

IRP reg,<aX,bX,cX,dX,cS,dS,eS,sS,sI,dI,bP,sP,Ax,Bx,Cx,Dx,Cs,Ds,Es,Ss,Si,Di,Bp,Sp>

IFIDN <reg>,<arg>

push arg

reg16 = 0ffffh

exitm

ENDIF

ENDM

IF reg16

exitm

ENDIF

;;33 检查输入参数是否为 8 位的寄存器

IRP reg,<al,bl,cl,dl,ah,bh,ch,dh,AH,BH,CH,DH,AL,BL,CL,DL>

IFIDN <reg>,<arg>

reg08 = 0ffffh

exitm

ENDIF

ENDM

IF reg08

IRPC char,arg ;;41 取得寄存器名的第一个字母

push char&&x ;;42 推入堆栈

exitm ;;43 跳出 IRPC

ENDM

exitm ;;45 跳出 push_op

ENDIF

;;48 检查输入参数是否为 8 位的寄存器

IRP reg,<Al,Bl,Cl,Dl,Ah,Bh,Ch,Dh,aL,bL,cL,dL,aH,bH,cH,dH>

IFIDN <reg>,<arg>

reg08 = 0ffffh

ENDIF

ENDM

IF reg08

IRPC char,arg

push char&&x

exitm

ENDM

exitm

ENDIF

;;62 检查输入参数是否为含有寄存器间接寻址模式,即 [BX][SI]……等等

IRPC char,arg

IF ('&char' eq '[')

addr=0ffffh

exitm

ENDIF

ENDM

IF addr

push arg

exitm

ENDIF

arg_size=((type arg) 1)/2 ;;74 输入参数之长度

arg_type=(.type arg) and 3 ;;75 输入参数之类型

;;77 检查输入参数是否为变量

IF arg_type eq 2

arg_offset =0

REPT arg_size

arg_address=word ptr arg arg_offset

push arg_address

arg_offset=arg_offset 2

ENDM

exitm

ENDIF

;;88 检查输入参数是否为标号

IF arg_type eq 1

push bp

mov bp,sp

push ax

mov ax,offset arg

xchg ax,[bp]

mov bp,ax

pop ax

exitm

;;98 若不是寄存器、寻址模式、变量、标号的话,应为立即值

ELSE

push bp

mov bp,sp

push ax

mov ax,arg

xchg ax,[bp]

mov bp,ax

pop ax

ENDIF

ENDM这个宏结构很明显,先检查要推入堆栈的参数是否为 16 位寄存器( 9 行到第 31 ),如果不是再检查是否为 8 位寄存器 ( 33 行到第 60 ),如果不是寄存器的话,再检查是否为寄存器间接寻址 ( 62 行到第 72 ),如果不是以上这几种的话,再检查是否推入变量到堆栈 ( 77 行到第 87 ),接下来检查是否推入标号到堆栈 ( 88 行到第 97 ),假如都不是上述情形的话,就是推入立即值到堆栈了 ( 98 行到第 107 )

检查是否为寄存器的方法是用不定重复块 ( IRP ) 来指定要比较的范围,故引数列 ( 即第 10 行角括号内的引数 ) 包含所有 16 位寄存器名称,但是因为参数与引数都被视为字串,所以大小写是有差别的,必须在引数列里包含不同的大小写排列方式。指定好比较范围后,再用 IFIDN 比较输入参数是否为引数列中的一个,假如是 16 位寄存器的话,则直接把该参数推入堆栈即可,并设定一个虚拟变量,reg16,为 0ffffh0ffffh 表示真的意思 ( 1213 )。然后再跳出 push_op 堆栈。

小木偶再把 IRP 重复块的执行方式说明一遍。第 10 行到第 16 行程序码为:

IRP reg,<AX,BX,CX,DX,CS,DS,ES,SS,SI,DI,BP,SP,ax,bx,cx,dx,cs,ds,es,ss,si,di,bp,sp>

IFIDN <reg>,<arg>

push arg ;;12 如果相等的话,推入堆栈

reg16 = 0ffffh ;;13 数定虚拟变量为真

exitm ;;14 跳出 IRP

ENDIF

ENDM

IF reg16 ;;17 reg16 为真

exitm ;;18 则跳出 push_op

ENDIF表示在第 10 行到第 16 行程序码会重复汇编。第一次时,reg 会以 AX 代入汇编,第 11 行是比较 arg reg 这两字串是否相等,如果相等则汇编第 12 行到第 14 行之间的程序码,不相等则结束 IFIDN,然后遇到 ENDM,故重复第二次,使 reg BX 代入汇编……一直到 sp 所有引数结束。

14 行,是因为假如已经找到相符合的寄存器,就没必要再比较了,这样可以加快汇编速度。( 虽然也没快多少。) 17 行到第 19 行,也是这样的道理,既已找到是把寄存器推入堆栈,也就没必要汇编以下的程序了,故直接跳出 push_op 堆栈。

您可能会问,第 14 行就已有了 exitm,为何第 18 行还要有个 exitm 呢?这是因为 RPTIRPIRPC 这三个重复块,类似宏结构,若要在中间停止汇编都可以用 exitm 来跳出宏或块,所以第 14 行是跳出 IRP 块,第 17 行是跳出 push_op 宏。

33 行到第 61 行,是检查参数是否为 8 位寄存器,方法和上述几乎相同,差别在于 8 位寄存器 ( 例如 ah ) 无法推入堆栈必须改成 16 位寄存器 ( 例如 ax )。所以第 41 行到第 44 行多了个 IRPC 重复块,此重复块是为了取得寄存器的第一个字母,当取得第一个字母就把该字母加上『x』再推入堆栈,然后跳出 IRPC 块及 push_op 宏。至于该 IRPC 重复块的运作方式如下:该 IRPC 块重复次数只有两次,分别以 8 位寄存器名称的两个字母代入汇编,当第一次时即以 8 位寄存器名称的第一个字母代入,然后加上『x』再推入堆栈,然后立刻跳出 IRPC 块,故事实上这个重复块只汇编一次而已。

42 行的『&&x』为何要有两个『&』呢?这是因为根据 MASM 手册上说每层块要使用『&』,故第二层要用两个『&』。

62 行到第 73 行是用来检查推入堆栈的参数是否为寄存器间接寻址,寄存器间接寻址模式是像底下的样子:

mov ax,[bx]

push [si]

sub ax,[bx 200h]

观察以上几个例子,您会发现,这种寻址模式含有两个中括号,因此检验方法就是以 IRPC 检查参数中是否有『[( 64 行到第 67 行的 IF 条件汇编 ),假如有的话,会使虚拟变量,addr,设为真。然后接下来的就直接使该参数推入堆栈,因为 PUSH 指令就可以直接推入寄存器间接寻址模式。

上面这些东西,其实大多是参考前辈的,希望能给正在寻找宏汇编资料的童鞋一点启发。

分享到:
评论

相关推荐

    IBM-PC汇编语言程序设计(第2版)

    第5章-第9章说明编程方法,内容包括循环、分支、子程序等基本程序结构,程序设计的基本方法和技术,多模块连接技术,宏汇编技术,以中断为主的输入输出程序设计方法,以及BIOS和DOS系统功能调用;第10章和第11章为...

    IBM-PC汇编语言程序设计(第二版 沈美明) 课后习题答案

    汇编语言是计算机科学的基础之一,它是一种低级编程语言,与机器指令系统直接对应,程序员可以通过它直接控制硬件操作。IBM-PC汇编语言主要针对Intel x86系列处理器,这一系列处理器在个人计算机领域广泛使用。通过...

    _IBM-PC宏汇编语言程序设计

    《IBM-PC宏汇编语言程序设计》是深入学习IBM个人计算机编程的重要教材,它主要讲解如何使用宏汇编语言来编写高效的程序。宏汇编语言是汇编语言的一个扩展,它允许程序员定义并使用宏,这些宏可以简化代码,提高代码...

    《IBM-PC汇编语言程序设计》教案 附:习题参考答案

    - **高级汇编语言技术**(第七章):介绍宏汇编、重复汇编和条件汇编等高级编程技术。 - **输入/输出程序设计**(第八章):探讨I/O设备的数据传送方式、程序直接控制I/O方式以及中断传送方式。 - **BIOS和DOS系统...

    IBM-PC汇编语言程序设计(第二版)沈美明和温冬婵的教案+答案

    汇编语言程序设计作为计算机科学中的核心课程之一,在培养学生的程序设计技能和硬件理解能力方面起着至关重要的作用。 #### 二、教材内容结构 《IBM-PC汇编语言程序设计(第二版)》由沈美明和温冬婵编著,共分为...

    BM-PC汇编语言程序设计课后习题答案(第二版+沈美明)

    《BM-PC汇编语言程序设计》是计算机科学领域中一本经典的教材,主要讲解IBM-PC架构下的汇编语言编程技术。沈美明教授作为该书的作者,以其深入浅出的讲解方式,使得读者能够更好地理解和掌握汇编语言的核心概念和...

    IBM-PC汇编语言程序设计(第二版)

    第5章-第9章说明编程方法,内容包括循环、分支、子程序等基本程序结构,程序设计的基本方法和技术,多模块连接技术,宏汇编技术,以中断为主的输入输出程序设计方法,以及BIOS和DOS系统功能调用;第10章和第11章为...

    IBM-PC汇编语言程序设计

    IBM-PC汇编语言程序设计 BIOS和DOS中断 IBM-PC汇编语言程序设计IBM-PC汇编语言程序设计

    IBM-PC汇编语言程序设计课后答案

    1. **汇编语言基础**:学习汇编语言首先要理解其基本结构和语法,如指令集、寄存器、寻址模式、运算符等。IBM-PC使用的是X86系列处理器,其汇编语言包含多种指令,如数据处理指令(加减乘除)、转移指令(跳转、循环...

    IBM PC汇编语言程序设计

    本书为清华大学计算机汇编语言程序设计课教材,主要阐述IBM PC及其兼容机汇编语言程序程序设计的方法和技术。全书共13章:第一、二章介绍基础知识;第三、四章说明IBM PC机的指令系统及包括伪操作在内的汇编语言程序...

    IBM-PC汇编语言程序设计(沈美明,温冬婵,第2版).part1

    第5章-第9章说明编程方法,内容包括循环、分支、子程序等基本程序结构,程序设计的基本方法和技术,多模块连接技术,宏汇编技术,以中断为主的输入输出程序设计方法,以及BIOS和DOS系统功能调用;第10章和第11章为...

    FULL-PACK-BIOS-BATOCERA-V32-TMCTV.rar

    【标题】"FULL-PACK-BIOS-BATOCERA-V32-TMCTV.rar" 提供的是一个完整的Batocera BIOS包,版本为V32,适用于TMCTV设备。Batocera是一款开源的游戏主机系统,它允许用户在各种硬件平台上运行经典游戏机的模拟器。这个...

    IBM-PC汇编语言程序设计(第二版)答案

    汇编语言是计算机科学的基础之一,它是直接对应机器指令的编程语言,对于理解计算机底层工作原理和进行高效计算具有重要意义。IBM-PC汇编语言则是针对IBM个人计算机系列设计的,与x86架构紧密相关。在第二版中,作者...

    IBM-PC汇编语言课件程序设计ppt

    总之,IBM-PC汇编语言是计算机科学中的基石之一,它不仅提供了对计算机硬件的直接控制,也为理解高级语言的工作机制提供了基础。虽然在日常开发中高级语言更为普遍,但在特定领域,如系统编程、性能优化和嵌入式系统...

    IBM-PC汇编语言程序设计(第二版)沈美明 温冬婵编著 课后答案

    学会使用BIOS或DOS系统调用来进行设备通信是汇编语言编程中不可或缺的一部分。 通过学习《IBM-PC汇编语言程序设计(第二版)》并参考课后答案,读者可以全面了解并熟练掌握IBM-PC环境下的汇编语言编程技术,为后续...

    《IBM-PC汇编语言程序设计》

    1. 前两章为基础知识,包括汇编语言的基本概念、语法和运算符,为后续的学习打下坚实基础。 2. 第三章深入探讨了80x86系列CPU的指令系统,包括不同类型的指令(如算术、逻辑、转移、比较等)以及各种寻址模式,这是...

    IBM-PC汇编语言程序设计电子教案

    1. **汇编语言基础知识**:介绍汇编语言的基本概念,如指令集架构(ISA)、操作码、寄存器、寻址模式等。学习者会了解到如何用汇编指令来执行基本的算术和逻辑运算。 2. **IBM-PC汇编语言特性**:IBM-PC采用的是x86...

Global site tag (gtag.js) - Google Analytics