Громов постучал тупым концом карандаша по колонке цифр. Широкая простыня распечатки с результатами утренней трансляции занимала половину выцветшего канцелярского стола. Рядом с перфолентой лежала распечатка Ланской и набросанная от руки карта адресного пространства «Сферы-82».
В бывшей кладовке, заставленной тяжелыми стеллажами из стального уголка, густо пахло свежемолотым зерном. Ручная мельница примостилась на самом краю столешницы. Морозов налил себе кофе. Громов плеснул в кружку молока — стеклянная бутылка как раз согрелась в эмалированном ковшике с горячей водой.
— Транслятор Беспалова отработал, — Громов сдвинул край распечатки. — Базовые структуры собрались в объектный модуль без ошибок. Но посмотри на итоговый объем.
Громов с нажимом обвел итоговую сумму.
— Шесть байт на каждое прямое обращение к массиву. Восемь байт на элементарный сдвиг указателя, — Громов ткнул пальцем в колонку ассемблерного листинга, который компилятор выдал на экран. — Смотри сюда. Транслятор языка Си писался под архитектуры, которые умеют аппаратно работать со структурами. А наш К580 этого не умеет, у него нет базово-индексной адресации. Чтобы сдвинуть указатель на следующий прямоугольник, твой хваленый компилятор не использует простую команду инкремента. Он кидает адреса в стек, делает вызов внешней арифметической подпрограммы, вычисляет смещение, потом вытаскивает результат обратно…
Громов раздраженно отпил молока.
— Программа переводит твой синтаксис, но если я пущу этот сгенерированный код в плотный цикл на проверку десяти тысяч пересечений, процессор просто захлебнется в пересылках стека. Машина встанет колом.
Морозов сделал глоток. Кофе получился отличным — крепким, без малейшей горечи.
— Зато этот текст скомпилировался за две минуты, и логика проверки прозрачна как стекло, — парировал Алексей. — Женя, мы не можем писать весь «СЛОЙ» на чистом ассемблере, мы утонем в отладке. Правило простое: мы пишем общую структуру на Си. Ищем узкое место — ту самую функцию, которая сжирает девяносто процентов времени на пересечениях. И вот ее, одну-единственную, ты переписываешь вручную на идеальном ассемблере, подменяя стандартную библиотеку компилятора.
Громов нахмурился, обдумывая этот подход. Комбинирование языков было не самой привычной практикой, но оно решало проблему сроков.
— Допустим. Вынесем математику пересечений в ассемблерный модуль. Но оперативной памяти он нам не прибавит.
— Значит, новые машины надо собирать сразу с полным адресным полем, — сказал Морозов. — Выжимать все шестьдесят четыре килобайта.
— Текущий живой стенд работает на тридцати двух, — напомнил Громов. — Новая полная карта — это дополнительные банки памяти, старшие адресные линии, усложненная дешифрация и увеличенная нагрузка на шину. Нам нужна абсолютная стабильность и одинаковость всех новых плат. Транслятор генерирует объемный код, а мы еще ни одной координаты с гибкого диска не считали. А дисковод ИЗОТ, между прочим, читает сектора не со скоростью света.
— Файл Каца придется читать порциями, — Алексей поставил кружку на стол.
— Файл Каца — это просто свалка координат, — отрезал Громов. — Чтобы читать его порциями по оси X, как ты хочешь, данные должны быть отсортированы слева направо. А топологи так не рисуют. Значит, до твоей красивой проверки на Си мне придется написать грубый сортировщик. Она будет заглатывать сырой массив с дискеты, сортировать прямоугольники в памяти и писать обратно на диск временные файлы. Мы упремся в физику магнитных головок.
Морозов кивнул, признавая проблему.
— Си удобен для описания структур данных, таблиц и общей логики, Женя. Нам проще оперировать терминами: слой, зазор, контактная площадка. Язык берет на себя рутину: мы видим размер записи и обращаемся по указателям, не высчитывая физическое смещение адреса в уме.
Громов скрестил руки на груди.
— Критичные участки, буферы, побайтовый ввод-вывод — это ассемблер, — подтвердил Морозов. — Куски, где важен каждый байт. Но общая структура «СЛОЯ» и форматы обмена остаются на Си.
— На бумаге всё выглядит красиво: общая логика на Си, узкие места на ассемблере, — протянул Громов. — А неприятности начнутся на стыке. Там, где пойдут вызовы функций, работа с памятью и соглашения о передаче параметров.
Морозов придвинул к себе распечатку Ланской. Страница пестрела карандашными эскизами печатных проводников. На мелкой сетке миллиметровки пересекались два прямоугольника.
— Давай от споров к рабочему примеру, — Морозов обвел ручкой узкую светлую полоску между фигурами. — Строгое зеленоградское правило. Минимальный допустимый зазор между двумя трассами на этом слое — две десятых миллиметра.
Он вывел на полях цифры «0,20».
— Плюс технологический запас на боковое подтравливание меди в ванне, — добавил он. — Три сотых. Итого — ноль двадцать три.
Морозов записал сумму рядом с эскизом.
— Нам нужно превратить это правило в числовую таблицу, — Морозов перевернул страницу. Ручка заскользила по бумаге, выстраивая колонки. — Первый байт — номер слоя. Второй байт — код проводимой проверки: замыкание, обрыв или нарушение зазора.
Громов наклонился ближе к столу.
— Дальше переводим миллиметры в целые единицы координатной сетки, — диктовал Морозов. — Дробная арифметика здесь не нужна. Это еще два байта.
— Четыре, — подсчитал Громов.
— Один байт на запас по травлению. Один байт — битовая маска свойств: флаг метки совмещения, признак контакта, маркер цепи питания.
— Шесть.
— И два байта под номер исходной строки из зеленоградского файла. — Морозов дважды подчеркнул последние цифры. — Иначе, найдя ошибку, мы не сможем объяснить топологам, где она находится на ватмане.
— Восемь байт на запись, — прикинул в уме Громов. — Тысяча правил уложится в восемь килобайт ОЗУ.
— Именно.
— Вполне приемлемо, — кивнул Громов. — Но это статический справочник. В реальной работе мы получим стопку гибких дисков с десятками тысяч координат. Причем мы понятия не имеем, в каком порядке зеленоградские операторы вбивали эти отрезки.
Он придвинул к себе чистый лист.
— Потребуется первый проход. Считываем текстовый файл построчно. Номер слоя, координаты, контрольное число. Мой модуль сверяет арифметическую сумму кодов с числом Каца. Это гарантирует только то, что оператор не ошибся при наборе, но никак не сортирует данные.
Громов быстро набросал схему на бумаге.
— После проверки строки придется переводить текст в двоичный формат и раскладывать записи. Группировать по слоям, строить компактный индекс в памяти. Здесь работает чистый ассемблер.
— А проверка геометрии? — спросил Морозов.
— Вычисляем прямоугольник охвата для каждого отрезка: минимальные и максимальные значения по осям. Точную математику запускаем только при пересечении рамок. Сверяем с таблицей правил. Нашли нарушение — формируем запись в журнал.
Морозов кивнул. Он провел длинную горизонтальную линию под схемой Громова.
— Проблема в масштабах топологии, — произнес он. — Платы огромные, записей сотни тысяч. Даже в полные шестьдесят четыре килобайта целый слой со всеми деталями не влезет. Значит, мы обязаны резать проверку на координатные окна.
Морозов отметил на линии два штриха.
— Берем узкое окно. Например, по оси икс от тысячной координаты до тысячи четырехсотой. Грузим отрезки, проверяем. Но резать встык нельзя.
Он нарисовал два параллельных отрезка, пересекающих отметку «1400».
— Разорвем проводник, — кивнул Громов, постучав карандашом по нарисованной границе. — Программа проверит левую часть, потом правую, а замыкание прямо на линии разреза пропустит.
— Значит, нужно перекрытие, — Морозов забрал карандаш и добавил к чертежу еще одну вертикаль. — На величину максимального зазора.
— Именно. Грузим окно от тысячи до тысячи четырехсот. Но реальную проверку ведем только до отметки тысяча триста восемьдесят.
— Оставшиеся двадцать единиц уходят в буфер, — Морозов провел тонкую черту под колонкой цифр.
— Мертвая зона, — согласился Громов. — При следующем проходе загрузка стартует не с тысячи четырехсот. Мы откатываемся назад и захватываем этот край заново. Да, машина прожует границу дважды, тратя машинное время, зато мы не упустим контакт на стыке.
Он сделал короткую запись в рабочей тетради: «Двойной буфер на краях. Откат».
Дверные петли коротко скрипнули. В комнату шагнул Птицын, постукивая указательным пальцем по пустой фаянсовой кружке.
— Сетку прогнал, — с порога сообщил он, направляясь к раковине у противоположной стены. — Луч ровный, без перекосов. Цвет пока отложил, жду, когда видеовыход допаяют.
Пустив холодную воду, он сполоснул кружку, стряхнул капли и покосился на разложенные по столу листы с пересекающимися отрезками.
— Пойду собирать макет сопряжения, — Птицын перекрыл кран. — Слушайте, я хотел узнать: когда вы свой файл с геометрией читаете, как проверяете, что кусок пришел целым? У меня пакеты по кабелю иногда бьются из-за рассинхронизации. Автомат приема просто виснет, бесконечно ожидая хвоста данных. У вас там в конце блоков есть проверка целостности?
Громов, не отрываясь от таблицы технологических допусков, ответил:
— Мы обязательно проверяем, чтобы файл с данными не побился. Раз при чтении аппаратура может давать сбои, то у нас там контрольная сумма есть.
— Прямо как с твоим обменом, Саша, — Морозов отложил карандаш. — Сначала убеждаемся, что байты прочитались без порчи. А уже потом программа разбирает смысл: пересекаются трассы или нет.
Птицын оперся бедром о подоконник, разглядывая белое дно чистой кружки.
— Значит, нужно жестко отсекать испорченные пакеты, а не пытаться их склеить? — Птицын вытащил из кармана рубашки сложенный вчетверо листок бумаги с набросками автомата приема. — Я сейчас расписываю логику работы порта. А если на середине происходит помеха на линии, машина глотает мусор и застревает в цикле ожидания.
Громов оторвал взгляд от распечатки Ланской и посмотрел на измятый листок.
— У тебя автомат виснет, потому что ты пытаешься ловить голые байты в потоке, — пояснил Громов, отодвигая распечатку Ланской. — Заверни их в конверт. Установи стартовый байт, уникальный маркер, например, шестнадцатеричное «7E». За ним — байт длины пакета. Потом сами данные. И в самом конце — контрольную сумму. Аппаратный контроллер порта ВВ51 умеет ловить ошибки кадра. Если приемник видит сбой синхронизации, или итоговая сумма не сошлась — не пытайся угадать, что тебе прислали. Весь конверт немедленно летит в мусорную корзину.
Громов указал карандашом на бумажку Птицына.
— Брось рисовать эти бесконечные ветвления. В голове все переходы автомата при сбоях не удержишь. Сделай нормальный программный журнал обмена. Выдели область в памяти, выводи на экран каждое изменение статуса: «старт», «прием», «ошибка КС», «таймаут». И посади прерывание на аппаратный таймер. Если передатчик замолчал и следующего байта нет больше ста миллисекунд — печатай код ошибки и немедленно сбрасывай регистр приемника в исходное состояние. Сеть не должна вешать машину.
— И напиши короткий стресс-тест, — добавил Морозов. — Напиши программу, которая будет намеренно гадить в линию. Отправь себе заведомо искаженный байт, нарушь контрольную сумму или искусственно обрежь пакет на середине. Посмотри, как твоя программа из этого выберется. Отработает она сброс или снова намертво повиснет. Тебе нужен четкий протокол поведения при авариях, а не гадания на зависших регистрах.
— Понял, сделаю, — Птицын спрятал листок обратно в карман. — Журнал кодов сбоя и таймер сброса.
Он кивнул, на ходу вполголоса бормоча последовательность команд для инициализации порта, и присел на край соседнего стола.
Дверь снова скрипнула. На пороге появились Володя и Зоя. Зоя положила поверх чертежа бланк комплектовки, густо исписанный инвентарными номерами.
— А со склада микросхемы выдали на полную набивку, — пояснила она. — Вместе с блокировочными конденсаторами. Нам бы сразу решить, какой лист брать. Если по старой схеме оставим пустоты, а потом скажете допаивать — опять текстолит греть и грязь разводить.
Громов отодвинул стопку распечаток.
— Никаких пустот, запаивайте сплошным массивом, — распорядился он.
Морозов перехватил растерянный взгляд лаборанта. Володя смотрел на бланки комплектовки с легкой опаской.
— Новые машины собирайте сразу на полные шестьдесят четыре килобайта, — распорядился Алексей. — Нам под зеленоградский проект нужен весь объем, до последнего байта.
— Ру-третьи ставим? — деловито уточнил Володя, имея в виду микросхемы динамической памяти К565РУ3. — Это же тридцать два корпуса на плату. Плюс к ним мультиплексоры шины адреса КП2 и жесткая логика регенерации. Если всё это запаять вплотную, на текстолите живого места не останется.
— Именно их, — подтвердил Громов. — Тридцать два корпуса. И учти: динамическая память при стробировании строк и столбцов дает дикие иголки по питанию. Проследи, чтобы Зоя впаяла блокировочные керамические конденсаторы КМ прямо на ноги питания каждого второго чипа. Сэкономите на керамике — память начнет сыпать случайными ошибками от собственных же помех.
— Как закончите пайку, — добавил Морозов, — пройдитесь осциллографом по старшим адресам и сигналам RAS/CAS. Убедитесь, что выборка микросхем идет чисто, без звона на фронтах импульсов.
Володя кивнул, достал из нагрудного кармана шариковую ручку и сделал жирную отметку на полях журнала.
— Понял. Значит, запаиваем тридцать два корпуса динамики, мультиплексоры и глушим всё конденсаторами по питанию. Без пустых мест и экспериментальных каскадов.
— Какую версию платы ставить в документации для учета? — спросила Зоя, занося ручку над бланком комплектовки.
— Пиши «Ноль-три, базовая», — ответил Морозов. — Все последующие эксперименты с расширением памяти поведем отдельной веткой, когда программное ядро проверки слоев будет полностью отлаженным.
Володя выпрямился, закрывая журнал. Он оглянулся и глубоко втянул носом воздух. В тесной комнате, среди металлических стеллажей со списанными приборами и высоких стопок технической документации, густо и уютно пахло кофе.
— Знаете, — Володя смущенно улыбнулся, разглядывая прикрученную к полке массивную ручную мельницу с потемневшей медной воронкой. — У вас здесь запах точь-в-точь как у моего отца в ванной, когда он там лабораторию разворачивает. Красный фонарь горит, в кюветах вода поблескивает, мокрой фотобумагой несет, фиксажем… и кофе. Прямо один в один.
Птицын, до этого момента изучавший таблицу состояний линейного приемника, удивленно поднял голову от бумаг.
— У тебя отец фотограф? А откуда там кофе взялся?
— Любитель, — Володя переступил с ноги на ногу, прижимая к груди пухлый монтажный журнал. — Вечно что-то изобретает по вечерам. Одно время сильно увлекся экспериментами с тонированием снимков. Сначала шла обычная проявка, промывка, а потом он брал готовые влажные отпечатки и долго держал их в крепчайшем растворе кофе с пищевой содой. Добивался теплого коричневого оттенка. Картинка выходила очень контрастной, с глубокими темными тенями. Он там часами запирался, елозил пластиковым пинцетом по ванночкам. У нас тогда весь коридор насквозь пропах баритовой бумагой, кислым уксусом и кофейной гущей.
Громов хмыкнул, тяжело откинулся на спинку скрипучего стула и сцепил руки на затылке, разглядывая потолок.
— Шикарная идея. Именно этим я и займусь на пенсии. Если доживу.
Дверь снова приоткрылась. На пороге появилась Люба с пустой чашкой в руках.
— Доживёшь, куда ты денешься, — сказала она, ставя чашку на металлическую полку.
Громов потер затылок, усмехнулся и снова подался к столу. Он сгреб перфоленту и распечатку, глядя на Морозова уже без улыбки.
— Так, воспоминания в сторону. Мне нужен новый прогон. Дай мне окончательные допуски по зазорам, я пересоберу структуру данных и посмотрю размер кода. Нужно знать, сколько памяти это сожрет в реальности. Иначе мы просто не влезем.
Птицын похлопал по карману с набросками.
— Можно я после обеда займу машину на короткий прогон программы обмена?
Громов махнул ему свернутой лентой: сначала тест обмена, потом таблица прямоугольников, и чтобы результаты легли в одну папку, а не расползлись по тетрадям. Володя дождался, пока Зоя допишет номер версии, и шагнул к двери. Морозов придвинул к себе общую тетрадь Ланской и аккуратно перевел миллиметровый допуск технологической метки совмещения в целые единицы координатной сетки.