Показать сообщение отдельно

  #2  
Старый 17.03.2007, 01:54
_Great_
Флудер
Регистрация: 27.12.2005
Сообщений: 2,372
Провел на форуме:
5339610

Репутация: 4360


Отправить сообщение для _Great_ с помощью ICQ
По умолчанию

Код:
;
; 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.