Programowanie: Program odporny na RESET
From Atariki
Wersja z dnia 21:49, 18 paź 2013 Mono (Dyskusja | wkład) (uodpornienie na reset) ← Previous diff |
Aktualna wersja Mono (Dyskusja | wkład) (.db i .dw -> .byte i .word) |
||
Linia 1: | Linia 1: | ||
- | 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. | + | 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. |
= BOOT = | = BOOT = | ||
Linia 5: | Linia 5: | ||
Program ładowany z nośnika za pośrednictwem procedury BOOT otrzymuje sterowanie w dwóch momentach: | 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. | + | 1. Po załadowaniu wszystkich sektorów/rekordów programu określonych w [[BOOT (format pliku)|nagłówku BOOT]]. |
Wywoływana jest wtedy procedura inicjalizacji umiejscowiona pod adresem BOOT_ADDRESS+6. | 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. | + | 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 RESET-u. |
- | 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. | + | Ponieważ długość programu bootowalnego ograniczona jest do 256 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. | + | Jeśli proces inicjalizacji przebiegł poprawnie, procedura powinna zakończyć się ze skasowanym znacznikiem C. Ustawiony znacznik C przy wyjściu z procedury skutkuje zgłoszeniem błędu BOOT ERROR i powtórką (lub, w przypadku magnetofonu, zakończeniem) procesu BOOT-owania. |
2. Po zainicjalizowaniu programu. | 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. | + | 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 BOOT-owano 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. | 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. | 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. | + | 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 jest ustawiany przez OS w początkowej fazie procedury RESET na adres wejścia do SELF-TEST, więc jeżeli z procedury DOSINI/CASINI powrócimy do systemu nie ustawiwszy wektora DOSVEC, wtedy OS na końcu odda sterowanie właśnie tam. |
- | Wektor DOSVEC służy również do przekazania sterowania po wydaniu komendy DOS BASIC-a. | + | Wektor DOSVEC służy również do przekazania sterowania po wydaniu komendy "DOS" BASIC-a, oraz jest ogólnie wektorem wskazującym procedurę uruchomienia programu powłoki (zazwyczaj Command Processor lub Menu DOS-a). |
Przykładowy program bootujący: | Przykładowy program bootujący: | ||
<pre> | <pre> | ||
+ | DOSVEC = $a | ||
+ | |||
prog = $2000 | prog = $2000 | ||
Linia 33: | Linia 35: | ||
;nagłówek boot | ;nagłówek boot | ||
- | .db 0 ;flaga boot - nieużywana przez system (niektóre DOS-y ustawiają tu swój identyfikator) | + | .byte 0 ;flaga boot - nieużywana przez system (niektóre DOS-y ustawiają tu swój identyfikator) |
- | .db count ;ilość sektorów boot | + | .byte count ;ilość sektorów boot |
- | .dw prog ;adres ładowania | + | .word prog ;adres ładowania |
- | .dw start ;adres uruchomienia programu | + | .word start ;adres uruchomienia programu |
init: | init: | ||
;tu można zainicjalizować dane nie inicjalizowane po RESET | ;tu można zainicjalizować dane nie inicjalizowane po RESET | ||
+ | ... | ||
clc | clc | ||
rts | rts | ||
start: | start: | ||
+ | ;a tu zainicjalizować dane inicjalizowane po RESET | ||
+ | ... | ||
+ | ;i przejąć sterowanie | ||
+ | ... | ||
+ | ;lub ewentualnie zapisać DOSVEC | ||
+ | ldx #<run | ||
+ | ldy #>run | ||
+ | stx DOSVEC | ||
+ | sty DOSVEC+1 | ||
+ | ;i wrócić do systemu | ||
+ | rts | ||
+ | |||
+ | run: | ||
+ | ;a to się będzie wykonywać po zakończeniu BOOT-a lub po wydaniu polecenia DOS z BASIC-a | ||
... | ... | ||
Linia 50: | Linia 67: | ||
</pre> | </pre> | ||
- | = AtariDOS = | + | = DOS = |
- | 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. | + | Program pracujący pod kontrolą jakiegokolwiek DOS-u nie jest odporny na wciśnięcie klawisza RESET. Zostaje on wtedy przerwany, a kontrola przekazywana jest bezpośrednio do DOS-u. |
- | Aby uodpornić taki program na RESET wykorzystuje się tradycyjny systemowy mechanizm inicjalizowania systemu: | + | Aby uodpornić taki program na RESET wykorzystuje się tradycyjny 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 ma szanse zainicjalizować się DOS oraz programy TSR zainstalowane w systemie wcześniej. |
- | 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. | + | == Format nierelokowalny (zgodny z AtariDOS) == |
- | 2. Poprzez wektor DOSVEC oddaje się sterowanie do programu, który ma być docelowo uruchomiony po RESET. | + | Programy w [[Binarny plik DOSu|tradycyjnym formacie binarnym]] nie są relokowalne. |
- | Odbywa się to przez zapisanie wektora DOSVEC w procedurze inicjalizacji uruchamianej przez DOSINI/CASINI. | + | Niezależnie od rodzaju DOS-u, przez który są uruchamiane, przedłużanie wektora CASINI/DOSINI odbywa się w taki sam sposób: |
+ | <pre> | ||
+ | CASINI = 2 | ||
+ | BOOT? = 9 | ||
+ | DOSVEC = $a | ||
+ | DOSINI = $c | ||
- | 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: | ||
- | <pre> | ||
prog = $2000 | prog = $2000 | ||
Linia 77: | Linia 93: | ||
init: | init: | ||
jsr prolong | 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 | ;w przypadku TSR można tutaj podnieść MEMLO | ||
... | ... | ||
- | rts | ||
- | |||
- | start: | ||
;tutaj wykonujemy program główny | ;tutaj wykonujemy program główny | ||
... | ... | ||
- | ;po czym przy wyjściu z programu przywracamy zawartości wektorów | + | ;po czym przy wyjściu z programu przywracamy zawartość wektora |
- | ldx olddosvec | + | |
- | ldy olddosvec+1 | + | |
- | stx DOSVEC | + | |
- | sty DOSVEC+1 | + | |
lda BOOT? | lda BOOT? | ||
ldx #0 ;BOOT z kasety (przywracamy CASINI=$02) | ldx #0 ;BOOT z kasety (przywracamy CASINI=$02) | ||
Linia 106: | Linia 107: | ||
sta CASINI,x | sta CASINI,x | ||
sty CASINI+1,x | sty CASINI+1,x | ||
- | rts | + | jmp (DOSVEC) |
prolong: | prolong: | ||
Linia 124: | Linia 125: | ||
sty CASINI+1,x | sty CASINI+1,x | ||
rts | rts | ||
- | |||
- | olddosvec .ds 2 | ||
run init | run init | ||
</pre> | </pre> | ||
- | = SpartaDOS X = | + | == Format relokowalny (SpartaDOS X) == |
- | Generalnym imperatywem jest zakaz modyfikowania zawartości wektora DOSVEC (!), gdyż zawiera on adres bazowy dla różnych zmiennych DOS-a. | + | [[SpartaDOS X]] definiuje specjalny [[COM#SpartaDOS X|relokowalny format pliku binarnego]]. Pozwala on dodatkowo na wykorzystanie symboli oraz rezerwacje pamięci. |
- | Ponieważ SDX nie obsługuje magnetofonu, darujemy sobie rozpoznawanie rodzaju BOOT-a i aktualizację CASINI - skoncentrujemy się wyłącznie na DOSINI. | + | Przedłużenie wektora DOSINI następuje tu analogicznie jak w przypadku programów nierelokowalnych. Okazuje się jednak, że to nie wystarcza do poprawnego uruchomienia programu po wciśnięciu RESET, ponieważ podczas wywoływania łańcucha inicjalizującego poprzednio zainstalowane w systemie programy, DOS przejmuje kontrolę i nie wraca do procedury inicjującej program. |
- | == Program nierelokowalny == | + | Zapobiega temu wyzerowanie zmiennej COMTAB+$20 (nieoficjalnie zwanej DOSFLG) w fazie inicjalizacji. Gdy odporność na RESET stanie się niepotrzebna (np. tuż przed zakończeniem programu), zmienną tę należy ustawić na dowolną ujemną wartość. |
- | Bazujemy na systemowym mechanizmie i modyfikujemy jedynie wektor DOSINI/CASINI. | + | Ponieważ SDX nie obsługuje magnetofonu, można darować sobie rozpoznawanie rodzaju BOOT-a i aktualizację CASINI, a skoncentrować się wyłącznie na DOSINI. |
- | Przykładowy program nierelokowalny uruchamiany komendą X (dla AtariDOS): | + | Przykładowy program relokowalny (dla SpartaDOS X): |
<pre> | <pre> | ||
- | prog = $2000 | + | DOSVEC = $a |
+ | DOSINI = $c | ||
- | opt h+ o+ | + | COMTAB smb 'COMTAB ' |
- | 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 | ||
- | </pre> | ||
- | |||
- | == 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): | ||
- | <pre> | ||
opt h+ o+ | opt h+ o+ | ||
blk reloc main | blk reloc main | ||
Linia 195: | Linia 160: | ||
sty DOSINI+1 | sty DOSINI+1 | ||
lda #$ff | lda #$ff | ||
- | sta COMSPEC+$20 | + | sta COMTAB+$20 |
- | rts | + | jmp (DOSVEC) |
prolong: | prolong: | ||
Linia 209: | Linia 174: | ||
sty DOSINI+1 | sty DOSINI+1 | ||
lda #0 | lda #0 | ||
- | sta COMSPEC+$20 | + | sta COMTAB+$20 |
rts | rts | ||
- | initad .dw init | + | initad .word init |
</pre> | </pre> | ||
+ | |||
+ | = Linki = | ||
+ | |||
+ | * [[BOOT (format pliku)|Format BOOT]] | ||
+ | * [[Binarny plik DOSu|Format binarnego pliku DOS]] | ||
+ | * [[COM#SpartaDOS X|Format binarnego pliku SpartaDOS X]] | ||
[[Kategoria:Programowanie Atari 8-bit]] | [[Kategoria:Programowanie Atari 8-bit]] | ||
[[Kategoria:Niezbędnik kodera]] | [[Kategoria:Niezbędnik kodera]] |
Aktualna wersja
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 RESET-u. Ponieważ długość programu bootowalnego ograniczona jest do 256 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 zgłoszeniem błędu BOOT ERROR i powtórką (lub, w przypadku magnetofonu, zakończeniem) procesu BOOT-owania.
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 BOOT-owano 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.
Wektor DOSVEC jest ustawiany przez OS w początkowej fazie procedury RESET na adres wejścia do SELF-TEST, więc jeżeli z procedury DOSINI/CASINI powrócimy do systemu nie ustawiwszy wektora DOSVEC, wtedy OS na końcu odda sterowanie właśnie tam.
Wektor DOSVEC służy również do przekazania sterowania po wydaniu komendy "DOS" BASIC-a, oraz jest ogólnie wektorem wskazującym procedurę uruchomienia programu powłoki (zazwyczaj Command Processor lub Menu DOS-a).
Przykładowy program bootujący:
DOSVEC = $a prog = $2000 opt h- o+ org prog ;nagłówek boot .byte 0 ;flaga boot - nieużywana przez system (niektóre DOS-y ustawiają tu swój identyfikator) .byte count ;ilość sektorów boot .word prog ;adres ładowania .word start ;adres uruchomienia programu init: ;tu można zainicjalizować dane nie inicjalizowane po RESET ... clc rts start: ;a tu zainicjalizować dane inicjalizowane po RESET ... ;i przejąć sterowanie ... ;lub ewentualnie zapisać DOSVEC ldx #<run ldy #>run stx DOSVEC sty DOSVEC+1 ;i wrócić do systemu rts run: ;a to się będzie wykonywać po zakończeniu BOOT-a lub po wydaniu polecenia DOS z BASIC-a ... len = *-prog count = (len+127)/128 ;obliczenie ilości sektorów boot - ceil(len/128)
DOS
Program pracujący pod kontrolą jakiegokolwiek DOS-u nie jest odporny na wciśnięcie klawisza RESET. Zostaje on wtedy przerwany, a kontrola przekazywana jest bezpośrednio do DOS-u.
Aby uodpornić taki program na RESET wykorzystuje się tradycyjny mechanizm inicjalizowania systemu.
Odbywa się to poprzez tzw. przedłużenie wektora DOSINI/CASINI, dzięki czemu przed inicjalizacją własnego programu ma szanse zainicjalizować się DOS oraz programy TSR zainstalowane w systemie wcześniej.
Format nierelokowalny (zgodny z AtariDOS)
Programy w tradycyjnym formacie binarnym nie są relokowalne.
Niezależnie od rodzaju DOS-u, przez który są uruchamiane, przedłużanie wektora CASINI/DOSINI odbywa się w taki sam sposób:
CASINI = 2 BOOT? = 9 DOSVEC = $a DOSINI = $c prog = $2000 opt h+ o+ org prog init: jsr prolong ;w przypadku TSR można tutaj podnieść MEMLO ... ;tutaj wykonujemy program główny ... ;po czym przy wyjściu z programu przywracamy zawartość wektora 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 run init
Format relokowalny (SpartaDOS X)
SpartaDOS X definiuje specjalny relokowalny format pliku binarnego. Pozwala on dodatkowo na wykorzystanie symboli oraz rezerwacje pamięci.
Przedłużenie wektora DOSINI następuje tu analogicznie jak w przypadku programów nierelokowalnych. Okazuje się jednak, że to nie wystarcza do poprawnego uruchomienia programu po wciśnięciu RESET, ponieważ podczas wywoływania łańcucha inicjalizującego poprzednio zainstalowane w systemie programy, DOS przejmuje kontrolę i nie wraca do procedury inicjującej program.
Zapobiega temu wyzerowanie zmiennej COMTAB+$20 (nieoficjalnie zwanej DOSFLG) w fazie inicjalizacji. Gdy odporność na RESET stanie się niepotrzebna (np. tuż przed zakończeniem programu), zmienną tę należy ustawić na dowolną ujemną wartość.
Ponieważ SDX nie obsługuje magnetofonu, można darować sobie rozpoznawanie rodzaju BOOT-a i aktualizację CASINI, a skoncentrować się wyłącznie na DOSINI.
Przykładowy program relokowalny (dla SpartaDOS X):
DOSVEC = $a DOSINI = $c COMTAB smb 'COMTAB ' 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 COMTAB+$20 jmp (DOSVEC) 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 COMTAB+$20 rts initad .word init