Рухаємося далі. Наступив черговий, 1982 рік. В травні вийшла наступна версія PC-DOS, 1.10. Революційною вона не була. Виправлено помилки, додано підтримку двосторонніх дискет (320кб). Для файлів почав зберігатися і час зміни, не тільки дата. З'явився ще один системний виклик, "set/reset verify switch", AH=2Eh/INT 21h (не траплялося мені в документації згадок, що він тільки з 1.10, але в коді їх IBMDOS.COM це видно однозначно). TIME i DATE стали внутрішніми командами, тепер їх виконанням займався COMMAND.COM. Всілякі дрібні зміни там і сям. В Microsoft ця версія мала номер 1.24 (наступна, 1.25, постачалася іншим ОЕМ). (Детальніше про номери версій див. тут, шукайте за стрічкою "1.24").
Подивимося, що змінилося в SYS.COM з виходом цієї версії. Повторюватися не буду, загальну інформацію див. попередній пост ("Аналіз SYS.COM з PC-DOS 1.00") і посилання там.
Подивимося, що змінилося в SYS.COM з виходом цієї версії. Повторюватися не буду, загальну інформацію див. попередній пост ("Аналіз SYS.COM з PC-DOS 1.00") і посилання там.
SYS.COM все ще залишилася COM програмою (і, візуально, продовжувала залишатися ним до самого кінця мікрософтівського DOS, хоча всередині все стало значно складніше -- слідкуйте за наступними постами).
Код -- сумісний з Flat Assembler.
; ; This file is generated by The Interactive Disassembler (IDA) ; Copyright (c) 2010 by Hex-Rays SA, <support@hex-rays.com> ; Licensed to: Freeware version ; ; Modified to compile by nasm and commented by Indrekis, indrekis2.blogspot.com
; Input MD5 : F4759B60F3A5FB8B8323CED9B2437E5A ; File Name : PC-DOS_1.10\SYS.COM ; Format : MS-DOS COM-file ; Base Address: 0h Range: 100h-35Dh Loaded length: 25Dh IBMDOS_DTA = 35Eh IBMBIO_DTA = 235Eh ; CPU.086 use16 org 100h include "my_fcb_2a.inc" start: jmp short EntryPoint1 AnyArgIsInvalid: mov dx, aInvalidParamet ; "Invalid parameter$" jmp PrnMsgNExit BadDriveLetter: mov dx, aInvalidDriveSp ; "Invalid drive specification$" jmp PrnMsgNExit ; =========================================================================== ; Now situation when one of system files is absent ; is handled in more structured way --- just in one place. ; ; If asking to insert disk, all drive checks are done once more ; ; Unlike PC-DOS 1.00, do not attempt file closing. ; Also, "anykey" input is done without echo. AskToInsertSysDisk: mov al, [currentDrive] add al, 40h ; Cbrrent FCB driver number to letter mov [drive_letter_char], al mov dx, aInsertSystemDi ; "Insert system disk in drive " mov ah, 9 int 21h ; DOS - PRINT STRING ; DS:DX -> string terminated by "$" mov ax, 0C08h ; Waiting for any key int 21h ; DOS - CLEAR KEYBOARD BUFFER ; AL must be 01h, 06h, 07h, 08h, or 0Ah. xor al, al ; --------------------------------------------------------------------------- EntryPoint1: cmp byte [ds:5Dh], 20h ; PSP: 5Ch-6Bh (16 bytes) -- Unopened Standard FCB 1 ; So here will be any symbol, entered after [a-z][:][\] jnz short AnyArgIsInvalid cmp al, 0FFh ; COM-format executables begin running with the following register values: ; AL = 00h if first FCB has valid drive letter, FFh if not ; AH = 00h if second FCB has valid drive letter, FFh if not ; CS,DS,ES,SS = PSP segment SP = offset of last word available in first 64K segment ; ( http://www.ctyme.com/intr/rb-2939.htm ) jz short BadDriveLetter ; Change from PC-DOS-1.00: Swapped order of checks. ; Now first check if there are any spurious letters (filenames, switches, etc) ; and if none, check if driver letter is correct. ; May be -- attempt to have more adequate error messages ; Change from PC-DOS-1.00: added check for attempt ; to write system files into disk, we are running (and reading them from) cmp byte [ds:5Ch], 0 ; Check for current drive, ; disabling "sys" to self jz short BadDriveLetter mov ah, 19h int 21h ; DOS - GET DEFAULT DISK NUMBER inc al mov [currentDrive], al cmp [ds:5Ch], al ; One more check to not "sys" to self jz short BadDriveLetter ; Open system files mov ah, 0Fh mov dx, FCBext_1 int 21h ; DOS - OPEN DISK FILE ; DS:DX -> FCB ; Return: AL = 00h file found, FFh file not found or al, al jnz short AskToInsertSysDisk mov dx, FCBext_2 mov ah, 0Fh int 21h ; DOS - OPEN DISK FILE ; DS:DX -> FCB ; Return: AL = 00h file found, FFh file not found or al, al jnz short AskToInsertSysDisk ; ; We have found IBMBIO.COM and IBMDOS.COM files. (No check for COMMAND.COM) mov ah, 1Ah ; Set DTA somewhere high in the our segment mov dx, IBMBIO_DTA ; 235Eh, above our code int 21h ; DOS - SET DISK TRANSFER AREA ADDRESS ; DS:DX -> disk transfer buffer ; Both FCBs are opened, so we can set record size mov ax, 1 mov word [FCBext_1.RecordSize], ax mov word [FCBext_2.RecordSize], ax ; Read IBMBIO mov bx, FCBext_1 mov cx, 8000h ; Reading at most 8000h bytes=32768=64*512 ; Real size =1920=3*512, hasn't changed from DOS 1.00 call ReadFile ; On entry BX -- FCB, DTA is set ; Saves after the FCB file date, file time and number ; of readed bytes (recors, but recors size=1) ; IBMBIO readed, proceeding to IMBDOS mov dx, IBMDOS_DTA ; Code in file ends at 34Fh. Above it we have several ; variables, and 35Eh is the next free byte address. ; So set DTA just above it. mov ah, 1Ah int 21h ; DOS - SET DISK TRANSFER AREA ADDRESS ; DS:DX -> disk transfer buffer mov bx, FCBext_2 mov cx, 2000h ; Read at most 2000h bytes = 8192 = 16*512 ; Real size =6400=12*512, hasn't changed from DOS 1.00 call ReadFile ; On entry BX -- FCB, DTA is set ; Saves after the FCB file date, file time and number ; of readed bytes (recors, but recors size=1) ; IBMDOS readed. ; ; Both files readed, their date and size saved. ; mov al, [ds:5Ch] ; Target disk from PSP FCB1 mov [FCBext_2.Driver], al ; Set for FCB2 mov [FCBext_1.Driver], al ; Set for FCB1 ; DOS 1.10 knows two types of disk -- single side, 160 kb, ; and two-sided, 320kb,(instead of just one for DOS 1.00) ; So SYS.COM need to check. mov dl, [ds:5Ch] ; Get FAT info for target drive mov ah, 1Ch int 21h ; DOS - GET ALLOCATION TABLE INFORMATION FOR SPECIFIC DRIVE ; DL = drive number to check (0=default, 1=A, etc) ; ; Return (http://www.ctyme.com/intr/rb-2591.htm): ; AL = sectors per cluster (allocation unit), or FFh if invalid drive ; CX = bytes per sector ; DX = total number of clusters ; DS:BX -> media ID byte ; Just ignore media byte push cs ; Previous call "spoiled" DS (DS:BX -> media ID byte) pop ds ; Restoring mov ah, 0 mul cx ; AX=sectors per cluster, CX=bytes per sector ; DX:AX=CX*AX -- bytes in cluster ; DX will be 0 -- cluster size is, of course, less than 64K xchg ax, cx ; Cluster size to CX ; Check if files are present in target disk and ; are compatible in sizes ; If any of files fails to open, (because it do not exists ; or for any other reason), print "No room for system on destination disk" and exit. ; If file exists, but uses different number of clusters ; print "Incompatible system size" and exit. mov bx, FCBext_1 call ChkSysFilesOnTARGETdisk ; Checks if system file with same name already ; exists on target disk and is compatible ; by size. Strange behaviour, as for system transfering... ; BX -- points to file FCB and filse size, stored after the FCB. ; ; Opens file during it. If opening failed, jumps to ; output of error "No room for system". ; ; At exit sets flags by comparison of number in ; cluster for source and target files (cmp <src>,<dst>). jnz short IncompatibleSysSize ; Error, if files size in clusters is not equal. mov bx, FCBext_2 call ChkSysFilesOnTARGETdisk ja short IncompatibleSysSize ; Error, if source file size (in clusters) is large ; than destination file size. ; It is interesting -- difference of decision algorithms ; for IBMBIO and IBMDOS is intetional? Equality of sizes ; is requested only for IBMBIO, other file can be of same ; or large size. ; PC-DOS 1.00 checked both files for equal sizes. ; ; All checks successfully completed. Proceeding to write. mov dx, IBMBIO_DTA ; DTA to readed IBMBIO mov ah, 1Ah int 21h ; DOS - SET DISK TRANSFER AREA ADDRESS ; DS:DX -> disk transfer buffer mov bx, FCBext_1 call WriteSysFile ; At call BX -- unopened tagret extended FCB, size, ; date and time is saved after the FCB, DTA is set ; to memory with source file contents. mov dx, IBMDOS_DTA ; DTA to readed IBMDOS mov ah, 1Ah int 21h ; DOS - SET DISK TRANSFER AREA ADDRESS ; DS:DX -> disk transfer buffer mov bx, FCBext_2 call WriteSysFile ; At call BX -- unopened tagret extended FCB, size, ; date and time is saved after the FCB, DTA is set ; to memory with source file contents. mov dx, aSystemTransfer ; "System transferred$" ; Single exit point. Just messages differs. PrnMsgNExit: mov ah, 9 int 21h ; DOS - PRINT STRING ; DS:DX -> string terminated by "$" int 20h ; DOS - PROGRAM TERMINATION ; returns to DOS--identical to INT 21/AH=00h ; =========================================================================== noRoomForSys: mov dx, aNoRoomForSyste ; "No room for system on destination disk$" jmp short PrnMsgNExit ; =========================================================================== IncompatibleSysSize: mov dx, aIncompatibleSy ; "Incompatible system size$" jmp short PrnMsgNExit ; Reading of files is much more structured than in ; PC-DOS 1.00. Even separate procedure used, instead ; of code mess before. ; ; Though, still no error checking -- we hope, that ; files was read correctly. virtual at bx bxExtFCB_t ExtFCB_t 0," "," ",0,0,0 ; To cheat fasm, needs exactly 8/3 spaces ; In fact, values are ignored. end virtual ; ============== subroutine ================================================= ; On entry BX -- FCB, DTA is set ; Saves after the FCB file date, file time and number ; of readed bytes (recors, but recors size=1) ReadFile: mov ah, 27h mov dx, bx int 21h ; DOS - RANDOM BLOCK READ ; DS:DX -> FCB ; CX = number of records to be read ; After the FCB we have variables IBM???_date, IBM???_time, IBM???_readed_size mov [bx+30h], cx mov ax, word [bxExtFCB_t.FileDate] mov [bx+2Ch], ax ; mov ax, word [bxExtFCB_t.FileTime] mov [bx+2Eh], ax ; retn ;ReadFile endp ; ============== subroutine ================================================= ; That function had changet it role (from DOS 1.00). Now it also ; opens the file. So, different int 21h function is used. ; Also, partially filled clusters handling code simplified. ; Checks if system file with same name already ; exists on target disk and is compatible ; by size. Strange behaviour, as for system transfering... ; BX -- points to file FCB and filse size, stored after the FCB. ; ; Opens file during it. If opening failed, jumps to ; output of error "No room for system". ; ; At exit sets flags by comparison of number in ; cluster for source and target files (cmp <src>,<dst>). ChkSysFilesOnTARGETdisk: mov ah, 0Fh ; On entry BX -- FCB, DTA is set ; CX -- bytes in cluster mov dx, bx int 21h ; DOS - OPEN DISK FILE ; DS:DX -> FCB ; Return: AL = 00h file found, FFh file not found ; ; We have not closed file, read before, because for ; DOS 1.XX all info about opened file is in FCB, so ; no internal structures used. or al, al jnz short noRoomForSys ; At any error prints "No room"... mov ax, word [bxExtFCB_t.FileSize] ; Opened in target disk file size xor dx, dx add ax, cx dec ax ; AX - file size+cluster size-1 div cx ; AX -- clusters in opened file, rounded to upper. push ax mov ax, [bx+30h] ; Saved file size add ax, cx dec ax xor dx, dx div cx ; AX -- clusters in source file, rounded to upper. pop dx ; DX (saved later AX) -- clusters in opened file cmp ax, dx ; Check if source and target files are equal, measuring in entire clusters. retn ;ChkSysFilesOnTARGETdisk endp ; ============== subroutine ================================================= ; At call BX -- unopened tagret extended FCB, size, ; date and time is saved after the FCB, DTA is set ; to memory with source file contents. ; Unlike for PC-DOS 1.00, at call of this subroutine file is already opened. ; Additionally it restores file time. ; Still no error checking. WriteSysFile: mov dx, bx xor ax, ax mov word [bxExtFCB_t.DirectRecord], ax mov word [bxExtFCB_t.DirectRecord+2], ax inc ax mov word [bxExtFCB_t.RecordSize], ax ; Record size = 1 mov ah, 28h mov cx, [bx+30h] ; Readed file size int 21h ; DOS - RANDOM BLOCK WRITE ; DS:DX -> FCB ; CX = number of records to be written ; if zero, truncate file to current random file position mov ax, [bx+2Ch] ; Set target file date and time, saved from source file mov word [bxExtFCB_t.FileDate], ax mov ax, [bx+2Eh] mov word [bxExtFCB_t.FileTime], ax mov ah, 10h int 21h ; DOS - CLOSE DISK FILE ; DS:DX -> FCB ; Return: AL = 00h directory update successful ; FFh file not found in directory retn ;WriteSysFile endp ; =========================================================================== aInvalidDriveSp db 'Invalid drive specification$' aInvalidParamet db 'Invalid parameter$' aInsertSystemDi db 'Insert system disk in drive ' drive_letter_char db 41h aAndStrikeAnyKe db 0Dh,0Ah db 'and strike any key when ready',0Dh,0Ah,'$' aNoRoomForSyste db 'No room for system on destination disk$' aIncompatibleSy db 'Incompatible system size$' aSystemTransfer db 'System transferred$' currentDrive db 0 FCBext_1 ExtFCB_t 0,"IBMBIO ", "COM", 0,6,0 ;FCBext_1 db 0FFh ; ExtMarker ; db 5 dup(0) ; Reserved1 ; db 6 ; Attribute ; db 0 ; Driver ; db 'I', 'B', 'M', 'B', 'I', 'O', 2 dup(' '); FileName ; db 'C', 'O', 'M' ; FileExt ; db 2 dup(0) ; CurBlock ; db 2 dup(0) ; RecordSize ; db 4 dup(0) ; FileSize ; db 2 dup(0) ; FileDate ; db 2 dup(0) ; FileTime ; db 8 dup(0) ; Reserved2 ; db 0 ; CurRecord ; db 4 dup(0) ; DirectRecord IBMBIO_date dw 0 IBMBIO_time dw 0 IBMBIO_readed_size dw 0 FCBext_2 ExtFCB_t 0,"IBMDOS ", "COM", 0,6,0 ;FCBext_2 db 0FFh ; ExtMarker ; db 5 dup(0) ; Reserved1 ; db 6 ; Attribute ; db 0 ; Driver ; db 'I', 'B', 'M', 'D', 'O', 'S', 2 dup(' '); FileName ; db 'C', 'O', 'M' ; FileExt ; db 2 dup(0) ; CurBlock ; db 2 dup(0) ; RecordSize ; db 4 dup(0) ; FileSize ; db 2 dup(0) ; FileDate ; db 2 dup(0) ; FileTime ; db 8 dup(0) ; Reserved2 ; db 0 ; CurRecord ; db 4 dup(0) ; DirectRecord IBMDOS_date dw 0 IBMDOS_time dw 0 IBMDOS_readed_size dw 0
Розмір, як не смішно, 605 байт. При тому, програма ще й дещо мудріша від своєї попередниці (розміром 896 байт).
Скачати код, разом із лістингом, згенерованим IDA, скомпільованим файлом та, для порівняння, оригінальним SYS.COM, можна тут. Скомпільований код, з точністю до опкодів-синонімів, тотожній оригінальному.
Алгоритм роботи
На початку --- стрибок до точки входу. Між початком і нею знаходиться коди виведення повідомлень "Invalid parameter" і "Invalid drive specification" та код прохання вставити системний диск. Код цей, на відміну від попередника, не закриває ніякі файли, і не виводить еха натиснутої "any key". При тому, розташований він так, що зразу по завершенню програма перейде до точки входу.
Аналогічно, після коду основного тіла програми, знаходиться ще два обробника помилок, "No room for system on destination disk" і "Incompatible system size".
На відміну від всіх досліджених системних програм з версії 1.00, стек не переставляється. Тому, якщо пам'яті замало, буде ОЙ. Хоча, ОЙ був би і в старішій версії, але більш контрольований, тоді як зараз затиратиметься стек.
Першим кроком йде перевірка, чи немає у імені файлу з першого FCB в PSP чогось, окрім пробіла. Якщо є -- стрибає на виведення "Invalid parameter" і завершується. Далі перевіряє, чи коректний диск у цьому ж FCB. Якщо ні --- повідомляє, про це, "Invalid drive specification". Порядок перевірок змінено, в порівнянні з 1.00, ймовірно, для більш адекватного повідомлення про помилки.
Далі перевіряється, чи ми не пробуємо записати файли на той же диск, з якого їх читаємо. Робиться дві перевірки, спершу на код диску 0, -- чи не є "поточним", а потім на співпадіння "абсолютного" коду поточного диску із тим, що передано (враховується відмінність нумерації дисків для FCB і взагалі в DOS/BIOS). Якщо диски співпадають, пише "Invalid drive specification" і завершується. 1.00 такого не робила.
Наступним кроком відкриваємо, користуючись розташованим в коді FCB, IBMBIO.COM і IBMDOS.COM. Якщо не вдалося відкрити котрийсь з них, просимо вставити системний диск і починаємо з початку. Цей код став значно прямолінійнішим, ніж був раніше.
Якщо відкрили успішно, кожен із файлів читаємо в пам'ять, підпрограмою ReadFile, якій в BX передається FCB, в CX -- скільки прочитати (як і раніше, "з перельотом"). Перед викликом ReadFile також встановлюється DTA. ReadFile зберігає реальний розмір, дату і час вихідного файлу у змінні, що знаходяться зразу після кожного з FCB. (І тут порядок навели.) Контролю помилок не здійснюється.
DOS 1.10 вже знав про двосторонні дискети, тому, перш ніж порівнювати розмір файлів, програма отримуває інформацію про FAT (AH=1Ch/INT 21h), множить кількість секторів у кластері на кількість байт у секторі, щоб знайти розмір кластера в байта, зберігає його в CX.
Далі, для кожного файлу, змінивши в його FCB диск на цільовий і передавши адресу цього FCB в BX, викликається ChkSysFilesOnTARGETdisk. Функція хитріша, ніж її аналог раніше. Спершу вона пробує відкрити відповідний файл (але вже на цільовому диску), якщо їй не вдалося (його немає) --- повідомляє "No room for system on destination disk" і виходить. Якщо відкрився -- порівнює розмір вихідного та цільового файлів у кластерах (cmp <src-size>, <dest-size>), відповідним чином встановивши прапорці. (Розмір у кластерах рахується трішки елегантніше, ніж раніше --- (file size+cluster size-1)/cluster size).
Після повернення з ChkSysFilesOnTARGETdisk код робить дещо дивне (для мене, принаймні). Для "IBMBIO.COM" перевіряє, чи розміри, в кластерах, рівні. Для "IBMDOS.COM" менш строго, лише чи цільовий файл є не меншим за вихідний. Якщо ці умови не виконуються -- "No room for system on destination disk", інакше переходить до запису.
Запис відбувається практично так само, як і раніше, єдине що відновлюється і час файлу. Перевірки на помилки все ще немає.
Після повернення з ChkSysFilesOnTARGETdisk код робить дещо дивне (для мене, принаймні). Для "IBMBIO.COM" перевіряє, чи розміри, в кластерах, рівні. Для "IBMDOS.COM" менш строго, лише чи цільовий файл є не меншим за вихідний. Якщо ці умови не виконуються -- "No room for system on destination disk", інакше переходить до запису.
Запис відбувається практично так само, як і раніше, єдине що відновлюється і час файлу. Перевірки на помилки все ще немає.
Ніяких стрічок із копірайтом чи датою в коді немає.
"Документація"
Основна відмінність від попередньої версії -- здійснюється перевірка запису на диск, з якого запускається програма, розмір файлів порівнюється в кластерах а не секторах і трішки адекватнішими стали повідомлення про помилки.
Про код
Код став акуратнішим --- менше хаотичних переходів туди-сюди, програма стала структурованішою. Цікаво, що хоча опкоди-синоніми відрізняються, на відміну від програмок з DOS 1.00, жодного разу не довелося асемблеру казати вибрати менш ефективний код. Очевидно, їхній тогочасний асемблер теж покращився.
Наступний крок --- PC-DOS 2.00. Зовсім інша ОС, насправді: з'явилася підтримка директорій, жорстких дисків, кількох форматів дискет. Подивимося.
Дякую за увагу!
Немає коментарів:
Дописати коментар