通过现代手段将ZX Spectrum的程序改编为TR-DOS。 第三部分

正如我们在上一部分中发现的那样 ,游戏的机器代码无法直接从软盘下载到目标地址。 我们会将它们上传到另一个位置,下载后,我们会将它们移到必要的位置。 另外,当引导加载程序和加载的数据都在同一基本文件中时,我们想制作一个单块引导加载程序。 这样的加载器只能用机器代码编写。 同时,由于我们有一个整体文件,因此需要将机器代码中的加载器放在BASIC加载器的注释中。


软盘5.25“


总计,结果显示出以下多个步骤:


  1. 从BASIC,我们以机器代码的形式将控制权转移到程序中。
  2. 机器代码中的程序将引导加载程序从BASIC区域转移到不受游戏机器代码影响的另一个区域,并将控制权转移给该区域。
  3. 下载并解压缩引导映像。
  4. 我们将游戏机代码加载到不与系统变量区域重叠的区域中。
  5. 我们将机器代码转移到目标地址。
  6. 我们将控制权转移给程序。

发展必须从中间开始(第3段)。 事实是,为了编写要移动的程序,您需要知道要移动的程序的大小,并且要在基本代码中嵌入机器代码,就需要知道要移动的程序的大小。


整体引导加载程序(机器代码中的一部分)


在TR-DOS中,当简单地从当前位置读取预定大小的数据并将其加载到特定的内存区域时,从整体文件加载数据更像是从磁带加载无头文件。 为此,在TR-DOS中, #3D13的例程。 首先,下载并解压缩图片:


 LD DE, ($5CF4) ;        LD BC, $0805 ;  B  -  (9)*, ;   —   #05 ( ) LD HL, $8000 ;    32768** CALL $3D13 ;   TR-DOS CALL $8000 ;    

* -请参阅上一部分中的启动映像压缩;
** -解包程序可重定位,因此您可以在任何地方下载。


同样,下载游戏机代码:


 LD DE, ($5CF4) ;        LD BC, $2505 ;  B  - , ;   —   #05 ( ) LD HL, $6000 ;    24576 CALL $3D13 ;   TR-DOS 

在这一阶段,我们不再需要TR-DOS;我们可以使用LDIR处理器LDIR将机器代码传输到目标地址:


 LD HL, $6000 ;  (,      ) LD DE, $5B00 ;  LD BC, $2500 ;     (  data.bin) LDIR 

好吧,最后,我们以与原始引导加载程序相同的方式将控制权转移到程序中,即移动堆栈指针:


 LD SP, $5D7C RET 

既然加载器代码已经准备好了,您就需要对其进行编译以了解其大小,我们将需要进一步的编译。


 $ pasmo tmp.asm tmp.bin $ wc -c tmp.bin 44 tmp.bin 

引导加载程序转移过程


引导加载程序占用44个字节。 现在,您需要编写将引导加载程序从BASIC中的注释中移出的过程(本文开头列表的第2点)。 麻烦的是,BASIC区域的地址可能会根据连接到计算机的外围设备而有所不同,因此,要确定要将数据传输到的位置,您需要关注PROG系统变量(如在原始引导加载程序中)或到软件计数器( PC处理器寄存器)。


访问软件计数器并不是那么容易-没有像LD HL, PC这样的处理器指令。 我在Laser Compress中监视了该解决方案,它看起来像这样(不是真正有针对性地使用UNSTACK_Z过程):


 LD DE, $00 ;     ,     , ;    .     ;    1 INC E ;  1  E,    ,    ;      .     1  CALL $1FC6 ;    ( ,  LD HL, PC) ADD HL, DE ;       LD DE, $F800 ;    LD BC, $002C ;  ,   (44 ) LDIR JP $F800 ;    ;      ;        

在调用ROM过程#1FC6 ,下一条指令的地址( ADD HL, DE )将在堆栈上。 在HL中调用该程序的结果将是他被记录。 因此,要确定需要在第一行中写入的数字,您需要重新编译从ADD HL, DE到末尾的代码,并查看将花费多少:


 $ pasmo tmp.asm tmp.bin $ wc -c tmp.bin 12 tmp.bin 

原来是12个字节。 因此,在第一行中,我们写入11( #0B )。


接下来,我们使用加载器(请参阅完成的文件 )编写移动过程,它将进行移动并再次编译。 结果应该是56个字节。


这里应该注意的是,在我写完这篇文章之后,我发现不用计算要移动的程序的长度,而是可以使用标签并让汇编器来确定它的长度。 但是为了历史正义,让我们保持现状。


整体引导加载程序(基础部分)


现在我们已经知道了用机器码表示的引导加载程序的大小,我们可以用BASIC编写引导加载程序并将所有内容收集到一个整体文件中。


机器代码嵌入在基本文件中的注释中或文件末尾。 第二个通常会使文件的研究复杂化,并且更适合于保护,因此我们将使用第一个选项。 comment选项如下:


  1 REM @#$%... 10 RANDOMIZE USR (PEEK 23635+256*PEEK 23636+5) 

23635#5C53 )是我们前面提到的PROG系统变量的地址。 5是注释的第一个字符相对于PROG的偏移量(2个字节是行号,2个字节是行的长度,1个字节是REM运算符)。 如果要在机器代码之前添加其他注释,例如您的姓名,电话号码或邮寄地址,则需要调整值5


如果不使用任何其他实用程序来创建引导加载程序,则需要在REM之后输入任意字符,其数量不得少于我们要放置在注释位置的机器代码中程序的长度(在我们的示例中为56个字节)。 之后,可以通过LOAD "" CODE PEEK 23635+256*PEEK 23636+5加载程序并保存文件。


但是, bas2tap可以使该过程更加容易。 如果每个字节在大括号中表示为十六进制数,则它可以编译基本文件并将二进制数据嵌入其中。 为此,请通过hexdump运行编译的引导程序:


  $ hexdump -ve '1/1 "{%02x}"' loader.bin {11}{0b}{00}{1c}{cd}{c6}{1f}{19}{11}... 

hexdumpREM之后的第一行中将hexdump输出放在注释的位置,然后在-sboot上编译bootloader( -sboot是磁带上文件的名称, -a10-a10的行号):


 $ bas2tap -sboot -a10 boot.bas boot.tap 

通过中间格式0将引导加载程序从tap格式转换为hobeta


 $ tapto0 -f boot.tap $ 0tohob boot.000 

创建一个文件


至此,我们已经具有创建软盘映像所需的所有文件。 您可以创建图像并将所有必需的文件复制到其中:


 createtrd Pac-Man.trd hobeta2trd boot.\$$B Pac-Man.trd hobeta2trd screen.\$$C Pac-Man.trd hobeta2trd data.\$$C Pac-Man.trd 

生成的软盘映像应该已经可以工作。 您可以在仿真器中运行它并进行检查,但这还不是全部。 由于我们引导加载程序不是按名称而是根据驱动器磁头的位置来下载后续文件,因此只有当文件一个接一个地位于磁盘上时,加载才能工作。 这需要解决。


原理如下:TR-DOS存储冗余文件大小信息:


  1. 扇区大小-用于将文件放置在软盘上并进行复制。
  2. 大小(以字节为单位)-用于加载内容。

通常,这些大小彼此对应(每个扇区256字节),但这不是必需的。 我们将利用这一点。 如果将引导文件的大小(以扇区为单位)更改为等于我们要下载的所有文件的总大小的值,但不更改字节数,则TR-DOS会将所有数据复制为一个大文件,但是在引导时仅加载基本文件部分。


在真实Spectrum上或在仿真器中,可以使用Disk Doctor之类的程序(例如Hex Disk Editor)来编辑零磁道:


十六进制磁盘编辑器


但这可以变得更简单:trd映像仅是软盘上所有数据的字节副本,因此可以在任何十六进制编辑器中对其进行编辑:


 $ hexdump -C Pac-Man.trd | head -4 00000000 62 6f 6f 74 20 20 20 20 42 d0 00 d0 00 01 00 01 |boot B.......| 00000010 73 63 72 65 65 6e 20 20 43 40 9c 14 07 08 01 01 |screen C@......| 00000020 64 61 74 61 20 20 20 20 43 00 5b 00 25 25 09 01 |data C.[.%%..| 00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 

如您所见,在软盘的最开始(零磁道)上有一个文件分配表,其中每个文件的信息占用16个字节。 扇区大小存储在偏移量为#0D字节中(右侧第三列)。 我们文件的大小为#01#08#25扇区,总共为#2E 。 我们将此值写入相应的字节,然后删除其余的标头,因为 不再需要它们:


 $ hexdump -C Pac-Man.trd | head -4 00000000 62 6f 6f 74 20 20 20 20 42 d0 00 d0 00 2E 00 01 |boot B.......| 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 

现在我们有了一个成熟的软盘映像。 它必须正确加载并从磁盘完全复制到磁盘。 它仅保留以减小图像的尺寸。 由于trd映像是字节副本,因此它始终需要640KB。 实际上,在大多数情况下,使用scl格式更为方便,这就像hobeta直接存储文件数据一样:


 $ trd2scl Pac-Man.trd Pac-Man.scl 

现在确定。 从头到尾的适应过程可以在github上的项目存储库中找到。


工具:


  1. Pasmo是Z80的交叉装配机。
  2. bas2tap是Spectrum BASIC方言的交叉编译器。
  3. trd2scl -trd图像转换器到scl。

相关链接:


  1. Nikolai Rodionov “对TR-DOS系统进行程序调整”
  2. 信息指南》杂志第1期的“ TR-DOS功能”
  3. 专业人士和业余爱好者 的TR-DOS”一书中的“ TR-DOS 软盘的结构”
  4. 有关系统变量和Spectrum ROM过程的参考

Source: https://habr.com/ru/post/zh-CN458782/


All Articles