1. 主引导扇区
处理器加电或复位之后,如果硬盘是首选的启动设备,那么 RMO-BIOS 将尝试读取硬盘 0 面 0 道 1 扇区。传统上,这就是主引导扇区(Main Boot Sector,MBR)。
主引导扇区数据又512字节,ROM-BIOS 将其加载到逻辑 0x0000:0x7c00 处,即物理地址 0x07c00 处。一个有效的主引导扇区,其最后两个字节是 0x55 和 0xaa。如果主引导扇区有效,ROM-BIOS 就会段间跳转 jmp 0x0000:0x7c00 到该处继续执行。
2. 在屏幕上显示文字
为了显示文字,需要将内容写到显存中(Video RAM,VRAM)。如下图所示,是字符在屏幕上显示的原理图:
文本模式和图型模式是显卡的两种基本工作模式。上述专门用于显示字符的工作方式称为文本模式。它需要将字符的 ASCII 码送到显存中。
处理器为了能够直接,有效的访问显存,将显存映射到了处理器可以直接访问的地址空间中。即内存中。如下图是显存到内存的映射:
8086 可以访问 1MB 的内存。其中 0x0000 - 0x9FFFF 属于常规内存,由内存条提供;0xF0000 - 0xFFFFF 由主板上的一个芯片提供,即 ROM-BIOS。中间 0xA0000 - 0xEFFFF 320KB 的地址由特定的外围设备提供,其中包括显卡 0xB8000 - 0xBFFFF。
所有在个人计算机上使用的显卡,在加电自检后都会把自己初始化到 80 * 25 的文本模式。每屏 25 行,每行 80 字符,共 2000 字符。
3. 分析代码
书本参考代码如下:
;代码清单5-1
;文件名:c05_mbr.asm
;文件说明:硬盘主引导扇区代码
;创建日期:2011-3-31 21:15
mov ax,0xb800 ;指向文本模式的显示缓冲区
mov es,ax
;以下显示字符串"Label offset:"
mov byte [es:0x00],'L'
mov byte [es:0x01],0x07
mov byte [es:0x02],'a'
mov byte [es:0x03],0x07
mov byte [es:0x04],'b'
mov byte [es:0x05],0x07
mov byte [es:0x06],'e'
mov byte [es:0x07],0x07
mov byte [es:0x08],'l'
mov byte [es:0x09],0x07
mov byte [es:0x0a],' '
mov byte [es:0x0b],0x07
mov byte [es:0x0c],"o"
mov byte [es:0x0d],0x07
mov byte [es:0x0e],'f'
mov byte [es:0x0f],0x07
mov byte [es:0x10],'f'
mov byte [es:0x11],0x07
mov byte [es:0x12],'s'
mov byte [es:0x13],0x07
mov byte [es:0x14],'e'
mov byte [es:0x15],0x07
mov byte [es:0x16],'t'
mov byte [es:0x17],0x07
mov byte [es:0x18],':'
mov byte [es:0x19],0x07
mov ax,number ;取得标号number的偏移地址
mov bx,10
;设置数据段的基地址
mov cx,cs
mov ds,cx
;求个位上的数字
mov dx,0
div bx
mov [0x7c00+number+0x00],dl ;保存个位上的数字
;求十位上的数字
xor dx,dx
div bx
mov [0x7c00+number+0x01],dl ;保存十位上的数字
;求百位上的数字
xor dx,dx
div bx
mov [0x7c00+number+0x02],dl ;保存百位上的数字
;求千位上的数字
xor dx,dx
div bx
mov [0x7c00+number+0x03],dl ;保存千位上的数字
;求万位上的数字
xor dx,dx
div bx
mov [0x7c00+number+0x04],dl ;保存万位上的数字
;以下用十进制显示标号的偏移地址
mov al,[0x7c00+number+0x04]
add al,0x30
mov [es:0x1a],al
mov byte [es:0x1b],0x04
mov al,[0x7c00+number+0x03]
add al,0x30
mov [es:0x1c],al
mov byte [es:0x1d],0x04
mov al,[0x7c00+number+0x02]
add al,0x30
mov [es:0x1e],al
mov byte [es:0x1f],0x04
mov al,[0x7c00+number+0x01]
add al,0x30
mov [es:0x20],al
mov byte [es:0x21],0x04
mov al,[0x7c00+number+0x00]
add al,0x30
mov [es:0x22],al
mov byte [es:0x23],0x04
mov byte [es:0x24],'D'
mov byte [es:0x25],0x07
infi: jmp near infi ;无限循环
number db 0,0,0,0,0
times 203 db 0
db 0x55,0xaa
为了显示字符串,需要将字符的 ASCII 依次送入 显存中。
- 6-7 行代码:由于文本模式下显存的起始物理地址是 0xB8000,所有将 ES 指向 0xB800 作为段地值。所有 2000 字符的偏移地址为 0x0000 - 0x0FA0。
- 10-35行代码:显示字符串 “Label offset:”,在文本模式下,需要两个字节显示一个字符,低字节代表字符的 ASCII 码,高字节代表显示属性。字符代码和字符属性示意图如下:
在8086下,80*25 文本模式的颜色表如下:
由表中可知, 属性0x07代表黑底白字,无闪烁,无加亮。当屏幕一片漆黑时,实际上显示的是,黑底白字的空格字符。 - 37行:取得标号 number 的汇编地址 302。程序中还需要显示 number 处的汇编地址显示出来。汇编地址是编译器编译期间会给每一条代码一个汇编地址,这个汇编地址实际上是从0开始。在分段机制中,偏移地址也是从0开始。实际上,这个汇编地址就是与偏移地址是对应的。如下图:
- 38行:将bx寄存器赋值为10 ,作为下面除以10 的时候的除数。
- 100行:定义了五字节的数据,用来暂存分解 number 汇编地址的各个数位。
- 41-42行:我们将DS寄存器指向代码段,这里代码段和数据段是同一个段。
- 70-93行:先将各个数位转化成十进制显示,然后送入到显存,在每一个字符后面写入显示属性0x04,代表黑底红字。
- 95-96行:显示字符 D ,黑底白字。
- 98行:无限循环,防止处理器再指向接下来的指令。
- 102行:由于主引导扇区是512字节的,我们写的程序并没有达到512字节。所以我们应该将主引导扇区未满的地方填满。
- 103行:一个有效的主引导扇区,最后两个字节是0x55和0xaa。