Код:
;
; IRQ 1 обработчик - клавиатура
;
int9_handler:
push ax
push edi
xor ax, ax
; запрашиваем позиционный код клавиши
in al, 060h
dec al ; Нажат ли <Esc> ? (его сканкод = 1)
jnz _continue_handling
; Esc нажат - пробуем переключиться в реальный режим, вызвать там
; прерывание 10h с кодом AH=3 (очистка экрана) и вернуться обратно
mov ax, 3
push 10h
call REAL_MODE_SWITCH_SERVICE
jmp Ack
_continue_handling:
; отжатия не обрабатываем, только нажатия
mov ah, al
and ah, 80h
jnz clear_request
; преобразуем позиционный код в ASCII по таблице
and al, 7Fh
push edi
mov edi, ascii
add di, ax
mov al, [edi]
pop edi
; выводим символы на экран один за другим
mov edi, dword [cursor]
shl edi, 1
mov byte [es:edi], al
inc dword [cursor]
; посылка подтверждения обрабоки в порт клавиатуры
; (установка и сброс 7 бита порта 061h)
Ack:
in al, 061h
or al, 80
out 061h, al
xor al, 80
out 061h, al
clear_request:
pop edi
pop ax
jmp int_EOI
Нам осталось определить функцию REAL_MODE_SWITCH_SERVICE, которой в стеке нужно передать номер прерывания и которая будет делать следующее:
- полноценный переход в 16битный реальный режим
- генерация прерывания с заданным номером с сохранением регистров
- переход обратно в 32битный защищенный режим и возврат управления.
Чтобы не портить контекст перед вызовом прерывания, нам нужно позаботиться о сохранении тех регистров, которые мы собираемся изменять в процессе перехода:
Код:
use32
old_ax dw ?
old_cl db ?
REAL_MODE_SWITCH_SERVICE:
mov [old_cl], cl
mov [old_ax], ax
Далее достаем из стека номер прерывания командой mov cl, [esp+4]. После этого отключаем аппаратные прерывания и NMI уже известным нам способом, предварительно запомнив командой pushfd старые флаги. После этого загружаем в IDTR регистр, описывающий таблицу векторов прерываний в реальном режиме:
Код:
; переключаемся обратно в реальный режим...
lidt fword [REAL_IDTR]
Теперь нам нужно передать управление в 16битный сегмент с лимитом 64К. Это нужно обязательно сделать перед (!) переключением в реальный режим. Иначе мы получим не реальный режим, а так называемый Unreal Mode, который нас не интересует. Итак, перезагрузка регистра CS новым селектором:
Код:
; загружаем в CS селектор 16-битного сегмента с лимитом 64к
jmp 00100000b:__CONT
use16
__CONT:
Конечно, нужно поставить директиву use16, т.к. дальнейший код выполняется уже в 16битном сегменте. Теперь можно переключиться в реальный режим сбросом бита Protect Enable (PE) в управляющем регистре CR0:
Код:
; мы в 16битном сегменте. переключаемся в реальный режим.
mov eax, cr0
and al, 0FEh
mov cr0, eax
jmp 0:REAL_ENTRY
; Код реального режима
REAL_ENTRY:
Теперь мы находимся в реальном режиме и в теневой части регистра CS находится дескрикптор 16битного малого сегмента. Однако, регистры DS,SS,ES все еще хранят старые значения, для чего их сразу же нужно перезагрузить. Заодно разрешаем аппаратные прерывания и NMI.
Код:
REAL_ENTRY:
mov ax, cs
mov ds, ax
mov ss, ax
mov es, ax
; разрешаем аппаратные прерывания и NMI
in al, 70h
and al, 7Fh
out 70h, al
sti
Теперь динамически сформируем команду INT xx, потому что система команд архитектуры IA-32 поддерживает только команду INT с фиксированным номером прерывания. Нам придется сформировать ее динамически: опкод команды INT xx равен CD xx. Перед генерацией прерывания заодно восстановим регистры ax и cl, которые подвергались изменению в процессе переключения:
Код:
mov [int_no], cl
mov ax, [old_ax]
mov cl, [old_cl]
db 0CDh ; INT xx
int_no db 0
mov [old_ax], ax
После возврата из прерывания запомним возвращенное значение ax, т.к. мы его поменяем в процессе обратного переключения. Далее все по-новой: запрет аппаратных прерываний и NMI, инициализация защищенного режима, прыжок в 32битный сегмент кода, перезагрузка селекторов DS,ES,SS.
Далее восстанавливаем AX, восстановление флагов командой popfd (нельзя тупо сделать sti, что казалось бы более очевидным, потому как перед во время вызова REAL_MODE_SWTICH_SERVICE прерывания отключены!) и делаем RET с выталкиванием четырех байт из стека (номер прерывания):
Код:
mov ax, [old_ax]
; разрешаем аппаратные прерывания и NMI
in al, 70h
and al, 7Fh
out 70h, al
popfd
ret 4
На этом мы, собственно, закончим наше и так затянувшееся повествование. Исходный код загрузчика и файл конфигурации Bochs (только в нем надо пути сменить на другие к ROM-BIOS) можно найти здесь: http://gr8.cih.ms/uploads/loader.rar
Пока!
(C) Great, 2007
В статье использованы материалы wasm.ru, dims.karelia.ru и книги А.Жуков, А.Авдюхин "Ассемблер", СПб.:"БХВ-Петербург", 2003.