Сопроцессор можно назвать небольшим автономным компьютером. В ответ на получение конкретной машинной инструкции для выполнения операции с плавающей точкой (например, команды FSQRT для вычисления квадратного корня) сопроцессор выполняет собственную последовательность команд, сохраненных в ПЗУ. Эти внутренние команды называются микрокодом. Как правило, эти вычисления осуществляются с помощью цикла, поэтому их результат предоставляется не сразу. Тем не менее математический сопроцессор обычно решает задачи по меньшей мере в десять раз быстрее, чем эквивалентные процедуры, реализованные в виде ПО.
Материнская плата первого компьютера IBM PC предусматривала 40-контактное гнездо для микросхемы 8087 рядом с процессором 8088. К сожалению, это гнездо было пустым. Пользователям, которым требовалось ускорить операции с плавающей точкой, приходилось приобретать микросхему 8087 отдельно и самостоятельно устанавливать ее. Однако даже после установки математического сопроцессора не все приложения начинали работать быстрее. Некоторые программы, например текстовые редакторы, практически не нуждаются в вычислениях с плавающей точкой. Другие программы, вроде электронных таблиц, могут выполнять подобные вычисления гораздо чаще, поэтому они должны работать быстрее, однако от использования этой микросхемы скорость работы увеличивалась далеко не у всех приложений.
Дело в том, что программистам нужно было писать специальный код для использования машинных инструкций сопроцессора. Поскольку математический сопроцессор не являлся стандартным компонентом аппаратного обеспечения, многие себя этим не утруждали. В конце концов им все равно приходилось писать собственные подпрограммы для вычислений с плавающей точкой (поскольку у большинства пользователей не было математического сопроцессора), поэтому микросхема 8087 не освободила их от лишней работы, а наоборот, выявила дополнительные задачи. Со временем программисты научились писать приложения для использования математического сопроцессора, если он входил в состав компьютера, и эмулировать его работу, если не входил.
В дальнейшем компания Intel также выпустила математические сопроцессоры 287 и 387 для микропроцессоров 286 и 386 соответственно. В 1989 году появился процессор Intel 486DX, в который сопроцессор был встроен. С тех пор он перестал быть дополнительным компонентом. К сожалению, в 1991 году Intel сконструировала более дешевый микропроцессор 486SX без встроенного сопроцессора, а также отдельный математический сопроцессор 487SX. Однако с изготовлением процессора Pentium в 1993 году встроенный сопроцессор снова стал стандартом, вероятно навсегда. Компания Motorola интегрировала сопроцессор в микросхему 68040 в 1990 году. До этого Motorola продавала математические сопроцессоры 68881 и 68882 для изготовленных ранее микропроцессоров семейства 68000. Микросхемы PowerPC также предусматривают встроенный сопроцессор для выполнения вычислений с плавающей точкой.
Несмотря на то что аппаратный компонент для операций с плавающей точкой — подспорье для ассемблерного программиста, это устройство кажется малозначимой вехой в истории развития вычислительной техники, особенно если сравнивать с другими разработками, начатыми в 1950-х годах. Далее мы поговорим о языках программирования.
Глава 24
Языки высокого и низкого уровня
Писать программы в машинных кодах — все равно что есть с помощью зубочистки. Кусочки еды настолько малы, а процесс столь трудоемок, что обед может длиться вечно. Точно так же фрагменты машинного кода выполняют элементарные вычислительные задачи: загрузку числа из памяти в процессор, прибавление к нему другого числа, сохранение результата. На самом деле сложно понять, какую роль они играют.
По крайней мере, мы значительно продвинулись относительно описанного в начале главы 22 примитивного использования переключателей пульта управления для ввода в память двоичных данных. Тогда мы разобрались с написанием простых программ, позволяющих использовать клавиатуру и дисплей для ввода и просмотра шестнадцатеричных байтов машинного кода. Разумеется, это далеко не последнее из возможных улучшений.
Как вы знаете, байты машинного кода соответствуют некоторым коротким мнемоническим кодам, таким как MOV, ADD, CALL и HLT, благодаря чему компьютерные команды отдаленно напоминают английские слова. Эти мнемонические коды часто сопровождаются операндами, уточняющими действие машинной инструкции. Например, машинная инструкция для микропроцессора 8080 перемещает в регистр B содержимое ячейки памяти, 16-битный адрес которой хранится в паре регистров HL. Вот ее краткая запись.
MOV B, [HL]
Конечно, писать программы на языке ассемблера проще, чем в машинных кодах, однако микропроцессор этот язык не понимает. Я уже объяснял, как писать ассемблерные программы на бумаге. Когда вы будете готовы запустить подобную программу, вы вручную преобразуете инструкции на языке ассемблера в машинный код и введете их в память.
Прекрасно, если компьютер мог бы выполнить это преобразование вместо вас. Компьютер с процессором 8080, работающий под управлением операционной системы CP/M, предусматривает для этого все необходимые инструменты. Это работает следующим образом.
Сначала вы создаете текстовый файл, который будет содержать вашу программу, написанную на языке ассемблера. Для этого можете обратиться к программе CP/M ED.COM или к текстовому редактору, позволяющему создавать и изменять текстовые файлы. Предположим, вы создали текстовый файл с именем PROGRAM1.ASM. Тип ASM говорит, что файл содержит программу на языке ассемблера. Сам файл может выглядеть примерно так.
В этом файле встречается пара новых для нас команд. Первая — ORG, которая не является частью системы команд процессора 8080; она указывает, что адрес следующей команды должен начинаться с ячейки 0100h. Как вы помните, именно с этого адреса CP/M загружает программы в память.
Следующая команда — LXI (Load Extended Immediate — непосредственная загрузка регистровой пары), загружающая 16-битное значение в пару регистров DE. В данном случае это 16-битное значение указывается в качестве метки Text в нижней части программы перед оператором DB (Data Byte — байт данных), который мы также встречаем впервые. За этим оператором могут следовать несколько байтов, разделенных запятыми (как в приведенном выше примере), или некоторый текст в одинарных кавычках.
Команда MVI (Move Immediate — передача непосредственного операнда) перемещает значение 9 в регистр C. Команда CALL 5 вызывает функции CP/M. Функция 9 отображает на экране строку символов, начинающуюся по адресу, который находится в паре регистров DE, и заканчивающуюся значком доллара. (То, что текст в последней строке программы завершается значком доллара, может показаться странным, однако именно так работает операционная система CP/M). Последняя команда, RET, завершает программу и возвращает управление системе CP/M. (На самом деле это лишь один из способов завершения программы CP/M.) Оператор END обозначает окончание файла на языке ассемблера.