Сравнение на 22 32-битови системни архитектури (системи от машинни команди)

Според IBM (14,4 MB, стр. 1-1) архитектурата на една изчислителна система дефинира атрибутите ѝ от гледна точка на програмиста, т.е. концептуалната структура и функционално поведение на машината, за разлика от организацията на потока от данни, логическата и физическа структура и производителността на отделните реализации на тази архитектура. (Няколко различни реализации или „микроархитектури“ могат да принадлежат на една и съща архитектура. Когато изпълнението на множество програми на различни микроархитектури дава резултатите, дефинирани от една-единствена архитектура, те се считат съвместими за тези програми.) Понятието „системна архитектура“ е идентично с понятието „система от машинни команди“ („instruction set architecture“) и се отнася не само до командите, но и до програмния модел, методите на адресация и др. характеристики на съответната системна архитектура.

Тук са разгледани целочислените операции на 22 такива архитектури (системи от команди), от които първите 6 са CISC, а останалите – RISC. Всички примерни програми на всеки асемблерен език са изпробвани със съответна тестова програма на C, откъдето се извикват. За някои това е направено на реална машина, за други – на емулатор (QEMU, GXemul, Hercules или SIMH).

1. S/390

Тази архитектура е най-старата от тип CISC (S/360 – 1964 г., S/370 – 1970 г., S/390 – 1990 г., S/390x или z/Architecture – 2000 г.), която още се използва и развива активно без загуба на обратна съвместимост. S/390 е последната 32-битова архитектура от тази серия. За разлика от останалите 32-битови архитектури тук адресите са само 31-битови (в S/360 и ранните S/370 – 24-битови).

Както и по-късната архитектура VAX, тук има сравнително малко команди и специализирани групи от команди за работа с пакетирани двоично-десетични числа, както и извънредно мощни специализирани команди, подпомагащи реализация на алгоритъма за сортиране чрез сливане, генериране на контролна сума, преобразуване в и от Unicode, работа с низове и др.п. Има възможност с една-единствена команда (MVC) да се копират произволен брой байтове от една област на паметта в друга, включително ако тези области се застъпват. Имената на командите са твърде къси и съкратени, което прави разтълкуването на имената им по-трудно. В сравнение с повечето 32-битови архитектури тип CISC, тук има твърде малко методи за адресация на паметта. Всъщност има само трикомпонентна адресация (база, индекс, отместване), в която някои от компонентите могат да липсват. Но отместването не може да бъде отрицателно, индексът не се мащабира и адресните регистри не се могат да се обновяват автоматично. В резултат на това, както и на факта, че командите за преходи с относителна адресация са само 32-битови (16-битови няма), плътността на кода тук е сравнително ниска и както се вижда от таблиците по-долу, за реализация на един и същ алгоритъм са необходими повече команди и размерът на машинния код се оказва по-голям, отколкото при повечето CISC-архитектури.

Може би най-интересната особеност на тази архитектура е начинът на отразяване на резултата от аритметичните и логически операции. Тук това става само в 2 бита и съответно с 4 възможни комбинации, като има отделни команди за работа с числа с и без знак. Условията се проверяват с 4-битова маска, всеки бит от която обозначава дали съответната комбинация отговаря на условието за преход или не. В документацията липсват опростени буквени мнемоники за тези маски, но за радост такива има в книгата „Assembler Language Programming for IBM z System Servers“ от Джон Ерман.

Друга интересна особеност са регистровите двойки, които се използват за 64-битовите числа при някои операции. Двойката трябва да се състои регистри с два последователни поредни номера, като първият, който съдържа старшата дума, е с четен номер, а вторият, съдържащ младшата – с нечетен. И накрая, за разлика от повечето CISC-архитектури, тук адресът на връщане се предава на извиканата подпрограма в регистър, а не чрез стека, което спестява машинни тактове, но ако от своя страна извиканата подпрограма извиква друга, то тя трябва предварително да съхрани този регистър в друг регистър или в стека.

2. ID32

Тази архитектура тип CISC е на първия 32-битов миникомпютър с цена под 10000 щ.д. – 7/32 на „Interdata“, както и на няколко по-късни съвместими модела (8/32 и серията 32XX). Тя е много подобна на S/390. Най-голямата разлика е в кодовете на условието. Тук има 4 флага за тях – C (пренос), V (препълване), L (по-малко от) и G (по-голямо от). За разлика от всички останали разгледани тук архитектури с програмно достъпни флагове на кода на условието, тук няма флаг Z (нула). За нулев резултат се приема такъв, в който и L, и G са нули. За съжаление липсва команда за условен преход при по-голямо след беззнаково (логическо) сравнение.

Поради необходимостта от използване на оригиналния компилатор на C за UNIX, който не поддържа 64-битови целочислени типове, функциите, които иначе трябва да връщат 64-битов резултат, го записват по подаден адрес, което добавя по две-три команди в тези функции. Но дори ако можеше да се използва поддържащият 64-битови типове GCC, пак за тази архитектура броят на необходимите за реализация на функциите команди би бил най-голям (виж таблицата по-долу). Това се обяснява с това, че няма команда за едновременно зареждане от стека в регистри на всички аргументи на функциите, а също с това, че аритметичните и логически команди са двуоперандни, а няма мощни команди за управление на цикли, макар да има команди за сложни реализирани с микрокод операции като изчисляване на контролен цикличен код и работа със свързани списъци.

3. VAX

Класическа 32-битова архитектура тип CISC. Има сравнително малко команди, но някои от тях са толкова мощни, че реализацията на един и същ алгоритъм може да се напише с по-малко команди, отколкото всички останали разглеждани тук CISC-архитектури. Командите и адресните режими са подбрани много сполучливо. Програмният модел е прост – 16 регистъра, 4 от които специализирани – програмен брояч, указател на стека, указател на рамката на стека и указател към аргументите, удобен, защото всички аргументи на функциите се предават само чрез стека. Недостатък спрямо другите архитектури е, че има команди за умножение с 64-битов резултат и за делене с 64-битово делимо само за числа със знак, но не и за числа без знак. За да се направи например умножение на числа без знак с 64-битов резултат, трябва да се направят корекции на старшата половина на произведението. VAX има специални команди за обработка на низове, пакетирани десетични числа и опашки, които са изключително мощни, макар и силно специализирани. Но много по-често се използват командите за обща употреба, които са малко на брой, но са много добре подбрани и изчистени до най-малката подробност. Най-голямото им предимство е абсолютната, стопроцентна ортогоналност. Всеки операнд може да бъде както регистър, така и клетка от паметта, а също и клетка от паметта с адрес в друга клетка – пълна косвена адресация. Повечето команди имат както двуоперандна, така и триоперандна разновидност, а има и такива с 4 операнда, например изключително мощната команда „ACB“, която обединява в себе си действията на три команди (три в едно) – събиране, сравнение и преход.

VAX има извънредно мощни методи за адресация и команди за цикли, както и нещо уникално – работи с 64 и дори 128-битови числа (само за прехвърляне и нулиране – АЛУ-то е 32-битово), включително 64- и 128-битови константи. Няма друга 32-битова архитектура с такива възможности. Показателно е, че макар на пръв поглед 4-те флага на регистъра за кода на условието да са както при повечето други архитектури (N, Z, V, C), при VAX след сравнение флаг N показва винаги истинския знак, дори и при препълване, а флаг V се нулира, защото понятието „препълване“ при сравнение е безсмислено. Тогава флаг N най-вероятно се получава от изключващото „ИЛИ“ между най-старшия разред на резултата и флага за препълване. (Става дума за състоянието на флаг N след сравнение; след изваждане той отразява знаковия бит като при всички архитектури с програмно достъпни флагове N, Z, V, C.)

VAX е наистина забележително постижение, с което по мнение на автора не може да се сравнява никоя друга архитектура тип CISC. Много е жалко, че още в края на XX век производството на компютри с тази архитектура беше спряно. Ако беше продължило, можеше чрез вътрешна транслация на мощните, но дълги и бавни команди до по-къси, прости и бързи команди тип RISC (подобно на подхода на Intel от „Pentium Pro“ нататък) да се получи производителност, сравнима с останалите архитектури.

4. WE32x00 (WE32K)

WE32000 (първоначално наричана „Bellmac 32“) е архитектурата на първия в света 32-битов микропроцесор, излязъл още през 1980 г. Това е „работният кон“ на най-влиятелната операционна система в историята – „UNIX System V“, която дава началото на огромно множество „юниксоподобни“ операционни системи. За CISC-архитектура тя има достатъчен брой целочислени регистри – 16 при WE32100, а при по-новия WE32200 – 32. За разлика от последния, WE32100 не поддържа индексна адресация с мащабиране, адресация с базов и индексен регистър и адресация с автоматично обновяване на адреса, макар че поддържа мощната косвена адресация през паметта. За съжаление в тази архитектура няма умножение с 64-битово произведение и делене с 64-битово делимо. Работата с флаговете на регистъра за кода на условието и предаването на аргументите са подобни на VAX, но липсват нейните мощни команди за управление на цикли. Затова пък има микропрограмна реализация във вид на команди с неявна адресация на някои функции и части от функции на стандартната библиотека на езика C за работа с ASCIIZ-низове – „strcpy“ (като команда „STRCPY“) и „strlen“ / „strcat“ (като команда „STREND“, която намира края на низа). За разлика от VAX и M68K (виж по-долу), при операции върху байтове и полудуми не се запазват останалите по-старши битове на операнда-местоназначение. Но има специални режими за привеждане на типове, което също показва, че тази архитектура е правена с оглед на поддръжка на езика C, разработен впрочем в същата фирма – „AT&T“ – заедно с операционната система „UNIX“.

В началото на всяка функция има по една команда за зареждане на всеки регистър, в който трябва да има аргумент на функцията. Изчисляването на адресите на по-големите от байт елементи на масиви също изисква повече команди. А и асемблерът автоматично заменя някои команди с поредици от други команди и поставя тук-там команди NOP за избягване на грешки в микропроцесора, открити след пускането му в производство. Всичко това понижава плътността на кода (виж таблиците по-долу).

5. 680x0 (M68K)

Тази архитектура е много подобна на VAX. Основните разлики са три – 16-те регистъра са разделени на 2 групи (8 даннови и 8 адресни), към флаговете е добавен флаг за разширение X, който съхранява стари преноси и с който работят някои команди за ротация вместо с преноса, и липсват специализираните команди за опашки и низове. Така, макар и степента на ортогоналност да е малко по-ниска, отколкото при VAX, програмирането за тази архитектура е много приятно. Макар че много персонални компютри бяха с нея (Macintosh, Amiga, Atari, SHARP и др.), нейната съдба не беше по-различна от тази на VAX. Тя просъществува още по-кратко от нея и още в средата на 90-те години на XX век беше, уви, изоставена.

6. x86 (IA-32)

Това е архитектура тип CISC. За разлика от предходните четири разгледани тук архитектури, тя все още се използва, и то изключително широко, вече няколко десетилетия. Въпреки това по мнение на автора тази архитектура е най-голямото недоразумение сред системните архитектури, скалъпено според проф. Дейвид Патерсън само за две седмици като временно решение, докато излезе iAPX-432 на Intel, но както често става, няма по-постоянно решение от временното. Тя има ниска степен на ортогоналност – много команди могат да се използват само с точно определени регистри и методи на адресация. Единствено при нея от всичките 22 разгледани тук архитектури се използва понятието „акумулатор“, което е характерно за 8-битовите микропроцесори. Тази ниска степен на ортогоналност прави програмирането за x86 подобно на бягане с препятствия, които са буквално на всяка крачка. Освен това броят на регистрите за обща употреба (доста условно понятие, защото всеки от тях е специализиран) е много малък – само 8. Този недостиг на регистри допълнително увеличава броя на необходимите за реализация на алгоритмите команди. Все пак IA-32 има мощни методи за адресация, включително трикомпонентна с мащабиране на индекса (но за разлика от VAX, WE32K и M68K, без пълна косвена адресация чрез паметта), както и мощни команди за обработка на низове, които за съжаление са и най-неортогоналните – работят с точно определени регистри и методи на адресация. Броят на командите за 40 години се удесетори, което затрудни програмистите още повече. Но защо тогава тази архитектура е толкова разпространена? По две причини – изборът на тази архитектура за IBM PC, чиято отворена спецификация позволи масово да бъде копиран по целия свят, включително и у нас, както и дългогодишното запазване на обратна съвместимост, подобно на S/360 на IBM.

7. ARM

Архитектура тип RISC с мощни методи за адресация с мащабиране на индекса и програмен модел с 16 регистъра почти като при VAX, което е характерно по-скоро за CISC. Това е най-използваната в момента архитектура в света, най-вече в мобилни устройства с батерийно захранване, чийто брой стана много по-голям от броя на персоналните компютри. Но не само там – вторият в света по производителност суперкомпютър в момента на писане на този текст – японският „Фугаку“, също е изграден на базата на ARM. Освен мощните методи за адресация, има още няколко причини за това, за реализацията на един и същ алгоритъм при ARM да са необходими почти толкова малко команди, колкото при VAX, и много по-малко, отколкото в x86. На първо място, командните са три- и четириоперандни. На второ място, всяка команда може да бъде условна, т.е. да се изпълнява само ако флаговете в регистъра за кода на условието са в определено състояние, установено от някоя предходна команда. На трето място, това дали дадена команда да променя тези флагове или не, може да се променя от програмиста. На четвърто място, десният операнд на много команди минава през изместващ блок, който може да го подложи на изместване или ротация, без за това да са необходими допълнителни машинни тактове. На пето място, има команди за едновременно прехвърляне на произволен набор от всички регистри, подобно на M68K. И на шесто място, ортогоналната система от команди позволява всеки регистър да се използва за произволни цели във всяка команда. За съжаление, много от така изброените особености са премахнати в 64-битовата версия на тази архитектура.

8. ARC

Много подобна на ARM RISC-архитектура с няколко важни различия. На първо място, плътността на кода е по-голяма поради командите с променлив размер, като най-кратките са 2 байта (при ARM за това има отделна система 16-битови команди). На второ място, целочислените регистри за обща употреба са двойно повече (32), като има и още толкова еднакво достъпни за командите специализирани регистри. На трето място, има апаратна поддръжка на цикли, като със специална команда се задава адресът на първата команда след цикъла, който започва със следващата команда след специалната, а броят итерации се задава в специализиран регистър. На четвърто място, освен че подобно на ARM има възможност почти всяка команда (без двубайтовите) да бъде условна, има мощни комбинирани команди за сравнение и разклонение, които нито използват, нито променят флаговете на регистъра за кода на условието. На пето място, има мощни команди за изчисляване на адреси чрез изместване на индекса и събиране с базовия адрес, а командите за умножение и натрупване използват специализиран акумулатор. На шесто място, няма възможност десният операнд да минава през изместващ блок, а командите за съхранение в паметта, за разлика от тези за зареждане от паметта, не могат да използват базово-индексен метод за адресация с променлив индекс, записан в регистър. Поради (или въпреки) тези различия, именно при ARC са необходими най-малко команди за решаване на примерните задачи в сравнение с другите архитектури (виж таблицата по-долу). Може само да се съжалява, че тази толкова добре проектирана архитектура не е така популярна като ARM.

9. PowerPC (PPC)

Тази архитектура е малко „по-RISC“ от ARM и ARC. Има по-малко методи на адресация и повече регистри за обща употреба – 32. От тях r0 е „scratch register“ (за временни променливи), r1 стеков указател, в r3 до r10 се предават аргументи на функциите, като тези регистри могат да се използват и за локални променливи, без да се съхраняват, а повечето от останалите регистри трябва да се съхраняват в стека, което може да става с 1 команда като при ARM. Това, че за предаване на аргументите се използват цели 8 (а не 3 или 4) регистъра, преди да се наложи предаване на следващите аргументи в стека, е много удобно. Удобно е и наличието на специален регистър-брояч на цикли и команди, които го намаляват и правят преход само ако той е различен от 0, като могат да комбинират това и с други проверки на условия. Има също условен преход към подпрограма и условно връщане от подпрограма. Адресът на връщане се пази в специален регистър със същото име като при ARM (lr). За всяка адресация и разновидност на всяка команда си има отделно име и ако се броят за различни команди, общият брой на командите става сравнително голям.

Най-интересните особености са при кодовете на условията. PPC има 2 отделни регистъра за тях. В първия („Condition Register“ или CR) има битове за по-малко, по-голямо и равно, а също и бит за препълване, който, веднъж установен, се нулира само със специална команда. Във втория („Excepton Regsiter“ или XER) са записани преносът CA, същото това препълване и „нормалното“ препълване, което се изменя, когато трябва. А кога трябва? Когато иска програмистът. Има избор или командата да не обнови нито един от двата регистъра, или да обнови само единия от тях (в повечето случаи), или да обнови и двата. Сравнението бива знаково и беззнаково („аритметично“ и „логическо“), а командите за условен преход след сравнение са едни и същи за числа със знак и без знак. Т.е. истинският бит за пренос след сравнението е скрит и командите за сравнение не обновяват бита за пренос CA в XER, а само битовете за по-голямо, по-малко и равно в CR в зависимост от типа си. Това налага след някои сравнения да се добавя по 1 команда за обновяване на преноса (в случаите, когато при x86 и ARM се използва това, че командата за сравнение променя и него).

Регистърът CR е разделен на 8 еднакви полета. Всяко от тях може да запомня по 1 условие. Условията в различните полета могат да се комбинират с побитови операции. Това е направено за поддръжка на сложни условия, макар че в езика C условията се изчисляват отляво надясно и в случай на логическо „И“ при първото неизпълнено условие изчисляването на следващите се прекратява (а при „ИЛИ“ то се прекратява след първото изпълнено условие). Явно тази възможност за запомняне на повече от 1 условие е направена за някои други езици. Изобщо тази архитектура е правена с отчитане на особеностите на езиците от високо ниво.

Няма команда „mov“. Тя се емулира чрез събиране с 0. Няма команда за проверка на флага за пренос. Това налага в случаите, когато след аритметична команда се проверява за пренос, с още 1 команда да се обновяват флаговете в CR и после вече те могат да се проверят с команда за условен преход. Няма ротация през пренос. Наляво това може да стане чрез събиране на регистър сам със себе си и преноса, но надясно няма как да стане, което е много неудобно в някои случаи. Но за сметка на това има 2 изключително мощни команди, използващи възможностите на вградения „barrel shifter“ (изместващ блок), които правят ротация наляво (без пренос) на даден регистър и копират поле от битове от него в друг на същото място (т.е. на същите битове), като или нулират останалите битове, или ги оставят без промяна.

Колкото до методите за адресация на паметта, има само 2 – адресът може да е равен или на съдържанието на някой регистър плюс постоянно отместване (косвена регистрова адресация с отместване), или на сбора от съдържанието на 2 регистъра (базово-индексна адресация). Има команди, завършващи на „u“, които обновяват съдържанието на адресния регистър при косвената регистрова адресация с отместване. При това първо се изчислява адресът чрез събиране на регистъра и отместването, след това се прави достъп до паметта по този адрес и накрая, ако командата завършва с „u“, се записва така полученият адрес в адресния регистър (т.е. това е преинкрементна или предекрементна адресация). Това налага преди всеки цикъл да се прави корекция на адреса чрез изваждане от адресния регистър на отместването, за да може при първото изпълнение на командата в цикъла първият достъп да е до началния адрес на масива. Така може да се използва преинкрементна или предекрементна адресация, спестявайки 1 команда за обновяване на адреса в цикъла. (И без това се налага да се добавя 1 команда, та по-добре тя е да е извън цикъла.)

10. SPARC

Архитектурата SPARC е още „по-RISC“ от PowerPC. Въпреки някои неудобства (липса на команди за ротация и за работа с битови полета, което увеличава малко броя на необходимите за реализацията на даден алгоритъм команди), това е важна архитектура, защото това е наследникът на първия RISC-процесор, разработен в Калифорнийския университет в Бъркли от Дейвид Патерсън. (Вторият е MIPS, който пък е наследник на разработката на Университета в Станфорд от Джон Хенеси. Именно Патерсън и Хенеси са авторите на книгите за организация и архитектура на компютъра, които се използват най-много за обучение по тези дисциплини в света.) Освен това SPARC е единственият микропроцесор, допуснат за използване в космоса от Европейската Космическа агенция. И накрая, системата от команди от известно време е свободна, т.е. няма нужда да се плаща за реализация. Съответно има доста реализации. Разработката ѝ продължава от японската фирма „Фужицу“.

В SPARC има регистър за състоянието, в който са и четирите класически бита N, Z V и C. Подобно на много други RISC, преходите в SPARC са задържани, т.е. има т.н. „branch delay slot“. За разлика от MIPS, асемблерът не може автоматично да поставя NOP, а тази задача е оставена на програмиста или компилатора, като има възможност, разбира се, вместо NOP да се постави полезна команда. При задържаните преходи е важно да се помни, че условието за изпълнение или неизпълнение на прехода се изчислява преди да бъде изпълнена следващата команда след командата за преход, но самият преход (т.е. извличането и дешифрирането на новата команда) се отлага за следващия такт след изпълнението на въпросната команда след командата за преход.

Най-характерната особеност при SPARC е т.н. „регистров прозорец“. С командата „save“ автоматично прозорецът се превърта до следващия набор от регистри, изходните регистри на извикващата програма стават входни на извикваната и се появяват нови локални регистри. Обратната команда е „restore“. Има възможност за до 8 нива с такива прозорци. Правено е изследване, че при това положение случаите на препълване на брояча на прозорците средностатистически са около 1,5%. Това препълване се обработва с програмно прекъсване, което, както и при M68K, се нарича „капан“ (trap). Чрез регистровите прозорци в повечето случаи се избягва необходимостта от съхраняване на регистри и създаване на стекова рамка. Всичко става по апаратен път за 1 такт или малко повече. Що се отнася до предаването на аргументите на функциите, SPARC заема междинно място между ARM и MIPS32, където само първите 4 думи на аргументите се предават в регистри (а останалите в стека) и PowerPC, където в регистри се предават първите 8 думи. При SPARC се предават в регистри първите 6 думи. Но за седмата и следващите думи трябва да се „превърти“ регистровият прозорец, при което указателят на стека се намалява най-малко с 96.

Някои микропроцесори с архитектура SPARC имат апаратен умножител, който умножава 32 по 32 бита само за 1 такт.

11. MIPS

Тази „ултра-RISC“ архитектура вече е със свободна система от команди – трета такава след SPARC и RISC-V. MIPS има изключително прост и „изчистен“ дизайн. Няма програмно достъпен регистър за кода на условието (т.н. „флагов регистър“); естествено в АЛУ-то такива флагове има, но те са скрити от програмиста. Условията се проверяват само с 4 команди – една с непосредствена адресация за числа със знак (SLTI), една с непосредствена адресация на втория операнд за числа без знак (SLTIU), една за регистрова адресация за числа със знак (SLT) и една с регистрова адресация за числа без знак (SLTU). Всяка от тези 4 команди може да установява регистъра в първия операнд в 1 или 0 в зависимост от това, дали е изпълнено или неизпълнено едно единствено условие, а именно – дали числото в регистъра на втория операнд е по-малко от числото на регистъра във втория операнд или не. (Да, само „по-малко“!) След това с втора команда се проверява дали така установеният регистър е равен или не е равен на 0 и ако е изпълнено условието, се прави преход към даден адрес по етикет. А как става проверката на останалите 3 условия? С размяна на регистрите във втория и третия операнд се постига проверка за по-голямо, с инверсия на логическото условие във втората команда – за по-голямо или равно и със съчетание на размяната на регистрите в първата команда и инверсията на логическото условие на втората команда – за по-малко или равно. (При командите с непосредствена адресация на втория операнд регистри не могат да се разменят, но може просто да се промени с 1 числото за сравнение.) Всички останали команди за сравнение и преход всъщност представляват макроси, които асемблерът автоматично транслира до някаква комбинация от гореописаните 2 вида команди, а именно – команда за установяване на регистър в зависимост от отношението между две числа и команда за проверка на този регистър и преход (разклонение). Гениално и просто.

Интересна особеност на тази архитектура е и че, подобно на SPARC, всяка команда след команда за преход (разклонение) се изпълнява, независимо от това дали се извършва преходът или не. (Това се нарича задържан преход или „branch delay slot“.) По тази причина асемблерът автоматично генерира команда NOP след всяка команда за преход, за да се изпълни именно този NOP, а не командата след него, освен ако не намери подходяща команда преди прехода, която да премести на мястото на този NOP. Изобщо асемблерът на MIPS е изключително интелигентен. Например той автоматично добавя команди за проверка за нулев делител при делене и генериране на изключение в този случай. Тази интелигентност може да се изключи с директивата „.set noreorder“, но тогава програмистът трябва сам да решава какво да постави вместо NOP-а (с други думи, коя команда да премести след командата за преход), ако това изобщо е възможно. Това в някои случаи намалява броя команди в програмата, но при всички случаи затруднява четенето на кода. Затова е добре, поне докато се придобие навик за това, да се оставя асемблерът да го прави.

Във второто издание на MIPS са били добавени няколко много полезни команди за работа с битови полета и за условно прехвърляне на данни. Общият брой на командите при MIPS е по-голям заради големия брой команди за обработка на преноса при многоразрядна аритметика. Например за събиране и изваждане на 64-битови числа в MIPS трябват 4 команди вместо 2 в процесорите с програмно достъпен флаг за пренос. Същото е и при изместването на 64-битови числа. Но това е цената, която се плаща за простотата. Не случайно MIPS беше първият микропроцесор, който премина към 64 бита – радикалният начин за отърваване от проблема с преноса, защото при 64 бита вече рядко се налага да се ползва такъв. Не трябва да се забравя и че през 90-те години фирмата „Silicon Graphics“ произвеждаше мощни работни станции с MIPS, на каквито беше правена и изключително реалистичната компютърна анимация на прочутия филм на Стивън Спилбърг „Юрски парк“ („Jurassic Park“) още през 1993 г.

12. RISC-V

Тази архитектура тип RISC е изначално проектирана като свободна. В RISC-V са избегнати някои недостатъци на по-ранните архитектури. Има само 4 метода за адресация – непосредствена, регистрова, косвена регистрова с отместване и относителна. Сравнението става само с 1 команда, а не с 2, както при повечето архитектури. Няма регистър за кода на условието („флагов регистър“). 32-битовият вариант има само 47 команди, които могат да са и 16-битови, и 32-битови, т.е. да се поберат в 2 или 4 байта. Изключително изчистен дизайн, който прилича на MIPS и може би води началото си от него, но е по-опростен. Например в него няма задържани преходи, което е голямо облекчение в сравнение с MIPS. В момента много фирми проектират такива процесори. Много перспективна архитектура, но все още не е успяла да се наложи масово, защото е относително нова – предложена е през 2010 г. Според автора x86 е масовата архитектура на миналото, ARM – на настоящето, а RISC-V – на бъдещето.

13. Nios II

Подобно на MicroBlaze (виж по-долу), това е архитектура за FPGA. Системата ѝ от команди е много близка до тази на RISC-V, а конвенцията на извикване – до тази на MIPS. Подобно на RISC-V, тук липсват задържани преходи, но за разлика от RISC-V, няма 16-битови команди – всички команди са 32-битови. Затова и плътността на кода е по-малка от тази при RISC-V, но по-голяма от тази при MIPS.

14. Xtensa

Архитектура тип RISC с дву- и трибайтови команди. Имената и действието на голяма част от командите съвпадат с тези на RISC-V и също като при нея, тук няма регистър за кода на условието. Друга прилика с RISC-V е това, че командите за сравнение и условен преход са обединени в една команда. Но за разлика от RISC-V, тук има само 16 регистъра за обща употреба и възможност за използване на регистрови прозорци подобно на SPARC. Рядко срещана особеност на тази архитектура е наличието на апаратно управлявани цикли (подобно на ARC), за управлението на които не се изразходват машинни тактове. С командата за цикъл се задават в специални регистри началният адрес на цикъла (адресът на следващата команда), крайният адрес на цикъла (с етикет, обозначаващ първата команда след цикъла) и броят итерации, равен на съдържанието на регистър с общо предназначение минус 1. Когато програмният брояч достигне крайния адрес на цикъла, ако броячът на цикъла още не е 0, процесорът автоматично го намалява с 1 и прави преход към началния адрес на цикъла. Това се извършва схемно, паралелно и независимо от работата на останалата част от процесора и затова не изисква допълнителни машинни тактове.

15. M88K

Типична RISC-архитектура на „Motorola“ с 32 целочислени регистъра за обща употреба и 32-битови команди. За разлика от почти всички RISC-архитектури, поддържа мощния базово-индексен метод на адресация с мащабиране на индекса. Друг интересен момент е обработката на условните преходи. Има единствена команда за сравнение, която сравнява съдържанието на 2 регистъра или на регистър с константа и установява в определено състояние 12 бита в регистър-местоназначение. Всеки от тях съответства на определено условие за преход. След това с команда за проверка на бит се избира един от тези битове (с двубуквен мнемоничен код) за проверка на съответното условие и ако той е лог. 1, се извършва преход. Има и съответната команда за проверка дали даден бит е лог. 0. Налична е и команда за условен преход, която проверява дали съдържанието на даден регистър е по-малко, по-малко или равно, равно, по-голямо или равно или по-голямо от нула. За съжаление няма команда за получаване на старшата половина на 64-битовото произведение на две 32-битови числа. За още по-голямо съжаление, тази перспективна архитектура скоро е била изоставена от „Motorola“.

16. OpenRISC 1000 (OR1K)

Още една свободна архитектура, обявена 10 години преди RISC-V, но така и не успяла да спечели сериозна популярност. Защо? Най-вече поради сериозни недоглеждания в системата от команди. Така например, старшата дума на 64-битовите произведения е недостъпна в потребителски режим, освен ако специален бит SUMRA в регистър SR не бъде изрично установен по програмен път в лог. 1. Освен това, немалка част от кодовото пространство е прахосана за инверсни едно на друго и следователно дублиращи се сравнения, а липсват някои полезни команди като изваждане с пренос, макар да има събиране с пренос. В гореспоменатия регистър SR има битове за пренос CY и препълване OV, но резултатът от сравненията се записва в отделен флаг F, който после с отделна команда се проверява като условие за преход. Мощните команди като условното прехвърляне CMOV са твърде малко, а изместванията и ротациите не работят с преноса. Всичко това прави програмите по-дълги, както може да се види от таблицата по-долу. С бит ND на регистър CPUCFGR процесорът може да се конфигурира да има или да няма задържани преходи, което означава, че програмите, писани за различните конфигурации са несъвместими помежду си.

17. MicroBlaze (µBlaze)

Също типична RISC-архитектура, но на т.н. „мек“ процесор за FPGA. Много добре подбрани команди, включително ротация през флага за пренос. Условните преходи стават с 2 команди. Първата от тях е команда за знаково (CMP) или беззнаково (CMPU) сравнение. За разлика от повечето архитектури, тя има 3 операнда. Втората от тези команди е команда за сравнение с 0 на регистъра – първи операнд на командата за сравнение, и преход в зависимост от резултата. Тази архитектура има една уникална особеност. Във въпросния регистър командата за сравнение записва разликата от сравняваните регистри, с изключение на най-старшия бит, където записва лог. 1, ако числото във втория операнд е по-голямо от това в третия, и лог. 0 в противен случай. Това води до следния „подводен камък“: при беззнаково сравнение на 0 с 0x80000000 и знаково сравнение на 0x80000000 с 0, най-старшият бит на регистъра-резултат от сравнението става 0, което води до нулиране на целия резултат, въпреки че двата операнда са различни! Затова сравнение за равенство не може да се прави с команди CMP и CMPU, а има специални команди PCMPEQ и PCMPNE, с които това може да става. Резултатът от тези команди е 0 (лъжа) или 1 (истина), който после може да бъде сравняван с 0 като условие за преход.

18. PA-RISC (HPPA)

Още една типична RISC-архитектура. Има много мощни команди, които могат да бъдат и условни, също както при ARM, но условността тук се изразява в анулиране на следващата команда при изпълнение на условие като резултат от текущата. Има 8 (16 за 64-битовата версия) флага за пренос – по 1 за всяка тетрада битове. В първата версия команда за целочислено умножение няма. Във версия 1.1 е добавена такава (под името „умножение на числа с фиксирана запетая без знак“), която работи с регистрите за числа с плаваща запетая, в/от които могат да се прехвърлят данни само чрез паметта (стека). Освен това няма делене, а само еднобитова „стъпка“ за деление, която трябва се повтаря 32 пъти заедно с още една команда. Поради гъвкавите и мощни условни команди за реализация на даден алгоритъм са необходими сравнително малък брой команди. Но за съжаление, и PA-RISC като VAX и M68K е „мъртва“ – HP я изоставиха в полза на „Itanium“, която свой ред също изоставиха след дълга агония. Изобщо, имайки предвид не само VAX, PA-RISC и „Itanium“, но и RISC-архитектурата „Alpha“, която също изоставиха, може да се каже, че фирмата HP се специализира в „погребения“ на компютърни архитектури. Това е много жалко.

19. SH-4

Тази RISC-архитектура на японската фирма „Хитачи“ е със силно опростени двуоперандни команди, за да могат да се поберат в 16 бита – за разлика от командите на повечето RISC-архитектури, които са 32-битови. Иначе регистрите и аритметиката са си 32-битови. Има и един-единствен флаг T, който служи ту като флаг за пренос, ту като флаг за препълване, ту като флаг за нула. Има команди за събиране и изваждане с пренос, но само те променят преноса – командите за събиране и изваждане без пренос не го променят. Така че за да се съберат или извадят 64 бита, първо трябва да се нулира T, а след това да се използва на два пъти команди за събиране или изваждане с пренос. Подобно на PA-RISC, няма команда за делене, а само за „стъпка“ за деление. Въпреки по-големия брой команди, необходими за реализацията на даден алгоритъм, поради двойно по-късите команди размерът на кода става много малък като при CISC.

Макар че и ARM има вариант с 16-битови команди, MIPS – също, RISC-V органично съчетава 32-битови с 16-битови команди, а при CRIS (виж по-долу) повечето команди са 16-битови, все пак SH-4 е единствената от разглежданите архитектури, при която всички команди са 16-битови.

20. CRIS

Подобно на SH-4, и при тази RISC-архитектура командите са 16-битови, но има и изключения. Допускат се команди с непосредствена адресация, при които командата става 32- или дори 48-битова. Въпреки това плътността на кода е голяма като при SH-4. За разлика от много RISC-архитектури, тук условията се проверяват с „класическите“ флагове на условията – N, Z, V и C (знак, нула, препълване и пренос). Това е единствената от разгледаните тук архитектури, която, въпреки че се води от тип RISC, позволява достъп до паметта при аритметичните и логически команди. Така този иначе верен критерий за различаване на RISC от CISC вече не може да се счита за верен при всички случаи. Още едно потвърждение на известния факт, че понастоящем CISC и RISC са доста размити понятия. Друга уникална особеност е тази, че тук липсва не само трикомпонентна, но дори и двукомпонентна адресация на паметта. Всички адреси на паметта се адресират с един-единствен регистър, в който предварително може да се зареди с една-единствена команда адресът като сбор от начален адрес и мащабиран индекс. Изобщо, доста оригинална архитектура, която, уви, вече не се поддържа.

21. Blackfin

Това е RISC-архитектура на микропроцесори с разширени възможности за обработка на сигнали. Също като в CRIS, тук повечето команди са 16-битови. Проверката на условията става подобно на SH-4 с един-единствен флаг CC, който може да се присвоява на резултатите от сравненията, стойностите на битове и др. Има отделни 8 даннови и 6 адресни (указателни) регистри, подобно на M68K. Също като Xtensa поддържа апаратно управлявани цикли (виж по-горе). Но най-характерната особеност на Blackfin е синтаксисът на командите. За разлика от всички останали архитектури, тук няма разделение на мнемоничен код на командата и операнди. Повечето команди използват подобен на езика C математически синтаксис с аритметични и логически изрази. Например вместо „ADD R0,R1,R2“ се пише просто „R0 = R1 + R2;“. Като разделител на командите се използва не символ за нов ред, а точка и запетая като в езика C. Има и команди със синтаксис, подобен на извикването на функции в C – с аргументите в скоби. Това оригинално решение прави писането и особено четенето на програмите на асемблер за тази архитектура по-привично за свикналите с езиците от високо ниво.

22. Hexagon

Тази архитектура е с „много дълга дума на командата“ (VLIW). При тези RISC-архитектури много (в случая до 4) команди се групират в т.н. „широка“ команда или пакет от команди, изпълнявани едновременно. Тук степента на контрол върху процесора е максимална. Разпределението и групирането на командите, което при останалите архитектури се прави апаратно по време на изпълнението, тук се прави по време на компилирането на програмата. При писане на асемблерен език програмистът трябва да групира командите така, че в един и същи пакет само една команда да променя даден регистър. Прочитан от друга команда в пакета регистър може да бъде записван от команда в същия пакет, защото преди изпълнението командите прочитат съдържанието на регистрите, а евентуалните нови стойности се записват чак след изпълнението им. Някои команди могат да използват и резултата от други команди в същия пакет. Разклоненията стават чрез проверка на т.н. предикати, които се променят от командите за сравнение, както и чрез някои сравнения на регистър с 0. Специално тази архитектура, също като предходната, е със синтаксис, подобен на езиците от високо ниво. Друга нейна особеност е възможността да се използват регистрови двойки, което много опростява операциите с 64-битови данни.

За да сравни плътността на кода на различните архитектури, авторът написа подпрограми с различни видове алгоритми за обработка на различни данни, ръчно оптимизирани за всяка архитектура, а именно:

 um64x64.s: линеен алгоритъм
absdif64.s: разклонен алгоритъм
sqroot64.s: цикличен алгоритъм
  lcmult.s: съчетание между цикличен и разклонен алгоритъм (с gcdivis.s)
nsamebit.s: обработка на битове
bitrev64.s: обработка на битове
 ucnvrf.s:  анализ на непозната програма
sequence.s: адресация на паметта и работа с масиви
search64.s: адресация на паметта и работа с масиви
 binsrch.s: адресация на паметта и работа с масиви
 inssort.s: адресация на паметта и работа с масиви
heapSort.s: адресация на паметта и работа с масиви + извикване на функция
  getvlq.s: работа с низове (поредици) от байтове
  putvlq.s: работа с низове (поредици) от байтове
getipstr.s: работа със символни низове
putipstr.s: работа със символни низове
getmacst.s: работа със символни низове
putmacst.s: работа със символни низове
  udiv64.s: многоразредни изчисления
 udiv128.s: многоразредни изчисления

В таблицата по-долу е даден броят на командите за различните подпрограми и архитектури:

          S/390  ID32  VAX  WE32K  M68K IA-32  ARM   ARC PowerPC SPARC MIPS RISC-V Nios2 Xtensa 88K OR1K µBlaze PA-RISC SH-4 CRIS Bfin Hexagon
absdif64    13    24    11    21    11     7     6     7    11     8    12    12    12    13    11    14    12     6    18    11    18     6
 binsrch    23    22    16    21    22    20    15    15    19    21    18    18    18    17    18    22    19    14    25    27    24    15
bitrev64    16    20     7    17     9     6     4     6    13    15    13    13    15    12    12    16     9     8     9     6     6     2
 gcdivis    16     0    11    12    11    11    11    10    15    16    12    13    13    13    14    20    15    13    21    16    18    13
getipstr    15    15     9    16    15    15    11    11    13    14    14    15    14    15    14    15    14    11    17    14    15    13
getmacst    30    32    17    29    28    27    22    22    27    30    25    27    26    28    27    30    27    24    35    30    33    20
  getvlq    15    13     8    10     9    11    10    10    12    12     9    11    10    11    11    11    11     8    12    11    11    10
heapSort    52    56    29    48    37    38    35    33    44    48    44    43    44    38    37    52    42    35    56    50    49    33
 inssort    17    19    13    20    19    17    15    14    17    20    16    16    16    15    17    21    19    15    21    21    20    14
  lcmult     7    24     5     8     9    12     5     5     6     5     6     5     5     5     5     5     5    19    18    29    14     9
nsamebit    12    14    10    11    15     6     8     8     8    11     9    10     9    11     9    11    10     9    12    13    12     6
putipstr    30    27    15    25    24    25    23    19    26    29    26    24    26    24    27    30    26    24    42    31    35    24
putmacst    24    27    12    23    18    20    16    17    19    23    20    22    22    21    20    25    22    16    29    23    23    14
  putvlq    20    20    14    17    13    15    12    12    15    19    15    16    16    16    15    19    16    13    21    18    18    13
search64    14    15    12    17    17    14    12     9    14    15    12    13    12    12    14    16    14    10    13    17    16    10
sequence     9    12     7    13     8    10     7     6    10    12    10    10    10     9    10    12    11    13    13    10    10     6
sqroot64    12    18    10    18    14    12    10    10    11    12    15    16    16    16    13    14    11     9    14    15    19     8
  ucnvrf    24    20    12    20    18    18    16    13    22    24    20    18    20    18    23    22    22    18    32    19    26    16
 udiv128    22    49    19    41    24    27    22    17    24    28    34    31    36    32    22    30    27    22    30    31    33    13
  udiv64     4    27    13    26     5     4    13    11    16     9    20    19    20    18    16    19    15    13    14    18    13    17
 um64x64    21    54    31    35    16    30    12    11    15    17    25    21    21    27    60    46    15    30    23    22    24    15
----------------------------------------------------------------------------------------------------------------------------------------------
           396   508   281   448   342   345   285   266   357   388   375   373   381   371   395   450   362   330   475   432   437   277

Както се вижда, най-малко команди за реализация на всички подпрограми са били нужни за RISC-архитектурата ARC, а най-много – за CISC-архитектурата ID32. Това опровергава разпространеното схващане, че архитектурите тип CISC постигат същия резултат с по-малко команди от тези от тип RISC. Например от двете най-използвани днес архитектури (x86 и ARM), именно за ARM, която е RISC, са били необходими значително по-малко команди за реализация на подпрограмите, отколкото за x86, която е CISC. Този парадокс е обяснен в бележките за двете архитектури по-горе.

А в следващата таблица е дадена дължината на всяка подпрограма в байтове (lcmult.s включва в себе си gcdivis.s):

          S/390  ID32  VAX  WE32K  M68K IA-32  ARM   ARC PowerPC SPARC MIPS RISC-V Nios2 Xtensa 88K OR1K µBlaze PA-RISC SH-4 CRIS Bfin Hexagon
absdif64    36    66    36    56    32    14    24    24    44    32    80    38    48    36    44    56    48    24    36    22    36    24
 binsrch    80    62    68    72    54    37    60    50    76    84    96    56    72    41    72    88    76    56    50    56    48    56
bitrev64    48    62    28    68    24    14    16    22    52    60    64    36    60    36    48    64    36    32    18    12    20     8
getipstr    48    38    40    52    38    25    44    36    52    56    64    40    56    38    56    60    56    48    34    32    32    44
getmacst   100    96    72    96    80    51    88    70   108   120   128    74   104    74   108   120   108    96    70    72    72    76
  getvlq    52    38    32    32    28    21    40    26    48    48    48    28    40    27    44    44    44    32    24    28    24    32
heapSort   172   172   100   156   110    85   140   110   176   192   224   134   176   107   148   208   168   144   112   108   104   116
 inssort    60    66    56    64    56    35    60    40    68    80    96    48    64    36    68    84    76    64    42    44    40    48
  lcmult    64    58    47    72    52    41    64    46    84    84   112    48    72    49    76   100    80   128    78    98    70    84
nsamebit    32    38    32    36    42    13    32    20    32    44    48    26    36    29    36    44    40    40    24    28    28    24
putipstr   116    84    88   100    76    50    96    74   104   120   144    80   108    68   108   120   108    96    92    88    84   104
putmacst    88    82    44    80    56    41    68    60    76    92    96    62    88    56    80   100    88    64    58    48    56    56
  putvlq    64    54    56    56    36    31    48    40    60    76    80    42    64    44    60    76    64    56    42    44    40    48
search64    48    42    44    52    48    31    48    26    56    60    64    38    48    28    56    64    56    40    26    36    32    44
sequence    32    28    24    36    22    17    28    18    40    48    48    24    40    20    40    48    44    56    26    20    24    24
sqroot64    36    42    36    52    40    27    40    32    44    48    80    42    60    42    52    56    44    40    28    32    44    32
  ucnvrf    88    58    56    78    66    44    72    58    96   110   128    72    96    55    98    94    96   160    80    60    80    78
 udiv128    80   140    64   148    66    75    88    64    96   112   176   100   144    88    88   120   108    88    60    64    88    56
  udiv64    12    72    40    84    22     9    52    36    64    36    96    48    80    51    64    76    60    56    32    40    36    68
 um64x64    64   136   116   128    44    47    48    42    60    68   112    70    84    71   240   184    60   120    46    44    64    52
----------------------------------------------------------------------------------------------------------------------------------------------
          1320  1434  1069  1518   992   708  1156   894  1436  1570  1984  1106  1540   996  1586  1806  1460  1440   978   976  1022  1074

Тази таблица се различава значително от предходната. Тук най-малка дължина (и съответно най-висока плътност) на кода има при CISC-архитектурата IA-32 заради кратките команди – средно по 2 байта, макар да има и много по-дълги. Най-голяма е дължината на кода при MIPS, която е RISC с 32-битови команди, но асемблерът на някои места е добавил команди NOP, команди за проверка за нулев делител и подравняващи команди NOP, които имат машинен код на операцията 0. При подобните на MIPS архитектури RISC-V и Xtensa е постигната много по-добра плътност на кода по три основни причини – липса на задържани преходи, обединяване на проверката на условие и условния преход в една-единствена команда и наличието на команди с дължина, по-малка от 32 бита.

Не само по тази причина, но най-вече поради това, че е свободна, RISC-V, която е и най-новата от разгледаните тук 22 архитектури, наистина може да се нарече системна архитектура на бъдещето.

Листинги за горепосочените 32-битови архитектури и за някои 64-битови (S/390x, AMD64, ARM64, ARC64, PPC64, SPARC64, MIPS64, RV64, LA64, Alpha, MMIX, PA-RISC 2.0, TILE-Gx, IA-64, E2K).

Заб.: Достъпът до листингите за 32-битовите архитектури става с името и паролата на администратора на зали 401Т и 402Т.