[原创]BIOS 入门之一: 8086 模式
要成为一个真正的BIOS工程师,最基本的必需熟悉整个80X86体系,PCI规范,ACPI规范,如果对PCIE,VESA,SATA,USB,ISA,PNP,AGP等均全面深入,那你应该是一个真正的高手了.今天开始, 本人班门弄斧, 不定期写一些基本的计算机体系方面的知识, 希望对刚刚踏入BIOS这一领域的同行有所帮助! 以下所有文字除特殊说明外, 所有的均属本人原创!
BIOS入门之一: 8086模式
我们目前几乎所有的计算机使用的都是 Intel X86 兼容的CPU体系, 至少你眼前现用的电脑就是, (当然,也有其它的, 如Apple使用的是另外的架构:), Intel最成功的CPU是8086的推出, 拥有20条地址线, 最大寻址为1MB, 之后再推出的一系列 186/286/386/486...为了兼容已经成形的计算机革命, 均向下支持所有的8086程序和 OS, 当然真成功的是80386的推出, 提供32位4GB的空间寻址,基于段方式的保护模式管理和先进的虚拟页管理, 之后再推出的都是基于80386架构, 只是增加了新的一些多媒体指令, 如MMX, SSE等, 当然, 其中, 不得不提到AMD所做的贡献... 越说越远了, 回到主题上来, 先了解一下8086的东西, 80386以后的东西会在下一篇文章中写.
先说一下目前的CPU的工作模式:
1. Real Mode (实模式): DOS系统就是在这个模式下运行的,也就是8086兼容模式,所有的操作与8086 CPU完全一样.
2. Protect Mode (保护模式):Windows系统就是在这个模式下运行的.等下一篇....
3. VM86 Mode (虚拟86模式):RealMode和ProectMode不能同时运行的,因此,在Porect下,可以在这个模式下运行基于16位的8086程序, 下下一篇会讲到....
3. SMM Mode (系统管理模式): 提供给 BIOS 最底层的 CPU 的一个管理模式,再等下一篇...
4. Long Mode : 64位 CPU的保护模式, 再再下一篇...
今天说说Real Mode 8086 寻址模式:
8086全是16位工作模式,为了访问整个1M空间,使用了<段:偏移>的方式来实现,目标地址=(段<<4)+偏移
段寄存器: CS, DS, ES, SS, FS, GS 来定位以16Byte为单位的基地址,
寄存器: AX, BX, CX, DX, SI, DI, BP, SP 则作为偏移.
当CPU上电后,CS=0F000h, IP=0FFF0h, 指向 0FFFF0h 也就是1MB - 10h Byte的地方, 最多只有16字节的空间,放不了什么代码了,因此,这里一般放的是一条 JMP 指令,
试试在DOS下运行Debug:
-u F000:FFF0
F000:FFF0 EA5BE000F0 JMP F000:E05B
F000:FFF5 3130 XOR ,SI
F000:FFF7 2F DAS
F000:FFF8 3130 XOR ,SI
F000:FFFA 2F DAS
F000:FFFB 3037 XOR ,DH
F000:FFFD 00FC ADD AH,BH
F000:FFFF 57 PUSH DI
对于Award BIOS来说, F000:FFF0的指令一定是 JMP F000:E05B ,其它的厂商BIOS可能不是E05B.
试一下,
-G=F000:FFF0
看看会什么样:)
以上的操作在真正的的DOS下运行,不要在Windows下的 DOS Command 下!!!那个是VM86 Mode..
我们可以以多种方式来对8086寻址, 下面列出的是常的:
MOV ES:, AX ;把AX的内容复制到 (ES<<4)+SI 地址所指向的内存
MOV DS:, AX ;把AX的内容复制到 (DS<<4)+SI+1234 地址所指向的内存
MOV , AX ;把AX的内容复制到 (DS<<4)+SI+1234 地址所指向的内存,
;通常,没有指定段,则 SI 使用 DS, DI 使用 ES, SP/BP使用SS
MOV CS: ;把AX的内容复制到 (CS<<4)+1234 地址所指向的内存
JMP 1234h ;跳转到 CS:1234的地方, 之后 IP = 1234h, CS不变
JMP 5678:1234 ;跳转到 5678:1234的地方, 之后 IP = 1234h, CS = 5678
JMP BX ;跳转到 BX 指向的地方, 之后 IP = BX, CS不变
JMP word ptr ;跳转到 DS:的内容指向的地方, 之后 IP=DS:, CS不变
JMP dword ptr ;跳转到 DS:的内容指向的地方, 之后 IP=DS:, CS=DS:
知道了什么寻址,我们来试试, 同样,运行Debug,
=F B800:0 1000 55 17 ;填充 B800:0, 填1000h个字节, 使用55, 17, 你别告诉我debug使用不熟.
看看什么现象再说.
现在你应该知道了 DOS 1M 的空间, 实际上有些区域是已经固定给一些硬件使用的了,
经常提到的640K基本内存, 就是00000-9FFFF, 这部分是可用内存, A0000-BFFFF 给VGA显存使用, 而你刚才填
B8000则是文本缓冲区, 向该地址写任何东西, 均会立即反应在显示器上(记得是在文本模式下), C0000-CFFFF
一般是给VGA BIOS占用, D0000-DFFFF给一些其它ROM使用, E0000-FFFFF一般给BIOS占用, 提供BIOS相关的服务
在基本内存中, 0-3FF共256*4, 存放INT0~FF的入口地址, 400-4FF存放BIOS的数据, 看一下吧!
-D 0:400
0000:0400F8 03 F8 02 E8 03 E8 02-BC 03 78 03 78 02 80 9F ..........x.x...
0000:041023 C8 00 80 02 00 00 00-00 00 24 00 24 00 30 0B #.........$.$.0.
0000:042030 0B 0D 1C 20 39 30 0B-3A 27 34 05 30 0B 0D 1C 0... 90.:'4.0...
0000:043044 20 20 39 34 05 08 0E-30 0B 3A 27 34 05 00 00 D94...0.:'4...
0000:0440E0 00 C3 00 00 00 00 00-4D 03 50 00 40 1F 00 00 ........M.P.@...
0000:045000 31 00 00 00 00 00 00-00 00 00 00 00 00 00 00 .1..............
0000:046007 06 00 D4 03 29 30 F6-03 00 F0 FF D1 C4 0C 00 .....)0.........
0000:047000 00 00 00 00 00 00 00-14 14 14 14 01 01 01 01 ................
第1,2,3,4个字:03F8, 02F8, 03E8, 02E8, 原来是COM1/COM2/COM3/COM4的地址..
.... 继续...
了解了一些东西, 我们试试什么调用BIOS提供的服务, 启动到DOS, 注意,以下提到的,均是在真正的DOS环境,不是在WINDOWS下的Command窗口.
运行Debug:
-A
0B62:0100 MOV AX,0013
0B62:0103 INT 10
0B62:0105 MOV AX,A000
0B62:0108 MOV ES,AX
0B62:010A XOR DI,DI
0B62:010C MOV BL,C8
0B62:010E DEC BL
0B62:0110 JZ 011B
0B62:0112 MOV CX,0140
0B62:0115 MOV AL,BL
0B62:0117 REPZ
0B62:0118 STOSB
0B62:0119 JMP 010E
0B62:011B XOR AX,AX
0B62:011D INT 16
0B62:011F MOV AX,0003
0B62:0122 INT 10
0B62:0124 INT 3
-G
-Q
INT10 VGA BIOS服务, 上面开始设置为320*200*256色模式, 最后恢复为文本模式,INT 16 键盘服务, 找找看, 把在学校学的计算机书看看, 你会找到答案的.
再来一个,
-A
0B62:0100 INT19
-G
看看出现了什么, 都是BIOS提供的东西!! 如果你已经是一个BIOS工程师了, 有机会接触到BIOS Code, 那就到里面找找答案吧!!!
我们再来看看IO的访问, 8086提供0000-FFFF共64K的IO Space的访问, 其中部分地址已经固定, 00-1F给我DMA, 20-23给了中断, 60/64给了KBC/MS, 70/71给了RTC, 那80给谁了, 就是debug卡了, 如果你板上插有debug卡, 试试在debug下:
-O 80 AA
看是不是显示AA了.
对于IO的访问,需要使用AL, 或AX, 或EAX (用到了386寄存器),和DX
00-FF的访问, 可以直接寻址
OUT 80h, AL ;向80端口输出一BYTE, 必需通过AL实现
IN AL, 80h ;从80端口读入一Byte, 也要通过 AL
对于WORD, 则为
OUT 80h, AX
IN AX, 80h
那DWORD呢, 得用上了386的32位寄存器了.
OUT80h, EAX
IN EAX, 80h,
当端口地址>=100h后,则必需要通过 DX来寻址了.
如:
MOVDX, 3F8h
MOVAL, 55h
OUTDX, AL
知道了IO的访问,我们来试试,debug
-A
0B62:0100 MOV AL,FE
0B62:0102 OUT 64,AL
0B62:0104 INT 3
0B62:0105
-G
出现了什么,你现在应该知道了reboot.com是什么实现的了,上面提供了两种方法!
因 IO Space 只有64K的地址空间,对于一个需要比较多的IO地址分配的系统来说,会不够用,因此,对于IO设备的访问,还有一种方式,就是一个端口作为Index,另外一个端口作为Data, 如RTC CMOS的访问.使用70来选择Index, 71来输入或输出数据,只通过两个端口就可以以访问到256个数据(在此不提NMI),试试访问RTC的时间(Index=0):
-a
0B62:0100 mov al,00
0B62:0102 out 70,al
0B62:0104 inal,71
0B62:0106 int 3
0B62:0107
-g=100
AX=0042BX=0000CX=0000DX=0000SP=FFEEBP=0000SI=0000DI=0000
DS=0B62ES=0B62SS=0B62CS=0B62IP=0106 NV UP EI PL NZ NA PO NC
0B62:0106 CC INT 3
-g=100
AX=0044BX=0000CX=0000DX=0000SP=FFEEBP=0000SI=0000DI=0000
DS=0B62ES=0B62SS=0B62CS=0B62IP=0106 NV UP EI PL NZ NA PO NC
0B62:0106 CC INT 3
-g=100
AX=0048BX=0000CX=0000DX=0000SP=FFEEBP=0000SI=0000DI=0000
DS=0B62ES=0B62SS=0B62CS=0B62IP=0106 NV UP EI PL NZ NA PO NC
0B62:0106 CC INT 3
-A
运行了三次,AL就是当前的秒数,是不是一直在变?
现在如果是访问Super IO,你应该知道什么访问了. ......
8086 入门相关的东西就简单地说到这里, 借花献佛, 送给大家一个好工具:
一个由 David Jurgens 写的一个非常牛的工具 HelpPC,
释放后, 进入 helppc21 目录运行 helppc.com, 你想知道的东西就在里面:)
..... 能总结就是牛人呀!
要像楼主学习。 顶,
好的开头,期待!!!:victory: 看牛人的总结,总是能获得收获。支持lz。
lz速度更新啊:lol 期待david老大尽快更新!讲得易懂,好! 真是让人茅舍顿开啊,呵呵,期待david的后作! 好东西,学习中!谢谢! 现在的CPU上电第一条指令都是FFFFFFF0而不是FFFF0了,这段地址译码为Flash ROM芯片,应该是BIOS的Bootblock区域,进行一些初始化后BIOS在1M内存顶端建立所谓的影射内存,把BIOS内部模块解压到E段F段等,然后跳到FFFF0h开始接下来的初始化工作。
所以F0000段读出的BIOS内容并非真正的BIOS,而只是其中一个模块,真正的BIOS在物理地址4GB-1向下的区域 coodkey说的对,楼主是犯了一点小错误 to 楼上的两位:
楼主开头就讲了,他讲的是8086的东西,而非80386。 学习了,期待连载:lol 这个疑问, 大家可以看一下Intel的北桥的EDS, 如果有仔细看完过, 一定找到相关的答案.
可以肯定的是, CPU的上电第一条指令地址一定是 Real Mode 的 F000:FFF0, 而不是FFFF_FFF0, 不管是8086 还是386, 这个是一定不会错的.
Flash ROM 是放在 4G 位置, 但北桥上电RESET后, 是把 对F_0000- F_FFFFh 的访问都映射到 FFFF_0000-FFFF_FFFF的,
因此, CPU 一定是在F_000-F_FFFF中运行, 当Memory Size 完成后, 会Shadow F_0000 到 RAM,访问该区时, 就会访问到RAM, 而不是访问到 FFFF_0000 的BIOS ROM 区了, 这区域r的内容需要填入其它的内容, 通常是BIOS的Rontine服务程序(已经不是Bootblock的内容了), 具体的可以看看你的CODE.
附上一些相关的NorthBridge 信息, 可以参考一下, 更具体的只能看SPEC了, 希望对大家有所帮助:
System BIOS Area (F_0000h–F_FFFFh):
This area is a single 64 KB segment (000F_0000h – 000F_FFFFh). This segment can be
assigned read and write attributes. It is by default (after reset) Read/Write disabled
and cycles are forwarded to DMI Interface. By manipulating the Read/Write attributes,
the GMCH can “shadow” BIOS into the main DRAM. When disabled, this segment is not
remapped.
PAM0 定义:
BIT 5:4Core 0F0000-0FFFFF Attribute (HIENABLE):
This field controls the steering of read and write cycles
that address the BIOS area from 0F0000 to 0FFFFF.
Defalut = 00b, 即所有的访问指向DMI, 也就是Flash ROM的最后64K的区域.
00: DRAM Disabled: All accesses are directed to DMI.
01: Read Only: All reads are sent to DRAM. All writes
are forwarded to DMI.
10: Write Only: All writes are sent to DRAM. Reads
are serviced by DMI.
11: Normal DRAM Operation: All reads and writes are
serviced by DRAM.This register is locked by LT. 谢谢david大哥的回答,小弟我也是新手刚入门,不过还是有一些疑问,《Intel® 64 and IA-32 Architectures
Software Developer’s Manual》上是这么说的
8.1.4 First Instruction Executed
The first instruction that is fetched and executed following a hardware reset is located at physical address FFFFFFF0H. This address is 16 bytes below the processor’s uppermost physical address. The EPROM containing the software-
initialization code must be located at this address.
The address FFFFFFF0H is beyond the 1-MByte addressable range of the processor while in real-address mode. The processor is initialized to this starting address as follows. The CS register has two parts: the visible segment selector part and the hidden base address part. In real-address mode, the base address is normally formed by shifting the 16-bit segment selector value 4 bits to the left to produce a 20-bit base address. However, during a hardware reset, the segment selector in the CS register is loaded with F000H and the base address is loaded with FFFF0000H. The starting address is thus formed by adding the base address to the value in the EIP register (that is, FFFF0000 + FFF0H = FFFFFFF0H).
The first time the CS register is loaded with a new value after a hardware reset, the processor will follow the normal rule for address translation in real-address mode (that is, ). To insure that the base address in the CS register remains unchanged until the EPROM based software-initialization code is completed, the code must not contain a far jump or far call or allow an interrupt to occur (which would cause the CS selector value to be changed).
系统第一条指令在哪,取决于CPU而不是北桥,地址由CPU地址线发出。386以后的CPU上电第一条指令虽然是实模式,但是CS寄存器的隐藏基址部分是FFFF_0000,因此第一条指令确实是FFFF_FFF0,这有点类似实模式下访问4GB内存的方法。而且文档里特别注明,此时不能用长跳转,以免CS寄存器被重新装载,导致CPU跳到1M内存里去。
Intel北桥把F_0000-F_FFFFh 的访问都映射到 FFFF_0000-FFFF_FFFF,这是没错的,但我们不能保证所有北桥都会这样做,如果有的北桥需要手动打开映射呢?
小弟的一些愚见,欢迎拍砖。 coodkey 是个有心人, 你说的没有错!赞扬一个:victory: , 看来下一个主题 386 保护模式你是少不了了:).
本来我是想在写完下一篇386 保护模式之后谈到这个问题的. 因为在8086过程中谈到这个会涉及到比较多的保护模式的东西,
现今的CPU都是像你说的, 的确是第一条指令会寻址在在FFFF_FFF0,但是CS:IP 还是为 F000:FFF0, 只不过CPU在使用 Base=FFFF_0000 Base 的选择子, 因此, 对外产生的地址就为 FFFF_FFF0了,不过, 我们现在的Lagecy BIOS 的第一条指令就是 JMP F000:E05B, 因此, 也就立即跑到 1M 以下的地方去了, 因此就跟8086实模式没有什么区别了. 呵呵,让david大哥见笑了,期待你的下一篇文章 针对Reboot 来讲,CPU 运行的第一条指令F000:FFF0 实际就是Fooo 段的内容。BIOS 只做简单的初始化的动作,并没有对系统的资源重新分配。 积累每一块铺往成功之路的基石,期待下一篇! 辛苦了,继续呀!好好学习,经验之谈。