Архив рубрики: Уроки

Ассемблер уроки :: Программируем на низкоуровневом языке Ассемблер.

Урок 5. Арифметические операции:умножение и деление

В этом уроке по языку ассемблер мы будем говорит об умножении и делении, обрабатывать числа будем как со знаком так и без него. Хочу заметить то что процессор работает только с целыми числам, и это конечно нужно знать и учитывать. Для работы с вещественными числами(числа с плавающей точкой) существует сопроцессор, о котором мы еще поговорим в следующих уроках. 

Умножение

Для умножения в ассемблере используется команда MUL, она принимает лишь одни параметр это множитель а множимое находиться в AX или в AL. Это зависит от размера множителя. К примеру мы хотим умножит содержимое AL на BL для этого нам нужно занести в эти младшие части зачернения и выполнить команду mul bl, результат будет в AX. А чтоб умножить слово на слово: mul bx, но в этом случае результат будет находиться в двух регистрах DX:AX. И еще уточним одну деталь перед умножением слов желательно обнулить DX, это можно сделать просто поместив в него ноль. Также умножать можно и на переменные. Давайте посмотрим на пример кода:

view source

print?

01org 100h
02mov ax,1111h
03mov bx,0
04mov al,2
05mov bl,3
06mul bl
07mov dx,0
08mov bx,0f002h
09mul bx
10mul [A]
11mov ax,4c00h
12int 21h
13db 2

Как мы видим с рисунка трассировки при умножении на AL на BL старшая часть регистра AX принимает новое значение а старое стирается. Также мы видим при умножении двух слов AX=6h и BX=0f002h  результатом будет шестнадцатеричное число 0005A00Ch которое не может поместиться в одном регистре, соответственно оно было помещено в два регистра. И последняя команда умножает переменную A на AL и результат соответственно будет в AX.

 Для того чтоб выполнить знаковое умножение ассемблер имеет команду IMUL. Она работает точно также как и mul только с учетом значка числа. К примеру давайте выполним такой пример 50*(-2)+101 и результат запишем в BX.

view sourceprint?

1org 100h
2mov al,-50
3imul [A]
4mov bx,101
5add bx,ax
6mov ax,4c00h
7int 21h
8db 2

Деление 

Деление 

 Для деление в ассемблере используется команда DIV. Она по своему принципу подобна и команде умножения. Единственно тут происходит не деление слова на слова а деление слова на байт и деление двойного слова на слово. При деление слова на байт а AX находиться делимое а после деления AH содержит остаток от деления а AL частное, то есть результат. При делении двойного слова на DX — содержит старшую часть числа а AX младшую после деления DX — содержит остаток а AX частное. Давайте рассмотрим пример кода: 

view sourceprint?

01org 100h
02mov dx,0
03mov cx,0ffffh
04mov ax,17
05mov bl,2
06div bl
07mov ah,0
08div [A]
09mul cx
10div cx
11mov ax,4c00h
12int 21h
13db 2

Как видно из рисунка выше мы делим 17(11h) на 2 в итоге у нас остаток 1 в старшей части регистра AX (подчеркнуто зеленым) и в младшей находится результат от деления. Затем обнуляем старшую часть регистра AX и потом делим на переменную а то что вышло умножаем на CX, предварительно обнулили регистр DX так как у нас получиться в результате двойное слова и старшая часть будет находиться в DX. В итоге при умножении 4h на ffffh мы имеем число  0003fffch  которое находиться в двух регистрах DX и AX затем мы это двойное слово делим на CX (ffffh) и как видим получили исходный результат в AX

Деление чисел со знаком осуществляется также как и без знака, предлагаю Вам выполнить трассировку  кода ассемблера в отладчике:​

view sourceprint?

01org 100h
02mov ax,-9
03mov bl,-3
04idiv bl
05mov ax,-0ffh
06mov cx,1234h
07mov dx,0 
08imul cx
09mov cx,-1234h
10idiv cx
11mov ax,4c00h
12int 21h  

На этом наш урок подошел к концу.  Надеюсь наши  ассемблер уроки Вам помогают в изучении языка.  Желаю Вам успехов.

Урок 4. Арифметические операции: сложение и вычитание

Приветствую Вас на наших уроках по ассемблеру. Сегодня мы поговорим о арифметических операциях. Сразу хочу сказать о том что все наши дальнейшие уроки будут проходить с использованием FASM ассемблера, а не TASM как это было в первом и во втором. Особо сложного вы ничего не увидите при переходе на fasm. Связано это с тем что fasm поддерживается разработчика по сегодняшний день, что нельзя сказать про tasm. Также в сегодняшнем уроке нам понадобиться отладчик про который шла речь на прошлом уроке.​​

Сложение и вычитание

Начнем пожалуй  с самого простого это сложение и вычитание. В ассемблере за них отвечают две команды это ADD и SUB. Они могут работать как переменными так и с регистрами процессора, а точнее можно сложить или вычесть регистр с регистром, регистр с памятью и наоборот. Пример программы на fasm:​

01org 100h
02mov ax,1
03mov bx,4
04add ax,bx
05add bx,[B]
06mov cl,3
07add cl,[A]
08add [B],ax
09mov ax,4c00h
10int 21h
11db 2
12dw 3

В данном примере мы сначала в mov ax,1 заносим в ах регистр единицу затем в bx 4 и прибавляем к ax bx и результат сохраняется в ax. Так же я думаю Вы заметили что в нашей программе есть две переменные, переменная А размером в байт и переменная B размером в слово(два байта). Затем мы к bx прибавляем слово, то есть переменную B и результат соответственно будет в регистре bx. далее мы младшей части регистра cl присваиваем значение 3 и  прибавляем в следующей командой сложения переменную А, которая имеет размер один байт. И команда add [B],ax прибавляет к переменной B значение регистра ax, и результат будет в переменной B что мы и видим на рисунках выше.

Команда вычитания по действию нечем не отличаться от команды сложения. Вот пример кода, который Вы можете самостоятельно протрассировать на отладчике:

01org 100h
02mov ax,1
03mov bx,7
04sub bx,ax
05sub bx,[B]
06mov cl,3
07sub cl,[A]
08sub [B],ax
09sub [B],2
10mov ax,4c00h
11int 21h
12db 2
13dw 4

Посмотрите на строчку кода sub [B],2 — тут происходит вычитание 2 с переменной B. Такие же действия можно проводить и со сложением. Данная команда также применима и к регистрам к примеру если Вам нужно прибавить к регистру ах 3 то это можно сделать таким образом: add ax,3

В языке ассемблер и соответственно в процессорах х86 существует команда которая автоматически увеличивает либо уменьшает регистр или переменную на единицу. 

INC — увеличивает(прибавляет  единицу). DEC — уменьшает(вычитает единицу)

view sourceprint?​

1inc ax
2dec bx
3inc cl
4dec ch
5inc [A]
6dec [B]

Как известно один байт имеет восемь бит, соответственно может вместит в себя  28= 256 символов. Если мы работаем с положительными (без знаковыми) данными то в байт может поместиться от  0 до 255. Ну а если мы работаем с данными которые могут быть как положительные так и отрицательные то один байт содержит от -128 до +127, а слово в свою очередь от -32768 до +32767. И во многих случаях это нужно учитывать. А теперь представим что нам нужно прибавить регистру bx отрицательное значение которое содержится в младшей части регистра ax. Соответственно прибавит так не получиться, так как bx имеет размер в слово а al в байт. И если просто прибавить к bx значение то что в al путем обнуления старшей части регистра ax и сложением этих двух регистров приведет к тому что отрицательное число с al будет принято за положительное (это было бы приемлемо для без знакового сложения но не для знакового). К примеру если в al=-5 то в регистре содержится число 251 и соответственно оно будет прибавлено к bx а не -5. Для решения этой проблемы существует команда CBW которая расширяет al до ax, к примеру:​​

view sourceprint?

1mov al,-5
2mov bx,8
3cbw
4add bx,ax

После выполнения этих действий можно увидеть что регистр bx будет равен трем.


Пожалуй и все по поводу сложения и вычитания в ассемблере. До встречи на следующей статье ассемблер уроки. Желаю Вам успехов.

Отладчик ассемблера

Урок 3. Отладчик ассемблера

На сегодняшнем нашем уроке мы поговорим о том как можно следить за ходом выполнения программы и за состоянием регистров процессора и всех его флагов состояния. Так же нам понадобиться программа debug.com — она входит в состав dos а также и в windows с ее помощью мы все это и будем делать. 

 Программа debug позволяет нам протестировать программу ассемблер, также проверить любой участок оперативной памяти и много другое. Для начала наберем в консоли команду debug, и у Вас появиться примерно следующее:

Рассмотрим команду которая просматривает память, она принимает адрес + смещение (процессор в реальном режиме имеет шину адреса в 20бит и соответственно может адресовать память 220= 1048576 байт = 1 мегабайт.) Для того чтоб использовать всю шину данных процессор складывает регистр данных и смещение в итоге получаем реальный адрес. К примеру если сегмент данных равен 45f0h а смещение 0032h то реальный адрес к которому мы обратимся будет  04622h. Исходя и этого команда которая показывает участок памяти принимает адрес+смещение к примеру мы хотим посмотреть память по адресу FE000h(кстати по этому адресу находиться серийный номер компьютера) для этого нам нужно ввести в отладчик команду D fe00:0 и нажать Enter.  Мы видим что все данные делятся на три части первая это номер строки памяти, вторая часть данные в шестнадцатеричном формате и третья часть это данные представлены в виде ascii символов. Все вводимые числа отладчик принимает как шестнадцатеричные. 

Для того чтоб узнать дату ROM BIOS которая в формате mm/dd/yy просто посмотрите адрес памяти FFFF5h (D ffff:5). У Вас должно появится примерно такое как на рисунке выше.

Также отладчик  позволяет вводит ассемблер код с помощью команды А. Введите ее и нажмите enter и у вас появиться возможность вводить команды к примеру давайте введем такую команду :view sourceprint?

1mov al,25
2mov bl,32
3add al,bl
4ret

И в конце просто вводим пустую строку и мы выходим из режима ввода команд. Как я думаю Вы заметили что мы использовали команду ADD которую ранее не использовалась в предыдущих уроках, эта команда складывает два регистра, либо части регистров. В данном случае мы сложил две младшие части регистра ax и bx, результат будет в al. Для того чтоб выполнить каждый шаг программы(протестировать) введите t и смотрите за изменениями регистров. 

Также отладчик ассемблера позволяет нам с помощью команды u просмотреть код программы или введенного нами кода, либо части кода. как это мы видим на рисунке ниже.

 Если Вы желаете сохранить введенный код в исполняемый файл то для этого нужно с помощью команды N присвоит имя файла а также его расширение, затем в регистре cx (для это вводим R CX и меняем на нужное значение) указать отладчику размер программ(в нашем случае 7 байт) а затем сохранит с помощью команды W. И затем можно ввести команду q для выхода из отладчика.

 Для того чтоб открыт файл для отладки в отладчике ассемблера и сделать тестирование ассемблер кода нужно указать в качестве параметров полное имя файла. К примеру мы сохранили файл в примеры выше с именем name.com для это вводим в консоли debug name.com . Теперь можно с помощью команды u просмотреть код или с помощью R посмотреть состояние регистров процессора а также первую ассемблер команду которую будет выполнятся при трассировке. Также можно не трассировать все команды подряд а пропустить это и перейти к нужной нам команде, к примеру мы хотим оказаться на конце программы и сразу увидеть результат сложения регистров al и bl для это воспользуемся командой g и укажем ей адрес где хотим оказаться к. в нашем случае это будет выглядеть так:

На этом наш урок по отладчику окончен. Желаю Вам успехов в программировании. Можете приступить к следующим темам на странице  ассемблер уроки .

Урок 2. Переменные в ассемблере и работа с ними

Снова приветствую Вас на нашем сайте по языку ассемблер, и представляю второй урок в котором мы рассмотрим как делать ввод и вывод строки на ассемблере, а также поговорим о переменных и как их объявлять.

Перемеренные 

Предложения языка ассемблера делятся на команды и псевдокоманды (директивы). Команды ассемблера — это символические имена машинных команд, обработка их компилятором приводит к генерации машинного кода. Псевдокоманды же управляют работой самого компилятора. На одной и той же аппаратной архитектуре могут работать различные ассемблеры: их команды обязательно будут одинаковыми, но псевдокоманды могут быть разными.

Так вот объявление переменных осуществляется с помощью псевдокоманд. Переменная это по сути область памяти с которой мы работаем. Для того чтоб объявить ее используют директивы (псевдокоманды) DB, DW DD,DQ и DT . Вернее эти директивы просто говорят ассемблеру что нужно заполнить память указанным количеством байт, а также чем именно заполнить.

  • DB  — говорит что нужно выделить 1 байт.
  • DW — что два байта (define word — определить слово)
  • DD — определить двойное слово (четыре байта)
  • DQ -определить восемь байт или четыре слова
  • DT -определить десть байт

К примеру чтоб объявить перемененную на ассемблере в 1 байт которая будет равна 10:

имя_переменной DD 10 ​

Все наши перемеренные должны быть облеплены в сегменте данных нашей программы. А также нужно явно определить сегментный регистр даты (DS) чтоб он указывал на область памяти где находятся наши переменные.  С переменными можно работать как и с регистрами.  К примеру чтоб занести значение в переменную можно воспользоваться командой MOV :

mov ax,100   ;
mov temp,ax ;теперь наша переменная равна 100(прошу обратить внимание что в данном случае переменная должна равняться два байта )

Я думаю Вы уже за метели что в тексте программы ассемблера часто встречаться точка с запятой, так вот это комментарии, вернее все то что после точки запятой к примеру: команда ; комментарий 

Теперь давайте рассмотрим ситуацию когда нам нужно объявить строку, для это нужно всего навсего написать такой код:

tempstr DB «hello world$»

Код выше что мы объявили строку  в 12 байт! Прошу Вас заметить что при работе со строками в DOS символ $ является концом строки.

Для вывода строк  используется  прерывание 21h и его 9я функция.  То есть  регистр AH должен содержать номер функции а регистр DX должен хранить адрес переменной которую хотим вывести на экран. Для того чтобы поучить адрес переменно используется директива OFFSET. К примеру чтоб внести в DX адрес переменной tempstr нужно выполнить следующее:​

MOV DX, OFFEST tempstr 

Давайте теперь рассмотрим пример программы которая выводит строку на экран:view sourceprint?

01.model small
02.data ;начало сегмента даты
03tempstr     DB "Hello World! $"
04.code ;начало сегмента кода
05main: ;начало нашей программы
06 
07mov ax, @data ; настраивается сегментный регистр, вносим адрес сегмента даты в AX
08mov dsax ; а затем с АХ помещаем адрес в DS чтоб можно было работать с переменными которые в ;сегменте даты
09 
10mov ah, 09
11mov dx, offset tempstr ; вывод сообщения
12int 21h
13 
14mov ax,4c00h ; выход
15int 21h
16 
17end main


Как собрать и выполнить программы мы рассматривали в первом уроке по ассемблеру.

Ввод строки

Для ввода строк предусмотрено множество функций. Мы для примера возьмем все тоже прерывание DOS 21h в котором есть функция под номером 0ah. Данная функция принимает строку первый элемент которой число которое указывает за максимальное количество вводимых символов, а на выходе второй элемент будет указывать на количество введенных пользователем символов. 

|5|0|0|0|0|0|0|0|$|  наша строка на начале ввода, и затем мы ввели HELLO

|5|5|H|E|L|L|O|#|$| такая строка у нас получается на выходе.

И это все нужно учитывать! Для вывода  нам нужно к адресу строки прибавить 2 чтоб мы вывели суму введенную строку.  Вот пример программы которая принимает строку а затем выводит ее на экран: 

view sourceprint?

01.model small
02.data
03 
04  
05EnterS DB "Enter String:$"
06endl     DB 10,13,'$'
07buf db  20 dup('$')
08 
09.code
10main:
11 
12mov ax, @data ; настраивается сегментный регистр
13mov dsax
14 
15mov ah, 9
16mov dx, offset EnterS
17int 21h
18 
19mov ah, 0ah
20mov dx, offset buf
21int 21h
22 
23mov ah, 9
24mov dx, offset endl
25int 21h
26 
27mov ah, 09
28mov dx, offset buf
29add dx,2
30int 21h
31 
32mov ah, 9
33mov dx, offset endl
34int 21h
35 
36mov ax,4c00h ; выход
37int 21h
38 
39end main

Результат работы программы:

Ввод и вывод строки, ассемблер

В программе есть для вас новая директива DUP. К примеру если Вам нужно ввести 100 одинаковых символов или вам просто требуется выделить память для того чтоб ее потом использовать вот для этого и служит эта директива. В данном примере мы выделили 20 байт которые заполнили символом $ . Эта переменная из 20 байт служила для того чтоб в нее вводилась строка.  И обратите внимание когда мы выводим нашу введенную строку , сначала мы получаем адрес ее а потом прибавляем  2 с помощь команды AAD, про которую мы поговорим в следующем уроке, для того чтоб пропустить 2 первых элемента которые нужны для ее ввода.

 На этом второй урок из цикла уроки ассемблера окончен. Желаю успехов Вам! ​

Урок 1 . Основы ассемблера.

Приветствую всех на нашем сайте по языку ассемблера. Предлагаю вам серию уроков по ассемблеру. Все примеры к урокам приведены к компилятору TASM.

Я думаю всем известно что сердцем компьютера является Процессор, он непосредственно выполняет все команды программы. Процессор состоит из нескольких частей, а также имеет регистры, которые мы по ходу наших уроков по ассемблеру рассмотрим подробно. А в данном уроке поговорим про регистры общего назначения, а также скажем про сегментные регистры.

Регистры общего назначения

Сначала рассмотрим регистры общего назначения. Они называются АХ, ВХ, СХ и DX (Аккумулятор, База, Счетчик и Данные).  Кроме названий, они больше ничем другим не отличаются друг от друга, поэтому рассмотрим только первый регистр — АХ  . В свою очередь, регистр АХ может быть разделен  на два 8-битных регистра — АН и AL. Если мы заносим в регистр АХ значение 1234h, (12h в АН и 34h в AL), а значение  других регистров общего назначения называются по такому же принципу: ВХ который, в свою очередь, содержит ВН и BL и т.д.

 Сегментные регистры

Эту группу регистров можно отнести к регистрам состояния. Регистры из этой группы используются при вычислении реального адреса (адреса, который будет передан на шину адреса, с помощью которой процессор обращается к оперативной памяти).  Названия этих регистров соответствуют выполняемым функциям: CS (Code Segment, сегмент кода) вместе с IP  определяют адрес памяти, откуда нужно прочитать следующую инструкцию; аналогично регистр SS (Stack Segment, сегмент стека) в паре с SP (SS:SP) указывают на вершину стека. Сегментные регистры DS, ES, FS, и GS (Data, Extra, F и G сегменты) используются для адресации данных в памяти.

 Первая программа на ассемблере

И так приступим сразу к делу, напишем простую программу на ассемблере. Для этого мы будем использовать команду MOV которая заносит значения в регистр, или с регистра в память. Наша программа будет выводить на экран лишь один символ. который будут помешен в младшую часть регистра DX, то есть  в DL. К примеру мы хотим внесли символ # в DL, для этого нужно прописать MOV DL,’#’ .  Чтоб вывести на экран символ для мы воспользуемся прерыванием dos под номером 21h. Про прерывания поговорим подробно чуть позже, а сейчас лишь скажу что в этот момент наша программа передает управление операционной системе в нашем случае это DOS. прерывание делается с  помощью команды INT.  Далее нам нужно создать файл с расширением asm, к примеру first.asm с таким содержимым: ​

view source

Print?

01.MODEL SMALL  ;говорим сборщику модель программы
02CODE SEGMENT ;директива указывает на начала сегмента кода
03ASSUME CS:CODE ;присваиваем сегментному регистру кода адрес сегмента нашего кода
04START: ;точка с которой начинаться код программы
05 
06mov ah,2
07mov dl,'#'
08int 21h
09 
10mov ah,4ch
11mov al,0
12int 21h
13 
14CODE ENDS ;конец сегмента кода
15END START ;и также говорим о окончании нашей программы

 К примеру если у Вас TASM размещен в корне диска С: (то есть прям на диске С:) и файл first.asm находиться в папке по адресу c:tasm\bin\first.asm тогда вызовете консоль (нажмите wint и R одновременно, в появившейся строке напишите CMD или нажмите Пуск а затем выполнить, и также наберите CMD ). Затем  в консоли введите  cd  C:\tasm\bin\ таким образом мы окажемся в папке C:\tasm\bin\ как видно на рисунке ниже.

Далее с помощью TASM собираем наш код. tasm first.asm , если все сделали верно то должно вывести сообщение что ошибок и предупреждений нет и файл  успешно собран:

Затем у нас получился объектный файл который нужно еще будет собрать с помощью линкера в исполняемый файл exe .  В пакет Turbo assembler входит также и линкер TLINK. Чтоб собрать нам нужно линковщику лишь указать имя объектного файла(  tlink first.obj) 

Мои поздравления! Теперь Вы собрали программу на ассемблере за пару простых шагов. Для того чтоб запустить ее просто введите в консоле имя программы и результат ее действия вы увидите на экране. Вот как это сделал я:

Собрать ассемблер программу онлайн 

Так же возможно собрать код языка ассемблер онлайн, прямо в вашем браузере. Для этого нужно перейти по ссылке на виртуальную машину VM-DOS которая работает прям в браузере. Подождать когда она погрузиться и можно приступать к работе с ней. 

1. С начало нужно набрать код. Для этого мы воспользуемся редактором EDITV, и укажем ему имя файла который мы хотим создать таким образом:  editv first.asm

assembler online. создание файла.

Затем мы откроется редактор.assembler online. Редактор.

Редактор EDITV

И затем в редакторе набираем код  программы который мы набирали раньше.

assembler online. Набираем код программы в редакторе.

Далее сохраняем клавишей F2 и выходим с редактора Alt+X. Затем с помощью TASM собираем наш кода ассемблера. Так как делали раннее (tasm first.asm). И если все верно то должен получится объектный файл.

Сборка программы. Далее собираем объектный файл в исходный с помощью программы LINK. Для это го наберем link first.obj . На заданные вопросы ничего не отвечаем просто жмем энтер. Затем у нас получиться исходный файл который вы уже знаете как выполнить.

assembler online. Сборка объектного файла.

assembler online. Выполнение исходного файла.

Желаю успехов Вам. По все вопросом пишите на почту или на форум.