Главная страница

кр гаряев. Чарльз Петцольд - Код_ тайный язык информатики-Манн, Иванов и Фе. Книга принадлежит Контакты владельца Культовая книга талантливого преподавателя стала для многих первым уверенным шагом в программировании


Скачать 6.11 Mb.
НазваниеКнига принадлежит Контакты владельца Культовая книга талантливого преподавателя стала для многих первым уверенным шагом в программировании
Анкоркр гаряев
Дата19.10.2022
Размер6.11 Mb.
Формат файлаpdf
Имя файлаЧарльз Петцольд - Код_ тайный язык информатики-Манн, Иванов и Фе.pdf
ТипКнига
#743064
страница27 из 28
1   ...   20   21   22   23   24   25   26   27   28
на самом деле?
Во-первых, если вы попытаетесь выразить значения 16 777 216 и 16 777 217 в виде чисел одинарной точности с плавающей точкой, они окажутся одинако- выми. Более того, любое число в промежутке между этими двумя значениями
(например, 16 777 216,5) тоже будет совпадать с ними. Все три десятичных числа сохраняются в памяти в виде 32-битного числа одинарной точности с плаваю- щей точкой, которое, будучи разделенным на биты знака, порядка и значащей части, выглядит следующим образом.
4B800000h
0 10010111 00000000000000000000000
И оно эквивалентно
1,00000000000000000000000
ДВА
× 2 24
Следующее значение, выраженное двоичным числом с плавающей точкой, эквивалентно числу 16 777 218:
1,00000000000000000000001
ДВА
× 2 24
Хранение двух разных десятичных значений в виде одинаковых чисел с плавающей точкой не всегда создает проблемы.
Правда, если при написании банковской программы вы используете чис- ла одинарной точности с плавающей точкой для хранения денежных сумм

398
Код в долларах и центах, вас, вероятно, будет беспокоить то, что 262 144,00 долла- ра равны 262 144,01 доллара. Обе эти суммы выражаются числом:
1,00000000000000000000000
ДВА
× 2 18
Это одна из причин, почему при работе с долларами и центами предпо- чтительнее применять формат с фиксированной точкой. При использовании чисел с плавающей точкой вы можете обнаружить и другие раздражающие нюансы. Например, программа, выполняющая вычисление, в результате ко- торого долж но получиться число 3,50, выдает значение 3,499999999999. Так часто бывает при использовании чисел с плавающей точкой, и с этим ничего нельзя поделать.
Если вы твердо решили остановиться на числах с плавающей точкой, но вам недостаточно одинарной точности, попробуйте применить числа с плавающей точкой двойной точности. Числа в этом формате занимают восемь байт памя- ти, распределенных следующим образом.
1 знаковый бит (s)
11 бит порядка (e)
52 бита дробной значащей части (f)
Смещение порядка равно 1023, или 3FFh, поэтому число в этом формате записывается так:
(–1)
s
× 1,f × 2
е − 1023
К нулю, бесконечности и значениям NaN применяются правила, анало- гичные тем, которые мы рассматривали, когда говорили о числах одинарной точности.
Наименьшее положительное или отрицательное число двойной точности с плавающей точкой следующее:
1,0000000000000000000000000000000000000000000000000000
ДВА
× 2
–1022
В этом числе после двоичного разделителя следуют 52 нуля. Наибольшее число:
1,1111111111111111111111111111111111111111111111111111
ДВА
× 2 1023

Глава 23. Фиксированная точка, плавающая точка
399
Соответствующие десятичные числа формируют диапазон примерно от 2,2250738585072014 × 10
–308
до 1,7976931348623158 × 10 308
. Число 10 308 очень велико, оно представляет единицу с 308 нулями.
Пятидесятитрехбитная значащая часть числа (включая первый неучиты- ваемый бит) приблизительно эквивалентна 16 десятичным знакам. Это уже намного лучше формата с одинарной точностью, однако вероятность того, что какое-то число однажды станет равно другому, по-прежнему существует. Возь- мем, к примеру, числа 140 737 488 355 328,00 и 140 737 488 355 328,01. Они оба бу- дут храниться в виде 64-битного числа двойной точности с плавающей точкой:
42E0000000000000h.
В двоичном формате это число выглядит так:
1,0000000000000000000000000000000000000000000000000000
ДВА
× 2 47
Разумеется, разработка формата для хранения в памяти чисел с плаваю- щей точкой является лишь небольшим этапом в процессе фактического исполь- зования этих чисел в программах, написанных на языке ассемблера. Если бы вы конструировали компьютер с нуля, сейчас бы возникла проблема создания набора функций для сложения, вычитания, умножения и деления чисел с пла- вающей точкой. К счастью, эти задачи можно разбить на более мелкие под- задачи, связанные со сложением, вычитанием, умножением и делением целых чисел, решать которые вы уже научились.
Например, сложение чисел с плавающей точкой сводится к сложению их значащих частей; сложность заключается лишь в порядках. Предположим, вам необходимо выполнить следующую операцию сложения:
(1,1101 × 2 5
) + (1,0010 × 2 2
).
В данном случае нужно сложить числа 11101 и 10010, однако второе число необходимо преобразовать с учетом разницы в значениях порядков. Фактичес- ки требуется сложить целые числа 11101000 и 10010. Итоговая сумма составит:
1,1111010 × 2 5
Иногда разница в порядках может быть такой большой, что одно из двух чисел даже не повлияет на сумму. Это может произойти, например, при сло- жении расстояния от Земли до Солнца и радиуса атома водорода.

400
Код
Перемножение двух чисел с плавающей точкой сводится к перемножению двух значащих частей как обычных целых чисел и сложению двух целочис- ленных значений порядков. Нормализация значащей части может привести к уменьшению нового значения порядка.
Следующий уровень сложности при использовании чисел с плавающей точкой связан с вычислением квадратных корней, степеней, логарифмов и три- гонометрических функций. Однако все эти задачи можно решить с помощью четырех основных операций: сложения, вычитания, умножения и деления.
Например, такая тригонометрическая функция, как синус, вычисляется с помощью разложения в ряд.
sin ( x) = xx
3 3! +
x
5 5! −
x
7 7! + ...
Аргумент x должен выражаться в радианах, где 360° — это 2π радиан, вос- клицательный знак — факториал числа, или произведение всех целых чисел от 1 и до этого числа. Например, 5! = 1 × 2 × 3 × 4 × 5. В данном случае все сво- дится к простому умножению. Возведение числителей дробей в степень тоже предполагает умножение. Остальными операциями являются деление, сложе- ние и вычитание. Единственная по-настоящему сложная часть — многоточие в конце выражения, означающее, что это вычисление может продолжаться
бесконечно. На практике если вы ограничитесь диапазоном от 0 до π/2 (из ко- торого можно вывести все остальные значения синуса), то сможете избежать лишних вычислений. Вам достаточно десятка слагаемых в этом разложении для получения 53-битных значений двойной точности.
Если учесть, что компьютеры предназначены для того, чтобы облегчать людям жизнь, может показаться, что написание множества подпрограмм для выполнения арифметических операций с плавающей точкой противоречит цели их создания. Однако в этом вся прелесть программного обеспечения.
Написанные кем-то подпрограммы для конкретного компьютера могут ис- пользоваться другими людьми. Арифметика с плавающей точкой настолько важна для научных и инженерных приложений, что ей традиционно придает- ся огромное значение. На заре компьютерной эры при создании программного обеспечения для нового типа компьютеров написание подпрограмм для вы- полнения расчетов с плавающей точкой было одной из первоочередных задач.
Целесообразно разработать машинные инструкции специально для выпол- нения вычислений с плавающей точкой! Очевидно, это легче сказать, чем сде- лать, однако важность такой задачи трудно переоценить. Если вы сможете реа- лизовать арифметику с плавающей точкой на уровне аппаратного обеспечения, подобно командам умножения и деления в 16-разрядных микропроцессорах,

Глава 23. Фиксированная точка, плавающая точка
401
то все вычисления с плавающей точкой будут выполняться компьютером го- раздо быстрее.
Первым коммерческим компьютером, в котором вычисления с плавающей точкой могли осуществляться на аппаратном уровне, был IBM 704, выпущенный в 1954 году. Все числа в нем хранились в виде 36-битных значений. Числа с пла- вающей точкой разбивались на 27-битную значащую часть, 8-битный порядок и однобитный знак. Специальные аппаратные компоненты для расчетов с пла- вающей точкой могли выполнять операции сложения, вычитания, умножения и деления. Остальные функции реализовывались в программном обеспечении.
В настольном компьютере аппаратное обеспечение для вычислений с пла- вающей точкой появилось в 1980 году, когда компания Intel выпустила чип 8087.
Интегральные микросхемы такого типа в наши дни называются математиче-
скими сопроцессорами, или блоками вычислений с плавающей точкой. Микро- схема 8087 была названа сопроцессором, поскольку не могла работать сама по себе. Ее можно было использовать только в сочетании с первыми 16-раз- рядными микропроцессорами Intel 8086 и 8088.
Сопроцессор 8087 — микросхема с 40 выводами, которая использует мно- гие из тех же сигналов, что и микропроцессоры 8086 и 8088. С помощью этих сигналов микропроцессор и математический сопроцессор взаимодействуют.
Когда ЦПУ считывает специальную команду ESC (Escape), сопроцессор пере- хватывает управление и выполняет следующий машинный код, соответствую- щий одной из 68 команд для вычисления тригонометрических функций, сте- пеней, логарифмов и т. д. Типы данных основаны на стандарте IEEE. В свое время сопроцессор 8087 считался самой сложной интегральной схемой.
Сопроцессор можно назвать небольшим автономным компьютером. В от- вет на получение конкретной машинной инструкции для выполнения операции с плавающей точкой (например, команды 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-х годах. Далее мы поговорим о языках программирования.

403
Глава 24
Языки высокого и низкого уровня
Писать программы в машинных кодах — все равно что есть с помощью зу- бочистки. Кусочки еды настолько малы, а процесс столь трудоемок, что обед может длиться вечно. Точно так же фрагменты машинного кода выполняют элементарные вычислительные задачи: загрузку числа из памяти в процессор, прибавление к нему другого числа, сохранение результата. На самом деле слож- но понять, какую роль они играют.
По крайней мере, мы значительно продвинулись относительно описанного в начале главы 22 примитивного использования переключателей пульта управ- ления для ввода в память двоичных данных. Тогда мы разобрались с написани- ем простых программ, позволяющих использовать клавиатуру и дисплей для ввода и просмотра шестнадцатеричных байтов машинного кода. Разумеется, это далеко не последнее из возможных улучшений.
Как вы знаете, байты машинного кода соответствуют некоторым корот- ким мнемоническим кодам, таким как MOV, ADD, CALL и HLT, благодаря чему компьютерные команды отдаленно напоминают английские слова. Эти мнемо- нические коды часто сопровождаются операндами, уточняющими действие ма- шинной инструкции. Например, машинная инструкция для микропроцессора
8080 перемещает в регистр B содержимое ячейки памяти, 16-битный адрес ко- торой хранится в паре регистров HL. Вот ее краткая запись.
MOV B, [HL]
Конечно, писать программы на языке ассемблера проще, чем в машинных кодах, однако микропроцессор этот язык не понимает. Я уже объяснял, как писать ассемблерные программы на бумаге. Когда вы будете готовы запус тить подобную программу, вы вручную преобразуете инструкции на языке ассемб- лера в машинный код и введете их в память.

404
Код
Прекрасно, если компьютер мог бы выполнить это преобразование вмес- то вас. Компьютер с процессором 8080, работающий под управлением опера- ционной системы CP/M, предусматривает для этого все необходимые инстру- менты. Это работает следующим образом.
Сначала вы создаете текстовый файл, который будет содержать вашу про- грамму, написанную на языке ассемблера. Для этого можете обратиться к про- грамме CP/M ED.COM или к текстовому редактору, позволяющему созда- вать и изменять текстовые файлы. Предположим, вы создали текстовый файл с именем PROGRAM1.ASM. Тип ASM говорит, что файл содержит программу на языке ассемблера. Сам файл может выглядеть примерно так.
ORG 0100h
LXI DE, Text
MVI C, 9
CALL 5
RET
Text: DB 'Hello! $'
END
В этом файле встречается пара новых для нас команд. Первая — 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 обозначает окончание файла на языке ассемблера.

Глава 24. Языки высокого и низкого уровня
405
Итак, у нас есть текстовый файл, содержащий семь строк текста. Теперь его нужно ассемблировать, то есть преобразовать в машинный код. Раньше мы делали это вручную. Однако теперь можем использовать предусмотрен- ную в CP/M специально для этой цели программу-ассемблер ASM.COM. Она запускается из командной строки CP/M следующим образом.
ASM PROGRAM1.ASM
Программа ASM просматривает файл PROGRAM1.ASM и создает новый файл с именем PROGRAM1.COM, который содержит машинный код, соответ- ствующий написанным нами на языке ассемблера командам. (Этот процесс включает еще один этап, однако он не имеет особого значения в описываемом примере.) Теперь вы можете запустить файл PROGRAM1.COM из командной строки CP/M. На экране отобразится текст Hello! — и все закончится.
Файл PROGRAM1.COM содержит следующие 16 байт.
11 09 01 OE09 CD05 00 C9 48 65 6C6C6F 21 24
Первые три байта — инструкция LXI, следующие два байта — инструк- ция MVI, следующие три байта — инструкция CALL, следующий байт — ин- струкция RET. Последние семь байт — это ASCII-коды, соответствующие пяти буквам слова Hello, восклицательному знаку и значку доллара.
Действия ассемблера, такого как ASM.COM, сводятся к чтению ассемб- лерной программы, часто называемой файлом с исходным кодом, и написа- нию исполняемого файла, содержащего машинный код. По большому счету ассемблеры довольно простые программы, поскольку между мнемониче- скими ассемблерными обозначениями команд и машинным кодом суще- ствует взаимно-однозначное соответствие. Ассемблер разделяет каждую текстовую строку на мнемокоды команд и аргументы, а затем сравнивает их с содержащимся в нем списком всех возможных мнемокодов и аргумен- тов. Это сравнение показывает, какие машинные инструкции соответству- ют каждой команде.
Обратите внимание на то, как ассемблер «понимает», что инструкция LXI должна сохранить в паре регистров DE адрес 0109h. Если сама инструкция
LXI находится по адресу 0100h (а она там и находится, когда система CP/M загружает программу в память для последующего запуска), адрес 0109h соот- ветствует началу текстовой строки. Обычно программисту, использующему ассемблер, не нужно беспокоиться о конкретных адресах, связанных с различ- ными частями программы.

406
Код
Создателю первого ассемблера, разумеется, пришлось вручную преобра- зовывать программу в машинный код. Человек, пишущий новый (возможно, улучшенный) ассемблер для того же компьютера, может воспользоваться язы- ком ассемблера, чтобы затем преобразовать первый ассемблер. Как только но- вый ассемблер будет превращен в машинный код, он сможет ассемблироваться.
С выходом нового микропроцессора возникает необходимость в но- вом ассемблере. Однако новый ассемблер можно написать на существующем компьютере, используя его же транслятор. Когда ассемблер, работающий на ком- пьютере А, создает код, который выполняется на компьютере Б, он называет- ся кросс-ассемблером.
Несмотря на то что ассемблер избавляет от необходимости решать наи- менее творческие задачи программирования (вручную преобразовывать про- грамму в машинный код), он не решает двух основных проблем, связанных с этим языком. Возможно, вы уже догадались, что первая проблема в том, что все действия с ассемблером могут быть крайне утомительными, поскольку вам приходится работать на уровне микропроцессора и беспокоиться о каждой мелочи.
Вторая проблема: написанные на языке ассемблера программы не явля- ются переносимыми. Если вы пишете ассемблерную программу для микропро- цессора Intel 8080, она не подойдет для микропроцессора Motorola 6800. Вам придется переписать ее на языке ассемблера 6800. Вероятно, это будет не так сложно по сравнению с написанием исходной программы, поскольку вы уже решили основные организационные и алгоритмические задачи. Однако это все равно потребует серьезных действий.
В предыдущей главе я говорил, что в современные микропроцессо- ры встроены машинные инструкции, выполняющие арифметические опе- рации над числами с плавающей точкой. Это, безусловно, удобно, но это мало что дает. Предпочтительнее было бы полностью отказаться от машин- но-зависимых инструкций, которые производят отдельные элементарные арифметичес кие операции, а вместо этого выражать множество математи- ческих операций, используя проверенную временем алгебраическую форму записи. Например:
A × sin (2 × π + B) / C,
где A, B и C — числа, а число π равно 3,14159.
Почему бы и нет? Если такое выражение записано в текстовом файле, у вас должна быть возможность написать ассемблерную программу, которая читает этот текстовый файл и преобразует алгебраическое выражение в машинный код.

Глава 24. Языки высокого и низкого уровня
407
Если бы вам требовалось вычислить значение этого алгебраического вы- ражения только один раз, вы могли бы сделать это вручную или с помощью калькулятора. Вероятно, вы собираетесь использовать компьютер, поскольку вам необходимо вычислить значение этого выражения при многих различ- ных значениях A, B и C. По этой причине вы также должны предусмотреть для данного алгебраического выражения некоторый контекст, позволяющий вычислять его значение при разных коэффициентах.
То, к созданию чего вы приблизились, называется высокоуровневым язы- ком программирования. Язык ассемблера считается низкоуровневым, посколь- ку взаимодействует непосредственно с аппаратным обеспечением. Несмотря на то что термин «язык высокого уровня» используется для описания любого языка программирования, отличного от языка ассемблера, некоторые языки считаются более высокоуровневыми по сравнению с другими. Если бы вы, бу- дучи президентом компании, могли бы сесть за компьютер и ввести команду
(еще лучше просто положить ноги на стол и продиктовать): «Рассчитать все прибыли и убытки за этот год, написать годовой отчет, распечатать несколько тысяч копий и разослать всем нашим акционерам», это бы означало, что вы работаете с языком очень высокого уровня! В реальном мире языки програм- мирования даже не приближаются к идеалу.
Человеческие языки — это сотни и тысячи лет сложных взаимодействий, случайных изменений и приспособлений. Даже в основе таких искусственных языков, как эсперанто, лежит реальный язык. Однако компьютерные языки высокого уровня — результат более целенаправленной работы. Задача изобре- тения языка программирования интересна для некоторых людей, поскольку язык определяет то, как человек передает инструкции компьютеру. По оцен- кам, сделанным в 1993 году, с начала 1950-х были изобретены и внедрены бо- лее тысячи языков высокого уровня.
Конечно, недостаточно просто создать высокоуровневый язык (что под- разумевает разработку синтаксиса, то есть набора правил для составления вы- ражений). Вы обязательно должны написать компилятор — программу, кото- рая преобразует инструкции вашего высокоуровневого языка в машинный код.
Подобно ассемблеру, компилятор должен прочитать файл с исходным кодом символ за символом и разбить его на короткие слова, символы и цифры. Тем не менее компилятор намного сложнее, чем ассемблер. Относительная про- стота последнего обусловлена взаимно-однозначным соответствием между инструкциями на языке ассемблера и машинным кодом. Компилятору обыч- но приходится преобразовывать одну инструкцию, написанную на языке вы- сокого уровня, во множество машинных. Компиляторы писать нелегко, о чем свидетельствуют множество книг, посвященных их разработке.

408
Код
Языки высокого уровня имеют свои преимущества и недостатки. Основ- ная ценность в том, что их обычно легче изучать и использовать, чем языки ассемблера. Программы, написанные на языках высокого уровня, часто по- лучаются более понятными и краткими. Кроме того, такие языки в основном являются переносимыми, то есть не зависят от конкретного процессора, что позволяет программисту не учитывать базовую структуру компьютера, где бу- дет работать программа. Разумеется, если хотите запустить программу более чем на одном процессоре, вам потребуются компиляторы, которые генериру- ют машинный код для этих процессоров. Исполняемые файлы по-прежнему будут специфическими для каждого из них.
Однако код, написанный хорошим ассемблерным программистом, почти всегда превосходит код, созданный компилятором. Это означает, что испол- няемый файл, получившийся из программы, написанной на языке высокого уровня, будет более объемным и медленным по сравнению с функционально идентичной программой, написанной на языке ассемблера. (В последние годы это стало менее очевидным в связи с усложнением микропроцессоров и усо- вершенствованием компиляторов в плане оптимизации кода.)
Несмотря на то что язык высокого уровня может упростить работу с про- цессором, он не сделает его более мощным. С помощью ассемблера вы можете получить доступ ко всем функциям процессора. Поскольку высокоуровневый язык необходимо преобразовывать в машинный код, он может только сокра- тить возможности процессора. Действительно, если высокоуровневый язык является переносимым, он не может использовать функции, характерные для определенных процессоров.
Например, многие процессоры предусматривают команды побитового сдвига. Как вы помните, эти команды сдвигают содержащиеся в аккумулято- ре биты вправо или влево. Однако таких команд практически не существует ни в одном высокоуровневом языке программирования *. Если перед вами стоит задача, требующая использования побитового сдвига, придется имитировать его путем умножения или деления на 2. (Нельзя сказать, что это плохо: многие современные компиляторы используют команды побитового сдвига процес- сора для умножения или деления на степень двойки.) Кроме того, во многих языках не предусмотрены и булевы операции над битами **.
На заре эры домашних компьютеров большинство прикладных программ были написаны на ассемблере. Однако в наши дни этот язык используется редко
* На сегодняшний день такие команды есть и в C++, и в C#, и в Python. Как правило, сдвиг влево и вправо задается конструкциями вида x<>n соответственно, при этом x задает число, а n — количество разрядов, на которые нужно произвести сдвиг x. Прим. науч. ред.
** Тем не менее в наиболее распространенных языках высокого уровня такие операции имеются.

Глава 24. Языки высокого и низкого уровня
409
и только для решения особых задач *. По мере добавления в процессоры аппа- ратного обеспечения для конвейеризации — одновременного прогрессивного выполнения нескольких команд — язык ассемблера постоянно усложнялся.
В то же время компиляторы становились все более интеллектуальными. Уве- личение емкости диска и оперативной памяти современных компьютеров так- же сыграло свою роль: программистам больше не нужно создавать код, тре- бующий небольшого объема памяти и умещающийся на небольшой дискете.
Несмотря на то что разработчики многих ранних компьютеров пыта- лись формулировать для них задачи, используя алгебраическую форму запи- си, первым настоящим рабочим компилятором считается A-0 для компьютера
UNIVAC, созданный в 1952 году Грейс Мюррей Хоппер (1906–1992) в корпо- рации Remington Rand. Доктор Хоппер пришла в компьютерную индустрию в 1944 году, когда присоединилась к команде Говарда Эйкена для работы над компьютером «Марк I». В свои восемьдесят с лишним лет она продолжала работать в этой сфере, занимаясь связями с общественностью в корпорации
Digital Equipment Corporation (DEC).
Самый старый из используемых сегодня языков высокого уровня —
ФОРТРАН (FORTRAN) (хотя за прошедшие годы он был многократно пере- смотрен). Названия многих языков программирования пишутся прописными буквами, потому что являются акронимами. Название FORTRAN образовано от слов FORmula и TRANslation («трансляция формул»). Он был разработан в IBM для компьютеров серии 704 в середине 1950-х годов. На протяжении многих лет именно ФОРТРАНом пользовались ученые и инженеры. Он пред- усматривает обширную поддержку операций с плавающей точкой и работу с комплексными числами (которые, как я объяснил в предыдущей главе, пред- ставляют собой комбинации действительных и мнимых чисел).
У каждого языка программирования существуют сторонники и против- ники, которые могут горячо отстаивать свои предпочтения. В попытке занять нейтральную позицию при описании следующих концепций программиро- вания я выбрал язык, который почти никто уже не использует, — АЛГОЛ
(ALGOL — ALGOrithmic Language, алгоритмический язык; так же называется вторая по яркости звезда в созвездии Персея).
АЛГОЛ подходит для исследования природы высокоуровневых языков про- граммирования, поскольку во многих отношениях это прямой предок многих популярных языков общего назначения, разработанных за последние 40 лет.
* Он также используется в ходе обучения программистов по вопросам, связанным с устройством и адресацией памяти персонального компьютера: на простых командах легче вникнуть в этот аспект. Прим. науч. ред.

410
Код
Даже сегодня люди называют некоторые языки программирования языками типа АЛГОЛ.
Первая версия этого языка, АЛГОЛ 58, была разработана международным комитетом программистов в 1957 и 1958 годах. Два года спустя, в 1960-м, язык был доработан, а его пересмотренная версия получила название АЛГОЛ 60.
Со временем появился АЛГОЛ 68, однако в этой главе я буду обращаться к вер- сии, описанной в документе «Переработанное описание алгоритмического языка
АЛГОЛ 60», который был завершен в 1962 году и впервые опубликован в 1963-м.
Давайте напишем краткий код на языке АЛГОЛ. Предположим, у нас есть компилятор под названием ALGOL.COM, который работает под управлени- ем операционной системы CP/M или MS-DOS. Наша первая программа, на- писанная на языке АЛГОЛ, — текстовый файл с именем FIRST.ALG. Обратите внимание на тип файла ALG.
Программа на этом языке должна быть заключена между словами begin и end. Отобразим следующую текстовую строку.
begin print ('This is my fist ALGOL program!');
ende
Вы можете запустить компилятор, указав на программу FIRST.ALG, сле- дующим образом.
ALGOL FIRST.ALG
Скорее всего, в ответ компилятор выведет что-то вроде этого.
Line 3: Unrecognized keyword 'ende'
К орфографическим ошибкам компилятор относится строже, чем придир- чивый преподаватель. Я допустил ошибку в слове end, когда писал код, поэто- му компилятор сообщил мне о наличии синтаксической ошибки. Вмес то ende он ожидал встретить ключевое слово, которое способен распознать.
После исправления ошибки вы можете снова запустить компилятор. Ино- гда он может создать исполняемый файл напрямую (с именем FIRST.COM или FIRST.EXE в MS-DOS); иногда вам нужно выполнить еще одно действие. В лю- бом случае, вскоре вы сможете запустить программу FIRST из командной строки.
FIRST

Глава 24. Языки высокого и низкого уровня
411
Программа FIRST выведет на экран следующую строку.
This is my fist ALGOL program!
Ой! Еще одна орфографическая ошибка (в слове first («первая») пропу- щена буква r). Компилятор не в состоянии обнаружить эту ошибку, поэтому она называется ошибкой времени выполнения — проявляется только при за- пуске программы.
Вероятно, вы уже поняли, что оператор print в нашей первой программе на языке АЛГОЛ отображает что-то на экране, в данном случае строку текс- та (эквивалент программы, написанной на языке ассемблера CP/M). На самом деле оператор print — это не часть официальной спецификации языка АЛГОЛ, но я предполагаю, что конкретный используемый нами компилятор предусма- тривает такую функцию, иногда называемую встроенной. Большинство опера- торов АЛГОЛ (не считая begin и end) должны сопровождаться точкой с запя- той. Отступ для оператора print не обязателен, однако он часто применяется, чтобы сделать структуру программы более четкой.
Предположим, что вы хотите написать программу, которая перемножает два числа. В каждом языке программирования существует понятие перемен-
ной. Именем переменной в программе может быть буква, короткая последова- тельность букв или даже короткое слово. Переменная соответствует области памяти, однако в программе для обращения к ней используется имя, а не ад- рес памяти в числовом выражении. В этой программе задействованы три пе- ременных с именами a, b и c.
begin real a, b, c;
a:= 535.43;
b:= 289.771;
c:= a
×
b;
print ('Произведение ', a, ' и ', b, ' равно ', c);
end
Оператор real необходим для объявления переменных в программе. В дан- ном случае переменные называются a, b и c и представляют собой действи- тельные числа или числа с плавающей точкой. (Для объявления целочислен- ных переменных в языке АЛГОЛ есть ключевое слово integer.) Как правило,

412
Код языки программирования требуют того, чтобы имена переменных начинались с буквы. Еще они могут содержать числа, но в переменных не должны упо- требляться пробелы и бóльшая часть других символов. Часто компиляторы ограничивают длину имени переменной. В приведенном в этой главе примере я использую просто буквы.
Если наш компилятор АЛГОЛ поддерживает стандарт IEEE для представ- ления чисел с плавающей точкой, то для хранения каждой из трех переменных нужны четыре байта памяти (для чисел одинарной точности) или восемь байт памяти (для чисел двойной точности).
Следующие три выражения — операторы присваивания. В языке АЛГОЛ такой оператор легко узнается по двоеточию и знаку равенства. (В большин- стве компьютерных языков в качестве оператора присваивания используется только знак равенства.) В левой части находится переменная, в правой — вы- ражение. Переменной присваивается число, полученное в результате вычис- ления. Первые два оператора присваивания указывают, что переменным a и b назначаются конкретные значения. Третий оператор присваивает переменной
с значение произведения переменных a и b.
В настоящее время использование всем известного символа умножения «×» в языках программирования обычно не допускается, поскольку он отсутствует в наборах символов ASCII и EBCDIC. В большинстве языков операция умно- жения обозначается звездочкой «*». Хотя в АЛГОЛе для деления используется косая черта «/», этот язык также предполагает употребление символа «÷» для операции целочисленного деления, результат которого показывает, сколько раз делитель умещается в делимом. Кроме того, для возведения в степень в язы- ке АЛГОЛ используется символ «↑», который также не входит в набор ASCII.
Для отображения данных на экране применяется оператор print. Выводи- мые текст и переменные разделяются запятыми. Отображение символов ASCII, вероятно, не является для оператора print трудной задачей, однако в данном случае функция также должна преобразовать в символы ASCII числа с пла- вающей точкой.
Произведение 535.43 и 289.771 равно 155152.08653
Затем программа завершает работу и возвращает управление операци- онной системе.
Если хотите перемножить еще несколько чисел, нужно отредактировать программу, изменить числа, перекомпилировать и снова запустить ее. Вы мо- жете избежать этой многократной перекомпиляции, воспользовавшись другой встроенной функцией под названием read.

Глава 24. Языки высокого и низкого уровня
413
begin real a, b, c;
print ('Введите первое число: ');
read (a);
print ('Введите второе число: ');
read (b);
c:= a
×
b;
print ('Произведение ', a, ' и ', b, ' равно ', c);
end
Оператор read считывает символы ASCII, которые вы вводите с клавиа- туры, и преобразует их в числа с плавающей точкой.
В языках высокого уровня важной конструкцией является цикл, позво- ляющий написать программу, выполняющую одни и те же действия с разными значениями переменной. Предположим, вы хотите, чтобы программа вычис- ляла кубы чисел 3, 5, 7 и 9. Это можно сделать таким образом.
begin real a, b;
for a:= 3, 5, 7, 9 do begin b:= a
×
a
×
a;
print ('Куб числа ', a, ' равен ', b);
end end
Оператор for сначала присваивает переменной a значение 3, а затем вы- полняет команду, следующую за ключевым словом do. Если команд, которые требуется выполнить, несколько (как в приведенном примере), то между клю- чевыми словами begin и end необходимо задействовать несколько операторов.
Эти два ключевых слова определяют блок операторов. Затем оператор for вы- полняет те же команды для переменной a, которой присвоены значения 5, 7 и 9.
Вот еще одна версия инструкции for, которая вычисляет кубы нечетных чисел от 3 до 99.

414
Код begin real a, b;
for a:= 3 step 2 until 99 do begin b:= a
×
a
×
a;
print ('Куб числа ', a, ' равен ', b);
end end
Сначала оператор for присваивает переменной a значение 3 и выполняет блок операторов, следующий за инструкцией for. Затем значение переменной a увеличивается на величину, следующую за ключевым словом step, то есть на 2.
Новое значение переменной a (5) используется для выполнения блока опера- торов. Значение переменной a будет и дальше увеличиваться на 2. Когда оно превысит 99, цикл for завершится.
Как правило, языки программирования имеют строгий синтаксис. Напри- мер, в АЛГОЛе 60 за ключевым словом for может следовать только имя перемен- ной. Однако в английском после слова for могут располагаться всевозможные слова. Несмотря на сложность написания компиляторов, создать их намного проще, чем программы для интерпретации человеческой речи.
Еще одна важная особенность большинства языков программирова- ния — условные структуры, позволяющие выполнить некоторое действие только в случае истинности конкретного условия. Вот пример использова- ния встроенной функции языка АЛГОЛ sqrt, которая вычисляет квадратный корень. Функция sqrt не работает с отрицательными числами, и данная про- грамма это учитывает.
begin real a, b;
print ('Введите число: ');
read (a);
if a < 0 then print('Извините, введено отрицательное число.');
else begin b:= sqrt(a);

Глава 24. Языки высокого и низкого уровня
415
print ('Квадратный корень из ', a, ' равен ', b);
end end
Левая угловая скобка (<) — знак «меньше». Если пользователь введет отрицательное число, то будет выполнен первый оператор print. При вводе положительного числа будет запущен блок операторов, содержащий дру- гой print.
До сих пор каждая из переменных в приведенных выше программах хра- нила лишь одно значение. Часто бывает удобно хранить в одной переменной несколько значений. В этом случае переменная называется массивом. В про- грамме на языке АЛГОЛ массив объявляется следующим образом.
real array a[1:100];
В данном случае мы указали, что хотим использовать эту переменную для хранения 100 различных чисел с плавающей точкой, называемых элементами
массива. Для обращения к первому элементу массива используется выраже- ние a[1], ко второму — a[2], к последнему — a[100]. Число в квадратных скоб- ках называется индексом элемента массива.
Эта программа вычисляет квадратные корни всех чисел от 1 до 100 и со- храняет их в массиве. Затем она выводит их на экран.
begin real array a[1:100];
integer i;
for i:= 1 step 1 until 100 do a[i]:= sqrt(i);
for i:= 1 step 1 until 100 do print ('Квадратный корень из ', i, ' равен ', a[i]);
end
Кроме того, программа показывает целочисленную переменную с именем
i, которое является традиционным, поскольку это первая буква в слове integer
(«целое»). В первом цикле for каждому элементу массива присваивается зна- чение квад ратного корня из его индекса, во втором элементы массива выво- дятся на экран.

416
Код
Помимо типов real и integer, АЛГОЛ предусматривает тип переменных
Boolean. (Помните Джорджа Буля из главы 10?) Такая переменная может иметь только два возможных значения: true и false. Буду использовать массив бу- левых переменных (и почти все, что мы изучили до сих пор) в последней программе этой главы, в которой реализуется известный алгоритм нахожде- ния простых чисел под названием «Решето Эратосфена». Эратосфен (около
276–196 до н. э.) служил главным библиотекарем в легендарной Александрий- ской библиотеке и сегодня широко известен благодаря вычислению точной длины окружности Земли.
Простыми являются целые числа, которые без остатка делятся только сами на себя и на 1. Первое простое число — 2 (единственное четное простое число), затем следуют 3, 5, 7, 11, 13, 17 и т. д.
Первый шаг в алгоритме Эратосфена — составление списка положитель- ных целых чисел начиная с 2. Поскольку 2 — простое число, необходимо уда- лить все числа, кратные двум (все четные числа, кроме 2), а так как 3 — простое число, следует исключить все числа, кратные 3. Мы уже знаем, что 4 не явля- ется простым числом, потому что оно было вычеркнуто. Следующее простое число — 5, значит, нужно удалить все числа, кратные 5. Продолжая действо- вать таким способом, вы будете находить новые простые числа.
В программе для нахождения простых чисел от 2 до 10 000, написанной на языке АЛГОЛ, этот алгоритм можно реализовать, объявив булев массив с индексами от 2 до 10 000.
begin
Boolean array a[2:10000];
integer i, j;
for i:= 2 step 1 until 10000 do a[i]:= true;
for i:= 2 step 1 until 100 do if a[i] then for j:= 2 step 1 until 10000 ÷ i do a[i
×
j]:= false;
for i:= 2 step 1 until 10000 do if a[i] then print (i);
end

Глава 24. Языки высокого и низкого уровня
417
Первый цикл for присваивает всем элементам булева массива значение true.
Таким образом, предполагается, что все числа, находящиеся в начале програм- мы, простые. Второй цикл проверяет числа от 1 до 100 (квадратный корень из 10 000). Если число простое (a[i] имеет значение true), то вложенный цикл for задает значение false всем числам, кратным ему. Последний цикл for вы- водит на экран все простые числа, то есть значения i, при которых a[i] — true.
Иногда люди спорят, является ли программирование искусством или наукой.
С одной стороны, существуют учебные программы в области компьютерных
наук, а с другой — есть такие знаменитые книги, как «Искусство программиро-
вания» Дональда Кнута. Как писал физик Ричард Фейнман: «Информатика ско- рее подобна инженерному делу: нужно просто заставить что-то делать что-то».
Если попросите 100 разных людей написать программу, которая выводит на экран простые числа, получите 100 различных решений. Даже те програм- мисты, которые используют алгоритм Эратосфена, реализуют его не так, как это сделал я. Если бы программирование действительно было наукой, не суще- ствовало бы такого множества возможных решений, а неправильные заключе- ния были бы более очевидными. Иногда стоящая перед программистом проб- лема вызывает у него озарения и творческие вспышки: в этом и заключается
«искусство». И все же программирование в основном сводится к проектиро- ванию и строительству, подобно процессу возведения моста.
Многие из первых программистов были учеными и инженерами, способ- ными формулировать задачи в виде математических алгоритмов, что было необходимо при использовании языков ФОРТРАН и АЛГОЛ. Тем не менее на протяжении истории развития этой сферы осуществлялись попытки со- здания языков для более широкого круга пользователей.
Одним из первых удачных языков, предназначенных для бизнеса, был
КОБОЛ (COBOL, COmmon Business Oriented Language, «универсальный язык, ориентированный на коммерческие задачи»), который широко распространен и сегодня. Разработка языка КОБОЛ была начата в 1959 году комитетом, со- стоявшим из представителей промышленности и министерства обороны США, и большое влияние на него оказали ранние компиляторы Грейс Хоппер. Язык
КОБОЛ был создан так, чтобы менеджеры, не занимавшиеся написанием кода, могли по крайней мере прочитать его и проверить, что он делает именно то, что от него ожидается. (Однако в реальной жизни так бывает нечасто.)
В языке КОБОЛ предусмотрены обширные возможности для чтения запи-
сей и создания отчетов. Запись — это набор взаимосвязанных данных. Напри- мер, страховая компания может хранить большие файлы с информацией обо всех проданных ею полисах. Каждому полису будет соответствовать отдельная пометка, включающая имя клиента, дату рождения и прочие данные. Многие

418
Код ранние программы на языке КОБОЛ были написаны для работы с 80-столб- цовыми записями, хранящимися на перфокартах IBM. Для экономии места на этих картах годы часто кодировались с помощью двух цифр вместо четы- рех, что в дальнейшем послужило причиной широко распространенной «про- блемы 2000 года».
В середине 1960-х IBM в связи со своим проектом System/360 разработа- ла язык под названием ПЛ/1 (PL/I, Programming Language I, «язык програм- мирования номер один»). Цель его создания — объединить блочную струк- туру АЛГОЛа, математические функции ФОРТРАНа и средства КОБОЛа для работы с записями. Однако этот язык так никогда и не достиг популярности
ФОРТРАНа и КОБОЛа.
Несмотря на существование версий ФОРТРАНа, АЛГОЛа, КОБОЛа и ПЛ/1 для домашних компьютеров, главным языком для них стал БЕЙСИК.
Язык БЕЙСИК (BASIC, Beginner’s All-purpose Symbolic Instruction Code,
«универсальный код символических инструкций для начинающих») был при- думан в 1964 году профессорами математического факультета Дартмутского университета Джоном Кемени и Томасом Курцем для работы с университетской системой распределения времени. Большинство студентов Дартмута не были математиками или инженерами, поэтому никто не ожидал, что они будут во- зиться с перфокартами и сложным синтаксисом. Вместо этого студент, сидя перед терминалом, мог написать простую программу, просто набрав команды, которым предшествовали числа, обозначающие их порядок. Команды, не со- провождающиеся числами, предназначались для системы: SAVE (сохранить на диске), LIST (отобразить строки по порядку), RUN (скомпилировать и за- пустить). Вот первая программа на языке БЕЙСИК, опубликованная в первом печатном руководстве.
10 LET X = (7 + 8) / 3 20 PRINT X
30 END
В отличие от АЛГОЛа язык БЕЙСИК не требовал указывать тип перемен- ной. Большинство чисел сохранялись в виде значений с плавающей точкой.
Многие последующие реализации языка БЕЙСИК имели форму интерпре-
таторов, а не компиляторов. Как я объяснял, компилятор читает файл с исход- ным кодом и создает исполняемый файл. Интерпретатор читает исходный код и сразу выполняет его, не создавая исполняемого файла. По сравнению с ком- пиляторами интерпретаторы легче писать, однако интерпретируемая програм- ма выполняется медленнее, чем скомпилированная. На домашних компьютерах

Глава 24. Языки высокого и низкого уровня
419
язык БЕЙСИК начал использоваться в 1975 году, когда два приятеля, Билл Гейтс
(род. 1955) и Пол Аллен (род. 1953), написали интерпретатор языка БЕЙСИК для микрокомпьютера Altair 8800 и основали корпорацию Microsoft.
Язык программирования Паскаль (Pascal), который унаследовал бóльшую часть своей структуры от АЛГОЛа, но включал функции обработки запи- сей КОБОЛа, был разработан в конце 1960-х годов швейцарским профес- сором информатики Никлаусом Виртом (род. 1934). Язык Паскаль был до- вольно популярен среди программистов, работавших на компьютере IBM
PC, но лишь в специфической реализации — Turbo Pascal, выпущенной компанией Borland International в 1983 году. Реализация Turbo Pascal, на- писанная датским студентом Андерсом Хейлсбергом (род. 1960), представ- ляла версию, дополненную интегрированной средой разработки (Integrated
Development Environment, IDE). Текстовый редактор и компилятор были объединены в одну программу, что значительно ускорило процесс програм- мирования. Интегрированные среды разработки широко использовались на мейнфреймах, а среда Turbo Pascal ознаменовала начало их применения на небольших компьютерах.
Паскаль также серьезно повлиял на язык Ада (Ada), созданный для минис- терства обороны США. Язык был назван в честь Августы Ады Байрон, о ко- торой я упоминал в главе 18, когда описывал аналитическую машину Чарльза
Бэббиджа.
Затем появился очень популярный язык C (Си), созданием которого в пе- риод c 1969 по 1973 год в основном занимался Деннис Ритчи из Bell Telephone
Laboratories. Часто спрашивают, почему язык называется С. Все просто: он был написан на основе более раннего языка B — упрощенной версии BCPL
(Basic CPL), предшественником которого являлся CPL (Combined Programming
Language, «комбинированный язык программирования»).
В главе 22 я упоминал, что операционная система UNIX задумывалась как переносимая. Большинство операционных систем в то время были разра- ботаны на языке ассемблера для конкретного процессора. В 1973 году UNIX была написана (вернее, переписана) на языке C, и с тех пор эта система и этот язык тесно связаны.
Как правило, в языке C операторы записываются весьма кратко. Напри- мер, вместо ключевых слов begin и end, используемых в АЛГОЛе и Паскале для разграничения блоков, в C применяются фигурные скобки { и }. Вот еще один пример. Программисту часто требуется увеличить значение переменной на некую постоянную величину.
i = i + 5;

Код
В C вы можете сократить этот оператор.
i += 5;
Если вам нужно увеличить значение переменной на 1 (инкрементировать ее), данный оператор можно записать еще короче.
i++;
Такой оператор на 16- или 32-разрядном микропроцессоре может выпол- няться с помощью одной машинной инструкции.
Ранее я упоминал, что бóльшая часть языков высокого уровня не пред- полагает операций побитового сдвига или булевых операций над битами, ко- торые являются частью функционала многих процессоров. Язык C — исклю- чение из правила. Кроме того, важная особенность этого языка — поддержка
указателей, которые, по сути, являются числовыми представлениями адресов памяти. Поскольку язык C поддерживает операции, реализующие многие ин- струкции процессора, он иногда классифицируется как язык ассемблера высо-
кого уровня. По сравнению с любым другим языком типа АЛГОЛ язык C точ- нее всего имитирует общие наборы команд процессора.
Тем не менее все языки типа АЛГОЛ, то есть наиболее распространенные языки программирования, разрабатывались для компьютеров с архитектурой фон Неймана. Выйти за рамки соответствующего образа мышления непро- сто, а заставить других людей использовать такой язык еще сложнее. Один из подобных языков — LISP (List Processing, «обработка списков»), создан- ный Джоном Маккарти в конце 1950-х годов и пригодный для работы в обла- сти искусственного интеллекта. Другой язык, столь же необычный, как LISP, но совершенно на него не похожий, — APL (A Programming Language, «язык программирования») — был придуман в конце 1950-х Кеннетом Айверсоном.
В нем используется набор специальных символов, которые выполняют опера- ции одновременно над целыми массивами чисел.
Несмотря на то что языки типа АЛГОЛ продолжают доминировать, в по- следние годы они были несколько усовершенствованы, что привело к появле- нию так называемых объектно-ориентированных языков, удобных при работе с графическими операционными системами, о которых я расскажу в следую- щей главе.

421
Глава 25
Графическая революция
В выпуске журнала Life от 10 сентября 1945 года читатели обнаружили при- вычную чересполосицу статей и фотографий: заметки об окончании Второй мировой войны, рассказ о жизни танцора Вацлава Нижинского в Вене, фото- репортаж о профсоюзе рабочих автомобильной промышленности. Однако тот выпуск содержал и нечто неожиданное: провокационную статью Вэни- вара Буша (1890–1974) о будущем научных исследований. С 1927 по 1931 год, работая профессором Массачусетского технологического института, Ван Буш
(как его называли) внес свой вклад в историю вычислительной техники, скон- струировав один из самых значимых аналоговых компьютеров — дифферен- циальный анализатор. В 1945 году, когда вышел его артикул, Буш возглавлял управление научных исследований и разработок, которое во время войны ко- ординировало научные исследования США, включая Манхэттенский проект.
Статья Буша «Как мы можем мыслить» (As We May Think), несколько со- кращенная с момента своего первого появления в The Atlantic Monthly двумя месяцами ранее, содержала описания некоторых будущих гипотетических изоб- ретений, предназначенных для ученых и исследователей, которым приходит- ся иметь дело с постоянно растущим объемом специализированных изданий.
Решение этой проблемы Буш видел в использовании микропленки и приду- мал устройство под названием Memex для хранения книг, статей, звукозаписей и изображений. Это устройство также должно было позволять пользователю устанавливать тематические связи между этими фрагментами информации по аналогии с тем, как человеческий разум формирует ассоциации. Буш даже представлял новую группу профессионалов, которые должны были занимать- ся созданием этих ассоциативных связей.
Несмотря на то что на протяжении всего XX века о чудесах будущего было написано множество работ, статья Буша выделялась. В ней речь не шла о бы- товых устройствах для облегчения домашнего труда, о футуристических видах транспорта или о роботах. Она была посвящена информации и тому, как новые технологии могут помочь справиться с увеличением ее объема.

422
Код
За десятилетия, прошедшие с момента создания первых релейных кальку- ляторов, компьютеры стали меньше, быстрее и дешевле. Эта тенденция изме- нила саму природу вычислений. Чем более дешевыми становятся компьютеры, тем больше людей могут их себе позволить. По мере уменьшения их размера и повышения роста быстродействия программное обеспечение оказывается все более изощренным, благодаря чему расширяется круг задач, решаемых этими машинами.
Дополнительная мощность и скорость могут использоваться для совер- шенствования самой важной части компьютерной системы — пользователь-
ского интерфейса, который обеспечивает человеко-машинные взаимодействия.
Люди и компьютеры — разные создания. К сожалению, людей гораздо легче убедить в необходимости приспосабливаться к особенностям вычислительных машин, чем наоборот.
Поначалу цифровые компьютеры не были интерактивными. Для про- граммирования одних использовались переключатели и кабели, для програм- мирования других — перфорированная лента или пленка. В период с 1950-х до начала 1970-х годов компьютеры эволюционировали до такой степени, что нормой стала пакетная обработка с минимальным вмешательством операто- ра: программы и данные сохранялись на перфокартах, которые затем считы- вались в память. Программа анализировала полученную информацию, делала некоторые выводы и печатала результаты на бумаге.
Самые ранние интерактивные компьютеры работали с помощью теле- тайпных аппаратов. Такие установки, как система распределения времени
Дартмутского университета (начала 1960-х), описанная в предыдущей главе, позволяли одновременно применять несколько телетайпов. Пользователь на- бирал на телетайпе строку текста, на которую компьютер отвечал одной или несколькими строками, отпечатываемыми на рулоне бумаги. Обмен информа- цией между телетайпом и компьютером осуществлялся с помощью потоков
ASCII-кодов (хотя могла быть и другая кодировка), включающих, помимо ко- дов символов, простые управляющие коды, например для возврата каретки и перевода строки.
Однако электронно-лучевая трубка, которая получила более широкое рас- пространение в 1970-е, не имела таких ограничений. Программное обеспече- ние могло использовать экран более гибко — в качестве двумерной платфор- мы для отображения информации. Тем не менее разработчики большинства программ для небольших компьютеров, вероятно привыкшие к определенной логике отображения информации, продолжали рассматривать экран в каче- стве «стеклянного телетайпа». Создаваемые ими программы выводили дан- ные на экран построчно сверху вниз, а по достижении нижней границы его

Глава 25. Графическая революция
423
содержимое необходимо было прокручивать вверх. Именно таким спосо- бом дисплей использовали все программы CP/M и большинство программ
MS-DOS, а операционная система UNIX до сих пор с гордостью поддержи- вает эту традицию.
Интересно, что набор символов ASCII не является столь уж неуместным при работе с ЭЛТ. Изначально эта кодировка включала код 1Bh (Escape), спе- циально предназначенный для расширения набора отображаемых символов.
В 1979 году Американский национальный институт стандартов опубликовал стандарт «Дополнительные управляющие символы для использования с ASCII».
Его цель — «удовлетворение ожидаемых потребностей в контроле над вводом и выводом на двумерных устройствах отображения информации, включая ин- терактивные терминалы с электронно-лучевой трубкой и принтеры».
Код 1Bh занимает всего один байт, поэтому может иметь только одно зна- чение. Код Escape предваряет последовательности символов, которые выполня- ют определенные функции. Например, последовательность, состоящая из кода
Escape, за которой следуют символы [2J, стирает все содержимое экрана и пе- ремещает курсор в верхний левый угол.
1Bh 5Bh 32h 4Ah
Телетайпный аппарат не позволяет сделать ничего подобного. Последо- вательность, состоящая из кода Escape, за которой следуют символы [5; 29H, перемещает курсор на пятую строку 29-го столбца.
1Bh 5Bh 35h 3Bh 32h 39h 48h
Комбинация ЭЛТ и клавиатуры, которая реагирует на ASCII-коды (воз- можно, и на Escape-последовательности), поступающие с удаленного компью- тера, иногда называется «немым» терминалом. Такой терминал работает бы- стрее и гибче по сравнению с телетайпными аппаратами, однако его скорости недостаточно для внедрения нового в пользовательском интерфейсе. Настоя- щие инновации были реализованы в 1970-х годах в небольших компьютерах, в которых, как и в гипотетическом компьютере из главы 21, видеопамять была частью адресного пространства микропроцессора.
Первым признаком того, что домашние компьютеры будут сильно отли- чаться от своих более крупных и дорогих предшественников, было, вероятно, приложение VisiCalc, разработанное Даниэлем Бриклином (род. 1951) и Бобом
Фрэнкстоном (род. 1949) в 1979 году для компьютера Apple II. Приложение
VisiCalc выводило на экран двумерное изображение электронной таблицы.

424
Код
До этого электронная таблица представляла собой разлинованный лист бума- ги, обычно используемый для выполнения вычислений. Приложение VisiCalc заменило бумагу экраном, позволив пользователю перемещаться по электрон- ной таблице, вводить числа и формулы, а также пересчитывать результаты по- сле внесения изменений.
Удивительным в VisiCalc было то, что это приложение нельзя было вос-
создать на больших компьютерах. Подобным программам необходимо очень быстро обновлять экран. По этой причине приложение VisiCalc записывало данные непосредственно в видеопамять компьютера Apple II, являющуюся час тью адресного пространства микропроцессора. Интерфейс между большим компьютером с системой распределения времени и «немым» терминалом ра- ботал недостаточно быстро для такой электронной таблицы.
Чем быстрее компьютер может реагировать на сигналы клавиатуры и об- новлять видеоизображение, тем плотнее его взаимодействие с пользователем.
Бóльшая часть программ, написанных в течение первого десятилетия пос- ле выхода IBM PC (в 1980-х), предполагала запись данных непосредственно в видеопамять. Поскольку стандартов аппаратного обеспечения, введенных компанией IBM, придерживались и другие производители компьютеров, раз- работчики программ могли обойти операционную систему и напрямую за- действовать аппаратное обеспечение, не опасаясь, что на некоторых компью- терах их программы будут работать неправильно (вообще не будут работать).
Если бы во всех клонах IBM PC применялись разные аппаратные интерфейсы для видеодисплеев, то программистам было бы сложно учесть особенности различных конструкций.
В большинстве ранних приложений для IBM PC на экране отображался только текст без графики, что обеспечивало максимальную скорость работы.
Когда видеодисплей устроен так, как описано в главе 21, программа может отоб разить на экране конкретный символ, просто записав в память соответ- ствующий ASCII-код. Программе, использующей графический видеодисплей, обычно требуется записать в память восемь или более байтов для вывода на эк- ран изображения текстового символа.
Переход от отображения текста к отображению графики стал чрезвы- чайно важным шагом в эволюции. Однако процесс развития компьютер- ного оборудования и программного обеспечения, работающего не только с текстом, но и с графическими изображениями, происходил очень медлен- но. Еще в 1945 году Джон фон Нейман рассматривал возможность выво- да графических изображений на дисплей, основанный на принципе работы осциллографа. Однако только в начале 1950-х годов компьютерная графи- ка стала реальностью, когда в Массачусетском технологическом институте

Глава 25. Графическая революция
425
(при содействии компании IBM) была учреждена Лаборатория Линкольна, перед которой стояла задача разработать компьютер для системы ПВО ВВС
США. Этот проект под названием SAGE (Semi-Automatic Ground Environment,
« полуавтоматическая наземная среда») подразумевал использование графиче- ских экранов, с помощью которых операторы могли бы анализировать боль- шие объемы информации.
Первые видеодисплеи, использованные в таких системах, как SAGE, не были похожи на мониторы современных персональных компьютеров. Сегодня ПК оснащены так называемыми растровыми дисплеями. Как и в телевизоре, изоб- ражение на устаревших ЭЛТ-мониторах состоит из серии горизонтальных ра- стровых линий, рисуемых лучом электронной пушки, который быстро пробегает по экрану. Такой экран можно представить в виде большого прямоугольного массива точек, называемых пикселами. Для хранения видеоизображения в па- мяти компьютера выделяется целая область, в которой один или несколько би- тов соответствует пикселу на экране. Значения этих битов определяют степень светимости и цвет пикселов.
Например, разрешение большинства современных компьютерных дисплеев составляет как минимум 640 пикселов по горизонтали и 480 пикселов по вер- тикали. Их общее количество соответствует произведению этих двух чисел:
307 200. Если для каждого пиксела выделяется только один бит памяти, то его цвет ограничивается двумя вариантами, обычно черным и белым. Например, значение 0 может указывать на черный пиксел, значение 1 — на белый. Такой видеодисплей требует 307 200 бит памяти, или 38 400 байт.
Чтобы расширить диапазон отображаемых цветов, необходимо выделить для каждого пиксела большее количество битов, а это увеличивает требова- ния видеоадаптера к памяти. Например, для кодирования оттенков серого под каждый пиксел можно предоставить один байт памяти. При таком подходе значение 00h будет соответствовать черному цвету, значение FFh — белому, а промежуточные — оттенкам серого.
Цветное изображение ЭЛТ создается с помощью трех электронных пу- шек, по одной для каждого из трех основных цветов: красного, зеленого и си- него. (В этом можно убедиться, рассмотрев цветной экран телевизора или компьютера через увеличительное стекло; в принтерах другой набор цветов.)
Сочетание красного и зеленого дает желтый цвет, красного и синего — ма- линовый, зеленого и синего — голубой, а комбинация всех трех основных цветов — белый.
Простейший цветной графический адаптер требует, чтобы на один пиксел приходилось три бита, по одному биту на каждый из основных цветов. Цвет пикселов может быть закодирован следующим образом.

426
Код
1   ...   20   21   22   23   24   25   26   27   28


написать администратору сайта