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.