Здесь ты сможешь найти конструкции, которые могут часто пригодится при программировании на FASM
. По мере возможности и необходимости список будет пополняться. В данном случае рассматривается работа с MS DOS.
Для вывода определённой информации можно работать с видеопамятью напрямую:
mov ax, 0xb800
mov es, ax
mov di, 0
mov word[es:di], 0xE141
Здесь происходит следующее. Чтобы нам обратиться к памяти, нужно использовать сегмент и смещение. Начало видеопамяти находится в ячейке 0xb800:0x0. Для работы используются пары ds:si
и es:di
.
В сегментный реестр данные нельзя положить напрямую, поэтому для этого мы используем регистр АХ
.
Адрес 0xb800:0x0
содержит данные для вывода верхнего левого угла. При дальнейшем движении по памяти, вывод смещается вправо вдоль строки, как только строка заканчивается, происходит переход на следующую.
В одной строке 80 символов. Всего на экране 25 строк.
Чтобы сместиться на один символ, нужно выполнить смещение в памяти на 2. Для перехода на новую строку - 160(0xA0)
mov ah, 0x2
mov dl, 0x41
int 0x21
Здесь в AH
хранится функция прерывания. Для вывода символа это 0x2
. В DL
хранится номер ASCII символа, который нужно вывести.
mov ah, 0x9
mov dx, msg
int 0x21
msg: dw "Text", 0xD, 0xA, '$'
В AH
хранится функция прерывания. Для вывода строки это 0x9
. В DX
записывается адрес-смещение строки (в данном случае метка является адресом-смещения)
Для того, чтобы организовать собственные прерывания, нужно написать резидентную программу (чтобы она всегда находилась в памяти). Это можно сделать следующим образом:
_interrupion_:
sti
<ваш код>
iret
_init:
mov ah, 0x25
mov al, <Номер вашего прерывания>
mov dx, _interruption
int 0x21
mov dx, _init
int 0x27
Здесь два блока. Блок _interruption
- часть, которая будет выполняться при вызове прерывания из другой программы, а в _init
мы делаем нашу программу резидентной.
В _interruption
есть два ключевых слова, которые должны там присутствовать. STI
- это команда разрешающая маскируемые прерывания, а iret
- возврат из функции (используется для резидентных программ)
В _init
происходит следующее:
mov ah, 0x25
- Указываем функцию прерывания 0x21. В данном случае запись в вектор прерыванийmov al, <номер>
- Задаём номер прерывания, по которому его можно будет вызвать. Если указать занятый, то поверх старого запишется новое прерываниеmov dx, _interruption
- Указание адреса-смещения блока кода, который будет исполняться при вызове прерыванияint 0x21
- После вызова прерывания, наше прерывания записывается в вектор прерыванийmov dx, _init
- указываем адрес-смещение, который следует за резидентной программойint 0x27
- Переводим программу в формат резидентной
Код для создания и открытия файла:
create_and_open_file: ;В ds:dx имя файла. В AX запишется логический номер файла
push cx
xor cx,cx ;Обнуляем СХ, чтобы создался обычный файл(без флагов)
mov ah, 0x3C ;Функция создания файла
int 0x21 ;Вызываем прерывание
pop cx
ret
В памяти по адресу DS:DX
должно хранится имя файла. После успешного создания файла в AX
запишется логический номер файла.
Рассмотрим на примере записи символа в файл:
write_endl: ;Блок по выводу конца строки в файл
pusha
mov byte[ds:buff], 0xA
mov cx, 1
mov dx, buff
mov ah, 0x40
int 0x21
popa
ret
В файл после вызова прерывания будет записана память DS:DX
. В BX
должен лежать логический номер файла, в который будет производиться запись.
Данная функция при вызове заставит компьютер подождать. Как только время ожидания выйдет - закончится выполнение функции
timeout: ;Задержка в тиках
pusha
xor ah,ah ;Обнуляем ah
int 1ah ;Читаем часы
add dx, 2 ;Добавили к прочитанному времени задержку
mov bx, dx ;Записали в BX время начала задержки + задержку
.wait:
int 1ah ;Читаем часы
cmp dx, bx ;Сравниваем нынешнее время с необходимым
jl .wait
popa
ret
Вреия задержки указывается в строке
add dx, 2
. В таком случае задержка составит 2 тика.
В одной секунде 18.2 тика