quinta-feira, 12 de dezembro de 2019

Dicas: rotina C/M para anti-Break

Neste post, partilhamos uma rotina ANTI-BREAK para o ZX Spectrum 48K BASIC, adaptada por este vosso autor. Saliente-se que a rotina não desactiva necessariamente o BREAK, por exemplo, em rotinas de LOAD e SAVE. Desactiva, sim, o retorno ao BASIC, e respectiva mensagem de erro.

No código fonte de assembly que acompanha este artigo, o endereço/ORG está construída de tal forma, que é carregada na primeira linha de BASIC, que deve ter um REM seguido de 58 caracteres. A rotina é carregada a seguir ao REM, se o BASIC estiver na posição normal.

Também partilhamos um .TAP já com a rotina carregada no REM.

O código assembly está escrito de tal forma que a rotina START:
  • usa o registo BC para saber com que endereço foi invocada/onde está a ser executada;
  • modifica a rotina HANDLER: para este bloco de código poder ser carregado em qualquer endereço;
  • faz update do endereço apontado por ERR_SP para redireccionar a rotina de tratamento de erros do BASIC para HANDLER:
  • volta ao BASIC
A rotina de HANDLER:
  • verifica se o código de erro é BREAK - CONT ou BREAK into program;
  • se não, salta para o endereço, que normalmente vai, sem este programa, para um ponto alternativo de entrada da rotina MAIN, o ciclo principal do interprete BASIC - MAIN_4;
  • de outra forma, limpa o código de erro e o sinal de ter tecla(s) em buffer;
  • Salta para uma entrada alternativa, da rotina da ROM onde chegam rotinas de BASIC sem erros, STMT_R_1.
        ; BASIC ANTI-BREAK
        ; BASED IN YS/WOS ROUTINE
        ; RUI RIBEIRO/2019

        ORG     $5CD0 ; 1st Basic LINE after REM

START:

        ; HL,(HANDLER)
        LD HL,HANDLER-START
        ADD HL,BC   ; HL=HANDLER

        PUSH HL
        POP  IX     ; LD IX,HANDLER

        ; self modifying code
        ; update handled address loaded
        ; in HL in HANDLER: line
        ; LD (HANDLER+1),HANDLER

        LD (IX+1),L ; HANDLER+1
        LD (IX+2),H ; HANDLER+2

        ; Update ERR_SP pointer

        PUSH HL                      ; preserve handler address

        ; GET ADDRESS pointed by ERR_SP
        ; HL,(ERR_SP)
        LD L,(IY+3) ; $5C3D ERR_SP
        LD H,(IY+4) ; $5C3E ERR_SP

        PUSH    HL
        POP     IX                      ; LD IX,(ERR_SP)
        POP     HL
        ; LD (IX),HANDLER
        LD      (IX+0),L ;
        LD      (IX+1),H ;
        RET

HANDLER:

        LD HL,HANDLER     ; value will be modified
        LD A,(IY+0)               ;  ERR NR Sys Var 1 less than error code
        CP $14                      ;  L BREAK into program
        JR Z,label1                ; =
        CP $0C                      ;  D BREAK - CONT repeats
        JP NZ,$1303              ; != GO  MAIN_4
                                           ; usual value pushed in (ERR_SP)
label1:
        PUSH HL                   ; PUSH HANDLER
        LD (IY+0),$FF           ;  $5C3A ERR-NR 
                                          ; 0-1 clear error
        RES 5, (IY+1)           ;  $5C3B
                                          ; FLAGS  - 5 Set when a new key has been pressed
                                          ; unset it

        JP $1B7D ; STMT_R_1 - Continue here as the BREAK key was not pressed.


O TAP carrega uma linha 1 com um REM seguido da rotina em código máquina. A linha 2 é para calcular o endereço execução, já que o endereço de BASIC pode variar, notavelmente se estiver uma interface 1 ligada.

2 RANDOMIZE USR (PEEK 23635+PEEK 23636*256+5)

Se esta rotina for incluída num conjunto de protecção BASIC (pode ter outros usos, como usar à vontade BREAK como tecla de INPUT), para tornar a linha 1 em 0 e criar assim uma protecção ANTI-MERGE, fazer, depois de fazer load ao BASIC do TAP:

POKE (PEEK 23635+PEEK 23636*256+1),0

Nota: Um handler com um wrapper / rotina de tratamento pendurado em ERR_SP, é uma gateway possível, para ampliar o BASIC da ROM normal do ZX Spectrum 48K com mais comandos/funcionalidades.

O IY na ZX Spectrum ROM, aponta sempre para $5C3A. 

Para clarificar o uso do registo BC nesta rotina, veja Devolver um número de uma rotina C/M

Temos conhecimento de POKEs anti-break, mas eles parecem ser derivados empiricamente e não são fiáveis, por exemplo, em GOSUBs, uma operação que mexe com o stack, deixam de trabalhar e tem que se fazer outra vez o POKE.

Sem comentários:

Publicar um comentário