freeldr(Free Loader) 显示输出分析(转载)
reactos吧
全部回复
仅看楼主
level 6
fros 楼主
2009年04月09日 05点04分 1
level 6
fros 楼主
从 printf() 开始
printf() 的实现位于 freeldr/rtl/print.c 中,很容易就可以找到其输出函数 MachConsPutChar() 来一个一个的输出字符显示。
MachXXXFunc() 函数簇在 freeldr/machine.c 文件中转成一个全局结构 MachVtbl 的成员函数间接调用,或者直接定义成宏(freeldr/include/machine.h 中定义)。
也就是这个全局结构变量 MachVtbl 可以自由的配置成不同的实现以便 freeldr 移植到不同的平台。
2009年04月09日 05点04分 2
level 6
fros 楼主
# 继续 printf() 的 PcConsPutChar()
PcConsPutChar() 的实现在 freeldr/arch/i386/pccons.c 中,除开 '\n' 和 '\t' 的处理,PcConsPutChar 其实是直接使用 BIOS 调用 10h 来完成显示输出。
实际上,在 freeldr 中 printf() 只是用于特殊情况(内部异常、设备出错等等)的显示,而一般的显示输出使用的是 UI (User Interface) 用户界面。
#
UI 用户界面
freeldr/ui/ 目录为 freeldr 的用户界面实现,定义有两个用户界面,gui 图形和 tui 文本,基于 i386 pc 显示系统的实际情况。gui 图形用户界面当前是一个未完成的实现。所以 ui.c 的 UiXXXFuns() 簇函数只能分派给 TuiXXXFuns() 簇实现。
当前基于 UI 的设计层面,很多与实际实现无关的代码就在这里完成了。UiScreenWidth 和 UiScreenHeight 之类的定义也是在这里。
2009年04月09日 05点04分 3
level 6
fros 楼主
TUI 文本用户界面
TUI 具体实现了 UI 的各种输出/输入界面,大部分代码在 freeldr/ui/tui.c 中,菜单相关内容在 freeldr/ui/tuimenu.c 单独文件中。
tui 的显示输出并没有直接使用 MachVideoPutChar() 函数,而是用了一种 bank 显示的方法。
bank 是将输出内容先输出到一块和显存等大的内存中,待当前输出完成后,将输出的 bank 内存内容一次性直接复制给实际显存。这样做的目的在于对显存的操作不存在延迟,不会在屏幕上出现输出操作中状态。
所以 TuiInitialize() 在直接清除屏幕显示后,使用 VideoAllocateOffScreenBuffer() 来分配 TextVideoBuffer 显示缓存。tui 的显示输出操作均是通过这个 TextVideoBuffer 展开的。
freeldr/video/video.c 的 VideoAllocateOffScreenBuffer() 先确保缓存只存在一个,然后使用 MachVideoGetBufferSize() 得到显存大小,并分配相应内存空间。同时保留到自己的 VideoOffScreenBuffer 变量中。
ui 显示输出函数也有层次分别,其中低层次输出函数(TuiDrawText...)使用 TextVideoBuffer 和 UiScreenWidth/UiScreenHeight 直接写显存(文本显示模式的意思就是显存中直接存放要显示的文本 ASCII 码内容)输出,较高层次的输出再调用低层次的输出函数,而最高层次的输出完成后会使用 VideoCopyOffScreenBufferToVRAM() 函数来更新输出缓存到实际显存完成显示。
VideoCopyOffScreenBufferToVRAM() 定义在 freeldr/video/bank.c 中,直接使用 MachVideoCopyOffScreenBufferToVRAM() 也就是 freeldr/arch/i386/pcvideo.c 中的 PcVideoCopyOffScreenBufferToVRAM() 函数,后者仅仅是直接的内存复制。
2009年04月09日 05点04分 4
level 6
fros 楼主
# 其他内容
freeldr 显示输出用户界面当前虽然是个未完成的实现,但是很多地方都为其计划实现的功能进行了预备编码,比如显示模式切换方面。除去当前还不支持的 gui 图形界面外,即使是文本显示也拥有相当多的显示模式。这些都使得 freeldr 代码过于繁琐和杂乱,并且拥有相当部分的灰色(未使用)代码。当然这些代码完整的展示了 i386 pc 显示的种种内容。:P
另外,freeldr 的 machine 实现(包括显示)基本是用 BIOS 的调用完成的。本文没有描述 BIOS 调用中保护模式和实模式的切换,其具体内容在 freeldr/arch/i386/arch.S 中实现。
# 非英文字符的显示改进
printf() 输出的特殊性可以不需要考虑,所需要做的是 UI 用户界面显示输出的处理。
对于 freeldr 计划的 gui 图形显示界面,一是该模块并未完成,二是图形模式下已经应有字符的具体显示实现,只是字库的选择问题,这里不作叙述。
对于 tui 文本界面,非英文的扩展 ASCII 单字节字符集字符的显示,使用 pcvideo 中的 SetFont 之类函数应该可以解决问题。而我们所使用 CJK 双字节扩展 ASCII 字符集一般就无法这样轻松了。
对于 CJK 这样海量的字符集,若非硬件支持,软件方法只能使用图形显示模式下模拟文本显示。tui 很好的使用 bank 技术,为我们提供了很简单的实现方法。即,只需要对 PcVideoCopyOffScreenBufferToVRAM() 函数进行处理,将其简单的文本显存复制扩展成 CJK/ASCII 字模绘制。而对 tui 的其他实现无需处理。当然,显示模式的设定以及字库加载和 ui 输出时可能遇到的单个双字节字符的问题都是值得考虑的。
# 国际化信息
纵观 freeldr 的输出信息,直接是以 ASCII 字符硬编码于代码之中。如果要对 freeldr 进行国际化设置,一个简单的设定是使用宏定义处理来分别定义输出信息内容。这样自然会造成代码的琐碎,以及因为信息分布而造成遗漏的状况,后续维护也会更繁琐。而将 freeldr 重新编码设定为国际化信息模式则需要大范围的修改。
2009年04月09日 05点04分 5
level 6
fros 楼主
- 作者: larryli 2005年04月4日, 星期一 17:16 
原文地址:http://reactos.bokee.com/1102460.html
又被审核的不成样子了。郁闷!
2009年04月09日 05点04分 6
level 6
fros 楼主
Machine 的平台设置
现在从头开始分析 freeldr,首先是 Boot 启动代码,在 bootsect/ 目录下有几种启动介质和文件系统的不同启动扇区代码。其中最基本的是 fat.asm 即 DOS/WIN FAT12/16 文件系统的启动扇区代码,同时适用于软盘和硬盘。
fat.asm 作为系统硬件启动后最先执行的 freeldr 代码,用于在当前磁盘(软盘或者硬盘)文件系统的根目录下寻找 freeldr.sys 文件项目,并且载入该文件的第一个簇(其实只有 fat12/16 因为其代码繁琐和启动扇区 512 字节大小限制仅仅载入 freeldr 第一个簇以外,其他的启动代码都是完成了整个 freeldr.sys 文件的载入)。然后启动代码跳转到载入内容偏移 + 3 (8003h)处(其他类型的启动代码不需要二次载入,所以会直接跳转到 8000h 处)。
因为 fat 启动代码仅仅只载入了 freeldr.sys 的第一个簇(512 字节),所以 freeldr 开始处为 freeldr/arch/i386/fathelp.asm 二次载入代码,完成自身文件的完整载入系统内存。fathelp.asm 必须链接在 freeldr.sys 的最开始处,并且正好占用 512 字节(fat12/16 的一个文件簇)。需要注意的是,fathelp.asm 一开始用三个字节的硬编码指令跳转到 fathelp.asm 之后。因为除了 fat.asm 启动代码需要 fathelp.asm 外,其他启动代码并不需要。从偏移 3 开始,fathelp.asm 继续 bootsect/fat.asm 的工作继续载入 freeldr.sys 余下的文件簇。完成载入后,ret 到 8000h(fathelp.asm 最开始处)。由于 fathelp.asm 最后使用了 512 字节对齐使自己保证了 512 字节大小。所以代码执行序列转到紧跟着链接在 fathelp.asm 后的 arch.S 中(查看 freeldr/Makefile 文件的 LD 操作)。
freeldr/arch/i386/arch.S 是系统平台的底层操作,必须 i386 的 A20 地址空间以及保护模式和实模式切换等等。完成之后,arch.S 调用 BootMain() 函数。
2009年04月09日 05点04分 7
level 6
fros 楼主
Machine 的平台设置
现在从头开始分析 freeldr,首先是 Boot 启动代码,在 bootsect/ 目录下有几种启动介质和文件系统的不同启动扇区代码。其中最基本的是 fat.asm 即 DOS/WIN FAT12/16 文件系统的启动扇区代码,同时适用于软盘和硬盘。
fat.asm 作为系统硬件启动后最先执行的 freeldr 代码,用于在当前磁盘(软盘或者硬盘)文件系统的根目录下寻找 freeldr.sys 文件项目,并且载入该文件的第一个簇(其实只有 fat12/16 因为其代码繁琐和启动扇区 512 字节大小限制仅仅载入 freeldr 第一个簇以外,其他的启动代码都是完成了整个 freeldr.sys 文件的载入)。然后启动代码跳转到载入内容偏移 + 3 (8003h)处(其他类型的启动代码不需要二次载入,所以会直接跳转到 8000h 处)。
因为 fat 启动代码仅仅只载入了 freeldr.sys 的第一个簇(512 字节),所以 freeldr 开始处为 freeldr/arch/i386/fathelp.asm 二次载入代码,完成自身文件的完整载入系统内存。fathelp.asm 必须链接在 freeldr.sys 的最开始处,并且正好占用 512 字节(fat12/16 的一个文件簇)。需要注意的是,fathelp.asm 一开始用三个字节的硬编码指令跳转到 fathelp.asm 之后。因为除了 fat.asm 启动代码需要 fathelp.asm 外,其他启动代码并不需要。从偏移 3 开始,fathelp.asm 继续 bootsect/fat.asm 的工作继续载入 freeldr.sys 余下的文件簇。完成载入后,ret 到 8000h(fathelp.asm 最开始处)。由于 fathelp.asm 最后使用了 512 字节对齐使自己保证了 512 字节大小。所以代码执行序列转到紧跟着链接在 fathelp.asm 后的 arch.S 中(查看 freeldr/Makefile 文件的 LD 操作)。
freeldr/arch/i386/arch.S 是系统平台的底层操作,必须 i386 的 A20 地址空间以及保护模式和实模式切换等等。完成之后,arch.S 调用 BootMain() 函数。
BootMain() 位于 freeldr/freeldr.c 文件中,是整个 freeldr 的主函数。至此,freeldr 的启动完成。
BootMain() 使用 freeldr/arch/i386/archmach.c 中 MachInit() 来初始化系统平台。MachInit 首先对 MachVtbl 清零,然后根据一个I/O端口返回的魔数来判断平台(是XBox?),最终 MachInit 继续调用 freeldr/arch/i386/machpc.c 的 PcMachInit()。
PcMachInit() 给 MachVtbl 的成员函数赋值,这些函数的具体实现在 freeldr/arch/i386/pcXXX.c 文件中。
这样,freeldr 的 machine 设置为 pc...
看完了上面长长的一段,就应该知道 MachConsPutChar() -> MachVtbl.ConsPutChar() -> PcConsPutChar() 的关系了。下面的叙述就只是 PcXXXFuns() 的实现了,虽然高层次的代码会直接调用 MachXXXFuns() 函数。
2009年04月09日 05点04分 8
1