Как сделать отрицательное число в ассемблере
За время программирования чипов 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. Эта команда также генерирует прерывание при делении на ноль или слишком большом частном.
Читайте также: