`
阅读更多

续上:

 

8086/8088共含有14个16位寄存器,掌握这些寄存器的名称符号、长度、含义和用法是学习指令系统的基础,对考生掌握指令编写汇编语言程序是非常重要的。8086/8088中的寄存器从功能上可划分成4类。如图2-2 所示:

 

 

 

 

 

 

 

 

 

 

1.数据位表达

 

1.1 idata 如 mov ax,1

1.2 寄存器 如 mov ax,bx

1.3 段地址SA和偏移地址EA 如 mov ax,[0]

 

段寄存器,默认在ds中:

mov ax,[bx+si+8]

 

段地址默认在ss中:

mov ax,[bp+si]

 

 

2.寻址方式:

 

直接寻址

[idata] EA=idata ; SA=ds

 

间接寻址

(ds)中

[bx][si][di]

 

(ss)中

[bp]

 

 

相对寻址

用于结构体如,数组二维数组

[bx+idata] 如 [bx].idata , idata [si],[bx][idata]

 

基址变址寻址:

用于二维数组如:[bx+si][bx+di][bp+si][bp+di]

 

相对基址变址寻址

用于表格结构中数组和而未数组:

[bx].idata[si]

idata[bx][si]

 

 

3.定义数据段 与 div指令

 

//定义数据段 data segment dd 100001 //dd define double word双字 dw 100 //dw define word db 1 //db define byte data ends //定义栈段 stack segment dw 200 dup 0 //dup相当于 dw 0,0,0,0……定义200个字节的数据 stack ends //div 除法指令 /** 规则: 1.除数有8位和16位两种 2.被除数默认放在ax 或dx 和 ax中, 3.除数为8位,al存商,ah存余数如果16位,ax存商,dx存余数 例子: 100001/100 100001大于65535,需要ax和dx联合存放,100001=186A1H **/ mov dx,1 mov ax,86A1H mov bx,100 div bx /** 结果: ax=03E8H=1000 dx=1 **/

 


 

 

4.标记寄存器

用于标记正负等的寄存器

 

 

5.CPU中断

 

CPU用8位中断类型码通过中断向量表查找到终端处理程序的入口地址。

中断向量表在内存中存放,地址为0000:0000到0000:03FF的1024个单元

 

 

6.练习:

 

linux 下汇编编程

 

as -o hello.o hello.s //AT & T 语法编译 nasm -f elf hello.asm // inter 语法的编译 ld -o hello hello.o //连接

 


 

 

调试hello:

 

as --gstabs -o hello.o hello.s ld -o hello hello.o gdb hello (gdb) break 12 //在line 12 break (gdb) i reg eax //输入寄存器ax的数据 #hello.s .data # 数据段声明 msg : .string "Hello, world!\\n" # 要输出的字符串 len = . - msg # 字串长度 .text # 代码段声明 .global _start # 指定入口函数 _start: # 在屏幕上显示一个字符串 movl $len, %edx # 参数三:字符串长度 movl $msg, %ecx # 参数二:要显示的字符串 movl $1, %ebx # 参数一:文件描述符(stdout) movl $4, %eax # 系统调用号(sys_write) int $0x80 # 调用内核功能 # 退出程序 movl $0,%ebx # 参数一:退出代码 movl $1,%eax # 系统调用号(sys_exit) int $0x80 # 调用内核功能 linux 下 c hello world vi hello.c 内容: #include<stdio.h> int main() {    printf("Hello World\n"); //prints "Hello World"    return 0; } gcc hello.c -o hello.c.o ./hello.c.o 输出Hello World

 


 

 

 

 

 

 

 

3. 第二个汇编程序 请点评

例 18.2. 求一组数的最大值的汇编程序

#PURPOSE: This program finds the maximum number of a # set of data items. # #VARIABLES: The registers have the following uses: # # %edi - Holds the index of the data item being examined # %ebx - Largest data item found # %eax - Current data item # # The following memory locations are used: # # data_items - contains the item data. A 0 is used # to terminate the data # .section .data data_items: #These are the data items .long 3,67,34,222,45,75,54,34,44,33,22,11,66,0 .section .text .globl _start _start: movl $0, %edi # move 0 into the index register movl data_items(,%edi,4), %eax # load the first byte of data movl %eax, %ebx # since this is the first item, %eax is # the biggest start_loop: # start loop cmpl $0, %eax # check to see if we've hit the end je loop_exit incl %edi # load next value movl data_items(,%edi,4), %eax cmpl %ebx, %eax # compare values jle start_loop # jump to loop beginning if the new # one isn't bigger movl %eax, %ebx # move the value as the largest jmp start_loop # jump to loop beginning loop_exit: # %ebx is the status code for the _exit system call # and it already has the maximum number movl $1, %eax #1 is the _exit() syscall int $0x80


汇编、链接、运行:

$ as max.s -o max.o $ ld max.o -o max $ ./max $ echo $?

这个程序在一组数中找到一个最大的数,并把它作为程序的退出状态。这组数在.data段给出:

data_items: .long 3,67,34,222,45,75,54,34,44,33,22,11,66,0

.long指示声明一组数,每个数占32位,相当于C语言中的数组。这个数组开头定义了一个符号data_items,汇编器会把数组的首地址作为data_items符号所代表的地址,data_items类似于C语言中的数组名。data_items这个标号没有用.globl声明,因为它只在这个汇编程序内部使用,链接器不需要用到这个名字。除了.long之外,常用的数据声明还有:

  • .byte,也是声明一组数,每个数占8位

  • .ascii,例如.ascii "Hello world",声明11个数,取值为相应字符的ASCII码。注意,和C语言不同,这样声明的字符串末尾是没有'\0'字符的,如果需要以'\0'结尾可以声明为.ascii "Hello world\0"

data_items数组的最后一个数是0,我们在一个循环中依次比较每个数,碰到0的时候让循环终止。在这个循环中:

  • edi寄存器保存数组中的当前位置,每次比较完一个数就把edi的值加1,指向数组中的下一个数。

  • ebx寄存器保存到目前为止找到的最大值,如果发现有更大的数就更新ebx的值。

  • eax寄存器保存当前要比较的数,每次更新edi之后,就把下一个数读到eax中。

_start: movl $0, %edi

初始化edi,指向数组的第0个元素。

movl data_items(,%edi,4), %eax

这条指令把数组的第0个元素传送到eax寄存器中。data_items是数组的首地址,edi的值是数组的下标,4表示数组的每个元素占4字节,那么数组中第edi个元素的地址应该是data_items + edi * 4,写在指令中就是data_items(,%edi,4),这种地址表示方式在下一节还会详细解释。

movl %eax, %ebx

ebx的初始值也是数组的第0个元素。下面我们进入一个循环,循环的开头定义一个符号start_loop,循环的末尾之后定义一个符号loop_exit

start_loop: cmpl $0, %eax je loop_exit

比较eax的值是不是0,如果是0就说明到达数组末尾了,就要跳出循环。cmpl指令将两个操作数相减,但计算结果并不保存,只是根据计算结果改变eflags寄存器中的标志位。如果两个操作数相等,则计算结果为0,eflags中的ZF位置1。je是一个条件跳转指令,它检查eflags中的ZF位,ZF位为1则发生跳转,ZF位为0则不跳转,继续执行下一条指令。可见比较指令和条件跳转指令是配合使用的,前者改变标志位,后者根据标志位决定是否跳转。je可以理解成“jump if equal”,如果参与比较的两数相等则跳转。

incl %edi movl data_items(,%edi,4), %eax

edi的值加1,把数组中的下一个数传送到eax寄存器中。

cmpl %ebx, %eax jle start_loop

把当前数组元素eax和目前为止找到的最大值ebx做比较,如果前者小于等于后者,则最大值没有变,跳转到循环开头比较下一个数,否则继续执行下一条指令。jle表示“jump if less than or equal”。

movl %eax, %ebx jmp start_loop

更新了最大值ebx然后跳转到循环开头比较下一个数。jmp是一个无条件跳转指令,什么条件也不判断,直接跳转。loop_exit符号后面的指令调_exit系统调用退出程序。

4. 寻址方式 请点评

通过上一节的例子我们了解到,访问内存时在指令中可以用多种方式表示内存地址,比如可以用数组基地址、元素长度和下标三个量来表示,增加了寻址的灵活性。本节介绍x86常用的几种寻址方式(Addressing Mode)。内存寻址在指令中可以表示成如下的通用格式:

ADDRESS_OR_OFFSET(%BASE_OR_OFFSET,%INDEX,MULTIPLIER)

它所表示的地址可以这样计算出来:

FINAL ADDRESS = ADDRESS_OR_OFFSET + BASE_OR_OFFSET + MULTIPLIER * INDEX

其中ADDRESS_OR_OFFSET和MULTIPLIER必须是常数,BASE_OR_OFFSET和INDEX必须是寄存器。在有些寻址方式中会省略这4项中的某些项,相当于这些项是0。

  • 直接寻址(Direct Addressing Mode)。只使用ADDRESS_OR_OFFSET寻址,例如movl ADDRESS, %eax把ADDRESS地址处的32位数传送到eax寄存器。

  • 变址寻址(Indexed Addressing Mode) 。上一节的movl data_items(,%edi,4), %eax就属于这种寻址方式,用于访问数组元素比较方便。

  • 间接寻址(Indirect Addressing Mode)。只使用BASE_OR_OFFSET寻址,例如movl (%eax), %ebx,把eax寄存器的值看作地址,把内存中这个地址处的32位数传送到ebx寄存器。注意和movl %eax, %ebx区分开。

  • 基址寻址(Base Pointer Addressing Mode)。只使用ADDRESS_OR_OFFSET和BASE_OR_OFFSET寻址,例如movl 4(%eax), %ebx,用于访问结构体成员比较方便,例如一个结构体的基地址保存在eax寄存器中,其中一个成员在结构体内的偏移量是4字节,要把这个成员读上来就可以用这条指令。

  • 立即数寻址(Immediate Mode)。就是指令中有一个操作数是立即数,例如movl $12, %eax中的$12,这其实跟寻址没什么关系,但也算作一种寻址方式。

  • 寄存器寻址(Register Addressing Mode)。就是指令中有一个操作数是寄存器,例如movl $12, %eax中的%eax,这跟内存寻址没什么关系,但也算作一种寻址方式。在汇编程序中寄存器用助记符来表示,在机器指令中则要用几个Bit表示寄存器的编号,这几个Bit也可以看作寄存器的地址,但是和内存地址不在一个地址空间。

1. 最简单的汇编程序 请点评

例 18.1. 最简单的汇编程序

#PURPOSE: Simple program that exits and returns a # status code back to the Linux kernel # #INPUT: none # #OUTPUT: returns a status code. This can be viewed # by typing # # echo $? # # after running the program # #VARIABLES: # %eax holds the system call number # %ebx holds the return status # .section .data .section .text .globl _start _start: movl $1, %eax # this is the linux kernel command # number (system call) for exiting # a program movl $4, %ebx # this is the status number we will # return to the operating system. # Change this around and it will # return different things to # echo $? int $0x80 # this wakes up the kernel to run # the exit command


把这个程序保存成文件hello.s(汇编程序通常以.s作为文件名后缀),用汇编器(Assembler)as把汇编程序中的助记符翻译成机器指令,生成目标文件hello.o

$ as hello.s -o hello.o

然后用链接器(Linker,或Link Editor)ld把目标文件hello.o链接成可执行文件hello

$ ld hello.o -o hello

为什么用汇编器翻译成机器指令了还不行,还要有一个链接的步骤呢?链接主要有两个作用,一是修改目标文件中的信息,对地址做重定位,在第 5.2 节 “可执行文件”详细解释,二是把多个目标文件合并成一个可执行文件,在第 2 节 “main函数和启动例程”详细解释。我们这个例子虽然只有一个目标文件,但也需要经过链接才能成为可执行文件。

现在执行这个程序,它只做了一件事就是退出,退出状态是4,第 2 节 “自定义函数”讲过在Shell中可以用特殊变量$?得到上一条命令的退出状态:

$ ./hello $ echo $? 4

所以这段汇编代码相当于在C程序的main函数中return 4;。为什么会相当呢?我们在第 2 节 “main函数和启动例程”详细解释。

下面逐行分析这个汇编程序。首先,#号表示单行注释,类似于C语言的//注释。

.section .data

汇编程序中以.开头的名称并不是指令的助记符,不会被翻译成机器指令,而是给汇编器一些特殊指示,称为汇编指示(Assembler Directive)或伪操作(Pseudo-operation),由于它不是真正的指令所以加个“”字。.section指示把代码划分成若干个段(Section),程序被操作系统加载执行时,每个段被加载到不同的地址,操作系统对不同的页面设置不同的读、写、执行权限。.data段保存程序的数据,是可读可写的,相当于C程序的全局变量。本程序中没有定义数据,所以.data段是空的。

.section .text

.text段保存代码,是只读和可执行的,后面那些指令都属于.text段。

.globl _start

_start是一个符号(Symbol),符号在汇编程序中代表一个地址,可以用在指令中,汇编程序经过汇编器的处理之后,所有的符号都被替换成它所代表的地址值。在C语言中我们通过变量名访问一个变量,其实就是读写某个地址的内存单元,我们通过函数名调用一个函数,其实就是跳转到该函数第一条指令所在的地址,所以变量名和函数名都是符号,本质上是代表内存地址的。

.globl指示告诉汇编器,_start这个符号要被链接器用到,所以要在目标文件的符号表中标记它是一个全局符号(在第 5.1 节 “目标文件”详细解释)。_start就像C程序的main函数一样特殊,是整个程序的入口,链接器在链接时会查找目标文件中的_start符号代表的地址,把它设置为整个程序的入口地址,所以每个汇编程序都要提供一个_start符号并且用.globl声明。如果一个符号没有用.globl声明,就表示这个符号不会被链接器用到。

_start:

这里定义了_start符号,汇编器在翻译汇编程序时会计算每个数据对象和每条指令的地址,当看到这样一个符号定义时,就把它后面一条指令的地址作为这个符号所代表的地址。而_start这个符号又比较特殊,它所代表的地址是整个程序的入口地址,所以下一条指令movl $1, %eax就成了程序中第一条被执行的指令。

movl $1, %eax

这是一条数据传送指令,这条指令要求CPU内部产生一个数字1并保存到eax寄存器中。mov的后缀l表示long,说明是32位的传送指令。这条指令不要求CPU读内存,1这个数是在CPU内部产生的,称为立即数(Immediate)。在汇编程序中,立即数前面要加$,寄存器名前面要加%,以便跟符号名区分开。以后我们会看到mov指令还有另外几种形式,但数据传送方向都是一样的,第一个操作数总是源操作数,第二个操作数总是目标操作数。

movl $4, %ebx

和上一条指令类似,生成一个立即数4并保存到ebx寄存器中。

int $0x80

前两条指令都是为这条指令做准备的,执行这条指令时发生以下动作:

  1. int指令称为软中断指令,可以用这条指令故意产生一个异常,上一章讲过,异常的处理和中断类似,CPU从用户模式切换到特权模式,然后跳转到内核代码中执行异常处理程序。

  2. int指令中的立即数0x80是一个参数,在异常处理程序中要根据这个参数决定如何处理,在Linux内核中int $0x80这种异常称为系统调用(System Call)。内核提供了很多系统服务供用户程序使用,但这些系统服务不能像库函数(比如printf)那样调用,因为在执行用户程序时CPU处于用户模式,不能直接调用内核函数,所以需要通过系统调用切换CPU模式,经由异常处理程序进入内核,用户程序只能通过寄存器传几个参数,之后就要按内核设计好的代码路线走,而不能由用户程序随心所欲,想调哪个内核函数就调哪个内核函数,这样可以保证系统服务被安全地调用。在调用结束之后,CPU再切换回用户模式,继续执行int $0x80的下一条指令,在用户程序看来就像函数调用和返回一样。

  3. eaxebx的值是传递给系统调用的两个参数。eax的值是系统调用号,Linux的各种系统调用都是由int $0x80指令引发的,内核需要通过eax判断用户要调哪个系统调用,_exit的系统调用号是1。ebx的值是传给_exit的参数,表示退出状态。大多数系统调用完成之后会返回用户空间继续执行后面的指令,而_exit系统调用比较特殊,它会终止掉当前进程,而不是返回用户空间继续执行。

x86汇编的两种语法:intel语法和AT&T语法 请点评

x86汇编一直存在两种不同的语法,在intel的官方文档中使用intel语法,Windows也使用intel语法,而UNIX平台的汇编器一直使用AT&T语法,所以本书使用AT&T语法。movl %edx,%eax这条指令如果用intel语法来写,就是mov eax,edx,寄存器名不加%号,源操作数和目标操作数的位置互换,字长也不是用指令的后缀l表示而是用另外的方式表示。本书不详细讨论这两种语法之间的区别,读者可以参考[AssemblyHOWTO]

介绍x86汇编的书很多,UNIX平台的书都采用AT&T语法,例如[GroudUp],其它书一般采用intel语法,例如[x86Assembly]

习题 请点评

1、把本节例子中的int $0x80指令去掉,汇编、链接也能通过,但是执行的时候出现段错误,你能解释其原因吗?

4. 寻址方式 请点评

通过上一节的例子我们了解到,访问内存时在指令中可以用多种方式表示内存地址,比如可以用数组基地址、元素长度和下标三个量来表示,增加了寻址的灵活性。本节介绍x86常用的几种寻址方式(Addressing Mode)。内存寻址在指令中可以表示成如下的通用格式:

ADDRESS_OR_OFFSET(%BASE_OR_OFFSET,%INDEX,MULTIPLIER)

它所表示的地址可以这样计算出来:

FINAL ADDRESS = ADDRESS_OR_OFFSET + BASE_OR_OFFSET + MULTIPLIER * INDEX

其中ADDRESS_OR_OFFSET和MULTIPLIER必须是常数,BASE_OR_OFFSET和INDEX必须是寄存器。在有些寻址方式中会省略这4项中的某些项,相当于这些项是0。

  • 直接寻址(Direct Addressing Mode)。只使用ADDRESS_OR_OFFSET寻址,例如movl ADDRESS, %eax把ADDRESS地址处的32位数传送到eax寄存器。

  • 变址寻址(Indexed Addressing Mode) 。上一节的movl data_items(,%edi,4), %eax就属于这种寻址方式,用于访问数组元素比较方便。

  • 间接寻址(Indirect Addressing Mode)。只使用BASE_OR_OFFSET寻址,例如movl (%eax), %ebx,把eax寄存器的值看作地址,把内存中这个地址处的32位数传送到ebx寄存器。注意和movl %eax, %ebx区分开。

  • 基址寻址(Base Pointer Addressing Mode)。只使用ADDRESS_OR_OFFSET和BASE_OR_OFFSET寻址,例如movl 4(%eax), %ebx,用于访问结构体成员比较方便,例如一个结构体的基地址保存在eax寄存器中,其中一个成员在结构体内的偏移量是4字节,要把这个成员读上来就可以用这条指令。

  • 立即数寻址(Immediate Mode)。就是指令中有一个操作数是立即数,例如movl $12, %eax中的$12,这其实跟寻址没什么关系,但也算作一种寻址方式。

  • 寄存器寻址(Register Addressing Mode)。就是指令中有一个操作数是寄存器,例如movl $12, %eax中的%eax,这跟内存寻址没什么关系,但也算作一种寻址方式。在汇编程序中寄存器用助记符来表示,在机器指令中则要用几个Bit表示寄存器的编号,这几个Bit也可以看作寄存器的地址,但是和内存地址不在一个地址空间。

 

 

0
0
分享到:
评论

相关推荐

    汇编2的n次方

    通过汇编语言实现2的n次方,作业,调试结果正确

    计原及汇编2

    计原及汇编2

    AGP总线资料汇编2.rar

    AGP总线资料汇编2rar,AGP总线资料汇编2

    汇编语言 王爽 第2版(完全版)2/2

    汇编语言 王爽 第2版(完全版)pdf格式

    汇编API 汇编API 汇编API

    2. **函数原型**:定义函数的输入参数和返回值类型,尽管在汇编中没有类似高级语言的类型系统,但依然需要明确这些信息以避免错误。 3. **参数传递**:根据调用约定,将参数值放入指定的寄存器或内存位置。 4. **...

    汇编金手指 - 汇编语言学习软件

    2.高级查询功能:软件支持多种查询方式,使用户能够快速找到所需的资料和指令信息。 3.实用性强:软件包含了指令系统、常用伪操作、DOS中断、错误信息等编码资料,这些都是在汇编编程中常用的内容。 4.易于使用:...

    汇编语言2-2

    汇编语言2-2

    汇编进制转换2,10,16

    汇编写的进制转换2 10 16互转 当时课设写的

    汇编汇编语言汇编语言

    汇编语言汇编语言汇编语言汇编语言汇编语言汇编语言汇编语言汇编语言汇编语言汇编语言汇编语言

    汇编贪吃蛇 汇编贪吃蛇

    汇编贪吃蛇 汇编贪吃蛇 汇编贪吃蛇 汇编贪吃蛇

    汇编语言+答案汇编汇编汇编

    汇编语言+答案汇编汇编汇编,很好哦!下载了不后悔~~

    单片机反汇编软件汇总

    以上软件为网上收集来的反汇编专用软件 PIC16FDisAsm.exe 为pic16fxxx单片机反汇编软件 u51V12.rar为mcs51单片机反汇编软件 EMCdasm.exe为emc单片机反汇编软件 reavr.rar为AVR单片机反汇编软件 STM8反汇编.rar为stm8...

    汇编语言 第2版_郑晓薇

    郑晓薇编著的这本《汇编语言(第2版)》以80X86系列微型计算机为基础,以MASM5.0为汇编上机实验环境,重点介绍Intel8086指令系统。《汇编语言(第2版)》中实验练习贯穿始终,在各章中布置了实验任务模块,并在第10...

    汇编语言常见面试题.pdf

    什么是汇编语言(Assembly Language)?请简要介绍汇编语言的特点和作用。 汇编语言与高级编程语言相比有哪些优势和劣势?请说明汇编语言的优点和限制。 汇编语言中的寄存器(Registers)是什么?请列举常见的CPU...

    汇编指令与二进制对应手册

    汇编指令与二进制对应手册,从此再也不会觉得00010101010神秘了。

    汇编金手指 汇编语言

    2. 寻址模式:汇编语言中的寻址模式决定了如何访问内存中的数据,如立即寻址、直接寻址、间接寻址等。 3. 注释:为了提高代码可读性,汇编语言允许添加注释,但通常没有高级语言那么灵活,一般采用分号(;)作为...

    Linux下的汇编语言.pdf

    " Linux下的汇编语言.pdf" Linux下的汇编语言是指在Linux操作系统下的汇编语言编程。汇编语言是一种低级编程语言,使用symbolic representation来表示机器语言指令。汇编语言在操作系统与硬件打交道的过程中,需要...

    机器码转换汇编

    2. **操作数解析**:机器码指令通常包含操作数,反汇编器需要识别并解析这些操作数,包括立即数、寄存器、内存地址等。 3. **指令集架构**:不同的处理器架构有不同的指令集,因此反汇编器需要针对特定的架构进行...

    汇编学习资源 汇编学习

    汇编学习资源 汇编 汇编学习 汇编指令查询器AsmFun 1.3 8088 汇编速查手册.txt IBM-PC汇编语言指令集.txt The+Shellcoders+Handbook.rar 编程高手箴言.chm 汇编零起点教学.chm 汇编指令学习.chm

    51反汇编 Feeling反汇编软件 PIC 51单片机 反汇编工具

    在IT行业中,反汇编(Disassembler)和反编译(Decompiler)是两种用于理解和分析二进制代码的工具。本文将详细介绍标题和描述中提到的51反汇编、Feeling反汇编软件,以及它们在PIC和AVR单片机中的应用。 51反汇编...

Global site tag (gtag.js) - Google Analytics