Статьи
Задать вопрос автору
×

Возможность задать вопрос автору напрямую доступна подписчикам.

Программируемый микрокалькулятор на транзисторах с нуля

·
  • DEV notes
·

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

И так, я уже собрал 2 компьютера на транзисторах, моя собственная первая сборка собственного компьютера на своем процессоре, который отдаленно напоминает процессор mos6502, вся сборка была по принципу смотрим книгу Код и собираем. Компьютер получился и даже работает, но не совместимый и все надо делать самостоятельно, для этого есть ассемблер и текстовый редактор, но кажется никто ничего не сделал на нем до сих пор. Кстати почему?

Второй заход был уже повторить настоящий mos6502 и собрать на нем полностью совместимый, на сколько это возможно, компьютер APPLE ][. И это тоже было сделано, но у этих сборок была одна особенность, в них использовались примитивы: клавиатура, монитор, модем, дисковод. То есть некие устройства которые были черными ящиками, а они сами по себе весьма интересны. Причина такого выбора была в скорости, собрать такую схему и заставить ее работать в симуляторе, написанном на JS в браузере это не просто. 

Но желание то никуда не делось и решил найти что-то, что можно собрать, что будет работать и что можно от и до собрать на транзисторах, и я нашел такой прибор — программируемый микрокалькулятор. Сначала я думал про советский МК61, но потом решил собрать, что-то по настоящему легендарное и выбор пал на HP41C, калькулятор, который будет максимально непривычен современному жителю - калькулятор, который умеет решать сложные математические задачи и который летал в космос в программе Шаттл. 

Схема

Чтобы собрать устройство нужна схема. Хотя примерное понимание того, как будет оно работать. И вот тут была проблема, с одной стороны есть принципиальная схема калькулятора HP41C, но его собирали не из транзисторов, и даже не из базовых микросхем, а из кастомных. Он имел свой собственный процессор NUT, специально разработанный для этого калькулятора.

Принципиальная схема калькулятора HP41C

Это в целом помогало с пониманием того, что именно предстоит сделать. Изучив схему пришел к следующем выводам, процессор калькулятора будет системой ввода данных с обратной польской нотацией (RPN), это очень не привычно сейчас, но тогда позволяло избежать проблемы использования скобок при вычислениях, что просто мега удобно и упрощало реализацию вычислений. Например в расчете 2+2×2 = ? при использовании RPN споров не возникает, тут все строго 2 ENT 2 ENT 2 × + что давало 6. Для тех, кто не в курсе, вот эта запись 2 ENT 2 и так далее это и есть работа с RPN где сначала вносятся числа, а потом в нужном вам порядке операции, в стек (в память для вычислений), а калькулятор уже берет по очереди числа и операции и применяет их к друг другу. В общем удобно, хотя фактически это эквивалент раскрытия скобок и современные расчетные движки делают точно также, только за спиной у пользователя.

Процессор

Процессор калькулятора...

Дисплей

Далее из интересного, был дисплей, у HP41C он весьма интересный. Для своего времени он был просто революционный, это был сегментный дисплей типа звездочка, которые применяли в то время, но инженеры HP добавили сегменты и это позволило отображать буквы латинского алфавита, и данный калькулятор был алфавитно цифровом, при программировании можно было команды писать буквами. 

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

Кроме того, сразу же при создании, я решил, что в отличие от оригинала в моем калькуляторе, будет 3 строки, а не одна. Я хотел, чтобы показывалось текущее значение регистра Х, то что ввел пользователь или результат расчета, предыдущее значение, которое находиться в регистре Y, а также полезная информация о работе калькулятора, например, режим работы, режим отображения числе, режим работы клавиатуры и т.д.

Размер экрана позволял вводить 12 символов и это включая разделитель ., тогда как у HP41C разделитель был составной частью знакоместа, и он выводил 12 цифр в числе, мой же калькулятор 14 символов, включая разделитель.

В итоге у меня получился, вот такой экран.

Клавиатура

Изучив устройство ввода в HP41C я также решил пойти более простым путем, первое от чего я отказался, это от ввода букв. Дело в том, что клавиатуры калькуляторов устроены несколько иначе, чем привычные нам клавиатуры компьютеров, речь не про механику работы, а про пользовательский опыт.

Хотя строго говоря, клавиатуры компьютеров того времени иногда были очень похожи на клавиатуры калькуляторов. На фото ниже клавиатура популярного в начале 80-х годов компьютера ZX Spectrum, в постсоветском пространствен он был популярен до конца 90-х. Обратите внимание, что каждая кнопка это большой набор команд, то есть вы не набирали команду PRINT, вы нажимали кнопку P в режиме ввода команд. Просто так было принято, но даже уже в самом спектруме в итоге отказались от этого и дали возможность работать с клавиатурой в привычном виде.

Ранее, в сборке APPLE ][ я использовал стандартный примитив симулятора, некоторое устройство, которое возвращает код клавиши, но такой подход не удобен если у вас разные устройства, и разные клавиатуры, например, как у калькуляторов, по этому я решаюсь собирать клавиатуру с контроллером с нуля, из простых кнопок, которые замыкают контакт. А это значит, проектирование клавиатуры очень важная задача, надо понимать сколько ты готов взять кнопок, сколько режимов у кнопок будет, потому что все это определяет сложность дешифратора, который должен понимать какую именно кнопку нажал пользователь и выдавать ее код, также отдельно расскажу о том, какие трудности были при сборке с хранением код кнопки, пока процессор занят и не может забрать код клавиатуры.  

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

Проект клавиатуры калькулятора

Сборка

Определившись с проектом, надо было составить план, в каком порядке и что собирать. Например, сначала собрать процессор, потом память, и контроллер клавиатуры и контроллер экрана, или сначала контроллеры клавиатуры и экрана и только потом процессор. Так как у меня не промышленная сборка, а учебная, чтобы лучше понимать работу компьютерной техники, мне конечно же хотелось собрать контроллеры, но начал я с процессора, потому что он тоже оказался интересным, и связано это с тем, как работали компьютеры в то время и какие задачи стояли перед инженерами, которые использовали калькуляторы. 

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

Числа

Главное, что отличало калькуляторы того времени, а я говорю, об инженерных программируемых калькуляторах, и как пример, HP41C, это высокая точность расчетов в том числе с числами с плавающей запятой. 

Если вы не понимаете, а что такого делать на компьютере точные вычисления, то будет, наверное, полезно узнать, что компьютеры в целом не знают, что есть какие то скажем иррациональные числа, которые используют инженеры, например, число π, которое, как мы знаем, бесконечная дробь, начинающаяся как 3.14159... и оказывается для реальных расчетов, число знаков после запятой в числе π или e или еще в каких иррациональных и не только дробях имеет значение.

Компьютеры ничего не знают о числах, вообще, они вообще в целом ничего не знают и не думают, компьютер, как это я однажды подметил в интернете:

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

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

И как то они при этом помогают нам вычислять, обрабатывать цифры. Многие, думаю, знают про двоичную систему, про нули и единицы, что компьютер все хранит и использует именно в нулях и единицах. Так вот, компьютер не знает, ни каких нулей и единиц, даже этого там нет.

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

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

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

Если посмотреть на то, как хранятся числа в калькуляторах, то может показаться, что это расточительство, зачем так усложнять жизнь, но оказывается, без этого работает точность. Грубо говоря, если компьютер умеет работать только с целыми числами, то нам надо придумать, как представить рациональное число 3.14159... в виде целых чисел, при этом этот формат и подход должен быть универсальным и позволять работать с любыми числами. 

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

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

Но мантисса у нас все еще дробная, и ее тоже надо как то хранить, и вот с ней решили поступить так, раз целая часть у нас всегда имеет один знак, значит она изменяется от 0 до 9, то мы будем хранить ее отдельно от дробной части. И в итоге мы будем хранить целое число мантиссы, знак мантиссы, число после запятой как целое число, затем целое число степени 10 (экспоненту) и знак экспоненты. В самом минимальной виде нам понадобиться 3 байта, чтобы просто хранить дробное число, даже для такого числа, которое без проблем храниться в 1 байте. Это плата за точность. 

Отмечу отдельно, в калькуляторе любое число занимает ровно 7 байт или 56 бит в полном формате. И про этом расскажу ниже.

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

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

То есть умножение очень большого числа на очень маленькое число, это определение итогового знака, сложение степеней и умножение чисел одинаковой размерности.  Сейчас мы не замечаем уже этой разницы, но тогда это было заметно.

Представление больших чисел с плавающей точкой в памяти

На рисунке изображено то, как хранятся числа в памяти калькулятора. Каждая цифры мантиссы храниться отдельно как двоичное число, для записи цифр от 0 до 9 надо 4 бита — тетрада, в итоге на запись числа выделено 14 тетрад или 56 бит, из которых, 4 бита на знак числа, 4 бита на старшую цифру мантиссы, потом 9 раз по 4 бита на цифры дробной части, 4 бита на знак экспоненты и 2 раза по 4 бита, то есть 2 цифры степени, то есть степень от -99 до 99. 

Из интересного отмечу, что на знаки уходит по 4 бита, хотя хватило бы и 1 бита, но инженерно реализовывать хранение и обработку тетрад оказалось проще, чем отдельно тетрады и отдельно биты. Это было именно инженерным рещением.

Пример хранения разных чисел

Пример хранения числа 2315 побитно

На иллюстрациях выше показано, как хранятся разные числа в памяти и отдельно число 2315 побитно.

Выбор чисел, определил то, что калькулятор будет строиться как BCD (Binary-Coded Decimal) устройство, фактически калькулятор будет работать с числами также как привык это делать человек.

Например, вам надо сложить 15 + 18 = 33. В двоичной логике компьютер переводит число 15 в бинарное 1111, а 18 в 10010. Затем складывает их и получает 1111 + 10010 = 100001, которое переводит потом обратно в десятичное как 33. В BCD это работает иначе, каждое число переводиться в двоичное 1 = 0001, 5 = 0101, 8 = 1000. Затем складывает  15 и 18 как мы делаем в столбик. И потом полученное число возвращает уже нам как результат.

В целом это выглядит неоправданным, но все упирается в точность, BCD арифметика позволяет хранить числа так как они есть, число с точностью до 10 знаков после запятой будет иметь строго 10 знаков после запятой и все они будут точны. Также такие цифры очень просто обрабатывать с точки зрения вывода на экран, но об этом я расскажу отдельно.  Ну и в то время ALU процессора был 4битным, и мог обрабатывать не более 4 бит за такт, а это как раз наша тетрада в BCD числе.

Это было очень важным блоком и решением, потому что, я раньше никогда даже не думал, что буду собирать BCD сумматор. 

Схема

В итоге, план получил небольшую схему, как именно я буду собирать блоки.

Второе важное, к чему я пришел в этом проекте, это отказ от попытки собрать полностью всю логику работы процессора на транзисторах1, мало того, именно так и работал процессор NUT в калькуляторе, да и большинство современных процессоров. 

Как устроена работа, сначала я составил таблицу всех операций, команд и функций, которые должен уметь делать калькулятор. Эта таблица нужна для того, чтобы составить список команд, которые должен уметь выполнять процессор. Например, команда сложение - в процессоре должно быть реализовано сложение двух чисел на транзисторах, при этом (это отдельная задача) откуда эти 2 числа попадут в арифметическо логическое устройство ALU оба из памяти, оба из регистров, одно из памяти втрое из регистра и т.д. 

Если вы посмотрите на таблицу, то вы увидите, команду например, возведение числа  y в степень х, например, 34.566^17.567 = 1.07016117178×1027 чтобы реализовать такую операцию на транзисторах, она получить неимоверно огромной, будет содержать очень большое число транзисторов, тогда как оригинальный HP41C содержал всего то порядка 10 000 транзисторов, моя сборка в итоге уложился в чуть более 11 000. Для сравнения современный процессор типа M6 компании  APPLE это десятки миллиардов транзисторов.

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

Итого моя таблица команда микрокода.

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

Строим ALU

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

Так как наш калькулятор работает по BCD модели, и хранит число в 14 тетрадах, то наше ALU умеет делать очень простые вещи, а именно складывать, вычитать две любых цифры от 0 до 9, то есть по одной цифре за раз, также, есть умение делать перенос при переполнении и собственно все. 

Так выглядит SALU внутри, он использует разные логические модули, такие как NOT, OR, AND, XOR, однобитные сумматоры, которые нужны, чтобы сложить наши 4битные цифры, и несколько мультиплексоров (MUX), которые нужны для того, чтобы определять сигналы от какой операции выпускать как результат работы ALU, например мы складываем или вычитаем.

А так свернутый SALU до чипа на холсте сборки калькулятора.

Теперь главное, чтобы сложить 2 числа в калькуляторе, он прогонит эти числа через SALU 14 раз, по одному разу для каждой тетрады. Данная схема состоит из 336 транзисторов.

Как заставить калькулятор считать

Собрав блок мы просто получили транзисторы, которые пропуская сигнал через себя меняют его на другой. Причем на входе мы имеет много разных сигналов, а на выходе 5, 4 бита расчетные и 1 бит переноса. Как калькулятор понимает, что ему надо делать сумму, или скажем что сейчас идет 5 тетрада первого числа?

Все устроено следующим образом, микропроцессор в момент прохождения сигнала получает информацию - команду сложить 2 числа. Процессор знает, он так собран инженерами, что числа у нас  разбиты на 14 тетрад, значит нам надо 14 раз выполнить процедуру, взять 1 тетраду числа A и числа B, взять операцию (сложение, вычитание, корректировка переноса) и отправить все это в SALU, получить результат, сохранить его и если надо отметить, что был перенос.

Все это реализуется через счетчик тетрад, на схеме TC. Для каждого такта процессора он выдает номер тетрады числа в текущей операции, кроме того рядом расположен чипа FIELDSEL, который определяет, а с чем именно идет работа с мантиссой целой частью, дробной частью или экспонентой, со всем числом или только с одной какой то цифрой, и это уже определяется сигналом из PREG. 

Для вычисления x^y делается огромное число операций — сотни, числа в инженерном виде, прогоняются через функции логорифмирования, умножения и т.д. По этому необходима память, которая организована как REGFILE, но в моей сборке в симуляторе это просто чип памяти. Такой выбор осознаный, потому что собирать микросхему памяти на транзисторах можно, но это тысячи транзисторов и используется просто виртуальная память, а раз так, то все регистры просто части этой памяти, а не отдельные наборы транзисторов.

Итоговая схема расчета через SALU выглядит таким образом:

А отдельная микрокоманда в тактах вот так

Пример: в регистре лежит 1.234567890×1005. Микрокоманда C=C+A всегда проходит все 14 цифр. Поле задаёт, какие из них SALU меняет (+), а какие проходят без изменений (·). На нижней цифре поля carryReset обнуляет перенос (Cin=0). Поле P касается одной цифры, номер которой лежит в указателе P; поле WP охватывает цифры от 0 до P.

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

Отобразить результат

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

При этом мы не задумываемся о том, что компьютер ничего не знает о числах, цифрах, и уже тем более о том, как они выглядят. Ему все равно. По этому, когда мы с вами хотим вывести число 3.141592654 то, устройство должно во первых понимать, что надо сделать, где и сколько раз. 

Как устроено это в моей сборке. Выбран экран, который умеет отображать 3 строки по 14 символов, каждый из которых состоит из таблицы 5 на 7 точек. Таким образом, нужно нарисовать все символы, которые нужны по точкам, и потом каким-то образом превратить их в зажженные точки на экране. При этом нарисовать шрифт самая простая задача.

А вот вывести его на экран это уже отдельная задача, и она не простая.

Сначала, процессор дает команду нарисовать на экране символ, код которого, например, 5 - у нас это число 5.  Но мы можем нарисовать любой символ. Далее, в специальной памяти, которая называется FRAMEBUFER мы размещаем этот код, при этом эта память содержит ровно 42 ячейки памяти или 42 байта, у нас 3 строки по 14 символов. Если мне надо в строке 3 в позиции 2 разместить этот символ то в соответствующую ячейку памяти размещаем код символа, который надо вывести.

Далее, у нас есть специальная память, которая содержит сами символы. Каждый символ, это ячейка 6 на 8 точек, из которых символ занимает 5 на 7, есть один ряд на расстояние между символами. 

Внутри каждый символ занимает 8 байт, каждый байт скажем это просто число, например, 31, 16, 30, 1, 1, 17,14 кодируют следующую матрицу по битно:

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

Далее нам надо передать эти числа в дисплей, который просто зажжет точки там где 1 и погасит там где 0. Для этого надо вычислить адреса, по которым надо отобразить эти точки на экране.

Весь экран это просто большой набор точек, которые сгруппированы в 83 на 23 точки, но активны только 70 на 21 (14×3 символа). Значит у нас есть 1909 точек, но активных точек только 1470.  У каждой точки есть свой адрес, на самом деле просто порядковый номер от 0 до 1908, при этом адреса зазоров просто пропускаются. Для вычисления адреса точки, надо знать строку, позицию в строке, номер строки символа и номер бита символа.

После чего мы передаем наш символ по битно, на экран, а он уже включает нужные точки на нем. Общая схема работы этого модуля выглядит так.

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

Часть сборки PIXADDR

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

Но, чтобы нажать кнопку, нам нужна клавиатура!

Клавиатура

Устройство ввода, это тоже интересное инженерное решение. Начинается все с простого, размыкатель, простое прибор, вы все его видели в своей жизни, когда включали свет на кухне и открывали холодильник, сначала вы нажимаете выключатель света (или включатель, как правильно?), замыкаете цепь, она начинает проводить ток и лампочка загорается, и аналогично с холодильником, при открытии двери вы отпускаете контакт и он замыкает цепь питания лампочки и вы можете узреть вашу заначку колбаски на ночь.

Так собственно и в калькуляторе. Кнопка - это просто размыкатель и замыкатель цепи. Остается понять, а как компьютер/калькулятор узнает, что вы нажали именно кнопку 1, или, кнопку COS, но для вычисления ACOS.

Как это устроено в общих чертах, компьютер не знает, что есть какие то кнопки и уж тем более, что на них изображено и что должно произойти если вы их нажмете. По этому инженеры придумали делать так, нужно присвоить каждой кнопке какой то код, и когда мы будем замыкать ее, то будем понимать, что замкнута именно такая кнопка. И так делали на самых первых компьютерах. Но там кнопки задавали значение 0 или 1 для конкретного бита. То есть общение с компьютером было натурально через биты.

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

В современных компьютерах используют несколько иной подход, если разобрать штекер клавиатуры (если он у вас еще есть, но лет 10 назад был повсеместно), то вы увидите, что там проводов сильно меньше, чем кнопок на клавиатуре. А значит информаци о нажатой кнопке передается как то иначе. А именно она кодируется, для этого инженеры решили, что все кнопки будут размещены на некоторой матрице, у которой есть строки и столбцы, в моем случае калькулятор имеет 40 клавиш, это 8 строк по 5 кнопок. Когда я нажимаю кнопку, я замыкаю контакты в конкретном столбце и строке.

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

Реальный чип KBDENC содержит 468 транзисторов, которые получая на вход 40 контактов от каждой кнопки превращают их в 8 рядов по 5 кнопок в ряд и выдают на выходе код нажатой кнопки.

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

Для решения этой проблемы придумали временную короткую память, которая хранит код нажатой клавиши, пока процессор не заберет его и не очистит тем самым эту память, она называется KBUF или буфер клавиатуры. Вообще по схеме буфер кладет код нажатой кнопки в регистр, при этом нам еще важно учесть, какой именно код мы отдадим, потому что, в калькуляторе у нас есть клавиши модификации F и G, они говорят какое значение, написанное на кнопке, имеется ввиду, у меня их 3: основное, написаное желтым сверху (F) и написанное синим снизу (G), причем есть кнопки, которые имеют и то и другое.

Это общая схема, которая показывает как формирует код нажатой кнопки, мы нажимаем кнопку, через шифратор мы получаем код кнопки на клавиатуре, затем она попадает в KTAB, который учитывает не только код кнопки, но и был ли на предыдущем шаге нажат модификатор. Затем код запоминается в буфере и раскладывается на тетрады, и тут важно понять, что весь калькулятор живет в циклах, любая команда выполняется не за один такт — пропуск сигнала через всю сеть, а за 14 такта. И сигнал To0-3 это 4 бита счетчика этих тетрад, следовательно код нажатой копки получается за 2 такта, на первом мы получаем значение кнопки в матрице, причем мы получаем сразу код, который отдаем нам KTAB, а на втором тип кнопки: основная F или G модификация, которое определялось предыдущим нажатием и хранится в памяти как флаг. И в итоге получаем в регистр памяти уже готовый код команды для процессора, который обрабатывает прошивка микрокода процессора. 

Таблица KTAB, которая расшифровывает код кнопки и превращает его  в код команды устроена как обычная hash таблица, то есть это ячейка памяти в которой для каждой комбинации код кнопки и код модификатор храниться код, который должен попасть в регистр. Изначально я пробовал все собрать на транзисторах, но схема становилось очень большой, а цель собрать устройство, которое будет работать, а значит число транзисторов надо уменьшать. Память же в симуляции бесплатна, да и устроена с точки зрения симуляции ровно как hash таблица.

Вот таким хитрым путем устроена схема работы клавиатуры в калькуляторе.

Прошивка

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

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

Исходный код прошивки выглядит примерно так

                 main:               ; главный цикл
14 2a000383 RD M,5 ; M = флаг RUN из ОЗУ
15 00100180 PLD 0 ; дальше работаем с цифрой 0
16 00000a1a COPY C,M,P
17 00801205 LC S,1,P ; S = 1
18 0000062a SUB C,S,P ; C = RUN − 1, заём, если RUN = 0
19 75090180 GOTONC run_fetch ; заёма нет → идёт программа
20 80000384 RK N ; N = клавиша: N[0] значение, N[1] тип
21 00000ba5 COPY S,N,W
22 01400385 SHR S,W ; S[0] = тип
23 00100180 PLD 0
24 00000a3a COPY C,U,P ; U[0] = режим записи PRGM
25 00801203 LC M,1,P
26 0000061a SUB C,M,P
27 54896180 GOTONC prgm_rec ; PRGM включён → записать клавишу
28 00800383 LC M,0,W
29 00100180 PLD 0
30 00801203 LC M,1,P
31 00000a2a COPY C,S,P
32 0000061a SUB C,M,P ; тип − 1
33 35050180 GOTOC main_t0 ; тип 0 → покой или банк 0
34 00105180 PLD 5
35 00000a3a COPY C,U,P ; U[5] = ждём номер регистра
36 00801203 LC M,1,P
37 0000061a SUB C,M,P
38 fc094180 GOTONC do_reg ; после STO/RCL → это номер регистра
39 00100180 PLD 0
40 00801203 LC M,1,P
41 00000a2e COPY T,S,P ; T = тип
42 0000061e SUB T,M,P
43 0e050180 GOTOC main ; тип 0 (сюда уже не дойдёт)
44 0000061e SUB T,M,P
45 00053180 GOTOC doapp ; тип 1 → набор числа
46 2f010180 GOTO docmd ; тип 2, 3 → команды

При этом каждая строка храниться в памяти как 32 бита, на примере это может быть так:

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

При этом процессор не останавливается и всегда по кругу выполняет программу микрокода.

Фактически процессор непрерывно крутит код main, пока не придет указание делать, что-то иное. Когда вы нажимаете клавишу y^x то вызывается программа прошивки, для вычисления этой команды.

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

Умножение строится также как и учили нас в школе столбиком. с тригонометрией все несколько сложнее, но принцип тот же, главная задача уложиться в имеющуюся память, причем важно сама память в симуляторе может быть любой, а вот процессор для адресации использует шину адреса, и она ограничена, для ее расширения надо переделывать процессор. По этому под прошивку было заложено 8192 слова по 32 бита. Вся прошивка занимает 6695 команд. А исходный код прошивки 7797 строки. Если мне понадобиться функция, которая вычисляет факториал, то я должен буду просто реализовать ее в микрокоде и назначить код кнопки с модификатором. Или написать свою программу, потому что калькулятор программируемый.

Программирование

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

Рассмотрим пример программирования на той самой программе, расчета факториала.

fact:                                                            
0 000 3 3 STO 000 [STO 0] ; R0 = n — счётчик
1 000 1 1 1 [1] ; число 1
2 001 3 3 STO 001 [STO 1] ; R1 = 1 — произведение
3 001 0 9 LBL 001 [G GTO 1] ; метка 1: начало цикла
4 000 3 4 RCL 000 [RCL 0] ; X = счётчик
5 000 0 B X=0? [G 7] ; счётчик = 0?
6 002 0 6 GTO 002 [GTO 2] ; да → на выход; нет → пропуск
7 001 3 4 RCL 001 [RCL 1] ; X = произведение, Y = счётчик
8 000 2 5 × [×] ; X = произведение · счётчик
9 001 3 3 STO 001 [STO 1] ; R1 = новое произведение
10 000 3 4 RCL 000 [RCL 0]
11 000 1 1 1 [1]
12 000 2 4 − [−] ; счётчик − 1
13 000 3 3 STO 000 [STO 0] ; R0 = счётчик − 1
14 001 0 6 GTO 001 [GTO 1] ; назад к метке 1
15 002 0 9 LBL 002 [G GTO 2] ; метка 2: выход
16 001 3 4 RCL 001 [RCL 1] ; X = n!
17 000 0 8 RTN [G XEQ] ; стоп, результат на экране

Программа, которая приведена тут может быть приведена просто как набор нажатия кнопок STO 0 ENTER 1 STO 1 ENTER G GTO 1 ENTER RCL 0 ENTER G 7 GTO 2 ENTER RCL 1 ENTER × STO 1 ENTER RCL 0 ENTER 1 − STO 0 ENTER GTO 1 ENTER G GTO 2 ENTER RCL 1 ENTER G XEQ, и это еще продвинутый режим, потому что, на языке советского микрокалькулятора MK-61 он выглядел бы так: 40 01 41 60 57 15 61 12 41 60 01 11 40 51 03 61 50.

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

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

После того как программа введена ее надо запустить. Для этого нужно выйти из режима программирования еще раз нажать кнопку PRGM и уже в режиме калькулятора нажать, например, 5, а после, нажать кнопку R/S. 

Демо

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

Саму изначальную схему этого калькулятора на основе схемы HP41C разработал агент, я же собирал схему руками, находил ошибки, такие как переиспользование логических элементов, а для меня это было критичным, задача была уменьшить число транзисторов и приходилось в схеме 8 MUX2 заменять на два MUX2x4, казалось это мелочь, но проблема кроется в симуляции. Симулятор работает в двух режимах REAL и TURBO, в первом схема разворачивается до каждого отдельного транзистора, а во втором до блока к которому известна таблица истинности. И вот имея 8 MUX2 это 8 элементов которые считаются в симуляции, а один MUX2x4 тоже имеет таблицу истинности, а значит мы получаем сокращение просчета в 4 раза.

Вы же можете сами посмотреть мою сборку в симуляторе: https://cs.khanin.info нужно нажать кнопку Demo на верхней панели и выбрать калькулятор KH41C.

Также, подписывайтесь на мой youtube канал, я планирую записать видео про этот проект.

Подпишись на Telegram-канал и YouTube, чтобы не пропустить новые статьи

Практические методы юнит-экономики и финмоделирования

50€/год

менее 1€ в неделю · оплата раз в год
  • Закрытые статьи: формулы ценообразования, когортный анализ, расчёт метрик, финмоделирование
  • Новые статьи по мере выхода — на весь срок подписки
  • Электронная книга «Юнит-экономика» в подарок 10€
  • Все выпуски подкаста
  • Комментарии и вопросы автору напрямую

А ещё: выбор темы оформления, настройка шрифта, печать статей и увеличение изображений.

Для доступа к материалам оформите подписку.
Если вы уже клиент, то просто .

* – оплата через Boosty позволяет оплачивать картами Мир, оплата через Stripe для международных карт Visa, Mastercard и т.д.

Статьи