Wydział Informatyki Politechniki Białostockiej Data:
Laboratorium Informatyki Technicznej,
Przedmiot: Architektura komputerów
11.06.2013
Ćwiczenie nr 10
Temat:
Program rezydentny (TSR)
Prowadzący:
Autor:
Dariusz Mikołajczuk
dr inż. Mirosław Omieljanowicz
Plik: TSR2.ASM
; z pomocą:
; http://coding.derkeiler.com/Archive/Assembler/alt.lang.asm/2006-04/msg00575.html
; http://rudy.mif.pg.gda.pl/~bogdro/dos/tsr_tut.htm
; http://abreojosensamblador.net/Productos/AOE/html/Codigos/Cap12/RelojCN2.asm
; Napisać program, który "zostawia" w pamięci ślad (to część rezydentna)
; w postaci napisu np. "tu byłem". Program uruchamia się i sprawdza czy
; w pamięci jest napis. Jeśli nie to umieszcza go w pamięci i kończy
; działanie wypisaniem komunikatu: "Zainstalowałem napis:xxxx".
; Jeśli jest napis w pamięci to usuwa napis z pamięci i kończy działanie
; komunikatem: "Usunąłem napis:xxxx"
; Program dodatkowo wyświetla zegar w rogu ekranu w czasie działania
; programu rezydentnego
BITS 16
ORG 0x100
SegVid EQU 0B800h
; Segment video w trybie tekstowym
ColVid EQU (80-10)*2
; offset ósmej kolumny od końca w pamięci Attributes EQU 00111110b
; atrybuty wyświetlanych znaków
Separator EQU ":"
; separator HH:MM:SS
;; Makro wypisuje komunikat z adresu podanego jako parametr modyfikuje AH i DX
%macro MACRO_printmes
1
mov
AH, 09h
mov
DX, %1
int
21h
%endmacro
; ------------------------------ MACRO_putbyte ------------------------------
; Liczba jednobajtowa w AL wypisuje ją na ekran szesnastkowo (modyfikuje AX i DX)
%macro MACRO_putbyte
0 ; AX=xxxx|xxxx|HHHH|LLLL
xor
AH, AH
; AX=0000|0000|HHHH|LLLL
shl
AX, 4
; AX=0000|HHHH|LLLL|0000
shr
AL, 4
; AX=0000|HHHH|0000|LLLL
add
AX, 3030h
; jeśli AH<10,AL<10 w AX => AH,AL in {'0','9'}
cmp
AH, '9'
; czy AH <= '9'
jbe
%%check_AL
; jeśli tak AH gotowy
add
AH, 07h
; AH > '9', konwertuje do {'A'...'F'}
%%check_AL:
cmp
AL, '9'
; czy AL <= '9'
jbe
%%ascii_ok
; AH i AL gotowe
add
AL, 07h
; AL > '9', konwertuje do {'A'...'F'}
%%ascii_ok:
; w AH górny półbajt, AL - dolny liczby w kodzie ASCII mov
DX, AX
mov
AH, 02h
xchg
DH, DL
; zamiana miejscami, bo pierwszy wypisywany DL
int
21h
shr
DX, 8
int
21h
%endmacro
%macro MACRO_bcdtoascii
0
; http://abreojosensamblador.net/Productos
;
/AOE/html/Codigos/Cap12/RelojCN2.asm
; Opis:
Konwertuje liczbę dwucyfrową w kodzie BCD do ASCII
;
i zapisuje pod DS:DI jako dwa znaki
; wejście:
DI = offset pod którym zapisać liczbę (jako dwa znaki w pamięci video)
; AL = liczba dwucyfrowa w kodzie BCD
; AH = atrybuty znaku
; Zwraca:
DI - pozycja kolejnego znaku w pamięci video
; Niszczy:
BX, CX, DI
push AX
; zachowaj AX
cld
; wyzeruj znacznik kierunku
mov
BL, AH
; kopiuj atrybuty znaku BL=AAAA
mov
CL, 4
; AX=xxxx|xxxx|HHHH|LLLL
xor
AH, AH
; AX=0000|0000|HHHH|LLLL
shl
AX, CL
; AX=0000|HHHH|LLLL|0000
shr
AL, CL
; AX=0000|HHHH|0000|LLLL
or
AX, 3030h
; wiadomo, że w AH i AL są liczby <= 9 (OR szybszy niż dodawanie) xchg AL, AH
; AH=LLLL, AL=HHHH
xchg AH, BL
; AH=AAAA, AL=HHHH, BL=LLLL
stosw
; zapisuje AX (pierwszy znak BCD) pod [DS:DI] i zwiększa DI o 2
mov
AL, BL
; AH=AAAA, AL=LLLL, BL=HHHH
stosw
; zapisuje AX (drugi znak BCD) pod [DS:DI] i zwiększa DI o 2
pop
AX
; przywróć AX
%endmacro
;;; Rezydentna część kodu ;;; SECTION .text
start:
;; przejście do właściwej części kodu przy "ręcznym" uruchamianiu programu jmp main
;; tu zostaje zachowany adres oryginalnej p. obsługi przerwania
;; zmienne te zostają zachowane w programie rezydentnym olddos_off dw
0000h
;; (offset) początek: newdosint-13
olddos_seg dw
0000h
;; (segment) początek: newdosint-11
marker
db
'tu bylem$'
;; początek: newdosint-9
;;; Nowa procedura obsługi przerwania ;;; newdosint:
; nowa procedura obsługi przerwania int 08h cli
; wyłączenie przerwań (nieużywane w tym przerwaniu) pushf
; kopie rejestrów i flag procesora
push
AX
push
BX
push
CX
push
DI
push
ES
mov
AH, Attributes
; atrybuty wyświetlanych znaków
mov
DI, SegVid
; segment pamięci video
mov
ES, DI
; zapisanie segmentu w ES
mov
DI, ColVid
; kolumna video do zapisu
mov
AL, ' '
stosw
; Pobierz godzinę z pamięci CMOS
mov
AL, 4
; wybór adresu nr. 4 pamięci CMOS [godzina RTC]
out
70h, AL
; zapisanie adresu na porcie 70h
jmp
$+2
; opóźnienie
in
AL, 71h
; pobranie godziny BCD z portu 71h
MACRO_bcdtoascii
; wypisuje godzinę w pamięci video
mov
AL, Separator ; pobierz znak separatora stosw
; wpisz znak separatora AL, z atrybutami
; w AH do pamięci video (pod ES:DI)
; Pobierz minuty z pamięci CMOS
mov
AL, 2
; wybór adresu nr. 2 pamięci CMOS [minuty RTC]
out
70h, AL
; zapisanie adresu na porcie 70h
jmp
$+2
; opóźnienie
in
AL, 71h
; pobranie minut BCD z portu 71h
MACRO_bcdtoascii
; wypisuje minuty w pamięci video
AL, Separator
; pobierz znak separatora
stosw
; wpisz znak separatora AL, z atrybutami
; w AH do pamięci video (pod ES:DI)
; Pobierz sekundy z pamięci CMOS
xor
AL, AL
; wybór adresu nr. 0 pamięci CMOS [sekunda RTC]
out
70h, AL
; zapisanie adresu na porcie 70h
jmp
$+2
; opóźnienie
in
AL, 71h
; pobranie sekund BCD z portu 71h
MACRO_bcdtoascii
; wypisuje sekundy w pamięci video
mov
AL, ' '
stosw
pop
ES
; przywrócenie wartości rejestrów
pop
DI
; i flag procesora
pop
CX
pop
BX
mov
AL, 20h
;? Sygnał dla kontrolera przerwań
out
20h, AL
;? że przerwanie zostało zakończone
pop
AX
popf
jmp
far [CS:olddos_off] ; int 08h (skok bez powrotu do oryginalnej
; procedury obsługi przerwania)
;;; Koniec nowej procedury obsługi przerwania ;;;
;;; Właściwa (nierezydentna) część kodu ;;;
;;; Sprawdzenie czy znacznik zainstalowany i przejście do instalacji lub deinstalacji main:
MACRO_printmes
mes_check_znacznik
;; Pobranie adresu p. obsługi przerwania int 08h (być może naszej) mov
AX, 3508h ; pobierz wektor przerwania int 08h int
21h
; zwraca ES:BX - segm:off procedury obsługi mov
CX, BX
; kopia tymczasowa offsetu
sub
BX, 9
; początek znacznika TSR-a
;; porównanie znaczników (aktualnego i na początku aktualnej
;; procedury obsługi przerwania)
mov
DI, marker
; marker obecnego programu
compare_znaczniki:
mov
DH, [ES:BX]
; kolejny znak z początku akt. p. obsł. przerwania mov
DL, [DS:DI]
; kolejny znak znacznika (w aktualnym kodzie) cmp
DH, DL
jne
zn_not_installed ; znaki różne - znacznik i procedura nie zainstalowane cmp
DL, '$'
; takie same, czy koniec znacznika
je
zn_installed
; znacznik zainstalowany (ale może to nie nasz) inc
DI
; zwiększ przesunięcia i kontynuuj pętlę inc
BX
jmp
compare_znaczniki
zn_installed:
; znacznik jest zainstalowany
mov
AH, 09h
mov
DX, mes_installed
int
21h
;; Sprawdzenie czy offsety aktualnej procedury przerwania i
;; procedury newdosint są zgodne
cmp
CX, newdosint
jne
znacznik_nie_nasz
MACRO_printmes
mes_zn_isok
; jeśli ok, przejdź do deinstalacji
jmp
deinstalacja
znacznik_nie_nasz:
; offsety niezgodne, przejdź do instalacji MACRO_printmes
mes_bad_znacznik
mov
BX, CX
; przywrócenie segmentu oryginalnej p. obsł. przerwania jmp
instalacja
zn_not_installed:
; znacznik nie jest zainstalowany
mov
BX, CX
; przywrócenie segmentu oryginalnej p. obsł. przerwania MACRO_printmes
mes_no_znacznik ; i przejście do instalacji poniżej
;;; Instalacja procedury obsługi przerwania i przejście w stan rezydentny ;;; instalacja:
;; Zapisanie adresu oryginalnej p. obsługi przerwania w zm. kopiowanej do TSR
mov
[olddosint+0], BX
; zapisz offset oryginalnej procedury int 08h mov
[olddosint+2], ES
; zapisz segment
; w pamięci zapisywane kolejno offset:segment
; little endian!
;; Ustawienie nowego adresu p. obsługi przerwania w wektorze przerwań push
CS
pop
DS
; DS := CS (z wykorzystaniem stosu)
mov
DX, newdosint
; w DS:CX adres nowej procedury obsługi przerwania int 08h mov
AX,2508h
; ustaw ją
int
21h
;; Usuwany segment środowiska programu
mov ES, [CS:002Ch]
; w ES zapisywany adres segmentu pamięci do zwolnienia mov AH, 49h
; tutaj usuwana cała zawartość PSP począwszy i włącznie z int 21h
; segmentem środowiska (przesunięcie 2Ch względem początka PSP)
; naprawdę ta część PSP zajmuje tylko 212 bajtów, a nie tyle co
; segment (64KB), ale z uwagi że jest to DOS to ten program
; jest ostatnim w pamięci
MACRO_printmes
mes_isinstalled
;; Określenie ilości paragrafów do zostawienia w pamięci i przejście do pracy w tle
;; ilość bajtów jaką należy zachować można obliczyć:
;;
bajtów_do_zach = offset_etykiety_main - offset_etykiety_start
;; liczba paragrafów 16-bajtowych do zachowania:
;;
paragraf_do_zach = bajtów_do_zach/16 + 16(dla PSP) + 1(ze względu na dzielenie
;; całkowite po lewej)
;;
mov DX, main
; pobierz offset etykiety początku części nierezydentnej sub DX, start
; odejmij od niej offset etykiety start (początek TSR-a) shr DX, 04
; podziel przez 16
add DX, 17
; dodaj 16 paragrafów dla PSP + 1 extra (dzielenie całkowite) mov AX, 3100h
; zakończ program i przejdź w stan rezydentny int 21h
;;; Koniec instalacji ;;;
;;; Deinstalacja naszej procedury obsługi przerwania i przywrócenie oryginalnej ;;; deinstalacja:
;; Przywrócenie oryginalnego przerwania int08h
;; ES - segment, CX - offset naszej procedury obsługi przerwania
;; [olddosint+2] - segment, [olddosint+0] - offset oryginalnej p. obsługi przerwania mov
DI, CX
sub
DI, 13
; określenie offsetu (względem ES) kopii oryginalnej proc. przerwania
;; [ES:DI] -- offset oryginalnej procedury int 08h
;; [ES:DI+2] -- segment oryginalnej procedury int 08h mov
AX, [ES:DI]
; AX := oryginalny offset
add
DI, 2
mov
BX, [ES:DI]
; BX := oryginalny segment
mov
CX, ES
; kopia segmentu naszego TSR-a (offset już niepotrzebny) xor
DX, DX
; DX := 0
mov
DI, 08h
; DI = 08h*4 ; offset adresu p. obsługi przerwania int 08h
; w tablicy wektorów przerwań (mnożone przez 4,
; bo adresy 4 - bajtowe)
shl
DI, 2
;
cli
; wyłączenie przerwań na czas zmiany wektora przerwań mov
ES, DX
; ES := 0 -- segment tablicy wektorów przerwań mov
[ES:DI], AX
; przywrócenie offsetu p. obsługi przerwania add
DI, 2
; 2 bajty dalej
mov
[ES:DI], BX
; przywrócenie segmentu p. obsługi przerwania sti
; odblokowanie przerwań
;;; usunięcie TSR-a z pamięci ;;;
mov
ES, CX
; segment naszego TSR-a
mov
AH, 49h
; usuń jego segment z pamięci
int
21h
MACRO_printmes
mes_uninstalled
jmp
normal_exit
;;; Koniec deinstalacji ;;;
normal_exit:
mov
AX, 4C00h
int
21h
mes_check_znacznik db '<> Sprawdzam czy znacznik jest zainstalowany...',0Dh,0Ah,'$'
mes_installed
db '// Znacznik juz zapisany w pamieci (moze to nie nasz)',0Dh,0Ah,'$'
mes_no_znacznik
db '// Nie znaleziono znacznika. [] Przechodze do instalacji.',0Dh,0Ah,'$'
mes_bad_znacznik
db '// Offsety procedur niezgodne, znacznik nie nasz. [] Przechodze do instalacji.',0Dh,0Ah,'$'
mes_zn_isok
db '// Znacznik zainstalowany i jest nasz. [] Przechodze do deinstalacji.',0Dh,0Ah,'$'
mes_uninstalled
db '// Znacznik odinstalowany.',0Dh,0Ah,'$'
mes_isinstalled
db '// Zainstalowano znacznik.',0Dh,0Ah,'$'