发掘我为Am29000自制计算机用机器码编写的1996年窗口操作系统

Hacker News Top 新闻

摘要

Oscar Toledo G. 讲述了他于1996-1997年间为自制Am29000计算机用机器码开发窗口操作系统的经历,以及近期重新发现并记录这一系统的历程。

暂无内容
查看原文
查看缓存全文

缓存时间: 2026/08/06 14:02

# 为自制 Am29000 计算机开发一个窗口操作系统 来源:https://nanochess.org/the_am29000_computer.html *作者:Oscar Toledo G\. 2026年7月30日* 我的模拟窗口操作系统运行在 G11V1(Am29000 自制计算机)上 很久以前,在墨西哥的神秘土地上,从 1996 年秋天到 1997 年春天,我用 32 位机器码编写了一个窗口操作系统,把它塞进一张软盘,然后在它启动时兴奋得跳了起来。 当然,我知道这是一件了不起的事,因为你无法从软盘启动 Windows 3.1 或 GEM,但我远远不知道它到底有多酷。不幸的是,这不是我能拿出来给所有人看的东西,因为它是为一台基于 Am29000 处理器的自制计算机编写的。 让我们回到我的记忆、一些笔记,以及重新发现我已遗忘之物的旅程。 ## 世界上最快的处理器 如果你一直在关注我关于 transputer 的系列文章(https://nanochess.org/pascal.html),我对自己的操作系统感到非常满意,而且我已经触及了该系统的极限。与此同时,我父亲正忙于开发一个基于新型 32 位处理器的新计算机系统。嗯,在 1994 年,它对我们来说只是新东西。AMD Am29000 处理器于 1987 年问世,也被宣布为世界上最快的处理器(你看到这里的梗了吗?),它每个周期可以处理一条指令,所以确实很快。 AMD Am29050 处理器 Am29000 处理器及其后继者 Am29050 被用于多种打印机的 Postscript 语言扩展卡、Quadra Mac 的视频 NuBus 卡、街机游戏如 F-15 Strike Eagle 和 Battle of the Solar System(均由 Microprose 出品),而且我在维基百科上读到,它也在航空电子设备中用于双处理器冗余板。 这种速度基于大量的假设,比如拥有足够快的 RAM 或 ROM,或者使用非常高速的 TTL 芯片。而在 1990 年代初,这三者都仍然非常昂贵。此外,Am29k 系列的一些总线设计决策,例如分离的数据和指令总线,使得它与外设接口非常麻烦。最终,用 Am29k 做任何有用的事情都需要 DRAM 内存,而 DRAM 很慢,于是所有优势都丧失了。 然而,我父亲对静态 RAM 内存充满信心,他采购了数十片 128KB 的静态 RAM 芯片,8 位总线。表面贴装技术开始出现,其中几片是由四个 32K RAM 芯片安装在一个漂亮的微型绿色电路板上构成的。 因此,我父亲制造的第一台 Am29000 计算机以 6 MHz 运行,拥有惊人的 512 KB RAM 容量,以及 128 KB EPROM(使用两片 16 位芯片)。它有 ISA 连接器,显卡是 Cirrus Logic GD5429,还有一块标准的并行/串行/软盘控制器卡(多端口卡),并复用了一台旧 PC 的键盘芯片来连接 AT 键盘。这台计算机被命名为 G11。 这是一项团队工作,他做了一些初始编程来启动处理器,我翻译了我用于初始化 Cirrus Logic VGA 的 Z280 代码,制作了键盘解码器和磁盘例程。一旦这些稳定下来,我就继续编写监控程序,用于列出内存、保存数据,而且由于指令集足够复杂,我还集成了一个微反汇编器和一个微汇编器。这意味着我们可以使用真正的助记符来编码,尽管不能使用标签,而且移动代码完全是一大痛苦。 我们需要回到 Z280 机器来烧写 EPROM,但一切进展顺利。我撞上了黑暗的现实:我需要把*所有东西*从 Z280 机器移植到这台机器上,大约有 200 个程序,包括应用程序、工具和游戏。此外,Z280 的程序基本上已经过时,我无法复制同样的东西。 G11V2 计算机内部。我想要一张 G11V1 的照片,但 1999 年之前没有数码相机,我也不知道那台机器在哪里。所以这是一张 G11V2 计算机的照片,你可以看到四片 512KBx8 内存,而且它已经有 PCI 总线(G11V1 上没有)。 ## 幕间休息 当 G11 计算机完成后,我父亲接手了它,开始研究每个处理器特殊寄存器是如何工作的,或者为什么它会有某些值,并为此编写文档。我有点惊讶,因为我相信处理器已经在数据手册中记录得很清楚了,而且我对未记录的行为不感兴趣,但不管怎样,我最终还是把他的所有笔记打出来并打印了。 我现在可以专注于 transputer 了,因为那台机器可以供我使用,而当 G11 计算机空闲时,我也会偶尔切换过去,以增强软盘操作系统。我开始编写一些游戏、一个能处理我们新的 Canon BJC-600 气泡喷墨打印机的文本编辑器,甚至还有一个 MSX 模拟器。那是我第一次看到 Gradius 运行,我沉迷其中。 我在重新创建我的 Z280 程序,而我感觉自己在倒退,因为我在 transputer 上已经有一个先进的操作系统了。与此同时,Windows 3.1 看起来更有用,它有着刺眼颜色的图标,这是从 CGA 一路继承下来的调色板的产物。 我有兴趣了解如何做一个窗口系统,但我无法弄到一本关于 Windows 3.1 编程的书。当地的图书馆里仍然堆满了大量的 Turbo-C、Turbo-Pascal、Quick-BASIC 和 QBasic 书籍。最后,我得到了一本关于 OS/2 编程的折扣书,并开始阅读。作者在介绍 OS/2 方面做得非常出色,而且很简单:打开一个窗口,在窗口中绘图,关闭窗口。 真正的发现是…… *咚咚咚* 消息管道。每个带窗口的进程都有一个消息管道,当你创建一个窗口时,你应该给它分配一种界面处理器(窗口类),这个处理器读取消息,并根据用户的交互执行操作。 用户的交互对初学者来说很简单:一条表示鼠标点击的消息,一条表示鼠标在窗口上移动的消息,然后事情开始变得复杂,出现了双击消息。你可以在窗口内创建窗口,但这些是控件,如按钮、滑块、图标、指示器、状态栏等。这些控件可以发回消息。 其美妙之处在于,你可以非常快速地开发用户界面,而且每个程序看起来都很棒(嗯,在 90 年代,我们确实不太挑剔,数字研究公司的 GEM 可以作证)。 一个 AMD 寄给我们的盒子。在研究这篇文章时,我发现我还保留着一个盒子,里面是 AMD 寄来的两本数据手册。有人于 1990 年 6 月 7 日把它丢在了邮局。 ## 窗口系统 首先,我需要多任务。我觉得这令人生畏且复杂,最终我学会了如何做协作式多任务。它保存当前状态,加载新状态,然后跳到新位置。抱歉,操作系统设计师们,我没有考虑那么多。我只是想让它能工作。 其次,我需要窗口。我冥思苦想,想弄清楚如何将一个东西叠加在另一个东西之上。然后,当我阅读我父亲买的一张新 CD-ROM 中的一个个文件时,我发现了 X11R5。如果你不知道,X11 是一个网络化窗口系统,设计用于几乎任何图形终端,而且由于大学有很多不同的硬件,它应该能在所有这些硬件上工作。当然,这意味着它在处理颜色或游戏方面极其笨拙。 这里重要的是,CD 上有 X11R5 的完整源代码。当然,我找不到它的用途,因为我的电脑没有网络,也没有 C 编译器,但有一个源代码文件让我深受启发:XRegion.c。 该算法的基本概念是:每个窗口都有一个矩形显示列表。如果一个窗口没有被重叠,那么矩形覆盖整个窗口。如果一个窗口被重叠了,就剪掉被重叠的矩形,并继续剪掉每一个重叠。这意味着,如果一个矩形在中间被剪开,它会创建四个矩形,而在显示信息时,你应该尝试在这四个矩形中绘制。因此,被大量裁剪的窗口绘制得很慢。顺便说一下,苹果有一个技巧来解决这个问题,它使用裁剪位图,但这是一项专利技术。 第三,我根据从 OS/2 书中学到的东西,在笔记本上创建了一个极其简化的 API 规范,这让编码容易了很多。虽然 OS/2(和 Windows 3.1)使用带有画笔类型和填充图案的图形上下文来绘图,但我认为这太麻烦了,而我直接在窗口中按像素单位绘图。 我的窗口系统最初几个函数的规范。我的窗口系统最初几个函数的规范。 调试器(Depurador)的用户界面设计 调试器(Depurador)的用户界面设计 有了这三块拼图,我从 1996 年秋天到 1997 年春天开始用机器码编写我的窗口系统,那时我 18 岁,而且我真的从没想过我的微汇编器会对我不利。它很容易使用,我可以非常快地编写例程,然后我的微反汇编器向我展示的正是我写的东西。我只是需要在纸上记下一些指针,但不幸的是,这并不完全是源代码。 这篇文章之所以可能,是因为我有两张 1997 年早期机器码版本的磁盘。我知道我有一些好东西,即使很原始,因为没有其他窗口操作系统能从软盘启动,但如果没有 Am29000 计算机,别人是不可能体验它的。那么,让我们编写一个模拟器吧! ## 1997 年的磁盘 可启动磁盘的主目录如下所示: 可启动磁盘的目录。可启动磁盘的目录。ESPEJO.IMG 是转储文件,文件夹有恢复日期。 两张容量为 1.44MB 的软盘,里面装着我的窗口操作系统和程序。两张容量为 1.44MB 的软盘,里面装着我的窗口操作系统和程序。 我的第一个任务是编写一个 Am29000 模拟器。我手头某处有用户手册,但我没有花时间去找它,而是花时间在 Google 上搜索,却什么都找不到。我很惊讶,因为几年前它还能找到。不管怎样,我去了 archive.org (http://archive.org/),在输入芯片型号后很轻松地得到了它。 我认为关系-断言指令是 Am29k 系列处理器中最大的硅片空间浪费。我猜想设计者有一个使用场景,因为军方需要 Ada 语言,但这只是我的猜测。 另一个奇怪的想法是比较运算符的结果值:一个布尔值,为 0 和 0x80000000,而条件跳转只检查高位来决定是否跳转,所以你需要右移 31 位才能得到 C 语言的布尔值。 加法和减法指令也带有额外的包袱。在这种情况下,如果结果超过无符号值或有符号值,处理器会生成一个陷阱。这意味着有 ADD, 还有 ADDU,以及 ADDS。如果我们考虑进位标志,它又会增加 3 条指令: ADDC、 ADDCU 和 ADDCS。 减法指令有一组操作数颠倒的版本,所以我们有 SUB、 SUBU、 SUBS、 SUBC、 SUBCU、 SUBCS,但还有 SUBR、 SUBRU、 SUBRS、 SUBRC、 SUBRCU、 SUBRCS。也许我不够聪明,看不出如何使用 SUBRCU。 ## 一切都在哪里? 我曾经以为我能记住一切,但显然我的大脑不同意。我有这两张软盘镜像,但它加载到哪里去了?幸运的是,我放了一条启动消息,写着“Arrancando”,而指向这条消息的代码是这两条指令: `` CONST gr68,0x0180 CONSTH gr68,0xbfff `` 这意味着启动数据应该加载到 0xbfff0000。这并不意味着我有那么多内存(RAM 从 0x80000000 开始),它只是利用了 RAM 解码器的镜像特性,总是将启动代码放在 RAM 的最高 64KB 中。我还可以从指令的字节顺序看出,所有代码都是大端序。Am29000 处理器允许通过配置寄存器中的一个位来选择端序。 在模拟器开发过程中,我开始用磁盘镜像运行它。令我沮丧的是,我发现我制作了一个不错的启动画面,但它需要我手头没有的 ROM 例程(处理方框绘制、图标显示、磁盘读取以及处理器寄存器的初步设置),所以我打了一个补丁跳过这些,加载器一直运行完成,然后跳到了 0x80000000 的空虚中,因为什么都没有加载。 我做了一个幻影 ROM,它使用模拟器中编码的一条特殊指令读取磁盘,并在未处理的服务上返回。 不完整的模拟器表现得非常好,它加载了我窗口系统的初始代码,然后在一个未处理的指令 0xe2 处停了下来。我很困惑,因为在 Am29000 用户手册 (https://archive.org/details/bitsavers_amdAm29000ual_16568459) 中没有显示任何指令,直到我拿到了 Am29050 手册 (https://archive.org/details/bitsavers_amdAm29000nual_30055159)。我真傻!它是 MULTIPLU 指令。 Am29000 手册在陷阱指令的定义上已经过时了。当 AMD 创建了带有浮点支持和硬件乘法指令的 Am29050 时,他们重新定义了陷阱指令集。例如,操作码 0xe4 在 Am29000 用户手册中是 CVINTF,但在 Am29050 手册中是 CONVERT,而且 AMD 提供了一个数学仿真库,允许 Am29000 处理器使用这些新操作码运行 Am29050 软件。我认为编写一个 Am29050 仿真器是过度的(也许以后再说),所以我限制自己只做 Am29000,因为我觉得他们用陷阱和 32 条连续的 MUL 指令(一路移位)来实现乘法指令很有趣。 顺便说一下,我们的 Am29050 处理器是直接从 AMD 的墨西哥代表处获得的样品。我在笔记本上记下了日期,1997 年 4 月 8 日。不幸的是,同一年 AMD 陷入困境,放弃了 AM29k 产品线,转而制造 K5 处理器,就这样与 Intel 的处理器战争开始了。 在对我关于向量表的解释产生了一点分歧之后,我终于让 MULTIPLU 指令触发陷阱以进行仿真,然后我跑去实现 MUL 指令,却陷入了乘法陷阱的无限循环。原来,要从陷阱返回,你必须重建两个 PC 寄存器!!!只更新其中一个会让我代码再次运行自己,而且陷阱还设置了错误的返回地址。 我必须修正这些错误,然后再次运行模拟器时,它直接退出乘法例程,进入了一个溢出陷阱 *小丑脸*。确实有一个溢出,因为它在读取内存,而我不记得机器启动时那些内存里有什么。我放了一些猜测的值,直到它到达 IRET 指令,这意味着它可以从被仿真的指令返回。 它工作了,然后在未实现的 DIV 指令处停了下来。我当然对指令位处理有疑虑,但我按照手册中描述的算法实现了它。 我到达了原始代码中一个点,它开始从内存中读取字节,这需要再实现一个位(用于设置内部字节指针),以及 EXBYTE、 EXHW 和 EXHWS 指令来提取字节、无符号半字和有符号半字。 很快 INBYTE 和 INHW 指令也出现了,并且也被实现了。 ## 这是什么? 出于某种原因,它在系统初始化深处读取了一个端口 0x90,更糟糕的是,它等待位 7 变为开启……我不知道它是什么!我只是在那个端口放了一个 0x80 字节。顺便说一下,读取端口使用特殊的 LOAD control 0x41。内存的正常加载是 0x04 和 0x14(用于字节或半字)

相似文章

在现代 glibc 上剖析 House of Apple 2

Lobsters Hottest

本文剖析了在现代 glibc 版本上的 House of Apple 2 利用技术,提供了交互式 GDB 演练,并通过沙箱环境分析了其可行性。

在浏览器中启动 Apple Copland D11E4

Hacker News Top

本文描述了如何使用 DingusPPC 模拟器在浏览器中启动苹果公司失败的 Copland 操作系统,包括运行说明和必要补丁的链接。