Assembler for newbies in Low level Programming
Ассемблер для новичков в низкоуровневом программировании.
Для процессора не существует никакой разницы между данными и кодом.
Фон Нейман.
Выпуск 01.
Если после числа стоит h, то число 16-ричное(HEX), если b то бинарное(Binary, в двоичной системе счисления), если d то десятичное(Decimal). Обычно по умолчанию число считается десятичным. А, еще hex числа нам нужны так как любой байт будет соответствовать одному hex числу.
Программы состоят из команд микропроцессора и данных, команды представляют собой бинарный машинный код. Те команды, что мы пишем на ассемблере, преобразуются транслятором в машинный код, или в несколько команд. К примеру mov ax,21 будет выглядеть как B821 а mov bx,2145 преобразуется в BB4521. Чтобы программа выполнилась, необходимо в первую очередь загрузить её в память. Этим у нас занимается ОС, она загружает нашу программу, и передает управление на точку входа (эта та метка начала программы). Программа выполняет свои какието действия, и дает знать операционке что она типа закончила работу. (4Сh функция 21h прерывания).
Прерывания – сигнал процессору, что какое-то устройство требует его внимания. Их разрешили посылать и программам. Т.е. что у нас делается:
вызывается прерывание, и процессор смотрит номер прерывания, 21h – это прерывания закрепленные для использования ДОС-ом. Далее он смотрит значение регистра AH, и вызывает функцию, номер которой лежит в AH. К примеру 09h функция выводит на экран текст, адрес которого лежит в регистре DX.
Вдумчивый читатель задаст вопрос, а собственно говоря где лежат эти функции? Ведь они тоже являются кодом, иначе как бы они смогли что-нибудь делать! А на расположение их указывает 4 байта по адресу получаемому таким образом: 4*N где N -номер прерывания, т.е. 21h находится 4*21h = 84h
Память (RAM или ОЗУ) cостоит их массива однобайтовых ячеек, а то как мы имеем к ней доступ, мы определяем сами. Каждой ячейке мы присваиваем свой номер(0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,10). Это называется физическим адресом. Это конечно удобно, однако подумайте, один регистр может вместить максимальное число FFFFh. Так что-же, память выше этой границы не использовать? Выход был найден в использовании двух регистров для адресации, один регистр содержит адрес сегмента, а другой – адрес внутри этого сегмента (смещение). Соответственно размер сегмента не сожет быть больше FFFFh, или 64kb (65535 байт). Таким образом мы можем получить доступ к памяти до 1 мегабайта.
Значит память у нас разделена на 64-килобайтовые сегменты. Следовательно наша программа (ведь она тоже грузится в память) должна иметь хотя-бы один сегмент.
Примечание: Сегментные регистры должны перед началом работы содержать адреса сегментов, иначе многие команды не будут работать! Команда ASSUME кроме того помещает в регистр CS адрес сегмента кода.
;\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
Title input_char
codeseg segment
ASSUME cs:codeseg,ds:codeseg,ss:codeseg
start:
mov ax,codeseg
mov ds,ax
mov ah,09h
mov dx,offset text
int 21h
mov ah,4Ch
int 21h
text db 'test$'
codeseg ends
end start
;\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
Кстати, гораздо удобнее код, данные и стек размещать в разных сегментах. Директива ASSUME определяет какой сегмент является сегментом кода, какой – данных, какой – стека. (CS-Code Segment сегмент кода, DS-Data Segment сешмент данных, SS -Stack Segment). Следовательно мы в этой програмке все сегменты определяем на codseg, на сегмент кода. Если мы откроем эту прогу (в уже скомпилированном виде, т.е. в exe) в дизассемблере, то увидим вод такую картину(лишнее урезано):
dseg:0000 start proc near
dseg:0000 mov ax, seg dseg
dseg:0003 mov ds, ax
dseg:0005 assume ds:dseg
dseg:0005 mov ah, 9
dseg:0007 mov dx, 10h
dseg:000A int 21h ; DOS - PRINT STRING
dseg:000A ; DS:DX -> string terminated by "$"
dseg:000C mov ah, 4Ch
dseg:000E int 21h ; DOS - 2+ - QUIT WITH EXIT CODE (EXIT)
dseg:000E start endp ; AL = exit code
dseg:000E
dseg:000E ; ---------------------------------------------------------------------------
dseg:0010 db 74h, 65h, 73h, 74h, 24h
dseg:0010 dseg ends
dseg:0010
dseg:0010
dseg:0010 end start
Объясняю, что все это значит. dseg:0000 start proc near -это наша метка (start:)
Да, адреса здесь в байтах, и значит, что команда к примеру mov ah,09h весит 2 байта, следующая команда будет через 2 байта.
Дальше с адреса 0000 по 0003 идет инициализация сегмента данных.
dseg:0005 assume ds:dseg Это подсказка, что теперь в DS содержится адрес текущего сегмента.
dseg:0005 помещаем в ah, число 09h
dseg:0007 помещаем в регистр dx 10h (смещение нашего текста(test$)). Команда offset заставляет транслятор вычислять адрес метки стоящей после него, и подставить реальное значение.
dseg:000A вызов 21h прерывания, судя по комментарию вставленном IDA, печатает на экране строку адрес которой лежит в регистре DX.
dseg:000C помещаем в ah, число 4Ch
dseg:000E вызов 21h прерывания, по комменту IDA выход с кодом выхода, регистр AL содержит код выхода.
dseg:0010 наш текст.В hex виде.
Директива db заставляет транслятор резервировать в тексте программы байт.
Пример:
db 10 ; зарезервирует 1 байт и запишет в него число 10.
db 'hello' ; зарезервирует 5 байт, в которые поместит ASCII коды букв hello
db 10 dup(0) ; зарезервирует 10 байт в которые поместит нулики.
Вот теперь вам пример программы с несколькими сегментами (кода, данных, стека)
title segments
dseg segment
link db 10 dup(0)
db '$'
dseg ends
stseg segment stack
db 20 dup(0)
stseg ends
codseg segment
ASSUME cs:codseg,ds:dseg,ss:stseg
begin:
mov ax,dseg
mov ds,ax
mov ax,stseg
mov ss,ax
mov dx,0000
mov byte ptr link,01 ; помещаем в байт по адресу метки link число 01.
mov ah,09h
int 21h
mov ah,4Ch
int 21h
codseg ends
end begin
Кстати, поскольку нет никакой разницы между данными и кодом, можно сделать самомодифицирующуюся программу.
Задание: Поэксперементируйте с различными цифрами, посмотрите дизасм листинг всех этих прог.
Приложение к рассылке, со списком функций ДОС-а, списком регистров, описанием команд, и многого другого, вы найдете на http://ass3mbler.narod.ru
(оно там обозначено как приложение к статье)
ведущий рассылку Skif_Q
Да пребудет с вами знание!