1.AT&T汇编语法
前面写过,boot.s程序使用的是as86汇编语法,而进入head.s需要启动linux的时候,就需要使用AT&T汇编语法了,这与intel的汇编语法不一样,微软使用的是intel的汇编语法,暂时就不去了解了。我们目前使用的GNU工具,使用的是AT&T汇编语法,所以AT&T汇编语法是可以简单的学习一下的。
2.开始汇编代码编写测试
2.1 第一个测试程序
这部分主要是参考网络上的一些文章,从这些文章中快速了解at&t汇编语法在机器上实现的效果。
首先,编写demo.s汇编程序,代码如下所示:
.section .data
.section .text
.globl _start
_start:
movl $1, %eax
movl $4, %ebx
int $0x80
编译程序,如下所示:
$ as demo.s -o demo.o
$ ld demo.o -o demo
$ ./demo
$ echo $?
4
我们需要查询一下int 0x80的调用规则,才能更近一步了解程序的含义。
我截取一部分图,如下图所示:
所以,只要eax=1,我们就能调用到sys_exit函数。而ebx则是sys_exit函数的返回值。
2.2 汇编语法
2.2.1 基本语法
内存寻址在指令中可以表示成如下的通用格式:
ADDRESS_OR_OFFSET(%BASE_OR_OFFSET,%INDEX,MULTIPLIER)
它所表示的地址可以这样计算出来:
目标地址 = ADDRESS_OR_OFFSET + BASE_OR_OFFSET + MULTIPLIER * INDEX
其中ADDRESS_OR_OFFSET和MULTIPLIER必须是常数,BASE_OR_OFFSET和INDEX必须是寄存器。在有些寻址方式中会省略这4项中的某些项,相当于这些项是0。
1.直接寻址
根据以上公式,只使用ADDRESS_OR_OFFSET寻址,例如movl ADDRESS, %eax把ADDRESS地址处的32位数传送到eax寄存器。
2.变址寻址
movl data_items(,%edi,4), %eax就属于这种方式,用于访问数组很方便。
3.间接寻址
只使用BASE_OR_OFFSET寻址,例如movl (%eax), %ebx,把eax寄存器的值看作地址,把这个地址处的32位数传送到ebx寄存器。
4.基址寻址
只使用ADDRESS_OR_OFFSET和BASE_OR_OFFSET寻址,例如movl 4(%eax), %ebx,用于访问结构体成员比较方便,例如一个结构体的基地址保存在eax寄存器中,其中一个成员在结构体内偏移量是4字节,要把这个成员读上来就可以用这条指令。
5.立即数寻址
就是指令中有一个操作数是立即数,例:movl $3, %eax。
6.寄存器寻址
就是指令中有一个操作数是寄存器。在汇编程序中寄存器用助记符来表示,在机器指令中则要用几个Bit表示寄存器的编号,这几个Bit与可以看做寄存器的地址,但是和内存地址不在一个地址空间。
我从网上摘抄一个整体的描述表格:
2.2.2 asm volatile GCC的内嵌汇编语法
我们分为两种,一种是基本的内联汇编语法,一种是带有c/c++表达式的内联汇编语法。
1.基本的内联汇编语法
格式为:
__asm__ __volatile__("Instruction List");
2.带有c/c++格式的内联汇编语法
格式为:
__asm__ __volatile__("Instruction List" : Output : Input : Clobber/Modify);
3 中断功能
3.16 INT 10H
1.AH=00H 设定显示模式的服务程序,AL 寄存器表示欲设定的模式
AL | 文字/图形 | 分辨率 | 颜色 |
---|---|---|---|
00 | 文字 | 40*25 | 2 |
01 | 文字 | 40*25 | 16 |
02 | 文字 | 80*25 | 2 |
03 | 文字 | 80*25 | 16 |
04 | 图形 | 320*200 | 2 |
05 | 图形 | 320*200 | 4 |
06 | 图形 | 640*200 | 2 |
2.AH=01H
光标起始处与终止处分别由 CL 与 CH 的 0 到 4 位表示。
CH 的第 7 位必须是 0,第 5、6 位表示光标属性
位 6 | 位 5 | 属性 |
---|---|---|
0 | 0 | 正常 |
0 | 1 | 隐形 |
1 | 0 | N/A |
1 | 1 | 闪烁缓慢 |
3.AH=02H
此功能是设定光标位置,位置用 DH、DL 表示,DH 表示列号,DL 表示行号。由左至右称之为『列』,屏幕最上面一列为第零列,紧靠第零列的下一列称为第一列……;由上而下称之为『行』,屏幕最左边一行称之为第零行,紧靠第零行右边的一行为第一行。故最左边,最上面的位置为 DH=0 且 DL=0;最左边第二列,DH=1,DL=0。如果是文字模式时,BH 为欲改变光标位置的显示页,如果是图形模式,BH 要设为 0。
以行列来说明 DH、DL 之意义,底下以坐标方式解释。在文字模式下,字符的位置类似数学直角座标系的座标,但是 Y 轴方向相反,Y 轴是以屏幕最上面为零,越下面越大,直到 24 为止,存于 DH 内。X 轴和直角座标系相同,越右边越大,存于 DL 内,其最大值视显示模式而变。
4.AH=03H
这个中断服务程序返回时,会在 DX 里面有光标的行列位置,CX 内有光标的大小,DX、CX 之数值所代表的意义和 AH=01H/INT 10H、AH=02H/INT 10H 相同。
5.AH=13H
参数 | 说明 |
---|---|
AL | 显示模式 |
BH | 视频页 |
BL | 属性值(如果AL=0x00或0x01) |
CX | 字符串的长度 |
DH,DL | 屏幕上显示起始位置的行、列值 |
ES:BP | 字符串的段:偏移地址 |
显示模式分如下几种:
参数 | 说明 |
---|---|
AL=0x00 | 字符串只包含字符码,显示之后不更新光标位置,属性值在BL中 |
AL=0x01 | 字符串只包含字符码,显示之后更新光标位置,属性值在BL中 |
AL=0x02 | 字符串包含字符码及属性值,显示之后不更新光标位置 |
AL=0x03 | 字符串包含字符码及属性值,显示之后更新光标位置 |
3.19 INT 13H
1.AH=00H 软、硬盘控制器复位
功能说明:
此功能复位磁盘(软盘和硬盘)控制器板和磁盘驱动器,它在磁盘控制器芯片上完成复位操场作并在磁盘进行所需的操作之前做一系列用于磁盘校准磁盘操作。
当磁盘I/O功能调用出现错误时,需要调用此功能,此刻复位功能将使BIOS象该磁盘重新插入一样检查驱动器中磁盘状态,并将磁头校准使之在应该在的位置上。
此功能调用不影响软盘或硬盘上的数据。
参数1 | 入口参数2 | 说明 |
---|---|---|
AH=00H | DL需要复位的驱动器号 | 若产生错误,进位标志CF=1,错误码在AH寄存器。详情请见磁盘错误状态返回码一文。 |
2.AH=02H 读扇区
调用此功能将从磁盘上把一个或更多的扇区内容读进存贮器。因为这是一个低级功能,在一个操作中读取的全部扇区必须在同一条磁道上(磁头号和磁道号相同)。BIOS不能自动地从一条磁道末尾切换到另一条磁道开始,因此用户必须 把跨多条磁道的读操作分为若干条单磁道读操作。
参数 | 说明 |
---|---|
AL | 置要读的扇区数目,不允许使用读磁道末端以外的数值,也不允许使该寄存器为0 |
DL | 需要进行读操作的驱动器号 |
DH | 所读磁盘的磁头号 |
CH | 磁道号的低8位数 |
CL | 低5位放入所读起始扇区号,位7-6表示磁道号的高2位 |
ES:BX | 读出数据的缓冲区地址 |
返回参数:
如果CF=1,AX中存放出错状态。读出后的数据在ES:BX区域依次排列。
详情请参见磁盘错误状态返回码一文。
3.AH=03H 写扇区
功能说明:
调用此功能将从磁盘上把一个或更多的扇区内容写入驱动器。因为这是一个低级功能,在一个写入操作中的全部扇区必须在同一条磁道上(磁头号和磁道号相同)。BIOS不能自动地从一条磁道末尾切换到另一条磁道开始,因此用户必须把跨多条磁道的写操作分为若干条单磁道写操作。
参数 | 说明 |
---|---|
AL | 置要写的扇区数目,不允许使用读磁道末端以外的数值,也不允许使该寄存器为0 |
DL | 需要进行写操作的驱动器号 |
DH | 所写磁盘的磁头号 |
CH | 磁道号的低8位数 |
CL | 低5位放入所读起始扇区号,位7-6表示磁道号的高2位 |
ES:BX | 放置写入数据的存储区地址 |
返回参数:
如果CF=1,AX中存放出错状态。
详情请参见磁盘错误状态返回码一文。
4.AH=08H 得到磁盘驱动器参数
参数 | 说明 |
---|---|
AX | 0 |
BH | 0 |
BL | 1=360K 5.25寸,2=1.2M 5.25寸 ,3=720K 3.5寸 ,4=1.44M 3.5寸 |
CH | 存放10位磁道数的低8位(高2位在CL的D7、D6中)。1表示有1个磁道,2表示有2个磁道,依次类推。 |
CL | 0~5位存放每磁道的扇区数目。6和7位表示10位磁道数的高2位。 |
DH | 最大磁头号(或说磁面数目)。0表示有1个磁面,1表示有2个磁面 |
DL | 本机软盘驱动器的数目 |
ES:SI | 指向软盘参数表 |
错误信息:
若产生错误,进位标志CF=1,AH存放错误信息码。
把以上得到的磁盘参数分别放到parameters处相应的位置,磁盘参数占11字节的空间。 由于si是1个字节,所以是ax,bx,cx,dx,es,si共2*6-1=11字节空间。
3.21 INT 15H
1.AH=88H 获取扩展内存大小
返回:
参数 | 说明 |
---|---|
AX | 内存大小,获取1MB存储区以上的内存大小 |
CF | 标志位,0表示没有错误 |
磁盘错误状态:
AH | 说明 |
---|---|
00H | 未出错 |
01H | 非法功能调用命令区。 |
02H | 地址标记损坏,扇区标识(ID)无效或未找到。 |
03H | 企图对有写保护的软盘执行写操作。 |
04H | 所寻找的扇区没找到。 |
05H | 复位操作失败。 |
06H | 无介质。 |
07H | 初始化错误,数据未存在DMA的64K缓冲区内。 |
08H | DMA故障 |
09H | DMA边界错误,数据未存在DMA的64K缓冲区内。 |
0AH | 检测出错误码率的扇区标志。 |
0BH | 所寻找的磁道没找到。 |
0CH | 介质类型没发现。 |
0DH | 扇区号有问题。 |
0EH | 发现控制数据地址标记。 |
0FH | 超出DMA边界 |
10H | 读磁盘时奇偶校验错,且纠错码(EDC)不能纠正。 |
11H | 读磁盘时奇偶校验错,但纠错码(EDC)已纠正错误。 |
20H | 控制器错。 |
40H | 查找操作无效。 |
80H | 超时错误,驱动器不响应。 |
AAH | 驱动器未准备好。 |
BBH | 不明错误。 |
CCH | 被选驱动器出现写故障。 |
E0H | 错误寄存器是零 |
FFH | 非法操作。 |
4 保护模式以及编程
4.1 内存管理寄存器
从网上抄录一幅图:
从这幅图中,我们可以看出,内存管理器分为全局描述符表寄存器GDTR、中断描述符表寄存器IDTR、TR任务寄存器、局部描述符表寄存器LDTR。
- GDTR
GDTR寄存器用于存放全局符号描述表(GDT)的线性基地址(32位)和表长度值(16位)。基地址指定GDT表中的字节0在线性地址空间中的地址,表长度指明GDT表的字节长度值,指令LGDT和SGDT分别用于加载和保存GDTR寄存器的内容。在机器刚上电或处理器复位后,基地址被默认设置为0,而表长度被设置成0xFFFF。在保护模式初始化过程中必须给GDTR加载一个新值。
上面所说的就是GDTR寄存器。我们需要注意,是寄存器。而下面要说的是全局描述符表,是64位的。我们只需要放到内存中即可。然后将内存地址给GDTR寄存器。就可以访问表中的内容了。
相应的描述如下:
G:
G=0时,段限长的20位为实际段限长,最大限长为220=1MB。
G=1时,则实际段限长为20位段限长乘以212=4KB,最大限长达到4GB。
D/B:
D位:
当描述符指向的是可执行代码段时,则会形成D位,D=1使用32位地址和32/8位操作数,D=0使用16位地址和16/8位操作数。
B位:
当指向的是向下扩展的数据段,这一位就叫做B位,B=1时段的上界为4GB,B=0时段的上界为64KB。
当指向的是堆栈段,这一位也叫做B位,B=1使用32位操作数,堆栈指针用ESP,B=0时使用16位操作数,堆栈指针用SP。
P:
表示段是否存在于内存中,P为1表示存在。
该位在未开启分页模式的情况下有用。如果内存不足可以把内存中的某些段暂时移动到硬盘上去,腾出空间。这样如果在访问段时CPU发现P为0,就会触发异常。操作系统捕获到异常后会把段从硬盘挪会内存去,然后把P置为1。
如果开启了分页模式,那么分页模式本身就支持内存置换。就不需要这个P字段了。
DPL:
该位为特权级,0为最高特权级,3为最低,表示访问该段时CPU所需处于的最低特权级。
TYPE:
当TYPE<=7时,表示的是数据段。
值 | 说明 |
---|---|
0 | 只读 |
1 | 只读,已访问 |
2 | 读写 |
3 | 读写,已访问 |
4 | 只读,向下扩展 |
5 | 只读,向下扩展,已访问 |
6 | 读写,向下扩展 |
7 | 读写,向下扩展,已访问 |
当TYPE>=8时,表示的是代码段。
值 | 说明 |
---|---|
8 | 只执行 |
9 | 只执行,已访问 |
10 | 执行,可读 |
11 | 执行,可读,已访问 |
12 | 只执行,一致 |
13 | 只执行,一致,已访问 |
14 | 执行,可读,一致 |
15 | 执行,可读,一致,已访问 |
- IDTR
与GDTR的作用类似,IDTR寄存器用于存放中断描述符表的32位线性基地址和16位表长度值。指令LIDT与SIDT分别用于加载和保存中断描述符表的内容。在机器刚刚上电或处理器复位后,基地址默认设置为0,长度值被设置为0xFFFF
- TR
TR寄存器用于存放当前任务TSS段的16位段选择符,32位基地址、和16位段长度和描述符属性值。它引用GDT表中的一个TSS类型放入描述符,指令LTR和STR分别用于加载和保存TR寄存器的段选择符部分。
- LDTR
LDTR用于存放局部描述符表LDT的32位线性基地址、16位段限长和描述符属性值。指令LLDT和SLDT用于加载和保存LDTR寄存器的段描述符部分,包含LDT表的段必须在GDT表中有一个段描述符项。