суботу, 24 вересня 2022 р.

Порівняння дизасемльованого SYS.COM з PC-DOS 2.10 та опублікованих Microsoft текстів для цієї утиліти


Колись, для відпочинку та розваги, дизасемблював SYS.COM різних версій DOS (посилання в кінці). Суттєво пізніше Microsoft опублікувала джерельні тексти MS-DOS, офіційно, версій MS-DOS 1.25 (еквівалентна PC-DOS 1.10) і 2.00. Від того часу мені захотілося порівняти справжній код із своїм дизасемблом. Цього літа з'явилася нагода. 

Аналіз показав, що 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 в цьому блозі


Немає коментарів:

Дописати коментар