Robotron S6130 — Восставший из мертвых
- вторник, 4 марта 2025 г. в 00:00:10
История ремонта этой пишущей машинки тянется уже 8 лет! Шутка что ли, это Самый Первый Артефакт моего YouTube-канала!
Robotron S6130 — многофункциональная пишущая машинка формата A2 на базе процессора Z80, с функцией запоминания набранных текстов во встроенной оперативной памяти, с записью и воспроизведением на магнитофон, а также возможностью приема и передачи текстов с ЭВМ по последовательному порту! Мне попадалась информация, что аппарат любили бухгалтеры: на магнитофон можно было записать таблицы и шаблоны документов, чтобы потом оперативно печатать их пачками.
Однако, перед тем как появиться у меня, агрегат порядка четырех лет пролежал в сыром, заброшенном помещении на горе заплесневевших книг. За это время питающая RAM резервная батарейка протекла и пожрала дорожки на материнской плате. Коррозией покрылись и ноги ближайших микросхем, RAM. Разумеется, на первом обзорном видео машинка не подала признаков жизни, и я увез ее в гараж на длительное хранение.
С тех пор я сменил две работы (сейчас работаю в YADRO), женился, завел детей, построил компьютер на реле и на пневмонике, замахнулся на ламповый компьютер, на самодельные микросхемы, а машинка пылилась в гараже и ждала своего часа.
Но не надо думать, что я про нее забыл. Например, в 2019 году я предпринял первую попытку ремонта. Проверил работоспособность платы драйверов шаговых двигателей, а также выпаял микросхемы памяти для снятия прошивки и проверки микросхем RAM на работоспособность. 8 из 16 чипов были мертвы. Зачем-то купил советскую версию машинки «Элема». Ее механика до последнего винтика повторяет серию S6100, но электроника сделана на базе процессора КР580ВМ80А и прочих отечественных комплектующих. Я попытался починить и ее, за что в ответ получил выстрел конденсатором себе в бок. «Роботрон» во второй раз уехал в гараж, а «Элема» — в Муромский кибер-музей.
Тогда стало понятно: лучший способ спасти машинку — это изготовить для нее новую материнскую плату взамен сгнившей. Тем более я нашел схему, инструкцию по эксплуатации, а также несколько сервисных мануалов для ее родственников. Нужно было только оцифровать схему в современной САПР и развести новую плату с некоторыми изменениями.
Вместо двух десятков мелких корпусов RAM и ROM я поставил по одной микросхеме памяти каждого типа, так как теперь это не дефицит. Особый интерес представлял последовательный порт RS-232 — на оригинальной плате он не был распаян, хотя функционал работы с ним в прошивке был! А значит, очень высок шанс, что интерфейс заведется.
Проект замерзает еще на пару лет, пока один из подписчиков, тов. Folk, не взялся за отрисовку платы в KiCAD. Месяц неспешной работы, и к январю 2022 года рождается она — новая плата для «Роботрона»:
Разрабатывалась плата один в один по габаритам старой, даже основные детали были на тех же самых местах. И тут я допустил три стратегических просчета. Серию машинок S6100 выпустили в 1986 году в ГДР. Есть шанс, что часть номенклатуры спустя 40 лет уже не найти и список компонентов стоило бы проверить на «доставабельность». Например, вместо родных диодов пришлось ставить современные в другом корпусе. Но заменить двухвыводной компонент довольно легко. То ли дело rail-to-rail операционный усилитель в корпусе DIP-6! Один у меня был снят с родной платы, а вместо второго пришлось запаивать ОУ в SMD-корпусе, слегка враскорячку. В итоге он заработал, но не буду забегать вперед.
При замене 16 корпусов RAM на один-единственный требуется корректировка сигналов управления. Нам достаточно объединить все сигналы ChipSelect. Ничтоже сумняшеся я собираю их в кучу пачкой диодов, как уже ранее делал в релейном компьютере. Аналогично поступил и для ROM. Стоит ли говорить, что требования к качеству фронтов на 25 Гц и на 2,5 МГц немного различаются? Пришлось все диоды выкусывать и навесным монтажом делать слегка по-другому. Опыт. Сын ошибок трудных…
Еще мне стоило досконально проверить новую схему на предмет ошибок, особенно во внесенных изменениях. Благо пара перерезанных дорожек и 10 минут работы паяльником смогли исправить упущенный косяк.
Осталось эту плату заказать. В то время я сотрудничал с PCBWay, и, вследствие обстоятельств непреодолимой силы, к концу 2022 года у них скопился передо мной небольшой должок. Я предложил забрать его натурой: заказал кучу деталей для проекта «ГИП овер 10к на стероидах», а также вожделенную новую плату для машинки, которая, на секундочку, имеет размер 130х450 мм! Доставка в РФ, правда, тоже была невозможна... В марте 2023 года я как раз поехал в командировку в Китай, на пару часов заглянув в Ханчжоу!
Мне провели экскурсию по офису (фабрика находится в Шэньчжэне, и на нее попасть так и не вышло), выдали платы, детальки, мы интересно пообщались, и я вернулся домой. Плата с легкостью поместилась в багаж, так что с перевозкой проблем не возникло. Осталось ее собрать и запустить.
И наконец в январе 2025, спустя еще полтора года после этих событий, я разом запаиваю полный набор компонентов на новую печатную плату. Проект реставрации набрал критическую массу и резко перешел в активную фазу
Не считая пары микросхем враскорячку, сборка новой платы прошла без осложнений. За 8 долгих лет я не только не растерял родные детали, но и набрал приличное количество доноров от различной техники Robotron, так что этого запаса микросхем хватит еще на одну машинку. С одного такого донора я и снял недостающие микросхемы для СОМ-порта. Буквально за несколько вечеров все компоненты были на месте, и пришло время подавать питание.
Прежде чем начать «поднимать» плату, ознакомимся, наконец, со структурной схемой машинки:
Сердцем системы является процессорный комплект на базе U880 — нелицензионного клона самого популярного 8-битного процессора Zilog Z80. Сам процессор работает на частоте 2,5 МГц. ROM была представлена в виде 7 корпусов по 2 Кб каждый. К диагностическому разъему машинки можно подключить специальное устройство с тестовой программой, и ROM этого устройства будет располагаться в восьмом корпусе по адресу 3800-3FFFh. В новой схеме с одним чипом это изначально поддерживалось, но после ремонта ChipSelect — временно нет. 8 Кб RAM находятся чуть выше — с 4000h до 5FFFh.
Для процессора вся область памяти доступна по сигналу MREQ без дополнительных плясок с бубном. Хочешь — иди в ROM, хочешь — в RAM.
Периферия же хоть и сидит на общей шине адреса и данных, но доступна через IOREQ, то есть только через инструкции IN/OUT, а также дьявольскую OTIR — пакетную выгрузку данных из памяти в порт. Адресация тут уже 8-битная. {VA7, VA6} == 00h — разрешают доступ к чипам, {VA4, VA3, VA2} выбирают один чип из семи (восьмой вывод дешифратора остался в воздухе), а младшие биты VA0 и VA1 выбирают канал микросхемы — A/B и управление/данные соответственно. Таким образом, у каждой микросхемы есть четыре возможных адреса для чтения или записи. Сами микросхемы должны быть хорошо знакомы любому спектрумисту:
контроллер параллельного интерфейса U855 — клон Z80-PIO,
контроллер последовательного интерфейса U856 — клон Z80-SIO,
микросхема четырехканального счетчика U857 — клон Z80-CTC.
Полная идентичность чипов нам только на руку, так как документация на U880 нашлась лишь на немецком языке. Будем пользоваться тоннами русской и английской литературы по Z80. Пробежимся по назначению основных микросхем:
Системный таймер CTC.cs1. Активно используются все четыре счетчика. Например:
Канал 3 настроен на период в 1 мс, и его обработчик проверяет нажатие кнопки СТОП. А также каждые 32 мс дергает контроллер дисплея.
Канал 1 используется всеми моторами для обновления состояния выходов на драйверах. В зависимости от того, какой сейчас работает мотор, подгружается нужный адрес обработчика прерывания.
Контроллер дисплея PIO.cs2. Каждые 32 мс через него отправляется 16 байт данных в плату индикации. Технически это коды 12 символов для отображения на матричном дисплее 5х7 плюс пара светодиодов. Изображения символов хранятся в ROM. Плата самостоятельно выводит их на сегменты с помощью схемы динамической индикации. Эту таблицу мы достанем позднее.
Контроллер моторов PIO.cs3. Всего здесь четыре мотора — протяжка бумаги, перемещение каретки, выставление нужной литеры и перемотка печатающей ленты. Драйверы моторов простейшие — четыре силовых ключа, по два мотора на каждый из 8-битовых каналов. Никакого STEP/DIR и микрошага — обмотки приходится переключать вручную, для чего в ROM даже лежат уже готовые к отправке в порты байты.
Клавиатура тут большая — всего 89 клавиш, схематически разбитых на алфавитно-цифровую область и на область функциональных клавиш.
Микросхема PIO.cs5 на канале А активирует одну строку, а микросхема PIO.cs4 считывает состояние столбцов. Канал А отвечает за опрос функциональных клавиш, канал В — за алфавитно-цифровую область. Канал B микросхемы PIO.cs5 нам дополнительно интересен как отвечающий за сигналы приема и передачи на магнитофон. Еще пара линий уходит на драйвер шаговых двигателей, а схема на резисторах, dip-переключателях и 8-входовом И — используется для настройки скорости работы последовательного порта. Ни в оригинале, ни на новой плате эти элементы не запаяны, прошивку я не анализировал и узнал об этом в самый последний момент, найдя еще один документ.
Последние два чипа — контроллер последовательного интерфейса SIO.cs7 и микросхема таймер CTC.cs6 — отвечают за порт RS-232 с полной поддержкой контроля потока — сигналов DCD/DTR и CTS/RTS. Один из каналов таймера явно тактирует схемотехнику последовательного порта, но в CPU прерывания от него уже не заходят, только от самого SIO. Необходимые для работы COM-порта ±12 В формируются на материнской плате очень сложной схемой делителя-удвоителя.
Сам процессор, U880, представляет собой 40-ногую микросхему с однополярным питанием +5В, с раздельными 16-разрядной шиной адреса и 8-разрядной шиной данных. Также, у него имеются дополнительные выводы:
Вход CLK, куда мы логично получаем тактирование как самого процессора, так и периферии. Исходная частота кварцевого резонатора 9832 КГц, но она сразу делится до 2458 КГц.
Вход RESET, в активном состоянии которого все регистры процессора и его состояние сбрасываются в нули, а после снятия сигнала начинается исполнение инструкции по адресу 0x0000h. Микросхема RAM при этом не очищается. При старте прошивки очистка памяти тоже не происходит. На плате можно установить батарейку, чтобы информация в памяти сохранилась аж до трех недель. Это позволяет не бояться отключения электроэнергии — вы не потеряете набранные тексты.
Выход M1, активный сигнал на котором означает, что процессор в данный момент занимается выборкой инструкции. Если в это время активен еще и сигнал MREQ, значит, процессор считывает инструкцию из RAM.
Выход IOREQ активен, когда процессор обращается к периферийным устройствам. Выводы RD, WR позволяют определить направление данных — от процессора или к нему.
Вход INT используется для получения уведомления о прерывании от периферии. У Z80 довольно богатый функционал по организации прерываний. В нашем случае используется векторный режим: предварительно в каждую микросхему периферии записывается номер вектора от 00h до FFh, и при получении сигнала прерывания этот вектор запрашивается процессором назад. Вместе с I-регистром, в котором зашит старший байт адреса (40h для машинки), мы получаем адрес указателя, куда надо прыгнуть процессору для обработки прерывания. Например, для прерывания Sys_Timer_ch0 вектор прерывания 48h: и по адресу 4048h лежит адрес обработчика прерывания, 9D5h.
Вход WAIT используется для остановки работы процессора извне. Например, чтобы подождать медленную периферию (чем пользуется последовательный порт) или же реализовать пошаговую отладку.
Выход HALT показывает соответствующее состояние процессора, из которого мы выйдем только по прерыванию или по сбросу. В обычном режиме машинка в него свалиться не должна.
Питание подано, тактирование на CLK имеется, фронты хорошие, RESET снят, сигнал HALT также отсутствует. На шине адреса и шине данных кипит жизнь, процессор активно что-то выполняет, как вдруг…
…я замечаю, что линии адреса на ROM идут слегка не по порядку, а значит, и инструкции будут запрашиваться совсем не те, что мы ожидаем. Можно, конечно, переложить байты в бинарнике согласно новой разводке, а можно за 10 минут сделать хард-фикс паяльником. История этого косяка такова, что подключение шины адреса к RAM в исходной плате сделано вразнобой, исходя из простоты разводки, Это свойство перекочевало и в новую схему подключения RAM, а потом Ctrl+C/Ctrl+V — и вот мы уже перерезаем дорожки и кидаем перемычки.
Подключаемся 16-канальным логическим анализатором к шине данных процессора и наблюдаем первые инструкции:
Что-то работает! Сразу после сброса выполняется инструкция по адресу 0h. По шине данных в процессор приходит три байта 31h, 40h, 40h — инициализация стека. Он находится по адресу 4040h и растет вниз, до 4000h.
Следующая инструкция FFh — rst 38h. Не думайте, что это сброс, это просто CALL размером в один байт вместо трех — все для экономии места памяти программ. Rst точно так же кладет в стек указатель возврата. Посмотрим, что там:
Check_debug:
ld a, a
push af
ld a, (Debug_code)
cp 52h ; 'R' ; if Debug Code is started with R
jr z, Run_debug
ld a, i
jr z, loc_46
halt ; halt if I reg already set
loc_46:
pop af
ret
Run_debug:
pop af
jp Debug_routine
Ячейка DebugCode по адресу 0x3800h проверяется на наличие символа R. Если он будет найден, то мы прыгнем в функцию Debug_routine по адресу 3803h — в тот самый восьмой корпус ROM, расположенный в сервисной плате. Мне удалось найти фотографии таких устройств. Например, для машинки S6010 оно выглядит так:
Разъемом она подключается к шине процессора и имеет существенно больший контроль над системой, чем микросхемы на плате. Согласно сервисному мануалу на S6010 (стр. 7), на устройстве хранится прошивка для тестирования всей периферии. Оно использовалось либо выходным ОТК завода, либо сервисной службой.
Этой платы у нас нет, да и временные изменения схемы ChipSelect полностью передают полномочия над всей областью 0000h-3FFFh внутренней ROM. Так что мы вернемся обратно в адрес 0004h, где нас ожидает безусловный переход в адрес 19EFh. После него должен быть Call 1ee0h, но его нет: процессор запрашивает только один байт из 19F0h и делает прыжок на 19F5h, пролетая мимо нужной мне инструкции. Внимание, вопрос: как, при наличии 16-канального логического анализатора мониторить одновременной 8 линий данных, 16 линий адреса, не забывая про служебные линии? Никак.
Благо удалось дешево купить 32-канальный логический анализатор Hantek 4032L, хотя я уже успел придумать проект 64-канального анализатора на плате RV901T и заказать ее…
Для работы с анализатором я настоятельно рекомендую OpenSource проект sigrok.org — он поддерживает несколько сотен различных устройств и предоставляет мощный интерфейс для работы. А главное — декодеры многих популярных процессоров! Дабы не сойти с ума и каждый раз не накидывать на процессор 32 мелких крокодила, я изготовил очень простой переходник с диагностического разъема на IDC40 с распиновкой как у анализатора. Запускаем PulseView, именуем каналы, добавляем декодер процессора Z80 и собираем трассу:
Все шевелится, и мы получаем полную трассу инструкций, приправленную данными с RAM и периферии! Как человек, который 6 лет работал в Intel над поддержкой Processor Trace в различных пользовательских сценариях, я был счастлив и печален одновременно. Счастлив — потому что я точно вижу, что делал процессор в то или иное время. Печален — потому что вьетнамские флешбеки, вот почему.
Сохраняем выход с декодера в текстовый файл, применяем немного магии питона, который оформляет данные в трассу следующего вида:
1057881: 0000 31 40 40 LD SP,4040h
1057891: 0003 FF RST 38h PUSH 0004 -> 0x403F
Call Check_debug@0038 | Unknown -> Stack
1057902: 0038 7F LD A,A
1057906: 0039 F5 PUSH AF PUSH 0054 -> 0x403D
1057917: 003A 3A 00 38 LD A,(3800h) op: 0x3800(FF)
1057930: 003D FE 52 CP 52h
1057937: 003F 28 07 JR Z,$+9
1057944: 0041 ED 57 LD A,I
1057953: 0043 28 01 JR Z,$+3
1057965: 0046 F1 POP AF POP 0054 <- 0x403C
1057975: 0047 C9 RET
Return from Check_debug@0038 | Stack -> Unknown
1057985: 0004 C3 EF 19 JP 19EFh
1057995: 19EF CD E0 1E CALL 1EE0h PUSH 19F2 -> 0x403F
Call Setup_all@1EE0 | Unknown -> Stack
1058012: 1EE0 CD 57 02 CALL 0257h PUSH 1EE3 -> 0x403D
Call IOnVarInit@0257 | Setup_all@1EE0 -> Stack
1058029: 0257 21 28 02 LD HL,0228h
1058039: 025A CD 8A 02 CALL 028Ah PUSH 025D -> 0x403B
Call IO_WriteFromTable@028A | IOnVarInit@0257 -> Stack
Теперь у нас есть дамп трассы в удобном виде, который можно читать одновременно с дампом в IDA. Здесь инструкция CALL 1EE0h выполняется корректно, так как я уже сделал хардфикс поверх хардфикса линий адреса, перепутав местами два проводника.
Кстати, вы заметили что я отдельно трассирую вызовы функций и возврат из них? Сохраним их в формате Trace Events и скормим в perfetto.ui
Так-то лучше. Теперь наглядно видно, что мы закинули базовые настройки в периферию, а потом застряли в функции Setup и усердно опрашиваем блок функциональных клавиш, однако ни одна из них не дает результата. Смотрим ближе:
KeyboardReadColumn:
ld e, a
cp 10h
jr c, Kb_A_less_10h ; if A < 10h, jump
and 38h ; '8' ; A = A & (0b00111000) mask
rrca
rrca
rrca ; A = {00000, A[5:3]}
call BV_A ; B = (1<<A[5:3])
ld d, b ; D = B
ld a, e ; restore A value
and 7 ; Apply 00000xxx mask
call BV_A ; B = (1<<A[2:0])
ld c, 0Dh ; ColumnDataB
bit 7, e ; Status.Z = ~A[7]
jr z, loc_10E7 ; Jump if A[7] is Zero
ld c, 0Ch ; ColumnDataA
bit 6, e ; Status.Z = ~A[6]
jr z, loc_10E7 ; Jump if A[6] is zero
ld c, 11h ; RowDataB
ld a, b ; Move o1 <- o2
cpl ; A = ~(1<<A[5:3])
ld b, a ; B = A
loc_10E7:
push de
call GetKbColValue ; Arguments:
; B - value to port
; C - Selected input channel for read
; Return:
; D == A = ~KbCol(A or B).value
; Note: Interrupt Disabled if 40BDh.bit6 is set
pop de
bit 7, e ; if A[7] is set
jr z, loc_10F5 ; A = f(KbColA) & (1<<A[5:3])
loc_10F0:
bit 6, e ; If A[6] is set
jr z, loc_10F5 ; A = f(KbColA) & (1<<A[5:3])
cpl ; We are here only if A[7:6] == 11
; A = KbRowB
loc_10F5:
and d ; A = f(KbColA) & (1<<A[5:3])
ret ; Return: A - Value from port, Z flag if A is empty
b_A_less_10h:
rlca
rlca
rlca
rlca ; A <<< 4
ld d, a
in a, (Keyboard_Col_Data_A) ; Input from port to A
cpl ; A = ~KbColDataA.{BE,Tygs,Paper,SP24}
and d ; Apply mask from D
ret ; Return: A - Value from port, Z flag if A is empty
Аргумент кладется в регистр А. Если он меньше 10h, то мы формируем из него маску вида xxxx0000 и накладываем на инвертированный результат чтения из порта Keyboard_Col_Data_A. Там висит несколько датчиков. Если при наложении маски ничего в регистре А не останется, мы получим вожделенный флаг Status.Zero. По трассе видно, что в А записано число 99h, следовательно, нам надо идти по основной ветке.
С помощью двух старших битов регистра А мы выбираем, куда будем писать — в ColumnDataB(0x), ColumnDataA(10) или же RowDataB(11). Функции BV_A делают очень простую операцию B = (1<<A) — это маски записи A(00000xxx) и чтения А(00ххх000). Регистр А содержит номер строки и столбца для опроса, а также канал, то есть обозначает одну-единственную кнопку для считывания.
Все, что нам осталось, это понять, что нулевой флаг будет тогда, когда замаскированный бит выставится в ноль. В нашем случае А = {2’b10 3’b011 3’b001} — выставляем мы вторую строку клавиатуры и опрашиваем четвертый столбец группы A.
Что у нас там по схеме? Концевой выключатель крышки. Разумеется, я безрезультатно пробовал его переключать на запитанной машинке. А вот если его замкнуть и потом включить машинку, то… каретка идет на парковку. Всего каких-то 6 дней, новый логический анализатор и дизассемблирование прошивки, чтобы выяснить что машинка отказывается стартовать с открытой крышкой… Другой вопрос, почему она отказывается стартовать, когда крышка закрывается?
Как минимум я теперь бегло читаю опкоды Z80 и распарсил заметную долю прошивки. Собираем новую трассу бута, но с откинутым концевиком.
Ай какая красота! За 4 секунды работы машинка сначала перемещает каретку влево до упора, потом вращает ромашку с литерами в нулевое положение до срабатывания датчика, а в финале возвращает каретку на начальный отступ, после чего ожидает команды с клавиатуры. Кроме того, мы явно видим, что UART контроллер получает настройки, а потом активно опрашивается!
Тем временем я добрался до инструкции и начал печатать всякое. И все работает! Особенно после очистки соленоида и установки новых пятаков на пленочную клавиатуру. Машинка официально ожила после 8 лет простоя только лишь у меня — это не считая еще пары-тройки лет забвения до.
У «Роботрона» есть два способа связи с внешним миром. Первый — выход на магнитофон. Можно набрать любой документ и записать его на магнитофонную ленту. В любой момент спустя день, неделю или год можно загрузить текст с ленты обратно в машинку и напечатать его вновь, символ в символ.
Второй интерфейс — RS-232. Он также упоминается в документации, но изначально на плате он не был распаян, да и в меню MOD не отображается. Однако еще при самом первом изучении дампа в далеком 2019 году код работы с интерфейсом был найден, а полученные выше трассы показали, что микросхемы SIO и CTC даже получают настройки! Значит, режим просто сделали недоступным для пользователя.
Текущая схема получения трассы из Z80 выглядит так: к диагностическому разъему подключен логический анализатор, в PulseView отображаются сигналы и работает декодер инструкций Z80. Их мы сохраняем и прогоняем уже через свой питоновский скрипт.
Из IDA Pro экспортируем адреса функций, дабы отобразить их в Perfetto UI. Эту схему можно значительно улучшить, взяв консольный sigrok-cli, и, объединив функционал встроенного декодера с моим, сразу получать готовые дампы и трассы для вьювера без множества ручных операций. Но потом.
Активно входим в меню и выходим из него обратно, параллельно собирая новую трассу. Во вьювере мы видим пару областей, похожих на то, что в эти моменты мы были внутри контекстного меню. На дисплей все также отправляется 16 байт информации, но теперь вместо пустых 12 символов F0 подряд их 4 — а нам бы хотелось иметь только одно пустое место.
Согласно дампу трассы, данные в порт отправляются с адресов 40AC-40BB:
LD A,(HL) op: 0x40AF(0F) <---
CPL
OUT (04h),A OUT F0 -> 0x0004: Display_Data_A
...
LD A,(HL) op: 0x40AE(0F) <---
CPL
OUT (04h),A OUT F0 -> 0x0004: Display_Data_A
...
LD A,(HL) op: 0x40AD(0F) <---
CPL
OUT (04h),A OUT F0 -> 0x0004: Display_Data_A
...
LD A,(HL) op: 0x40AC(0F) <---
CPL
OUT (04h),A OUT F0 -> 0x0004: Display_Data_A
Так как это RAM, выше по трассе ищем, кто туда записывает значения. Находим два места — либо с 4191h-419Ch, либо при вызове меню MOD с 419Dh-41A8h, что тоже RAM. Следующая итерация поиска приводит нас к области ROM F5h, из которой копируется 7 байт данных. Из них три штуки 0F — пустой код.
LD HL,00F5h
LD BC,0007h
LDIR 0x00F5(BC) -> 0x419D(BC)
LDIR 0x00F6(03) -> 0x419E(03)
LDIR 0x00F7(B8) -> 0x419F(B8)
LDIR 0x00F8(88) -> 0x41A0(88)
LDIR 0x00F9(0F) -> 0x41A1(0F) <--
LDIR 0x00FA(0F) -> 0x41A2(0F) <--
LDIR 0x00FB(0F) -> 0x41A3(0F) <--
На дисплейной плате расположено небольшое ROM размером 2 Кб. Считываем с него прошивку, и каждые 8 байт подряд превращаем питоном в символ:
Коды BCh, 03h, B8h, 88h как раз соответствуют уже отображаемым иконкам. Нам же нужны символы BBh и BAh. Дополнительно выведем и символ смены языка 94h, хоть у нас и нет других ромашек. Табличка, к слову, поддерживает и русский, и немецкий — c ее помощью вполне удастся напечатать на экране какое-нибудь straße.
Модифицируем прошивку, прошиваем (использую для этого TL866 Plus II) и проверяем:
Успех! Но мы лишь на полпути, так как на нажатие соответствующих клавиш машинка не реагирует. Соответственно...
Возвращаемся обратно в трассу и смотрим, что там по опросу клавиш. Мы ведь помним, что вызов KeyboardReadColumn(A) при активных старших битах А опрашивает ровно одну кнопку, индекс которой закодирован в значении регистра.
Будучи в меню MOD, мы опрашиваем ровно четыре клавиши! Справа есть какой-то непонятный мусор, который привлекает мое внимание. Вызваниваем клавиатуру и убеждаемся, что коды 88h, 89h, 90h и 8Bh соответствуют клавишам «удар», «шаг», «картридж» и «выравнивание справа». Этот список грузится с адреса 41B0h по три байта на символ, где первый — код кнопки, а два других — 16-битный адрес обработчика кнопки. Так как адрес опять лежит в области RAM, ищем, кто туда положил значение, — и находим, что 21 байт данных, то есть 7 кнопок, копируются с региона E0h:
LD HL,00E0h
LD BC,0015h
LDIR op: 0x00E0(88) op: 0x41B0(88) <-- Код кнопки
LDIR op: 0x00E1(4A) op: 0x41B1(4A)
LDIR op: 0x00E2(18) op: 0x41B2(18) <-- Обработчик кнопки по адресу 184Ah
LDIR op: 0x00E3(89) op: 0x41B3(89)
LDIR op: 0x00E4(6F) op: 0x41B4(6F)
LDIR op: 0x00E5(18) op: 0x41B5(18)
LDIR op: 0x00E6(90) op: 0x41B6(90)
LDIR op: 0x00E7(8D) op: 0x41B7(8D)
LDIR op: 0x00E8(18) op: 0x41B8(18)
LDIR op: 0x00E9(8B) op: 0x41B9(8B)
LDIR op: 0x00EA(F1) op: 0x41BA(F1)
LDIR op: 0x00EB(18) op: 0x41BB(18)
LDIR op: 0x00EC(00) op: 0x41BC(00) <--- 8Ch UART Receive
LDIR op: 0x00ED(E6) op: 0x41BD(E6)
LDIR op: 0x00EE(18) op: 0x41BE(18)
LDIR op: 0x00EF(00) op: 0x41BF(00) <--- 9Dh UART Send
LDIR op: 0x00F0(C8) op: 0x41C0(C8)
LDIR op: 0x00F1(18) op: 0x41C1(18)
LDIR op: 0x00F2(00) op: 0x41C2(00) <---
LDIR op: 0x00F3(A4) op: 0x41C3(A4)
LDIR op: 0x00F4(18) op: 0x41C4(18)
И тут бросаются в глаза подозрительные три нулевых байта. Прописываем сюда правильные опкоды кнопок (правда, кнопку «К» вызвонить не удалось), загоняем новую прошивку в ROM и... Мы вошли в меню! Победа!
Но нет, RS-232 все еще не работает. Вы же еще помните, что машинка не стартовала с открытой крышкой?
В первые дни дебага я обнаружил, что настройки в системный таймер приходят, однако сигналов прерывания от него нет. Я поменял местами микросхемы CTC, запросы прерывания появились, но машинка все равно не стартовала... И вот тут я уже дошел до датчика открытой крышки.
Теперь дохлый СТС стоит… на последовательном порту. Настройки в него приходят, однако сигнала тактирования он не выдает! Дежавю: я успел забыть про перестановку микросхем! Мертвый чип, к слову, родной, с оригинальной платы. Видимо, его тоже сожрало кислотой от батарейки. Выпаиваем таймер еще с одного донора, меняем — и на ножке TO появились импульсы! Анализируем отправляемую в SIO конфигурацию:
Usart_ctrl_A:
db 18h ; WR0:
; Channel Rst
db 4 ; WR4:
db 40h ; Parity disable
; Sync modes enable
; 8 bit sync character
; X1 Clock mode
db 1 ; WR1:
db 18h ; INT On All Rx Characters
db 5 ; WR5:
db 28h ; Tx Enable. 7bit character
db 3 ; WR3:
db 41h ; Rx Enable
; 7bit character
Эм, что еще за синхронный режим такой? Меняем 40h на 44h, включив привычный нам асинхронный режим с одним стоп-битом, еще раз обновляем прошивку на машинке, подключаемся к ней с помощью полноценного нуль-модемного кабеля, выставляем 110 бод, 7 бит, 1 стоп-бит, контроль четности отсутствует, и… поехали.
Одна досада: машинка либо получает информацию с удаленной машины, либо отправляет ее. Активировать режим терминала, включив оба режима одновременно, у меня не вышло. Еще несколько напрягает разница кодов символов — например, маленькие буквы э, ш и щ на машинке имеются, но при передаче отправляются коды 07h, 02h и 0Ah, при получении которых обратно происходит совсем другое. Если отправить в машинку все коды 00h-FFh, буквы э, ш и щ все еще остаются недоступными. Исправить это можно, скорректировав табличку кодировки. Она очень похожа на КОИ7-Н1, но при ее наполнении что-то пошло не так. У машинки есть Escape-последовательности, например включение подчеркивания делается командой 1Bh 52h. Может, буквы спрятаны где-то там?
Целое десятилетие машинка бороздила бескрайние просторы загробного мира, но теперь она официально ожила, причем с большими возможностями чем при выпуске с завода в 1989 году!
Но что дальше? Не мешало бы оживить и «Элему». Прошивка для нее тоже есть в репозитории, равно как и схема, и инструкция по эксплуатации. Уверен: по схожему маршруту, да с разработанным инструментарием это можно сделать быстро (флейм-чарт в Trace View — потрясающий инструмент). Посадочное место под последовательный порт на материнской плате «Элемы», к слову, тоже имеется.
Что до «Роботрона», то у него все еще остались слабые места вроде чрезмерного нагрева снаббера в источнике питания и очень странного перегрева некоторых элементов в драйвере моторов. Я бы не стал пока оставлять машинку надолго без присмотра с такими проблемами, но фестиваль Chaos Constructions 2025 уже анонсировали, так что я еще вернусь…