GW-基本来源说明

2020-06-25 19:23:12

当我得知微软发布了GW-Basic源代码时,我有点好奇,想知道那里有什么,没有什么。简短的答案是,有很多,但也有很多缺失。拼写注意:“GW-BASIC”和“GW BASIC”都可以在源代码中找到。为保持一致性,此处将使用连字符拼写。

第一个问题是:源代码来自什么时候?微软将源文件标记为1983年2月10日,但这几乎可以保证是错误的。日期来自代码中的注释:“此翻译由版本4.3创建了10-FEB-83”。这反映了通过生成8086代码的翻译器运行某种类型的主BASIC源代码。几乎可以肯定的是,源代码在该日期之后进行了修改。

我目前最好的猜测是,源代码大约是1983年年中的。但这只是一种猜测。

下一个任务是弄清楚如何汇编源代码。Microsoft源代码版本在这方面完全没有提供任何线索。没有Makefile(虽然对它来说可能太旧了),没有批处理文件,没有构建说明,什么都没有。

GW-Basic源代码多次提到Intel的ASM86,但是源代码使用了太多的MASM细节。很可能是某些较旧的版本使用了ASM86,而不是发布的源代码。

带着一系列MASM版本,我尝试汇编源代码。事情进行得并不顺利。什么都不能组装。MASM 5.1似乎走得最远,这很奇怪,因为它实在太新了(1988);而且,MASM 5.1有一个内置的INSTR操作符,它与GW-BASIC源代码中的INSTR符号冲突。

事实证明,MASM 5.1只是对UNIX行结尾的容忍度更高。旧的MASM版本需要DOS样式(CR/LF)行结尾,否则会非常恼火,会产生令人困惑的错误。

在修改了源文件以使它们更容易被MASM接受之后,事情变得更有趣了。长话短说,几乎所有的文件都可以用Microsoft MASM 1.00或1.10以及IBM MASM 1.0汇编。非常旧的MASM版本存在一些已知问题,可以通过将常规内存大小减少到512KB来避免。

大多数文件不能用Microsoft MASM 1.12或更高版本或IBM MASM 2.0汇编。问题通常是在较新的MASM版本中有更好的诊断,这些版本拒绝GW-Basic源代码中有问题的构造。

例外的是GWMAIN模块。MASM 1.x版本无法汇编它,因为它们耗尽了内存。该模块可以与IBM MASM 2.0或Microsoft MASM 3.0成功组装。再多的恳求也无法说服MASM 1.x发挥作用。

这引发了一些疑问。对于GW-BASIC源代码来说,IBM MASM 2.0/MS MASM 3.0(1984)实在太新了。微软可能使用了MASM的开发版本;众所周知(参见第337页),微软以目标代码的形式将大量GW-BASIC提供给OEM,而OEM需要提供GW-BASIC与其平台接口所需的胶水代码。因此,有可能不能使用普遍可用的现成工具来实际汇编代码。

也有可能微软确实使用了MASM 1.0或1.1,但没有托管在DOS上。无论如何,IBM MASM 1.0和IBM MASM 2.0都可以用来汇编源代码,Microsoft MASM 1.10和MASM 3.0也可以用来汇编源代码。

还有一个与GW-Basic数学包有关的容易解开的谜团。有MATH1.ASM和MATH2.ASM两个源文件,都不能汇编。但是如果它们被合并在一起,例如通过从主源文件包括两者,汇编就成功了。数学模块可能已经被拆分,因为源代码几乎是180KB,160KB的软盘肯定放不下。

更新:在写完上述内容后不久,我就赚到了钱。表面上看,1982年的MASM1.06可以干净地汇编所有的GW-Basic源文件,没有语法错误,也没有内存不足。可以在此处(如MACRO86.EXE)和此处找到副本;这两个可执行文件具有不同的日期戳,但实际上是完全相同的。为什么旧的和新的MASM版本在GWMAIN.ASM上都会耗尽内存,目前仍然是个谜,但我们现在知道,至少有一个MASM版本可以在一台PC上组装所有东西。

下一个任务是查找与发布的源代码接近的GW-Basic二进制文件。很快就发现,大多数GW-Basic二进制文件要么更旧,要么更新。正确的显示。

但是可以显示各种版本号。他们可能会也可能不会提到GW-BASIC。最后,我将重点放在了两个二进制文件上。其中一个是GWBASIC.EXE,日期为1983年11月11日,文件大小为56,832字节,显示如下:

另一个是BASICA.EXE,日期为1983年5月13日,文件大小为54,272字节。登录消息是:

对于已发布的源代码来说,这两个都是非常好的,但不是完美的匹配。我几乎可以肯定,Compaq版本比源代码稍微旧一些(因为少了几个比特),而Eagle版本稍微新一些(因为有几个额外的比特)。这意味着发布的源代码比1983年11月更早,但可能比83年5月更新。

我专注于Eagle计算机GWBASIC.EXE,因为它似乎与源代码稍微匹配。我能够将所有源代码与二进制代码进行匹配,并得出以下源代码模块序列(请注意,BI代表BASIC解释器):

ASM是一个假想的OEM提供的模块,它不是GW-Basic源代码发行版的一部分。它不是一段微不足道的代码,在Eagle GWBASIC.EXE中占据了超过6,000字节的目标代码(占总代码的10%以上)。

其他GW-Basic实现很可能会对模块进行不同的排序,尽管某些模块在开头和结尾的顺序可能是固定的(例如,GWDATA.ASM需要排在第一位)。

。其他内容和快速CODE.MONTE Davidoff编写的数学包(F4I.MAC)。

没有迹象表明,当该产品成为GW-Basic时,比尔·盖茨或保罗·艾伦已经参与其中。

源代码是按照当时常见的方式全部大写(尽管不是完全)编写的。

最令人不快的事情之一是,因为在过去糟糕的日子里也很常见,标识符被限制在六个字符以内。这会导致FRMQNT、SKPMRF、LEVFRE或XCESDS等难看、拥挤且难以破译的标识符。6个字符的限制也适用于文件名。

代码通常相当无结构,很难理解。根本不使用proc关键字。使用了程序,但相当松散。代码非常频繁地跳到另一个例程的中间,或者通过使用JMP而不是RET从例程返回。因此,只有很少的尝试将值保存在寄存器中,并且几乎所有数据都保存在内存中。跳跃的编程风格也使得不可能在堆栈上使用局部变量。毫无疑问,代码是这样编写的,因为它最初是针对Intel8080的。

该代码包含英特尔推荐的“不要做什么”的精美集合。公平地说,这些建议并不真正适用于8086。违反样式的行为包括混合代码和数据以及跳到指令中间。

例如,调用SYNCHR例程后紧跟一个字节的数据(摘自FIVEO.ASM):

调用SYNCHR数据库偏移量540;必须是逗号CMP AL,低54O;许可行号(例如使用ALL)。

字节不是代码,它是数据。SYNCHR将返回地址从堆栈中弹出,处理数据并递增地址,然后将其推回。

公共SNERRSNERR:MOV DL,低偏移量ERRSN;";语法错误";DB 2710;SKIP;";LXI B,";在下一个2PUBLIC DV0ERRDV0ERR:MOV DL,低偏移量ERRDV0;除以零DB 2710;跳过;";LXI B,";在下一个2PUBLIC NFERRNFERR:MOBLIC NFERRNFERR。

请注意,LXI是一条8080指令,清楚地揭示了想法的出处。当调用方跳转到其中一个标签时,它将执行MOV DL,后跟一系列MOV CX指令。忽略Cx值,仅使用DL的内容。

这两种技术都会使反汇编变得有些困难和混乱,尽管在配备了源代码的情况下只会有很小的困难和混乱。

找到与发布的源代码完全匹配的现有GW-BASIC可执行文件会很方便。到目前为止,我还没有成功,事实上,绝大多数Microsoft Basic解释器要么是较旧的(Basic 5.x)版本,要么是较新的(GW-Basic 3.x)版本。

还应该可以对缺失的OEM源模块(或多个模块)进行反向工程/反汇编/重建,以生成完整的GW-BASIC可执行文件。这可能是相当大的工作量。