Как сделать отрицательное число в ассемблере

Обновлено: 05.07.2024

За время программирования чипов AVR, нарыл я разных математических подпрограмм для этих чипов. Может кому пригодятся. Что мне жалко этого добра? Пущай народ чесной пользуется. Если у кого то есть что то еще, то можно добавить это в статью.

Комментарии ( 45 )

Да, это, конечно, круто, но лично я вряд ли возьмусь за ассемблер АВР, хотя если бы были комментарии, кое-что, возможно, использовал бы в пиковском ассемблере, например bin2bcd. Хотя можно и в этом разобраться при желании.

Логическое ударение на ассемблер или на АВР? Конечно лучше бы платформо-независимые алгоритмы, чем конкретная реализация.

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

Всего 35 команд? Это же ужас как неудобно. Все приходится через задницу делать. Чем больше команд тем проще!

Команд или мнемоник? Тут есть нюанс… У Z80 было под 700 команд. Включая такие которые одной мнемоникой могли целый блок памяти скопировать. Это было круто! У пика же все очень и очень куцо. Даже сложения вроде бы нету.

По моему, таки команд. Я про x86. Если про MSP430 — у него 27 команд и 53 (или около того) мнемоники.

В обоих вариантов есть плюсы и минусы. Для программирования удобнее всего трехадресные системы команд с ортогональной адресацией (когда любой из операндов и результат можно достать/положить любым доступным способом адресации). Эти же наборы команд сложнее всего реализовать в железе и параллельно исполнять. И так же трудно добиться малого потребления. Малое число простых команд, отсутствие заумных методов адресации + (относительно) большой регистровый блок — другая альтернатива. Скорость и малое потребление достигаются относительно легко, малыми затратами железа, но писать становится, мягко говоря, совсем не просто. Неудобство писания на асме решается переходом на более высокоуровневые языки, а вот скорость и потребление так в лоб не решаются. Потому, вобщем, и ушли от сложных систем команд. Впрочем, можно сломать сразу все, в чем легко убедиться посмотрев на х86 — горбатая и при этом сложная система команд, неудобная ни для писания на асме ни для оптимизации ЯВУ. При этом сложная чисто аппаратно и плохо поддающаяся оптимизации под малое потребление.

Это если не знать ничего другого. За сегментную адресацию и привязку некоторых команд к регистрам (которых мало) всегда хотелось стукнуть разработчиков чем-нибудь тяжелым.

Ну почему же ничего другого. Я штук шесть архитектур относительно свободно знаю.

А по х86… не любишь сегменты? Протект мод и флат режим к твоим услугам :) Хотя можно и без ПМ в флэт режиме работать. У Зубкова пример был. Опять же до 64к можно было в COM прекрасно уложиться и в одном сегменте. Вот узкое регистровое горло это да, порой вымораживает. Зато самих комбинаций регистров навалом. Хоть целый, хоть побайтно, в любой комбинации. Куча разных косвенных индексаций, возможность делать паровозы из адресов и смещений. Вообще удобно было с памятью работать, в отличии от той же load-Store. А еще сопроцессор и mmx добавляют лулзов. Хотя я их особо глубоко не копал. Так пару раз для вычисления одной шняги делал, когда писал на асме курсач по МПС (там надо было сэмулировать систему управления из движка, нагрузки и двух обратных связей по моменту и оборотам с ПИД регулятором). Лохов, наш препод по МПС, жог напалмом в заданиях.

Эти все режимы в подметки не годились возможностям PDP-11 или, скажем, Motorola 68000. Замечу, М68К и i86 — одногодки, объяснить кривость x86 тем, что они, типа, первопроходцы, не получается.

Вообще есть расширенный набор команд, используется в пик18 и в новых сериях пик16

Задача: Вывести число -1 на экран.
Почему программный код работает неправильно, хотя должен работать. А стоит изменить регистр на CL начинает работать как надо.
.model small
.data
.stack 80h
.code
start:
mov ax,@data
mov ds,ax
mov al,-1 // меняю здесь mov cl,-1
mov dl,'-'
mov ah,2
int 21h // добавляю строчку mov al,cl и начинает работать
neg al
mov dl,al
add dl,'0'
int 21h
mov ax,4c00h
int 21h
end start

я попытался написать код для вычитания(12-23)в ассемблере ,но видимо чего то не хватает и результат 245.Подскажите пожалуйста чего не хватает ?Заранее благодарен. ПС.Я новичок в ассемблере .Прошу прошения если глупый вопрос.Но очень хочется разобраться.


Разность - отрицательна, вот Вы и получаете отрицательное значение в дополнительном коде. А интерпретировать его пытаетесь как беззнаковое.

@Akina А понял спасибо, а это можно считать полноценную программу которая вычитает или что то нужно добавить ? И вообще кто то можно увидеть результат(-11) ?

1 ответ 1

Все ты делаешь так.

Все ты делаешь так. Просто 12 - 23 = -11 Отрицательное число.

А как представляются отрицательные числа в памяти современных процов ? В виде дополнения до двух, а такое представление очень удобно
потому что одна и та-же операция сложения или вычитания годится как
для знаковых, так и для беззнаковых чисел. От этого процессор не
знает знаковое число или беззнаковое. Различать их должен
программист. -11 знаковое в двоичном виде в байте будет хранится
как 11110101 245 беззнаковое в двоичном виде в байте будет
хранится тоже как 11110101 Ты должен их различать и никто иной.

Т.е. смотришь на 11110101 двоичное или что тоже самое F5 шестнавдцатеричное и говоришь себе - я понимаю это число как знаковое и тогда оно -11 или я понимаю это число как беззнаковое и тогда оно 245 А процик оба варианта не различает, он с обоими вариантами работает одними и теми-же операциями ADDWF SUBWF. А отладчик мысли твои читать не умеет и показывает это число как 245. Но для тебя, если ты в своей проге понимаешь это число как знаковое оно -11 и ничто иное.

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

Умножение положительных чисел

Для умножения положительных чисел в ассемблере предназначена команда “MUL”. У этой команды только один операнд — второй множитель, который должен находиться в регистре или в памяти. Местоположение первого множителя и результата задаётся неявно и зависит от размера операнда:

Размер операнда Множитель Результат
Байт AL AX
Слово AX DX:AX

Некоторые тонкости умножения:

  • Если аргументом команды mul является 1-байтовый регистр (например mul bl), то значение этого регистра bl умножится на значение регистра al, а результат запишется в регистр ax, и так будет всегда, независимо от того, какой 1-байтовый регистр взять. bl*al = ax
  • Если аргументом является регистр из 2 байт (например mul bx), то значение в регистре bx умножится на значение, хранящееся в регистре ax, а результат умножения запишется в регистр eax. bx*ax = eax
  • Если аргументом является регистр из 4 байт (например mul ebx), то значение в регистре ebx умножится на значение, хранящееся в регистре eax, а результат умножения запишется в 2 регистра: edx и eax. ebx*eax = edx:eax

Умножение отрицательных чисел

Для умножения чисел со знаком предназначена команда “IMUL”. Эта команда имеет три формы, различающиеся количеством операндов:

  • С одним операндом — форма, аналогичная команде MUL. В качестве операнда указывается множитель. Местоположение другого множителя и результата определяется по таблице.
  • С двумя операндами — указываются два множителя. Результат записывается на место первого множителя. Старшая часть результата в этом случае игнорируется. Эта форма команды не работает с операндами размером 1 байта.
  • С тремя операндами — указывается положение результата, первого и второго множителя. Второй множитель должен быть непосредственным значением. Результат имеет такой же размер, как первый множитель, старшая часть результата игнорируется. Это форма тоже не работает с однобайтными множителями.

Деление положительных чисел

Деление целых двоичных чисел — это всегда деление с остатком. По аналогии с умножением, размер делителя, частного и остатка должен быть в 2 раза меньше размера делимого. Деление положительных чисел осуществляется с помощью команды “DIV”. У этой команды один операнд — делитель, который должен находиться в регистре или в памяти. Местоположение делимого, частного и остатка задаётся неявно и зависит от размера операнда:

Размер операнда
(делителя)
Делимое Частное Остаток
Байт AX AL AH
Слово DX:AX AX DX

Некоторые тонкости деления:

  • Если аргументом команды div является 1-байтовый регистр (например div bl), то значение регистра ax поделится на значение регистра bl, результат от деления запишется в регистр al, а остаток запишется в регистр ah. ax/bl = al, ah
  • Если аргументом является регистр из 2 байт (например div bx), то процессор поделит число, старшие биты которого хранит регистр dx, а младшие ax на значение, хранящееся в регистре bx. Результат от деления запишется в регистр ax, а остаток запишется в регистр dx. (dx,ax)/bx = ax, dx
  • Если аргументом является регистр из 4 байт (например div ebx), то процессор аналогично предыдущему варианту поделит число, старшие биты которого хранит регистр edx, а младшие eax на значение, хранящееся в регистре ebx. Результат от деления запишется в регистр eax, а остаток запишется в регистр edx. (edx,eax)/ebx = eax, edx

Деление отрицательных чисел

Для деления отрицательных чисел предназначена команда IDIV. Единственным операндом является делитель. Местоположение делимого и частного определяется также, как для команды DIV. Эта команда также генерирует прерывание при делении на ноль или слишком большом частном.

Читайте также: