; Работа студента группы 2084/3 ; Макаренко Антона ; по курсу ЦВМ ; Программа, производит переход в Protrcted Mode, ; перепрограмирует контроллер прерываний и производит ; перехват прерывания от клавиатуры. ; Макрос преобразования смещения из 32-х битного сегмента в 16-битную переменную so equ small offset .386p ; Сегмент Real Mode, 16 бит RM_seg segment para public "CODE" use16 assume cs: RM_seg, ds: PM_seg, ss: Stack_seg start: ; Очистить экран mov ax, 3 int 10h ; Подготовить сегментные регистры push PM_seg pop ds ; Проверить режим (PM или нет) mov eax, cr0 test al, 1 jz no_V86 ; Если уже в PM - сообщить об ошибке и выйти mov dx, so v86_msg err_exit: mov ah, 9 int 21h mov ah, 4Ch int 21h ; Проверить на присутствие Windows no_V86: mov ax, 1600h int 2Fh test al, al jz no_Windows ; Если под Wondows - сообщить и выйти mov dx, so win_msg jmp short err_exit ; ***************************************** ; Все впорядке, начать выполнение программы no_Windows: ; Вычислить базы для всех используемых дескрипторов сегментов xor eax, eax mov ax, RM_seg ; Базой 16bitCS будет RM_seg shl eax, 4 mov word ptr GDT_16bitCS+2, ax ; биты 15-0 shr eax, 16 mov byte ptr GDT_16bitCS+4, al ; биты 23-16 mov ax, PM_seg shl eax, 4 mov word ptr GDT_32bitCS+2, ax ; Базой всех 32bit* будет mov word ptr GDT_32bitSS+2, ax ; PM_seg mov word ptr GDT_32bitDS+2, ax shr eax, 16 mov byte ptr GDT_32bitCS+4, al mov byte ptr GDT_32bitSS+4, al mov byte ptr GDT_32bitDS+4, al ; Вычислить линейный адрес GDT xor eax, eax mov ax, PM_seg shl eax, 4 push eax add eax, offset GDT mov dword ptr gdtr+2, eax ; Загрузить GDT lgdt fword ptr gdtr ; Вычислить линейный адрес IDT pop eax add eax, offset IDT mov dword ptr idtr+2, eax ; Загрузить IDT lidt fword ptr idtr ; Т.к. собираемся работать с 32-х битной памятью - открыть A20 in al, 70h or al, 2 out 92h, al ; Отключить прерывания cli ; Отключить немаскируемые прерывания (NMI) in al, 70h or al, 80h out 70h, al ; Перейти в защищенный режим mov eax, cr0 or al, 1 mov cr0, eax ; Загрузить SEL_32bitCS в CS db 66h ; Префикс преобразования разрядности db 0EAh ; Дальний переход dd offset PM_entry dw Sel_32bitCS RM_return: ; Перейти в Real Mode mov eax, cr0 and al, 0FEh mov cr0, eax ; Сбросить очередь и загрузить CS реальным числом db 0EAh dw $+4 dw RM_seg ; Установить регистры для работы в реальном режиме mov ax, PM_seg mov ds, ax mov es, ax mov ax, Stack_seg mov bx, stack_l mov ss, ax mov sp, bx ; Загрузить IDTR для RM mov ax, PM_seg mov ds, ax lidt fword ptr idtr_real ; Разрешить NMI in al, 70h and al, 07Fh out 70h, al ; Разрешить прерывания sti ; и выйти mov ah, 4Ch int 21h RM_seg ends ; 32-х битный сегмент PM_seg segment para "CODE" use32 assume cs: PM_seg ; Таблица GDT и IDT д.б. выровнены, поэтому размещаются в начале сегмента GDT label byte db 8 dup(0) ; 32-x битный 4-х Гб сегмент с базой =0 GTD_flatDS db 0FFh,0FFh,0,0,0,10010010b,11001111b,0 ; 16-и битный 64-х Кб сегмент кода с базой RM_seg GDT_16bitCS db 0FFh,0FFh,0,0,0,10011010b,0,0 ; 32-x битный 4-х Гб сегмент кода с базой PM_seg GDT_32bitCS db 0FFh,0FFh,0,0,0,10011010b,11001111b,0 ; 32-x битный 4-х Гб сегмент данных с базой PM_seg GDT_32bitDS db 0FFh,0FFh,0,0,0,10010010b,11001111b,0 ; 32-x битный 4-х Гб сегмент данных с базой Stack_seg GDT_32bitSS db 0FFh,0FFh,0,0,0,10010010b,11001111b,0 gdt_size = $-GDT gdtr dw gdt_size-1 ; Предел GDT dd ? ; Линейный адрес GDT ; Названия селекторов SEL_flatDS equ 001000b SEL_16bitCS equ 010000b SEL_32bitCS equ 011000b SEL_32bitDS equ 100000b SEL_32bitSS equ 101000b ; Таблица дескрипторов прерываний IDT IDT label byte ; Все дескрипторы имеют тип 0Eh - 32-х битный шлюз прерывания ; INT 00-07 dw 8 dup(so int_handler, SEL_32bitCS, 8E00h,0) ; INT 08(irq0) dw so irq0_7_handler,SEL_32bitCS,8E00h,0 ; INT 09(irq1) dw so irq1_handler,SEL_32bitCS,8E00h,0 ; INT 0Ah-0Fh (IRQ2-IRQ8) dw 6 dup(so irq0_7_handler,SEL_32bitCS,8E00h,0) ; INT 10h-6Fh dw 97 dup(so int_handler,SEL_32bitCS,8E00h,0) ; INT 70h-78h (IRQ8-IRQ15) dw 8 dup(so irq8_15_handler,SEL_32bitCS,8E00h,0) ; INT 79h-FFh dw 135 dup(so int_handler,SEL_32bitCS,8E00h,0) idt_size = $-IDT idtr dw idt_size-1 ; Предел IDT dd ? ; Линейный адрес начала IDT ; Содержимое регистра IDTR в реальном режиме idtr_real dw 3FFh,0,0 ; Сообщения об ошибках при старте v86_msg db "Процессор в режиме v86 - нельзя переключиться в PM$" win_msg db "Программа на может бать запущена из под Windows$" ; Таблица для перевода OE скан-кодов в ASCII scan2ascii db 0,1Bh,'1','2','3','4','5','6','7','8','9','0','-','=',8 screen_addr dd 0 ; Текущая позиция на экране ; Точка входа в 32-х битный защищенный режим PM_entry: ; Установить 32-х битный стек и другие регистры mov ax, SEL_flatDS mov ds, ax mov es, ax mov ax, SEL_32bitSS mov ebx, stack_l mov ss, ax mov esp, ebx ; Разрешить прерывания sti ; и войти в вечный цикл jmp short $ ;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ;! Обработчики прерываний ! ;! Многие не корректны - нельзя допускать ошибок ! ;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ; Обработчик обычного прерывания int_handler: iretd ; Обработчик аппаратного прерывания IRQ0-IRQ7 irq0_7_handler: push eax mov al, 20h out 20h, al pop eax iretd ; Обработчик аппаратного прерывания IRQ8-IRQ15 irq8_15_handler: push eax mov al, 20h out 0A1h, al pop eax iretd ; Обработчик IRQ1 - прерывание от клавиатуры irq1_handler: push eax ; Это аппаратное прерывание - push ebx ; сохранить регистры push es push ds in al, 60h ; Прочитать скан-код нажатой клавиши cmp al, 0Eh ; Если он больше, чем обслуживаемый ja skip_translate ; нами, - не обрабатывать cmp al, 1 ; Если это ESC, je esc_pressed ; выйти в реальный режим mov bx, SEL_32bitDS ; Иначе: mov ds, bx ; DS:EBX - таблица для перевода mov ebx, offset scan2ascii ; скан-кода в ASCII xlatb ; Преобразовать mov bx, SEL_flatDS mov es, bx ; ES:EBX - адрес текущей mov ebx, screen_addr ; Позиции на экране cmp al, 8 ; Если нажата BkSpace je bs_pressed ; на соответствующий обработчик mov es:[ebx+0B8000h], al ; иначе послать символ на экран add dword ptr screen_addr, 2; Увеличить адрес позиции на 2 jmp short skip_translate bs_pressed: ; Нажат BkSpace: mov al,' ' ; Вывести пробел sub ebx, 2 ; в позиции предыдущего символа mov es:[ebx+0B8000h], al mov screen_addr, ebx ; и сохранить адрес передыд символа, ; как текущий skip_translate: ; Разрешить работу клавиатуры in al, 61h or al, 80h out 61h, al ; Послать EOI контроллеру прерываний mov al, 20h out 20h, al ; Восстановить регистры и выйти pop ds pop es pop ebx pop eax iretd ; ******************* ; Нажата ESC esc_pressed: ; Разрешить работу клавиатуры, послать EOI и восстановить регистры in al, 61h or al, 80h out 61h, al mov al, 20h out 20h, al pop ds pop es pop ebx pop eax ; Вернуться в реальный режим cli db 0EAh dd offset RM_return dw SEL_16bitCS PM_seg ends ; Сегмент стека, используется как 16-битный в 16-битной части программы и как ; 32-х битный (через селектор SEL_32bitSS) в 32-х битной части Stack_seg segment para stack "STACK" stack_start db 100h dup(?) stack_l = $-stack_start ; Длина стека для инициализации ESP Stack_seg ends end start