Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Отчет / Лаба.docx
Скачиваний:
21
Добавлен:
15.09.2014
Размер:
37.06 Кб
Скачать

3. Листинг программы:

.model tiny

.code

.386p

org 100h

;;;;;;;;;;;;;;;;;;;;;;;

; Структура

;;;;;;;;;;;;;;;;;;;;;;;

; Сегментный дискриптор

segment_descriptor struc

limit_low dw 0 ; Младшие два байта поля Segment limit

base_low dw 0 ; Младшие два байта поля Base Address

base_high0 db 0 ; Второй байт поля Base Address

type_and_permit db 0 ; Флаги

flags db 0 ; Ещё одни флаги

base_highl db 0 ; Старший байт поля Base Address

segment_descriptor ends

;вместо дальних адресов в таблице прерываний используются

;дескрипторы специальных системных объектов, так называемых шлюзов

; Дескриптор шлюза

gate_descriptor struc

offset_low dw 0 ;Два младших байта поля Offset

selector dw 0 ; Поле Segment Selector

zero db 0

type_and_permit db 0 ;Флаги

offset_high dw 0 ; Старшие байты поля Offset

gate_descriptor ends

; Регистр, описывающий таблицу

table_register struc

limit dw 0 ; Table Limit

base dd 0 ; Linear Base Address

table_register ends

;;;;;;;;;;;;;;;;;;;;;;

; Код

;;;;;;;;;;;;;;;;;;;;;;

start:

;Подготавливаем DS

push cs

pop ds

;В es - начало видеобуфера. Можно было сделать то же

;самое средствами защищенного режима, но так проще

push 0b800h

pop es

;Устанавливаем правильный сегмент в long-jmp-to-RM

mov ax, cs

mov cs:rm_cs, ax

;Прописываем адрес начала cs в качестве базового адреса ;сегмента

call cs_to_eax

mov dsc64kb.base_low, ax

shr eax, 16

mov dsc64kb.base_high0, al

;Сохраняем IDTR реального режима

;sidt - Stores the Interrupt Descriptor Table (IDT) Register ;into the specified operand.

sidt fword ptr old_idtr

;Запретили прерывания

call disable_interrupts

;Инициализируем GDT

call initialize_gdt

;Инициализируем IDT

call initialize_idt

;Переключаем режим

call set_PE

;16-разрядный дальний переход. Переключает содержимое cs из нормального

;для реального режима (адрес) в нормальное для защищенного (селектор).

;Базовый адрес целевого сегмента совпадает с cs,

;поэтому смещение можно прописать сразу

db OEAh ;код команды дальнего перехода

dw $ + 4 ;смещение

dw 8 ;селектор

; В данный момент сегмент кода - 64 Кб, базовый адрес равен

; адресу сегмента кода до переключения в защищенный режим.

;Сохраняем состояние масок ПКП

in al, 021h

mov old_maskl, al

in al, 0Alh

mov old_mask2, al

;Инициализация ПКП, в bl и bh - базовые номера прерываний

;Ведущий ПКП ставим с 20h, ведомый с 28h

mov bl, 020h

mov bh, 028h

call initialize_pic

mov al, Ofbh ;на ведущем маскируем всё, кроме IRQ2

out 021h, al

mov al, Ofeh ;на ведомом маскируем все, кроме IRQ8 (RTC)

out OAlh, al

;Установка частоты периодического прерывания RTC

mov al, Oah

out 70h, al

in al, 71h

or al, 0fh ;2 раза в секунду

out 71h, al

;Разрешение периодического прерывания RTC

mov al, Obh

out 70h, al

in al, 71h

or al, 01000000b

out 71h, al

mov ecx, 10 ;в ecx - счётчик прерываний

call enable_interrupts

;в цикле ждём, пока прерывание произойдёт 10 раз и обнулит есх

test_end:

cmp ecx, 0

jne test_end

call disable_interrupts

;Запрещаем переодическое прерывание от RTC

mov al, 0bh

out 70h, al

in al, 71h

and al, 10111111b

out 71h, al

;Возвращаем стандартные номера прерываний

mov bl, 08h

mov bh, 70h

call initialize_pic

mov al, old_maskl ;снимаем маскировку

out 021h, al

mov al, old_mask2 ;снимаем маскировку

out 0Alh, al

call clear_PE

;Мы в реальном режиме, осталось разобраться с ; значением регистра cs

;16-разрядный дальний переход. Перключает содержимое cs из нормального

;для защищённог режима (селектор) в нормальное для реальног (адрес).

;Адрес сегмента вычисляется и прописывается во время выполнения,

db OEAh ; код команды дальнего перехода

dw $ + 4 ; смещение

rm_cs dw 0 ; сегмент

;восстанавливаем IDTR реального режима

lidt fword ptr old_idtr

;разрешаем прерывания

call enable_interrupts

ret

;;;;;;;;;;;;;;;;;;;;;;;;

; Обработчик прерывания

;;;;;;;;;;;;;;;;;;;;;;;;

;Обработчик прерывания RTC, ведёт обратный отсчёт

intRtc_handler:

push eax

push bax

dec ecx ;уменьшение ecx

mov bh, 07h ;белый текст на чёрном фоне

mov bl, '0'

add bl, cl ;если cl меньше 10, то в bl ;соответствующая цифра

mov eax, 80*24*2

mov word ptr es:[eax], bx

call upscroll_screen ; прокручивает экран на строчку вверх

;читаем из регистра 0Ch RTC, иначе прерываний больше не будет

mov al, 0ch

out 70h, al

in al, 71h

mov al, 020h ; "неспецифичное" завершение прерывания

out 020h, al ; на ведущем ПКП

out 0A0h, al ; на ведомом ПКП

pop ebx

pop eax

iretd ; 32-х разрядный возврат из прерывания

;;;;;;;;;;;;;;;;;;;;;

;Данные

;;;;;;;;;;;;;;;;;;;;;

;Глобальная таблица дескрипторов

GDT label byte

;Нулевой дескриптор

segment_descriptor <>

;Дескриптор сегмента кода, размер 64 Kb

dsc64kb segment_descriptor <0ffffh, 0, 0, 10011010b, 0, 0>

;10011010b - 1001, C/D - 1, 0, R/W - 1, 0

;0 - G - 0, 000, Limit - 0

;Данные для загрузки в GDTR

gdtr table_register <$ - GDT - 1, 0>

;Таблица дескрипторов прерываний

IDT label byte

db 32 dup ( 8 dup (0)) ; 0 - 1Fh

db 8 dup ( 8 dup (0)) ; 20h - 27h, ведущий ПКП

;Дескриптор шлюза прерывания.

;Обработчик прерывания находится в сегменте, соответствующем первому

;дескриптору GDT. Поскольку базовый адрес сегмента такой же, как

;в реальном режиме, смещение обработчика тоже совпадает.

gate_descriptor <intRtc_handler, 8, 0, 8Eh, 0>

;Данные для загрузки в IDTR

idtr table_register <$ - IDT - 1, 0>

;Место для хранения IDTR реального режима

old_idtr table_register <>

;Место для хранения старых значений масок ПКП

old_maskl db 0

old_mask2 db 0

;;;;;;;;;;;;;;;;;;;;;;

;Служебные функции

;;;;;;;;;;;;;;;;;;;;;;

;Инициализирует IDT (Interrupt Descriptor Table)

initialize_idt:

;Вычисляем линейный адрес начала массива дескрипторов

call cs_to_eax

add eax, offset IDT

;Записываем его в структуру

mov idtr.base, eax

;Загружаем IDTR

lidt fword ptr idtr

ret

;Инициализирует GDT глобальную дескрипторную таблицу

initialize_gdt:

;Вычисляем линейный адрес начала массива дескрипторов

call cs_to_eax

add eax, offset GDT

;Записываем его в структуру

mov gdtr.base, eax

;Загружаем GDTR (lgdt - Load Global Descriptor Table)

;команда выполняет загрузку 16 бит размера и 32 бит значения ;базового адреса начала таблицы GDT в памяти

;в системный регистр gdtr. Эта загрузка производится в ;соответствии с форматом этого регистра.

lgdt fword ptr gdtr

ret

;Запрещает маскируемые и немаскируемые прерывания

disable_interrupts:

cli ; запретить прерывания

in al, 70h ; индексный порт CMOS

or al, 80h ; установка бита 7 в нем запрещает NMI

out 70h, al

ret

; Разрешает маскируемые и немаскируемые прерывания

enable_interrupts:

in al, 70h ; индексный порт CMOS

and al, 7Fh ; сброс бита 7 отменяет блокирование NMI

out 70h, al

sti ; разрешить прерывания

ret

;Устанавливает флаг РЕ

set_PE:

mov eax, cr0 ; прочитать регистр CR0

or al, 1 ; установить бит РЕ,

mov cr0, eax ; с этого момента мы в защищенном режиме

ret

;Сбрасывает флаг РЕ

clear_PE:

mov eax, cr0 ;прочитать CR0

and al, 0FEh ;сбросить бит РЕ

mov cr0, eax ;с этого момента мы в реальном режиме

ret

;Вычисляет линейный адрес начала сегмента кода

cs_to_eax:

mov eax, 0

mov al, cs

shl eax, 4

ret

;Инициализация ПКП.

;bl - базовый номер прерывания ведущего ПКП

;bh - базовый номер прерывания ведомого ПКП

initialize_pic:

push eax

mov al, 00010001b ;ICW1

out 020h, al

out 0A0h, al

mov al, bl ;ICW2, ведущий

out 021h, al

mov al, bh ;ICW2, ведомый

out 0Alh, al

mov al, 00000100b ;ICW3, ведущий

out 021h, al

mov al, 2 ;ICW3, ведомый

out 0Alh, al

mov al, 00000001b

out 021h, al

out 0Alh, al

pop eax

ret

;Прокручивает экран на строчку вверх

upscroll_screen:

push eax

push ecx

push edx

mov eax, 0 ;Текущий символ

mov ecx, 80*24 ;Колическтво символов на экране

;(без последней строки, она отдельно)

screen_loop:

mov dx, word ptr es:[eax * 2 + 80*2]

mov word ptr es:[eax * 2], dx ;Меняем символ

inc eax

loop screen_loop

mov ecx, 80 ;Длинна последней строки

mov dx,0720h ;символ, которым заполняется строка

last_line_loop:

mov word ptr es:[eax * 2], dx ; Меняем символ

inc eax

loop last_line_loop

pop edx

pop ecx

pop eax

ret

end start

Соседние файлы в папке Отчет