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

Ao contrário dos computadores modernos, nos espectros o conceito de sistema de arquivos não era tal. Isso significa que o download de cada tipo de mídia exigia uma implementação separada e, na maioria dos casos, o programa não podia ser apenas copiado da fita para o disco. Nos casos em que o carregador de programa foi escrito em BASIC, ele pode ser adaptado ao TR-DOS com uma revisão bastante simples. No entanto, a situação foi complicada pelo fato de que em muitos jogos (de marca e invadidos), os carregadores foram escritos em códigos de máquina e, às vezes, continham proteção contra cópia.


Disquete de 5.25 "


Apesar da presença de um "botão mágico" que simplesmente fazia um despejo completo da memória do computador e possibilitava salvar o programa em um disquete, foi considerado por especialistas criar versões de disco dos jogos, preservando a imagem de inicialização original e outros atributos.


Neste artigo, mostrarei como realizar essa adaptação no exemplo do jogo Pac-Man , a saber, a imagem original do Pac-Man.tzx .


As ferramentas


Apesar do fato de que antigamente todo esse trabalho era feito diretamente no ZX Spectrum (na ausência de outras opções), adaptarei o jogo usando os utilitários de emulador e linha de comando. A principal razão é que, especialmente no início, o processo de adaptação consiste em um grande número de tentativas e erros e é muito menos doloroso se for automatizado. Tudo o mesmo pode ser feito diretamente no Spectrum.


Na primeira parte, usaremos as seguintes ferramentas:


  1. Emulador de fusível para depuração e teste.
  2. SkoolKit para desmontar.

Desativando a Inicialização no Carregador de Inicialização


Como o arquivo de imagem e dados é baixado sem um bloco de cabeçalho (17 bytes com o nome e o tipo do arquivo), isso significa que o carregador é gravado em códigos de máquina. Você precisa descobrir onde esses códigos estão localizados e de qual endereço eles foram lançados.


Existem várias maneiras de analisar o código do carregador de inicialização:


  1. A maneira mais fácil é iniciar o download do programa, aguardar o início do gerenciador de inicialização e pará-lo pressionando a tecla Space . Em muitos casos, isso funciona, mas no caso de Pacman, como em muitos outros, isso leva a uma redefinição.


  2. A próxima maneira é carregar o programa usando MERGE "" vez de LOAD "" . Ao contrário de LOAD , MERGE ignora a execução automática do programa. No caso do Pac-Man, a inicialização através do MERGE faz com que o computador congele com um deslocamento característico da tela esquerda. Isso ocorre porque, em vez de executar o programa linha por linha, o MERGE tenta analisá-lo por inteiro e mesclá-lo com o programa já carregado. No entanto, se o programa tiver um bloco com códigos de máquina que viole a sintaxe do programa, isso causará um travamento.


  3. Se você não deseja montar seu cérebro, pode converter a imagem da fita de TZX em TAP e usar o utilitário de lista que acompanha o Fuse:


     $ tzx2tap Pac-Man.tzx $ listbasic Pac-Man.tap 1 RANDOMIZE USR (PEEK 23635+256*PEEK 23636+91) 

    O endereço 23635 ( $5C53 ) corresponde à variável de sistema PROG , que contém o endereço inicial da área BASIC. Portanto, o ponto de entrada para o carregador de inicialização é compensado por 91 bytes em relação à área BASIC.


  4. Outra maneira de examinar o gerenciador de inicialização é descrita no artigo Desativando uma autoexecução de um programa BASIC . No depurador do Fuse, você precisa definir um ponto de interrupção br 2053 , carregar o programa e, quando o download terminar e a execução do código parar, execute o set 23619 128 . Isso impedirá a inicialização do programa e permitirá que você vá para o básico.



Desmontagem do carregador de inicialização


Conhecendo a mudança do ponto de entrada em relação à área BASIC, você pode calcular seu endereço absoluto. No caso do ZX Spectrum 48K sem um TR-DOS carregado, a área BASIC começa em 23755 ( $5CCB ). Consequentemente, o carregador de inicialização começará em 23755 + 91 = 23846 ( $5D26 ).


Para começar, basta colocar um ponto de interrupção no endereço de partida e ver os códigos da máquina. No Fuse, você pode criar br 23846 e começar a baixar o programa. Assim que o carregador de inicialização começar a executar, o emulador irá parar:


Depurador


No caso em que o carregador é muito simples, basta olhar para o código desmontado no painel do meio e entender o que está sendo carregado. Normalmente, o código de download para um arquivo sem cabeçalho é mais ou menos assim:


 LD IX, $8000 ;    LD DE, $4000 ;    LD A, $FF ;    CALL $0556 ;  LD-BYTES JP $8000 ;    

Em um caso mais complexo com a execução de código, você precisa entender as etapas e fazer anotações. O pacote de utilitários do SkoolKit é adequado para isso. Se você definir uma meta, com sua ajuda, o jogo poderá ser analisado até o último parafuso (mensagem, sprite, som). Como isso é feito é descrito em detalhes na documentação .


Em resumo, faça o seguinte:


  1. Faça um instantâneo da memória do computador Pac-Man.z80 usando os recursos tap2sna.py ou emulador.
  2. Crie um arquivo de controle Pac-Man.ctl com um conjunto inicial de instruções para desmontar:
     i 16384 Ignore for now c $5D26 Loader 
  3. Execute a desmontagem: sna2skool.py -H -c Pac-Man.ctl Pac-Man.z80 > Pac-Man.skool .
  4. Ao estudar o código, adicione novas instruções e comentários ao arquivo de controle.
  5. Repita até ficar completamente iluminado.

Como resultado, após a primeira passagem, obtemos o seguinte (meus comentários, endereços são omitidos):


 ORG $5D26 ;   23846,   ;   DI IM 1 ;   LD D, IYh ; LD E, IYl ; LD B, $25 ;    EX DE, HL ; LD DE, $0019 ; ADD HL, DE ;    HL  $5C53 (  PROG) LD E, (HL) ;   PROG  DE  IX INC HL ; LD D, (HL) ; LD IXh, D ; LD IXl, E ; LD A, (IX+$7F) ;      (  $7F-  ;  PROG) LD HL, $0035 ;    ($35   PROG) ADD HL, DE ; PUSH HL ;      XOR (HL) ;    LD (HL), A ; INC HL ; DJNZ $5D43 ;   AND (HL) ; RET NZ ;           ;    DEFB $77 

Descriptografia do carregador de inicialização


Tudo o que é realmente importante é que o gerenciador de inicialização descriptografado esteja localizado em PROG + $35 . Isso significa que, se colocarmos um ponto de interrupção na br 23808 , nesse momento a descriptografia será concluída, veremos o gerenciador de inicialização descriptografado:


Carregador


Este programa já é muito mais parecido com o caso típico mencionado acima. O valor $4000 ( 16384 ) é carregado nos registradores IX e DE , outra coisa é feita e o controle é transferido para a rotina ROM em $055A (isto é, alguns bytes abaixo do ponto de entrada padrão no LD-BYTES ). Parece que essa abordagem implementa algum tipo de proteção contra cópia, porque o procedimento padrão não carrega esse arquivo e alguns copistas não o entendem.


Ponto de entrada do programa


Resta descobrir como o programa é chamado após o carregamento. Em vez do usual CALL LD-BYTES e JP , LD SP, XXXX e JP LD-BYTES são usados ​​aqui. A primeira opção (usual) funciona da seguinte maneira:


  1. CALL envia o valor atual do contador de software ( PC ) para a pilha.
  2. O controle é passado para a rotina chamada.
  3. Ao retornar de uma sub-rotina ( RET ), o valor é removido da pilha e ocorre uma transição para o programa de chamada.

Por que isso é feito de maneira diferente aqui? O fato é que o Pac-Man é compatível com o ZX Spectrum 16K e ocupa absolutamente toda a RAM (veja o tamanho do arquivo acima). Assim, ao carregar, o programa substitui a si próprio, tanto o carregador quanto a pilha, onde quer que estejam. Se quiséssemos mudar da ROM para o gerenciador de inicialização usando a pilha e depois chamar o programa baixado via JP , no momento em que o download foi concluído, não haveria endereço de memória no qual o JP estava localizado, nem a própria instrução.


Em vez disso, o ponteiro da pilha move-se para a área de memória onde, após o carregamento, o endereço do ponto de entrada do programa aparece e o processador, sem perceber a substituição, remove-o da pilha pelo novo ponteiro e vai para o endereço especificado.


O resultado completo da desmontagem pode ser visualizado no repositório do projeto no github.


Total


Como resultado do estudo do gerenciador de inicialização, descobrimos o seguinte:


  1. Um arquivo sem cabeçalho com um comprimento de 16384 bytes é baixado em 16384 (na área da tela, o que geralmente é óbvio durante o processo de download).
  2. No final do download, o ponteiro da pilha está localizado em $5D7C , para o qual o controle é transferido.

Nas partes a seguir, falarei sobre como preparar arquivos para gravar em disco e gravar um carregador de arquivos monobloco no assembler.


Links relacionados:


  1. Perfil "Espectrumista de TRUB . "
  2. Jogos de engenharia reversa ZX Spectrum (Z80) .
  3. Adaptação de jogos de fita para o Beta 48 .

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


All Articles