IO_CON.ASM
Ovladač
konzoly
V souboru IO_CON.ASM se
nachází funkce pro obsluhu konzoly - vstup znaků z klávesnice
a zobrazení na displeji. Nejdříve si popíšeme funkce pro
výstup znaků na displej.
Funkce pro práci s displejem
používají funkci Int10. Tato funkce nedělá
nic jiného než že uchová "málo časté" registry
jako SI, DI atd. Některé starší BIOS (buď ze základní
desky nebo z grafické karty) se o uchování těchto registrů
nestarají a navracejí náhodná data v nich, což by nám mohlo
způsobit nemalé potíže.
Při inicializaci systému je
nainstalována obsluha přerušení Int 29h, funkce MyInt29.
Přerušení Int 29h je v systému DOS používáno k zobrazení
znaků na displej v případech, kdy je požadován skutečný
výstup na displej bez možnosti přesměrování výstupu např.
do souboru. Přeinstalováním obsluhy přerušení Int 29h je
možné změnit chování ovladače konzoly - např. možnost
změny barvy textu. Funkce používá funkci 0eh přerušení Int
10h. Jedná se o konzolový výstup - text je vypisován na
aktuální pozici kurzoru. Kurzor je automaticky posouván.
Dosáhne-li konce řádku, přejde na začátek následujícího
řádku. Pokud by se nacházel další řádek již mimo spodní
okraj displeje, obraz se posune o řádek nahoru. Tato funkce je
podporovaná všemi textovými a zpravidla i všemi grafickými
videomódy. Barva textu je nastavena na bílou na černém
pozadí.
Funkce DispText
je pomocná funkce používaná vnitřně systémem. Umožňuje
zobrazení textu přímo na displej - např. chybová hlášení
systému. Text musí být zakončen binární nulou.
Funkce CONWData
je obsluha zápisu dat na zařízení. Zapisují se data o dané
délce z bufferu, jehož ukazatel je získán z paketu požadavku
na zařízení. K výstupu znaků je využito přerušení Int
29h.
; ============================================================================
;
; LT-DOS - Console device
;
; ============================================================================
BREAK EQU 3 ; break character (Ctrl+C)
PRINT EQU 10h ; print character (Ctrl+P)
CTRL_PRINTSCR EQU 7200h ; Ctrl+Print Screen key
; ----------------------------------------------------------------------------
; Device service tables
; ----------------------------------------------------------------------------
; ------------- CON service table
FNCCON: db 11 ; number of functions
dw DevIntOK ; 0 init
dw DevIntOK ; 1 media check
dw DevIntOK ; 2 build BPB
dw DevIntInv ; 3 IOCTL input
dw CONRData ; 4 read data
dw CONRTest ; 5 test read
dw CONRStatus ; 6 input status
dw CONRFlush ; 7 flush input
dw CONWData ; 8 write data
dw CONWData ; 9 write with verify
dw DevIntOK ; 10 output status
; ----------------------------------------------------------------------------
; Device interrupt service - CON read data
; ----------------------------------------------------------------------------
; INPUT: CX = number of characters to read
; ES:DI = data buffer
; DS = data segment
; OUTPUT: AX = status word
; ----------------------------------------------------------------------------
CONRData: jcxz CONRData4 ; there are no data to read
CONRData2: call CONRChar ; read character from keyboard
cld ; direction up
stosb ; store character to buffer
loop CONRData2 ; read next character
CONRData4: mov ah,S_DONE ; status OK
ret ; return from interrupt
; ----------------------------------------------------------------------------
; Device interrupt service - CON test read
; ----------------------------------------------------------------------------
; INPUT: DS = data segment
; OUTPUT: AX = status word
; DESTROYS: DS
; ----------------------------------------------------------------------------
CONRTest: call CONRTestChar ; test character
jc CONRTest2 ; no character ready
lds bx,[Strat] ; DS:BX <- strategy address
mov [bx+13],al ; output character
CONRTestOK: mov ah,S_DONE ; status OK
ret
CONRTest2: mov ah,S_DONE+S_BUSY ; status - device not ready
ret
; ----------------------------------------------------------------------------
; Device interrupt service - CON input status
; ----------------------------------------------------------------------------
; INPUT: DS = data segment
; OUTPUT: AX = status word
; ----------------------------------------------------------------------------
CONRStatus: call CONRTestChar ; test character
jc CONRTest2 ; no character ready
jmp short CONRTestOK; status OK
; ----------------------------------------------------------------------------
; Device interrupt service - CON flush input
; ----------------------------------------------------------------------------
; INPUT: DS = data segment
; OUTPUT: AX = status word
; ----------------------------------------------------------------------------
CONRFlush: call CONRTestChar ; any character ready?
jc CONRTestOK ; no character
call CONRChar ; input chracter from keyboard
jmp short CONRFlush ; next character
; ----------------------------------------------------------------------------
; Device interrupt service - CON write data
; ----------------------------------------------------------------------------
; INPUT: CX = number of characters to write
; ES:DI = data buffer
; DS = data segment
; OUTPUT: AX = status word
; ----------------------------------------------------------------------------
CONWData: jcxz CONWData4 ; there are no data to write
CONWData2: mov al,[es:di] ; AL <- character to display
inc di ; increase pointer
int 29h ; display character
loop CONWData2 ; write next character
CONWData4: mov ah,S_DONE ; status OK
ret ; return from interrupt
; ----------------------------------------------------------------------------
; Correct character from keyboard
; ----------------------------------------------------------------------------
; INPUT/OUTPUT: AX = character from keyboard
; ----------------------------------------------------------------------------
; ------------- Substitute extended scan code 0eh with 00h
CONRCorr: cmp al,0e0h ; is it extended scan code?
jne CONRCorr2 ; it is not extended scan code
or ah,ah ; has this character scan code?
jz CONRCorr2 ; this character has no scan code
mov al,0 ; substitute it with 0 code
; ------------- Substitute Ctrl+PrintScreen key with Ctrl+P
CONRCorr2: cmp ax,CTRL_PRINTSCR; is it Ctrl+Print Screen?
jne CONRCorr4 ; it is not Ctrl+Print Screen
mov al,PRINT ; substitute it with Ctrl+P
CONRCorr4: ret
; ----------------------------------------------------------------------------
; CON read character
; ----------------------------------------------------------------------------
; INPUT: DS = data segment
; OUTPUT: AL = character (0=scan code follows)
; DESTROYS: AH
; ----------------------------------------------------------------------------
; ------------- Read old character from buffer
CONRChar: mov al,0 ; AL <- new invalid old character
xchg al,[OldChar] ; AL <- old character
or al,al ; is it valid character?
jnz CONRChar8 ; it is valid character
; ------------- Read character from keyboard
mov ah,[KeyInCode] ; AH <- function code
int 16h ; read character from keyboard
; ------------- Skip Ctrl+Break code
or ax,ax ; is it Ctrl+Break?
jz CONRChar ; it is Ctrl+Break, ignore it
; ------------- Correct character from keyboard
call CONRCorr ; correct character from keyboard
; ------------- Store scan code
or al,al ; is it special key?
jnz CONRChar8 ; it is not special key
mov [OldChar],ah ; store scan code
CONRChar8: ret
; ----------------------------------------------------------------------------
; CON test character
; ----------------------------------------------------------------------------
; INPUT: DS = data segment
; OUTPUT: AL = character (0=scan code follows)
; CY = no character
; DESTROYS: AH
; ----------------------------------------------------------------------------
; ------------- Read old character from buffer
CONRTestChar: mov al,[OldChar] ; AL <- old character
or al,al ; is it valid character?
jnz CONRTestChar8 ; it is valid character
; ------------- Read character from keyboard
mov ah,[KeyTestCode]; AH <- function code
int 16h ; test character from keyboard
stc ; flag - no character is ready
jz CONRTestChar9 ; no character
; ------------- Skip Ctrl+Break code
or ax,ax ; is it Ctrl+Break?
jnz CONRTestChar2 ; it is not Ctrl+Break
mov ah,[KeyInCode] ; AH <- function code
int 16h ; read character from keyboard
jmp short CONRTestChar ; next key
; ------------- Correct character from keyboard
CONRTestChar2: call CONRCorr ; correct character from keyboard
CONRTestChar8: clc ; flag - a character is ready
CONRTestChar9: ret
; ----------------------------------------------------------------------------
; Internal display ASCIIZ text
; ----------------------------------------------------------------------------
; INPUT: DS:SI = text to display, terminated with zero
; DESTROY: AX
; ----------------------------------------------------------------------------
DispText: cld ; direction up
DispText2: lodsb ; load character to display
cmp al,0 ; is it end of text?
je DispText3 ; it is end of text
int 29h ; display character
jmp short DispText2 ; display next character
DispText3: ret
; ----------------------------------------------------------------------------
; Interrupt 29h handler (display character)
; ----------------------------------------------------------------------------
; INPUT: AL = character to display
; ----------------------------------------------------------------------------
; ------------- Push registers
MyInt29: push ax ; push AX
push bx ; push BX
; ------------- Display character
mov ah,0eh ; AH <- 0Eh function code
mov bx,7 ; BL <- color of text, BH <- page 0
call Int10 ; call Int 10h
; ------------- Pop registers
pop bx ; pop BX
pop ax ; pop AX
iret
; ----------------------------------------------------------------------------
; Call Int 10h interrupt with saving registers
; ----------------------------------------------------------------------------
; Notes: Some BIOSes destroy some registers, so we call it with saving them.
; ----------------------------------------------------------------------------
; ------------- Push registers
Int10: pushf ; push flags
push si ; push SI
push di ; push DI
push bp ; push BP
push ds ; push DS
push es ; push ES
; ------------- Call INT 10h
int 10h ; call INT 10h
; ------------- Pop registers
pop es ; pop ES
pop ds ; pop DS
pop bp ; pop BP
pop di ; pop DI
pop si ; pop SI
popf ; pop flags
ret
; ----------------------------------------------------------------------------
; Interrupt 1Bh handler (program break)
; ----------------------------------------------------------------------------
MyInt1B: mov byte [cs:OldChar],BREAK ; break character
MyInt01:
MyInt03:
MyInt04:
MyInt23:
MyInt24:
MyInt28:
iret
; ----------------------------------------------------------------------------
; Interrupt 10h handler (video services)
; ----------------------------------------------------------------------------
MyInt10: jmp far [cs:OldInt10] ; jump to old Int 10h routine
; ----------------------------------------------------------------------------
; Data
; ----------------------------------------------------------------------------
OldChar: db 0 ; old character from console (0=none)
KeyInCode: db 0 ; func. code for input (0 or 10h)
KeyTestCode: db 1 ; func. code for test (1 or 11h)
|
Ke vstup dat z konzoly je používáno BIOS
přerušení Int 16h. Používá se buď funkce 0 (vstup ze
standardní klávesnice) nebo funkce 10h (vstup z rozšířené
klávesnice, umožňuje vstup i speciálních kláves jako např.
F12). Rozlišení typu klávesnice se provádí při inicializaci
systému, do proměnné KeyInCode je uložen
kód funkce pro vstup z klávesnice. Podobně je připravena
funkce 1 nebo 11h pro test klávesnice, proměnná KeyTestCode.
Klávesy můžeme rozdělit na 2 typy -
znakové klávesy a řídicí klávesy. Znakové klávesy
generují tisknutelný znak (písmeno, číslice). Řídicí
klávesy nemají žádnou znakovou (ASCII) hodnotu, umožňují
řídit chování programu (šipky, Insert, F5). Některé
klávesy generují platné ASCII kódy, které nejsou sice
zobrazitelné, ale slouží k řízení konzolového výstupu a
proto je řadíme též mezi znakové klávesy - Enter, Esc, Back
Space, Tab.
BIOS funkce pro čtení kód klávesy
navrací kód klávesy ve tvaru 16-bitového slova. Nižší bajt
je ASCII hodnota klávesy (pokud se jedná o znakovou klávesu),
vyšší hodnota je tzv. scan kód klávesy, což je pořadové
číslo klávesy na klávesnici. Řídicí klávesy obsahují
namísto ASCII znaku v nižším bajtu hodnotu 0, čímž jsou
rozlišeny od znakových kláves, identifikace klávesy se
provásí vyšším bajtem (scan kódem).
Funkce ovladače pro čtení kódu klávesy
navracejí ve výstupním řetězci posloupnost jednotlivých
bajtů (znaků). Je-li stisknuta znaková klávesa, je navrácen
přímo ASCII kód klávesy. Je-li stisknuta řídicí klávesa,
je navrácen nejdříve bajt s hodnotou 0 jako prefix,
následující načtený bajt bude scan kód klávesy.
Následující bajt ke čtení z ovladače je uchován v
proměnné OldChar.
Vstup znaků z klávesnice probíhá
funkcí CONRChar. Je-li v proměnné OldChar
uchován od minule scan kód klávesy, je obsah proměnné
vynulován a scan kód navrácen bez čekání na další
klávesu. Není-li uchován předešlý bajt, bude se čekat na
stisk klávesy. Případně navrácené slovo s hodnotou 0 je
ignorováno a je provedeno čekání na novou klávesu. Tento
kód je navrácen v případě stisku kláves Ctrl+Break, tj.
přerušení programu. Tato kombinace kláves je obsloužena
zvláštním přerušením MyIntBreak, které do
proměnné OldChar uloží znak s hodnotou 3, což je kód
shodný s kombinací kláves Ctrl+C, která se také používá k
přerušení programu.
Kód klávesy je zkorigován funkcí CONRCorr.
Jedná-li se o řídicí klávesu rozšířené klávesnice,
která navrací v nižším bajtu namísto 0 hodnotu 0e0h, bude
nižší bajt nahrazen hodnotou 0 a tím převeden na klasickou
interpretaci kódu. Konverze se neprovede v případě, že
vyšší bajt je 0. Tento případ nastane, pokud na numerické
klávesnici s využitím levého Alt zadáme číselnou hodnotu
znaku 224. Klávesa PrintScreen je nahrazena kódem kláves
Ctrl+P, v systému DOS slouží k zapínání a vypínání
příznaku tzv. "hardcopy" obrazovky, výstup probíhá
nejen na displej, ale současně i na tiskárnu. Na závěr
funkce CONRChar je v případě řídicí klávesy uchován její
scan kód do bufferu OldChar pro příští načítání.
Funkce CONRData je
načítání dat z konzoly. Ke vstupu dat z klávesnice je
používána funkce CONRChar. Funkce CONRTest
provádí nedestruktivní vstup z klávesnice. Pomocí funkce CONRTestChar
je načten kód připravené klávesy, klávesa přitom není
zrušena z bufferu kláves. Obsluha funkce je obdobná jako u
funkce CONRChar. Funkce CONRFlush je obsluha
vyprázdnění vstupního bufferu klávesnice. Funkce opakovaně
testuje, zda je připraven nějaký znak z klávesnice a pokud
ano, tento znak načte a tím dojde k jeho zrušení.
Zpět na stránku
systému LT-DOS