Що таке SYS.COM --- якраз розповідалося. Кому цікаво, що являв собою PC-DOS 1.00, просимо сюди, а ми перейдемо до дизасемблювання.
Загальний вступ написано в пості про перші вправи з дизасемблюванням: "Аналіз DATE.COM з PC-DOS 1.00". Далі виходитиму з того, що ви його проглянули. Також знадобиться інформація, що таке FCB, і з чим їх їдять.
Нагадаю хіба, що SYS.COM --- програма типу COM. Такі програми побайтово вантажаться у пам'ять, починаючи із зміщення
100h від початку сегменту (перед нею, цих 0FFh байтах, знаходиться PSP). Коли програмі передається керування, гарантується, що:
- CS=DS=ES=SS, всі вказують на той же сегмент,
- SP=0FFFEh -- стек у кінці сегмента, росте вниз,
- IP=0100h -- починаємо зразу після PSP.
- AL = 00h, якщо перший FCB в PSP має правильну літеру диску, 0FFh, якщо ні.
- AH -- аналогічно для другого FCB з PSP.
NASM остаточно задовбав -- напевне, він хороший асемблер (це не іронія!), але ряд його звичок, наприклад говорити "times db" замість звичного "db dup", ISTRUC-збочення і т.д. занадто суперечать моїм дитячим звичка. Тому, нижче наведено код, зроблено сумісним з моїм улюбленим Flat Assembler (fasm). Файл-заголовок, з описом FCB, взято з поста про FCB, але підігнано під конкретні потреби, і виправлено пару косяків.
; 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 : E1B70C19E40A68D90FF9DFC498AB7374 ; File Name : <>\PC-DOS_1.0\SYS.COM ; Format : MS-DOS COM-file ; Base Address: 1000h Range: 10100h-10480h Loaded length: 380h ; CPU 8086 use16 org 100h include "my_fcb_2a.inc" start: jmp short EntryPoint1 BadDriveLetter: mov dx, aInvalidDriveSp ; "Invalid drive specification\r\n\n$" jmp PrnMsgNExit ; MSG -- in DX AnyArgIsInvalid: mov dx, aInvalidParamet ; "Invalid parameter\r\n\n$" jmp PrnMsgNExit ; MSG -- in DX ; =========================================================================== unused_a15Jul81 db '15-Jul-81' ; =========================================================================== EntryPoint1: mov sp, Stack_top ; Stack top. 46Fh 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 cmp byte [ds:5Dh], ' ' ; In PSP, 5Ch-6Bh (16 bytes) -- Unopened Standard FCB 1 ; So here will be any symbol, entered after [a-z][:][\] jnz short AnyArgIsInvalid ChkForIBMBIO: mov ah, 0Fh mov dx, FCBext_1 ; Check for file IBMBIO.COM on current disk. int 21h ; DOS - OPEN DISK FILE ; DS:DX -> FCB ; Return: AL = 00h file found, FFh file not found or al, al jz short IBMBIO_found InsertSysDisk1: call RequestInsertSysDisk ; If not found IBMBIO.COM -- ask to ; insert system disk and try once more jmp short ChkForIBMBIO IBMBIO_found: mov ax, word [FCBext_1.FileDate] mov [IBMBIO_date], ax ; Saving the IBMBIO.COM file date mov ah, 0Fh mov dx, FCBext_2 ; Checking for IBMDOS.COM on current disk int 21h ; DOS - OPEN DISK FILE ; DS:DX -> FCB ; Return: AL = 00h file found, FFh file not found or al, al jnz short InsertSysDisk1 ; ; We have found IBMBIO.COM and IBMDOS.COM files. (No check for COMMAND.COM) mov ax, word FCBext_2.FileDate mov [IBMBDOS_date], ax ; Saving IBMBDOS.COM file date mov ah, 1Ah mov dx, IBMBIO_DTA ; Data area contains strange byte... int 21h ; DOS - SET DISK TRANSFER AREA ADDRESS ; DS:DX -> disk transfer buffer xor ax, ax mov word [FCBext_1.DirectRecord], ax mov word [FCBext_1.DirectRecord+2], ax mov word [FCBext_2.DirectRecord], ax mov word [FCBext_2.DirectRecord+2], ax mov ax, 1 mov word [FCBext_1.RecordSize], ax mov word [FCBext_2.RecordSize], ax mov ah, 27h mov dx, FCBext_1 mov cx, 0C00h ; =3072=6*512; real size=1920=3*512 int 21h ; DOS - RANDOM BLOCK READ ; DS:DX -> FCB ; CX = number of records to be read mov [ReadedFromBIO], cx ; Size of file in bytes, if less than 3072 mov ah, 1Ah ; DTA for IBMDOS mov dx, IBMBIO_DTA+0C00h ; "IBMDOS_DTA", IBMBIO_DTA (46F) +0C00h = 106Fh int 21h ; DOS - SET DISK TRANSFER AREA ADDRESS ; DS:DX -> disk transfer buffer mov ah, 27h mov dx, FCBext_2 mov cx, 2000h ; 8196=32*512; real size=6400=12*512 int 21h ; DOS - RANDOM BLOCK READ ; DS:DX -> FCB ; CX = number of records to be read mov [ReadedFromDOS], cx ; Size of file in bytes, if less than 8196 ; 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 ; Check if files are present in target disk and ; are compatible in sizes 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, ; Global variable File_Date_ptr contains pointer ; to file date mov bx, FCBext_2 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, ; Global variable File_Date_ptr contains pointer ; to file date mov dx, IBMBIO_DTA mov ah, 1Ah int 21h ; DOS - SET DISK TRANSFER AREA ADDRESS ; DS:DX -> disk transfer buffer mov ax, IBMBIO_date mov [File_Date_ptr], ax ; Elegant! Almost (ersatz) pointer :-) ; Moreover, it is global variable, known by function... mov bx, FCBext_1 call WriteSysFile ; At call DX points to DTA with data to write, ; BX -- unopened tagret extended FCB mov ax, IBMBDOS_date mov [File_Date_ptr], ax mov dx, IBMBIO_DTA+0C00h ; "IBMDOS_DTA", IBMBIO_DTA (46F) +0C00h = 106Fh mov ah, 1Ah int 21h ; DOS - SET DISK TRANSFER AREA ADDRESS ; DS:DX -> disk transfer buffer mov bx, FCBext_2 call WriteSysFile ; At call DX points to DTA with data to write, ; BX -- unopened tagret extended FCB mov dx, aSystemTransfer ; "System transferred$" ; Single exit point. Just messages differs. PrnMsgNExit: mov ah, 9 ; MSG -- in DX int 21h ; DOS - PRINT STRING ; DS:DX -> string terminated by "$" int 20h ; DOS - PROGRAM TERMINATION ; returns to DOS--identical to INT 21/AH=00h ; ============== subroutine ================================================= ; 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, ; Global variable File_Date_ptr contains pointer ; to file date ChkSysFilesOnTARGETdisk: ; FUNCTION CHUNK AT 022B SIZE 0000000A BYTES mov ah, 23h mov dx, bx ; BX points to extended FCB int 21h ; DOS - GET FILE SIZE ; DS:DX -> unopened FCB with filename and record size ; All fields are already initialized or al, al jnz short noRoomForSys virtual at bx bxExtFCB_t ExtFCB_t 0," "," ",0,0,0 ; To cheat fasm, need exactly 8/3 spaces ; In fact, values are ignored. end virtual ;mov ax, word [bx+ExtFCB_t.DirectRecord] ; BX points to extended FCB mov ax, word [word bxExtFCB_t.DirectRecord] ; BX points to extended FCB ; [word ...] --- to use disp16, not disp8 ; +28h -- DirectRecord mov dx, [word bx+2Ch] ; After each FCB we have variable with ; number of bytes readed from file test ax, 1FFh ; 1FFh=512-1 ; Check if target file has sector, which is not full. ; If do have -- add 512 to size jz short NoPartialSector add ax, 200h ; 512 NoPartialSector: and ax, 0FE00h ; Zero last 9 bits -- offset in sector test dx, 1FFh jz short NoPartialSector2 ; Same for DX, readed source file size add dx, 200h ; 512 NoPartialSector2: and dx, 0FE00h cmp ax, dx ; Check if source and target files are equal, ; measuring in entire sectors. jnz short IncompatibleSysSize retn ; ChkSysFilesOnTARGETdisk endp ; ============== subroutine ================================================= ; At call DX points to DTA with data to write, ; BX -- unopened tagret extended FCB WriteSysFile: xor ax, ax ;mov word [bx+ExtFCB_t.DirectRecord], ax ; <= FASM do not support that form mov word [word bxExtFCB_t.DirectRecord], ax ; BX points to extended FCB ; +28h -- DirectRecord ;mov [bx+ExtFCB_t.DirectRecord+2], ax ; +2Ah -- DirectRecord+2 mov word [word bxExtFCB_t.DirectRecord+2], ax ; +2Ah -- DirectRecord+2 mov dx, bx mov ah, 0Fh int 21h ; DOS - OPEN DISK FILE ; DS:DX -> FCB ; Return: AL = 00h file found, FFh file not found ;mov word [bx+ExtFCB_t.RecordSize], 1 ; <= FASM do not support that form mov word [word bxExtFCB_t.RecordSize], 1 ; +15h -- Record size in Extended FCB mov ah, 28h mov cx, [word bx+2Ch] ; After each FCB we have variable with ; number of bytes readed from file 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, File_Date_ptr ;mov word [bx+ExtFCB_t.FileDate], ax ; <= FASM do not support that form mov word [word bxExtFCB_t.FileDate], ax ; Restore file date, date specified ; by pointer 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 ; =============================================================== ; START OF FUNCTION CHUNK FOR ChkSysFilesOnTARGETdisk noRoomForSys: mov dx, aNoRoomForSyste ; "No room for system on destination disk$" jmp short PrnMsgNExit ; MSG -- in DX ; =============================================================== IncompatibleSysSize: mov dx, aIncompatibleSy ; "Incompatible system size$" jmp short PrnMsgNExit ; MSG -- in DX ; END OF FUNCTION CHUNK FOR ChkSysFilesOnTARGETdisk ; ============== subroutine ================================================= RequestInsertSysDisk: mov ah, 10h ; Close files in case if we have already opened ; at least one of them. mov dx, FCBext_1 int 21h ; DOS - CLOSE DISK FILE ; DS:DX -> FCB ; Return: AL = 00h directory update successful ; FFh file not found in directory mov dx, FCBext_2 int 21h ; DOS - CLOSE DISK FILE ; DS:DX -> FCB ; Return: AL = 00h directory update successful ; FFh file not found in directory mov dx, aInsertSystemDi ; "Insert system disk and strike any key$" mov ah, 9 int 21h ; DOS - PRINT STRING ; DS:DX -> string terminated by "$" mov ax, 0C01h int 21h ; DOS - FLUSH BUFFER AND READ STANDARD INPUT ; AL = STDIN input function to execute after ; flushing buffer, other registers as appropriate ; for the input function. ; (If AL is not one of 01h,06h,07h,08h, or 0Ah, ; the buffer is flushed but no input is attempted) ; mov ah, 2 mov dl, 0Dh int 21h ; DOS - DISPLAY OUTPUT ; DL = character to send to standard output mov dl, 0Ah int 21h ; DOS - DISPLAY OUTPUT ; DL = character to send to standard output mov dl, 0Ah int 21h ; DOS - DISPLAY OUTPUT ; DL = character to send to standard output retn ;RequestInsertSysDisk endp ; =============================================================== aInvalidDriveSp db 'Invalid drive specification',0Dh,0Ah db 0Ah,'$' aInvalidParamet db 'Invalid parameter',0Dh,0Ah db 0Ah,'$' aIncompatibleSy db 'Incompatible system size$' db 0B0h ; ° aNoRoomForSyste db 'No room for system on destination disk$' db 0B0h ; ° aSystemTransfer db 'System transferred$' db 0B0h ; ° aInsertSystemDi db 'Insert system disk and strike any key$' db 0B0h ; ° FCBext_1 ExtFCB_t 0,"IBMBIO ", "COН", 0,6,4 ;FCBext_1 db 0FFh ; ExtMarker ; ; Extended FCB structure ; db 5 dup(0) ; Reserved1 ; db 6 ; Attribute ; db 0 ; Driver ; db 'I', 'B', 'M', 'B', 'I', 'O', 2 dup(' '); FileName ; db 'C', 'O', 'Н' ; FileExt ; db 4, 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 ReadedFromBIO dw 0 ; FCBext_2 ExtFCB_t 0,"IBMDOS ", "COН", 0,6,4 ;FCBext_2 db 0FFh ; ExtMarker ; ; Extended FCB structure ; db 5 dup(0) ; Reserved1 ; db 6 ; Attribute ; db 0 ; Driver ; db 'I', 'B', 'M', 'D', 'O', 'S', 2 dup(' '); FileName ; db 'C', 'O', 'Н' ; FileExt ; db 4, 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 ReadedFromDOS dw 0 ; File_Date_ptr dw 0 ; IBMBIO_date dw 0 ; IBMBDOS_date dw 0 ; STACK_1 db 100h dup(0) Stack_top: ; Initial stack top at the top of stack region IBMBIO_DTA db 0C9h ; Й ; db 10h dup(0)
my_fcb_2a.inc
; This file is generated by The Interactive Disassembler (IDA) ; Copyright (c) 2010 by Hex-Rays SA, <support@hex-rays.com> ; Licensed to: Freeware version ; ; Structures definition for SYS.COM and others struc FCB_t in_drive,in_name,in_ext,in_record_size ; (sizeof=0x25) -- http://indrekis2.blogspot.com/2013/02/dos-fcb.html { .Driver db in_drive; Drive specified, Init by User, 0 = default, 1 = A, etc, FFh is not allowed .FileName db in_name ; Filename, padded by spaces, 20h, Init by User assert $-.FileName <= 8 if $-.FileName < 8 lbl=$ .name_padding db lbl-.FileName dup 20h end if .FileExt db in_ext ; Extension, padded by spaces, 20h, Init by User assert $-.FileExt <= 3 if $-.FileExt < 3 lbl=$ .ext_padding db lbl-.FileExt dup 20h end if .CurBlock db 2 dup 0 ; Current Block, Init by DOS .RecordSize dw in_record_size+0 ; (2 dup 0) ; Record size, Init by DOS, def. val=80h=128d .FileSize db 4 dup 0 ; File size, Init by DOS .FileDate db 2 dup 0 ; File date, Init by DOS .FileTime db 2 dup 0 ; File time, Init by DOS .reserv1 db 8 dup 0 ; .CurRecord db 1 dup 0 ; Current record, Init by User .DirectRecord db 4 dup 0 ; Random record, Init by User } ; Attribute byte format ; bit meaning if bit = 1 ; --- --------------------------------------- ; 7 unused ; 6 unused ; 5 file has been changed since last backup ; 4 entry represents a subdirectory ; 3 entry represents a volume label ; 2 system file ; 1 hidden file ; 0 read-only struc ExtFCB_t in_drive,in_name,in_ext,in_record_size,in_attrib, in_curblock ; (sizeof=0x2C) { .ExtMarker db 0FFh ; Extended FCB ID, Init by User .reserv2 db 5 dup 0 ; .Attribute db in_attrib ; File attribute, Init by User .Driver db in_drive; Drive specified, Init by User, 0 = default, 1 = A, etc, FFh is not allowed .FileName db in_name ; Filename, padded by spaces, 20h, Init by User assert $ - .FileName <= 8 if $-.FileName < 8 lbl=$ .name_padding db lbl-.FileName dup 20h end if .FileExt db in_ext ; Extension, padded by spaces, 20h, Init by User assert $-.FileExt <= 3 if $-.FileExt < 3 lbl=$ .ext_padding db lbl-.FileExt dup 20h end if .CurBlock dw in_curblock ; Current Block, Init by DOS .RecordSize dw in_record_size+0 ; (2 dup 0) ; Record size, Init by DOS, def. val=80h=128d .FileSize db 4 dup 0 ; File size, Init by DOS .FileDate db 2 dup 0 ; File date, Init by DOS .FileTime db 2 dup 0 ; File time, Init by DOS .reserv1 db 8 dup 0 ; .CurRecord db 1 dup 0 ; Current record, Init by User .DirectRecord db 4 dup 0 ; Random record, Init by User }
Код цей успішно компілюється. Запускати результат під оригінальним PC-DOS 1.00 не пробував, але, за винятком різниці у кількох байтах, що відповідають командам, для яких сучасний асемблер вибрав інше кодування-синонім, ніж тогочасний, він тотожній оригінальній програмі. (Теоретично, строго по списку не всі відмінності перевірив, тому і тут 100% гарантії не дам, що якусь не прогледів, але :).
Довелося, окрім звичного з попередніх разів "near", також використати конструкцію типу "mov ax, word [word...]", щоб попросити асемблер вибрати інструкцію з disp16, а не на байт ефективнішу, з disp8. (Ще натяк на недопрацювання тодішнього асемблера). Для того, щоб сказати FASM, що в BX буде адреса структури FCB, використано його директиву "virtual at bx".
Розмір --- 896 байт. З них пару десятків можна було дуже просто зекономити (враховуючи тогочасну манію). Скачати код, разом із лістингом, згенерованим IDA, скомпільованим файлом та, для порівняння, оригінальним SYS.COM, можна тут.
Алгоритм роботи
На початку програми стоїть типовий для багатьох COM-файлів перехід на точку входу, EntryPoint1. Після нього --- два шматки коду, які поміщають в DX адресу того чи іншого повідомлення і здійснюють стрибок на кінець програми. Ці мітки я так і назвав: BadDriveLetter і AnyArgIsInvalid. Остання назва "кодує" той факт, що зустрівши хоч щось в переданому, окрім літери диску, програма повідомляє про "Invalid parameter".
Процедура завершення програми, за міткою PrnMsgNExit, виводить стрічку, на яку вказує DX, і завершує програму, викликавши int 20h. Якщо завершення успішне, то виводиться повідомлення "System transferred".
Після цих двох блоків йде стрічка '15-Jul-81', очевидно дата "копірайту", яка ніде не використовується. Далі --- точка входу (мітка EntryPoint1).
Цікаво, ще два таких фрагменти коду трапляються далі, ближче до кінця програми, між підпрограмами. Одна, noRoomForSys, повідомляє що "No room for system on destination disk", (хоча й обманює трохи при цьому, див. далі), друга --- IncompatibleSysSize, повідомляє, що системні файли на цільовому диску несумісного розміру. Що під цим має на увазі програма, побачимо нижче. Ймовірно, вони так розкидані, щоб дотягувалися короткі умовні переходи.
Процедура завершення програми, за міткою PrnMsgNExit, виводить стрічку, на яку вказує DX, і завершує програму, викликавши int 20h. Якщо завершення успішне, то виводиться повідомлення "System transferred".
Після цих двох блоків йде стрічка '15-Jul-81', очевидно дата "копірайту", яка ніде не використовується. Далі --- точка входу (мітка EntryPoint1).
Цікаво, ще два таких фрагменти коду трапляються далі, ближче до кінця програми, між підпрограмами. Одна, noRoomForSys, повідомляє що "No room for system on destination disk", (хоча й обманює трохи при цьому, див. далі), друга --- IncompatibleSysSize, повідомляє, що системні файли на цільовому диску несумісного розміру. Що під цим має на увазі програма, побачимо нижче. Ймовірно, вони так розкидані, щоб дотягувалися короткі умовні переходи.
Фактично виконання починається в EntryPoint1. Спочатку встановлюється стек на виділену в тілі програми 256-байтову (100h) область пам'яті. Потім відбувається перевірка, чи AL не дорівнює 0FFh. (При виклику COM-програми в AL буде 0, якщо диск в першому FCB з PSP коректний, тобто коректний диск було передано першим аргументом програми, 0FFh в іншому випадку.) Якщо диск не коректний, відбудеться перехід на мітку BadDriveLetter і завершення програми.
Якщо диск коректний, перевіряється чи в полі імені файлу стоїть пробіл. Якщо ні, тобто крім диску було введено ще якесь ім'я файлу, переходимо до AnyArgIsInvalid. (Однак, аргументи, передані після літери диску, але відділені хоча б пробілом, повністю проігноруються.)
Наступний крок --- на поточному диску пробуємо відкрити IBMBIO.COM. Робимо це за допомогою підготовленого Extended FCB, який збережено в тілі програми (мітка FCBext_1, диск -- 0, поточний). Якщо файл відкрився, зберігаємо його дату та, за допомогою другого розширеного FCB (FCBext_2), пробуємо відкрити IBMDOS.COM (теж на поточному диску). Якщо файл успішно відкрився, зберігається і його дата.
Якщо хоча б один із цих файлів не відкрився, викликається підпрограма, "RequestInsertSysDisk", яка просить вставити системний диск і знову стрибаємо на спробу відкрити IBMBIO.COM. Зверніть увагу на реалізацію цього блоку в коді -- істинне спагеті, раз стрибаємо, раз не стрибаємо і т.д. і т.п. :-)
RequestInsertSysDisk закриває обидва FCB -- раптом хоча б один із файлів було відкрито. (Якщо б хто дивився в OAK, код реалізації FCB, він би побачив, що там матюгають тупі програми, які FCB по декілька разів закривають. Чого б це? :). Потім виводиться текст: "Insert system disk and strike any key", очищаємо буфер і чекаємо натискання клавіші. Коли клавішу натиснуто, двічі переводиться рядок (ефективно це дає один порожній рядок перед наступним повідомленням) і підпрограма завершується.
Якщо всі файли знайдено, DTA встановлюється на мітку IBMBIO_DTA, яка знаходиться в кінці програми. Нагадую, при роботі з FCB, читання-запис відбувається в області DTA. Зрозуміло, що DTA знаходиться після тіла програми. (COM-файл вважає, що вся пам'ять вище нього -- його.) Але, після мітки IBMBIO_DTA, файл програми містив ще 11 байт, 10 нулів і загадковий 0C9h, який ніде не використовується. Якесь сміття потрапило?
Згадуючи про загадкові символи. Якщо подивитися на поле розширення обох FCB, там пише: "CO=" або "COН" -- залежно від вашого кодування. Справа в тому, що після звичайних англійських "CO" йде символ з кодом 0CDh. В кодуванні CP1251 це літера еН, в розширеному ASCII -- символ псевдографіки. Звідки він взявся, що означає і як після такого COM-файли успішно відкриваються? 0CDh = 1100 1101b. Якщо відкинути старший біт, отримаємо 0100 1101b = 4Dh = 'M'. Деталей не знаю, але траплялися згадки, що, десь в ту епоху, існувала традиція кінець стрічки помічати відмінним від нуля старшим бітом. Тобто, з точки зору такого підходу, це звичайна 'M'+ознака кінця рядка. Очевидно, код обробки FCB якось враховував таку можливість, бо успішно працював як варіант із старшим бітом, так і без нього. Поки його ще не проаналізував детально, але в "дизасемблі" від Don Jindra для PC DOS 1.10, при обробці імені, для кожної літери старший біт просто обнуляється.
Отож, встановивши DTA, для обох FCB (вже відкритих -- наперед немає сенсу) встановлюється розмір запису 1, початковий запис -- нульовий.
З IBMBIO.COM пробується прочитати 0C00h=3072 байти (6 секторів). Реальний розмір файлу -- 1920, не знаю, чи випадково, але рівно три сектори. Тобто пробується читати з запасом. Як відомо, в такій ситуації читання через FCB читає, скільки може, потім повертає кількість прочитаних записів у CX. Ця величина зберігається у змінні ReadedFromBIO.
Далі DTA переставляється на IBMBIO_DTA+0C00h --- зразу після області, яка була б використана, якщо б прочитали всіх 0C00h байт. Це потрібно зробити, так як DTA єдиний для всіх операцій, а наступний крок --- прочитати IBMDOS.COM. Читається 2000h = 8196 байт (32 сектори), за реального розміру файлу -- 6400 байт (рівно 12 секторів). Скільки реально прочитано, зберігається в ReadedFromDOS. Ніякого явного контролю помилок не проводиться. Важливо, що змінні з кількістю прочитаних байт знаходяться зразу після відповідних FCB.
Після того, як файли прочитані, їх дати збережені, в обидва FCB записується цільовий диск. Файли не закриваються --- може собі дозволити, у цій версії DOS вся інформація про відкритий файл зберігається безпосередньо в FCB. (Та й відбувалося тільки читання -- спорожнювати буфери, навіть якщо такі існують, немає сенсу).
Далі стає цікавіше. Адреса кожного з FCB (і розташованої зразу після нього кількості прочитаних байт) поміщається в BX і викликається підпрограма, умовно мною названа: ChkSysFilesOnTARGETdisk.
Ця підпрограма визначає розмір файлу, на який вказує переданий FCB (засобами FCB, AH=23h/INT 21h). Тобто, увага, програма визначає розмір файлу, на диску, куди вона має перенести системний файл із тим же іменем. Якщо розмір визначити не вдається, тобто, наприклад, файл не знайдено -- повідомляється, що "No room for system on destination disk". Нормально. :-) Щоб SYS.COM могла перенести систему, відповідні системні файли вже мають бути на цільовому диску. Звичайно, вміст їх може відрізнятися. Однак, як показує подальший код, розмір, у сектора, теж має співпадати. (Алгоритм порівняння розмірів кумедний -- див. код.) Якщо розмір співпав -- процедура повертає керування, якщо ні -- переходить до виведення повідомлення "Incompatible system size" і програма завершується.
Коли всі перевірки успішно відбулися, програма переходить до запису. Для обох системних файлів DTA встановлюється на образ в пам'яті файлу, який буде записуватися (спершу --- IBMBIO_DTA), в спеціальну змінну (File_Date_ptr) поміщається дата файлу, який записуватиметься, в BX --- адреса його FCB, і викликається підпрограма запису (WriteSysFile). File_Date_ptr --- така собі глобальна змінна-вказівник, щоб єдина процедура працювала з обома файлами. (Ще одна ілюстрація бардака --- читаються файли кожен окремо, хоч і однаково, розмір їх зберігаємо після FCB, дату в іншому місці. Ну ніякої одноманітності у вирішенні однакових задач. :)
Якщо WriteSysFile обидва рази відпрацювала успішно, (хоча, перевірки помилок у ній і так немає), виводиться повідомлення "System transferred" і програма закінчується.
На завершення подивимося на WriteSysFile. Вона відкриває файл, встановлює розмір запису в 1, запис з початку, кількість байт --- розмір прочитаного файлу, збережений раніше (у змінній зразу після FCB), здійснює запис. Після цього в FCB поміщається збережена раніше дата вихідного файлу і файл закривається. Жодних перевірок на помилки не здійснюється.
"Документація"
- Програмі слід передавати коректне ім'я диску. Що таке коректні диски, строго не скажу -- слід завершити аналіз коду обробки FCB, але так виглядає, що такі, які реально існують.
- Після імені диску не повинно бути імені файлу ("b:" припустиме, а "b:somefile.ext" чи "b" -- ні). Однак, відділене пробілом може бути все, що завгодно.
- Програма читає системні файли IBMIBIO.COM/IBMDOS.COM (напевне, в MS-DOS це були б IO.SYS i DOS.SYS) з поточного диску. Якщо не може їх прочитати --- просить вставити диск із системою.
- Після цього перевіряється, чи на цільовому диску є файли з такими ж іменами. Якщо немає --- повідомляє, що на диску недостатньо місця. Якщо є, але іншого розміру (з точністю до цілих секторів), повідомляється про несумісний розмір системи. (Яка чітка і точна діагностика, слів бракує...).
- Якщо файли є, їх розмір сумісний, то прочитані з поточного диску файли перезаписують своїх тезок на цільовому диску.
Про код
Як на конкретних прикладах згадувалося вище --- код, місцями, нахабне спагеті. Переходи туди-сюди, виходи з підпрограм коли як, дані розкидано і т.д. Розбиратися для розваги -- цікаво, але працювати з ним -- жах. Подивимося, як ситуація мінялася з часом.
Дякую за увагу!
Немає коментарів:
Дописати коментар