Bochs:适用于 Unix/X 的便携式 PC 模拟器
大部分与电脑相关的时间都花在了我的 Sun SPARCstation 工作站上。我可以完成几乎所有与电脑相关的事情,包括电子邮件互动、系统管理、网络冲浪、网络末日战士(我承认——是我传播了那个黑客程序,让您可以在 Solaris 2.3 上运行末日战士),所有这些都在我用于软件开发的同一台机器上完成。不过,偶尔我还是会启动桌子上的旧 PC,使用 MS Word 编写文档或备忘录,或者运行几年前购买的少数几个小型实用程序。嗯,这很快就过时了!桌子上有两套键盘、两台显示器和两台电脑,占用的不仅仅是桌面空间——维护起来需要更多时间,而且来自不同的操作系统设计也会让人感到沮丧。因此,我开始寻找一种软件解决方案,让我在我的 SPARCstation 上运行我使用的一些 PC 程序。
花很多钱来运行我已经购买的东西这个想法让我感到不舒服,而且我有兴趣找到一些必要时可以扩展的东西。在互联网上稍微搜索了一下,并没有找到任何低成本或无预算的程序可以在 SPARC 上运行 MS Windows 3.1,并且可以访问源代码。Wine 和 DOSEMU 项目正在取得巨大进展,但它们永远无法在非 x86 架构上运行。有一个名为 pcemu 的模拟器程序,可以在 SPARC 上运行。它很好地完成了它的预期目的——运行 DOS。不幸的是,它的设计目的是模拟 8086,并且不适合扩展以支持 80286 和 80386 功能。我的搜索还发现了另一个 8086 模拟器,它与旧版本的 MINIX 一起使用,允许 MINIX 在非 x86 平台上运行。由于它的目标是允许真实模式版本的 MINIX 在模拟器中运行,因此这似乎也不是增强的理想选择。
就是没有什么东西可以完成我想做的事情,或者如果我愿意努力增强它,也没有什么好的起点。与此同时,我注意到网上,特别是在 comp.emulators.* 新闻组中,也有类似的浓厚兴趣。在那个时候,我开始着手 Bochs(发音为“box”)这个便携式软件 PC 模拟器项目,目标是让 PC 软件可以在 Unix 工作站上运行。
回顾过去,仍然很难相信这一切是如何实现的。最大的障碍通常是文档。我们这里有一个有点缺乏文档的 DOS(以及后来的 Windows),试图使用大量非标准化的,有时文档也很差的 BIOS 的各个部分,使用并非总是准确或完整记录的硬件设备,并在非开放的 Intel x86 架构上运行,该架构具有未记录的指令和功能(例如,LOADALL)。如果不是像《未文档化的 PC》和《未文档化的 DOS》这样的书籍,我早就放弃了。另一个巨大的障碍是(而且将一直是)追踪和修复在这个如此复杂的系统中似乎几乎不可能找到的错误。这里翻转了一个错误的位,有时不良影响会在 1000 万条指令之后才显示出来,当屏幕上显示错误的字符时!依赖于时序的错误被证明是难以捉摸的,因为它们并非总是出现,而且时序会受到代码中插入的调试打印语句的严重影响。我曾多次筛选过一些 50 多兆字节的调试文件(我称之为隐形错误尾迹),结果却发现我没有打印出我需要的那一条信息!
在我从事这个软件工作的两年半时间里,Bochs 已经取得了几个非常重要的里程碑。第一个是启动 MS DOS 5.0 到 A 盘提示符!那时,我只有非常简陋的文本 HGA(单色视频卡)模拟,没有键盘支持,所以我不得不将按键硬编码到键盘 BIOS 中,以绕过启动期间的时间和日期提示,并在之后运行 DOS 命令。这并不漂亮,但它表明存在潜力。
然后我开始实现 HGA 的图形模式和基本的键盘支持,试图让 Windows 3.0 在实模式下运行。经过许多个不眠之夜,我设法让 Windows 3.0 启动并通过了初始的图形横幅屏幕。多么壮观的景象啊!我记得盯着屏幕,沉浸其中,不敢敲击任何键,生怕它会挂起 Windows。事实上它确实挂起了,那时我知道我走对了路...
虽然我从一开始就将模拟器设计为 32 位,但在当时我只有 8086 模拟。是时候冒险并实现 80286 的保护和内存管理模型了。令人惊讶的是,仅仅几个月后,我就编写了 80286 支持的很大一部分代码,并且能够让 MS Windows 3.1(和 3.0)在标准模式(286 保护模式)下运行。
虽然 MS Windows 和 MS-DOS 在很大程度上是我专注于在模拟器中运行的软件,但我也有一些其他有趣的经历。最近,我一直在与 MINIX 项目(Andrew S. Tanenbaum 和朋友们)合作,使其能够在 Bochs 模拟器下启动/工作。这个想法是允许 MINIX(一个适合学习内部工作原理的 Unix 克隆)在尚未移植到的平台上运行。最近,我能够在 Bochs 中执行完整的 MINIX 安装,并在 286 保护模式下启动 MINIX(尽管还需要做更多工作)。
我曾经与 DOSEMU 项目的源代码合作并将我的源代码与之集成了一段时间。然而,他们的代码更改非常高效,很难跟上,尤其是在 Bochs 的事情开始稳固之前,我很快就编写了自己的键盘和视频支持。
当我开始进行 80386 模拟时(我最近开始了这一旅程),我期待着与 Wine 团队交谈,探讨在编译时选择性地合并 Wine 和 Bochs 的可能性,从而允许 Wine 在非 x86 平台上运行。Wine 包含一种 MS Windows 到 X Windows/Unix 的转换技术,但它缺乏 x86 模拟,而 x86 模拟将使其能够在其他平台上运行。合并将意味着在 Bochs 中运行 Windows 时可以实现显着的性能提升,因为 Windows GUI 和 OS 调用将由 Wine 映射到本机 Unix/X 函数,而不是 Bochs 模拟 Windows 调用所包含的整个过程。这与 Sun 的 Wabi 产品背后的基本概念相同。
由于“模拟器”这个词最近被大量使用,让我澄清一下不同类型的模拟。像 DOSEMU 和 Wine 这样的软件,在我的术语中,是环境或操作系统模拟器。也就是说,它们以本机方式运行可执行文件(x86 代码在 x86 处理器上运行),并转换或“模拟”可执行文件期望的操作系统(如 MS Windows)与当前正在运行的操作系统(如 Linux)之间的交互。这种“模拟”受限于 x86 平台,因为不提供指令集模拟,但它非常高效,并且是 x86 机器上双启动设置的绝佳替代方案。
Bochs 是一个纯粹的模拟器,因为它在软件中模拟每个 x86 指令,以及您期望在 PC 上找到的必要 BIOS 和硬件。有一个巨大的解码循环,它紧密地模拟了 CPU 的取指-解码-执行动作。CPU 的组件——即寄存器——由表示 CPU 的大型结构中的字段建模。主内存由 C 程序中的大型内存数组表示。输入和输出设备,如键盘、定时器、PIC 等,采用非常模块化的设计,通过注册其相应的 IRQ、I/O 地址空间使用情况和中断服务例程“插入”代码的其余部分。
Bochs 需要模拟 Windows 支持的图形卡,我选择了单色 Hercules 图形适配器 (HGA),因为它最容易实现。使用 X 窗口来表示 PC 监视器和视频帧缓冲区是一个便携且自然的选择,它也提供了键盘输入机制。
由于所有指令都在 C 语言中模拟,因此 Bochs 不受任何特定处理器的限制,并且非常容易移植到许多其他 Unix 平台。我希望尽可能地扩展这个主题,并且希望看到 Bochs 移植到其他非类 Unix 操作系统,如 Mac OS。
与任何模拟软件一样,性能始终是一个问题,因为在模拟器下运行程序本质上比运行本机软件慢。我在编写 Bochs 时一直牢记性能,尽管我将一些优化推迟到以后,以便它们不会干扰 386 模拟的开发路径。
有些人问我是否有任何计划在 Bochs 中添加动态编译技术来提高性能。我强烈认同 KISS 原则,并且希望保持源代码简单、易于理解和管理,因此我怀疑我会在短期内添加它。然而,在不久的将来,还有一些更传统的性能增强空间。我认为标志处理是一个具有潜在性能改进的领域。不过,使用如此简单(且蛮力)的模拟方法的一个好处是,它没有许多花哨的优化,因此处理自修改代码没有问题,因为指令流是即时解码和执行的。
从一开始,我就将 Bochs 编码为一个 32 位模拟器(386 及更高版本)。在内部,寄存器和其他功能始终由 32 位量表示。这对我来说是一个巨大的帮助,因为我不断地向模拟支持添加 32 位处理器功能。
由于我开始使用 GNU “autoconf” 实用程序,配置和编译过程变得更加容易和便携。但是,设置环境(硬盘和软盘映像文件、VGA 字体等)以及在 Bochs 中安装 DOS/Windows 3.1 的过程有点冗长。有关详细信息,请参阅 Bochs 发行版中的 INSTALL 文件。但是,为了让您对编译和配置过程有所了解,我在这里包含了我在 Linux 机器上编译 Bochs 和安装 Windows 3.1 时使用的命令。请注意,我使用的是 tcsh,命令在不同的平台上可能会有所不同。
首先,解压缩发行版
$ cd parent-dir
$ gzip -dc bochs-YYMMDD.tgz | tar xvf -
然后配置 Makefile 和源代码,并进行编译。
$ cd bochs-YYMMDD
$ setenv CFLAGS "-O3 -Wall -m486"
$ ./configure --enable-80286 \\
--enable-native-floppy
$ make
如果您使用的是 Bourne shell,例如 bash,setenv 将不起作用。请使用
$ CFLAGS="-O3 -Wall -m486"; export CFLAGS
代替。
您可能需要安装 “VGA” 字体。请阅读 bochs 发行版中的 INSTALL 文件以了解此过程。您的字体目录可能是 /usr/lib/X11/fonts/misc、/usr/openwin/lib/X11/fonts/misc 或其他位置。
$ cp fonts/vga.pcf font-dir $ mkfontdir font-dir
创建一个 可启动 的 1.44M 软盘,其中包含 DOS FORMAT 和 FDISK 可执行文件,使用真实的 DOS 机器。假设 1.44MB 软盘是驱动器 A
C:> FORMAT /s /u A: C:> COPY FORMAT.COM A: C:> COPY FDISK.EXE A:
现在将此软盘插入您的工作站,并将其映像创建为 Unix 文件。假设您的 1.44MB 软盘驱动器是 /dev/fd0,请执行以下操作
$ dd if=/dev/fd0 ibs=512 of=1.44
为 B: 创建一个空的 1.2M 软盘映像文件(在本示例中未使用)
$ dd if=/dev/zero of=1.2 bs=512 count=2400
现在创建一个 20MB 硬盘映像文件(其他大小也是可能的,但我们现在坚持一个简单的例子)
$ dd if=/dev/zero of=20M bs=512 count=41820
编辑源代码顶层的 .bochsrc 文件。将 floppya 行更改为 floppya: file=./1.44,将 floppyb 行更改为 floppyb: file=./1.2,并将 diskc 行更改为 diskc: file=./20M。
使用 1.44M 软盘映像文件启动 Bochs
$ bochs -bootA
Bochs 现在应该正在运行。按几次回车键并接受日期和时间。
使用 FDISK 对 C: 驱动器(20M 文件)进行分区。将整个驱动器添加为主分区。也就是说,运行 FDISK,然后按三次回车键;一次用于“创建 DOS 分区或逻辑 DOS 驱动器”,一次用于“创建主 DOS 分区”,一次用于选择最大尺寸。
此时,只需单击窗口上的鼠标按钮即可退出 bochs。由于 Bochs 尚不支持重启,您必须退出并重启 Bochs,然后格式化 C: 驱动器并将 DOS 系统文件添加到其中
$ bochs -bootA A> FORMAT /u /s c:
再次,通过单击窗口中的任意鼠标按钮退出 Bochs。
现在将 .bochsrc 中的 floppya 行更改为 floppya: 1_44=/dev/fd0。将 Windows 3.1 安装盘的 Disk 1 放入软盘驱动器。然后重启 Bochs,从硬盘映像文件启动,并启动 Windows 3.1 安装
$ bochs -bootC C> a:setup
按 Enter 键立即安装 Windows,然后按“c”进行自定义安装。再次按 Enter 键将 Windows 安装到默认的 C:\WINDOWS 位置。
在 Windows 安装屏幕中,除了键盘类型外,一切都应该识别良好,您需要更改键盘类型。使用箭头键选择“所有 AT 键盘”。继续安装 Windows,仅使用按键(请记住,在窗口中单击鼠标会退出 Bochs)。设置打印机和应用程序没有意义。允许 Windows 创建(或更改)您的 AUTOEXEC.BAT 和 CONFIG.SYS 文件,并观看 Windows 启动。然后返回 DOS,在窗口中单击以退出 Bochs,然后重启 Bochs 和 Windows
$ bochs -bootC C> WIN
图 1 显示了一个示例屏幕。请注意,显示代码中存在一些错误伪像。
Bochs 支持的硬件、CPU 和 BIOS 功能集在很大程度上取决于我在模拟器环境中运行的软件套件。我的大部分努力都集中在支持足够的功能来运行 MS DOS 5.0 和 Windows 3.1 上。最近,我将 Minix 1.7.2 添加到我的目标软件列表中。鉴于此,Bochs 目前支持以下项目:(请记住,新功能总是在不断添加,其中一些可能仅在下一个版本中可用)
8086/80286 指令集,包括保护和虚拟内存模型。配置脚本中的一个选项允许为任一架构进行编译。
单色 Hercules 图形适配器 (HGA) 这是一种相当简单的图形适配器,具有字符和像素映射图形功能。文本和像素图形映射到 X 窗口。
软盘驱动器:1.44M 3.5 英寸、1.2M 5.25 英寸和 720K 3.5 英寸 这些通过将它们映射到相应大小的 Unix 文件或工作站上的软盘驱动器来实现。最初,仅支持 BIOS 软件中断(如子例程调用),但最近我添加了最少量的直接内存访问 (DMA) 和软盘 I/O 支持,用于 Minix。
硬盘驱动器:10、20 或 30 兆字节驱动器 硬盘驱动器也作为 Unix 文件实现。对硬盘驱动器的读写请求被定向到 read() 和 write() Unix 系统调用,这些调用在硬盘映像文件上操作。目前对硬盘驱动器的访问仅限于 BIOS 软件中断。很可能会添加 DMA 和硬盘驱动器 I/O 的使用。
主/从可编程中断控制器 (PIC) 需要传达其中断 CPU 以进行服务的硬件设备连接到任一 PIC。这些包括键盘、系统定时器、磁盘控制器等。提供了对主 PIC 和从 PIC 的相当完整的模拟。
非增强型键盘 来自用于视频显示的 X 窗口的按键输入,为键盘模拟提供输入。同时提供了 BIOS 键盘软件中断 (int 16H) 和硬件 I/O 访问。键盘代码仍然需要做一些工作。
CMOS 功能 我只实现了部分 CMOS 功能,尽管它允许 BIOS 软件中断和硬件 I/O 访问。主要提供处理 CMOS 时间、日期和关机状态的功能。CMOS 实时时钟 (RTC) 尚未实现。
相当数量的典型 BIOS 中断和数据区域值 这些功能包括检测您的 PC 的特性和功能、内存大小信息、读/写软盘和硬盘扇区、间隔定时器、视频功能、引导加载程序等。
通过 GNU 'autoconf' 进行 Makefile 配置
运行 DOS 5.0、Windows 3.0、Windows 3.1 此外,我能够安装和启动 Minix 1.7.2,但还需要做更多工作。
为了真正使 Bochs 成为一个有用的工具,还需要添加更多功能。在不久的将来,计划重点添加 80386 功能集,因为这将允许 Bochs 运行最新的操作系统,例如 Windows 95 和 Windows 3.1 中的 32 位代码。我还有其他改进计划,并且我从通过网络收到的建议和输入中获得了更多想法。以下是我计划添加的其他功能列表
386、486 和其他 x86 代功能 32 位模拟已经有了一个良好的开端。编码应该会顺利进行;测试和调试会更慢更困难。这应该允许 Bochs 运行 Minix for 386、增强模式下的 Windows 3.1、Windows 95 和 Linux。
网络
浮点协处理器模拟
鼠标模拟 我已经在 PS/2 鼠标模拟方面有了一个良好的开端,但由于它与键盘硬件如此紧密地集成在一起,因此使调试键盘/鼠标代码变得困难。鼠标代码目前已禁用,直到我重新审视它。我真的很想获得关于罗技或其他总线鼠标的良好(低级)文档,在这种情况下,我很乐意实现它。
文件系统重定向器 这将允许 DOS 或 Windows 将驱动器(例如 E:)映射到您的 Unix 目录(例如,/home/johndoe/windows-drive)。您还可以读取安装在 Unix 文件系统上的 CD。
更多性能增强 在 386 功能编码完成之前,我不太愿意在性能增强方面做太多工作,但之后,代码的许多元素都需要检查以寻找潜在的性能增强。这将是从世界各地的性能向导那里征求意见的好时机。
以及更多...
有关 Bochs、开发者电子邮件列表、FTP 站点等更多信息,请访问 world.std.com/~bochs/。
Kevin P. Lawton (bochs@world.std.com) 是纽约州立大学奥斯威戈分校 88 届计算机科学毕业生。他曾在 MIT 林肯实验室工作了 6 年半,担任软件工程师和系统管理员。他的线下兴趣包括山地自行车、滑雪、徒步旅行、极限飞盘以及几乎所有其他涉及肾上腺素和户外运动的项目。在 world.std.com/~bochs/ 查找有关 bochs 的更多信息。