Погружение в ассемблер. Работаем с большими числами и делаем сложные математические вычисления - «Новости» » Самоучитель CSS
Меню
Наши новости
Учебник CSS

Невозможно отучить людей изучать самые ненужные предметы.

Введение в CSS
Преимущества стилей
Добавления стилей
Типы носителей
Базовый синтаксис
Значения стилевых свойств
Селекторы тегов
Классы
CSS3

Надо знать обо всем понемножку, но все о немногом.

Идентификаторы
Контекстные селекторы
Соседние селекторы
Дочерние селекторы
Селекторы атрибутов
Универсальный селектор
Псевдоклассы
Псевдоэлементы

Кто умеет, тот делает. Кто не умеет, тот учит. Кто не умеет учить - становится деканом. (Т. Мартин)

Группирование
Наследование
Каскадирование
Валидация
Идентификаторы и классы
Написание эффективного кода

Самоучитель CSS

Вёрстка
Изображения
Текст
Цвет
Линии и рамки
Углы
Списки
Ссылки
Дизайны сайтов
Формы
Таблицы
CSS3
HTML5

Новости

Блог для вебмастеров
Новости мира Интернет
Сайтостроение
Ремонт и советы
Все новости

Справочник CSS

Справочник от А до Я
HTML, CSS, JavaScript

Афоризмы

Афоризмы о учёбе
Статьи об афоризмах
Все Афоризмы

Видео Уроки


Видео уроки
Наш опрос



Наши новости

       
7-10-2020, 00:01
Погружение в ассемблер. Работаем с большими числами и делаем сложные математические вычисления - «Новости»
Рейтинг:
Категория: Новости

Как ты знаешь, регистры процессора 8088 — 16-битные. Однако при необходимости ты можешь работать через эти регистры не только с 16-битными числами, но и с числами большей разрядности: и с 32-битными, и даже более крупными. В этой статье я сначала расскажу как, а затем мы нарисуем знаменитый фрактал — множество Мандельброта.

Другие статьи курса


  • Погружение в assembler

  • Делаем первые шаги в освоении асма

  • Осваиваем арифметические инструкции

  • Как работают переменные, режимы адресации, инструкции условного перехода

  • Учимся работать с памятью


Простейшие операции над 32-битными числами


Сразу возникает вопрос: если регистры у нас 16-битные, то как с их помощью обрабатывать 32-битные числа? Ответ очевиден: мы просто будем задавать каждое число не одним регистром, а сразу двумя.


Только нам надо сначала определиться, какими регистрами и как мы будем для этого пользоваться. Давай не станем изобретать велосипед, а поищем подсказки в самом процессоре 8088.


У 8088 есть инструкция mul, которая умножает AX на 16-битный регистр и кладет результат в DX:AX. Также Сѓ него есть инструкция div, которая делит DX:AX РЅР° 16-битный регистр; результат попадает РІ регистр AX, а остаток — в DX. Еще Сѓ 8088 есть инструкция cwd. Она конвертирует знаковое 16-битное число из регистра AX РІ 32-битное число DX:AX.


Давай и мы, по примеру этих трех инструкций, тоже будем хранить 32-битные числа в DX:AX (РІ DX старшее слово, в AX — младшее). РќРѕ чтобы выполнять арифметические операции, нам нужно еще РѕРґРЅРѕ 32-битное число. Его, РїРѕ аналогии СЃ первым, будем хранить РІ CX:BXCX старшее слово, РІ BX — младшее).


Ну вот, мы с тобой условились, где и как хранить 32-битные числа. Теперь давай реализуем для них операцию сложения и операцию вычитания. Для этого нам пригодятся инструкции adc Рё sbb. Вот так выглядит сложение.



Удивлен, что операция сложения у нас заняла всего две инструкции? Сейчас объясню, что тут происходит. Дело в том, что, когда ты выполняешь инструкцию add, РѕРЅР° РЅРµ только складывает РґРІР° числа, РЅРѕ Рё изменяет флаг переноса. РљРѕРіРґР° результат операции сложения РЅРµ умещается РІ сдвоенный байт, инструкция add помещает старшую цифру результата (это всегда единица) во флаг переноса.


Инструкция adc dx, cx выполняет РІРѕС‚ такую операцию: DX = DX + CX + перенос, то есть прибавляет к итоговому результату то значение, которое хранится во флаге переноса.


Теперь давай реализуем вычитание 32-битных чисел по такому же принципу.



Что тут происходит? Инструкция sub вычитает РёР· РѕРґРЅРѕРіРѕ числа РґСЂСѓРіРѕРµ, Р° еще изменяет флаг переноса. РљРѕРіРґР° операция вычитания делает «заем» РёР· соседнего разряда, флаг переноса устанавливается РІ единицу.


Р?нструкция sbb dx, cx выполняет вот такую операцию: DX = DX – CX – перенос, то есть вычитает РёР· итогового результата то значение, которое хранится РІРѕ флаге переноса.


Мы с тобой успешно реализовали операции сложения и вычитания. Теперь давай реализуем логическое инвертирование и арифметическое инвертирование.


Чтобы сделать логическое инвертирование 32-битного числа (not), нам надо просто переключить все биты числа на противоположные.



Если требуется выполнить арифметическое инвертирование (neg), то есть поменять знак числа, нужно сделать то же самое, РЅРѕ только прибавить единицу Рє результату.


 

Реализуем операцию умножения двух 32-битных чисел


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


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


Поочередно, справа налево, умножаем каждую цифру множимого на вторую цифру множителя. Так у нас получается первая строчка промежуточного результата. Затем умножаем каждую цифру множимого на первую цифру множителя. Так у нас получается вторая строчка промежуточного результата. Затем сдвигаем вторую строчку на один разряд влево и суммируем два промежуточных результата.


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


РќРѕ это если «в роли цифры» Сѓ нас выступают цифры РѕС‚ 0 до 9. Однако, зная, что Сѓ процессора 8088 есть инструкция для умножения 16-битных чисел, РјС‹ для удобства можем РІ своем алгоритме умножения «назначить РЅР° роль цифры» сдвоенный байт. РўРѕ есть будем считать значения РІСЂРѕРґРµ 0x6725 Рё 0x1561 РЅРµ числами, Р° цифрами!


Почему это удобнее? Потому что для умножения двух 32-битных чисел (по две 16-битные цифры на каждое) нам понадобится всего четыре инструкции умножения. Тогда умножение двух 32-битных чисел можно будет реализовать вот так.


Умножение, конечно, выглядит сложновато по сравнению со сложением и вычитанием. Но не переживай, сейчас все объясню. Здесь весь алгоритм разделен на четыре операции умножения: по одной на каждое 16-битное слово. Точно так же, как на рисунке с умножением в столбик.


Кстати, если такой же алгоритм реализовывать на 32-битном процессоре, его можно расширить до операций над 64-битными числами, а если на 64-битном процессоре, то над 128-битными числами.


РќРѕ давай вернемся Рє нашему 16-битному алгоритму. Обрати внимание, здесь РїРѕРґ результат отводится только 48 Р±РёС‚. Рђ это значит, что если умножить, допустим, 0xFFFFFFFF на 0xFFFFFFFF, то старшие два байта потеряются. Чтобы они не терялись, нужно 64 бита, а не 48. Можешь в качестве домашнего задания доделать функцию — чтобы она возвращала 64-битный результат.


Как ты знаешь, регистры процессора 8088 — 16-битные. Однако при необходимости ты можешь работать через эти регистры не только с 16-битными числами, но и с числами большей разрядности: и с 32-битными, и даже более крупными. В этой статье я сначала расскажу как, а затем мы нарисуем знаменитый фрактал — множество Мандельброта. Другие статьи курса Погружение в assembler Делаем первые шаги в освоении асма Осваиваем арифметические инструкции Как работают переменные, режимы адресации, инструкции условного перехода Учимся работать с памятью Простейшие операции над 32-битными числами Сразу возникает вопрос: если регистры у нас 16-битные, то как с их помощью обрабатывать 32-битные числа? Ответ очевиден: мы просто будем задавать каждое число не одним регистром, а сразу двумя. Только нам надо сначала определиться, какими регистрами и как мы будем для этого пользоваться. Давай не станем изобретать велосипед, а поищем подсказки в самом процессоре 8088. У 8088 есть инструкция mul, которая умножает AX на 16-битный регистр и кладет результат в DX:AX. Также Сѓ него есть инструкция div, которая делит DX:AX РЅР° 16-битный регистр; результат попадает РІ регистр AX, а остаток — в DX. Еще Сѓ 8088 есть инструкция cwd. Она конвертирует знаковое 16-битное число из регистра AX РІ 32-битное число DX:AX. Давай и мы, по примеру этих трех инструкций, тоже будем хранить 32-битные числа в DX:AX (РІ DX старшее слово, в AX — младшее). РќРѕ чтобы выполнять арифметические операции, нам нужно еще РѕРґРЅРѕ 32-битное число. Его, РїРѕ аналогии СЃ первым, будем хранить РІ CX:BX (в CX старшее слово, РІ BX — младшее). Ну вот, мы с тобой условились, где и как хранить 32-битные числа. Теперь давай реализуем для них операцию сложения и операцию вычитания. Для этого нам пригодятся инструкции adc Рё sbb. Вот так выглядит сложение. Удивлен, что операция сложения у нас заняла всего две инструкции? Сейчас объясню, что тут происходит. Дело в том, что, когда ты выполняешь инструкцию add, РѕРЅР° РЅРµ только складывает РґРІР° числа, РЅРѕ Рё изменяет флаг переноса. РљРѕРіРґР° результат операции сложения РЅРµ умещается РІ сдвоенный байт, инструкция add помещает старшую цифру результата (это всегда единица) во флаг переноса. Инструкция adc dx, cx выполняет РІРѕС‚ такую операцию: DX = DX CX перенос, то есть прибавляет к итоговому результату то значение, которое хранится во флаге переноса. Теперь давай реализуем вычитание 32-битных чисел по такому же принципу. Что тут происходит? Инструкция sub вычитает РёР· РѕРґРЅРѕРіРѕ числа РґСЂСѓРіРѕРµ, Р° еще изменяет флаг переноса. РљРѕРіРґР° операция вычитания делает «заем» РёР· соседнего разряда, флаг переноса устанавливается РІ единицу. Р?нструкция sbb dx, cx выполняет вот такую операцию: DX = DX – CX – перенос, то есть вычитает РёР· итогового результата то значение, которое хранится РІРѕ флаге переноса. РњС‹ СЃ тобой успешно реализовали операции сложения Рё вычитания. Теперь давай реализуем логическое инвертирование Рё арифметическое инвертирование. Чтобы сделать логическое инвертирование 32-битного числа (not), нам надо просто переключить все биты числа на противоположные. Если требуется выполнить арифметическое инвертирование (neg), то есть поменять знак числа, нужно сделать то же самое, РЅРѕ только прибавить единицу Рє результату. Реализуем операцию умножения РґРІСѓС… 32-битных чисел Рђ теперь давай реализуем операцию умножения. Это уже будет посложнее. Здесь нужно вспомнить то, что ты изучал РІ начальной школе. РўС‹ же, надеюсь, еще РЅРµ разучился умножать числа РІ столбик? РќР° РІСЃСЏРєРёР№ случай напомню, что РјС‹ тут делаем. Поочередно, справа налево, умножаем каждую цифру множимого РЅР° вторую цифру множителя. Так Сѓ нас получается первая строчка промежуточного результата. Затем умножаем каждую цифру множимого РЅР° первую цифру множителя. Так Сѓ нас получается вторая строчка промежуточного результата. Затем сдвигаем вторую строчку РЅР° РѕРґРёРЅ разряд влево Рё суммируем РґРІР° промежуточных результата. Получается, чтобы перемножить РґРІР° двузначных числа, нам нужно выполнить четыре операции умножения. Рђ если надо перемножить числа большей разрядности, то операций умножения потребуется еще больше. РќРѕ это если «в роли цифры» Сѓ нас выступают цифры РѕС‚ 0 до 9. Однако, зная, что Сѓ процессора 8088 есть инструкция для умножения 16-битных чисел, РјС‹ для удобства можем РІ своем алгоритме умножения «назначить РЅР° роль цифры» сдвоенный байт. РўРѕ есть будем считать значения РІСЂРѕРґРµ 0x6725 Рё 0x1561 РЅРµ числами, Р° цифрами! Почему это удобнее? Потому что для умножения РґРІСѓС… 32-битных чисел (РїРѕ РґРІРµ 16-битные цифры РЅР° каждое) нам понадобится всего четыре инструкции умножения. РўРѕРіРґР° умножение РґРІСѓС… 32-битных чисел можно будет реализовать РІРѕС‚ так. Умножение, конечно, выглядит сложновато РїРѕ сравнению СЃРѕ сложением Рё вычитанием. РќРѕ РЅРµ переживай, сейчас РІСЃРµ РѕР±СЉСЏСЃРЅСЋ. Здесь весь алгоритм разделен РЅР° четыре операции умножения: РїРѕ РѕРґРЅРѕР№ РЅР° каждое 16-битное слово. Точно так же, как РЅР° СЂРёСЃСѓРЅРєРµ СЃ умножением РІ столбик. Кстати, если такой же алгоритм реализовывать РЅР° 32-битном процессоре, его можно расширить РґРѕ операций над 64-битными числами, Р° если РЅР° 64-битном процессоре, то над 128-битными числами. РќРѕ давай вернемся Рє нашему 16-битному алгоритму. Обрати внимание, здесь РїРѕРґ результат отводится только 48 Р±РёС‚. Рђ это значит, что если умножить, допустим, 0xFFFFFFFF на 0xFFFFFFFF, то старшие два байта потеряются. Чтобы они не терялись, нужно 64 бита, а не 48. Можешь в качестве домашнего задания доделать функцию — чтобы она возвращала 64-битный результат.

Теги: CSS

Просмотров: 527
Комментариев: 0:   7-10-2020, 00:01
Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь. Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.

 
Еще новости по теме:



Другие новости по теме:
Комментарии для сайта Cackle