Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- .386p ; Чтобы разрешить транслятору обрабатывать расширенный набор команд 32-разрядного микропроцессора, часть к-х отн. к привелигерованным
- ; ОПИСАНИЕ СТРУКТУР
- ;структура для описания декскриптора сегмента
- descr struc
- lim dw 0 ; Граница (биты 0..15), номер последнего байта сегмента
- base_l dw 0 ; База, биты 0..15, начальный линейный адресс(номер байта, а не сегмент:смещение) сегмента в адресном пространстве процессора
- base_m db 0 ; база, биты 16..23
- attr_1 db 0 ; Байт атрибутов 1, характеристики сегментов.сегмент команд - 98h, сегмент данных(или стека) - 92h
- attr_2 db 0 ; Граница(биты 16..19) и атрибуы 2, страничная адресация или нет, тип дробности
- base_h db 0 ; База, биты 24..31
- descr ends
- ;структура для описания шлюзов ловушек
- trap struc
- offs_l dw 0 ; Смещение обработчика, биты 0..15
- sel dw 0 ; Селектор сегмента команд
- cntr db 0 ; Счетчик - не используется
- dtype db 8Fh; Тип шлюза - ловушка 80386 и выше
- offs_h dw 0 ; Смещение обработчика, биты 16..31
- trap ends
- ; СЕГМЕНТ КОДА ЗАЩИЩЕННОГО РЕЖИМА
- PROTECTED_MODE_SG SEGMENT PARA PUBLIC 'CODE' USE32
- ASSUME CS:PROTECTED_MODE_SG
- ; GDT - таблица глобальных дескрипторов GDT, в защищенном режиме обращение к сегментам возможно только через их дескрипторы
- gdt label byte
- gdt_null descr <0, 0, 0, 0, 0> ; Селектор 0, нулевой дескриптор
- gdt_flat descr <0FFFFh, 0, 0, 92h, 11001111b, 0>; Селектор 16, сегмент команд, 98h - исполняемый сегмент, запрещено чтение и запись
- gdt_code_16 descr <REAL_MODE_SG_size - 1, 0, 0, 98h, 0, 0>; Селектор 16, сегмент команд, 98h - исполняемый сегмент, запрещено чтение и запись
- gdt_code_32 descr <PROTECTED_MODE_SG_size - 1, 0, 0, 98h, 01000000b, 0>; Селектор 16, сегмент команд, 98h - исполняемый сегмент, запрещено чтение и запись
- gdt_data descr <PROTECTED_MODE_SG_size - 1, 0, 0, 92h, 11001111b, 0> ; Селектор 8, сегмент данных, 92h - разрешены чтение и запись
- gdt_stack descr <STACK_SIZE - 1, 0, 0, 92h, 01000000b, 0>; Селектор 24, сегмент стека
- gdt_size = $-gdt ; Размер GDT для передачи процессору вместе в адресом GDT
- gdtr df 0
- ; имена для селекторов
- SELECTOR_FLAT equ 8
- SELECTOR_CODE_16 equ 16
- SELECTOR_CODE_32 equ 24
- SELECTOR_DATA equ 32
- SELECTOR_STACK equ 40
- ; IDT
- idt label byte ; Метка начала таблицы дескрипторов исключений - IDT
- trap 32 dup (<dummy, SELECTOR_CODE_32, 0, 8Eh>) ; Первые 32 исключения
- int08 trap <0, SELECTOR_CODE_32, 0, 8Eh> ; Обработчик прерываний от таймера
- int09 trap <0, SELECTOR_CODE_32, 0, 8Eh> ; Обработчик прерываний от клавиатуры
- idt_size = $-idt ; Размер IDT
- idtr df 0
- idtr_real dw 3FFh, 0, 0 ; содержимое регистра idtR в реальном режиме
- master db 0 ;маска прерываний ведущего контроллера
- slave db 0 ;маска прерываний ведомого контроллера
- escape db 0 ;флаг - пора выходить в реальный режим, если ==1
- time_08 dd 0 ;счетчик прошедших тиков таймера
- ; Таблица перевода
- scan2ascii db 0,1Bh,'1234567890-=',8,0
- db 'qwertyuiop[]',13,0
- db 'asdfghjkl;''',0
- db 0,0,'zxcvbnm,./',0,0,0,' '
- ; Позиция начала ввода
- input_pos dd 0320h
- ; Сообщение - реальный режим
- msg db 'Return to real mode!', 27, '[0m$'
- ; Сообщение - защищенный режим
- msg_prot db 'P',4,'r',4,'o',4,'t',4,'e',4,'c',4,'t',4,'e',4,'d',4
- db ' ',4
- db 'm',4,'o',4,'d',4,'e',4,' ',4, 'o', 4, 'n', 4
- msg_prot_size = $-msg_prot
- ; Сообщение - таймер
- msg_time db 'T',6,'i',6,'m',6,'e',6,'r',6,':',6
- msg_time_size = $-msg_time
- ; Сообщение - память
- msg_mem db 'M',5,'e',5,'m',5,'o',5,'r',5,'y',5,':',5
- msg_mem_size = $-msg_mem
- ; точка входа в 32-битный защищенный режим
- PROTECTED_MODE_ENTRY:
- ; Сделать адресуемыми данные
- mov AX, SELECTOR_DATA ; Селектор сегмента данных
- mov DS, AX
- mov AX, SELECTOR_FLAT
- mov ES, AX
- ; Сделать адресуемыми стек
- mov AX, SELECTOR_STACK ; Селектор сегмента стека
- mov EBX, STACK_SIZE
- mov SS, AX
- mov ESP, EBX
- ; Защищенный режим
- mov ESI, offset msg_prot ; DS:ESI - сообщение
- mov EDI, 0B8000h ; ES:EDI - видеопамять
- mov ECX, msg_prot_size ; ECX - длина
- rep movsb ; вывод на экран
- ; Таймер
- mov ESI, offset msg_time ; DS:ESI - сообщение
- mov EDI, 0B80A0h ; ES:EDI - видеопамять
- mov ECX, msg_time_size ; ECX - длина
- rep movsb ; вывод на экран
- ; Память
- mov ESI, offset msg_mem ; DS:ESI - сообщение
- mov EDI, 0B8140h ; ES:EDI - видеопамять
- mov ECX, msg_mem_size ; ECX - длина
- rep movsb ; вывод на экран
- ; Разрешить обработку прерываний
- sti
- ; Вывести доступную память
- call show_memory
- ; Бесконечный цикл
- work:
- test escape, 1
- jz work
- ; запрещаем прерывания, всё по той же причине
- ;при этом немаскируемые уже запрещены, их не трогаем
- cli
- ; ВОЗВРАЩЕНИЕ В РЕАЛЬНЫЙ РЕЖИМ
- ; Сформироывть и загрузить дескрипторы для реального режима
- mov gdt_flat.lim, 0FFFFh
- mov gdt_data.lim, 0FFFFh
- mov gdt_code_16.lim, 0FFFFh
- mov gdt_code_32.lim, 0FFFFh
- mov gdt_stack.lim, 0FFFFh
- push DS ; Загрузить сегмент данных из теневого регистра
- pop DS
- push SS ; Стек
- pop SS
- push ES ; Видеопамять
- pop ES
- ; Загрузить регистр сегмента команд при помощи дальнего перехода
- db 0EAh
- dd offset REAL_MODE_RETURN
- dw SELECTOR_CODE_16
- ; Создание символа из цифры
- create_number macro
- local number1
- cmp DL, 10
- jl number1
- add DL, 'A' - '0' - 10
- number1:
- add DL, '0'
- endm
- ; Напечатать значение в eax в видеобуфер со смещение из ebp
- my_print_eax macro
- local prcyc1
- ; Занести регистры в стек
- push EBP
- push ECX
- push EDX
- ; Инициализация регистров
- mov ECX, 8 ; количество символов, которые распечатаем
- add EBP, 0B8010h ; печать числа с конца
- mov DH, 70h ; стиль символа
- ; Вывод в видеобуфер
- prcyc1:
- mov DL, AL ; кладём в ДЛ текущее значение АЛ (самый младший байт ЕАХ)
- and DL, 0Fh ; оставляем от него одно 16ричное число (последняя цифра)
- create_number 0 ; превращаем это число в символ
- mov ES:[EBP], DX ; заносим al в видеобуфер
- ror EAX, 4 ; циклически двигаем биты в ЕАХ
- sub EBP, 2 ; смещаемся на один символ влево (предыдущая цифра в ЕАХ)
- loop prcyc1 ; цикл по ecx
- ; Восстановить регистры из стека
- pop EBP
- pop EDX
- pop ECX
- endm
- ;заглушка для обработки первых 32х исключений
- dummy:
- nop
- iretd
- ; Обработчик прерывания от таймера
- new_08:
- ; Занести регистры в стек
- push EAX
- push EBP
- push ECX
- push EDX
- ; Считаем смещение для текста
- mov EBP, 0B8000h
- add EBP, 0A0h
- ; Вывести на экран текст
- mov ESI, offset msg_time ; DS:ESI - сообщение
- mov EDI, EBP ; ES:EDI - видеопамять
- mov ECX, msg_time_size ; ECX - длина
- rep movsb
- ; Указываем смещение видеобуфера
- mov EBP, 0A0h
- add EBP, msg_mem_size
- ; Вывести на экран eax - время
- mov EAX, time_08
- my_print_eax 0
- ; Инкремент счетчика времени
- inc time_08
- ; Отправить команду End of Interrupt ведущему контроллеру прерываний
- mov AL, 20h
- out 20h, AL
- ; Востановить регисты
- pop EDX
- pop ECX
- pop EBP
- pop EAX
- ; Выход из прерывания
- iretd
- ; Обработчик прерывания клавиатуры
- new_09:
- ; Занести регистры в стек
- push EAX
- push EBX
- push EDX
- push EBP
- ; прочитать скан-код нажатой клавиши,
- in AL, 60h
- ; нажат Enter - 1Ch
- cmp AL, 1Ch
- je enter_pressed
- ; если он больше, чем максимальный, пропустить
- cmp AL, 39h
- ja skip_translate
- ; DS:EBX - таблица для перевода скан-кода в ASCII
- mov EBX, offset scan2ascii
- xlatb
- ; Считаем смещение для выворда
- mov EBP, 0B8000h
- add EBP, input_pos
- ; Нажат backspace
- cmp AL, 8
- je bs_pressed
- ; Вывод символа
- mov AH, 0Bh ; color style
- mov ES:[EBP], AX
- add dword ptr input_pos, 2
- init:
- mov AL, 0Eh
- mov AL, BH
- out DX, AX
- mov AL, 0Fh
- mov AH, BL
- out DX, AX
- jmp short skip_translate
- bs_pressed:
- ; Вывести пробел в предыдущей позиции
- sub dword ptr input_pos, 2
- mov AL, ' '
- sub EBP, 2
- mov ES:[EBP], AX
- jmp init
- skip_translate:
- ; Разрешить работу клавиатуры
- in AL, 61h
- or AL, 80h
- out 61h, AL
- ; Послать EOI контроллеру прерываний
- mov AL, 20h
- out 20h, AL
- ; Востановить регисты
- pop EBP
- pop EDX
- pop EBX
- pop EAX
- ; Выход из прерывания
- iretd
- enter_pressed:
- mov escape, 1
- jmp skip_translate
- ; Подсчитать количество свободной памяти
- show_memory proc
- ; Занести сегмент данных в стек
- push DS
- ; Занести начальные значения
- mov AX, SELECTOR_FLAT ;кладем в него сегмент на 4 ГБ - все доступное виртуальное АП
- mov DS, AX
- mov EBX, 100001h ; пропускаем первый мегабайт оного сегмента
- ; мегабайт пропускаем потому, что в противном случае может произойти попытка редактирования процедуры собственного кода
- mov DL, 10101010b ; байт записи
- mov ECX, 0FFFFFFFFh ; количество оставшейся памяти (до превышения лимита в 4ГБ) - чтобы не было переполнения
- ; Цикл подсчета памяти
- check:
- mov DH, DS:[EBX] ;сохраняем в DH текущее значение по некоторому байту памяти
- mov DS:[EBX], DL ;кладём некоторое значение в этот байт
- cmp DS:[EBX], DL ;проверяем - считается обратно то же значение
- jnz end_of_memory ;если не совпало, то мы достигли дна (конец памяти)
- mov DS:[EBX], DH ;иначе восстанавливаем значение в памяти
- inc EBX
- loop check
- ; Завершить подсчет памяти
- end_of_memory:
- xor EDX, EDX
- mov EAX, EBX ;в EBX лежит количество посчитанной памяти в байтах; кладём его в EAX,
- mov EBX, 100000h ;делим на 1 Мб, чтобы получить результат в мегабайтах
- div EBX
- ; Восстанавливаем сегмент данных
- pop DS
- ; Вывести на экран текст
- mov ESI, offset msg_mem ; DS:ESI - сообщение
- mov EDI, 0B8140h ; ES:EDI - видеопамять
- mov ECX, msg_mem_size ; ECX - длина
- rep movsb
- ; Вывести количество доступной памяти
- push EBP
- mov EBP, 140h
- add EBP, msg_mem_size
- my_print_eax 0
- pop EBP
- ret
- show_memory endp
- PROTECTED_MODE_SG_size = $-GDT
- PROTECTED_MODE_SG ENDS
- ; СЕГМЕНТ СТЕКА
- STACK_SG SEGMENT PARA STACK 'STACK' USE32
- stack_main db 100h dup (?)
- STACK_SIZE = $-stack_main
- STACK_SG ENDS
- ; СЕГМЕНТ КОДА РЕАЛЬНОГО РЕЖИМА
- REAL_MODE_SG SEGMENT PARA PUBLIC 'CODE' USE16
- ASSUME CS:REAL_MODE_SG, DS:PROTECTED_MODE_SG, SS:STACK_SG
- ; MAIN
- main proc
- ; Очистить экран
- mov AX, 3h
- int 10h
- ; Настроить сегмент данных
- xor EAX, EAX
- mov AX, PROTECTED_MODE_SG ; Загрузим в DS сегментный
- mov DS, AX ; адресс сегмента данных
- ; Вычислить 32-битовый линейный адрес для сегмента команд и загрузить его в дескриптор GDT
- xor EAX, EAX
- mov AX, REAL_MODE_SG
- shl EAX, 4
- mov word ptr gdt_code_16.base_l, AX
- shr EAX, 16
- mov byte ptr gdt_code_16.base_m, AL
- mov byte ptr gdt_code_16.base_h, AH
- ; Вычислить 32-битовый линейный адрес для остальных сегментов
- mov AX, PROTECTED_MODE_SG
- shl EAX, 4
- mov word ptr gdt_code_32.base_l, AX
- mov word ptr gdt_stack.base_l, AX
- mov word ptr gdt_data.base_l, AX
- shr EAX, 16
- mov byte ptr gdt_code_32.base_m, AL
- mov byte ptr gdt_stack.base_m, AL
- mov byte ptr gdt_data.base_m, AL
- mov byte ptr gdt_code_32.base_h, AH
- mov byte ptr gdt_stack.base_h, AH
- mov byte ptr gdt_data.base_h, AH
- ; Подготовить псевдодескриптора и загрузить его в GDTR (6 байт: 0-1 граница, 2-5 линейный адрес )
- mov AX, PROTECTED_MODE_SG
- shl EAX, 4
- add EAX, offset gdt ; в eax будет полный линейный адрес GDT (адрес сегмента + смещение GDT относительно него)
- mov dword ptr gdtr + 2, EAX ; кладём полный линейный адрес в младшие 4 байта переменной gdtr
- mov word ptr gdtr, gdt_size - 1; в старшие 2 байта заносим размер gdt, из-за определения gdt_size (через $) настоящий размер на 1 байт меньше
- lgdt fword ptr gdtr
- ; Аналогично IDTR
- mov AX, PROTECTED_MODE_SG
- shl EAX, 4
- add eax,offset idt
- mov dword ptr idtr + 2,eax
- mov word ptr idtr, idt_size - 1
- lidt fword ptr idtr
- ; Заполним смещение в дескрипторах прерываний
- mov EAX, offset new_08
- mov int08.offs_l, AX
- shr EAX, 16
- mov int08.offs_h, AX
- mov EAX, offset new_09
- mov int09.offs_l, AX
- shr EAX, 16
- mov int09.offs_h, AX
- ; Сохранить маски прерываний контроллеров
- in AL, 21h ; ведущий, 21h - номер шины, in на неё даст нам набор масок (флагов)
- mov master, AL
- in AL, 0A1h
- mov slave, AL ; ведомый
- ; Перепрограммируем ведущий контроллер
- mov AL, 11h ; СЛИ1
- out 20h, AL
- mov AL, 32 ; СЛИ2 - бозовый вектор
- out 21h, AL
- mov AL, 4 ; СЛИ3
- out 21h, AL
- mov AL, 1 ; СЛИ4 - EOF
- out 21h, AL
- ; Запретим все прерывания в ведущем контроллере, кроме IRQ0 (таймер) и IRQ1(клавиатура)
- mov AL, 0FCh ; 0 - таймер, 1 - клавиатура
- out 21h, AL
- ; Запретим все прерывания в ведомом контроллере, иначе может прийти прерывание, для которого у нас нет обработчика
- mov AL, 0FFh
- out 0A1h, AL
- ; Если мы собираемся работать с 32-битной памятью, стоит открыть A20
- ; А20 - линия ("шина"), через которую осуществляется доступ ко всей памяти за пределами первого мегабайта
- ; если мы собираемся работать с 32-битной памятью, стоит открыть A20
- in AL, 92h ; получить набор флагов
- or AL, 2 ; добавить единичку во 2 бите
- out 92h, AL ; отправить обратно
- ; Запретить маскируемые прерывания
- cli
- ; Запретить немаскируемые прерывания
- in AL, 70h
- or AL, 80h
- out 70h, AL
- ; Переход в защищенный режим
- mov EAX, CR0
- or EAX, 1 ; установим бит защищенного режима
- mov CR0, EAX
- ; Переходив в сегмент защищенного режима, устанавливаем теневой регистр CS
- db 66h
- db 0EAh ; far jmp
- dd offset PROTECTED_MODE_ENTRY ; смещение
- dw SELECTOR_CODE_32 ; селектр сегмента команд
- REAL_MODE_RETURN:
- ; Переключить режим процессора на реальный режим
- mov EAX, CR0
- and EAX, 0FFFFFFFEh
- mov CR0, EAX
- ; Загрузить сегментный адрес в CS
- db 0EAh
- dw $+4 ; смещение - след строка
- dw REAL_MODE_SG ; сегмент
- ; Восстановить вычислительную среду реального режима
- mov AX, PROTECTED_MODE_SG
- mov DS, AX
- mov AX, SELECTOR_STACK
- mov EBX, STACK_SIZE
- mov ESP, EBX
- mov AX, STACK_SG
- mov SS, AX
- ; Восстановить ведущий контроллер
- mov AL, 11h
- out 20h, AL
- mov AL, 8
- out 21h, AL
- mov AL, 4
- out 21h, AL
- mov AL, 1
- out 21h, AL
- ; Восстановить маски прерываний
- mov AL, master
- out 21h, AL
- mov AL, slave
- out 0A1h, AL
- ; Загрузить таблицу дескрипторов прерываний реального режима
- lidt fword ptr idtr_real
- ; Разрешит немаскируемые прерывания
- in al,70h
- and al,07FH
- out 70h,al
- ; Разрешить аппаратные прерывания
- sti
- ; Очистить экран
- mov AX, 3
- int 10h
- ; Вывод сообщения о переходе в реальный режим
- mov AH, 09h
- mov DX, offset msg
- int 21h
- ; Завершить программу
- mov AX, 4C00h
- int 21h
- REAL_MODE_SG_SIZE = $-main
- REAL_MODE_SG ENDS
- END main
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement