company_banner

Реверс-инжиниринг калькулятора с логикой -17В и частотой работы 200КГц

Автор оригинала: AMEN
  • Перевод

Осторожно! Впереди кроличья нора





Разбираем внутреннее устройство старого промышленного калькулятора Rockwell 920 и на аппаратно-программном уровне пытаемся отследить неисправность, из-за которой он не работает. Процесс оказывается не столь простым, как можно было предположить, и на пути возникает ряд «странностей».

Rockwell 920/3


У меня есть калькулятор Rockwell 920/3.
Родом этот внушительный зверь, где-то года так из 1975. Он программируемый, а его полностью расширенная версия может иметь 996 программных шагов. Помимо этого, он оснащен устройством чтения/записи магнитных карт и шестнадцатизначным дисплеем. Этот калькулятор может сохранять на картах как данные, так и программы. Конкретно первый оказавшийся у меня экземпляр использовался для ведения платежных ведомостей в крупной английской каталожной компании.

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


Программы я напечатал и как-нибудь займусь их расшифровкой. Позже в продаже появился третий аналогичный калькулятор, который я также без сомнений приобрел. В результате это оказалась модель 920/2, а не 920/3, что означало меньший объем памяти. Внутренне эти машины оснащены одинаковой материнской платой, снаряженной скромным ОЗУ. Вот одна из этих плат с моими подключениями для отладки:



Исходя из его архитектуры, этот калькулятор можно поистине назвать одноплатным компьютером. Здесь есть процессор, ОЗУ и ПЗУ вместе с интерфейсом ввода/вывода общего назначения (GPIO). В качестве процессора установлен Rockwell PPS-4, четырехбитный чип, использовавшийся для небольшого числа устройств в 70-х годах, в частности калькуляторов и машин для игры в pinball. Работает он от нестандартного, по крайней мере на сегодня, источника питания -17В. Логика здесь отрицательная, следовательно ноль – это -17В, а единица – это 0В. В результате подключить такое устройство к логическому анализатору, да даже к осциллографу, оказалось проблематичным. Обычно логический анализатор позволяет отлаживать напряжения от нуля до +5В, где земля — это логический нуль, а +5В — логическая единица (прим. переводчика). Частота процессора тоже далека от современных показателей и составляет всего 200 кГц. Второй и третий экземпляр были в нерабочем состоянии, так что я взялся за их починку. И поскольку было бы кстати понять, что именно делает код, я решил инструментировать сигналы шины и проследить его выполнение, а также, если повезет, создать дамп ПЗУ.

Я мог бы выпаять эти ПЗУ (все из которых находятся в нестандартном 42-контактном корпусе QIP с шахматным порядком выводов), но тогда мне бы понадобилось устройство для их дампа, работающее также от -17В. Позже я, может, и соберу такое, если мне не удастся сделать дамп всего ПЗУ с помощью перехвата шины на работающем калькуляторе. Думаю, что если прогнать его по всем функциям при подключенном сканере, то должно получиться постепенно перехватить содержимое ПЗУ, как минимум, его выполняемые части. Я собрал тестовую схему, чтобы оценить, заработает ли вообще хоть что-то при -17В. Результат на этом видео:


Заработала схема отлично, поэтому я продолжил и собрал плату с достаточным количеством входов для перехвата нужных сигналов и анализа выполнения потока кода. Оказалось, что простой (и дешевый, что будет на руку, если при -17В я вдруг допущу ошибку) микроконтроллер STM32F103C8 имеет достаточно GPIO для обработки адресной шины (на языке PPS-4 это A/B), шины данных (или I/D) и сигналов управления шинами.



Техническое описание PPS-4 доступно в интернете, и мне удалось найти образец кода в его патенте. Теперь я могу протестировать свой дизассемблер и любой объектный код на работоспособность. При рассмотрении шина процессора может вызвать пугающие ощущения, по крайней мере в сравнении с Z80. Это мультиплексированная, чередующаяся двухфазная штуковина. В ней присутствует четыре фазы тактов (моя терминология). Адреса ПЗУ поступают на адресную шину в фазе 1, в то время как данные ОЗУ или ввода/вывода находятся на шине данных. Затем в фазе 3 адрес ОЗУ или ввода/вывода передается на адресную шину, а ПЗУ на шину данных. Для лучшего понимания стоит взглянуть на схему из технической документации:


Если поразглядывать ее минут 10, то постепенно все становится понятно. Да, у процессора есть два тактовых входа, работающих на двух разных частотах и несколько отличающихся по фазам. Здесь есть специальная микросхема, генерирующая эти тактовые сигналы. На самом деле выпаивание микросхем ПЗУ для считывания является наиболее простой частью всего процесса. После выпаивания нужно сделать их дамп, используя описанную схему шин, и при этом не забывая, что логические уровни сигналов равны 0В и -17В.

Из полезного в данной схеме шин можно выделить пару фаз «освобождения шины». Это позволяет легко управлять фазами при трассировке кода. Можно было подумать, что все просто, но PPS-4 ставит еще одну подножку, инвертируя частоту B и некоторые (только некоторые) данные на шине до их использования. Иногда он инвертирует полубайт, иногда байт. В данном случае уцепиться можно за пустую фазу.

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

Трассировка шины выглядят так:

A:000 D:04 CLKA:0 CLKB:1 WOL:0 DO:0  - 0
A:000 D:00 CLKA:0 CLKB:1 WOL:0 DO:0  - 1
A:000 D:00 CLKA:0 CLKB:1 WOL:0 DO:0  - 2
A:000 D:00 CLKA:0 CLKB:1 WOL:0 DO:0  - 3
 
A:003 D:00 CLKA:0 CLKB:0 WOL:0 DO:0  - 0
A:003 D:00 CLKA:0 CLKB:0 WOL:0 DO:0  - 1
A:003 D:07 CLKA:1 CLKB:0 WOL:0 DO:0  - 2
 
A:000 D:0F CLKA:1 CLKB:1 WOL:0 DO:0  - 0
A:000 D:00 CLKA:1 CLKB:1 WOL:0 DO:0  - 1
A:000 D:00 CLKA:1 CLKB:1 WOL:0 DO:0  - 2
A:000 D:00 CLKA:1 CLKB:1 WOL:0 DO:0  - 3
 
A:003 D:1C CLKA:1 CLKB:0 WOL:0 DO:0  - 0
A:FFF D:1C CLKA:1 CLKB:0 WOL:0 DO:0  - 1 1C
A:FFF D:1C CLKA:1 CLKB:0 WOL:0 DO:0  - 2
A:6AB D:1C CLKA:0 CLKB:0 WOL:0 DO:0  - 3

Обратите внимание на адрес 3 во втором блоке – это адрес ПЗУ. Затем в четвертом блоке данные из ПЗУ помещаются на шину. Содержимое шины данных при представлении адреса ПЗУ — это данные ОЗУ или ввода-вывода из предыдущей инструкции ввода-вывода или чтения.

Перехваченные данные копируются в текстовые файлы по одному для каждого «окна» предварительной задержки. Серия скриптов извлекает информацию ПЗУ. Это скудные данные, поскольку захватываются, очевидно, только адреса ПЗУ, которые выполняются, или к которым обращаются инструкции. Одна из основных последовательностей – это код запуска.

000     0x81       t 0x1
001     0x82       t 0x2
002     0x74       ldi 0xB
003     0x1C       iol 0x1E  
005     0x7E       ldi 0x1
006     0x1C       iol 0x1D  

Процессор после сброса начинает с адреса 000, так что у нас есть точка для начала трассировки. Следующая инструкция – это переход (t: transfer) из адреса 000 к адресу 001. Это несколько странно, но похоже на правду. Затем идет переход из 001 к 002. Опять же, странно, но может процессору требуется произвести какие-то настройки в первых циклах инструкций, либо счетчик программы таким образом «завершается», или что-то в том духе? Я нашел в патенте какой-то код PPS-4, который начинается так:

0000 81        T       #1                                                   
 	*SET O/P TO ZERO                                                          
 	0001 7F        LDI     0                                                    
 	0002 10 0E   IOL     #E                                                   
 	0004 7F        LDI     0                                                    
 	0005 10 0D   IOL     #D                                                   
 	0007 7F        LDI                                                          
 	0008 10 07   IOL     1   

Здесь мы видим такую же инструкцию перехода, что еще раз подтверждает наличие конкретной точки для трассировки. Тем не менее перехода из 001 к 002здесь уже нет. Мне не удалось найти объяснения, откуда они вообще взялись.

Инструкция IOL отправляет команду микросхемам GPIO. Они не отображаются в память или I/O, вместо чего в них заложены номера устройств. IOL ox1E отправляет команду oxE устройству 1. Так происходит настройка сигналов сканирования клавиатуры/дисплея. Я отследил достаточно сигналов на печатной плате, чтобы определить номера устройств для микросхем GPIO. До этого момента я называл микросхему PPS-4 процессором, но это не совсем верно. У этой микросхемы действительно есть порты ввода/вывода. Это не порты GPIO, поскольку являются фиксированными вводами или выводами, поэтому в некотором смысле данная микросхема больше походит на микроконтроллер. Порт вывода в 920-м используется для управления демультиплексором, который контролирует каждую цифру дисплея. Вводы же используются для распознавания матрицы клавиатуры (сигналы сканирования для клавиатуры – это те же сигналы сканирования, которые используются для управления цифрами дисплея).

Набор инструкций


Я знаком с 8-битными процессорами Z80 и 6502, а также с набором инструкций ARM. Мне также доводилось программировать на ассемблере для PIC, Z8, 6301, 8086, 8051, 4-битных микроконтроллеров и т.д. Но при этом некоторые из инструкций PPS-4 меня удивили. Такое ощущение, что они были придуманы до того, как люди сформулировали устойчивые правила. Например, инструкция load immediate:


Четырехбитное содержимое, поле immediate field I(4:1) инструкции, помещается в накопитель (см. примечание ниже)

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


Инструкции ADI, LD, EX, EXD, LDI, LB и LBL содержат в своих immediate field (полях с непосредственными данными) закодированное числовое значение. Это числовое значение должно присутствовать на шине в виде дополнения. Все immediate field, которые инвертируются, показываются в квадратных скобках
Например: инструкцияADI 1, которую программист пишет, желая добавить единицу к значению в накопителе, преобразуется в 6E(16)=0110[1110]. В скобках указано двоичное значение в том виде, в каком оно представлено на шине данных.

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

Хорошо, для этих инструкций (не всех инструкций) полубайт, являющийся непосредственными данными, инвертируется в ПЗУ и, следовательно, на шине. Мда, несколько странно, но дизассемблер или ассемблер это обработает. Далее мы замечаем, что в четвертом блоке слева есть ссылка на еще одно примечание. На этот раз ведет она сюда:


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

Так, теперь наблюдается некоторая странность. Если загрузить накопитель с 6, а затем с 4, то в итоге его значение будет 6. Когда я столкнулся с этим впервые, то удивился, но после изучения кода решил, что в этом есть смысл. Если вам нужно ввести программу с различными параметрами, то вы можете сделать следующее:

Enter6	LDI 6
Enter7	LDI 7
Enter 8   LDI 8
           * Выполнить действие с A
          RTN

Если перейти к Enter6, то накопитель загрузится с 6, последующие загрузки игнорируются, и вы выполняете действия с 6. Если перейти к Enter7, тогда накопитель загружается с 7, и следующая загрузка игнорируется, а вы выполняете нужные действия с 8. Я понимаю, в чем здесь польза.

Вот инструкция:



48 последовательных адресов на странице 3 ПЗУ содержат данные указателей, которые определяют адреса входа подпрограмм. Эти адреса входа ограничены страницами с 4 по 7. Данная инструкция TM будет сохранять адрес следующего слова ПЗУ в регистр SA после загрузки исходного содержимого SA в SB. После этого происходит переход к одному из адресов входа подпрограмм. Эта инструкция занимает в ПЗУ одно слово, но для выполнения требует два цикла.

Она показывает использование таблиц данных в ПЗУ на уровне инструкций.

Что дальше?


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

Расширение памяти


Небольшое отступление.

Модель 920/2 содержит одну подключаемую плату расширения памяти, а в 920/3 их две:



Удивляет, что на плате ОЗУ в модели 920/2 присутствует четыре микросхемы, а на платах 920/3 их по 6. Еще один сюрприз в том, что эти две платы микросхем отличаются друг от друга. Мне кажется, что у них разные распиновки разъемов, несмотря на то, что объем памяти на них одинаковый. Поэтому вместо того, чтобы задействовать одну печатную плату для обеих конфигураций расширения памяти (часть, заполняющая плату 920/2) я использовал три разные.

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

RUVDS.com
VDS/VPS-хостинг. Скидка 10% по коду HABR

Похожие публикации

Комментарии 20

    +7
    Интересно, если бы техническую литературу переводили без редакторов, там так же было бы? Но в этот раз уже лучше, чем в прошлой статье-переводе :)
    Транскрибирование — это что? В толковых словарях оно про составление транскрипцый и смысла не добавляет. Может реверс/дизассемблирование?
    immediate field (мгновенных полях) поле с непосредственными данными же!
    Внутри подпрограмм инструкция LB должна использоваться с осторожностью, потому что содержимое SB было изменено.

    Тут вообще к непосредственному автору вопрос. LB — Logical Brаnch, и, естественно, оно зависит от флагов состояния SB. Но зачем это в описании инструкцыи LDI? Как изменяются флаги состояния при этом — не ясно.
    Ага, и в чем проблемы с подключением осцыллографа к логике -17В тоже не ясно.

      +1
      Громадные спасибо за замечания! Не всегда есть знания как переводить статью, в действительности есть специфические термины, которые не так просто перевести, если с ними никогда не сталкивался. Те же «мгновенные поля». Почему поле с непосредственными данными? Что это означает в русском языке?

      Ну, и желаю вам немного подтянуть русский язык, а то от «транскрипцый», «инструкцыи» у меня кровь из глаз идёт. У вас не проверки орфографии в браузере?

      Ага, и в чем проблемы с подключением осцыллографа к логике -17В тоже не ясно.

      Анализатор таки удобнее, чем осциллограф. Но собрать на любом ОУ переходник труда не составит.
        0
        Почему поле с непосредственными данными?


        Immediate также имеет значение «непосредственный». Может поэтому?))
          0
          Э, товарищ, ты же спец по электронике, программированию и прочему ассемблеру? это же из темы «методы адресацыи».
          immediate field — это когда данные для операцыи указаны непосредственно (immediate) в опкоде команды, под них выделяется часть опкода (этот самый field), дешыфратор команд и определяет по опкоду, откуда данные взять — из самой команды, из регисторв, из памяти.
          и, конечно, извиняюсь за цы/жы/шы, но
          это мой стиль для неофицыальной переписки в виде форумов, комментариев и прочего.
          «Грамматика — это как кунг-фу: настоящий мастер не использует ее без крайней необходимости» (с)
            +2
            Э, товарищ, ты же спец по электронике, программированию и прочему ассемблеру? это же из темы «методы адресацыи».

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

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

              +2
              Почему поле с непосредственными данными? Что это означает в русском языке?

              Потому что так принято, еще с тех пор, когда в русскоязычной техносфере были свои разработки ЭВМ. Это устоявшийся термин, как верно отметили рядом, связанный с методом адресации.
              Complementary form — это «в дополнительном коде» пмсм, а не «в виде дополнения».

              Int_13h
              Тут вообще к непосредственному автору вопрос. LB — Logical Brаnch, и, естественно, оно зависит от флагов состояния SB. Но зачем это в описании инструкцыи LDI? Как изменяются флаги состояния при этом — не ясно.

              Да никак, глупость написана. То, что он написал про LDI — это Note к LB. На странице с LDI Note совсем другого характера: «только первая LDI в цепочке будет выполнена» и так далее.
              Я не знаю как, но автор оригинала взял примечание с другой страницы.

              Вот тут оригинал, смотрите сами:
              Страницы 18 и 19
                0
                Мнэээ. А зачем ОУ, когда простой транзистор быстрей?
                  0

                  На такой частоте разницы особо нет.

                  0
                  А зачем пихать ОУ или транзистор туда, где достаточно пары резисторов? Даже защитные диоды не нужны — они есть внутри входного буфера анализатора.
                +2
                Логика здесь отрицательная, следовательно ноль – это -17В, а единица – это 0В.
                172-оя если не путаю? Даже один раз использовал что бы не значащий ноль в часиках гасить. Тоже из платы калькулятора выломал, она правда на рассыпухе (логика малой степени интеграции) был собран. Давно бы выкинул, но плата закрывает дыру в двери, сливаясь цветом текстолита, прижилась уже.
                  0

                  Не только 172-я, но и почти вся 145-я так питалась.

                    0
                    С чего бы вдруг? 145 от -27 питалась.
                      0

                      Не только, там у разных микросхем и -15, и -27, но в большинстве случаев — отрицательное напряжение, и большое.

                      0
                      да, часики на 145-ой были
                    +1
                    Занятно. Сейчас реверсю К145ИК130* для МК-61. Всё же прикольней использовать более или менее полноценный процессор с архитектурой фон Неймана или гарвардской. А то К145, ещё то чудо инженерной мысли(ну да, меньше транзисторов, дешевле лалала).
                      +1
                      Получается?
                        0
                        Кое что получается.
                        	command = R[36] + 16 * R[39];	//8 bits текущая команда
                        	switch (command) {
                        	case 0x00: //IK1302  0
                        		//# Формирование маркера кольца и кода состояния.
                        		tS1 = R[30];
                        		for (int i=0; i<=33; i+=3){
                        			R[i]=0xF;
                        			R[i+2] = 0;
                        		}
                        		ST[38] = ST[37];
                        		ST[37] = ST[36];
                        		ST[36] = 0x0 + 1;
                        		ST[41] = ST[40];
                        		ST[40] = ST[39];
                        		ST[39] = 0x0;
                        		R[36] = 2;
                        		R[39] = 3;			//goto 0x32
                        		tCarry = 1;
                        		tS = R[41];
                        		break;

                        Кусок оптимизированного кода.
                        Тут или сам декомпилятор развивать, чтобы сам выкидывал мусор. Или вручную это делать, что довольно утомительно, команд то 256*3=768. Но уже всё не плохо, размер файла 79 кб (WinAPI) и ещё можно хорошо уменьшить, а значит засунуть в МК с 64 кб ПЗУ и 1-2 кб ОЗУ вполне реально. Хотя кто-то в это не верил.
                        0
                        В МК-61 стоит 750-я серия, бескорпусные. 145-я серия в корпусах. Лет 15 назад начал делать его аналог на АТ89С51, тригонометрия считалась в сотни раз быстрей, но потом стало меньше времени и забросил… Сейчас сделать бы на STM32…
                        +3

                        Опять мало pr0n'а...


                        А "ноль – это -17В, а единица – это 0В" легко заменяется на "привычные" подключеним земли ЛА к этим "-17" и неким делением напряжений на 17/5 (17/3,3).

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

                        Самое читаемое