Учебный курс. Часть 9. Сложение и вычитание
Автор: xrnd | Рубрика: Учебный курс | 28-03-2010 |
Распечатать запись
Теперь мы уже знаем, как представляются числа в компьютере, и можем перейти к изучению команд процессора. Начнём с самых простых арифметических операций: сложения и вычитания.
Сложение
Для сложения двух чисел предназначена команда ADD. Она работает как с числами со знаком, так и с числами без знака (это особенность дополнительного кода).
Операнды должны иметь одинаковый размер (нельзя складывать 16- и 8-битное значение). Результат помещается на место первого операнда. В общем, эти правила справедливы для большинства команд.
После выполнения команды изменяются флаги, по которым можно определить характеристики результата:
- Флаг CF устанавливается, если при сложении произошёл перенос из старшего разряда. Для беззнаковых чисел это будет означать, что произошло переполнение и результат получился некорректным.
- Флаг OF обозначает переполнение для чисел со знаком.
- Флаг SF равен знаковому биту результата (естественно, для чисел со знаком, а для беззнаковых он равен старшему биту и особо смысла не имеет).
- Флаг ZF устанавливается, если результат равен 0.
- Флаг PF – признак чётности, равен 1, если результат содержит нечётное число единиц.
Примеры:
add ax,5 ;AX = AX + 5 add dx,cx ;DX = DX + CX add dx,cl ;Ошибка: разный размер операндов. |
Вычитание
Вычитание выполняется с помощью команды SUB. Результат также помещается на место первого операнда и опять же выставляются флаги. Единственная разница в том, что происходит вычитание, а не сложение.
На самом деле вычитание в процессоре реализовано с помощью сложения. Процессор меняет знак второго операнда на противоположный, а затем складывает два числа. Если вам необходимо в программе поменять знак числа на противоположный, можно использовать команду NEG. У этой команды всего один операнд.
Примеры:
sub si,dx ;SI = SI - DX neg ax ;AX = -AX |
Инкремент и декремент
Очень часто в программах используется операция прибавления или вычитания единицы. Прибавление единицы называется инкрементом, а вычитание – декрементом. Для этих операций существуют специальные команды процессора: INC и DEC. Обратите внимание, что эти команды не изменяют значение флага CF.
Пример программы
Чтобы всё стало совсем понятно, напишем небольшую программу. Требуется вычислить значение формулы: e=a-(b+c-1)+(-d). Все числа являются 8-битными целыми со знаком. Объявим их после кода и придумаем какие-нибудь значения. Вот что у меня получилось:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | use16 ;Генерировать 16-битный код org 100h ;Программа начинается с адреса 100h mov al,[a] ;Загружаем значение a в AL mov ah,[b] ;Загружаем значение b в AH add ah,[c] ;AH = AH + c = b+c dec ah ;AH = AH - 1 = b+c-1 sub al,ah ;AL = AL - AH = a-(b+c-1) mov cl,[d] ;CL = d neg cl ;CL = -CL = -d add al,cl ;AL = AL + CL = a-(b+c-1)+(-d) mov [e],al ;Сохраняем результат в e mov ax,4C00h ;\ int 21h ;/ Завершение программы ;------------------------------------------------------- a db 2 b db 3 c db 5 d db -8 e db ? |
Квадратные скобки означают, что операнд находится по адресу, указанному внутри этих скобок. Так как вместо имени переменной FASM подставляет её адрес, то такая запись позволяет прочитать или записать значение переменной.
Запустив программу в Turbo Debugger, можно посмотреть её выполнение по шагам. Значения переменных можно увидеть в окне дампа памяти. Для этого нужно кликнуть правой кнопкой в этом окне и выбрать в меню пункт Goto…. Переменные начинаются в памяти с адреса 011Fh (этот адрес в первой команде).
В этих байтах легко угадываются наши переменные:
Упражнение
Напишите программу для вычисления формулы k=m+1-(n-1-r). Все числа 16-битные целые со знаком. Запустите в отладчике и проверьте правильность вычисления. Результаты можете выкладывать в комментариях 🙂
12-05-2011 18:41
Всё правильно. Только переменная k не используется, логично было бы записать в неё результат 😉
Делать операции только с переменными не получится.
Для вычислений один операнд обязательно должен быть в регистре.
04-06-2011 09:39
;k=m+1-(n-1-r)
use16
org 100h
mov ax,[m] ;Загружаем значение m в AX
mov bx,[n] ;Загружаем значение n в BX
inc ax ;AX = AX+1 = m+1
dec bx ;BX = BX-1 = n-1
sub bx,[r] ;BX = BX-r = n-1-r
neg bx ;BX = -BX
add ax,bx ;AX = AX+BX = m+1-(n-1-r)
mov [k],ax ;Сохраняем результат в k
mov ax,4c00h
int 21h
;——————————————
m dw 3
n dw 5
r dw 7
k dw ?
——————————————-
Написал сам, но прочитав все комментарии ))
Учел оптимизацию и не стал загружать r в регистр.
Единственное, где ошибся, это при описании переменных использовал db, поэтому поначалу fasm выдал ошибку. Ошибку нашел благодаря oleg’у.
23-06-2011 15:55
Всё правильно 🙂 Можно ещё заменить две команды:
одной:
25-06-2011 20:36
Что-то сам не догадался насчет sub’а ))
30-06-2011 11:29
Привет xrnd!
вот что я сам сделал 🙂 проверяй:
use16 ;генерируем 16бит 🙂
org 100h ;прога начинается с 100h
;————-прога————
mov al,[m] ;помещаем значение m в al
inc al ;добавляем к al 1
mov ah,[n] ;помещаем значение n в ah
dec ah ;вычитаем из ah 1
mov cl,[r] ;помещаем значение r в cl
sub ah,cl ;из ah вычитаем cl
sub al,ah ;из al вычитаем ah
mov [k],al ;сохраняем результаты в k
;———-завершаем прогу———-
mov ax,4c00h
int 21h
;—-данные—-
m db 5
n db 10
r db 5
k db ?
1 момент я не понял где ответы найти в отладчике перечитыва перечитывал но чёт не допёр (
30-06-2011 16:57
я реальнот сам написал, не смотря в комменты сначала на листе! 🙂
02-07-2011 02:14
Программа написана правильно, только с 8-битными числами, а не с 16-битными как в задании.
Можно немного оптимизировать – вычесть r, не загружая значение в регистр:
sub ah,[r]
В отладчике надо смотреть значения в регистрах, когда выполняешь программу по шагам. А также значения переменных в памяти. Естественно, числа будут в шестнадцатеричном виде 🙂 А вместо названий переменных – только адреса, но догадаться можно.
03-07-2011 18:30
Ааа, понял я хотел тк сделать sub ah,[r] только подумал как из регистра ah вычесть r если я его не добавил в регистр? 🙂
05-07-2011 18:36
блин точняк спасибо понятно:)
03-07-2011 18:33
мне вот интересно, думаю не только мне, ты так хорошо знаеш ассемблер, долго ты этому учился?:)
17-09-2011 14:21
На ассемблере я начал программировать где-то в 2005 году 🙂
Учился не долго, зато много практиковался. Лучший способ научитсья – это писать программы.
04-07-2011 09:41
После пары экспериментов и нажатий на Del был приятно удивлён тому что работает и такой вариан:
use16
org 100h
mov ax,[n]
dec ax
sub ax,[r]
mov bx,[m]
inc bx
sub bx,ax
mov [k],bx
mov ax,4c00h
int 21h
k dw ?
m dw 8
n dw 4
r dw 2
04-07-2011 09:43
Эх, не посмотрел то что выше есть уже подобный вариант))
04-07-2011 09:47
После TASM’а FASM кажется каким то “легким”)) В нём всё можно сделать проще (удобнее, понятнее) и быстрее)))
05-07-2011 17:22
У меня не потеме извините но вот натолкнулся, xrnd скажи пожалуйста что означает вот это
fmask db ‘*.*’,0 что это за команда? 🙂
или вот: lodsw
расскажи зачем эти команды нужны то?
зарание спасибо!
05-07-2011 18:23
и это что делать када в командной строке мне пишут: неправильная команда или имя файла? 🙁 абсурт создал программку включаю и вот(
29-07-2011 19:08
Ладно сейчас не об этом вот код:
;k=m+1-(n-1-r)
use16
org 100h
;—————————————–Прога начинается.
mov ax,[m]
mov bx,[n]
dec bx
sub bx,[r]; r будет без регистра на улице жить xD
inc ax
sub ax, bx
mov [k], ax
;—————————————–Прога заканчивается.
mov ax,4C00h
int 21h
;—————————————–Данные:
m dw 15
n dw 10 ; а вот ещё с директивами чёто не пойму правельно обьявил?(было в db ошибка! вот и в dw)
r dw 5
k dw ? ;12 =)
;всё идём в отладчик ответ искать…
17-09-2011 14:37
Да, правильно.
17-09-2011 14:33
fmask – это название переменной. Это строка из 3 символов, в конце нулевой байт, обозначающий конец строки.
lodsw – это команда, про неё я еще тут не рассказывал. Она аналогична по действию mov ax,[si]
11-07-2011 00:55
А откуда мне знать что переменные из моей программы лежат по адресу 011F? Например в программе “Hello World” я это узнаю потому что мне на это регистр DX указывает (о чем автор почемуто не говорит ученикам 🙂 ) а в этой программе почемуто DS ни на что не указывает ? У меня кстати эти переменные из программы выше лежат по адрессу ds:0118. Хотелось бы услышать какоенибудь объяснение 🙂
17-09-2011 15:14
Узнать можно, посмотрев программу в отладчике или воспользоваться листингом.
Тут легко догадаться, потому что в отладчике тот же код, только вместо названий переменных – их адреса. Переменная a расположена в начале блока данных и её адрес в первой команде.
11-07-2011 01:06
Кажеться я здесь писал каммент про адреса- извиняюсь я не внимательно читал.
Но всетаки не совсем понятно почему здесь DX не указывает на место где лежат переменные. В “Hello World” по первой команде можно видеть что передаеться адрес где лежит это сообщение как и здесь с переменной но там и DX содержит этот адрес ?
17-09-2011 15:15
В DX адрес помещается, чтобы вывести строчку на экран.
24-07-2011 00:06
Хех, у меня совершенно аналогичный код)
11-08-2011 20:19
17-09-2011 17:04
Правильно, но мой вариант немного короче.
31-08-2011 13:00
use16
org 100h
mov al,[m]
inc al
mov ah,[n]
mov cl,[r]
dec ah
sub ah,cl
sub al,ah
mov [k],al
mov ax,4C00h
int 21h
;————
k db ?
m db -3
n db 5
r db 8
17-09-2011 17:30
Всё верно.
06-10-2011 02:57
Зравствуйте.
а попробовал
вместо
mov al,[a]
написать
mov al,a
но выходит ошибка – error: value out of range. Почему? Оба ведь байты?
тогда я пишу так
mov ax,a
вместо адреса в кавычках (в дебагере)
mov al,[011F]
стоит тоже самое число.
mov ax, 011F
В чем тогда разница ?
27-10-2011 00:27
Разница в следующем
mov ax,[a] ;Поместить в AX слово из памяти по адресу a
mov ax,a ;Поместить в AX число a
Квадратные скобки обозначают обращение к памяти по адресу.
В первом случае берется значение из памяти, во втором в регистр помещается численное значение адреса.
mov al,a вызывает ошибку, так как a=011F, адрес здесь рассматривается как число и это число не влезает в байт.
11-10-2011 11:19
всесто двух строк
mov cl,[d] ;CL = d
neg cl ;CL = -CL = -d
можно написать одну
neg [d]
27-10-2011 00:29
Можно, но тогда значение d будет изменено в памяти.
Иногда это не желательно 🙂
25-10-2011 21:52
Вот мой вариант. Проверял дебаггером и калькулятором – все правильно.
use16
org 100h
mov ax, [m]
inc ax
mov bx, [n]
dec bx
sub bx, [r]
sub ax, bx
mov [k], ax
mov ax,4C00h
int 21h
k dw ?
m dw 17762
n dw 28473
r dw 21618
27-10-2011 00:59
Да, действительно правильно!
28-10-2011 19:39
use16
org 100h
mov ax, [m]
inc ax
mov bx, [n]
dec bx
sub bx,[r]
sub ax, bx
mov [k],ax
int 21h
k dw ?
m dw 96
n dw 23
r dw 12
17-12-2011 17:00
use16 ;Ãåíåðèðîâàòü 16-áèòíûé êîä
org 100h ;Ïðîãðàììà íà÷èíàåòñÿ ñ àäðåñà 100h
mov bx, [n]
dec bx
sub bx, [r]
mov cx, [m]
inc cx
sub cx, bx
mov [k], cx
mov al, 09h
mov dx, result
int 21h
mov ax,4C00h ;\
int 21h ;/ Çàâåðøåíèå ïðîãðàììû
;——————————————————-
m dw 02h
n dw 03h
r dw -05h
k dw ?
result db ‘Resultat=[k]$’
Вроде по самой калькуляции все нормально – но почемуто никак не могу увидеть результат на мониторе 🙁 Что не так? Как добиться вывода строчки ‘Resultat=[k]$’ ?
17-12-2011 17:06
И еще – почему в результате (с моими значениями) стоит FFFC (регистр сх)
24-12-2011 20:13
; Способ с использованием одного регистра
use16
org 100h
; k = m+1-(n-1-r)
; код
mov ax , [n]
dec ax
sub ax , [r]
sub ax , [m]
dec ax ; оба “dec” можно заменить на один “sub ax , 2”
neg ax
mov [k] , ax
; данные
n dw 10001
r dw 4999
m dw 15000
k dw ? ; 10000 ; 2711h в турбо деббагере
; конец
mov ax,4C00h
int 21h
31-12-2011 10:01
вот мой код поменьше вроде правильно
use16
org 100h
mov al,[n]
dec al
sub al,[r]
mov ah,[m]
inc ah
sub ah,al
mov [k],ah
mov ax,4C00h
int 21h
n db 6
r db 3
m db 3
k db ?
31-12-2011 22:45
И все-таки мне непонятно, почему мы ищем переменные по адресу 011F ?
Где это указывается ?
02-01-2012 13:16
use16
org 100h
mov ax,[m]
sub ax,[n]
add ax,[r+2]
mov [k],ax
mov ax,4c00h
int 21h
n dw -1
r dw -2
m dw -3
k dw ?
09-01-2012 13:08
Джентельмены, я все же не понимаю, почему мы ищем
значения переменных начиная с адреса 011F ?
Где это указывается ?
Предидущие комментарии читал, но все равно не разобрался.
Заранее спасибо за ответ!
12-01-2012 06:08
У Вас ошибка в статье:
“Флаг PF — признак чётности, равен 1, если результат содержит нечётное число единиц.” – parity flag равен 1, если результат(в бинарной системе) содержит ЧЁТНОЕ кол-во единиц, и 0, если в результате содержится НЕЧЁТНОЕ кол-во единиц
13-01-2012 22:02
Спасибо большое автору за уроки! Всё объяснено по-человечески, как раз для начинающих кодеров!!!
26-02-2012 12:30
use16
org 100h
mov ax,[m]
inc ax
mov bx,[n]
dec bx
sub bx,[r]
sub ax,bx
mov [k],ax
mov ax,4c00h
int 21h
m dw 9
n dw 6
r dw 2
k dw ?
26-02-2012 12:33
пробовал описать переменные в начале программы, но чего то не получилось, а по идее должно было….
16-03-2012 16:34
Как сделать, чтобы в Turbo Debugger подсвечивалися белым цветом данные из дампа памяти ?
01-05-2012 21:16
use16
org 100h
mov ax,[m-1]
mov bx,[n-1]
sub bx,[r]
sub ax,bx
mov [k],ax
mov ax, 4c00h
int 21h
;—————
k dw ?
m dw 8
n dw 11
r dw 5
01-05-2012 21:57
Поспешил.
use16
org 100h
mov ax,[m]
dec ax
mov bx,[n]
dec bx
sub bx,[r]
sub ax,bx
mov [k],ax
mov ax, 4c00h
int 21h
;—————
k dw ?
m dw 8
n dw 11
r dw 5
22-05-2012 21:58
use16
org 100h
; k = m + 1 – (n – 1 – r)= -(n – 1 – r) + m + 1
mov ax,[n]
dec ax
sub ax,[r]
neg ax
add ax,[m]
inc ax
mov [k],ax
mov ax,4c00h
int 21h
m dw 10
n dw 5
r dw 2
K dw ?