- •Содержание
- •Введение
- •ДЕНЬ 1
- •Знакомство с архитектурой компьютера
- •1.1. Что такое архитектура компьютера
- •1.2. Системы счисления
- •1.3. Биты и байты
- •1.4. Фон-неймановская архитектура
- •1.5.1. Режимы работы процессора
- •1.5. Процессор
- •1.5.2. Регистры процессора
- •1.5.2.1. Пользовательские регистры
- •1.5.2.1.1. Регистры общего назначения
- •1.5.2.1.2. Сегментные регистры
- •1.5.2.1.3. Регистр флагов и указателя команд
- •1.5.2.2.Системные регистры
- •1.5.2.3. Регистры FPU и MMX
- •1.5.2.4. Регистры XMM (расширение SSE/SSE2)
- •1.6. Память
- •1.8. Шины
- •ДЕНЬ 2
- •Основы программирования на ассемблере
- •2.1. Какой ассемблер выбрать
- •2.2. Этапы создания программы
- •2.3. Структура программы
- •2.3.1. Метка
- •2.3.2. Команда или директива
- •2.3.3. Операнды
- •2.3.4. Комментарий
- •2.4. Некоторые важные директивы ассемблера
- •2.4.1. Директивы определения данных
- •2.4.2. Директива эквивалентности
- •2.4.3. Директива присваивания
- •2.4.4. Директивы задания набора допустимых команд
- •2.4.5. Упрощенные директивы определения сегмента
- •2.4.6. Директива указания модели памяти
- •2.5. Разработка нашей первой программы на ассемблере
- •2.5.1. Программа типа COM
- •2.5.2. Программа типа EXE
- •2.6. Основные различия между программами типа EXE и COM
- •2.7. Функции BIOS и DOS
- •2.8. Префикс программного сегмента (PSP)
- •2.9. Знакомство с отладчиком
- •2.10. Младший байт по младшему адресу
- •ДЕНЬ 3
- •Основные конструкции ассемблера
- •3.1. Цикл
- •3.2. Безусловный переход
- •3.3. Сравнение и условные переходы
- •3.4. Стек
- •3.5. Подпрограммы (процедуры)
- •3.6. Директива INCLUDE
- •3.7. Конструкции времени исполнения программы
- •3.8. Директивы условного ассемблирования
- •3.9. Макросы
- •3.9.1. Блоки повторений
- •ДЕНЬ 4
- •Основные команды ассемблера
- •4.1. Команды пересылки
- •4.2. Оператор PTR
- •4.3. Способы адресации
- •4.3.1. Непосредственная адресация
- •4.3.2. Регистровая адресация
- •4.3.3. Косвенная адресация
- •4.3.4. Прямая адресация (адресация по смещению)
- •4.3.5. Базовая адресация
- •4.3.6. Индексная адресация
- •4.3.7. Базовая-индексная адресация
- •4.3.8. Адресация по базе с индексированием и масштабированием
- •4.4. Относительные операторы
- •4.5. Логические команды
- •4.6. Команды сдвига
- •4.6.1. Команды линейного (нециклического) сдвига
- •4.6.2. Команды циклического сдвига
- •4.7. Команды обработки строк/цепочечные команды
- •4.7.1. Команды пересылки цепочек
- •4.7.2. Команды сравнения цепочек
- •4.7.3. Команды сканирования цепочек
- •4.7.4. Команды загрузки элемента из цепочки в аккумулятор
- •4.7.6. Команды ввода элемента цепочки из порта ввода-вывода
- •4.7.7. Команды вывода элемента цепочки в порт ввода-вывода
- •4.8. Команды работы с адресами и указателями
- •4.9. Команды трансляции (преобразования) по таблице
- •ДЕНЬ 5
- •Арифметические команды. Сопроцессор
- •5.1. Арифметические операторы
- •5.2. Команды выполнения целочисленных операций
- •5.2.1. Целые двоичные числа
- •5.2.2. BCD-числа
- •5.2.3. Команды, работающие с целыми двоичными числами
- •5.2.3.1. Сложение и вычитание
- •5.2.3.2. Инкремент и декремент
- •5.2.3.3. Умножение и деление
- •5.2.3.4. Изменение знака числа
- •5.2.4. Ввод и вывод чисел
- •5.2.5.1. Сложение и вычитание неупакованных BCD-чисел
- •5.3.1. Вычисления с фиксированной запятой
- •5.2.5.2. Умножение и деление неупакованных BCD-чисел
- •5.2.5.3. Сложение и вычитание упакованных BCD-чисел
- •5.3. Команды выполнения операций с вещественными числами
- •5.3.2. Вычисления с плавающей запятой
- •5.3.2.1. Сравнение вещественных чисел
- •5.4. Архитектура сопроцессора
- •5.4.1. Типы данных FPU
- •5.4.2. Регистры FPU
- •5.4.2.1. Регистры данных R0-R7
- •5.4.2.2. Регистр состояния SWR (Status Word Register)
- •5.4.2.3. Регистр управления CWR (Control Word Register)
- •5.4.2.4. Регистр тегов TWR (Tags Word Register)
- •5.4.2.5. Регистры-указатели команд IPR (Instruction Point Register) и данных DPR (Data Point Register)
- •5.4.3. Исключения FPU
- •5.4.4. Команды сопроцессора
- •5.4.4.1. Команды пересылки данных FPU
- •5.4.4.2. Арифметические команды
- •5.4.4.3. Команды манипуляций константами
- •5.4.4.4. Команды управления сопроцессором
- •5.4.4.5. Команды сравнения
- •5.4.4.6. Трансцендентные команды
- •ДЕНЬ 6
- •Программирование под MS-DOS
- •6.2. Вывод на экран в текстовом режиме
- •6.2.1. Функции DOS
- •02h (INT 21h) — вывод символа с проверкой на <Ctrl>+<Break>
- •06h (INT 21h) — вывод символа без проверки на <Ctrl>+<Break>
- •09h (INT 21h) — вывод строки на экран с проверкой на <Ctrl>+<Break>
- •40h (INT 21h) — записать в файл или на устройство
- •INT 29h — быстрый вывод символа на экран
- •6.2.2. Прямая запись в видеопамять
- •6.3. Ввод с клавиатуры
- •6.3.1. Функции DOS
- •01h (INT 21h) — ввод символа с эхо
- •06h (INT 21h) — ввод-вывод через консоль
- •07h (INT 21h) — нефильтрованный ввод без эхо
- •08h (INT 21h) — ввод символа без эхо
- •0Ah (INT 21h) — буферизированный ввод с клавиатуры
- •0Bh (INT 21h) — проверить состояние ввода
- •0Ch (INT 21h) — очистить буфер и считать символ
- •3Fh (INT 21h) — чтение из файла или устройства
- •6.3.2. Функции BIOS
- •00h, 10h, 20h (INT 16h) — прочитать символ с клавиатуры с ожиданием
- •01h, 11h, 21h (INT 16h) — проверка символа
- •02h, 12h, 22h (INT 16h) — считать состояние клавиатуры
- •6.4. Работа с файлами
- •6.4.1. Создание и открытие файлов
- •3Ch (INT 21h) — создать файл
- •3Dh (INT 21h) — открыть существующий файл
- •5Bh (INT 21h) — создать и открыть существующий файл
- •5Ah (INT 21h) — создать и открыть временный файл
- •6Ch (INT 21h) — создать или открыть файл с длинным именем
- •6.4.2. Чтение и запись в файл
- •3Fh (INT 21h) — чтение из файла или устройства
- •42h (INT 21h) — установить указатель чтения/записи
- •40h (INT 21h) — записать в файл или на устройство
- •68h (INT 21h) — сброс файловых буферов MS-DOS на диск
- •0Dh (INT 21h) — сброс всех файловых буферов на диск
- •6.4.3. Закрытие и удаление файла
- •3Eh (INT 21h) — закрыть файл
- •41h (INT 21h) — удалить файл
- •LFN 41h (INT 21h) — удалить файл c длинным именем
- •6.4.4. Поиск файлов
- •4Eh (INT 21h) — найти первый файл
- •4Fh (INT 21h) — найти следующий файл
- •LFN 4Eh (INT 21h) — найти первый файл с длинным именем
- •LFN 4Fh (INT 21h) — найти следующий файл
- •LFN A1h (INT 21h) — закончить поиск файла
- •6.4.5. Управление директориями
- •39h (INT 21h) — создать директорию
- •LFN 39h (INT 21h) — создать директорию с длинным именем
- •3Ah (INT 21h) — удалить директорию
- •LFN 3Ah (INT 21h) — удалить директорию с длинным именем
- •47h (INT 21h) — определить текущую директорию
- •LFN 47h (INT 21h) — определить текущую директорию с длинным именем
- •3Bh (INT 21h) — сменить директорию
- •LFN 3Bh (INT 21h) — сменить директорию с длинным именем
- •6.5. Прерывания
- •6.5.1. Внутренние и внешние аппаратные прерывания
- •6.5.2. Запрет всех маскируемых прерываний
- •6.5.3. Запрет определенного маскируемого прерывания
- •6.5.4. Собственный обработчик прерывания
- •Функция 35h (INT 21h) — получить вектор прерываний
- •Функция 25h (INT 21h) — установить вектор прерываний
- •6.5.5. Распределение номеров прерываний
- •ДЕНЬ 7
- •7.2. Первая простейшая программа под Windows на ассемблере
- •7.2.1. Директива INVOKE
- •7.3. Консольное приложение
- •7.4. Графическое приложение
- •7.4.1. Регистрация класса окон
- •7.4.2. Создание окна
- •7.4.3. Цикл обработки очереди сообщений
- •7.4.4. Процедура главного окна
- •7.5. Дочерние окна управления
- •7.6. Использование ресурсов
- •7.6.1. Подключение ресурсов к исполняемому файлу
- •7.6.2. Язык описания ресурсов
- •7.6.2.1. Пиктограммы
- •7.6.2.2. Курсоры
- •7.6.2.3. Растровые изображения
- •7.6.2.4. Строки
- •7.6.2.5. Диалоговые окна
- •7.6.2.6. Меню
- •7.7. Динамические библиотеки
- •7.7.1. Простейшая динамическая библиотека
- •7.7.2. Неявная загрузка DLL
- •7.7.3. Явная загрузка DLL
- •Приложение 1. Основные технические характеристики микропроцессоров фирмы Intel
- •Приложение 2. Таблицы кодов символов
- •Приложение 3. Сравнение двух синтаксисов ассемблера
- •Список литературы
http://www.sklyaroff.ru |
92 |
Физически команда просто вычитает операнд из нуля (операнд = 0 – операнд). При этом выполняются все правила представления чисел со знаком, о которых мы говорили выше, т. е. числа конвертируются в двоичный дополнительный код.
Команда NEG изменяет состояние следующих флагов: CF (флаг переноса), ZF (флаг нуля), SF (флаг знака), OF (флаг переполнения), AF (флаг служебного переноса), PF (флаг четности). Эти флаги можно использовать для анализа полученного результата.
Команду NEG удобно использовать для получения абсолютного значения числа следующим образом:
Mark: neg AX jl Mark
Если знак числа отрицательный будет выполнен переход на первую команду еще раз.
5.2.4. Ввод и вывод чисел
При работе с числами на ассемблере перед каждым программистом встает вопрос, как осуществлять ввод и вывод чисел в программе? Действительно, ввод осуществляется в ASCII-кодах, а вычисления нужно производить с обычными числами; результат вычислений на экран вновь нужно выводить в ASCII-кодах.
Таким образом, при работе с числами в программе на ассемблере необходимы две процедуры: первая должна преобразовывать введенные пользователем числа из ASCII-формата в бинарные числа, а вторая процедура должна наоборот преобразовывать результат вычислений в ASCII-коды для вывода на экран. К сожалению, стандартных команд, которое бы это делали, не существует. В листинге 5.1 вы можете видеть программу, которая запрашивает два числа, складывает их и выводит результат на экран.
В этой программе процедура str2int преобразовывает каждое число из буфера в ASCII-формате в соответствующие бинарное число.
Другая процедура int2str преобразовывает бинарное число (результат вычисления) в число в системе счисления base в ASCII-формате.
Обращаю ваше внимание, что авторство на данные процедуры принадлежит не мне (если авторы объявятся, я укажу их в переиздании этой книги).
К настоящему времени весь код программы вы должны в состоянии понять самостоятельно, а комментарии вам в этом помогут.
Ассемблирование и линковка выполняется обычным образом: ml numascii.asm
Листинг 5.1. Программа, демонстрирующая ввод и вывод чисел (numascii.asm)
.model small
.stack 100h
|
.data |
|
mess1 |
db "Введите число #1: ",'$' |
|
mess2 |
db "Введите число #2: ",'$' |
|
mess3 |
db "Результат: ",'$' |
|
mess4 |
db "Ошибка ввода $" |
|
crlf |
db 0Dh,0Ah,'$' |
; перевод строки |
n |
db 5 |
; максимальный размер буфера ввода |
nlength db 0 |
; здесь будет размер буфера после считывания |
|
ncontents db 5 dup (?) |
; содержимое буфера |
|
buff |
db 100 dup(0),'$' |
; размер буфера для преобразования числа |
table1 |
db '0123456789ABCDEF' |
; таблица соответствия |
base |
dw 10 |
; указываем систему счисления в которой |
|
|
; желаем выводить числа |
|
|
; например: 10 - десятичная, 2 -двоичная, |
http://www.sklyaroff.ru |
93 |
|
.code |
; 16 - шестнадцатеричная |
|
|
|
|
; процедура вывода строки на экран |
|
|
output PROC |
|
|
mov |
ah,9 |
|
int |
21h |
|
ret |
|
|
output ENDP |
|
|
;процедура считывания введенной строки,
;которая помещается в буфер ncontents input PROC
mov |
ah,0Ah |
int |
21h |
ret |
|
input ENDP |
|
;процедура преобразования числа в ASCII-формате
;из буфера ncontents в соответствующее бинарное число,
;результат преобразования помещается в AX
str2int PROC |
|
|
xor |
di,di |
; DI указывает номер байта в буфере |
xor |
ax,ax |
|
mov |
cl,nlength |
|
xor |
ch,ch |
|
xor |
bx,bx |
|
mov |
si,cx |
; в SI длина буфера |
mov |
cl,10 |
; множитель для MUL |
next1: |
|
|
mov |
bl,byte ptr ncontents[di] ; взять байт из буфера |
|
sub |
bl,'0' |
; вычитаем ASCII-код символа '0' |
jb |
error1 |
; если код символа меньше, чем код '0' |
cmp |
bl,9 |
; или больше, чем '9' |
ja |
error1 |
; выходим из программы с сообщением об |
ошибке |
|
|
mul |
cx |
; умножаем значение в AX на 10 |
add |
ax,bx |
; добавляем к нему новую цифру |
inc |
di |
; увеличиваем счетчик |
cmp |
di,si |
|
jb |
next1 |
|
ret |
|
|
error1: |
|
|
mov |
dx,offset mess4 |
|
mov |
ah,9 |
|
int |
21h |
|
jmp |
exit |
|
str2int ENDP |
|
|
;процедура вывода на экран числа в системе счисления base
;число передается в процедуру в регистре AX, а
;адрес результата помещается в DX
int2str PROC
http://www.sklyaroff.ru |
94 |
; сначала очищаем буфер (заполняем нулями)
xor |
di,di |
mov |
cx,99 |
zeroizing: |
|
mov |
byte ptr buff[di],0 |
inc |
di |
loop zeroizing |
|
;заносим в di адрес последнего байта буфера,
;т. к. буфер будет заполняться от конца к началу
xor |
di,di |
mov |
di,offset buff+99 |
;цикл получения цифр числа путем вычисления остатков
;от деления на основание системы
next2: xor |
dx,dx |
|
|
div |
base |
|
|
mov |
si,offset table1 |
|
|
add |
si,dx |
|
|
mov |
dl,[si] |
|
|
mov |
[di],dl |
|
|
dec |
di |
|
|
cmp |
ax,0 |
; если AX не равно 0 повторяем цикл |
|
jnz |
next2 |
|
|
mov |
dx,di |
; в DX заносим адрес результата |
|
ret |
|
|
|
int2str ENDP |
|
|
|
start: mov |
ax,@data |
|
|
mov |
ds,ax |
|
|
mov |
dx,offset mess1 |
; запрос первого числа |
|
call |
output |
|
|
mov |
dx,offset n |
|
; принимаем ввод первого числа |
call |
input |
|
|
mov |
dx,offset crlf |
|
; перевод строки |
call |
output |
|
|
call |
str2int |
|
; преобразование ASCII-строки в число |
|
|
|
; (результат помещается в AX) |
push |
ax |
|
; сохраняем первое число в стек |
mov |
dx,offset mess2 |
; запрос второго числа |
|
call |
output |
|
|
mov |
dx,offset n |
|
; принимаем ввод второго числа |
call |
input |
|
|
http://www.sklyaroff.ru |
|
|
95 |
|
call |
str2int |
|
; преобразование ASCII-строки в число |
|
|
|
|
; (результат помещается в AX) |
|
pop |
bx |
; восстанавливаем первое число из стека |
||
add |
ax,bx |
; складываем числа |
||
push |
ax |
; сохраняем результат в стек |
||
mov |
dx,offset crlf |
|
; перевод строки |
|
call |
output |
|
|
|
mov |
dx,offset mess3 |
; сообщение о выводе результата |
||
call |
output |
|
|
|
pop |
ax |
|
; восстанавливаем результат из стека |
|
call |
int2str |
|
; преобразовываем в ASCII-строку |
|
call |
output |
|
; выводим результат на экран |
|
exit: |
|
|
; выходим из программы |
|
mov |
ax,4C00h |
|
|
|
int |
21h |
|
|
|
end |
start |
|
|
|
5.2.5. Команды, работающие с целыми BCDчислами
5.2.5.1. Сложение и вычитание неупакованных BCD-чисел
Выше я уже говорил, что команды целочисленных операций, предназначенные для работы с обычными двоичными числами, будут неправильно работать с BCDчислами и наоборот. Пример сложения двух неупакованных BCD-чисел 5 и 6 с помощью команды ADD:
5 = 0000 0101
6 = 0000 0110
11 = 0000 1011
Однако в BCD-формате число 11 должно быть представлено как 0000 0001 0000 0001.
Разработчики микропроцессоров решили не вводить специальные команды для работы с BCD-числами, а ввести только несколько корректировочных команд.
Для корректировки сложения и вычитания неупакованных BCD-чисел имеются две команды:
AAA (ASCII Adjust for Addition) — коррекция результата сложения для представления в символьном виде;
AAS (ASCII Adjust for Subtraction) — коррекция результата вычитания для представления в символьном виде.
Команды AAA и AAS преобразуют содержимое регистра AL в правильную неупакованную десятичную цифру. Если результат корректировки превышает 9, то эти команды добавляют (вычитают — AAS) к содержимому регистра AH и устанавливают флаги CF и OF. Данные корректирующие команды лучше сразу использовать после команды сложения (вычитания) чисел.
Пример корректировки сложения: mov ax,5
mov bx,6
