Археология программиста
В прошедшую субботу решил провести «субботник» и наконец-то навести порядок на полках и в шкафах.
В старых коробках, среди всякого разного оборудования, разных плат, девелопер китов и пачек старых дисков обнаружил несколько интересных экземпляров устройств, просто настоящие артефакты древности. С этими артефактами связана целая история, которую и хотел бы рассказать.
Дело было так. В далеком-далеком году мы получили заказ на разработку прошивки для микросхемы Cypress CY7C63723. Это был такой PS/2-USB микроконтроллер. Задача: написать прошивку к этой микросхеме. Но был один нюанс. Микросхема предназначена для преобразования протокола из PS/2 в USB, а нужно было подключить два устройства PS/2, то есть нужно подключить PS/2 мышь и PS/2 клавиатуру с помощью одной такой микросхемы USB конвертера. Микросхема должна была бы устанавливаться на материнскую плату Jetway (если память не изменяет).
Точнее, дело было даже так: эта задача уже делалась каким-то разработчиком, но у него чего-то не получалось. Нам отдали его исходники и сказали, что нужно срочно починить и переделать, но чтоб работало. Кроме исходных текстов нашего неудачливого предшественника тогда мы получили вот это…
Это прототип конвертера Two-PS/2-to-USB, который и нужно было запрограммировать. На самом деле это только макет, микросхема потом устанавливалась прямо на материнскую плату, чтобы дать дополнительные мышь и клавиатуру. Предполагалось, что это будет материнская плата для двух пользователей одновременно, многопользовательский компьютер.
Эмулятор микросхемы Cypress CY7C63723! Такая маленькая микросхема и такой большой эмулятор. Такие вот технологии. Поскольку микросхема была одноразовая, с однократной прошивкой (OTP — One Time Programmable), то невозможно вот так просто взять и разработать прошивку. Нельзя сразу зашивать и пробовать — это слишком долго и расточительно. Поэтому, компания Cypress снабжала разработчиков вот такими странными устройствами — эмуляторами микросхемы. Скомпилировал программу, загрузил в эмулятор микросхемы, имитатор микросхемы через шлейф вставляется в панельку на плате будущего устройства — смотришь как работает. Вот сейчас не уверен, но кажется там даже внутрисхемный отладчик процессора был.
Вот такой нашелся универсальный программатор. К нему еще были пара десятков чистых чипов Cypress, но где уж они теперь?
Когда обнаружил эти железки в старых коробках появилось любопытство. Смогу ли я найти исходники тех программ? Как давно все это было?
Это сейчас все просто. У меня теперь каждый новый ноутбук имеет жесткий диск существенно больше, чем предыдущий. Получается, что покупаешь ноутбук и переписываешь на его большой диск весь старый маленький диск. Первый ноутбук имел 90 Гбайт, второй 320 Гбайт, а последний ноутбук 1 Тбайт. Опять же — сейчас есть «облака» и GIT. А тогда архивы хранились на CD дисках. Иногда записывали на всякий случай по нескольку дисков, чтоб наверняка.
Вот найти старые программы на старых дисках — вот это действительно сложная задача. Но — нашел! Датируется 2003 годом — это примерно 13 лет назад было. Ну и по случаю, мой совет дня: «Никогда не подписывайте диски фразой Current Versions».
Итак… Нас в команде на эту задачу было 2 человека, мы взялись со всем рвением, хотя не имели ни малейшего представления ни про USB, ни про микросхемы Cypress.
Чтобы описать суть происходящего, немного расскажу про микросхему CY7C63723. В этом микроконтроллере жили-были:
- 14-ти битный указатель на исполняемую команду PC (Program Counter);
- 8-ми битный аккумулятор, A;
- 8-ми битный индексный регистр, X;
- 8-ми указатель стека команд, PSP (Program Stack Pointer);
- 8-ми указатель стека данных, DSP (Data Stack Pointer);
И это почти все… ну конечно, разные способы адресации — это понятно. Писать нужно, естественно, на ассемблере. Конечно, внутри микроконтроллера еще есть всякая периферия, традиционная для микроконтроллеров: таймер, GPIO и прерывания от них, USB-функция и так далее.
Это я сейчас, рассматривая старую документацию на эту микросхему (нашел таки ее на дисках), вспоминаю этот проект и удивляюсь этому чуду. Чтобы вы в полной мере ощутили всю мощь микроконтроллера приведу фрагмент списка команд:
NOP — 4 такта
INC A — 4 такта
INC X — 4 такта
INC [EXPR] — 7 тактов
INC [X+EXPR] — 8 тактов
ADD A, EXPR — 4 такта
ADD A,[EXPR] — 6 тактов
ADD A,[X+EXPR] — 7 тактов
…
Несмотря на тактовую частоту микроконтроллера в 12МГц, на деле он оказался совсем не быстрым — все мегагерцы съели такты-на-команду.
Самое интересно, что мы смогли довольно быстро разобраться, как оно должно работать, быстро все починили, прошили первые чипы, испытали, и клавиатура и мышь работают, светодиоды на клавиатуре CAPS, NUM, LOCK — работают. Ура, все хорошо, все довольны и счастливы!
Но — нет.
Через неделю или две заказчик говорит, что не работает.
Пытаемся выяснять — ничего толком понять нельзя. Между нами и конечным заказчиком есть посредник. То есть, нам заказывает немецкая компания, а им заказывает тайваньская компания. Электронные письма с Тайваня прочитать почти нельзя. Они написаны на «чинглише» — это когда человек думает на китайском языке, а пишет по английски — с трудом поддается расшифровке. В конце концов договорились, что они нам пришлют программу в которой не работает наш PS/2-USB конвертер. Они говорят, что во всех программах работает, а конкретно в этой программе — нет.
Ну ладно — пытаемся выкачать их тестовую программу. 2003-й год, у нас еще используется диал-ап модем, соединение все время рвется, программа очень большая выкачать никак не удается. С какой-то там N-ной попытки выкачиваем… (барабанная дробь) Doom2. Но что? Почему? Как так?
Оказывается все очень просто: нужно нажимая часто на клавиши клавиатуры идти, бежать и прыгать и одновременно мышью вращать и стрелять. А мы так НИКОГДА НЕ ДЕЛАЛИ. Мы то ведь пробовали как все нормальные люди: попробовали клавиши клавиатуры — символы в редакторе набираются, потом попробовали мышь — ездит. Вот бывает же…
Пришлось полностью пересмотреть программу микроконтроллера. Теперь уже при детальном анализе стало понятно, что при одновременно летящих символах из обоих PS/2 устройств обработать сигналы DATA и CLK на двух портах чрезвычайно трудно. Не буду приводить здесь осциллограммы сигналов, их легко можно найти в интернете, хотя бы вот здесь marsohod.org/11-blog/56-ps2. Частота сигнала CLK в PS/2 может быть до 18КГц. Вроде бы очень низкая частота, но если посчитать, то получается совсем не весело. Соотношение частот PS/2 CLK и частоты процессора микроконтроллера: 12000000/18000=666 (о ужас). При средней длине команды 5 тактов получается, что между двумя фронтами PS/2 клока может исполниться не более 130 команд микроконтроллера. А у нас прерывания настроены на четыре сигнала двух портов PS/2: от DATA0, от CLK0, от DATA1, и от CLK1. Самый худший случай — когда фронты некоторых сигналов случайно совпадут — порты же асинхронные. Если подряд два прерывания от разных портов, то получится вообще на прерывание остается тактов по 60…
В общем, пришлось буквально учитывать длительность каждой команды. Приведу фрагмент ассемблерного кода обработчика прерывания, там на многих строках стоит сколько тактов уйдет на команду:
org 0d00h
snd_recv_tabl0:
jmp recv_startbit0
jmp recv_bit0
jmp recv_bit1
jmp recv_bit2
jmp recv_bit3
jmp recv_bit4
jmp recv_bit5
jmp recv_bit6
jmp recv_bit7
jmp recv_parity0
jmp recv_stop0
jmp send_startbit0
jmp send_bit0
jmp send_bit1
jmp send_bit2
jmp send_bit3
jmp send_bit4
jmp send_bit5
jmp send_bit6
jmp send_bit7
jmp send_parity0
jmp send_stop0
jmp send_stop1
rise1:
rise0:
pop a
reti
capture_a_isr:
; here we service interrupts from ps2 port 0
; we must make it as short as possible…
push a;[5]
iord port0;[5]
iord port0;[5]
rrc;[4]
jc rise0;[4]
mov a,[status0] ;[5]
asl;[4]
jacc snd_recv_tabl0;[7+5] — 44 ticks
;-------------------------------------------------------------------------------
recv_startbit0_:
inc [status1]
mov a, PS2_WD_TIMEOUT
mov [ps2_wd_b], a
pop a
reti
recv_bit0_:
recv_bit1_:
recv_bit2_:
recv_bit3_:
recv_bit4_:
recv_bit5_:
recv_bit6_:
recv_bit7_:
iord port1
rrc
rrc; now our strobed bit in flag C
mov a,[sh_in_lo_reg_p1]
rrc
mov [sh_in_lo_reg_p1], a
inc [status1]
pop a
reti
recv_parity0_:
inc [status1]
pop a
reti
recv_stop0_:
mov a,[flag_wait_fa_1]
cmp a,1
jz filter_fa1
mov a,[sh_in_lo_reg_p1] ;[5]
push x;[5]
mov x,[fifo_head_p1] ;[5]
mov [x+fifo_p1], a;[6] store our byte to fifo
inc x;[4] increment fifo head
mov a, x;[4]
pop x;[4]
and a,00fh;[4] fifo size is 16 byte?
mov [fifo_head_p1], a;[5] save new fifo ptr for future
filter_fa1:
mov a,0;[4]
mov [status1], a;[5]
mov [flag_wait_fa_1], a
mov [ps2_wd_b], a
pop a;[4]
reti;[8] — 63+44=107 ticks
nop
Конечно, из памяти уходят многие детали, многих подробностей я уже просто не помню. Знаю только, что глубоко переработанная программа заработала успешно даже в Doom2. Вот что подсчет тактов животворящий делает.
И знаете, что? Я даже сейчас смог откомпилировать эту свою старую программу, просто запустил BAT файл:
Интересно, что у этой истории было еще и продолжение. Коды PS/2 ведь конвертировались в HID коды (PS/2 протокол был сам по себе довольно сложный, смотри marsohod.org/11-blog/57-ps2proto). А когда заказчик из Юго-Восточной страны, то некоторые клавиши там не совсем такие как у нас.
Вот при археологических раскопках в нашем офисе нашел типичную клавиатуру тех времен — ее прислали нам для испытаний. Были и другие экзотические варианты, но не все сохранились. Помню была клавиатура с дополнительными клавишами катакана и хирагана — и что бы это значило? И заказчик требует сделать, чтоб они работали. А как они должны работать? Что должно происходить при их нажатии?
Делали так: брали два одинаковых компьютера и параллельно устанавливали на один ПК — английскую windows 2000, а на второй ПК — чистую японскую Windows 2000, все ОС брались из MSDN подписки. Два компьютера нужны, чтобы правильно отвечать на вопросы в диалогах при установке Windows — прочитать-то мы не можем и перевести так же не можем. Вот параллельная установка на двух ПК как-то спасала. Уже потом клавиатуру подключали к ПК с установленной японской Windows и нажимали эти спорные клавиши пытаясь понять, что должно происходить при их нажатии. Вот как-то так и решались проблемы. Как давно это было…
Ну и что получилось: в результате, разобрать на полках и привести их в порядок не удалось. Наоборот, разворошил все ящики и коробки в поисках иных ценных и интересных для археолога артефактов… Столько интересного нашлось…