Аналіз показав, що 1.25 -- це збірна солянка із файлів OAK -- OEM Adaptation Kit, та знайдених Тімом Патерсоном текстів референтної реалізації IO.SYS, асемблера та конвертера з асемблера Z80 в 8086. Версія 2.00 -- OAK. При чому, 2.00 виявилася, швидше, зовсім не 2.00 а ближче до 2.11.
Якщо строго, версії до 3.2 Microsoft випускав лише як OAK -- OEM adaptation kits, потім -- як OEM-версія для дрібних виробників комп'ютерів, а в роздрібну торгівлю MS-DOS потрапив з версії 5.0. Тобто, купити MS-DOS 1.25 чи 2.00 не можна було -- тільки комп'ютер із адаптацією від виробника типу Compaq, Zenith чи Corona Data Systems.
Тобто, номер версії типу 1.25 -- з одного боку, версія від Microsoft, з іншого -- те, що ми б зараз назвали ревізією коду. Це видно із опублікованих джерельних текстів -- кожна версія має свій "commit message".
На жаль, наявні файли MS DOS 1.25 не містять джерельних текстів SYS.COM. Версія 2.11 містить, однак, під час дизасемблювань, "ванільну" -- від Microsoft, 2.11 я не знайшов: "Спроба аналізу SYS.COM з DOS 2.11". Розібрав детально лише попередню: "Аналіз SYS.COM з PC-DOS 2.10". Довелося як звертати увагу на зміни між версіями, так і на різницю інтерпретації коду мною і авторами.
До речі, те, що опубліковані тексти із 2.11, а не 2.00, підтверджується і порівнянням опублікованого коду з "Аналіз SYS.COM з PC-DOS 2.00" та "Аналіз SYS.COM з PC-DOS 2.10". А ось двійковий v2.0\bin\SYS.COM -- Vers. 1.71, який відповідає MS-DOS 2.00.
Дизасембл порівнювався із SYS.ASM + визначення із DOSSYM.ASM / DOSSYM_v211.ASM + файл із повідомленнями SYSMES.ASM.
На початку файлу є історія "коммітів" -- змін:
; SYS - Copies system programs IBMBIO.COM/IO.SYS and IBMDOS.COM/MSDOS.SYS
; 1.6 05/21/82 Added rev number message
; 1.61 06/04/82 Allow SYS to blank disk TimP at SCP
; 1.70 06/30/82 NON contiguous DOS allowed on 2.00 IBM. Allows SYS to
; 1.0 1.1 disks.
; 1.71 07/02/82 Put in CHDIRs to make sure everything done in root dir.
; 1.80 04/26/83 MZ make sys work in small machines; use full 2.0 system
; calls
; 1.81 07/22/83 ARR Added check in IBM version for valid FAT ID on
; destination because of IBM problem with SYSing to
; unformatted disks which are really formatted.
; Prints NoDest message for ridic IBM reasons, should
; have a better message.
Оскільки дизасембльована версія -- 1.82 (див. далі), доповню рядком про неї із DOS 3.3 OAK, який колись витік в мережу:
; 1.82 08/12/83 ARR ZIBOed again. Mark fails to check for errors on
; his file I/O. Results in SYS saying system
; transferred when it hasn't been.
Загальний опис
Звичайно, код організований зовсім по іншому, ніж писав я, дизасемблюючи.
Є три макроси, які визначають трішки різну поведінку:
IBMJAPVER EQU FALSE
IBMVER EQU FALSE
MSVER EQU TRUE
Обидві версії IBM використовують IBMBIO.COM та IBMDOS.COM, MSVER -- IO.SYS та MSDOS.SYS.
Японська версія виділяє буфер на два сектори, ймовірно у зв'язку із тим, що бут-сектор тих японських комп'ютерів складався із двох секторів (чи не для PC-98?). Відповідно, іншим є код запису та читання. Другий сектор в коді названо List sector. Ще одна помітна відмінність -- перевіряє диск викликом функції CHECK_TRAN, і якщо диск їй не сподобався, повідомляє "Destination disk cannot be booted". Я її поверхнево проглянув, виглядає, що вона дозволяє вважати бутабельним лише або жорсткий диск, або диск із медіа дескриптором 0xFB. Це, згідно "Design of the FAT file system", відповідає: "3.5-inch and 5.25-inch double sided, 80 tracks per side, 8 sectors per track (640 KB)".
Я дизасемблював версію, де IBMVER == TRUE. Від MSVER вона відрізняється, в першу чергу тим, що MSVER не читає і не змінює бут сектор, а обидві варіанти IBM -- записують його. варіант IBMVER перевіряє медіа-дескриптор, MSVER -- ні (про це сказано як новинку версії 1.81 у SYS.ASM). Проте, різницю я ретельно не аналізував -- це потребувало б суттєво більше часу, ніж я міг би виділити.
Щодо тогочасного MASM, yезвичним було, що адреса змінної не завжди виділяється квадратними дужками, як звично у сучасних асемблерах. Наприклад, те що автори писали як:
MOV CX,BadParmLen
для сучасного асемблера я написав:
mov cx, [InvalidParametSize]
Воно іноді плутає, оскільки незрозуміло, без перевірки, що значить ім'я -- копіювати в регістр безпосереднє значення чи звертання до пам'яті.
Зрозуміло, що ім'я жодної змінної чи функції я не вгадав :-)
Розташування даних перекладено на асемблер і лінкер -- в коді широко використовуються директиви сегментів.
Системні виклики здійснюються не числами а іменами: не "mov AH, 9" а "MOV AH, STD_CON_STRING_OUTPUT". Взагалі, в коді Microsoft десь використано символьні константи, десь -- магічні, а останні, особливо -- зміщення, десь десяткові, десь шістнадцяткові.
Порівняння
З коду зразу видно, що версії різні: код Microsoft містить текст "Vers 1.81", а код із PC DOS 2.10 -- "Vers 1.82".
Зразу стає зрозумілою загадка двох байт, 0x8A, 0x05, на самому початку файлу -- безпосередньо під стрибком до коду, які в коді не використовуються. Змінна оголошується як "DW OFFSET DG:BOOT" -- зміщення до буфера для збереження бут сектора, не має імені і оголошена лише для IBMVER. Дизасембльована версія містить копію бут сектора у потрібному місці, але як вона потрапляє туди, з коду Microsoft не зрозуміло -- це не EXTERN змінна абощо, просто буфер нулів. Гіпотеза -- стороння утиліта дивиться на байти перед стрічкою "Vers" і копіює boot-сектор туди після компіляції. Поверхневий пошук кодом не знайшов чогось подібного, але архів, все ж, неповний.
Справді, як я і припускав, існує інтервал версій, в яких утиліта функціонує -- 1.54..2.10 (не 2.11!).
Кумедно, що функцію, яку я назвав GetFileSizeDateTime -- описавши в назві, що вона робить, Microsoft назвала OpenFile. Файл вона справді відкриває... На жаль, загадка, чому вона двічі зберігає розмір, в коментах не описано. Мій код:
int 21h ; DOS 2+ - MOVE FILE READ/WRITE POINTER (LSEEK)
; AL = method: offset from end of file
; Returns: DX:AX = new file position in
; bytes from start of file
stosw ; Save lower size word. Twice...
stosw
mov ax, dx
stosw ; Save higher size word. Twice...
stosw
Їх:
INT 21h ; get offsets
STOSW ; save low part of size
STOSW ; save low part of size
MOV AX,DX
STOSW ; save high part of size
STOSW ; save high part of size
Відповідна змінна в коді Microsoft називається "32-bit length of BIOS":
BIOSInFH DW ? ; file handle of source BIOS
BIOSLenLow DW 2 DUP (?) ; 32-bit length of BIOS
BIOSLenHigh DW 2 DUP (?) ; 32-bit length of BIOS
BIOSTime DW 2 DUP (?) ; place to store time of BIOS write
BIOSOutFH DW ? ; fh of BIOS destination
DOSInFH DW ? ; file handle of source DOS
DOSLenLow DW 2 DUP (?) ; 32-bit length of DOS
DOSLenHigh DW 2 DUP (?) ; 32-bit length of DOS
DOSTime DW 2 DUP (?) ; place to store time of DOS write
DOSOutFH DW ? ; fh of DOS destination
Щодо алгоритму, першу відмінність, яку помітив -- додано код встановлення кореневої директорії цільового диску його поточною директорією та відновлення її після пошуку системних файлів. Виглядає, що опинитися не в тій директорії файли б не мали все рівно, (це потребує детальнішого аналізу), але могла статися даремна помилка їх пошуку, коли вони присутні.
Здивувало: версія 1.81 шукає, чи є якісь файли ("*.*") в каталозі, викликом 0x4E (Find_First), новим для 2.00, а 1.82 користується 0x11 (Dir_Search_First), викликом епохи FCB.
Цікаво, справді чогось повернули старіший виклик, чи це наслідок роботи розробників OEM? Для перевірки можна спробувати проаналізувати інші 2.11 версії від OEM -- їх є багато. Проглянув декілька, включаючи "2.12 [Compaq OEM]" та "2.13 (Zenith OEM) [Z-100]", у всіх версія 1.81. З іншого боку, 3.00 використовує виклик 0x4E. Ще з іншого, версія 1.81 з Compaq-DOS 2.12 таки використовує 0x11 (але має помітні відмінності від "ванільної").
В 1.82 додано трохи обробки помилок, дуже схожої на виявлену в версії 1.81 з Compaq-DOS 2.12, і якої немає в 1.81. Тому, оця Compaq-вська певне, правильніше мала б називатися 1.81...
Новіша версія має пару "трамплінів", оскільки короткі стрибки перестали дотягуватися:
loc27b_noRoomForSys:
jmp noRoomForSys
Пошук системних файлів трішки перетасовано, як частину обробки помилок, додано перевірку, а чи немає volume label як єдиного файлу -- тоді каже, що немає місця для системи. Певне, щоб відповідні файли завжди були перші в каталозі. Версія 1.81 не перевіряє цього. Перевірка ця відбувається з допомогою викликів FCB -- за відсутністю альтернативи для роботи із міткою диску під DOS 2.00? Може тому і вище ним скористалися? -- в подальшому, для пошуку системних викликів, обидві версії використовують "сучасні" системні виклики із DOS 2.00+.
Із побачених дрібниць, моє припущення:
mov [PutativeMaxFreeMem], cx
; Possibly contains approximation of free mem,
; above code, below stack
цілком підтвердилося:
SUB CX,0200h+(OFFSET DG:BUF)
; leave room for all sorts of things
Підсумок
Мені було цікаво, як міркували розробники, що писали код DOS. Найпростіший спосіб відчути це -- взяти код за їх авторства, який я добре розумію і порівняти коментарі та назви змінних і функцій. Отримав певне задоволення від процесу, вияснив пару таємниць, на одну відповіді не отримав, але чогось особливо цікавого не трапилося -- ймовірно, тому що програмка дуже проста. Можливо, коли знову буде потреба у рекреаційному програмуванні, проаналізую SYS.COM 3.XX із OAK (хоча, остання, яку я аналізував ретельно -- 3.00, дизасемблював 3.10 -- але пост не завершив, ймовірно, опублікую його як є) -- там дивний printf є.
Додатки
Версії MS-DOS 0.34-1.25
Цитуючи опублікований MSDOS.ASM із 1.25:
; 0.34 12/29/80 General release, updating all past customers
; 0.42 02/25/81 32-byte directory entries added
; 0.56 03/23/81 Variable record and sector sizes
; 0.60 03/27/81 Ctrl-C exit changes, including register save on user stack
; 0.74 04/15/81 Recognize I/O devices with file names
; 0.75 04/17/81 Improve and correct buffer handling
; 0.76 04/23/81 Correct directory size when not 2^N entries
; 0.80 04/27/81 Add console input without echo, Functions 7 & 8
; 1.00 04/28/81 Renumber for general release
; 1.01 05/12/81 Fix bug in `STORE'
; 1.10 07/21/81 Fatal error trapping, NUL device, hidden files, date & time,
; RENAME fix, general cleanup
; 1.11 09/03/81 Don't set CURRENT BLOCK to 0 on open; fix SET FILE SIZE
; 1.12 10/09/81 Zero high half of CURRENT BLOCK after all (CP/M programs don't)
; 1.13 10/29/81 Fix classic "no write-through" error in buffer handling
; 1.20 12/31/81 Add time to FCB; separate FAT from DPT; Kill SMALLDIR;
; Add FLUSH and MAPDEV calls; allow disk mapping in DSKCHG;
; Lots of smaller improvements
; 1.21 01/06/82 HIGHMEM switch to run DOS in high memory
; 1.22 01/12/82 Add VERIFY system call to enable/disable verify after write
; 1.23 02/11/82 Add defaulting to parser; use variable escape character
; Don't zero extent field in IBM version (back to 1.01!)
; 1.24 03/01/82 Restore fcn. 27 to 1.0 level; add fcn. 28
; 1.25 03/03/82 Put marker (00) at end of directory to speed searches
Згідно листа Тіма Патерсона, автора QDOS, яку MS купила, щоб створити PC-DOS, а потім і MS-DOS: "Version 1.25 was the first general release to OEM customers other than IBM so was used by all the first clone manufacturers." та "IBM's DOS 1.1 corresponds to MS-DOS 1.24. There is one minor difference between 1.24 and 1.25, as noted in the revision history at the top of MSDOS.ASM.".
Посилання
- Загальний опис версій DOS 2.xx: https://www.os2museum.com/wp/dos/dos-2-0-and-2-1/ -- стаття 2011 року, вже дещо застаріла (через появу нових відомостей).
- Давніші часи:
- Розповідь про OAK: http://www.os2museum.com/wp/ms-dos-oaks/
- Блог Тіма Патерсона: http://dosmandrivel.blogspot.com/ та його статті із історії DOS (в Архіві -- оригінал недоступний).
- Відновлення та компілювання MS-DOS 1.25: https://www.os2museum.com/wp/pc-dos-1-1-from-scratch/
- Відновлення та компілювання MS-DOS 2.11: https://www.os2museum.com/wp/dos-2-11-from-scratch/
- http://www.os2museum.com/wp/ms-dos-1-1-2-0-source-code-released/. З коментарів: "COMMAND.COM identifies itself as “TeleVideo Personal Computer DOS Vers. 2.11”"
Список дизасемблювань утиліт DOS в цьому блозі
- "Аналіз DATE.COM з PC-DOS 1.00"
- "Аналіз TIME.COM з PC-DOS 1.00"
- "Аналіз SYS.COM з PC/MS-DOS --- вступ"
- "Аналіз SYS.COM з PC-DOS 1.00"
- "Аналіз SYS.COM з PC-DOS 1.10"
- "Аналіз SYS.COM з PC-DOS 2.00"
- "Аналіз SYS.COM з PC-DOS 2.10"
- "Спроба аналізу SYS.COM з DOS 2.11"
- "Аналіз SYS.COM версії 1.81 з Compaq-DOS 2.12"
- "Аналіз SYS.COM з попередника, 86-DOS"
- "Аналіз CHKDSK.COM з PC-DOS 1.00"
- "Аналіз SYS.COM з PC-DOS 3.00 -- частина перша"
- "Аналіз SYS.COM з PC-DOS 3.00 -- частина друга, printf"
- "Аналіз SYS.COM з PC-DOS 3.00 -- частина третя, нарешті SYS"
- "Аналіз SYS.COM з PC-DOS 3.10"
Немає коментарів:
Дописати коментар