0288 - MS-Dos - Od środka cz.8

W ostatniej części cyklu omówione zostaną dalsze wywołania systemowe oraz krótki program ilustrujący sposób wywołania programu z innego programu.

Funkcja 25h - umożliwia ustawienie wartości wektorów przerwań. W AL należy podać numer przerwania (00h do FFh), a w DS:DX - wartość wektora, czyli adres procedury obsługującej dane przerwanie. Wartość wektora można oczywiście wpisać bezpośrednio pod jeden z adresów od 0000:0000h do 0000:03FC. Nie jest to jednak zalecane, gdyż w przyszłych wersjach systemu (wielozadaniowych) bezpośrednia zmiana wartości wektorów w pamięci albo zostanie zablokowana przez mechanizmy systemu, albo zmodyfikowana bez wiedzy systemu mogłaby mieć fatalne skutki.

Funkcja 2Ah - pozwala odczytać datę. Po jej wykonaniu otrzymuje się :

  • w AL - dzień tygodnia (0 = niedziela, ... , 6 = sobota)
  • w DL - dzień miesiąca (1 ... 31)
  • w DH - miesiąc (1 ...12)
  • w CX - rok (1980 ... 2099)

Funkcja 2Bh - służy do ustawienia daty. Data zakodowana jest identycznie, jak przy funkcji 2Ah, tym razem jednak składniki daty są parametrami wejściowymi. Jeśli po powrocie z wywołania AL = 0, to podane parametry były prawidłowe, jeśli AL = FFh - nieprawidłowe.

Funkcja 2Ch - pozwala odczytać czas. Po jej wywołaniu otrzymuje się :

  • w CH - godziny (0 ... 23)
  • w CL - minuty (0 ... 59)
  • w DH - sekundy (0 ... 59)
  • w DL - setne sekundy (0 ... 99)

Mimo podawania przez procedurę setnych części sekundy, odczyt nie może być zrealizowany z dokładnością większą niż 1/20 sekundy - wynika to ze sposobu obsługi tej funkcji przez BIOS.

Funckcja 2Dh - służy do ustawienia czasu. Parametry wejściowe odpowiadają parametrom wyjściowym funkcji 2Ch, prawidłowość danych sygnalizowana jest identycznie jak przy funkcji 2Bh.

Funkcja 2Eh - ustawia lub zeruje wskaźnik weryfikacji zapisu na dysku. Normalnie wskaźnik ten jest wyzerowany, gdyż zapis na dysku jest stosunkowo niezawodny. Można jednak ustawić ten wskaźnik, wówczas po każdym zapisie na dysk następuje odczyt i sprawdzenie wartości kodu CRC (dane nie są porównywane). AL = 01h powoduje ustawienie wskaźnika weryfikacji, zaś AL = 00h - jego wyzerowanie.

Funkcja 30h - podaje numer wersji systemu operacyjnego. Po jej wywołaniu w AL przekazywany jest numer główny (2 dla wersji 2.X, 3 dla 3.X, 0 dla wersji wcześniejszych od 1.28), w AH - numer występujący po kropce (np. 08h dla MS-DOS 2.08).

Funkcja 33h - podaje albo zmienia sposób reakcji na naciśnięcie klawiszy CTRL-C. MS-DOS oferuje możliwość przerywania programu przez wciśnięcie CTRL-C. Normalnie reakcja na takie przerwanie możliwa jest tylko w czasie wykonywania funkcji o numerach 01h ... 0Ch. Funkcja 33h daje możliwość przerwania programu w czasie wykonywania każdej z funkcji systemowych, dla której nie jest to z założenia wykluczone. Jeśli chcemy użyć funkcji 06h lub 07h do odczytania znaku ASCII odpowiadającego CTRL-C, trzeba wpierw za pomocą funcji 33h wyłączyć reakcję na to przerwanie. Parametry wejściowe :

  • AL = 00h - pobranie chwilowego sposobu reakcji na CTRL-C
  • AL = 01h - zmiana sposobu reakcji na CTRL-C

Dla AL = 01h :

  • DL = 00h - wyłączenie reakcji na CTRL-C
  • DL = 01h - włączenie reakcji na CTRL-C

Parametry wyjściowe :

  • AL = FFh - zły kod w AL przy wywoływaniu funkcji
  • DL = 00h - reakcja na CTRL-C wyłączona
  • DL = 01h - reakcja na CTRL-C włączona

Funkcja 35h - podaje w ES:BX wartość wektora przerwania adres. Przed wywołanie w AL należy podać numer badanego przerwania.

Funkcja 36h - dostarcza informacji o organizacji dysku. W DL należy podać numer napędu (0 - aktualnie wybrany, 1 - A: itd.). W wyniku podaje :

  • w AX - liczba sektorów przypadających na jednostkę alokacji - klaster lub FFFFh - jeśli w DL podano zły numer napędu,
  • w BX - liczbę dostępnych klastrów,
  • w CX - liczbę bajtów w sektorze,
  • w DX - całkowitą liczbę klastrów na dysku.

Funkcja 38h - podaje lub ustala sposób przedstawiania takich informacji, jak: data i czas, symbol waluty, separator części dziesiętnej liczby, separator liczby tysięcy. Sposób przedstawiania zależy od kraju, w którym używa się komputera. Np. w Europie datę zapisuje się w postaci dd.mm.rr, a w USA - mm/dd/rr.

Po załadowaniu systemu MS-DOS sposób przedstawiania informacji wybrany jest zgodnie z wierszem COUNTRY kod znajdującym się w zbiorze CONFIG.SYS, przy czym kod ten jest trzycyfrowym międzynarodowym telefonicznym numerem kierunkowym danego kraju. Uwaga: podanie numeru kierunkowego Polski (48) nie przynosi oczekiwanych rezultatów. Niestety, nie wszystkie kraje są rozpoznawane przez DOS. Za pomocą funkcji 38h można sprawdzić, do jakiego kraju system jest w danej chwili dopasowany i na czym to dopasowanie polega.

Przy dopasowywaniu systemu do jakiegoś kraju w rejestrze DX trzeba umieścić FFFFh, a w rejestrze AL, kod tego kraju. Jeśli kod kraju jest większy od 254, to w AL należy umieścić FFh, a w BX 6-bitowy kod kraju. Jeśli po powrocie z wywołania funkcji ustawiony jest wskaźnik przeniesienia, to świadczy, że źle podano kod kraju (w AX znajduje się wówczas kod błędu).

Jeśli chcemy sprawdzić, do jakiego kraju system jest w danej chwili dopasowany, musimy w AL umieścić 00h, a w DS:DX adres 32-bajtowego bufora na informacje przekazywane zwrotnie przez system. Po powrocie z wywołania, jeśli wskaźnik przeniesienia jest zerem (brak błędu), to w rejestrze BX pojawi się kod kraju, a w 32-bajtowym buforze informacja o krajowym zapisu (tabela 1, albo tabela 2).

Tabela 1. Informacja opisująca sposób zapisu w danym kraju, zwracana przez funkcję 38h dla wersji MS-DOS3.0 i późniejszych.

Przemieszczenie względem DS:DX Zawartość
00h ... 01h

Format daty:

  • 0 - USA, miesiąc, dzień, rok,
  • 1 - EUROPA, dzień, miesiąc, rok
  • 2 - JAPONIA, rok, miesiąc, dzień
02h ... 06h Symbol waluty - ciąg ASCIIZ
07h ... 08h Separator tysięcy - ciąg ASCIIZ
09h ... 0Ah Separator części dziesiętnej - ciąg ASCIIZ
0Bh ... 0Ch Separator w dacie - ciąg ASCIIZ
0Dh ... 0Eh Separator w czasie - ciąg ASCIIZ
0Fh

Bajt opisujący sposób zapisu sum pieniężnych

  • Bit 0 = 0 - symbol waluty poprzedza wartość
  • Bit 0 = 1 - symbol waluty za wartością
  • Bit 1 - liczba spacji pomiędzy symbolem waluty i wartością (0 lub 1)
10h liczba znaczących cyfr dziesiętnych w sumach pieniężnych
11h

Bajt opisujący postać czasu :

  • Bit 0 = 0 - zegar 12-godzinny
  • Bit 0 = 1 - zegar 24-godzinny
12h ... 15h Adres (przemieszczenie, segment) procedury, która na podstawie kodu małej litery występującej w danym języku (80h ... FFh) podaje kod odpowiadającej jej dużej litery. Kod małej litery trzeba podać w AL, kod dużej litery zwracany jest też w AL.
16h ... 17h Separator pozycji na liście - ciąg ASCIIZ
18h ... 1Fh Zarezerwowane

Tabela 2. Informacja opisująca sposób zapisu w danym kraju, zwracana przez funkcję 38h, dla wersji MS-DOS wcześniejszych od 3.0

Przemieszczenie względem DS:DX Zawartość
00h ... 01h Format daty
02h ... 03h Symbol waluty - ciąg ASCIIZ
04h ... 05h Separator tysięcy - ciąg ASCIIZ
06h ... 07h Separator części dziesiętnej - ciąg ASCIIZ
08h ... 1Fh Nieużywane

Począwszy od wersji MS-DOS 3.0 można też sprawdzić, na czym polega sposób dopasowania systemu do jakiegoś kraju, bez zmieniania aktualnego dopasowania. W tym celu w AL należy umieścić kod tego kraju (lub w AL liczbę 255, zaś 16-bitowy kod kraju w BX). Podobnie jak poprzednio, adres bufora na informacje zwrotne trzeba podać w DS:DX, zaś zwracane w tym buforze informacje o sposobie zapisu są zgodne z tabelą 2.

Uwaga : wersje MS-DOS wcześniejsze od 3.0 nie pozwalają sprawdzić sposobu dopasowania do dowolnego kraju (przy sprawdzaniu dopasowania AL musi być równe 0), a ponadto możliwe jest dopasowanie się tylko do kilku krajów. Informacje o sposobie zapisu dla wcześniejszych od 3.0 są przedstawiane inaczej (tabela 1) niż dla wersji 3.0 i późniejszych (tabela 2). Wersja 3.0 i późniejsze dopuszczają istnienie symbolu waluty składającego się z jednego do 4 znaków.

Opisana funkcja umożliwia konstruowanie programów wyprowadzających wyniki w postaci uzależnionej od kraju, w którym pracuje komputer.

Funkcja 57h - podaje lub ustawia datę i czas ostatniego zapisu do już istniejącego pliku. Przy wywołaniu funkcji w AL należy umieścić 00h w odczytywaniu lub 01h w ustawianiu daty i czasu, w BX - nr kanału logicznego przyporządkowanego plikowi (handle). Jeśli data i czas są ustawiane (AL = 01h), w CX trzeba podać czas, a w DX - datę (w postaci zakodowanej, opisanej wcześniej, z tym, że w przypadku rejestrów bajty są zamienione, to znaczy DH zawiera mniej znaczącą część daty itd.). W przypadku odpytywania (AL = 0) po powrocie z procedury, w podobny sposób w rejestrze DX zakodowana jest data, a w CX - czas.

Uruchomienie programu z innego programu typu COM

Przyjrzyjmy się bliżej funkcji 4Bh. Umożliwia ona załadowanie z dysku do pamięci i uruchomienie programu. W szczególności programem tym może być standardowy interpreter poleceń - COMMAND.COM. Podając mu jako parametr ciąg znaków ASCII będących poleceniem, można spowodować wykonanie dowolnego polecenia wewnętrznego (np. DIR) lub zewnętrznego (np. DSKCHK), bądź poleceń znajdujących się w pliku typu .BAT. Przykład programu ładującego do pamięci dodatkową kopię interpretera poleceń pokazano na wydruku 1. Najpierw wywoływana jest funkcja 4Ah, zmieniająca rezerwację pamięci dla programu.

MS-DOS musi zawsze wiedzieć, jakie obszary pamięci przydzielone są programowi (w związku z tym system nie ma prawa w nich nic zapisywać) oraz jakie obszary są pozostawione do dyspozycji systemu (w związku z tym program nie ma prawa w nich nic zapisywać). Po załadowaniu i uruchomieniu programu cała dostępna pamięć (niezależnie od długości programu) jest przydzielona programowi. System nie może więc załadować żadnego innego programu. Najpierw trzeba zwolnić część pamięci. Służy do tego funkcja 4Ah, dla której w ES należy podać adres segmentowy bloku pamięci, w którym zaczyna się pierwszy program, a w BX - liczbę bloków, jaką należy pierwszemu programowi przyporządkować. Przyporządkowując pierwszemu programowi liczbę bloków, którą rzeczywiście wykorzystuje, zwalniamy pozostałą część pamięci. Dopiero po takiej operacji można żądać załadowania innego programu.

W opisywanym programie zmieniono sposób reakcji na wciśnięcie klawiszy CTRL+C. Normalnie po ich wciśnięciu nastąpiłoby przerwanie programu i powrót do systemu operacyjnego. Tutaj zastosowano zmianę wartości wektora odpowiadającego wciśnięciu CTRL+C. Nowa wartość jest adresem procedury, która porządkuje stos i wraca do głównej pętli programowej. Po wciśnięciu CTRL+C zamiast powrotu do systemu operacyjnego następuje więc powrót do wyświetlania menu. Do systemu operacyjnego można wrócić (poprzez jego powtórne załadowanie) po wybraniu z menu pozycji 2.

 Wydruk 1.

 NAME INVOKE

 ; Program ten najpierw wyświetla menu, z którego użytkownik może wybrać 3
 ; opcje. Pierwsza opcja umożliwia załadowanie standardowego interpretera 
 ; poleceń i wykonanie polecenia wewnętrznego DIR; druga opcja powoduje
 ; załadowanie tego samego interpretera i wykonanie polecenia zewnętrznego
 ; CHKDSK; trzecia opcja powoduje wykonanie sekwencji instrukcji
 ; powodujących restart systemu operacyjnego 
 ; 
 ; napęd systemowy (zawierający COMMAND.COM) powinien być napędem C: jeśli 
 ; tak nie jest, to w ciągu znaków zdefiniowanych za etykietą PROGRAM 
 ; należy odpowiednio zmienić literę dysku. Tekst źródłowy należy wpisać do 
 ; zbioru invoke.asm
 ; 
 ; Assemblacja            : masm invoke
 ; Konsolidacja           : link invoke
 ; Utworzenie zbioru .com : exe2com invoke invoke.com 
 ; Wykonanie              : invoke

 CODE SEGMENT WORD 'CODE'
 ASSUME CS:CODE, DS:CODE, ES:CODE, SS:CODE

 BUFFERED_INPUT  EQU  0Ah  ; DOS: odczyt wiersza wprowadz. z klaw. 
 CHECK_KEY       EQU  0Bh  ; DOS: sprawdzenie bufora klawiatury 
 CONSOL_INPUT    EQU  00h  ; kanał wprowadzania z klawiatury 
 CONSOL_OUTPUT   EQU  0ah  ; kanał wyprowadzania na klawiaturę 
 CTRL_C_INT      EQU  23h  ; nr. wektora przerwania CTRL-C 
 CR              EQU  0Dh  ; znak ASCII: powrót karetki 
 DOS             EQU  21h  ; przerwanie obsługujące funkcje DOS 
 EXECUTE         EQU  4Bh  ; DOS: załad. i wykonanie programu z dysku 
 FLUSH_BUFFER    EQU  0Ch  ; DOS: zerowanie bufora klawiatury 
 LF              EQU  0Ah  ; znak ASCII: nowy wiersz 
 SET_BLOCK       EQU  4Ah  ; DOS: zmiana wielk. pamięci programu 
 SET_VECTOR      EQU  25h  ; DOS: zmiana wektora przerwania 
 TERMINATE       EQU  4Ch  ; DOS: powrót do systemu operacyjnego 
 WRITE           EQU  40h  ; DOS: wyprow. do zbioru lub urządz.

 ; KOD ****************************************************************
 ORG 100h

 INVOKE_MOD:
   MOV  SP, OFFSET  STACK_END  ; przesunięcie stosu z końca 
                               ; segmentu na koniec programu
   MOV  AX, CS                 ; wpisanie adresu segmentowego 
   MOV  PARAMETER[4], AX       ; w odpowiednie miejsca bloku 
   MOV  PARAMETER[8], AX       ; parametrów dla funkcji 4Bh 
   MOV  PARAMETER[12], AX 
   MOV  BX, OFFSET STACK_END 
   MOV  CX, 4                  ; obliczenie liczby kompletnych 
   SHR  BX, CL                 ; 16-najtowych bloków w programie 
   INC  BX                     ; wzgl. ostatn niepełnego bloku 
   MOV  AH, SET_BLOCK          ; DOS: zmiana rezerwacji w pamię. 
   INT  DOS
   JNC  OUTPUT_MENU            ; jeśli błąd, to go obsłuż 
   JMP  ERROR

 OUTPUT_MENU:
   MOV  AH, SET_VECTOR         ; DOS: zał. wektora obsł. przerw
   MOV  AL, CTRL_C_INT
   MOV  DX, OFFSET CTRL_C_SERVICE ; zał. nowego adr. wektorowego
   INT  DOS
   MOV  AH, WRITE              ; DOS: wyprow. ciągu zn. na kons.
   MOV  BX, CONSOL_OUTPUT      ; standard. kanał wypr. na kons.
   INT  DOS
   JC   ERROR

 FLUSH_AGAIN:
   MOV  AH,FLUSH_BUFFER        ; zerow. bufora wprowadz. z kons.
   MOV  AL, FFh                ; i nic więcej
   INT  DOS 
   CMP  AL, 0
   JNZ  FLUSH_AGAIN            ; prób. znowu gdy buf. nie pusty

 WAIT_LOOP:
   MOV  AH, CHECK_KEY          ; DOS: czekaj na wciś. dow. klaw.
   INT  DOS 
   CMP  AL, 0                  ; czy wciśnięto znak?
   JZ   WAIT_LOOP              ; jeśli nie, to pętla oczek.
   RET
 WAIT  ENDP

 ; Poniższa procedura jest wykonywana po wciśnięciu CTRL_C; ustawia ona 
 ; wskaźnik stosu na wartość, jaką posiadał po zainicjowaniu programu i
 ; wymusza skok do etykiety OUTPUT_MENUCTRL_C_SERVICE

   MOV  SP, OFFSET STACK_END
   JMP  OUTPUT_MENU

 ; DANE *****************************************************************

 FCB1         DB 12 DUP(0)  ; wymagany, lecz nieużywany FCB
 FCB2         DB 12 DUP(0)  ; wymagany, lecz nieużywany FCB
 INPUT_BUFFER DB 128 DUP(0) ; bufor używany przez funkcję 0Ah
 STORE_SP     DW ?          ; bufor przech. wart. wsk. stosu

 ; tabl. skoków do procedur odpowiadających poz. menu
 JUMP_TABLE DW DIR_OUTPUT, CHECK_DISK, REBOOT

 ; Text menu wyświetlanego na ekranie
 MENU_TEXT DB 'Masz do wyboru następujące opcje:'
           DB CR,LF,LF
           DB '1 Wyświetlenie aktualnego katalogu',CR,LF
           DB '2 Test aktualnie wybranego dysku',CR,LF
           DB '3 Zakończenie tego programu',CR,LF,LF
           DB 'Twój wybór:'
 MENU_END  DB 0

 ; Deklaracje opisujące blok parametrów dla funkcji 4Bh
 PARAMETER DW 0                  ; 0 oznacza, że otoczenie
                                 ; (ENVIRONMENT) nie ulega zmianie
           DW ?.?                ; adr. (offset, segment) do ciągu 
                                 ; ASCIIZ opisuj. ładowany program 
           DW OFFSET FCB1,?      ; adres FCB1 (nieużywanego) 
           DW OFFSET FCB2,?      ; adres FCB2 (nieużywanego)
 PROGRAM   DB 'C:COMMAND.COM',0  ; nazwa ładowanego programu 

 ; Wiersze poleceń przekazywanych wywoływanemu programowi.
 ; Zawierają: liczbę znaków (bez CR), ciąg znaków, CR
 PROG_1_COMMAND  DB  OFFSET PROG_2_COMMAND - $ -2,' /C DIR', CR
 PROG_2_COMMAND  DB  OFFSET END_COMMAND - $ - 2,' /C CHKDSK/F',CR 
 END_COMMAND     DB  0
 REBOOT_ADDRESS  DW  0000h,FFFFh ; wektor inicj. procesora 8086/8088 
 WAIT_TEXT       DB  CR,LF,LF,'Dalej dowolny klawisz',CR,LF 
 WAIT_END        DB  0

 ; STOS ************************************************************
 DB 40 DUP(?)
 STACKEND:
 CODE ENDS

 END INVOKE MOD
 
   MOV  CX, OFFSET MENU_END - OFFSET MENU_TEXT ; dł. wypr. tekstu
   MOV  DX, OFFSET MENU_TEXT     ; adres ciągu znaków
   INT  DOS
   JC   ERROR                    ; jeśli był błąd, to go obsłuż 
   MOV  AH, FLUSH_BUFFER         ; DOS: zerow bufora wyprow. kons.
   MOV  AL, BUFFERED_INPUT       ; DOS: buf. czytanie z konsoli
   MOV  DX, OFFSET INPUT_BUFFER  ; ustalenie adresu bufora kons.
   MOV  BYTE PTR INPUT_BUFFER, 2 ; odczyt 2 zn (znak wyboru + CR)
   INT DOS
   MOV  AL, [INPUT_BUFFER+1]
   XOR  AH, AH
   CMP  AX, 1                    ; Czy wprow. dokł. jeden znak ?
   JNZ  OUTPUT_MENU              ; jeśli nie, to ponownie
   MOV  BL, [INPUT_BUFFER+2]     ; 
   XOR  BH, BH                   ;
   CMP  BX, '3'                  ; Czy wprow. znaki 1, 2 albo 3 ?
   JG   OUTPUT_MENU              ; nie, znak o zbyt dużym kodzie 
   CMP  BX, '1'
   JL   OUTPUT_MENU              ; nie, znak o zbyt małym kodzie   

opr. Zbigniew Pojmański.
Mikrolkan Marzec 1988r.