Саша Беспоясов
Это я.

Код. Тайный язык информатики. Часть 2

В первой части конспекта мы прочли главы 1–14. Научились строить электрические и логические цепи с лампочками. В конце первой части на их основе мы собрали цепь, которая считала от 0 до 256.

В этот раз мы прочитаем оставшиеся главы 15–25. В них мы соберём первую память, которая сможет хранить информацию, а потом подключим её к сумматору. Создадим первый процессор, на основе которого сделаем примитивный компьютер с вводом и выводом.

Глава 15. Байты и шестнадцатеричные числа

Байт — это (не открою Америки) 8 бит. Значения: от 00000000 до 11111111, 256 (28) вариантов, значения длинные, поэтому вместо двоичной записи можно использовать шестнадцатиричную.

0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F, 10, 11, 12, …

Тогда, например, 101101102 = B616.

Глава 16. Сборка памяти

Бумага для людей — это основная внешняя память:

Мы пишем, чтобы позже прочитать… Мы сохраняем информацию, чтобы позже использовать ее

D-триггер из прошлых глав, защёлка — это память размером в 1 бит. Мы можем переназвать выход Q «выводом данных», а вход Clk «записью». Из 8 таких защёлок, а также дешифратора 3 на 8 и селектора 8 на 1 мы получим память с произвольным доступом (random access memory, RAM):

Дешифратор с помощью адреса определяет, какой из входов под напряжением — в какую из защёлок идёт запись. Селектор определяет, сигнал с какого входа пойдёт на выход
Дешифратор с помощью адреса определяет, какой из входов под напряжением — в какую из защёлок идёт запись. Селектор определяет, сигнал с какого входа пойдёт на выход

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

При параллельном соединении двух RAM 8×1 получим RAM 8×2. При соединении с помощью селектора 2 на 1 и дешифратора 1 на 2 (как две отдельных «защёлки» внутри) — RAM 16×1.

Число значений в массиве RAM = 2Количество входов «Адрес»

Память с произвольным доступом называют энергозависимой, потому что

Для хранения информации ей требуется непрерывное энергоснабжение

Глава 17. Автоматизация

Построим наконец компьютер™. Будем предполагать, что под защёлками всегда имеются в виду защёлки со срабатыванием по фронту (когда Clk переходит из 0 в 1).

Подключим память RAM к сумматору с накоплением суммы.

Схема с памятью, сумматором с накоплением и пультом управления для ввода значений
Схема с памятью, сумматором с накоплением и пультом управления для ввода значений

Допустим, нужно найти три суммы. Представим в памяти слагаемые и адреса значений:

Массив RAM со значениями для суммирования
Массив RAM со значениями для суммирования

Для сложения сумматор теперь должен:

…Переписать [загрузить] содержимое ячейки памяти в аккумулятор. …Сложить байт из памяти с содержимым аккумулятора. …Сохранить в памяти сумму из аккумулятора. …Остановить работу сумматора

Для массива из рисунка выше:

  • загрузить значение из ячейки 0000h в аккумулятор;
  • сложить значение из ячейки 0001h с аккумулятором;
  • сложить значение из ячейки 0002h с аккумулятором;
  • сохранить значение из аккумулятора в ячейке 0003h;
  • загрузить значение из ячейки 0004h в аккумулятор;
  • сложить значение из ячейки 0005h с аккумулятором;
  • сохранить значение из аккумулятора в ячейке 0006h;
  • загрузить значение из ячейки 0000h в аккумулятор;
  • сложить значение из ячейки 0001h с аккумулятором;
  • сложить значение из ячейки 0002h с аккумулятором;
  • сохранить значение из аккумулятора в ячейке 0003h;
  • остановить работу

Чтобы этого добиться, каждое число в памяти сопроводим кодом нужного действия. Этим будет заниматься отдельный массив RAM («коды»), в который мы запишем нужные действия:

Массив RAM «коды» с записанными действиями для автоматического сумматора
Массив RAM «коды» с записанными действиями для автоматического сумматора

«Данные» и «коды» можно объединить в один массив, но чтобы было удобнее работать с адресами команд, стоит добавить команду «Перейти», которая укажет на адрес следующей команды для выполнения.

Также добавим флаг нуля (zero flag), выход которого равен 1, лишь если всего его входы равны нулям. С ним мы можем добавить ещё 4 команды. Итого сейчас есть коды команд:

Загрузить10hLOD
Сохранить11hSTO
Сложить20hADD
Вычесть21hSUB
Сложить с переносом22hADC
Вычесть с заимствованием23hSBB
Перейти30hJMP
Перейти, если 031hJZ
Перейти, если перенос32hJC
Перейти, если не 033hJNZ
Перейти, если не перенос34hJNC
ОстановитьFFhHLT

Этих команд хватит, чтобы перемножить два числа друг на друга:

Массив памяти с инструкциями к перемножению чисел
Массив памяти с инструкциями к перемножению чисел

Чтобы провести эти операции, потребуется схема:

Схема примитивного компьютера
Схема примитивного компьютера

возможность управляемых циклических процедур отличает компьютер от калькулятора

В схеме выше память — это 64-килобайтовый массив RAM. Ввод и вывод — переключатели и лампочки на пульте управления памятью. Всё остальное — 8-разрядный процессор (central processing unit, CPU).

С помощью мнемоник, команды можно записывать в виде:

Далее, так как:

При написании кодов численные значения адресов лучше не использовать, так как они могут измениться… Для обозначения ячеек памяти предпочтительнее пользоваться метками (labels)

Добавим метки, получим «ассемблер». Вот программа для умножения чисел:

Глава 18. От счетов к микросхемам

Реле для создания компьютеров не подходят, и в XX веке их заменили радиолампы:

Будучи механическими устройствами, действие которых основывалось на изгибании металлической пластины, после продолжительной работы они в самом прямом смысле слова ломались

Компьютер, который мы собрали ранее, — типичный пример архитектуры фон Неймана. Её недостаток в долгой загрузке из памяти.

В 1947 году появился полупроводниковый транзистор. Полупроводники — это элементы, проводимостью которых можно управлять (например, кремний). Если в полупроводнике есть избыточные электроны, это полупроводник n-типа, в обратном случае — p-типа.

Транзистор состоит из полупроводниковых элементов: коллектора, базы и эмиттера.

Небольшое напряжение на базе управляет гораздо большим током, проходящим из коллектора в эмиттер. Если на базе напряжения нет, транзистор практически закрывается
Небольшое напряжение на базе управляет гораздо большим током, проходящим из коллектора в эмиттер. Если на базе напряжения нет, транзистор практически закрывается

Транзисторы подходят для сборки логических вентилей:

Вентили AND и OR, собранные из транзисторов
Вентили AND и OR, собранные из транзисторов

В интегральных микросхемах умещаются сразу логические схемы. Например, в «счетверенной двухвходовой положительной схеме NAND» было 4 вентиля NAND:

Vcc — питающее напряжение, Gnd — земля
Vcc — питающее напряжение, Gnd — земля

Напряжения в диапазоне 0–0,8 В считаются логическим нулем, а напряжения от 2 до 5 В — логической единицей. …Так микросхемы защищаются от шума

Время установки — между изменением сигнала на входе и на выходе — измеряется в наносекундах.

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

Глава 19. Два классических микропроцессора

В этой главе автор в подробностях описывает принцип работы процессоров Intel 8080 и Motorolla 6800. Я приведу здесь лишь сокращённое описание 8080, там самая мякотка.

Начнём со схемы:

Назначение каждого из 40 выводов микропроцессора 8080
Назначение каждого из 40 выводов микропроцессора 8080

-5В, 5В и +12В — это питание с напряжением соответственно -5, 5 и +12 вольт, GND — земля. Направление стрелок на других выводах указывает направление сигнала, некоторые выводы работают в обе стороны.

Зачёркнутые нули — это синхронизирующие сигналы. От A0 до A15 — адресация памяти. С D0 до D7 — для данных. Остальное — управляющие сигналы.

У 8080 процессора 244 команды «как же я люблю их, вот они».

Кроме аккумулятора есть 6 регистров B, C, D, E, H, L. H и L — это High и Low. 8-битовый значения в регистрах HL рассматриваются как 16-битовая пара, где старший байт хранится в H, а младший в L. Регистры позволяют не обращаться постоянно к памяти, что ускоряет работу.

63 кода отведено под команд MOV — для перемещения содержимого из одного регистра в другой.

MVI — перемещает непосредственно байт данных в регистр или ячейку памяти, адрес которой записан в HL.

Для арифметических действий есть 32 команды. Сложение (ADD), сложение с переносом (ADC), вычитание (SUB), вычитание с заимствованием (SBB).

Есть также команды для логических операций. AND, OR и XOR выполняются побитово. CMP — сравнение:

CMA — дополняет содержимое аккумулятора до 1. DAA — для арифметических операций с десятичными числами в BCD (Binary-coded decimal). BCD — это когда 27h значит 27 в десятичной, а не 39.

Команды для увеличения и уменьшения значения регистров, аккумулятора или ячейки памяти на 1: INR, DCR. 4 команды для циклического сдвига: RLC, RRC, RAL, RAR. Команды для работы со стеком: PUSH, POP_.

CALL и RET — команды для создания подпрограмм. Команда CALL записывает в стек адрес команды, стоящей следом за командой CALL, а потом происходит переход в начало подпрограммы с меткой Multiply. По завершении выполняется команда RET, в результате чего в программный счетчик возвращается значение из стека.

(Кстати, именно в этой книге я-таки наконец полностью понял, что такое «стек вызова» и почему именно return стоит в конце функции.)

Команды IN и OUT — для общения с периферией.

Глава 20. ASCII — символы нашего времени

Американский стандартный код для обмена информацией (American Standard Code for Information Interchange, ASCII) — 7-битовая (технически) текстовая кодировка. Её коды принимают значения от 00h до 7Fh. Для хранения отдельных символов отводится 8 битов.

В ASCII содержатся знаки препинания, цифры, прописные и строчные буквы и управляющие символы. я отдельных символов отводится 8 битов. Коды строчных букв отличаются от соответствующих прописных на 20h, это позволяет относительно просто переводить строчные в прописные и обратно.

Для того, чтобы закодировать что-то кроме латиницы, появился Unicode. В ней символы занимают по 2 байта, первые 128 символов совпадают с ASCII.

Глава 21. Под шорох шин

Интегральные схемы, из которых состоит компьютер, монтируются на платах. Платы обмениваются информацией через шину. Сигналы на ней делятся на 4 категории:

  • Адресные — для адресации оперативной памяти или обращения к другим устройствам.
  • Вывода данных — для передачи данных в память и на другие устройства.
  • Ввода данных — генерируются различными устройствами компьютера и поступают в микропроцессор.
  • Управляющие — генерируются как микропроцессором, так и другими устройствами, которым нужно что-то сообщить процессору.

Шина во всеобщем пользовании может стать стандартом

Шины приходится модернизировать или заменять, когда микропроцессоры перерастают их либо по разрядности данных, либо по объему адресуемой памяти, либо по быстродействию

Ещё в этой главе есть рассказ о дискетах и катодно-лучевой трубке! 😃

Луч начинает свое путешествие в верхнем левом углу. Пройдя вправо до конца экрана, он возвращается назад и начинает рисовать следующую строку развертки

Закончив последнюю строку, луч возвращается из нижнего правого в верхний левый угол экрана

Чтобы вывести текст на экране, надо символы ASCII отобразить в виде чёрных и белых пикселей на экране. Генератор символов переводит 7-битовый код ASCII в 64-битовый код, определяющий внешний вид символа:

Каждому символу соответствует не только 7-битовый код ASCII, но и 64 бита на экране, которые определяют его внешний вид
Каждому символу соответствует не только 7-битовый код ASCII, но и 64 бита на экране, которые определяют его внешний вид

Чтобы раскрасить пиксель, надо увеличить количество битов, на него отведённое:

Количество цветов = 2Количество битов на пиксель

Глава 22. Операционная система

Нашему компьютеру не достаёт двух вещей:

  • программного обеспечения (ПО);
  • избавиться от пульта управления, которым мы вводили команды в виде двоичных кодов, и добавить клавиатуру.

Для работы клавиатуры нам нужна специальная программа — обработчик клавиатуры, а также командный процессор.

Кроме этого нам потребуется ПЗУ — постоянное запоминающее устройство, чтобы введённая информация не терялась после отключения питания. В качестве ПЗУ можно использовать жёсткий диск.

Вам придется помнить, что и куда вы сохранили… часть места на диске, вероятно, занята другими данными, сектора с одной и той же программой не обязательно будут следовать друг за другом

Сидеть перед компьютером и продолжать записывать на бумажке адреса секторов и отмечать, что в них сохранено, — слишком трудоемко. …[Настало время] разработки файловой системы

Файловая система — организация информации, когда она делится на файлы. Файл — набор данных с общим смыслом, записанный в одном или нескольких секторах.

Забавный факт о названиях файлов в и размере расширения в 3 символа:

Имя файла в CP/M состоит из двух частей. Первая часть — собственно имя файла (filename) длиной до 8 символов… Длина второй — типа файла (file type) — ограничена тремя символами…

Чтобы запустить операционную систему, её нужно переписать с диска в память — загрузить. В конце загрузки ОС полностью размещается в памяти, занимая её старшие адреса.

Прикладным программам (приложениям) не нужно заботиться о дорожках и секторах на диске при наличии ОС — интерфейса прикладного программирования, API.

API аппаратно независим. То есть при написании программ для CP/M нам не надо знать, как работают клавиатура, монитор и диск на конкретном компьютере.

Глава 23. Фиксированная точка, плавающая точка

Мы привыкли думать о непрерывном ряде чисел, но компьютеры работают с дискретными величинами.

Чтобы записать дробь, мы можем использовать фиксированную точку — отвести конкретное количество бит под десятичные числа. В таком случае десятичный разделитель всегда находится в определённом месте числа.

Формат с фиксированной точкой хорош, если вы знаете, что числа не «перерастут» ту область памяти, которую вы для них отвели

В обратном случае, под такие числа придётся резервировать слишком много места в памяти. Чтобы записывать числа, которые могут быть и очень большими, и очень маленькими используется плавающая точка.

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

В нормализованном виде слева от разделителя всегда стоит единица:

1.01 × 22 1.101 × 22

В большей части компьютеров для чисел с плавающей точкой используется стандарт IEEE, Standard 754 Floating Point Numbers. В нём есть 2 формата: с простой точностью (single precision, 4 байта на число) и двойной точностью (double precision, 8 байтов на число).

Точность числа с фиксированной точкой легко определить по внешнему виду: сколько чисел после запятой, такая и точность. С плавающей — ¯\_(ツ)_

В зависимости от порядка число с плавающей точкой может быть точным до долей пенса или до нескольких тысяч долларов

…С точки зрения программы 262 144,00 долларов не отличаются от 262 144,01 долларов… Действительно, оба числа представляются в компьютере так: 1.00000000000000000000000 × 218

Глава 24. Языки высокие и низкие

Кхе-кхм 😃

Программировать в машинных кодах — все равно что есть зубочисткой

Первым делом стоит автоматизировать перевод из команд мнемокодов (MOV, MVI и прочие) в настоящие коды команд. Этим занимаются ассемблеры.

Новый ассемблер приходится разрабатывать каждый раз при появлении нового процессора

У программ-ассемблеров два недостатка:

  • писать их — кропотливо;
  • они не переносимые: нельзя написать ассемблер для одного процессора и использовать его с другим.

Язык ассемблера считается языком низкого уровня, потому что он напрямую взаимодействует с оборудованием компьютера. Языки высокого уровня этим не занимаются.

Для работы языку высокого уровня нужен синтаксис и компилятор или интерпретатор. Первый — набор правил для построения выражений; второй — программа, которая преобразует код в машинные коды.

Далее в этой главе есть несколько примеров программирования на Алголе и Бейсике, здесь я их приводить не буду, но по фану почитать интересно.

Глава 25. Графическая революция

Чтобы получить на экране электронно-лучевой трубки цвет, приходится использовать уже не одну, а три электронные пушки, по одной для каждого из основных цветов — красного, зеленого, синего.

Таблица кодирования цветов для адаптера дисплея может быть такой:

БитыЦвет
000Черный
001Синий
010Зеленый
011Голубой
100Красный
101Малиновый
110Желтый
111Белый

Количество достпуных цветов выражается:

Число цветов = 2Число битов на пиксель

Чтобы работать со звуком, нужны преобразователи:

  • ЦАП, цифро-аналогоавый — для преобразования электрического сигнала в звук;
  • АЦП, аналогово-цифровой — для преобразования звука в электрический сигнал.

Частота, с которой аналоговый звуковой сигнал преобразуется в цифровой, называется частотой дискретизации

Заключение

“It all makes sense now” — вот так бы я назвал чувство, которое появилось после первого прочтения этой книги 😃

Всё, что здесь написано, я проходил в школе и университете. Эти знания были, но они не были... объединены что ли. Не было прочной уверенности в том, как всё на самом деле работает, было трудно переходить от одного уровня абстракций к другому. Сейчас всё наконец-то по-другому :–)

Ссылки по теме

Провёл 5-часовой воркшоп по TDDКод. Тайный язык информатики. Чарльз Петцольд