; Работа студента группы 2084/3 ; Макаренко Антона ; по курсу ЦВМ ; Программа, производит переход в Protrcted Mode, ; перепрограмирует контроллер прерываний и производит ; перехват прерывания от клавиатуры. ; Макрос преобразования смещения из 32-х битного сегмента в 16-битную переменную so equ small offset ; Макрос задержки. На входе - значение переменной задержки (в мкс). delay macro time local ext,iter push ecx mov ecx,time ext: push ecx mov cx,5000 ;это значение можно поменять, исходя из производительности процессора. iter: loop iter pop ecx loop ext pop ecx endm .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 dw so timer_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 ; Текущая позиция на экране N dd 64 ; Индекс в массиве MUS NM dd 1 ; Счетчик длительности ; Массив пар звучания: 1-е число - частота, 2-е - длительность MUS dw 1218,10, 767,10, 813,10, 912,10, 966,10, 912,30 dw 1218,10, 609,10, 678,10, 767,10, 813,10, 767,30 dw 912,10, 574,15, 609,5, 678,15, 767,5, 813,5 dw 767,5, 678,20, 574,10, 609,10, 678,10, 767,10 dw 678,10, 813,30, 1218,10, 767,10, 813,10, 912,10 dw 966,10, 912,30, 1218,10, 609,10, 678,10, 767,10 dw 813,10, 767,30, 912,10, 574,15, 609,5, 678,15 dw 767,5, 813,5, 767,5, 678,20, 574,10, 609,10 dw 678,10, 767,10, 678,10, 813,30, 0,2, 609,10 dw 609,10, 912,10, 724,10, 609,10, 609,5, 678,5 dw 724,5, 678,5, 574,15, 678,5, 678,10, 1024,10 dw 813,10, 678,10, 678,5, 767,5, 813,5, 724,5 dw 609,20, 678,5, 609,5, 574,5, 456,5, 342,5 dw 384,5, 406,5, 456,5, 456,20, 483,20, 678,5 dw 609,5, 574,5, 456,5, 342,5, 384,5, 406,5 dw 456,5, 456,10, 483,10, 456,20, 0,2, 456,5 dw 456,10, 483,10, 456,20, 0,30 ;MC = ($-MUS)/4; Music Count - количество нот MC = 100 ; Точка входа в 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 ;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ; Обработчик прерывания от таймера (int8) ! ;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! timer_handler: ; Сохранить регистры push eax push ebx push es push ds mov bx, SEL_32bitDS ; Загрузить сегмент данных mov ds, bx dec NM ; Текущее время звучания mov eax, NM ; Если не истекло, cmp eax, 0 jnz contin_mus ; то ничего не делать, иначе ; Выключить звук in al, 61h and al, 0FCh out 61h, al delay 1 ; Пауза ; Выбрать высоту следущеюй ноты inc N ; Увеличить на ед. mov eax, N ; Текущая позиция mov ebx, 4 ; ! Размер пары в байтах mul ebx ; Смещение пары, относительно начала массива add eax, offset MUS ; Адрес push eax ; сохранить ; xor eax, eax mov ax, [eax] ; В AX - частота ; Начать звучание mov ebx, eax ; Подготовка shr ebx, 8 ; параметров для and ax, 00FFh ; записи в порты push eax mov ax, 0B6h ; \ out 43h, ax ; _\outportb(0x43, 0xB6) pop eax ; EAX = Частота & 0x00FF out 42h, ax xchg ax, bx ; ax = Частота >> 8 out 42h, ax in al, 61h or al, 03h out 61h, al ; Выбрать длительность pop ebx ; EBX = адрес текущей пары xor eax, eax mov ax, word ptr [ebx+2] ; EAX = Длительность mov NM, eax cmp N, MC ; Если не в конце мелодии, jb contin_mus ; то ничего, mov N, 0 ; иначе - в начало массива contin_mus: ; mov NM, eax ; Сохранить текущее время звучания ; Снять с контроллера mov al, 20h out 20h, al ; Восстановить регистры, выйти pop ds pop es pop ebx 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 cli ; Отключить звук in al, 61h and al, 0FCh out 61h, al pop ds pop es pop ebx pop eax ; Вернуться в реальный режим 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