- •1. Основы программирования для dos 9
- •1.1.7. Средства bios 21
- •2. Приемы системного программирования 57
- •Введение
- •1.Основы программирования для dos
- •1.1. Структура и выполнение программ в dos
- •Программа типа сом
- •Программа типа ехе
- •1.2.Использование служебных функций dos и bios
- •1.3.Вывод на экран в текстовом режиме
- •Средства dos
- •Средства bios
- •Выбор видеорежима
- •Управление положением курсора
- •Вывод символов на экран
- •Прямая работа с видеопамятью
- •1.4.Ввод с клавиатуры
- •Средства dos
- •Средства bios
- •1.5.Графические видеорежимы
- •Работа со стандартными графическими режимами
- •Работа с svga-режимами
- •(Продолжение таблицы 4)
- •(Окончание таблицы 4)
- •1.6.Работа с мышью
- •1.7.Другие устройства
- •Системный таймер
- •Последовательный порт
- •Параллельный порт
- •1.8.Работа с файлами
- •Создание и открытие файлов
- •Чтение из файла и запись в файл
- •Закрытие и удаление файла
- •Поиск файлов
- •Управление файловой системой
- •1.9.Управление памятью
- •Обычная память
- •Область памяти umв
- •Область памяти нма
- •Интерфейс ems
- •Интерфейс xms
- •1.10.Организация процессов
- •Запуск программ на выполнение
- •Переменные окружения
- •Командные параметры
- •2.Приемы системного программирования
- •2.1.Управляющие структуры
- •Структуры if... Then... Else
- •Структуры case
- •Конечные автоматы
- •2.2.Процедуры и функции
- •Передача параметров
- •Передача параметров по значению
- •Передача параметров по ссылке
- •Передача параметров по возвращаемому значению
- •Передача параметров по результату
- •Передача параметров по имени
- •Передача параметров отложенным вычислением
- •Передача параметров в регистрах
- •Передача параметров в глобальных переменных
- •Передача параметров в стеке
- •Передача параметров в потоке кода
- •Передача параметров в блоке параметров
- •Локальные переменные
- •2.3.Обработка прерываний
- •Обработчики прерываний
- •Прерывания от внешних устройств
- •Взаимодействие прикладных и системных обработчиков прерываний
- •2.4.Резидентные программы
- •Системные средства организации резидентных программ
- •Взаимодействие с резидентной программой
- •2.5.Драйверы устройств в dos
- •Литература
2.1.Управляющие структуры
Структуры if... Then... Else
Это часто встречающаяся управляющая структура, передающая управление на один участок программы, если некоторое условие выполняется, и на другой, если оно не выполняется, записывается на ассемблере в следующем общем виде:
(набор команд проверяющих условие)
Jcc Else
(набор команд соответствующих блоку THEN)
Jmp Endif
Else: (набор команд соответствующих блоку ELSE)
Endif:
Для сложных условий часто оказывается, что одной командой условного перехода обойтись нельзя, так что реализация проверки может значительно увеличиться, например, следующую строку на языке С
if (((х>у) && (z<t)) || (a! = b)) c=d;
можно представить на ассемблере как:
;проверка условия
mov ax, A
cmp ax, В
jne then ; если а! = b – условие выполнено
mov ax, X
cmp ax, Y
jng endif ; если х < или = у – условие не выполнено
mov ax, Z
cmp ах, Т
jnl endif ; если z > или = t – условие не выполнено
then: ; условие выполняется
mov ах, 0
mov С, ах
endif:
Структуры case
Управляющая структура типа CASE проверяет значение некоторой переменной (или выражения) и передает управление на различные участки программы. Кажется очевидным, что эта структура должна реализовываться в виде серии структур IF... THEN... ELSE.
Пусть переменная i принимает значения от 0 до 2, и в зависимости от значения надо выполнить процедуры case0, case1 и case2:
mov ax, i
cmp ax, 0 ; проверка на 0
jne not0
call case0
jmp endcase not0: cmp ax, 1 ; проверка на 1
jne not1
call case1
jmp endcase not1: cmp ax, 2 ; проверка на 2
jne not2
call case2
not2: endcase:
Но ассемблер предоставляет более удобный способ реализации таких структур — таблицу переходов.
mov bx, i
shl bx, 1 ; умножить ВХ на 2 (размер адреса в таблице
; переходов – 4 для 32-битных адресов)
jmp cs: jump_table[bx] ; разумеется,
; в этом примере достаточно использовать call
jump_table dw foo0, foo1, foo2 ; таблица переходов
foo0: call case0
jmp endcase foo1: call case1
jmp endcase foo2: call case2
jmp endcase
Очевидно, что для большого числа значений переменной способ с таблицей переходов гораздо быстрее (не требуется многочисленных проверок), а если большая часть значений переменной — числа, следующие в точности друг за другом (так что в таблице переходов не окажется пустых участков), то эта реализация структуры CASE окажется еще и значительно меньше.
Конечные автоматы
Конечный автомат — процедура, которая помнит свое состояние и при обращении к ней выполняет различные действия для разных состояний. Например, рассмотрим процедуру, которая складывает регистры АХ и ВХ при первом вызове, вычитает при втором, умножает при третьем, делит при четвертом, снова складывает при пятом и т. д. Очевидная реализация, опять же, состоит в последовательности условных переходов:
state db 0
state_machine:
cmp state, 0
jne not_0
; состояние 0: сложение
add ax, bx
inc state
ret
not_0: cmp state, 1
jne not_1
; состояние 1: вычитание
sub ax, bx
inc state
ret
not_1: cmp state, 2
jne not_2
; состояние 2: умножение
push dx
mul bx
pop dx
inc state
ret
; состояние 3: деление
not_2: push dx
xor dx, dx
div bx
pop dx
mov state, 0
ret
Оказывается, что, как и для CASE, в ассемблере есть средства для более эффективной реализации этой структуры. Это все тот же косвенный переход, использованный выше для CASE:
state dw offset state_0
state_machine:
jmp state
state_0: add ax,bx ; состояние 0: сложение
mov state, offset state_1
ret
state_1: sub ax, bx ; состояние 1: вычитание
mov state, offset state_2
ret state_2: push dx ; состояние 2: умножение
mul bx
pop dx
mov state, offset stdte_3
ret state_3: push dx ; состояние 3: деление
xor dx, dx
div bx
pop dx
mov state, offset state_0
ret
Как и в случае с CASE, использование косвенного перехода приводит к тому, что не требуется никаких проверок и время выполнения управляющей структуры остается одним и тем же для четырех или четырех тысяч состояний.
Циклы
Несмотря на то что набор команд Intel включает команды организации циклов, они годятся только для одного типа циклов — FOR-циклов, которые выполняются фиксированное число раз. В общем виде любой цикл записывается в ассемблере как условный переход.
WHILE-цикл: (команды инициализации цикла)
метка: IF (не выполняется условие окончания цикла)
THEN
(команды тела цикла)
jmp-меткa
REPEAT/UNTIL-цикл:
(команды инициализации цикла)
метка: (команды тела цикла)
IF (не выполняется условие окончания цикла)
THEN (переход на метку)
(такие циклы выполняются быстрее на ассемблере и всегда следует стремиться переносить проверку условия окончания цикла в конец)
LOОР/ENDLOОР-цикл:
(команды инициализации цикла)
метка:
(команды тела цикла)
IF (выполняется условие окончания цикла)
THEN jmp метка2
(команды тела цикла)
jmp-метка
метка2: