Categories

  • retrocomputing
  • ibm_pc_compat

Tags

  • retrocomputing
  • IBM PC та сумісні
  • плати оновлення

Трапився мені1 цікавий артефакт епохи. Як відомо, в кінці 20-го сторіччя виникла проблема – багато коду покладалося на двоцифрову дату. З одного боку, проблема була серйозна – що б там ‘‘нехтувачі’’ не казали, з іншого – як завжди, дуже перебільшена в медіа2. Детальніше див., наприклад, вікіпедію та статтю “What was the Y2K problem and what was the solution?”.

Опис

Фото взято тут.

Єдиний вміст плати – ROM, з модулем BIOS, адресу якого можна вибрати перемичкою3. Я випробував її на кількох комп’ютерах та дизасемблював код і поверхнево його проаналізував. Також, колеги підказали ще один такий пристрій з Західного ринку – його код теж (частково) дизасемблював.

Плата – української розробки. Скориставшись посиланням з інструкції, знайшов на вебархіві сайт виробника:

  • Кто мы есть: ‘‘*Группа компаний “Компьютеринтерсервис” - системный интегратор, имеющий семилетний опыт деятельности на рынке корпоративного заказчика. <пропущено> В 1999-ом году на базе "Комьютеринтерсервис" был разработан и внедрен в серийное производство контроллер "BIOS-Y2000 PS", который помог решить аппаратную часть "проблемы-2000". Технические характеристики контроллера обеспечили группе компаний получение государственного заказа. Общий объем реализации составил более 70 000 штук.*''
  • Див. також архів новин.

Крім того, засновник та директор Львівського Музею Комп’ютерних Технологій каже, колись пиячив з розробником, але зараз, на жаль, контактів з ним немає.

Аналіз коду

Ремарка: модуль BIOS починається з 0x55, 0xAA, далі 8 – кількість 512-байтових блоків у модулі. Має бути вирівняним на границю 2 Кб. З третього байту починається код. Системний BIOS сканує адреси від 0xC0000 до 0xF4000 (старих, по 0xE0000 на нових), з кроком 2 Кб, зустрівши таку сигнатуру – передає керування.

Зміщення коду плати відносно початку модуля – 0x100, більшість байт між початком модуля і 0x100 – нульові і не використовуються. Можливо, код генерувався як COM-файл – із ‘‘звичним’’ org 100h, а місця на мікросхемі вистачало, то й не заморочувалися…

Виглядає (подробиці див. далі), плата вважає – якщо рік менший, ніж 1980-й то насправді це 20xx, інакше – 19xx, і намагається повідомити про це RTC.

  • Код дивний, видається неохайним.
  • Містить помилки, наприклад, функція виводу літери на екран, згідно документації на BIOS, виводить літеру стільки раз, скільки вказано в CX, і він же використовується як лічильник циклу для виводу послідовних літер.
    • Тобто, першу літеру виводити стільки раз, скільки літер у стрічці, другу – на один раз менше тощо.
    • Однак, візуально функція працює – оскільки кожного разу переводить курсор на правильну позицію. Для перевірки скопіював у окрему програму – справді, спершу заповнює все речення першою літерою, потім, з другої – другою і так до останньої. Результат правильний, тільки це потребує багато більше часу.
  • Виглядає4, що покладається на century регістр RTC, вважаючи, що якщо він містить не 20 і не 195, то встановити 19.
    • Тому, коли я встановив 1979 рік, він став 2079. :-)
    • Потенційна проблема в тому, шо century регістр з’явився в RTC, власне, як частина пошуку рішення проблеми 2000-го року… Враховуючи, що автори “іноземної” плати покладалися на схожий принцип, певне мали підстави довіряти наявності його в RTC.
  • Перехоплює переривання 1A та 1C.
    • Під час ініціалізації робить щось дивне з alarm – можливо, налаштовує на оновлений час.
  • Для 1A реалізовує функції 0-7.
    • Також, якщо код операції AH=0xB1, яка відповідає запиту до PCI BIOS, стрибає до захардкодженої адреси 0F000h:0FE6Eh – не видається, що воно всюди працюватиме…
      • Колега по хобі каже, що працювало на материнській платі ECS SI5PI AIO (REV. 1.1), обладнаній PCI. Чи то ця адреса справді була поширена – але не гуглиться, чи то тим PCI BIOS на практиці не дуже користувалися? Якщо хтось знає, розкажіть!
      • Інша плата теж вважає, що за цією адресою є обробник int 1A.
    • Реалізація підтримуваних викликів int 1A, окрім операції AH=04, GET REAL-TIME CLOCK DATE, видається тривіальною – виконує відповідні звертання до RTC і все.
    • Для операції AH=04, GET REAL-TIME CLOCK DATE:
      • Читає сторіччя і рік з RTC.
      • Якщо рік не менший, ніж 0x80 (RTC працює в BCD, тому це ‘‘шістнадцяткове’’ число буквально зображає десяткове), перевіряє сторіччя. Якщо не 0x19 – встановлює відповідний регістр RTC в 19-те.
      • Інакше (рік – до 0x80), перевіряє, чи сторіччя – 20-те (і так, з цією платою вилізе проблема 2080 року :=). Якщо так – не робить нічого, інакше – встановлює 20-те і записує в RTC.
      • Після чого вичитує вміст RTC та повертає його.
    • Також, при вході в обробник 1A, перевіряє, чи обробник 1C не вказує в сегмент 0xF000 – ROM BIOS, і якщо так, відновлює його на свій.
      • Як на мене, сумнівне рішення.
  • Переривання 1C викликається за кожного виклику int 08 від таймера – за замовчуванням, 18.2 раз за секунду.
    • Його обробник, якщо лічильник (BIOS Data Area Daily Timer Counter – кількість тіків int 08 з півночі) дорівнює 0x0F, читає час і дату (лише, якщо час 00:00) – можливо, оновлюючи century регістр в процесі, за описаною вище процедурою.
  • Для затримки використовує якісь хитрі танці з PIT – не став розбиратися.
  • Містить стрічки: BIOS-Y2000 ProblemSolver. Version 1.77, Your BIOS & RTC are ready for New Year!, Product of Ukraine.
  • Проект IDA з дизасемблюваним кодом та моїми анотаціями можна завантажити тут. Оскільки дивився нашвидкоруч і лише для задоволення, там точно є помилки.

Випробування

  • Спробував на кількох платах з 286, однією 386, однією з Cyrix 486DX2-66 та однією з Інтелівським 486DX-100. А також, на XT-сумісній.
  • Ініціалізується на всіх.
    • Однак, на 286-х не виводить текст – тому, спочатку, я вирішив, що воно взагалі не працює.
    • Хоча в інструкції пише про призначення для комп’ютерів від 286 і новіших, не має ні команд 286-го, ні перевірки на його наявність, тому на XT запустилося – але, через відсутність RTC, не працює. Також, показала текст.
    • На інших повідомлення показує лише частку секунди – витратив багато часу, щоб змогти сфотографувати, тому фото перекособочені.
    • Ймовірно, справа у якихось помилках в конфігуруванні PIT для затримок. Перша думка була? що причиною є швидкодія, однак, цю гіпотезу заперечує видиме повідомлення на XT.
  • Намагаючись зрозуміти, чому “не працює” на 286-х, пошукав дебагером сигнатуру і виявив, що вміст модуля ROM є, але відображається не, як обіцяно, на 0xD8000, 0xD0000, 0xC8000, а на 0xDA000, 0xD2000, 0xCA0006, відповідно – на 0x2000 байт, 8 Кб, зсунуто.
    • Проаналізував схему – вона двошарова і зовсім проста, там жодної магії немає – мінімальний інтерфейс до ISA, тому, підозрюю, проблема в тому, що код на мікросхему ROM зашитий зі зміщенням 0x2000.
    • Або я помилково прочитав схему і десь там помилка – переплутано адресні біти.
  • На 486-х, якщо використовувати разом з PicoMEM, довелося їх розносити на протилежні краї – D8000 і C8000, якщо використати D0000 – не ініціалізувалися обидві. Може це якось пов’язане із тим, що мікросхема ROM – на 32Кб… Однак, на 286-х такого не було.
Впійманий вивід плати.
Відповідний BIOS в пам’яті, коли перемичка на D800.

Аналіз коду “конкурента”

Колеги по хобі підкинули ще одну плату – DRBIOS2000 Y2K BIOS Update Card.

Коробочка плати, фото з архіву.

Код справляє враження більш акуратного і більш консервативного, однак, ідейно покладається на такі ж рішення.

  • При вході тестує на 286, і відмовляється працювати на старіших процесорах.
  • Також, тестує багато чого іншого:
    • Чи вектор int 1A в сегменті системного BIOS (0xF000).
    • Перевіряє сигнатуру машини в BIOS, за адресою 0xF000:0FFFE7.
    • Також, перевіряє чи зміщення обробника якесь конкретне (0xFE6E), а якщо ні – чи там стрибок з опкодом 0xE9, ціль якого збігається з іншим розрахованим значенням з магічними константами.
  • Має два обробника int 1A – для AT і для PS/2. Останній я не аналізував.
  • В обробнику для AT підтримує команди від 0 до 7, як і наша плата. Але всі інші повністю ігнорує.
    • Реалізація, окрім функції 4 – тривіальна.
    • У ній дивиться, якщо сторіччя не 19 – не робить нічого.
    • Якщо ж ні, то виконує таку арифметику: ((century/16)+1)*16. Для 1Ah вона дає 20h – 20-те сторіччя в BCD.
    • Ідея в тому, що, якщо до 19h додати 1, отримується 1A і воно транслюється в 20h – BCD зображення 20-го сторіччя.
  • Не перехоплює int 1C і не намагається відновлювати свій вектор переривань – воно, певне, і на краще.
  • Містить такі текстові рядки: “Digital Research Technologies, Inc.”, “DRBIOS 2000 v1.10”, “Copyright 1991-1998 Micro Firmware, Inc.”, “Portions Copyright 1985-1998 Phoenix Technologies Ltd.” – остання вказує, звідки могла бути взята реалізація більшості реалізованих функцій int 1A.
  • Код виводу тексту теж видається дещо переускладненим, але явних багів не бачу.
  • Додає більше затримок під час роботи з RTC, ретельніше перевіряє, чи він не на стадії оновлення.
  • Проект IDA з дизасемблюваним кодом та моїми анотаціями можна завантажити тут. Оскільки дивився нашвидкоруч і лише для задоволення, там точно є помилки.

Не маючи фізично, (і не маючи натхнення повозитися з “донором” – умовною Ethernet платою з слотом для ROM), не випробовував. В цілому, хоча без системних випробувань сказати впевнено неможливо, видається, що українська плата могла бути більш функціональною і менш капризною – хай може й небезпечнішою, через меншу перевірку, на якій машині вона працює.

Фото та інструкція

Коробочка

Плата прийшла до мене в акуратній коробочці з доволі гарно зробленою інструкцією – наводжу фото далі. На жаль, вони доволі неякісні – але, боюся, інакше я цей пост ніколи не дописав би.

Передня сторона коробочки.
Задня сторона.
Боки.
Вигляд зсередини.

Інструкція

Посилання

Виноски

  1. Завдяки ретрокомп’ютерній спільноті. ↩

  2. Згадуємо тут зразу нещодавній коронавірус. ↩

  3. Дві мікросхеми 74-ї серії виконують допоміжну роль – одна допомагає декодувати адресу, інша – буферизує вивід. ↩

  4. Якщо я все правильно зрозумів. ↩

  5. RTC працює з BCD-числами – потрібно про це пам’ятати, аналізуючи. ↩

  6. Вказую адреси в байтах, зазвичай тут (як ось на самій платі) вказують ‘‘сегмент’’ 8086-го – без останньої цифри. ↩

  7. Зізнаюся, не зважаючи на купу прочитаної літератури, я чи то не знав, чи то зовсім забув про цю сигнатуру. Детальніше див. тут, стор. 71 та тут. Підсумовуючи ці джерела, 0xFB – XT v2, v3 (XT v1 – FE?), 0xFC – XT286, 0xFC – AT, FF – PC. Також, з BIOS-ів PCem, 0xFC чи 0xF8 – PS/1, 0xFC – PS/2 M30, 0xF8 – PS/2 M70, 0xFD – PCJr. ↩