Ukryty assembler

"Ukryty assembler"

Autor: Marcin Przasnyski

Źródło: "Bajtek" 11/87

Pisząc programy w BASIC-u niejednokrotnie używamy w nich podprogramów w kodzie maszynowym.
Umieszczane są one ponad RAMTOPEM i przy nagrywaniu na taśmę cały program obejmuje dwa zbiory - BASIC i kod maszynowy. Ułatwmy sobie życie wpisując pod-program w asemblerze w pierwszej linii, po instrukcji REM. Najpierw napiszemy program w asemblerze - na przykład zmiana zawartości portu 254 (kolor BORDER-u i głośnik) w zależności od zawartości pamięci ROM.

        LD      C,254
        LD      HL,0
START:  LD      A,(HL)
        LD      B,8
PETLA:  OUT     (C),A
        RRA
        DJNZ    PETLA
        INC     HL
        LD      A,H
        CP      64
        JR      NZ,START
        LD      A,255
        OUT     (C),A
        RET
Procedura ta pobiera bajt z komórki pamięci o adresie 0 i przesyła go na wyjście 254. Następnie bajt obracany jest arytmetycznie w prawo i ponownie przesyłany na wyjście 254. Po ośmiu rotacjach te same czynności powtarzane są dla liczby od adresu 1 aż do 16383 (koniec ROM-u). Gdy zawartość pary rejestrów HL (adres pobieranego bajtu) osiągnie 16384, program zostaje zakończony. Ze względu na użycie tylko skoków względnych (jump relative - JR) procedura jest relokowalna.
Jak umieścić program maszynowy w pamięci?
1. REM xxxxxxxxxxxxxxxxxxxxxx
2. REM w pierwszej linii 24 znaki 
3. POKE 23756,0
10 FOR f = 237760 TO 23783 20 READ d: POKE f,d
30 NEXT f
40 RANDOMIZE USR 23760
50 DATA 14,254,3,3,00,126,6,8, 237,121,31,16,251,35,124,254,62,32,242,62,  
        255,237,121,201
Po uruchomieniu powyższego programu na obrzeżu ekranu pojawią się paski, które po chwili znikną. Próby wylistowania skończą się na pierwszej linii komunikatem Invalid Colour. Wynika to z odczytywania liczb przez interpreter jako kodów sterujących kolorami, dla przykładu liczba 16 oznacza kod koloru atramentu i sekwencja liczb 16251 spowoduje, że komputer będzie chciał wykonać instrukcje odpowiadającą INK 251 i stąd komunikat Invalid Colour. Tak więc, aby zobaczyć program, trzeba wpisać LIST 2.
Postarajmy się "schować" linię z kodem maszynowym w pamięci tak, by była niedostępna dla interpretera języka BASIC. W tym celu trzeba posłużyć się zmiennymi systemowymi.
Zmienna PROG (23635 i 23636) wskazuje początek programu w BASIC-u. Standardowo jej wartość wynosi 23755 (starszy bajt adresu pierwszej linii programu). Zmieniając zawartość tej zmiennej otrzymamy listing programu od wyznaczonego przez nią miejsca. Naszym celem jest ustawienie jej na początek drugiej linii tak, aby linia pierwsza była pomijana przez interpreter. Adres drugiej linii i w ogóle każdej linii w programie ustalić można znając jej budowę. Dwa pierwsze bajty to numer linii. Występuje tutaj inna niż zwykle kolejność - starszy bajt poprzedza młodszy. Następne dwa bajty to liczba o 5 większa niż długość linii. Dalej następuje treść linii a na końcu bajt o wartości 13 zwany znacznikiem końca linii.
Teraz ustalenie adresu drugiej linii będzie bardzo proste - wystarczy policzyć długość linii pierwszej i dodać te wartości do adresu pierwszej linii. 24 znaki plus REM plus pięć bajtów kontrolnych to razem 30 bajtów. 23755 + 30 = 23785 i to jest właśnie adres drugiej linii w naszym programie. Posługując się tym sposobem obliczenie adresu na przykład setnej z kolei linii będzie bardzo pracochłonne. Istnieje dużo prostszy sposób:
LD      HL,(23625)
CALL    6510 
LD      B,H
LD      C,L
RET
czyli:
99 STOP
100 FOR f = 60000 TO 60008
110 READ d: POKE f,d
120 NEXT f
130 DATA 42,73,92,205,110,25,68,77,201
Adres 23625 to zmienna E PPC wskazująca numer linii z kursorem edycji. Skok do procedury 6510 powoduje umieszczenie w HL adresu linii, przy której znajduje się kursor. Następnie zawartość pary rejestrów HL zostanie przepisana do pary rejestrów BC by stać się argumentem funkcji USR. Aby teraz otrzymać adres linii, należy wskazać ją kursorem edycji wiersza i napisać
PRINT USR 60000
Wiemy już, że adres drugiej linii to 23785. Liczbę tę należy rozbić na starszy i młodszy bajt i wpisać w zmienną PROG.
23785=233+92=256 więc po wpisaniu
POKE 23636,233: POKE 23636, 92
i wylistowaniu programu, nie zobaczymy już pierwszej linii. Będzie ona jednak obecna w pamięci i RANDOMIZE USR 23760 pokaże, że program działa. Aby z powrotem otrzymać schowaną linię, napisz:
POKE 23635,203: POKE 23636,92
Podanym sposobem można przechowywać bloki pamięci do około 750 bajtów - dłuższej linii nie ma możliwości otrzymać normalnie. Trzeba też pamiętać, że instrukcja SAVE nagra tylko linie widziane przez interpreter. Dlatego "chowania" linii należy dokonywać dopiero po wczytaniu programu z taśmy.

Marcin Przasnyski


Powrót do strony głównej...