Эмоциональная история процессоров для первых компьютеров с 70-х до начала 90-х

Intel: от 8086 до 80486

Безусловно, что одним из самых лучших процессоров, из сделанных в 70-е, является 8086, а также его более дешевый почти аналог 8088. Интересно, что 8088 и 8086 внешне выглядят идентично, их чипы имеют одинаковое число ножек и почти все ножки имеют одинаковую функциональность. Архитектуру этих процессоров приятно отличает отсутствие механических заимствований и следований абстрактным теориям, продуманность и взвешенность архитектуры, сбалансированность и ориентированность на дальнейшее развитие. Из недостатков архитектуры x86 можно назвать её некоторую громоздкость и склонность к экстенсивному росту числа инструкций.

Одним из гениальных конструктивных решений 8086 стало изобретение сегментных регистров. Этим как бы одновременно достигались две цели – "бесплатная" перемещаемость программ, размером до 64 КБ (это до середины 80-х был очень даже достойный объем для компьютерной памяти для одной программы), и адресуемость до 1 МБ адресного пространства. Можно ещё заметить, что 8086 как и 8080 или z80 имеет ещё и специальное адресное пространство для портов ввода-вывода размером 64 КБ (y 8080 и 8085 этот объем – 256 байт). Сегментных регистров всего четыре: для кода, для стека и два для данных. Таким образом, для быстрого использования доступны 64*4 = 256 КБ памяти, но это было очень много даже в середине 80-х. На самом деле проблем по размеру кода нет, так как можно использовать так называемые длинные вызовы подпрограмм с загрузкой и сохранением полного адреса из двух регистров. Есть только ограничение в 64 КБ на размер одной подпрограммы – это достаточно и для многих современных приложений. Некоторую проблему создаёт только невозможность быстрой адресации к массивам данных размером более 64 КБ – при использовании таких массивов нужно загружать сегментный регистр и собственно адрес при каждом обращении, что снижает скорость работы с такими большими массивами в несколько раз.

Сегментые регистры реализованы так, что их присутствие практически незаметно в машинных кодах, что позволило, когда пришло время, легко от них отказаться.

Довольно часто можно встретить критику сегментной организации памяти, т.е. такой организации, что в общем случае для адресации ячейки памяти нужно использовать два указателя. Однако, это какая-то странная критика, скорее надуманная. Сама по себе сегментация – это совершенно естественный способ организации виртуализации и защиты памяти. Фактически критике подвергалась не сама сегментация, а только максимальный размер сегментов, 64 КБ. Однако, это ограничение является прямым следствием реализации желания иметь большие объемы памяти при использовании 16-разрядных регистров. Поэтому вся критика сегментации – это фактически замаскированное требование перехода на 32-разрядную архитектуру. Ситуация усложнялась тем, что сегментация в первых x86 только частично имела функциональность нормального устройства управления памятью (MMU), в частности, работа с сегментными регистрами была доступна прикладным программам. В 80286 сделали полную поддержку сегментации, но это сделало прежние прикладные программы для 8086 несовместимыми с режимом, когда эта полная поддержка активировалась. Только с появлением 80386 все проблемы были решены и критика прекратилась, хотя 80386 по-прежнему использовал сементацию!

Удивительно, что почему-то практически невозможно найти подобную критику в отношении популярных PDP-11, где ограничения на использование памяти существенно более жёсткие. Не самые дорогие PDP-11 были существенно дороже лучших персональных компьютеров, а самые лучшие PDP-11 до середины 80-х были быстрее лучших IBM PC совместимых машин. PDP-11 были компьютерами более высокого класса до появления ПК на базе 80486 и использовали сегментирование...

Использование одного указателя для хранения полного адреса памяти было естественным в архитектуре мейнфреймов IBM, VAX и процессора 68000. Легко заметить, что в этом списке нет персональных компьютеров, так как даже 68000 изначально разрабатывали для сравнительно дорогих, неперсональных систем. Процессор 8086 сохранил много общего с 8080, который использовался больше как контроллер. Поэтому довольно странно сравнивать системы на базе 8088 с, например, VAX или даже рабочими станциями Sun – это совершенно разные классы машин. Но, возможно, благодаря Биллу Гейтсу IBM PC изначально стали равнять на гораздо более дорогие системы. Первый IBM PC имел всего 16 КБ памяти, а 64 КБ было в 1981 скорее роскошью для индивидуального покупателя. К середине 80-х типовые объемы памяти для IBM PC совместимых систем достигли 512 КБ – сегментация при таких объемах памяти практически никогда не могла создавать каких-либо затруднений. Когда типовые объемы памяти для IBM PC совместимых машин превысили 512 КБ появился 80386. Стоит вспомнить, что даже в 1985 большинство систем были 8-битные и для работы с памятью, объемами более 64 КБ, приходилось использовать переключения банков памяти – это на порядок или даже два более сложно и медленно, чем использование больших массивов с 8086. А первые IBM PC были вполне сопоставимы именно с 8-битными системами, а не с VAX. Кстати, альтернативный вариант дизайна IBM PC использовал Z80. Поэтому можно только восхититься инженерами Intel, которые смогли в течении более 40 лет развивать процессоры x86 так, чтобы они всё это время были относительно недорогими, технически одними из лучших и это при сохранении бинарной совместимости со всеми предшествующими моделями, начиная с 8086! Хотя это не рекорд, IBM поддерживает совместимость с архитектурой System/360 с 60-х.

Как это было отмечено, архитектура 8086 сохранила близость к архитектуре 8080, что позволило сравнительно небольшими усилиями переносить программы с 8080 (или даже с z80) на 8086 и особенно в случае, если были доступны исходные тексты программ.

Команды 8086 не отличаются высокой скоростью исполнения, но вполне сопоставимы с конкурентами, например, Motorola 68000, который появился на год позже. Одной из новинок, немного разгоняющих в целом неспешный 8086, стала очередь команд. Хотя влияние очереди команд на тайминги может делать эти тайминги значительно худшими по сравнению с приведенными в официальной документации. Дело в том, что в этой документации приводятся тайминги для инструкций, уже присутствующих в очереди команд, но для помещения инструкции в эту очередь 8086 или 8088 должны потратить 4 такта на каждый байт инструкции. Таким образом, например, инструкция MOV AX,BX, которая согласно документации выполняется за 2 такта в очередь команд считывается за 8 тактов! Если перед этой инструкцией стояла команда, время исполнения которой давало возможность полностью считать MOV, то задержки на считывание в очередь не будет и MOV отработает за 2 такта, но если, например, перед MOV стояла подобная инструкция с регистровым MOV, то будет значительная задержка. Такие факты позволяют некоторым утверждать, что Intel информацией по таймингам вводила потребителей в заблуждение. 80286 и 80386 требуют только 2 такта на считывание байта инструкции в очередь и имеют больший размер очереди, поэтому для них и более поздних процессоров острота проблемы была значительно уменьшена.

8086 использует восемь 16-разрядных регистров, некоторые из которых можно использовать как два байтовых регистра, а некоторые как индексные. Таким образом, регистры 8086 отличает некоторая разнородность, но она хорошо сбалансирована и регистры очень удобны в использовании. Эта разнородность, кстати, позволяет иметь более плотные коды. 8086 использует те же флаги, что и 8080, плюс несколько новых. Например, появился флаг типичный для архитектуры PDP-11 – пошагового исполнения. По сравнению с PDP-11 улучшилась логика описания работы с флагами для работы со знаковыми числами. Рассмотрим таблицу, где приведены соответствия между значениями флагов и отношениями между знаковыми числами.

Так по-разному описывали одни и те же отношения в разных компаниях

Из этой таблицы наверное естественно сделать вывод, что в Intel понимали логические операции, в DEC их понимали несколько хуже, ну а в Motorola похоже могли только списывать ДНФ из учебников по булевой алгебре.

8086 позволяет использовать очень интересные режимы адресации, например, адрес можно составить из суммы двух регистров и константного 16-разрядного смешения, на который накладывается значение одного из сегментных регистров. Из суммы, составляющей адрес, можно оставить только два или даже одно слагаемое. Такое на PDP-11 или на 68k одной командой не получится. Большинство команд 8086 не позволяют иметь оба операнда типа память, один из операндов должен быть регистром. Такой подход полностью аналогичен тому, что использовался на лучших тогда системах IBM/370. Вдобавок у 8086 есть ещё и строковые команды, которые как раз умеют работать с двумя адресами в памяти. Строковые команды позволяют делать быстрое блочное копирование (20 тактов на байт или слово), поиск, заполнение, загрузку и сравнение. Кроме того, строковые команды можно использовать при работе с портами ввода-вывода. Очень интересна идея 8086 использовать префиксы команд, позволяющие без существенного усложнения схем кодирования команд использовать часто очень полезную дополнительную функциональность.

8086 имеет одну из лучших среди всех компьютерных систем организацию работы со стеком. Используя только два регистра (BP и SP), 8086 позволяет решать все проблемы при организации вызовов подпрограмм с параметрами.

Среди команд есть знаковые и беззнаковые умножение и деление. Есть даже уникальные команды десятичной корректировки для команд умножения и деления. Трудно сказать, что в системе команд 8086 чего-то явно не хватает. Скорее наоборот.

Деление 32-разрядного делимого на 16-разрядный делитель с получением 32-разрядного частного и 16-разрядного остатка может потребовать до 300 тактов – не особенно быстро, но в несколько раз быстрее, чем такое деление на любых 8-битных процессорах (кроме 6309) и сравнимо по скорости с 68000. Деление на х86 имеет одну неожиданную и скорее неприятную особенность – оно непредсказуемо меняет флаги признаков.

В архитектуре x86 осталось унаследованная от 8080 команда XCHG, которая была усовершенствованна. Интересно, что x86 для команды NOP использует XCHG AX,AX. Из-за этого NOP получился сравнительно медленным, 3-тактовым. Подобных бесполезных операций MOV 8086 имеет всего 16 – это больше, чем Z80. Для XCHG бесполезных инструкций набирается даже 71, так как, например, эквивалентные инструкции XCHG BX,CX и XCHG CX,BX кодируются по-разному. XCHG – это редкий случай, когда AX обычно кодируется не как регистр общего назначения: XCHG с AX короче на один байт и быстрее на один такт, чем общий случай, и, кроме того, из-за меньшей длины, обычно быстрее, чем MOV. Однако, 7 более длинных и медленных инструкций XCHG, когда AX кодируется обычным образом – это особо некрасивая часть упомянутых бесполезных инструкций. В более поздних процессорах стали использоваться инструкции XADD, CMPXCHG и CMPXCHG8B, которые также могут выполнять атомарно обмен аргументов. Такие инструкции – одна из особенностей х86, их трудно встретить на процессорах других архитектур.

Можно резюмировать, что 8086 – это очень удачный процессор, сочетавший в себе как удобство программирования, так и привязанность к характерным для своего времени ограничениям по объему памяти. 8086 использовался сравнительно редко, уступив более дешевому 8088 почетное место быть первым процессором для основной для персональных компьютеров нашего времени архитектуры IBM PC. 8088 использовал 8-разрядную шину данных, что делало его несколько более медленным, но зато позволяло строить на его основе более доступные покупателям системы. Тут стоит отметить, что на момент своего появления, IBM PC был самым продвинутым персональным компьютером в мире, далеко опережая всех конкурентов.

IBM 5150 или первый IBM PC

Интересно, что Intel принципиально отказалась вносить усовершенствования в свои процессоры, предпочитая вместо этого разрабатывать их следующие поколения. Один из крупнейших субподрядчиков (second source) Intel, японская корпорация NEC, которая в начале 80-х была намного крупнее Intel, решила усовершенствовать 8088 и 8086, выпустив процессоры V20 и V30, совместимые с ними по разъёму и до 30% более быстрые. NEC даже предложила Intel стать своим субподрядчиком! Intel вместо этого начала судебный процесс против NEC, который, однако, не смогла выиграть. Почему-то эта большая разборка между Intel и NEC совершенно проигнорирована википедией. Интересно ещё, что почти все производители компьютеров в США и Европе упрямо и без комментариев продолжали использовать более медленные процессоры Intel.

80186 и 80286 появились в 1982. Таким образом Intel имела две почти независимые команды разработчиков. Тогда же появился и 80188, который отличался от 80186 только узкой шиной данных, – в Intel никогда не забывали о недорогих решениях для встроенных систем. 80186 – это улучшенный несколькими командами и сокращенными таймингами 8086 плюс несколько встроенных в чип схем, типичных для архитектуры х86: генератором тактов, таймерами, DMA/ПДП, контроллером прерываний, генератором задержек и др. Такой процессор, казалось бы, мог бы сильно упростить производство основанных на нём компьютеров, но из-за того, что встроенный контроллер прерываний оказался почему-то не совместимым с IBM PC, в ПК он почти никогда не использовался. Автору известна только система BBC Master 512 на основе компьютера BBC Micro, который не использовал встроенные схемы, даже таймер, но было ещё несколько систем, использующих 80186. Адресуемая память у 80186 осталась в как и у 8086 размером в 1 МБ. Японская корпорации NEC производила аналоги 80186, которые были совместимы с IBM PC.

Рассмотрим новые инструкции 80186:

80286 имел ещё лучшие тайминги, чем 80186, среди которых особо выделяется просто фантастическое деление (32/16=16,16) за 22 такта – с тех пор деление делать быстрее так и не научились! 80286 поддерживает работу с всеми новыми командами 80186 плюс много команд для работы в новом, защищенном режиме. 80286 стал первым процессором с встроенной поддержкой защищенного режима, который позволял организовать защиту памяти, правильное использование привилегированных инструкций, доступ к виртуальной памяти. Хотя работа в новом режиме сравнительно редко использовалась, это был большой прорыв вперёд. В этом новом режиме сегментные регистры приобрели новое качество, позволяя использовать до 16 МБ адресуемой памяти и до 1 ГБ виртуальной памяти на задачу. Главной проблемой 80286 была невозможность переключаться из защищенного режима в реальный, в котором работали тогда большинство программ. Используя "секретную" недокументированную инструкцию LOADALL, можно было использовать 16 МБ в памяти и в реальном режиме.

В 80286 вычисление адресов в операндах инструкций стало производиться отдельными схемами и перестало замедлять исполнение команд. Это добавило интересные возможности, например, командой LEA AX,[BX+SI+4000] всего за 3 такта стало возможным выполнить два сложения и перенос результата в регистр AX!

Сегментные регистры в защищенном режиме стали частью полноценной системы управления памятью (MMU). В реальном режиме эти регистры, как это уже упоминалось, лишь частично обеспечивали функциональность MMU.

Число производителей и конкретных систем, использующих 80286 огромно, но, конечно, первыми стали компьютеры IBM PC AT с почти фантастическими среди персональных компьютеров показателями по быстродействию. С этих компьютеров память начала отставать по быстродействию от процессора, появились состояния задержки, но тогда это казалось ещё чем-то временным.

У 80286, как и у 8086/8088, работа с прерываниями была реализована не на 100% корректно, что в очень редких случаях могло приводить к весьма неприятным последствиям. Например, команда POPF у 80286 всегда разрешала прерывания при своём исполнении, а при исполнении команды с двумя префиксами (в качестве примера можно взять REP ES:MOVSB) на 8086/8088 после вызова прерывания один из префиксов терялся. Ошибка в POPF была только у ранних выпусков 80286.

Защищенный режим 80286 был скорее неудобен, делил всю память на сегменты размером не более 64 КБ и требовал непростой программной поддержки работы с виртуальной памятью. Сегментный способ работать с памятью явно уступал почти по всем характеристикам страничному.

80386, появившись в 1985, сделал работу в защищенном режиме вполне комфортной, позволил использовать до 4 ГБ адресуемой памяти и легко переключаться между режимами. Кроме того, для поддержки многозадачности для программ для 8086 был сделан режим виртуального 8086. Для управления памятью стало возможным использовать как большие сегменты размером до 4 ГБ, так и удобный страничный режим. 80386 при всех своих новшествах сохранил полную совместимость с программами, написанными для 80286. Среди новшеств 80386 можно ещё назвать вытягивание регистров до 32-бит и добавление двух новых сегментных регистров. Кроме того, при вычислении адреса все регистры стали равноправными и стало возможным использовать масштабирование. Однако, это равноправие регистров добавило много бесполезных и некрасивых инструкций-двойников. Тайминги изменились, но неоднозначно. Был добавлен быстрый битовый сдвигатель (barrel shifter), что позволило делать множественные сдвиги с таймингами одного. Однако, это новшество очень сильно почему-то очень замедлило исполнение команд циклических сдвигов. Умножение стало чуть медленнее, чем у 80286. Работа с памятью стала, наоборот, чуть быстрее, но это не относится к строковым командам, которые остались быстрее у 80286. Автору этого материала не раз приходилось сталкиваться с мнением, что в реальном режиме с 16-битным кодом 80286 в итоге всё же чуть-чуть быстрее, чем 80386 на той же самой частоте.

В 80386 были добавлены новые команды, большинство из которых лишь давали новые способы для работы с данными, фактически дублируя с оптимизацией для некоторых случаев имеющиеся. Например, были добавлены команды:

Процессоры x86 до появления 80386 могли использовать только короткие, со смещением один байт условные переходы – этого было часто очень недостаточно. С 80386 стало возможным использовать смещения из двух или четырех байт, причем несмотря на то, что код новых переходов стал в два или три раза длиннее, время его исполнения осталось таким же как и у прежних, коротких переходов. Однако не всё было сделано идеально: возможно для защищенного режима стоило использовать 16-битные смещения вместо малополезных 8-битных.

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

Вследствие того, что основной защищенный режим стал гораздо проще для управления, чем в 80286, ряд унаследованных команд стали ненужными рудиментами. В этом основном защищенном, так называемом flat-режиме используют сегменты размером до 4 ГБ, что превращает все сегментные регистры в малозаметную формальность. А полудокументированный нереальный режим позволил даже использовать всю память как и во flat-режиме, но из простого для установки и управления реального режима.

С 80386 фирма Intel отказалась делиться своими технологиями, став фактически монопольным производителем процессоров для архитектуры IBM PC, а с ослаблением позиций Motorola, и для других архитектур персональных компьютеров. Системы на основе 80386 были очень дороги до начала 90-х, когда они стали наконец доступны массовым потребителям на частотах от 25 до 40 МГц. C 80386 IBM стала утрачивать позиции ведущего производителя IBM PC совместимых компьютеров. Это проявилось, в частности, в том, что первым ПК на основе 80386 стал в 1986 компьютер фирмы Compaq.

Трудно не сдержать восхищение теми объёмам работы, которая была проделана создателями 80386 и её результатами. Осмелюсь даже высказать предположение, что 80386 заключает в себе больше достижений, чем все технологические достижения человечества до 1970, а может и до 1980. Интересно, что команда разработчиков 80386 отличалась своеобразной и открытой религиозностью.

Довольно интересна тема ошибок в 80386. Напишу про две. Первые чипы имели некоторые команды, которые затем исчезли из руководств по этим процессорам и перестали исполняться на более поздних чипах. Если использовать первые источники информации по 80386 практически, может случиться неожиданное затруднение. Речь идет о командах IBTS и XBTS. У всех 80386DX/SX, производимых как AMD, так и Intel (что обнаруживает их любопытную внутреннюю идентичность), есть очень странный и неприятный баг, который проявлялся в уничтожение значения регистра EAX, если после записи в стек или выгрузке оттуда всех регистров командами POPAD или PUSHAD использовалась команда, использовавшая адрес с регистром BX. В некоторых ситуациях процессор мог даже зависнуть. Просто кошмарный баг и очень массовый, а в википедии нет даже упоминаний про него. Были и другие баги.

Появление ARM изменило ситуацию в мире компьютерных технологий. Несмотря на проблемы, процессоры ARM продолжали своё развитие. Ответом Intel стал 80486. В борьбе за быстродействие и за первое место в мире передовых технологий Intel пошла даже на уродующее до сих облик персонального компьютера решение – использование охлаждающего вентилятора.

В 80486 были улучшены тайминги большинства инструкций и некоторые из них стали выполняться как и на процессорах ARM за такт. Хотя умножение и деление почему-то стали чуть медленнее. Особенно странно, что однократные двоичные сдвиги и вращения регистра стали выполняться даже медленнее, чем с 8088! Появилась довольно большая для тех лет, размером 8 КБ, встроенная кэш-память. Появились и новые инструкции, например, CMPXCHG – она заняла место незаметно пропавших инструкций IBTS и XBTS (любопытно, что в качестве секретной эта инструкция была доступна уже и на поздних 80386). Новых инструкции совсем немного – всего шесть, из которых стоит упомянуть весьма полезную команду для смены порядка байт в 32-разрядном слове BSWAP. Большой полезной новинкой стало наличие встроенного в чип арифметического сопроцессора – так ещё никто не делал. Была также улучшена работа очереди команд.

Первые системы на базе 80486 были невероятно дороги. Довольно необычно, что первые компьютеры на базе 80486, модель VX FT, сделала английская фирма Apricot – их цена в 1989 была от 18 до 40 тысяч долларов, а вес системного блока – более 60 кг! Хотя появление именно здесь компьютерных систем на базе новейшего процессора Intel могло быть вызвано логикой конкуренции с ARM и Acorn. IBM выпустила первый компьютер на базе 80486 в 1990, это была модель PS/2 90 стоимостью $17000.

Трудно себе представить процессоры Intel без секретных, недокументированных официально возможностей. Часть таких возможностей скрывали от пользователей, начиная с самых первых 8086. Например, такой пусть и почти никому не нужный факт, что второй байт в инструкциях десятичной коррекции AAD и AAM имеет значение и может быть другим, вообще недесятичным (это было документировано только с процессора Pentium спустя 15 лет!). Более неприятно умолчание сокращенных команд AND/OR/XOR с операндом байтовой константой, например, AND BX,7 с опкодом длиной три байта (83 E3 07). Эти команды, делающие код более компактным, что было особенно важно с первыми ПК, были тихо вставлены в документацию только по 80386. Интересно, что в фирменных руководствах по 8086 или 80286 есть намек об этих командах, но конкретных опкодов по ним там нет. В отличие от похожих инструкций ADD/ADC/SBB/SUB, по которым была предоставлена полная информация. Это, в частности, привело к тому, что многие ассемблеры (все?) не умели производить более короткие коды. Ещё одна группа секретов скорее может быть названа некоторой странностью – ряд инструкций имеют по два кода операций. Речь идёт, например, об инструкциях SAL/SHL (опкоды D0 E0, D0 F0 или D1 E0, D1 F0) и некоторых других. Обычно, а может и всегда, используется только один код операции. Второй, секретный не используется практически никогда. Можно только удивляться, почему Intel так бережно сохраняет эти лишние, захламляющие пространство опкодов неофициальные дублирующие инструкции? Инструкция SALC ждала своего официального документирования до 1995 почти 20 лет! Инструкция для отладки ICEBP была официально несуществующей 10 лет с 1985 по 1995. Более всего писалось про тайные инструкции LOADALL и LOADALLD – они так навсегда и останутся тайными, так как их можно было использовать для простого доступа к большим объемам памяти только на 80286 и 80386 соответственно. До недавнего времени сохранялась интрига вокруг инструкции UD1 (0F B9), которая неофициально являлась примером неправильного опкода. Неофициальное недавно стало официальным.

В СССР был освоен выпуск клонов процессоров 8088 и 8086, а полностью воспроизвести 80286 так и не получилось. Удалось реализовать только расширенную систему команд 80186 и отдельный чип для управления памятью, что должно было позволять запускать программы для 80286. Интересно, что в ГДР смогли таки сделать клон 80286 к 1989.


Cтатья опубликована на geektimes, а также переведена на английский.