Dando sequência a esta série, eu estarei mostrando como adaptar jogos de fitas protegidas para disquetes Beta 48. Um conhecimento de código de máquina e assembly Z80 será requisito obrigatório no acompanhamento desta parte. Os comandos BASIC para carregar programas e dados da fita cassete podem ser facilmente pirateados, por isso os desenvolvedores de jogos foram criando diferentes esquemas de proteção.
Para cada dado salvo através do SAVE do BASIC, existem dois blocos de dados armazenados na fita, separados por um pequeno intervalo de tempo. O primeiro bloco, chamado de cabeçalho (header), contém dados como tipo (BASIC, DATA ou CODE), comprimento e outros dados dependendo do que está gravado. O bloco seguinte contém os dados propriamente ditos que serão lidos e armazenados na RAM (bloco de dados). Tanto um bloco de cabeçalho como de dados são compostos por um byte leader, seguido dos bytes a serem carregados e, por fim, um byte de paridade. Por exemplo, um bloco de 6912 bytes estará armazenado na fita como uma sequência de 6914 bytes. O byte leader, que é o primeiro, não é carregado na RAM mas simplesmente conferido por especificar o tipo de bloco (0 para cabeçalho e 255 para dados). O 2º ao 6913º bytes são lidos e carregados de fato na memória. Por fim o último byte, de paridade, não é gravado na RAM, mas serve simplesmente para detectar algum eventual erro que possa ter ocorrido. Maiores detalhes podem ser vistos neste artigo do TK-WIKI.
Como o BASIC só admite gravações em fita conforme descrito acima, basta alterar qualquer um desses detalhes na gravação que tornará o bloco irreconhecível a não ser por linguagem de máquina. O Cybernoid II será usado como exemplo para ilustrar esta técnica.
Foi utilizado o lado 2 da fita deste jogo, pois o intuito era adaptar a versão com som para Explorer (vide Google Drive ou 4 Shared), cujo arquivo imagem em formato TZX é "Cybernoid 2 - Side 2.tzx". Neste jogo há dois blocos sem cabeçalhos (headerless) que não são reconhecidos por um LOAD comum do BASIC.
A listagem do programa carregador em BASIC da fita, que pode ser obtida com as técnicas já apresentadas, tem o seguinte conteúdo:
A listagem do programa carregador em BASIC da fita, que pode ser obtida com as técnicas já apresentadas, tem o seguinte conteúdo:
10 CLEAR 24999
20 PAPER 0: BORDER 0: INK 6
30 CLS : LET a=PEEK (14446)
40 IF a=255 THEN GO SUB 120
50 PRINT : PRINT : PRINT
60 PRINT " NOTE THE SELECTION KEY CHANGES"
70 PRINT : PRINT
80 PRINT " Y = SMART BOMB"
90 PRINT " U = TRACERS"
100 LOAD "cyber"CODE
110 RANDOMIZE USR 25000
120 PRINT : PRINT : PRINT
130 PRINT " THIS IS THE 128K VERSION"
140 PRINT
150 PRINT " PLEASE USE OTHER SIDE OF TAPE"
160 RETURN
Na linha 100 um programa em código de máquina é carregada e executada pela linha 110, via
USR 25000
. O disassembly do código de máquina produz:
25000 DI ;Desabilita interrupção.
25001 LD IX,32768 ;Carrega 6912 bytes em 32768.
25005 LD DE,6912
25008 CALL 25036
A sub-rotina em 25036 é responsável pelo carregamento de um bloco de bytes mas, antes de analisá-la, é interessante conhecer a sub-rotina da ROM do TK90X no endereço 1366 ou #0556, conhecida como LD-BYTES, que serve para carregar um bloco da fita para a memória. O registrador IX deve conter o endereço inicial da RAM onde os dados serão gravados, DE deve especificar o número de bytes a carregar, A deve conter o byte leader (normalmente 0 ou 255, mas aceita qualquer valor) e o flag CARRY deve ser 1. O livro "The Complete Spectrum ROM Disassembly" contém a listagem desta sub-rotina, da qual apresento a parte inicial:
0556 LD-BYTES INC D This resets the zero flag. (D
cannot hold +FF.)
EX AF,A'F' The A register holds +00 for a
header and +FF for a block of
data.
The carry flag is reset for
VERIFYing and set for
LOADing.
DEC D Restore D to its original value.
DI The maskable interrupt is now
disabled.
LD A,+0F The border is made WHITE.
OUT (+FE),A
LD HL,+053F Preload the machine stack
PUSH HL with the address - SA/LD-RET.
IN A,(+FE) Make an initial read of port '254'
RRA Rotate the byte obtained but
AND +20 keep only the EAR bit,
OR +02 Signal 'RED' border.
LD C,A Store the value in the C register.
(+22 for 'off' and +02 for 'on'
- the present EAR state.)
CP A Set the zero flag.
Não é necessário tentar entender a sub-rotina LD-BYTES, porém é interessante guardar na memória algumas instruções iniciais, pois ocorrem em vários esquemas de fitas protegidas. No nosso exemplo, examinando o disassembly da sub-rotina abaixo:
25036 LD A,255 ; Valor do byte leader é 255.
25038 SCF ; Levanta flag CARRY.
25039 INC D ; Rotina semelhante ao LD-BYTES.
25040 EX AF,AF'
25041 DEC D
25042 LD A,15
25044 OUT (254),A
25046 JP 1378 ; Continua na ROM o carregamento da fita.
Fica claro então que tal sub-rotina faz carregamento de um bloco da fita para a RAM. Voltando para o disassembly a partir de 25000:
25000 DI ; Desabilita interrupção.
25001 LD IX,32768 ; Define a carga de 6912 bytes em 32768.
25005 LD DE,6912
25008 CALL 25036 ; Carrega o bloco.
25011 LD HL,32768 ; Copia os bytes carregados em 32768 para a
25014 LD DE,16384 ;RAM de vídeo em 16384.
25017 LD BC,6912
25020 LDIR
25022 LD IX,25344 ; Define a carga de 40191 bytes em 25344.
25026 LD DE,40191
25029 LD HL,25344 ; Endereço para iniciar o jogo que é guardado
25032 PUSH HL ;na pilha do Z80.
25033 JP 25036 ; Carrega o bloco.
O trecho acima faz o carregamento da tela a partir do endereço 32768, transfere a tela para o 16384 para assim ser exibida na TV, carrega o código de máquina a partir de 25344 e, por fim, roda o jogo a partir do endereço 25344.
Para carregar os dois blocos, deve-se empregar programa em assembly. Uma possibilidade é usar o programa abaixo para carregar a tela:
ORG 25000
LD IX,32768
LD DE,6912
LD A,255
SCF
JP 1366
Monte o programa acima num assembler, rode-o e carregue a tela na RAM. Depois, no DOS, para salvar a tela use o comando:
SAVE "CYBER2$"CODE 32768,6912
.Faça a mesma coisa com o bloco de código de máquina, porém com o seguinte programa de carregamento:
ORG 25000
LD IX,25344
LD DE,40191
LD A,255
SCF
JP 1366
No DOS, use para salvar no disco o comando:
SAVE "CYBER2C"CODE 25344,40191
. Agora só falta elaborar um programa de carregamento em BASIC (listagem abaixo) e salvar no disco.
10 PAPER 0: BORDER 0: INK 6: CLEAR 24999
20 RAND USR 15363: REM : LOAD "CYBER2$"CODE 16384
30 RAND USR 15363: REM : LOAD "CYBER2C"CODE
40 RAND USR 25344
Assim, a adaptação estará pronta. Do jeito que foi descrito, houve a necessidade de montar programas em assembly para poder carregar os blocos. Existe uma outra forma que faz uso do debugger num emulador. Trata-se de um método muito mais cômodo, embora não possa ser realizado num TK90X real. Entretanto hoje em dia não vejo motivos para insistir em adaptar programas fora de um ambiente de PC moderno.
Muito legal!
ResponderExcluirOutro dia usei um utilitário do WoS (o writetrd) que grava TAP direto pra um TRD e gerou um arquivo headerless... eu não sabia como ler. Agora acho que consigo. Vou tentar. Muito elucidativo! Obrigado novamente.
Flavio, vc conhece o WRITETRD?
Eu nunca usei o WRITETRD, pois normalmente abro um TAP ou TZX no emulador Fuse e copio o conteúdo para o TRD na mão (isto é, usando comandos TR-DOS). Se você conseguir um resultado interessante, pode relatar aqui, o espaço está aberto.
ExcluirParabéns pela iniciativa, estou acompanhando!
ResponderExcluirValeu, Daniel!
Excluir