Programowanie: Program odporny na RESET
From Atariki
Uodpornienie programu na wciśnięcie klawisza RESET realizowane jest zależnie od tego w jaki sposób program jest ładowany do pamięci komputera.
Spis treści |
BOOT
Program ładowany z nośnika za pośrednictwem procedury BOOT otrzymuje sterowanie w dwóch momentach:
1. Po załadowaniu wszystkich sektorów/rekordów programu określonych w nagłówku BOOT.
Wywoływana jest wtedy procedura inicjalizacji umiejscowiona pod adresem BOOT_ADDRESS+6. Ta procedura wywoływana jest jednorazowo w całym procesie BOOT i jest to dobre miejsce do inicjalizacji danych, które nie będą inicjalizowane po wykonaniu RESETu. Ponieważ długość programu bootowalnego ograniczona jest do 255 sektorów/rekordów, jest to również dobre miejsce do załadowania pozostałej części programu do pamięci. Jeśli proces inicjalizacji przebiegł poprawnie procedura powinna zakończyć się ze skasowanym znacznikiem C. Ustawiony znacznik C przy wyjściu z procedury skutkuje głoszeniem błędu BOOT ERROR i zakończeniem procesu BOOTowania.
2. Po zainicjalizowaniu programu.
Adres startu programu umiejscowiony pod adresem BOOT_ADDRESS+4 przepisywany jest do DOSINI ($0C) jeśli BOOT odbywał się z dysku, lub do CASINI ($02) jeśli BOOTowano z kasety, i jest to automatycznie adres, pod który system operacyjny będzie przekazywał kontrolę programowi po naciśnięciu klawisza RESET.
Nie należy ingerować w zawartość rejestru BOOT? ($09), gdyż jest on ustawiany przez system operacyjny.
Wektor inicjalizacji DOSINI/CASINI może zostać wykorzystany do natychmiastowego uruchomienia programu, lub też do jego inicjalizacji i ustawienia wektora DOSVEC ($0A), do którego sterowanie przekazywane jest przez procedurę RESET po całkowitym zakończeniu jej działania. W ten sposób instaluje się w systemie DOS.
Jeżeli z procedury DOSINI/CASINI powrócimy do systemu nie ustawiwszy wektora DOSVEC, wtedy OS ustawi tam adres procedury SELF TEST.
Wektor DOSVEC służy również do przekazania sterowania po wydaniu komendy DOS BASIC-a.
Przykładowy program bootujący:
prog = $2000 opt h- o+ org prog ;nagłówek boot .db 0 ;flaga boot - nieużywana przez system (niektóre DOS-y ustawiają tu swój identyfikator) .db count ;ilość sektorów boot .dw prog ;adres ładowania .dw start ;adres uruchomienia programu init: ;tu można zainicjalizować dane nie inicjalizowane po RESET clc rts start: ... len = *-prog count = (len+127)/128 ;obliczenie ilości sektorów boot - ceil(len/128)
AtariDOS
Standardowy program pracujący pod kontrolą DOS-u nie jest odporny na wciśnięcie klawisza RESET, a więc po jego wciśnięciu kontrola zostaje przekazana do DOS-u.
Aby uodpornić taki program na RESET wykorzystuje się tradycyjny systemowy mechanizm inicjalizowania systemu:
1. Poprzez wektor DOSINI/CASINI inicjalizuje się programy TSR i sterowniki zainstalowane w systemie i rozszerzające jego działanie.
Odbywa się to poprzez tzw. przedłużenie wektora DOSINI/CASINI dzięki czemu przed inicjalizacją własnego programu mają szanse zainicjalizować się DOS oraz programy TSR zainstalowane w systemie wcześniej.
2. Poprzez wektor DOSVEC oddaje się sterowanie do programu, który ma być docelowo uruchomiony po RESET.
Odbywa się to przez zapisanie wektora DOSVEC w procedurze inicjalizacji uruchamianej przez DOSINI/CASINI.
Standardowo DOS podczas własnej procedury inicjalizacyjnej zapisuje tu adres programu DUP.SYS lub CP.SYS.
Programy TSR zazwyczaj potrzebują jedynie zainicjalizować swoje działanie i wykorzystują jedynie DOSINI/CASINI nie zapisując wektora DOSVEC.
Przykładowy fragment programu odpornego na RESET:
prog = $2000 opt h+ o+ org prog init: jsr prolong ldx DOSVEC ldy DOSVEC+1 stx olddosvec sty olddosvec+1 ldx #<start ldy #>start stx DOSVEC sty DOSVEC+1 ;w przypadku TSR można tutaj podnieść MEMLO ... rts start: ;tutaj wykonujemy program główny ... ;po czym przy wyjściu z programu przywracamy zawartości wektorów ldx olddosvec ldy olddosvec+1 stx DOSVEC sty DOSVEC+1 lda BOOT? ldx #0 ;BOOT z kasety (przywracamy CASINI=$02) cmp #2 beq @+ ldx #10 ;BOOT z dysku (przywracamy DOSINI=$0C) @ lda init+1 ldy init+2 sta CASINI,x sty CASINI+1,x jmp (DOSVEC) prolong: ;a to wykona się tylko raz po uruchomieniu programu przez DOS lda BOOT? ldx #0 ;BOOT z kasety (przedłużamy CASINI=$02) cmp #2 beq @+ ldx #10 ;BOOT z dysku (przedłużamy DOSINI=$0C) @ lda CASINI,x ldy CASINI+1,x sta init+1 sty init+2 lda #<init ldy #>init sta CASINI,x sty CASINI+1,x rts olddosvec .ds 2 run init
SpartaDOS X
Generalnym imperatywem jest zakaz modyfikowania zawartości wektora DOSVEC (!), gdyż zawiera on adres bazowy dla różnych zmiennych DOS-a.
Ponieważ SDX nie obsługuje magnetofonu, darujemy sobie rozpoznawanie rodzaju BOOT-a i aktualizację CASINI - skoncentrujemy się wyłącznie na DOSINI.
Program nierelokowalny
Bazujemy na systemowym mechanizmie i modyfikujemy jedynie wektor DOSINI/CASINI.
Przykładowy program nierelokowalny uruchamiany komendą X (dla AtariDOS):
prog = $2000 opt h+ o+ org prog init: jsr prolong ... ;tutaj wykonujemy program główny ... ;po czym przy wyjściu z programu przywracamy zawartość wektora ldx init+1 ldy init+2 stx DOSINI sty DOSINI+1 rts prolong: ;a to wykona się tylko raz po uruchomieniu programu przez DOS ldx DOSINI ldy DOSINI+1 stx init+1 sty init+2 ldx #<init ldy #>init stx DOSINI sty DOSINI+1 rts run init
Program relokowalny
Okazuje się, że to jednak nie wystarcza żeby relokowalny program dla SpartaDOS X mógł poprawnie uodpornić się na RESET, ponieważ podczas wywoływania łańcucha inicjalizującego poprzednio zainstalowane w systemie programy DOS nie wraca na powrót do naszej procedury inicjalizującej, a skacze bezpośrednio przez DOSVEC. Zapobiega temu wyzerowanie zmiennej COMTAB+$20 (nieoficjalnie zwanej DOSFLG) - po zakończeniu programu należy ustawić w niej dowolną niezerową wartość.
Przykładowy program relokowalny (dla SpartaDOS X):
opt h+ o+ blk reloc main init: jsr prolong ... ;tutaj wykonujemy program główny ... ;po czym przy wyjściu z programu przywracamy zawartość wektora ldx init+1 ldy init+2 stx DOSINI sty DOSINI+1 lda #$ff sta COMSPEC+$20 rts prolong: ;a to wykona się tylko raz po uruchomieniu programu przez DOS ldx DOSINI ldy DOSINI+1 stx init+1 sty init+2 ldx initad ldy initad+1 stx DOSINI sty DOSINI+1 lda #0 sta COMSPEC+$20 rts initad .dw init