1.1 旅程的出发点
无论操作系统多么庞大,可是BIOS只认识一个小小的引导扇区,这个半KB[1]的小小的程序却需要负责载入整个庞大的操作系统。引导扇区(BootSector)通常指设备的第一个扇区,用于加载并转让处理器控制权给操作系统。而我们的旅程也是从这里开始的。
一切编程都是从那个小小的Hello World开始的。但是,没有了操作系统,也就没有了库,没有了printf那么好用的函数。所以,我们有必要了解一下BIOS中断服务[2]。
1.2 BIOS中断服务概述
BIOS中断服务程序实质上是微机系统中软件与硬件之间的一个可编程接口,主要用于程序软件功能与微机硬件之间连接。在系统启动初期,虽然没有办法直接操作端口,也没法自力更生,但是BIOS是在加电时就加载入内存的程序。所以,我们的一切都得依靠BIOS。我们在读取磁盘之前,先学学如何显示文字吧。(不要说我偷工减料!)
1.3 BIOS显示服务
BIOS显示服务(Video Service),顾名思义(我是没看出来哪里顾名了),是在实模式[3]下的的显示服务。BIOS显示服务使用INT 10H语句。
下面的表格是显示服务的功能一览表。
AH的值(入口参数) | 功能 |
00H | 设置显示器模式 |
01H | 设置光标形状 |
02H | 设置光标位置 |
03H | 读取光标信息 |
04H | 读取光笔位置 |
05H | 设置显示页 |
06H、07H | 初始化或滚屏 |
08H | 读光标处的字符及其属性 |
09H | 在光标处按指定属性显示字符 |
0AH | 在当前光标处显示字符 |
0BH | 设置调色板、背景色或边框 |
0CH | 写图形象素 |
0DH | 读图形象素 |
0EH | 在Teletype[4]模式下显示字符 |
0FH | 读取显示器模式 |
10H | 颜色 |
11H | 字体 |
12H | 显示器的配置 |
13H | 在Teletype模式下显示字符串 |
1AH | 读取/设置显示组合编码 |
1BH | 读取功能/状态信息 |
1CH | 保存/恢复显示器状态 |
我们暂时还不需要了解这么多,我们只要知道如何显示文字就可以了。查表后看到,显示字符串使用13H入口参数,即AH=13H。查询BIOS标准后得知:
BIOS 10h中断显示服务 功能13h | |||
寄存器 | 取值 | 描述 | |
AX | AH | 入口参数(0x13) | 在Teletype模式下显示字符串 |
AL | 输出方式 | Bit 0: 显示后光标位置是否改变 0:不变 1:改变 | |
Bit 1: 显示属性的位置 0:在BL中 1:包含在字符串中 | |||
BX | BH | 页码 | AL寄存器Bit 1=0时使用,指示显示的页码 |
BL | 显示属性 | 用来指示字体颜色和背景颜色 | |
CX | ES:BP所指向的字符串长度 | ||
DX | DH | 坐标行 | 字符串在BH指定的页上显示的位置 |
DL | 坐标列 | ||
ES:BP | 要显示的字符串地址 |
我们看看,嗯……我们输出的话光标位置当然要改变,所以AL的Bit 0选择1,显示属性放在BL中比较方便,所以选择Bit 1=0,那么我们的AL就应该是01h。BH代表了页面号码,我们令其为0。BL的值代表了显示字符的颜色,由于我比较喜欢黑底白字,所以在这里使用0Fh(0000黑底,1111白字)。CX,DX,ES:BP则顾名思义,存放长度、位置和字符串地址。当然,在实际操作中,要注意保存非易失寄存器,例如BX、SI、BP、SP等。
因此我们可以写出一个字符串输出函数:
; DispStr: CX=字符串长度,AX=字符串地址, DX=字符串位置 DispStr: movbp,ax movax,01301h ; AH= 13h, AL = 01h movbx,0000Fh ; BH= 00h, BL = 0Fh int10h ret |
但是使用这个函数有一个问题,那就是每次输出都要自己指定字符串位置。所以,我们需要自己利用循环+0Eh号功能输出字符串。
BIOS 10h中断显示服务 功能0Eh | |||
寄存器 | 取值 | 描述 | |
AX | AH | 入口参数(0x0E) | 在Teletype模式下显示字符 |
AL | 字符 | 要输出的字符。 | |
BX | BH | 页码 | AL寄存器Bit 1=0时使用,指示显示的页码 |
BL | 显示属性 | 用来指示字体颜色和背景颜色 |
我们可以编写一个这样的函数来输出字符串。
; DispStr: SI=字符串地址 DispStr: moval,[si] ;获得字符 incsi ; SI指向下一个字符 or al,al ;等价于 cmp al,0 jz .end ;如果是\0结尾的话就结束循环 movah,0Eh ; AH= 0Eh,指定功能号 movbx,0000Fh ; BH= 00h, BL = 0Fh,黑底白字 int10h ;调用显示服务 jmp DispStr ;继续循环 .end ret |