Adaptação de programas do ZX Spectrum ao TR-DOS por meios modernos. Parte 3

Como descobrimos na parte anterior , os códigos de máquina do jogo não podem ser baixados diretamente do disquete para o endereço de destino. Nós os carregaremos para outro local e, após o download, os moveremos onde necessário. Além disso, queremos criar um carregador de inicialização de um bloco quando o carregador de inicialização e os dados carregados estiverem no mesmo arquivo básico. Esse carregador só pode ser escrito em códigos de máquina. Ao mesmo tempo, como temos um arquivo monobloco, o carregador em códigos de máquina precisará ser colocado nos comentários do carregador no BASIC.


Disquete 5,25 "


No total, ocorre a seguinte etapa múltipla:


  1. Do BASIC, transferimos o controle para o programa em códigos de máquina.
  2. O programa em códigos de máquina transfere o gerenciador de inicialização da área BASIC para outra área que não é afetada pelos códigos de máquina do jogo e transfere o controle para ele.
  3. Baixe e descompacte a imagem de inicialização.
  4. Carregamos os códigos das máquinas de jogo em uma área que não se sobrepõe à área das variáveis ​​do sistema.
  5. Transferimos códigos de máquina para o endereço de destino.
  6. Transferimos o controle para o programa.

O desenvolvimento terá que começar no meio (parágrafo 3). O fato é que, para escrever um programa para mover, você precisa saber o tamanho do programa a ser movido e, para incorporar códigos de máquina de maneira básica, precisa saber o tamanho do programa para mover.


Carregador de inicialização monobloco (parte em códigos de máquina)


No TR-DOS, carregar dados de um arquivo monobloco é mais como carregar um arquivo sem cabeçalho de uma fita, quando dados de tamanho predeterminado são simplesmente lidos da posição atual e carregados em uma área específica da memória. Por isso, no TR-DOS, a rotina em #3D13 . Primeiro, baixe e descompacte a imagem:


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

& ast; - veja a compactação da imagem de inicialização na parte anterior;
& ast; & ast; - O desembalador é realocável, para que você possa fazer o download em qualquer lugar.


Da mesma forma, faça o download dos códigos das máquinas de jogos:


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

Nesse estágio, não precisamos mais do TR-DOS; podemos transferir códigos de máquina para o endereço de destino usando a LDIR processador LDIR :


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

Bem, no final, transferimos o controle para o programa da mesma maneira que no gerenciador de inicialização original - movendo o ponteiro da pilha:


 LD SP, $5D7C RET 

Agora que o código do carregador está pronto, você precisa compilá-lo para saber seu tamanho, o que precisaremos ainda mais.


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

Procedimento de transferência do carregador de inicialização


O carregador de inicialização ocupa 44 bytes. Agora você precisa escrever o procedimento para mover o carregador de inicialização dos comentários no BASIC (ponto 2 da lista no início do artigo). O problema é que o endereço em que a área BASIC está localizada pode variar dependendo dos periféricos conectados ao computador; portanto, para determinar para onde deseja transferir dados, você precisa se concentrar na variável de sistema PROG (como no carregador de inicialização original) ou ao contador do software (registro do processador do PC ).


Não é tão fácil acessar o contador do software - não existem instruções do processador como LD HL, PC . Eu espiei a solução no Laser Compress e parece com isso (uso não direcionado do procedimento 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 ;    ;      ;        

No momento da chamada do procedimento de ROM #1FC6 , o endereço da próxima instrução ( ADD HL, DE ) estará na pilha. É ele quem será gravado como resultado da chamada do procedimento em HL . Assim, para determinar o número que precisa ser escrito na primeira linha, você precisa compilar uma peça de ADD HL, DE até o final novamente e ver quanto será necessário:


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

Descobriu-se 12 bytes. Assim, na primeira linha, escrevemos 11 ( #0B ).


Em seguida, compomos o procedimento de movimentação com o carregador (consulte o arquivo finalizado ), que será movido e compilado novamente. Deve aparecer 56 bytes.


Deve-se notar aqui que, depois que escrevi esta peça, descobri que, em vez de calcular o tamanho do programa a ser movido, você poderia usar etiquetas e deixar o montador descobrir. Mas para a justiça histórica, vamos deixar como está.


Carregador de inicialização monobloco (parte base)


Agora que sabemos o tamanho do carregador de inicialização nos códigos de máquina, podemos escrever o carregador de inicialização no BASIC e coletar tudo em um arquivo monobloco.


Os códigos de máquina são incorporados em um arquivo básico em um comentário ou no final de um arquivo. O segundo geralmente complica o estudo do arquivo e é mais adequado para proteção; portanto, usaremos a primeira opção. A opção de comentário é a seguinte:


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

23635 ( #5C53 ) é o endereço da variável de sistema PROG que mencionamos anteriormente. 5 é o deslocamento do primeiro caractere do comentário em relação a PROG (2 bytes são o número da linha, 2 bytes são o comprimento da linha e 1 byte é o operador REM ). Se você quiser adicionar outros comentários antes dos códigos da máquina, por exemplo, seu nome, número de telefone ou endereço, o valor 5 precisará ser ajustado.


Se não utilizássemos utilitários adicionais para criar o carregador de inicialização, precisaríamos inserir caracteres arbitrários após o REM em uma quantidade não inferior ao tamanho do programa nos códigos de máquina que queremos colocar no lugar do comentário (no nosso caso, 56 bytes). Depois disso, pode-se carregar o programa através do LOAD "" CODE PEEK 23635+256*PEEK 23636+5 e salvar o arquivo.


No entanto, o bas2tap pode facilitar o processo. ele pode compilar um arquivo básico e incorporar dados binários nele se cada byte for representado como um número hexadecimal entre colchetes. Para fazer isso, execute o gerenciador de inicialização compilado via hexdump :


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

Colocamos a saída hexdump no lugar do comentário na primeira linha após o REM e compilamos o carregador de inicialização no -sboot ( -sboot é o nome do arquivo na fita, -a10 é o número da linha do -a10 ):


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

Converta o carregador de inicialização do formato de tap para hobeta através do formato intermediário 0 :


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

Criando um arquivo de peça única


Neste ponto, já temos todos os arquivos necessários para criar uma imagem de disquete. Você pode criar uma imagem e copiar todos os arquivos necessários para ela:


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

A imagem resultante do disquete já deve funcionar. Você pode executá-lo no emulador e verificar, mas isso não é tudo. Como o carregador de inicialização não faz o download dos arquivos subsequentes, não por nome, mas com base na posição da cabeça da unidade, o carregamento funcionará apenas se os arquivos estiverem localizados no disco um após o outro. Isso precisa ser corrigido.


O princípio é o seguinte: O TR-DOS armazena informações redundantes de tamanho de arquivo:


  1. Tamanho em setores - usado para colocar arquivos em um disquete e copiar.
  2. Tamanho em bytes - usado para carregar o conteúdo.

Geralmente esses tamanhos correspondem um ao outro (256 bytes por setor), mas isso não é necessário. Vamos tirar proveito disso. Se você alterar o tamanho do arquivo de inicialização nos setores para um valor igual ao tamanho total de todos os arquivos que queremos baixar, mas não alterar o tamanho em bytes, o TR-DOS copiará todos os dados como um arquivo grande, mas apenas o básico será carregado na inicialização. parte.


Em um Spectrum real ou em um emulador, a faixa zero pode ser editada com programas como o Disk Doctor, por exemplo, Hex Disk Editor :


Editor de disco hexadecimal


Mas isso pode ser simplificado: uma imagem trd nada mais é do que uma cópia em bytes de todos os dados em um disquete, para que possa ser editada em qualquer editor hexadecimal:


 $ 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 |................| 

Como você pode ver, no início do disquete (na faixa zero) existe uma tabela de alocação de arquivos na qual as informações sobre cada arquivo ocupam 16 bytes. O tamanho em setores é armazenado em byte com deslocamento #0D (terceira coluna à direita). O tamanho dos nossos arquivos é #01 , #08 e #25 setores, que no total é #2E . Escrevemos esse valor no byte correspondente e excluímos os cabeçalhos restantes, porque eles não são mais necessários:


 $ 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 |................| 

Agora, temos uma imagem completa de disquete. Ele deve carregar corretamente e ser completamente copiado de disco para disco. Resta apenas reduzir o tamanho da imagem. Como uma imagem trd é uma cópia de byte, sempre são necessários 640 KB. Na prática, na maioria dos casos, é mais conveniente usar o formato scl, que é mais parecido com o hobeta armazena diretamente os dados do arquivo:


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

Agora com certeza. O processo de adaptação do início ao fim pode ser encontrado no repositório do projeto no github.


Ferramentas:


  1. Pasmo é um montador cruzado para o Z80.
  2. bas2tap é um compilador cruzado do dialeto Spectrum BASIC.
  3. trd2scl - conversor de imagem trd para scl.

Links relacionados:


  1. “Adaptação de programas ao sistema TR-DOS” por Nikolai Rodionov.
  2. “Funções do TR-DOS” da Info Guide Magazine No. 1.
  3. "A estrutura do disquete do TR-DOS" do livro "TR-DOS para profissionais e amadores" .
  4. Uma referência às variáveis ​​do sistema e aos procedimentos do Spectrum ROM .

Source: https://habr.com/ru/post/pt458782/


All Articles