- Разработать резидентную программу, которая выводит на экран содержимое 256 байт адресного пространства ЦП по 16 байт в одной строке, сегмент и смещение которого передаются через пару регистров на выбор. Вызвать свой обработчик из другой программы
- Номер прерывания получить путём сложения номера студента в группе и числа 128(0х80)
Необходимо реализовать обработчик прерывания, при этом для решения задачи по выводу адресного пространства можно использовать LR3, так как это одна и та же задача
Пробежимся по коду, попутно разбираясь что, где и как.
Это программа будет содержать резидентную часть, которая всегда будет находится в памяти компьютера
В самом начале программы находятся "организационные" команды
use16
org 100h
start_:
jmp init_
Первые две строки начальные директивы, а последняя - безусловных переход к следующему блоку кода
В этом блоке мы из обычной программы делаем резидентную:
init_:
mov ah, 0x25 ;Функция прерывания 0x21
mov al, 0x8b ;Номер вектора прерывания
mov dx, interruption_ ;Адрес-смещение обработчика прерывания
int 0x21 ;Запись параметров прерывания в память
mov dx, init_ ;Адрес-смещение следующего за резидентной программой
int 0x27 ;Прерывание для резидентной программы
mov ah, 0x25
- Задаём функцию прерывания 0x21. То есть указывает, что именно нужно будет сделатьmov al, 0x8b
- Указываем номер прерывания, по которому в последствии можно будет вызвать нашу программуmov dx, interruption_
- Указываем адрес-смещение. То есть кладём вDX
метку блока кода, к которому перейдёт программа при вызове прерыванияint 0x21
- Вызов прерывания. В нашем случае запись адреса прерывания в памятьmov dx, init_
- Указываем следующий блок кода, после резидентной программы.int 0x27
- Вызов прерывания. Переводим программу в формат резидентной.
Это блок кода, который начнёт выполняться, когда мы вызовем прерывание, ранее нами записанное в память в блоке INIT
. Рассмотрим код:
interruption_:
sti ;Разрешение маскированных прерываний
push ax ;Сохранение в стек значений регистров
push bx
push dx
push ds
mov ax, cs
mov ds, ax
mov es, bx
mov bx, 0
mov bx, 0x6e ;Записываем адрес смещения(из прошлой лабы)
call main
pop ds ;Возрат значений регистров
pop dx
pop bx
pop ax
iret
sti
- Ключевое слово, разрешающее маскированное прерывание(прерывание нашего типа)push <Регистр>
- Сохраняем значения регистров в стек, чтобы соблюсти концепцию сохранности данных при выполнении функцииmov ax, cs
mov ds, ax
- После вызова резидентной программы в cs записались данные, которые нужно положить в ds.mov bx, 0x6e
- Смещение в памяти из прошлой лабораторной. Необходимо для работы блокаmain
pop <Регистр>
- Возврашаем значения регистров, чтобы соблюсти правила по сохранности данныхiret
- Возврат из резидентной функции
В данном блоке находится сама программа по выводу. Про неё можно почитать в репизитории с LR3
main:
pusha
mov cx,16 ;Пробегаем по строкам
row_run:
push cx
mov cx,16 ;Пробегаем по столбцам
column_run:
mov al, byte[es:di] ;записали значение из памяти для вывода
call print_al
inc di ;сделали шаг для номера ячейки в памяти
call make_space
loop column_run
call make_endl
pop cx
loop row_run
popa
ret
;Вывод пробела в консоль
make_space:
pusha
mov ah, 0x2
mov dl, 0x20
int 21h
popa
ret
;Переход на новую строку
make_endl:
pusha
mov ah, 0x9
mov dx, new_line
int 21h
popa
ret
;Печать символа ASCII. В DL должен находиться код символа
print_one:
pusha
mov ah, 0x02
int 21h
popa
ret
print_al:
pusha
mov cx, 1
push ax ;Сохранили значение на будущее
shr al, 4 ;Сдвигаем, чтобы осталась первая цифра
and al, 0xf ;Оставляем нужную тетру
cmp al, 0x9 ;Проверяем, число или буква
ja symbol_wrd ;Если цифра больше 9, это буква
jmp symbol_figure ;Если цифра не больше 9, это цифра
back_get: ;Проверяем, делали ли мы второй блок
cmp cx, 0
je back_get_2
sub cx, 1 ;Учёт выполнения второго блока
pop ax ;Вернули значение на вывод
and al, 0xf ;Оставляем нужную тетру
cmp al, 0x9 ;Проверяем, число или буква
ja symbol_wrd ;Если цифра больше 9, это буква
jmp symbol_figure ;Если цифра не больше 9, это цифра
back_get_2:
popa
ret
symbol_wrd:
mov dl, al ;Запись значения
add dl, 0x37 ;Смещение по коду ASCII, чтобы получить нужный символ
call print_one
jmp back_get
symbol_figure:
mov dl, al ;Запись значения
add dl, 0x30 ;Cмещение по коду ASCII, чтобы получит нужный символ
call print_one
jmp back_get
new_line:
db 0xD, 0xA, '$'
Эта маленькая программа предназначена для вызова прерывания, которое мы написали в первой программе. Сам код:
use16
org 0x100
mov bx, 0x0
mov di, 0x0
int 8bh ;Вызов резидентной программы
mov ax, 0 ;Выход из программы
int 16h
int 20h
use16 и org 0x100
- Знакомые нам начальные директивыmov bx,0x0
mov di, 0x0
- Указываем сегмент и смещение для выводаint 8bh
- Вызов нашего прерывания из первой программы. 8bh аналогично записи 0x8bmov ax, 0
int 16h
int 20h
- Завершение программы
Чтобы всё работало правильно, выполняем следующие действия:
Запускаем первую программу
. После запуска первой программы в память будет записана резидентная программа, которую нужно будет вызватьЗапускаем вторую программу
. Вторая программа предназначена для вызова прерывания. После её запуска будет выполнен код, который мы указали при создании прерывания. В нашем случае это блокInterruption
(Этот блок может иметь любое название)
В случае, если что-то у вас не работает или вам не понятно, не стесняйтесь открывать issue
. Обязательно посмотрю и постараюсь помочь.