Indeks | English version |
Uruchamianie programów maszynowych okazało się możliwe dzięki lukom w implementacji komendy INPUT.
Tradycyjnie pierwszym przykładem, który się pisze przy poznawaniu nowego systemu lub języka programowania, jest program wyświetlający napis "Hello world". Tak będzie również w tym przypadku:
.radix 16 ;wszystkie liczby szesnastkowe start: mov #41,@#8264 ;zmienna systemowa loop: jsr r4,print1 .asciz "HELLO WORLD! " br loop print1: jsr pc,@#1248 ;procedura ROM wyświetlająca łańcuch rts r4
Niestety, tego programu nie da się wpisać, ponieważ nie wszystkie kody znaków są dostępne z klawiatury, a poza tym program musi trochę wyglądać jak BASIC żeby został zaakceptowany przez edytor. Oto zmodyfikowana wersja programu spełniająca te warunki:
start: 8272: bmi 82B6 8296: jsr r4,@#82B0 829A: .asciz "HELLO=WORLD!=" 82A8: jsr r5,@#82AE 82AC: sob r0,8296 82AE: rts r5 82B0: jsr pc,@#1248 82B4: rts r4 82B6: mov #8041,@#8264 82BC: sob r0,8296
Tak wygląda gotowy program, który należy zapisać jako P0 (w odróżnieniu od pozostałych ma stały adres początkowy):
Zwykłe znaki alfabetu łacińskiego są w kolorze czarnym, cyrylica w kolorze czerwonym, znaki graficzne w kolorze zielonym, słowa kluczowe BASIC w kolorze niebieskim. Trzeba na to bardzo uważać, ponieważ różne znaki z różnych zestawów mogą wyglądać podobnie lub identycznie.
8260: 7B 81 00 00 81 00 00 00 00 08 00 0A 00 DF 38 32 {.............82 8270: 35 36 21 81 30 00 14 00 E7 00 1E 00 21 30 31 32 56!.0.......!012 8280: 33 34 35 36 37 38 39 00 28 00 21 30 31 32 33 34 3456789.(.!01234 8290: 35 36 37 38 39 00 1F 09 B0 82 48 45 4C 4C 4F 3D 56789.....HELLO= 82A0: 57 4F 52 4C 44 21 3D 00 5F 09 AE 82 0C 7E 85 00 WORLD!=._....... 82B0: DF 09 48 12 84 00 DF 15 41 80 64 82 14 7E 00 00 ..H.....A.d.....
mk85fipr.zip - plik obrazu pamięci RAM dla emulatora
Komenda INPUT traktuje stałą numeryczną w polu argumentu tak jak zmienną łańcuchową $ (w wyniku braku sprawdzenia co rzeczywiście jest zwracane przez procedurę przetwarzającą wyrażenia w okolicy adresu 1C62). Kopiuje ona wpisany łańcuch o długości do 1E znaków pod adres pamięci przeznaczonej dla zmiennej. Adres zmiennej jest pobierany z drugiego słowa danej na stosie. W przypadku stałej numerycznej to słowo zawiera pierwsze cztery cyfry mantysy.
Jest to możliwe przez wykorzystanie instrukcji skoku pośredniego poprzez zmienną 8258, znajdującego się w pamięci ROM pod adresem 0E76. Ten fragment kodu jest między innymi dostępny z głównej pętli obsługi trybu interaktywnego, mniej więcej pod adresem 0BC6. Oryginalnie jest przeznaczony do wznowienia wykonywania programu BASIC po wystąpieniu warunku STOP.
Procedura obsługi komendy INPUT używa zmiennej 8258 do tymczasowego przechowania wskaźnika do interpretowanego programu BASIC. Wynika z tego, że można wykonać jako program maszynowy dane następujące po INPUT, jeżeli udałoby się przejść do pętli trybu interaktywnego przy spełnieniu poniższych warunków:
W takiej sytuacji wciśnięcie klawisza [EXE] jest błędnie interpretowane jako żądanie wznowienia wykonywania programu BASIC, co w efekcie powoduje uruchomienie kodu maszynowego użytkownika wskazywanego przez zmienną 8258.
Aktualnie znane mi są dwie metody przejścia do pętli trybu interaktywnego:
Zwykle stan STOP jest spowodowany przez wciśnięcie klawisza [STOP], wykonaniem instrukcji STOP, lub pracą w trybie TRACE. Obsługą tego stanu zajmuje się procedura 179A, która wyświetla numer programu i wiersza, a następnie przechodzi do trybu interaktywnego.
Można wymusić taki stan przez ustawienie bitu 0 zmiennej 8256
(mode) zmieniając jej zawartość z 8000 na 8001.
Odbywa się to przez wpisanie łańcucha w odpowiedzi na komendę
INPUT 8256
w wierszu 10.
Znaku o kodzie 01 wprawdzie nie da się wpisać z klawiatury, ale
znak o kodzie 31 ('1') pasuje również.
Jednak sam znak '1' zmieniłby również bardziej znaczący bajt zmiennej
mode na 00, ponieważ łańcuch zakończony jest zerem.
Konieczne jest wpisanie jeszcze trzech dalszych znaków, żeby również
zachować stan zmiennej 8258.
Końcowy bajt 00 naruszy dopiero zmienną 825A, która nie jest w
ważna w tym kontekście.
W celu przejścia to trybu interaktywnego pozostało wykonać jakąś komendę BASIC dozwoloną zarówno w programie jak i w trybie interaktywnym, na przykład VAC (w wierszu 20). Ta grupa komend ma wspólny punkt wyjścia pod adresem 1652, a stamtąd do pętli trybu interaktywnego 0BA0 lub pętli interpretacji programu BASIC, zależnie od stanu bitu 0 zmiennej mode.
Wciśnięcie klawisza [AC] jest obsługiwane pod adresem 05C0 w ROM. Ta procedura kasuje bufor wejściowy oraz wyświetlacz, a następnie przechodzi do pętli trybu interaktywnego, nie modyfikując zmiennych 8256 i 8258. Przy tej metodzie argument komendy INPUT jest bez znaczenia (nie musi to być stała numeryczna, można użyć dowolnej zmiennej). Również zbędna jest komenda VAC w wierszu 20 (nigdy nie jest wykonywana).
Pisanie programów maszynowych wyglądających jak BASIC okazało się kłopotliwym i czasochłonnym zajęciem. Poniżej opisana metoda pozwala na uruchamianie dowolnych programów bez żadnych sztucznych ograniczeń.
Ten etap rezerwuje bezpieczne miejsce w pamięci dla programów
maszynowych przez modyfikację zmiennej systemowej ramtop pod
adresem 8252.
Obszar pamięci od ramtop aż do końca pozostanie niedostępny
dla systemu BASIC.
Poniższy program modyfikuje zmienną ramtop:
10 INPUT 8252:VAC
Wpisanie do zmiennej ramtop wartości 8680 rezerwuje obszar pamięci o rozmiarze 0180 bajtów rozpoczynający się od adresu 8680 (wszystkie liczby szesnastkowe). Ta wartość odpowiada łańcuchowi , który trzeba wpisać w odpowiedzi na INPUT. Program wystarczy uruchomić tylko raz. Nowa konfiguracja pamięci będzie zachowana tak długo, jak nie zostanie wciśnięty przełącznik init na tylnej ściance lub wywołana komenda TEST.
Następny program kopiuje do przydzielonego obszaru plik binarny zapisany jako ciąg liczb szesnastkowych w wierszach programu BASIC, a następnie wykonuje instrukcję skoku na początek tego obszaru. Tak jak w poprzednim przykładzie, pewne udziwnienia są spowodowane koniecznością dopasowania się do struktury wymuszonej przez język BASIC.
.radix 16 .loc 8272 start: bmi start1 .loc 8286 exec: jmp (r5) loop: tstb (r2)+ ;separator lub znak końca wiersza nop bne byte nop jsr r0,@#return ;wypełniacz bez funkcji line: tstb (r2)+ cmpb (r2)+,#2727 ;test czy numer wiersza > 9987 bcc exec1 tstb (r2)+ ;pominięcie znaku '!' nop jsr r0,return ;wypełniacz bez funkcji byte: jsr pc,@#digit nop jsr pc,digit inc r4 ;wypełniacz bez funkcji movb r0,(r3)+ sob r1,loop exec1: sob r2,exec start1: bmi start2 return: rts r0 digit: asl r0 asl r0 asl r0 asl r0 nop mov r0,-(sp) movb (r2)+,r0 cmpb r0,#4141 ;'A' bcs digit1 sub #3737,r0 add (sp)+,r0 rts pc start2: mov #4080,r1 add #8800-4080,r1 ;r1 = koniec pamięci RAM nop mov #4080,r2 add #data+1-4080,r2 ;r2 = adres źródłowy nop mov 8252,r3 ;adres docelowy = zmienna ramtop mov r3,r5 ;wektor skoku sub r3,r1 ;maksymalna ilość bajtów sob r2,line digit1: sub #3030,r0 add (sp)+,r0 rts pc data: ;tu rozpoczynają się dane
Po programie ładującym oczekiwane są wiersze BASIC zawierające listę bajtów w formacie szesnastkowym poprzedzoną wykrzyknikiem. Poszczególne bajty rozdzielone są pojedynczym znakiem (preferowany przecinek lub spacja). Ilość bajtów w wierszu może być dowolna. Wszystko powinno być zakończone wierszem o numerze 9988 lub większym. Poniższy przykład powstał w wyniku asemblacji pierwotnej wersji programu "Hello world":
9000 !DF,15,41,00,64,82,37,09 9010 !10,00,48,45,4C,4C,4F,20 9020 !57,4F,52,4C,44,21,20,00 9030 !F6,01,DF,09,48,12,84,00 9999 END
Jest to sporo wpisywania i łatwo się pomylić.
Wszystko trzeba dwukrotnie sprawdzić!
Program ładujący razem z danymi wyglądają w pamięci tak:
8260: 6D 81 00 80 00 80 00 00 00 00 00 0A 00 DF 38 32 m.............82 8270: 35 36 21 81 30 00 14 00 E7 00 1E 00 21 30 31 32 56!.0.......!012 8280: 33 34 35 36 37 00 4D 00 D2 8B A0 00 0B 02 A0 00 34567.M......... 8290: 1F 08 B8 82 D2 8B 97 A4 27 27 0C 86 D2 8B A0 00 ........''...... 82A0: 37 08 14 00 DF 09 BA 82 A0 00 F7 09 0C 00 84 0A 7............... 82B0: 13 90 56 7E 98 7E 0F 81 80 00 C0 0C C0 0C C0 0C ..V............. 82C0: C0 0C A0 00 26 10 80 94 17 A0 41 41 13 87 C0 E5 ....&.....AA.... 82D0: 37 37 80 65 87 00 C1 15 80 40 C1 65 80 47 A0 00 77.e.....@.e.G.. 82E0: C2 15 80 40 C2 65 7D 42 A0 00 C3 17 52 82 C5 10 ...@.e}B....R... 82F0: C1 E0 B0 7E C0 E5 30 30 80 65 87 00 28 23 21 44 ......00.e..(#!D 8300: 46 2C 31 35 2C 34 31 2C 30 30 2C 36 34 2C 38 32 F,15,41,00,64,82 8310: 2C 33 37 2C 30 39 00 32 23 21 31 30 2C 30 30 2C ,37,09.2#!10,00, 8320: 34 38 2C 34 35 2C 34 43 2C 34 43 2C 34 46 2C 32 48,45,4C,4C,4F,2 8330: 30 00 3C 23 21 35 37 2C 34 46 2C 35 32 2C 34 43 0.<#!57,4F,52,4C 8340: 2C 34 34 2C 32 31 2C 32 30 2C 30 30 00 46 23 21 ,44,21,20,00.F#! 8350: 46 36 2C 30 31 2C 44 46 2C 30 39 2C 34 38 2C 31 F6,01,DF,09,48,1 8360: 32 2C 38 34 2C 30 30 00 0F 27 E4 00 00 00 00 00 2,84,00..'......
mk85load.zip - plik obrazu pamięci RAM dla emulatora
Program uruchamia się w identyczny sposób jak poprzednią wersję "Hello world".
Gdy kod maszynowy zostanie przekopiowany we właściwe miejsce, program ładujący może zostać skasowany. Kod pozostanie w pamięci dopóki nie zostanie wciśnięty przełącznik init na tylnej ściance lub wywołana komenda TEST.
Poniższy krótki program uruchamia załadowany kod maszynowy przy braku programu ładującego:
82B6: mov @#8252,r0 82BA: jmp (r0)
8260: 6D 81 00 80 00 80 00 00 00 00 00 0A 00 DF 38 32 m.............82 8270: 35 36 21 81 30 00 14 00 E7 00 1E 00 21 30 31 32 56!.0.......!012 8280: 33 34 35 36 37 38 39 00 28 00 21 30 31 32 33 34 3456789.(.!01234 8290: 35 36 37 38 39 00 32 00 21 30 31 32 33 34 35 36 56789.2.!0123456 82A0: 37 38 39 00 3C 00 21 30 31 32 33 34 35 36 37 38 789.<.!012345678 82B0: 39 30 31 32 33 00 C0 17 52 82 48 00 00 00 00 00 90123...R.H.....
mk85strt.zip - plik obrazu pamięci RAM dla emulatora