本文转载自EE Archeology(http://7400.me/2022/09/10/informer213_2/),经作者授权转载至CSDN平台
简单记录了通过分析硬件和逆向固件,把IBM版本Informer 213改造为VT100版本Informer 213AE的过程。
在之前的文章中,我们记录了Informer 213终端的拆解维修过程。但由于这是IBM协议版本的机器,放在当下,可玩性很差。虽然我们可以在电脑上运行诸如Hercules的IBM小型机模拟器,并使用这台终端作为输入输出设备,但如今大多数流行的操作系统,如Unix和Linux,则都是采用源自DEC的VT100/VT220协议。所以,改造这台机器变得很有必要。
幸运的是,Informer在213之后,推出了213AE,可支持VT52/VT100协议。通过网上的拆解图了解到,213与213AE采用了非常相似的硬件结构,均基于摩托罗拉MC6809设计。其显示控制芯片也没有外观上的差别。
经搜索,vcfed论坛上已有这台机器相关的讨论,也有网友dump了213AE的几颗ROM镜像。以下是链接:VCFED - Informer Portable Workstation 213 374 208B
我们能想到的最直接的改造方法,自然是直接把网友dump的固件直接烧进ROM芯片,替换进手头上这台机器,并且祈祷它能正常工作。然而实践表明,直接替换固件后,机器确实启动并且显示出了213AE的界面,可以进入设置菜单,但并不能正常操作。无论是在设置菜单里更改设置后保存,或是直接在终端界面里按键盘输入,机器都会立刻卡死。看来事情并没有那么简单。
此次逆向,使用了如下工具:
MAME [link]
MAME是一个开源模拟器框架,相信玩游戏机模拟器的朋友都不会陌生。它是目前世界上支持最多机型的模拟器,堪称软件版的计算机博物馆。它的底层实现方式,也力求与原始机器的硬件结构一致,而不仅仅是做到功能仿真。举例而言,在MAME里实现的FC模拟器,需要以功能块的形式实现FC游戏机里的CPU/PPU/RAM/ROM以及周边的逻辑芯片,然后通过MAME的框架将这些由代码实现的芯片交连在一起,就像焊在一块电路板上一样。最后再通过虚拟的显示器/输入输出设备实现机器的完整功能。MAME在对硬件的还原度和仿真效率之间选择了前者,正因如此,MAME也是最接近实际硬件的仿真环境,甚至能实现很多70年代由大量逻辑芯片组成的街机。
在检索与Informer 213相关的资料时,我很幸运地发现,已经有大神在MAME中做了这台机器的实现,虽然目前功能还不完善,但已经可以运行原机dump出的固件。在摩改固件的过程中,我使用了MAME进行调试,不仅可以支持单步运行,察看内存信息,还能避免使用实际机器时重复烧录EPROM/拆装机器的麻烦。
IDA Pro 7.5 [link]
专业的反汇编软件,支持相当多的CPU,包括此次Informer 213里的MC6809。因为功能很强,学习难度较大。这次只是学了点皮毛。
Beyond Compare 4 [link]
最好用的文件/文件夹比较软件之一,可以支持文本/二进制比较以及搜索。在摩改固件时,使用Beyond Compare 查找并替换所有需要改变的地址,比在IDA里方便。
上图是这台终端主板各个区域的功能划分,包含CPU,32KB固件EPROM,64KB运行DRAM,8KB由电池供电的SRAM(用于断电后保存配置),一颗用于UART通讯的Zilog SCC(型号Z8530),以及显示子系统等。除此之外,还有一些74LS芯片组成的胶合逻辑电路(glue logic),用于实现地址译码及总线驱动。其中,地址译码器为U15 74LS138。U15右边的74LS377是一颗8位D触发器,连接在总线上用于实现8位通用输出(GPO),驱动蜂鸣器、接口板上的继电器等外设。
上图是网友拍摄的Informer 213AE的主板照片,可以看到,主板上主要器件都是相似的,只是排布有所不同。另外有两颗带插座的DIP20芯片贴着标签,估计是PLD,用于替代之前的74LS138,以实现地址译码。
综合更换ROM后的现象,即机器虽然可以启动,正常响应键盘事件,但无法正常收发串口字符/保存设置会卡死/重启后设置回归默认,我认为213AE的固件在这台设备上可能没有正常操作NVRAM以及SCC。通过测量PCB走线,我确认了SCC与NVRAM的片选信号均是由U15 74LS138输出的。这部分外设的电路大致如下。
通过U15地址译码电路,我们可以知道如下信息:
机器的数据总线被U8缓冲器隔离为内部总线和外设总线,内部总线上挂着DRAM,NVRAM,ROM和显示控制器(图中未画出)。外设总线挂着输入输出接口,UART等
机器的UART芯片(SCC Z8530)地址为0x0080-0x008f,其中SCC的A/B和D/C信号线(用于选择需要操作的串口通道以及操作的是数据还是控制寄存器)分别接在地址总线的A0和A1上
U19是一个D触发器,作为GPO(通用输出接口)。机器的蜂鸣器/NVRAM最高地址选择信号和一些其他的控制信号经此锁存输出,地址为0x0090-0x009f
机器的NVRAM虽然有8KB,但最高地址线始终置0,实际只使用了4KB,且A11地址线由GPO控制,似乎是分为了两个区块
参考MAME中informer 213AE的模拟代码,我发现,213AE的外设地址与手上这台213有所不同。其中SCC的地址范围为0x0040-0x0043(0x004f),蜂鸣器地址为0x0060(-0x006f)。这就解释了为什么213AE的固件直接放在我的机器上无法运行。
幸运的是,213AE的DRAM/ROM和显示控制芯片的地址映射没有发生变化。因此,为了试图让213AE的固件能在本机运行,我们有两条选择,第一是修改U15 74LS138译码器的地址输入;第二是修改固件,把所有访问SCC和GPO寄存器的地址改为本机对应的地址。为了不破坏机器硬件,第二种选择明显是更优解。
这时候就轮到IDA出场了。在IDA中导入网上找到的213AE ROM镜像,选择CPU为MC6809,设置好地址映射,即可开始分析工作。MC6809共有16根地址线,寻址空间为64KB,即0x0000-0xffff。根据Informer213的硬件结构,前半部分(0x0000-0x7fff)为RAM和外设地址空间,后半部分(0x8000-0xffff)为固件区。ROM镜像需加载在后半部分,前半部分都可以在IDA中定义为RAM空间。这样IDA即可正确识别固件中对不同内存地址/外设地址的访问,并加上标签。
MC6809的复位地址向量存在ROM的最末端0xfffe和0xffff,CPU复位后会先读取这两个地址的数据,然后跳转到程序入口。我们到ROM最后两行,找到复位地址为0xff00,然后在IDA中从ff00开始反汇编,即可开始反汇编工作。
程序入口部分的代码截图:
由于已经过去一年,很多细节记不清楚,这边就不详细写固件分析过程了。总之,在看着像代码的区域按”C”,IDA会尝试将此地址和前后的16进制数据反汇编,并且找出其中的跳转,把所有引用都顺带反汇编,并给所有地址访问贴上标签,还是很方便的。结合MC6809的手册,我大致摸了一下整个固件的工作原理,包括键盘响应、显示刷新等流程。自然,我们也能找到213AE固件中对SCC和GPO地址访问的代码。可以看到,对SCC的访问地址在0x0040-0x0043,对GPO的访问地址在0x0060,与MAME中设置的地址一致。我们只需要找出所有对这些地址的访问指令,并且把0x0040-0x0043/0x0060分别替换为0x0080-0x0083/0x0090,理论上就能让这个固件在我的Informer 213上工作。举例而言,下图是找到的所有对SCC 地址0x0040的访问,以及其中的具体一段代码(貌似与SCC初始化相关)。我把所有指向0x0040的指令全部替换成0x0080。
我用了Beyond Compare中的二进制搜索功能,去搜索ROM中所有我感兴趣的地址,如0040 0041 0042 0043 0060等,然后根据前后的16进制判断这些是无关的数据还是数据转移指令,并对是指令的进行修改。例如,16进制的 F6 00 40 就是LDB 0x0040指令,表示把地址0x0040 (对应SCC通道B的控制寄存器)中的数据载入寄存器B。我们将其改为 B7 00 80,就可以用在我这套213上。实际上,因为机器的ROM一共只有32KB,并不是很大,查找的命中数量并不多,很快我就完成了所有地址的替换。
把修改过的固件烧入EPROM,装回机器。这次,开机的时候能听到机器里有继电器动作的声音,蜂鸣器也能工作,说明部分修改生效了。然而,串口收发依然不起作用。系统还是会在输入字符后卡死。此时,轮到使用MAME了。
我把MAME中informer 213AE的仿真程序的SCC和GPO对应地址映射改为213的映射,并且加载修改过的固件,即可通过模拟器仿真该固件在实际硬件上的运行情况。同时我在SCC的仿真代码中加入了输出log的功能,当固件访问SCC时就会打出debug信息。通过比较修改前后固件在模拟器中对SCC访问的差别,我发现修改后的固件虽然大部分初始化操作都与原固件相同,但还是少了不少初始化内容,如下图。左侧缩略图中红色部分均为缺失的初始化操作。
通过在MAME中设置断点并且结合MAME自带的反汇编功能,我最终找到了缺失的初始化操作代码所在位置。
分析这段代码,它并没有采用直接寻址方式操作SCC,而是采用间接寻址,先将SCC地址用LDY指令存入索引寄存器Y中,将包含初始化数据数组的起始地址采用LDX存入索引寄存器X中,然后通过循环依次读取数组中的初始化数据,并写入Y寄存器对应的SCC寄存器地址中。(循环程序在0xa244的子程序中,这边截图没有体现)。
之前修改地址,我只关注了对这些外设的直接寻址操作,忽略了间接寻址相关的指令。加上对6809汇编不熟悉,遗漏了这边的代码。我把LDY指令存入索引寄存器的地址改为新的地址,并且再次确认程序其他地方没有间接寻址相关操作。
重新生成固件,并用MAME验证。验证结果表明SCC初始化已不再有遗漏。将固件烧入EPROM,装机测试,机器可以正常运行。测试大部分功能均无问题,至此逆向和修改过程基本结束。
不要忘了,这台机器里除了固件ROM之外,还有一个字体ROM。213和213AE的字体ROM内容也有差异,并且213AE的字库大小为16KB,是213的两倍。我修改完固件后并没有第一时间替换字体ROM,但机器运行似乎没有看出问题,说明两个型号的字库基本是相同的。那么差别在哪里呢?
将本机中dump的字体ROM和网友贡献的213AE字体ROM解码并作比较,差别就很明显了。两个机器采用的都是5*8字体,按照16bytes/字符存储。两者的基础字符集差异不大,但扩展字符集的差别就很多了。这些扩展字符可能是用于显示控制字/菜单的,同时在213AE中加入了多语言选项,可显示英语/德语/挪威语/芬兰语等。为保险起见,我还是将字库ROM也一并作了替换。
这是我第一次尝试古代设备的固件逆向,可能过程看上去比较简单,但还是想在这里写出来,给其他人和日后的自己作个参考。事后我还用Python写了个《传送门》片尾曲的ASCII ART,并用此终端进行了演示,效果的确是对味了。演示视频可见我的Bilibili号:用80年代EL显示屏终端机播放《传送门》主题曲Still Alive
这台终端给我带来了很多快乐,也让我学习不少。整个过程中参考的资料/IDA工程/字库提取代码我都打包在下面的链接中,感兴趣可以自行下载:informer_213_pack.zip
如果你手头有一台Informer 213需要更改固件,以下是修改过的固件包:informer213_modified_AE_ROM.zip
用Python编写的 《Still Alive》 片尾曲代码,也可以运行在现代系统中的命令行里:Portal_StillAlive_Python