Программирование Базы данных
| |
Следующая
программа, которую мы вам представим,
выполняет все необходимые действия,
связанные с обработкой прерываний и
исключений в защищённом режиме. Наша первая программа (листинг
1) работала в защищённом режиме с
запрещёнными прерываниями. Она как бы
пролетала через неизведанное с завязанными
глазами и сразу же возвращалась в хорошо
освоенный реальный режим. Теперь же программа имеет
возможность "осмотреться" и может
реагировать на такие события, как
прерывания от клавиатуры и таймера. Кроме
того, в случае возникновения исключений вам
будет выдана некоторая диагностика,
включающая код исключения, код ошибки и
содержимое регистров процессора на момент
возникновения исключения. Таблица IDT содержит
дескрипторы для обработчиков исключений и
аппаратных прерываний. Вентили исключений
содержат ссылки на программы с именами
exc_00...exc_1F. Вслед за вентилями исключений в
таблице IDT следуют вентили аппаратных
прерываний. Из них задействованы только IRQ0
и IRQ1 - прерывания от таймера и клавиатуры. Те аппаратные прерывания,
которые нас не интересуют, приводят к
выдаче в контроллеры прерывания команды
конца прерывания. Для таких прерываний
предусмотрены заглушки - процедуры с
именами dummy_iret0 и dummy_iret1. Первая заглушка
относится к первому контроллеру прерывания,
вторая - ко второму. Вслед за вентилями
аппаратных прерываний мы поместили в
таблицу IDT вентиль программного прерывания
int 30h, предназначенного для организации
взаимодействия с клавиатурой на манер
прерывания BIOS INT 16h. При выдаче прерывания int
30h вызывается процедура с именем Int_30h_Entry. После запуска программа
переходит в защищённый режим и
размаскирует прерывания от таймера и
клавиатуры. Далее она вызывает в цикле
прерывание int 30h (ввод символа с клавиатуры),
и выводит на экран скан-код нажатой клавиши
и состояние переключающих клавиш (таких,
как CapsLock, Ins, и т.д.). Если окажется нажатой
клавиша ESC, программа выходит из цикла.
Далее в тексте программы следует ряд команд,
закрытых символом комментария. Эти команды
приводят к исключению. Вы можете
попробовать их работу, удалив
соответствующий символ комментария. При
возникновении исключения на экран будет
выведена диагностика, о чём позаботится
обработчик исключений. Перед завершением работы
программа устанавливает реальный режим
процессора и стирает экран. Обработчик аппаратного
прерывания клавиатуры - процедура с именем
Keyb_int. После прихода прерывания она выдаёт
короткий звуковой сигнал, считывает и
анализирует скан-код клавиши, вызвавшей
прерывание. Скан-коды классифицируются на
обычные и расширенные (для 101-клавишной
клавиатуры). В отличие от прерывания BIOS INT 16h,
мы для простоты не стали реализовывать
очередь, а ограничились записью
полученного скан-кода в глобальную ячейку
памяти key_code. Причём прерывания, возникающие
при отпускании клавиш игнорируются. Запись скан-кода в ячейку
key_code выполняет процедура Keyb_PutQ. После
записи эта процедура устанавливает признак
того, что была нажата клавиша - записывает
значение 0FFh в глобальную переменную key_flag. Программное прерывание int
30h опрашивает состояние key_flag. Если этот флаг
оказывается установленным, он сбрасывается,
вслед за чем обработчик int 30h записывает в
регистр AX скан-код нажатой клавиши, в
регистр BX - состояние переключающих клавиш
на момент нажатия клавиши, код которой
передан в регистре AX. Для того чтобы не
загромождать программу второстепенными
деталями, мы не стали перекодировать скан-код
в код ASCII, вы сможете при необходимости
сделать это сами. Обработчик прерываний
таймера - процедура с именем Timer_int. Эта
процедура примерно раз в секунду выдаёт
звуковой сигнал, чем её действия и
ограничиваются. Процедура rdump выводит на
экран содержимое регистров процессора и
может быть использована для отладки
программы. Обратите внимание на
использованный способ возврата процессора
в реальный режим:
DATASEG;
Пустой дескриптор для выполнения возврата
;процессора в реальный режим через перевод
;его в состояние отключения.
null_idt idt_struc <> CODESEGPROC
set_rmode NEAR
mov [real_sp],sp; Переводим процессор
в состояние отключения,
;это эквивалентно аппаратному сбросу, но;
выполняется быстрее.
;Сначала мы загружаем IDTR нулями, затем;
выдаём команду прерывания.
lidt [FWORD
null_idt] int
3rwait: hlt
jmp rwaitLABEL shutdown_return FAR
Регистр IDTR
загружается нулями, следовательно, предел
дескрипторной таблицы прерываний равен
нулю. После этого мы выдаём команду
программного прерывания. При обработке
прерывания возникает исключение, так как
регистр IDTR инициализирован неправильно. Но
это исключение не может быть обработано по
той же причине, что вызывает новое
исключение. Теперь процессор переходит уже
в состояние отключения и выполняет рестарт
в реальном режиме. Что нам и требовалось
получить! Для удобства мы вынесли в
отдельный файл tiny-os.inc все необходимые
определения структур данных и констант (листинг
2). Файл tiny-os.asm содержит саму программу,
текст которой приведён в листинге 3.
Листинг 2.
Структуры данных и константы.
----------------------------------------------------------
; --------------------------------------------------------
; Определения структур данных и констант
; --------------------------------------------------------
STRUC desc_struc ; структура дескриптора
limit dw 0 ; предел
base_l dw 0 ; мл. слово
физического адреса
base_h db 0 ; ст. байт
физического адреса
access db 0 ; байт доступа
rsrv dw 0 ;
зарезервировано
ENDS desc_struc
STRUC idt_struc ; вентиль
прерывания
destoff dw 0 ; смещение
обработчика
destsel dw 0 ; селектор
обработчика
nparams db 0 ; кол-во
параметров
assess db 0 ; байт
доступа
rsrv dw 0 ;
зарезервировано
ENDS idt_struc
STRUC idtr_struc ; регистр IDTR
idt_lim dw 0 ; предел IDT
idt_l dw 0 ; мл.
слово физического адреса
idt_h db 0 ; ст.
байт физического адреса
rsrv db 0 ;
зарезервировано
ENDS idtr_struc
;---------------------------------------------------------
; Биты байта доступа
ACC_PRESENT EQU 10000000b ; сегмент есть в
памяти
ACC_CSEG EQU 00011000b ; сегмент
кода
ACC_DSEG EQU 00010000b ; сегмент
данных
ACC_EXPDOWN EQU 00000100b ; сегмент
расширяется вниз
ACC_CONFORM EQU 00000100b ; согласованный
сегмент
ACC_DATAWR EQU 00000010b ; разрешена
запись
ACC_INT_GATE EQU 00000110b ; вентиль прерывания
ACC_TRAP_GATE EQU 00000111b ; вентиль исключения
;---------------------------------------------------------
; Типы сегментов
; сегмент данных
DATA_ACC = ACC_PRESENT OR ACC_DSEG OR ACC_DATAWR
; сегмент кода
CODE_ACC = ACC_PRESENT OR ACC_CSEG OR ACC_CONFORM
; сегмент стека
STACK_ACC = ACC_PRESENT OR ACC_DSEG OR ACC_DATAWR OR
ACC_EXPDOWN
; байт доступа сегмента таблицы IDT
IDT_ACC = DATA_ACC
; байт доступа вентиля прерывания
INT_ACC = ACC_PRESENT OR ACC_INT_GATE
; байт доступа вентиля исключения
TRAP_ACC = ACC_PRESENT OR ACC_TRAP_GATE
;---------------------------------------------------------
; Константы
STACK_SIZE EQU 0400 ; размер стека
B_DATA_SIZE EQU 0300 ; размер области данных BIOS
B_DATA_ADDR EQU 0400 ; адрес области данных BIOS
MONO_SEG EQU 0b000 ; сегмент
видеопамяти
; монохромного видеоадаптера
COLOR_SEG EQU 0b800 ; сегмент видеопамяти
; цветного видеоадаптера
CRT_SIZE EQU 4000 ; размер сегмента
видеопамяти
; цветного видеоадаптера
MONO_SIZE EQU 1000 ; размер сегмента
видеопамяти
; монохромного видеоадаптера
CRT_LOW EQU 8000 ; мл. байт
физического адреса
; сегмента видеопамяти
; цветного видеоадаптера
MONO_LOW EQU 0000 ; мл. байт
физического адреса
; сегмента видеопамяти
; монохромного видеоадаптера
CRT_SEG EQU 0Bh ; ст. байт
физического адреса
; сегмента видеопамяти
CMOS_PORT EQU 70h ; порт для доступа к
CMOS-памяти
PORT_6845 EQU 0063h ; адрес области данных BIOS,
; где записано значение адреса
; порта контроллера 6845
COLOR_PORT EQU 03d4h ; порт цветного
видеоконтроллера
MONO_PORT EQU 03b4h ; порт монохромного
видеоконтроллера
STATUS_PORT EQU 64h ; порт состояния
клавиатуры
SHUT_DOWN EQU 0feh ; команда сброса
процессора
VIRTUAL_MODE EQU 0001h ; бит перехода в защищённый
режим
A20_PORT EQU 0d1h ; команда
управления линией A20
A20_ON EQU 0dfh ; открыть
A20
A20_OFF EQU 0ddh ; закрыть A20
KBD_PORT_A EQU 60h ; адреса
клавиатурных
KBD_PORT_B EQU 61h ; портов
INT_MASK_PORT EQU 21h ; порт для маскирования
прерываний
EOI EQU 20 ;
команда конца прерывания
MASTER8259A EQU 20 ; первый контроллер
прерываний
SLAVE8259A EQU 0a0 ; второй контроллер
прерываний
;---------------------------------------------------------
; Селекторы, определённые в таблице GDT
DS_DESCR = (gdt_ds - gdt_0)
CS_DESCR = (gdt_cs - gdt_0)
SS_DESCR = (gdt_ss - gdt_0)
BIOS_DESCR = (gdt_bio - gdt_0)
CRT_DESCR = (gdt_crt - gdt_0)
MDA_DESCR = (gdt_mda - gdt_0)
;---------------------------------------------------------
; Маски и инверсные маски для клавиш
L_SHIFT equ 0000000000000001b
NL_SHIFT equ 1111111111111110b
R_SHIFT equ 0000000000000010b
NR_SHIFT equ 1111111111111101b
L_CTRL equ 0000000000000100b
NL_CTRL equ 1111111111111011b
R_CTRL equ 0000000000001000b
NR_CTRL equ 1111111111110111b
L_ALT equ 0000000000010000b
NL_ALT equ 1111111111101111b
R_ALT equ 0000000000100000b
NR_ALT equ 1111111111011111b
CAPS_LOCK equ 0000000001000000b
SCR_LOCK equ 0000000010000000b
NUM_LOCK equ 0000000100000000b
INSERT equ 0000001000000000b
Листинг 3. Демонстрация обработки
прерываний и исключений
в защищённом режиме для процессора 80286
----------------------------------------------------------
IDEAL
RADIX 16
P286
MODEL LARGE
include 'tiny-os.inc'
STACK STACK_SIZE
DATASEG
DSEG_BEG = THIS WORD
real_ss
dw ?
real_sp
dw ?
real_es
dw ?
GDT_BEG = $
LABEL
gdtr WORD
gdt_0 desc_struc <0,0,0,0,0>
gdt_gdt desc_struc
gdt_idt desc_struc
gdt_ds desc_struc
gdt_cs desc_struc
gdt_ss desc_struc
gdt_bio desc_struc
gdt_crt desc_struc
gdt_mda desc_struc
GDT_SIZE = ($ - GDT_BEG)
; Область памяти для загрузки регистра IDTR
idtr idtr_struc
; Таблица дескрипторов прерываний
IDT_BEG = $
; ---------------------- Вентили исключений
--------------------
idt idt_struc
idt_struc
idt_struc
idt_struc
idt_struc
idt_struc
idt_struc
idt_struc
idt_struc
idt_struc
idt_struc
idt_struc
idt_struc
idt_struc
idt_struc
idt_struc
idt_struc
idt_struc
idt_struc
idt_struc
idt_struc
idt_struc
idt_struc
idt_struc
idt_struc
idt_struc
idt_struc
idt_struc
idt_struc
idt_struc
idt_struc
idt_struc
; --------------- Вентили аппаратных прерываний
---------------
; int 20h-IRQ0
idt_struc
; int 21h-IRQ1
idt_struc
; int 22h, 23h, 24h, 25h, 26h, 27h-IRQ2-IRQ7
idt_struc 6 dup (
)
; int 28h, 29h, 2ah, 2bh, 2ch, 2dh, 2eh, 2fh-IRQ8-IRQ15
idt_struc 8 dup (
)
; -------------------- Вентиль прерывания --------------------
; int 30h
idt_struc
IDT_SIZE = ($ - IDT_BEG)
CODESEG
PROC start
mov ax,DGROUP
mov ds,ax
call set_crt_base
mov bh, 77h
call clrscr
; Устанавливаем защищённый режим
call
set_pmode
call
write_hello_msg
; Размаскируем прерывания от таймера и
клавиатуры
in
al,INT_MASK_PORT
and al,0fch
out
INT_MASK_PORT,al
; Ожидаем нажатия на клавишу
charin:
int 30h ;
ожидаем нажатия на клавишу
; AX - скан-код клавиши,
; BX - состояние переключающих клавиш
cmp al, 1 ; если
- выход из цикла
jz continue
push
bx
; выводим скан-код на экран
mov bx,
0301h ; координаты вывода
call Print_Word
pop bx
mov ax,
bx ; выводим
состояние
push
bx
; переключающих клавиш
mov bx, 0306h
call Print_Word
pop bx
jmp charin
; Следующий байт находится в сегменте кода.
; Он используется нами для демонстрации
возникновения
; исключения при попытке записи в сегмент
кода.
wrong1 db ?
continue:
; После нажатия на клавишу
выходим в это место
; программы. Следующие несколько строк
демонстрируют
; команды, которые вызывают исключение. Вы
можете
; попробовать их, если уберёте символ
комментария
; из соответствующей строки.
; Попытка записи за конец сегмента данных.
Метка wrong
; находится в самом конце программы.
mov [wrong],
al
; Попытка записи в сегмент кода.
; mov [wrong1], al
; Попытка извлечения из пустого стека.
; pop ax
; Загрузка в сегментный регистр
неправильного селектора.
; mov ax, 1280h
; mov ds, ax
; Прямой вызов исключения при помощи
команды прерывания.
; int 1
call
set_rmode ; установка
реального режима
mov bh,
07h ; стираем экран
и
call
clrscr ; выходим в
DOS
mov ah,4c
int 21h
ENDP start
MACRO setgdtentry
mov
[(desc_struc bx).base_l],ax
mov
[(desc_struc bx).base_h],dl
ENDM
; -------------------------------------------
; Установка защищённого режима
; -------------------------------------------
PROC set_pmode NEAR
mov ax,DGROUP
mov dl,ah
shr dl,4
shl ax,4
mov si,ax
mov di,dx
add ax,OFFSET
gdtr
adc dl,0
mov bx,OFFSET
gdt_gdt
setgdtentry
; Заполняем дескриптор в GDT, указывающий на
; дескрипторную таблицу прерываний
mov ax,si ;
загружаем 24-битовый адрес сегмента
mov dx,di ; данных
add ax,OFFSET idt ;
адрес дескриптора для IDT
adc dl,0
mov bx,OFFSET gdt_idt
setgdtentry
; Заполняем структуру для загрузки регистра
IDTR
mov bx,OFFSET
idtr
mov
[(idtr_struc bx).idt_l],ax
mov
[(idtr_struc bx).idt_h],dl
mov bx,OFFSET
gdt_ds
mov ax,si
mov dx,di
setgdtentry
mov bx,OFFSET
gdt_cs
mov ax,cs
mov dl,ah
shr dl,4
shl ax,4
setgdtentry
mov bx,OFFSET
gdt_ss
mov ax,ss
mov dl,ah
shr dl,4
shl ax,4
setgdtentry
; готовим возврат в реальный режим
push ds
mov ax,40
mov ds,ax
mov [WORD
67],OFFSET shutdown_return
mov [WORD
69],cs
pop ds
cli
mov al,8f
out
CMOS_PORT,al
jmp del1
del1:
mov al,5
out
CMOS_PORT+1,al
mov
ax,[rl_crt] ; сегмент видеопамяти
mov es,ax
call
enable_a20 ; открываем линию A20
mov
[real_ss],ss ; сохраняем сегментные
mov
[real_es],es ; регистры
;-------- Перепрограммируем контроллер
прерываний --------
; Устанавливаем для IRQ0-IRQ7 номера прерываний
20h-27h
mov
dx,MASTER8259A
mov ah,20h
call set_int_ctrlr
; Устанавливаем для IRQ8-IRQ15 номера прерываний
28h-2Fh
mov
dx,SLAVE8259A
mov ah,28h
call set_int_ctrlr
; Загружаем регистры IDTR и GDTR
lidt [FWORD idtr]
lgdt [QWORD
gdt_gdt]
; Переключаемся в защищённый режим
mov
ax,VIRTUAL_MODE
lmsw ax
; jmp far flush
db 0ea
dw
OFFSET flush
dw
CS_DESCR
LABEL flush FAR
; Загружаем селекторы в сегментные регистры
mov
ax,SS_DESCR
mov ss,ax
mov
ax,DS_DESCR
mov ds,ax
; Разрешаем прерывания
sti
ret
ENDP set_pmode
; --------------------------------
; Возврат в реальный режим
; --------------------------------
DATASEG
; Пустой дескриптор для выполнения возврата
; процессора в реальный режим через перевод
; его в состояние отключения.
null_idt idt_struc <>
CODESEG
PROC set_rmode NEAR
mov
[real_sp],sp
; Переводим процессор в состояние
отключения,
; это эквивалентно аппаратному сбросу, но
; выполняется быстрее.
; Сначала мы загружаем IDTR нулями, затем
; выдаём команду прерывания.
lidt [FWORD
null_idt]
int 3
; Это старый способ сброса процессора через
; контроллер клавиатуры.
; mov al,SHUT_DOWN
; out STATUS_PORT,al
rwait:
hlt
jmp rwait
LABEL shutdown_return FAR
in
al,INT_MASK_PORT
and al,0
out
INT_MASK_PORT,al
mov ax,DGROUP
mov ds,ax
assume ds:DGROUP
cli
mov
ss,[real_ss]
mov
sp,[real_sp]
mov ax,000dh
out
CMOS_PORT,al
sti
mov
es,[real_es]
call disable_a20
ret
ENDP set_rmode
; -------------------------------------------------
; Обработка исключений
; -------------------------------------------------
; Обработчики исключений. Записываем в AX
номер
; исключения и передаём управление
процедуре
; shutdown
LABEL exc_00 WORD
mov ax,0
jmp shutdown
LABEL exc_01 WORD
mov ax,1
jmp shutdown
LABEL exc_02 WORD
mov ax,2
jmp shutdown
LABEL exc_03 WORD
mov ax,3
jmp shutdown
LABEL exc_04 WORD
mov ax,4
jmp shutdown
LABEL exc_05 WORD
mov ax,5
jmp shutdown
LABEL exc_06 WORD
mov ax,6
jmp shutdown
LABEL exc_07 WORD
mov ax,7
jmp shutdown
LABEL exc_08 WORD
mov ax,8
jmp shutdown
LABEL exc_09 WORD
mov ax,9
jmp shutdown
LABEL exc_0A WORD
mov ax,0ah
jmp shutdown
LABEL exc_0B WORD
mov ax,0bh
jmp shutdown
LABEL exc_0C WORD
mov ax,0ch
jmp shutdown
LABEL exc_0D WORD
mov ax,0dh
jmp shutdown
LABEL exc_0E WORD
mov ax,0eh
jmp shutdown
LABEL exc_0F WORD
mov ax,0fh
jmp shutdown
LABEL exc_10 WORD
mov ax,10h
jmp shutdown
LABEL exc_11 WORD
mov ax,11h
jmp shutdown
LABEL exc_12 WORD
mov ax,12h
jmp shutdown
LABEL exc_13 WORD
mov ax,13h
jmp shutdown
LABEL exc_14 WORD
mov ax,14h
jmp shutdown
LABEL exc_15 WORD
mov ax,15h
jmp shutdown
LABEL exc_16 WORD
mov ax,16h
jmp shutdown
LABEL exc_17 WORD
mov ax,17h
jmp shutdown
LABEL exc_18 WORD
mov ax,18h
jmp shutdown
LABEL exc_19 WORD
mov ax,19h
jmp shutdown
LABEL exc_1A WORD
mov ax,1ah
jmp shutdown
LABEL exc_1B WORD
mov ax,1bh
jmp shutdown
LABEL exc_1C WORD
mov ax,1ch
jmp shutdown
LABEL exc_1D WORD
mov ax,1dh
jmp shutdown
LABEL exc_1E WORD
mov ax,1eh
jmp shutdown
LABEL exc_1F WORD
mov ax,1fh
jmp shutdown
DATASEG
exc_msg db
"Exception ...., ....:.... code ..... Press any key... "
CODESEG
; -----------------------------------------------
; Вывод на экран номера исключения, кода
ошибки,
; дампа регистров и возврат в реальный режим.
; -----------------------------------------------
PROC shutdown NEAR
call
rdump ; дамп регистров процессора
push ax
call
beep ; звуковой сигнал
; Выводим сообщение об исключении
mov
ax,[vir_crt]
mov es,ax
mov bx,1d
mov ax,4
mov si,OFFSET
exc_msg
mov dh,74h
mov cx, SIZE
exc_msg
call writexy
pop ax
mov bx,
040bh ; номер исключения
call Print_Word
pop ax
mov bx,
0420h ; код ошибки
call Print_Word
pop ax
mov bx,
0416h ; смещение
call Print_Word
pop ax
mov bx,
0411h ; селектор
call Print_Word
call
set_rmode ; возвращаемся в реальный режим
mov ax,
0 ; ожидаем нажатия на клавишу
int 16h
mov bh, 07h
call clrscr
mov ah,4Ch
int 21h
ENDP shutdown
; -------------------------------------------------
; Перепрограммирование контроллера
прерываний
; На входе: DX - порт
контроллера прерывания
;
AH - начальный номер прерывания
; -------------------------------------------------
PROC set_int_ctrlr NEAR
mov al,11
out dx,al
jmp SHORT $+2
mov al,ah
inc dx
out dx,al
jmp SHORT $+2
mov al,4
out dx,al
jmp SHORT $+2
mov al,1
out dx,al
jmp SHORT $+2
mov al,0ff
out dx,al
dec dx
ret
ENDP set_int_ctrlr
; -------------------------------
; Разрешение линии A20
; -------------------------------
PROC enable_a20 NEAR
mov
al,A20_PORT
out
STATUS_PORT,al
mov al,A20_ON
out
KBD_PORT_A,al
ret
ENDP enable_a20
; -------------------------------
; Запрещение линии A20
; -------------------------------
PROC disable_a20 NEAR
mov
al,A20_PORT
out
STATUS_PORT,al
mov
al,A20_OFF
out
KBD_PORT_A,al
ret
ENDP disable_a20
; ---------- Обработчик аппаратных прерываний
IRQ2-IRQ7
PROC dummy_iret0 NEAR
push ax
;Посылаем сигнал конца прерывания в первый
контроллер 8259A
mov al,EOI
out
MASTER8259A,al
pop ax
iret
ENDP dummy_iret0
; ---------- Обработчик аппаратных прерываний
IRQ8-IRQ15
PROC dummy_iret1 NEAR
push ax
; Посылаем сигнал конца прерывания в
первый
; и второй контроллеры 8259A
mov al,EOI
out
MASTER8259A,al
out
SLAVE8259A,al
pop ax
iret
ENDP dummy_iret1
; ------------------------------------------
; Процедура выдаёт короткий звуковой сигнал
; ------------------------------------------
PROC beep NEAR
push ax bx cx
in
al,KBD_PORT_B
push ax
mov cx,80
beep0:
push cx
and
al,11111100b
out
KBD_PORT_B,al
mov cx,60
idle1:
loop idle1
or
al,00000010b
out
KBD_PORT_B,al
mov cx,60
idle2:
loop idle2
pop cx
loop beep0
pop ax
out
KBD_PORT_B,al
pop cx bx ax
ret
ENDP beep
; ------------------------------------------------
; Процедура задерживает выполнение
программы
; на некоторое время, зависящее от
быстродействия
; процессора.
; ------------------------------------------------
PROC
pause NEAR
push cx
mov cx,10
ploop0:
push cx
xor cx,cx
ploop1:
loop ploop1
pop cx
loop ploop0
pop cx
ret
ENDP pause
; ------------------------------------------
; Процедуры для работы с
клавиатурой
; ------------------------------------------
DATASEG
key_flag
db 0
key_code
dw 0
ext_scan
db 0
keyb_status
dw 0
CODESEG
; ----------------------------------------------
; Обработчик аппаратного прерывания
клавиатуры
; ----------------------------------------------
PROC Keyb_int
NEAR
call
beep ; выдаём звуковой сигнал
push ax
mov al,
[ext_scan] ; расширенный скан-код
cmp al,
0 ; или
обычный ?
jz
normal_scan1
; --------- обработка расширенного скан-кода
-------------
cmp al,
0e1h ; это клавиша
?
jz
pause_key
in al,
60h ; вводим скан-код
cmp al,
2ah ; игнорируем
префикс 2Ah
jz
intkeyb_exit_1
cmp al,
0aah ; игнорируем
отпускание
jz
intkeyb_exit_1 ; клавиш
mov ah,
[ext_scan] ; записываем скан-код и
call
Keyb_PutQ ; расширенный скан-код
; в "очередь", состоящую
; из одного слова
mov al,
0 ;
сбрасываем признак
mov
[ext_scan], al ; получения расширенного
jmp
intkeyb_exit ; скан-кода
pause_key:
; обработка клавиши
in al,
60h ; вводим скан-код
cmp al,
0c5h ; если это код
,
jz
pause_key1 ; записываем его в
очередь,
cmp al,
45h ; иначе
игнорируем
jz
pause_key1
jmp
intkeyb_exit
pause_key1:
mov ah,
[ext_scan] ; запись в очередь
call
Keyb_PutQ ; кода клавиши
mov al,
0 ;
сбрасываем признак
mov
[ext_scan], al ; получения расширенного
jmp
intkeyb_exit ; скан-кода
; --------- обработка обычного скан-кода -------------
normal_scan1:
in al,
60h ; вводим скан-код
cmp al,
0feh ; игнорируем FEh
jz
intkeyb_exit
cmp al,
0e1h ; расширенный скан-код?
jz
ext_key ; если да, то
на обработку
; расширенного скан-кода
cmp al,
0e0h
jnz
normal_scan
ext_key:
mov
[ext_scan], al ; устанавливаем признак
jmp
intkeyb_exit ; расширенного скан-кода
; Сброс признака расширенного скан-кода и
выход
intkeyb_exit_1:
mov al, 0
mov
[ext_scan], al
jmp
intkeyb_exit
; Запись нормального скан-кода в очередь и
выход
normal_scan:
mov ah, 0
call Keyb_PutQ
intkeyb_exit:
in al,
61h ; разблокируем
клавиатуру
mov ah, al
or al,
80h
out 61h, al
xchg ah, al
out 61h, al
mov
al,EOI ; посылаем
сигнал конца
out
MASTER8259A,al ; прерывания
pop ax
sti
iret
ENDP Keyb_int
; ---------------------------------------------------
; Запись скан-кода и расширенного скан-кода
в
; "буфер", состоящий из одного слова.
; ---------------------------------------------------
PROC Keyb_PutQ NEAR
push ax
mov
[key_code], ax ; записываемый код
; ------- Обрабатываем переключающие клавиши
---------
cmp ax,
002ah
; L_SHIFT down
jnz @@kb1
mov ax,
[keyb_status]
or ax,
L_SHIFT
mov
[keyb_status], ax
jmp
keyb_putq_exit
@@kb1:
cmp ax,
00aah
; L_SHIFT up
jnz @@kb2
mov ax,
[keyb_status]
and ax,
NL_SHIFT
mov
[keyb_status], ax
jmp
keyb_putq_exit
@@kb2:
cmp ax,
0036h
; R_SHIFT down
jnz @@kb3
mov ax,
[keyb_status]
or ax,
R_SHIFT
mov
[keyb_status], ax
jmp
keyb_putq_exit
@@kb3:
cmp ax,
00b6h
; R_SHIFT up
jnz @@kb4
mov ax,
[keyb_status]
and ax,
NR_SHIFT
mov
[keyb_status], ax
jmp
keyb_putq_exit
@@kb4:
cmp ax,
001dh
; L_CTRL down
jnz @@kb5
mov ax,
[keyb_status]
or ax,
L_CTRL
mov
[keyb_status], ax
jmp
keyb_putq_exit
@@kb5:
cmp ax,
009dh ; L_CTRL up
jnz @@kb6
mov ax,
[keyb_status]
and ax,
NL_CTRL
mov
[keyb_status], ax
jmp
keyb_putq_exit
@@kb6:
cmp ax,
0e01dh ; R_CTRL down
jnz @@kb7
mov ax,
[keyb_status]
or ax,
R_CTRL
mov
[keyb_status], ax
jmp
keyb_putq_exit
@@kb7:
cmp ax,
0e09dh ; R_CTRL up
jnz @@kb8
mov ax,
[keyb_status]
and ax,
NR_CTRL
mov
[keyb_status], ax
jmp
keyb_putq_exit
@@kb8:
cmp ax,
0038h ; L_ALT down
jnz @@kb9
mov ax,
[keyb_status]
or ax,
L_ALT
mov
[keyb_status], ax
jmp
keyb_putq_exit
@@kb9:
cmp ax,
00b8h
; L_ALT up
jnz @@kb10
mov ax,
[keyb_status]
and ax,
NL_ALT
mov
[keyb_status], ax
jmp
keyb_putq_exit
@@kb10:
cmp ax,
0e038h ; R_ALT down
jnz @@kb11
mov ax,
[keyb_status]
or ax,
R_ALT
mov
[keyb_status], ax
jmp
keyb_putq_exit
@@kb11:
cmp ax,
0e0b8h ; R_ALT up
jnz @@kb12
mov ax,
[keyb_status]
and ax,
NR_ALT
mov
[keyb_status], ax
jmp
keyb_putq_exit
@@kb12:
cmp ax,
003ah ; CAPS_LOCK up
jnz @@kb13
mov ax,
[keyb_status]
xor ax,
CAPS_LOCK
mov
[keyb_status], ax
jmp
keyb_putq_exit
@@kb13:
cmp ax,
00bah
; CAPS_LOCK down
jnz @@kb14
jmp
keyb_putq_exit
@@kb14:
cmp ax,
0046h
; SCR_LOCK up
jnz @@kb15
mov ax,
[keyb_status]
xor ax,
SCR_LOCK
mov
[keyb_status], ax
jmp
keyb_putq_exit
@@kb15:
cmp ax,
00c6h
; SCR_LOCK down
jnz @@kb16
jmp
keyb_putq_exit
@@kb16:
cmp ax,
0045h
; NUM_LOCK up
jnz @@kb17
mov ax,
[keyb_status]
xor ax,
NUM_LOCK
mov
[keyb_status], ax
jmp
keyb_putq_exit
@@kb17:
cmp ax,
00c5h
; NUM_LOCK down
jnz @@kb18
jmp
keyb_putq_exit
@@kb18:
cmp ax,
0e052h ; INSERT up
jnz @@kb19
mov ax,
[keyb_status]
xor ax,
INSERT
mov
[keyb_status], ax
jmp
keyb_putq_exit
@@kb19:
cmp ax,
0e0d2h ; INSERT down
jnz @@kb20
jmp
keyb_putq_exit
@@kb20:
test ax,
0080h ; фильтруем отжатия клавиш
jnz
keyb_putq_exit
mov al,
0ffh ; устанавливаем
признак
mov
[key_flag], al ; готовности для чтения
; символа из "буфера"
keyb_putq_exit:
pop ax
ret
ENDP Keyb_PutQ
; -----------------------------------------------------
; Программное прерывание, предназначенное
для чтения
; символа из буфера клавиатуры. По своим
функциям
; напоминает прерывание INT 16h реального
режима.
; В AX возвращается скан-код нажатой клавиши,
; в BX - состояние переключающих клавиш.
; -----------------------------------------------------
PROC Int_30h_Entry NEAR
push
dx
; запрещаем прерывания и
cli
; сбрасываем признак
mov al,
0 ;
готовности скан-кода
mov
[key_flag], al ; в буфере клавиатуры
; Ожидаем прихода прерывания от клавиатуры.
; Процедура клавиатурного прерывания
установит
; признак в переменной key_flag.
keyb_int_wait:
sti ;
разрешаем прерывания
nop ; ждём
прерывание
nop
cli ;
запрещаем прерывания
mov al,
[key_flag] ; и опрашиваем флаг
cmp al,
0 ;
готовности скан-кода
jz
keyb_int_wait
mov al,
0 ;
сбрасываем флаг
mov
[key_flag], al ; готовности
mov ax, [key_code]
; записываем скан-код
mov bx, [keyb_status] ;
и состояние переключающих
; клавиш
sti
; разрешаем прерывания
pop dx
iret
ENDP Int_30h_Entry
; -------------------------------------------
; TIMER section
; -------------------------------------------
DATASEG
timer_cnt dw 0
CODESEG
PROC Timer_int NEAR
cli
push ax
; Увеличиваем содержимое счётчика времени
mov ax,
[timer_cnt]
inc ax
mov
[timer_cnt], ax
; Примерно раз в секунду выдаём звуковой
сигнал
test ax, 0fh
jnz
timer_exit
call beep
timer_exit:
; Посылаем команду конца прерывания
mov al,EOI
out
MASTER8259A,al
pop ax
sti
iret
ENDP Timer_int
; --------------------------------------------------
; Процедуры обслуживания видеоконтроллера
; --------------------------------------------------
DATASEG
columns
db 80d
rows
db 25d
rl_crt
dw COLOR_SEG
vir_crt
dw CRT_DESCR
curr_line dw
0d
text_buf
db
" "
CODESEG
; -----------------------------------------
; Определение адреса видеопамяти
; -----------------------------------------
PROC set_crt_base NEAR
mov ax,40
mov es,ax
mov bx,[WORD
es:4a]
mov
[columns],bl
mov bl,[BYTE
es:84]
inc bl
mov [rows],bl
mov bx,[WORD
es:PORT_6845]
cmp
bx,COLOR_PORT
je
color_crt
mov
[rl_crt],MONO_SEG
mov
[vir_crt],MDA_DESCR
color_crt:
ret
ENDP set_crt_base
; -------------------------------------
; Запись строки в видеопамять
; -------------------------------------
PROC writexy
NEAR
push si
push di
mov
dl,[columns]
mul dl
add ax,bx
shl ax,1
mov di,ax
mov ah,dh
write_loop:
lodsb
stosw
loop
write_loop
pop di
pop si
ret
ENDP writexy
; ---------------------------------------
; Стирание экрана (в реальном режиме)
; ---------------------------------------
PROC
clrscr NEAR
xor
cx,cx
mov
dl,[columns]
mov dh,[rows]
mov ax,0600
int 10
ret
ENDP clrscr
DATASEG
hello_msg db " Protected mode monitor *TINY/OS*,
v.1.1
for CPU 80286 ¦ © Frolov A.V., 1992 "
CODESEG
; ------------------------------------
; Вывод начального сообщения
; в защищённом режиме
; ------------------------------------
PROC write_hello_msg NEAR
mov
ax,[vir_crt]
mov es,ax
mov si,OFFSET
hello_msg
mov bx,0
mov
ax,[curr_line]
inc
[curr_line]
mov cx,SIZE
hello_msg
mov dh,30h
call writexy
call beep
ret
ENDP write_hello_msg
; ----------------------------------------------
; Процедура выводит на экран содержимое AX
; (x,y) = (bh, bl)
; ----------------------------------------------
PROC Print_Word near
push ax
push bx
push dx
push ax
mov cl,8
rol ax,cl
call Byte_to_hex
mov
[text_buf], dh
mov
[text_buf+1], dl
pop ax
call Byte_to_hex
mov
[text_buf+2], dh
mov
[text_buf+3], dl
mov si,
OFFSET text_buf
mov dh, 70h
mov cx, 4
mov al, bh
mov ah, 0
mov bh, 0
call writexy
pop dx
pop bx
pop ax
ret
ENDP Print_Word
DATASEG
tabl db '0123456789ABCDEF'
CODESEG
; -----------------------------------------
; Преобразование байта в шестнадцатеричный
; символьный формат
; al - входной байт
; dx - выходное слово
; -----------------------------------------
PROC Byte_to_hex near
push cx
push bx
mov bx,
OFFSET tabl
push ax
and al,0fh
xlat
mov dl,al
pop ax
mov cl,4
shr al,cl
xlat
mov dh,al
pop bx
pop cx
ret
ENDP Byte_to_hex
DATASEG
reg_title db " CS IP AX BX
CX DX SP BP SI DI
"
;
.... .... .... .... .... .... .... ....
sreg_title db " DS ES SS FLAGS "
;
.... .... .... .... .... .... .... ....
CODESEG
; ------------------------------------------------
; Вывод на экран содержимого регистров
процессора
; ------------------------------------------------
PROC rdump NEAR
pushf
pusha
mov di, es
mov
ax,[vir_crt]
mov es,ax
mov si,OFFSET
reg_title
mov
bx,1
; (X,Y) = (AX,BX)
mov ax,6
mov cx,SIZE
reg_title
mov
dh,1fh ; чёрный на голубом
фоне
call writexy
; Выводим содержимое всех регистров
mov
ax,cs ; cs
mov bx, 0702h
call Print_Word
mov bp, sp
mov ax,
[bp+18d] ; ip
mov bx, 0708h
call Print_Word
mov bx, 070eh
mov
ax,[bp+14d] ; ax
call Print_Word
mov bx, 0714h
mov
ax,[bp+8d] ; bx
call Print_Word
mov bx, 071ah
mov
ax,[bp+12d] ; cx
call Print_Word
mov bx, 0720h
mov
ax,[bp+10d] ; dx
call Print_Word
mov ax,bp
add
ax,20d ; sp
mov bx, 0726h
call Print_Word
mov
ax,[bp+4d] ; bp
mov bx, 072ch
call Print_Word
mov bx, 0732h
mov
ax,[bp+2] ; si
call Print_Word
mov bx, 0738h
mov ax,
[bp] ; di
call Print_Word
mov si,OFFSET
sreg_title
mov bx,1
mov ax,8
mov cx,SIZE
sreg_title
mov dh,1fh
call writexy
mov bx, 0902h
mov ax,
ds ; ds
call Print_Word
mov bx, 0908h
mov ax,
di ; es
call Print_Word
mov bx, 090eh
mov
ax,ss ; ss
call Print_Word
mov bx, 0914h
mov ax,
[bp+16d] ; flags
call Print_Word
; Восстанавливаем содержимое регистров
popa
popf
ret
ENDP rdump
CSEG_SIZE = ($ - start)
DATASEG
DSEG_SIZE = ($ - DSEG_BEG)
wrong db ?
END start
[ << Назад ] [ Содержание ] [ Далее >> ]
|