Все, після виколупування і розборок із printf-подібною штукою, можемо перейти до самого SYS-вже-не-зовсім-COM.
Хоча рекомпілювати вихідний "конвертований" COM-файл особливого сенсу немає, захований в ньому "SYS.EXE" дизасемблювати так, що рекомпілювати його цілком можна. Єдина проблема --- не знаю, як змусити FASM генерувати "великий" MZ-заголовок: вихідний SYS.EXE виділяє під заголовок аж 200h байт, з яких зайнято заледве 30h, решта заповнені нулями. Так як писати заголовок вручну, скориставшись директивою "binary as", було лінь, та й не так цікаво, просто перед сегментом printf вставив 1B8h нулів. Результуючий файл має трохи відмінностей, від адрес релокацій і до синонімічних опкодів, але (якщо я десь не помилився в конкретних інструкціях) еквівалентний вихідному. Нагадаю, що аналіз -- внизу, після коду.
; =================================================================================== ; This file is generated by The Interactive Disassembler (IDA) ; Copyright (c) 2010 by Hex-Rays SA, <support@hex-rays.com> ; Licensed to: Freeware version ; =================================================================================== ; ; Input MD5 : 27E0BA5D178A3FD5DB12EE491042D7B1 ; Modified to compile by fasm and commented by Indrekis, indrekis2.blogspot.com ; ; File Name : PC-DOS_3.00\"Decoded" SYS.COM ; Format : EXE include 'partialBPBrecord.inc' ;.8086 format MZ entry CodeSeg:CodeSegStart ;stack 80h stack StackSeg:StackSegEnd ; heap 0xFFFF ;segment temp db 1B8h dup(0) ; =================================================================================== ; Segment type: Regular segment PrintfSeg OutFileHandler dw 1 LeftJustify db 0 IsLong db 0 printHexAsLowerLetters db 0 HexLettersTblDispl db 0 DoPrintString db 0 PaddingSize dw 0 NumberBase dw 0 PaddingChar db 20h a0123456789abcd db '0123456789ABCDEFabcdef' temp_IP dw 0 temp_CS dw 0 PrintfOutBuffer db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; 20 EndOfPrintfOutBuffer db 0 db 15h db 0 ; === SUBROUTINE ==================================================== ; Address of the pointer to the string --- in stack. ; IF there are other data, pointers to them are after the pointer to string. ; Attributes: bp-based frame Arg_0 EQU 16h Printf_sub: ; far proc push bp push dx push cx push bx push ax push di push si push es push ds mov bp, sp ; Setup stack frame. push cs pop es ;assume es:PrintfSeg mov di, PrintfOutBuffer ; ofs mov bp, [bp+Arg_0] ; BP = pushed to stack, before the call, value ; (traditionally -- from the DX), ; which contains _address_of_pointer to string ; Important! Next 2-byte words, after the pointer, ; contain pointers to other printf arguments! mov si, [ds:bp+0] ; Now SI --- string address xor bx, bx ; BX --- next variadic argument ; displacement in stack (rel. to BP+2) call PrintfInitForNext NextSymbol: ; ; lodsb cmp al, 25h ; '%' loc_9C3C: jz short DirectiveHandling or al, al jz short ZeroByteMet ; Zero -- C-string terminator call StoreChar_and_FlushBufferIfFull ; DI --- destination in buffer ; AL --- symbol jmp short NextSymbol ; =================================================================================== ; Flush buffer at exit ZeroByteMet: ; call DoPrintIfNotEmpty ; DI points to last symbol in buffer. ; If it's address == bufer start address, does nothing. pop ds pop es ; assume es:nothing loc_9C4C: pop si pop di pop ax pop bx pop cx pop dx pop bp ; Take return address from the stack, ; clear agument --- pointer to string address, ; then put return address back to stack. pop word [cs:temp_IP] pop [cs:temp_CS] pop ax push [cs:temp_CS] push word [cs:temp_IP] retf ; =================================================================================== PrintPercentChar: ; call StoreChar_and_FlushBufferIfFull ; DI --- destination in buffer ; AL --- symbol jmp short NextSymbol ; =================================================================================== ; http://www.cplusplus.com/reference/cstdio/printf/ DirectiveHandling: ; ; lodsb ; Load char after '%' -- directive code cmp al, '%' jz short PrintPercentChar cmp al, '-' ; Left justify jz short SetLeftJustify cmp al, '+' ; ? jz short ContDirectivesHandling cmp al, 'L' ; Not Long double as in modern C code ; -- just long int jz short SetLong cmp al, 'l' ; long int jz short SetLong cmp al, 30h ; '0' jb short NotDigitDirective cmp al, 39h ; '9' ja short NotDigitDirective cmp al, 30h ; '0' jnz short SetDefPaddingChar0 ; cmp [cs:PaddingSize], 0 jnz short SetDefPaddingChar0 mov byte [cs:PaddingChar], '0' SetDefPaddingChar0: push ax ; We will read paddign size digit by digist, starting from hi. ; So for each next -- mul by 10 and add it mov ax, 10 mul [cs:PaddingSize] mov [cs:PaddingSize], ax pop ax xor ah, ah sub al, 30h ; '0' add [cs:PaddingSize], ax jmp short ContDirectivesHandling ; =================================================================================== SetLeftJustify: ; inc [cs:LeftJustify] jmp short ContDirectivesHandling ; =================================================================================== SetLong: ; ; inc [cs:IsLong] ContDirectivesHandling: ; ; jmp short DirectiveHandling ; =================================================================================== NotDigitDirective: ; ; cmp al, 58h ; 'X' ; Unsigned hexadecimal ; Use upper-case digits (ABCDEF) jz short DoPrintHex2 cmp al, 61h ; 'a' jb short NotSmallChar cmp al, 7Ah ; 'z' jg short NotSmallChar and al, 0DFh ; ToUpper ; Converted directives to Upper NotSmallChar: ; ; cmp al, 58h ; 'X' ; Unsigned hexadecimal ; Was small before "ToUpper". ; So if we here -- use low-case digits, (abcdef) jz short DoPrintHex cmp al, 44h ; 'D' ; Signed decimal integer jz short PrintDecimal cmp al, 43h ; 'C' ; Character jz short PrintChar ; Save ptr to format string cmp al, 53h ; 'S' ; String of characters jz short PrintString call PrintfInitForNext jmp NextSymbol ; =================================================================================== DoPrintHex: ; mov [cs:HexLettersTblDispl], 6 DoPrintHex2: ; mov [cs:NumberBase], 16 jmp short DoPrintInt ; =================================================================================== nop PrintDecimal: ; mov [cs:NumberBase], 10 jmp short DoPrintInt ; =================================================================================== nop PrintString: ; inc [cs:DoPrintString] PrintChar: ; push si ; Save ptr to format string mov si, bx add bx, 2 ; Traveling to each next arg ; Pointer to them are after the pointer to format str. mov si, [ds:bp+si+2] ; Addres of char/string to print cmp [cs:DoPrintString], 0 jnz short PrintingString lodsb cmp al, 0 jz short Exit_PrintCharOrStr call StoreChar_and_FlushBufferIfFull ; DI --- destination in buffer ; AL --- symbol jmp short Exit_PrintCharOrStr ; =================================================================================== PrintingString: ; mov cx, [cs:PaddingSize] or cx, cx jz short do_print_str cmp byte [cs:LeftJustify], 0 jnz short do_print_str push si call kindof_strlen_and_pad ; SI -- string adress ; CX --- padding size pop si do_print_str: ; ; push si ; Save ptr to printed string do_next_char_in_str: ; lodsb cmp al, 0 jz short zero_char_met_in_str call StoreChar_and_FlushBufferIfFull ; DI --- destination in buffer ; AL --- symbol jmp short do_next_char_in_str ; =================================================================================== zero_char_met_in_str: ; pop si ; Restore ptr to printed string cmp [cs:LeftJustify], 0 jz short Exit_PrintCharOrStr mov cx, [cs:PaddingSize] or cx, cx jz short Exit_PrintCharOrStr call kindof_strlen_and_pad ; SI -- string adress ; CX --- padding size Exit_PrintCharOrStr: ; ; call PrintfInitForNext pop si ; Restore ptr to format string jmp NextSymbol ; Printf_sub endp ; === SUBROUTINE ==================================================== ; SI -- string adress ; CX --- padding size kindof_strlen_and_pad: ; proc near xor dx, dx do_next_char_check: ; lodsb or al, al jz short str_finished inc dx jmp short do_next_char_check ; =================================================================================== str_finished: ; sub cx, dx ; padding symbols jbe short no_padding ; If <0 -- no padding call PerformPadding ; CX --- number of symbols for padding, ; symbol is saved in cs:PaddingChar no_padding: ; retn ; kindof_strlen_and_pad endp ; =================================================================================== DoPrintInt: ; ; push si mov si, bx add bx, 2 ; Traveling to each next arg ; The number is after the pointer to format str. mov ax, [ds:bp+si+2] ; Number to print -- from memory to AX cmp byte [cs:IsLong], 0 jz short notLongInt ; If not long --- upper two bytes, in DX, are zero mov si, bx add bx, 2 ; If printing long --- it is placed in next two bytes mov dx, [ds:bp+si+2] jmp short LongIntPrepared ; =================================================================================== notLongInt: ; xor dx, dx ; If not long --- upper two bytes, in DX, are zero ; Here DX:AX contains high and low part of number LongIntPrepared: ; push bx mov si, [cs:NumberBase] mov cx, [cs:PaddingSize] call ExtractDigits ; Here DX:AX contains high and low part of number ; SI -- base of system ; CX --- padding size ; Recursively calls itself ; On return --- CX=padding left call PerformPadding ; CX --- number of symbols for padding, ; symbol is saved in cs:PaddingChar call PrintfInitForNext pop bx pop si jmp NextSymbol ; === SUBROUTINE ==================================================== ; Here DX:AX contains high and low part of number ; SI -- base of system ; CX --- padding size ; Recursively calls itself ; On return --- CX=padding left ExtractDigits: ; proc near dec cx ; -1 for padding - we have real digit push ax ; Save AX -- lower part mov ax, dx ; AX = upper part xor dx, dx div si ; Div DX:AX pair (DX=0, AX -- upper part of number) by SI -- base ; Result: AX=Quo, DX=Rem ; SI is base, so DX --- obtained digit mov bx, ax ; quotient->BX pop ax ; Restore AX -- lower part div si ; DX(reminder of upper/base):AX(lower) by SI(base) xchg bx, dx ; BX -- rem, DX --- upper/base ; So, BX --- least significant digit in base=SI system push ax ; Save AX=(lower)/SI(base) or ax, dx ; upper/base OR lower/base?.. pop ax ; Restore AX jz short AllDigitsDone push bx call ExtractDigits ; Continue with (DX:AX)/SI --- remains ; (not remainder!!!) to generate next digit pop bx jmp short DoLeftJustify ; AX -- obtainded digit ; =================================================================================== AllDigitsDone: ; cmp [cs:LeftJustify], 0 jnz short DoLeftJustify ; AX -- obtainded digit call PerformPadding ; CX --- number of symbols for padding, ; symbol is saved in cs:PaddingChar DoLeftJustify: ; mov ax, bx ; AX -- obtainded digit cmp al, 10 ; Check if use non-0-9-digits jb short generateDigit cmp [cs:printHexAsLowerLetters], 0 jnz short generateDigit add al, [cs:HexLettersTblDispl] generateDigit: ; mov bx, a0123456789abcd ; "0123456789ABCDEFabcdef" push ds push cs pop ds ; assume ds:PrintfSeg xlatb ; Set AL to memory byte DS:[(E)BX + unsigned AL] ; So, after, AL --- char code for digit, that was in AL pop ds ; assume ds:nothing push cx call StoreChar_and_FlushBufferIfFull ; DI --- destination in buffer ; AL --- symbol pop cx retn ; ExtractDigits endp ; === SUBROUTINE ==================================================== ; CX --- number of symbols for padding, ; symbol is saved in cs:PaddingChar PerformPadding: ; proc near or cx, cx jle short locret_9E01 mov al, byte [cs:PaddingChar] NextSymbolP: ; push cx call StoreChar_and_FlushBufferIfFull ; DI --- destination in buffer ; AL --- symbol pop cx loop NextSymbolP locret_9E01: ; retn ; PerformPadding endp ; === SUBROUTINE ==================================================== ; DI --- destination in buffer ; AL --- symbol StoreChar_and_FlushBufferIfFull: ; proc near stosb cmp di, word EndOfPrintfOutBuffer ; word -- to choose correct instruction form; ofs ; it is not optimal, though jz short BufferIsFull locret_9E09: ; retn ; =================================================================================== BufferIsFull: ; mov cx, EndOfPrintfOutBuffer-PrintfOutBuffer ; Buffer size ; StoreChar_and_FlushBufferIfFull endp ; === SUBROUTINE ==================================================== ; Prints fixed-positioned in memory buffer. ; Number of bytes in CX. DoPrintSymbols: ; proc near push bx mov bx, [cs:OutFileHandler] push ds push cs pop ds ; assume ds:PrintfSeg mov dx, PrintfOutBuffer ; ofs mov ah, 40h int 21h ; DOS - 2+ - WRITE TO FILE WITH HANDLE ; BX = file handle, CX = number of bytes to write, DS:DX -> buffer pop ds ; assume ds:nothing pop bx mov di, PrintfOutBuffer ; ofs retn ; DoPrintSymbols endp ; === SUBROUTINE ==================================================== ; DI points to last symbol in buffer. ; If it's address == bufer start address, does nothing. DoPrintIfNotEmpty: ; proc near cmp di, word PrintfOutBuffer ; ofs jz short locret_9E09 sub di, word PrintfOutBuffer ; ofs mov cx, di call DoPrintSymbols ; Prints fixed-positioned in memory buffer. ; Number of bytes in CX. retn ; DoPrintIfNotEmpty endp ; === SUBROUTINE ==================================================== PrintfInitForNext: ; proc near xor ax, ax mov [cs:LeftJustify], al mov [cs:IsLong], al mov [cs:HexLettersTblDispl], al mov [cs:PaddingSize], ax mov byte [cs:PaddingChar], 20h ; ' ' mov [cs:DoPrintString], al retn ; PrintfInitForNext endp ; PrintfSeg ends segment CodeSeg CodeSegStart: jmp short EntryPoint2 ; =================================================================================== word_9E52 dw 6C8h ; NewBoot addr aSys1_86 db 'SYS 1.86' ; =================================================================================== EntryPoint2: ; push ax ; Save AX ; Then check for DOS version. Exit if older than 3.00 mov ah, 30h int 21h ; DOS - GET DOS VERSION ; Return: AL = major version number (00h for DOS 1.x) xchg ah, al cmp ax, 300h jb short WrongDOSVersion pop ax ; Restore AX ; ; Copy initial FCB from PSP to dedicated area ; Not done by 2.10 push cs pop es ; assume es:CodeSeg mov si, 5Ch ; '\' ; First FCB start in PSP mov di, FCBs_Copy ; ofs mov cx, 20h ; ' ' ; Two initial FCB's size rep movsb ; Copy them push cs pop ds ; Set new DTA ; Not done by 2.10 ; assume ds:CodeSeg mov dx, NewDefDTA ; ofs mov ah, 1Ah int 21h ; DOS - SET DISK TRANSFER AREA ADDRESS ; DS:DX -> disk transfer buffer jmp short Continue1 ; =================================================================================== nop ; Still uses AH=9/INT 21h, because system is unknown WrongDOSVersion: ; push cs pop ds mov dx, aIncorrectDosVe ; "Incorrect DOS version\r\n$" ; ofs mov ah, 9 int 21h ; DOS - PRINT STRING ; DS:DX -> string terminated by "$" push es ; Jump to PSP:0 --- efficiently exit program. ; Though, why this perverted way? xor ax, ax push ax retf ; =================================================================================== printInvalidParameter: ; mov dx, InvalidParameter_Adr ; ofs jmp call_printf_n_exit ; =================================================================================== printInvalidDriveSpec: ; ; mov dx, InvalidDriveSpec_Adr ; ofs jmp call_printf_n_exit ; =================================================================================== ; New in 3.00 ; Before ask to insert system disk, checks if it is ; removable CheckIfSrcRemovable: ; ; mov ah, 19h int 21h ; DOS - GET DEFAULT DISK NUMBER ; Return: ; AL = drive (00h = A:, 01h = B:, etc) mov bl, al inc bl ; From DOS to FCB driver numbering mov ax, 4408h int 21h ; IOCTL - CHECK IF BLOCK DEVICE REMOVABLE ; BL = drive number (00h = default, 01h = A:, etc) ; ; Return: ; CF clear if successful, CF set on error ; AX = media type (0000h removable, 0001h fixed) jb short AskToInsertSysDisk cmp ax, 0 jz short AskToInsertSysDisk ; If IOCTL is successfull and disk is not removable -- exit mov dx, NoSystemOnDefDrv_Adr ; ofs push dx call PrintfSeg:Printf_sub mov ax, 4C01h int 21h ; DOS - 2+ - QUIT WITH EXIT CODE (EXIT) ; AL = exit code ; =================================================================================== AskToInsertSysDisk: ; ; mov al, [currentDrive] add al, 40h ; '@' mov byte [DriverLetter1_Str], al ; "A" mov dx, InsertSystemDisk_Adr ; ofs push dx call PrintfSeg:Printf_sub call WaitForAnyKey ; Clears buffer before and after. xor al, al ; Restore AL -- 00 means correct first FCB (we have ; already checked). Continue1: ; cmp [FCBs_Copy+1], 20h ; ' ' ; Here we have copy of first FCB from PSP. ; FCBs_Copy+1 -- first byte of filename parameter ; So, here will be any symbol, entered after [a-z][:][\] ; ; IMPORTANT! ; sys b:\a123.bcd ; sys b:/cd.efg ; will just transfer system! -- '\', '/' are ignored in that FCB ; sys b:cd.efg ; ; Checks are in the same order as for 2.10 ; though error messages are -- "Invalid parameter", not "Invalid driver spec." jnz short printInvalidParameter cmp al, 0FFh jz short printInvalidDriveSpec cmp [FCBs_Copy], 0 ; "" jz short printInvalidDriveSpec mov ah, 19h int 21h ; DOS - GET DEFAULT DISK NUMBER inc al mov [currentDrive], al cmp [FCBs_Copy], al ; "" jz short printInvalidDriveSpec ; Check if disk is remote. (New check) ; Ralf Brown list states, that this call was present, starting from 3.1 ; ( http://www.ctyme.com/intr/rb-2891.htm ) ; We are in 3.00, but SYS.COM clearly use it :-) ; Though, http://www.os2museum.com/wp/?page_id=639 states, ; that redirector interface was already present in 3.00, so ; this call was already present too, though, may be, undocumented. push ax mov bl, [FCBs_Copy] ; "" mov ax, 4409h int 21h ; IOCTL - CHECK IF BLOCK DEVICE REMOTE ; http://www.ctyme.com/intr/rb-2891.htm ; ; Return: CF clear if successful ; DX = device attribute word ; ; bit 15: Drive is SUBSTituted ; bit 12: Drive is remote ; bit 9: Direct I/O not allowed. ; ; CF set on error, AX = error code (01h,0Fh,15h) ; ; For remote drives, the other bits appear to be undefined for MS-DOS versions prior to 5.0 (they are all cleared in DOS 5+) jb short ioctl_err_or_driver_is_not_remote test dx, 1000h ; 12-th bit set in mask jz short ioctl_err_or_driver_is_not_remote mov dx, CantSysToNetDrv_Adr ; ofs jmp call_printf_n_exit ; =================================================================================== ; We jump here, if IOCTL call returned error ; or driver is not remote. ; In opposite case we print "Incorrect DOS version"... ; Check FAT on destination disk -- as in 2.10 ioctl_err_or_driver_is_not_remote: ; ; pop ax push ax mov al, [FCBs_Copy] ; Target driver number dec al ; Adjust from FCB disks numbering to DOS numbering mov bx, buffer1 ; ofs ; Read first sector --- boot mov dx, 1 mov cx, dx ; Read one first (starting numeration from 1!) sector int 25h ; DOS - ABSOLUTE DISK READ (except DOS 4.0/COMPAQ DOS 3.31 >32M partitn) ; AL = drive number (0=A, 1=B, etc), DS:BX = Disk Transfer Address (buffer) ; CX = number of sectors to read, DX = first relative sector to read ; Return: CF set on error pop ax ; Pop flags, left by int 25 pop ax ; Restore AX jb short CheckSysFiles ; If error --- proceed to checking?! cmp byte [buffer1], 0F8h ; 'ш' ; Media ID for HDD "Designated to be used for any ; partitioned fixed or removable media, where the ; geometry is defined in the BPB." ; DOS 2.10 do not know smaller ID's. ; Looks like 3.00 -- too. jnb short AdditionalChecksForKnownDisk ; Here v2.10 jumps directly ; to CheckSysFiles jmp printNoRoomForSystem ; =================================================================================== CheckSysFiles: ; jmp CheckSysFiles_ ; Disk number to letter --- source ; =================================================================================== ; If we are sure, that disk is of known nature ; let's check, if it has correct structure ; for system files --- for example, their ; directory entries are first and second, correspondingly AdditionalChecksForKnownDisk: ; push ax ; Save AX ; Set driver letter to system filenames strings mov al, byte [FCBs_Copy] ; "" add al, 40h ; '@' mov byte [aAIbmbio_com], al ; "A:\\IBMBIO.COM" mov byte [aAIbmdos_com], al ; "A:\\IBMDOS.COM" ; mov ah, 4Eh ; 'N' mov dx, aAIbmbio_com ; "A:\\IBMBIO.COM" ; ofs mov cx, 6 int 21h ; DOS - 2+ - FIND FIRST ASCIZ (FINDFIRST) ; CX = search attributes ; DS:DX -> ASCIZ filespec ; (drive, path, and wildcards allowed) jb short local_skip_inc1 inc byte [IBMBIO_fount_flag] local_skip_inc1: ; mov ah, 4Eh ; 'N' mov dx, aAIbmdos_com ; "A:\\IBMDOS.COM" ; ofs int 21h ; DOS - 2+ - FIND FIRST ASCIZ (FINDFIRST) ; CX = search attributes ; DS:DX -> ASCIZ filespec ; (drive, path, and wildcards allowed) jb short local_skip_inc2 inc [IBMDOS_fount_flag] local_skip_inc2: ; mov dl, byte [FCBs_Copy] ; "" mov ah, 32h ; '2' push ds int 21h ; DOS - 2+ internal - GET DRIVE PARAMETER BLOCK ; DL = drive number, 0 = default, 1 = A, etc. ; DS:BX -> Drive Parameter Block (DPB) ; ; http://www.ctyme.com/intr/rb-2724.htm ; Format of DOS Drive Parameter Block: ; Offset Size Description (Table 01395) ; 00h BYTE drive number (00h = A:, 01h = B:, etc) ; 01h BYTE unit number within device driver ; 02h WORD bytes per sector ; 04h BYTE highest sector number within a cluster ; 05h BYTE shift count to convert clusters into sectors ; 06h WORD number of reserved sectors at beginning of drive ; 08h BYTE number of FATs ; 09h WORD number of root directory entries ; 0Bh WORD number of first sector containing user data ; 0Dh WORD highest cluster number (number of data clusters + 1) ; 16-bit FAT if greater than 0FF6h, else 12-bit FAT ; 0Fh BYTE number of sectors per FAT ; 10h WORD sector number of first directory sector ; 12h DWORD address of device driver header (see #01646) ; 16h BYTE media ID byte (see #01356) ; 17h BYTE 00h if disk accessed, FFh if not ; 18h DWORD pointer to next DPB ; ---DOS 2.x--- ; 1Ch WORD cluster containing start of current directory, 0000h=root, ; FFFFh = unknown ; 1Eh 64 BYTEs ASCIZ pathname of current directory for drive ; ---DOS 3.x--- ; 1Ch WORD cluster at which to start search for free space when writing ; 1Eh WORD number of free clusters on drive, FFFFh = unknown mov dx, [bx+10h] ; sector number of first directory sector in DPB pop ds ; assume ds:nothing mov al, byte [ds:FCBs_Copy] ; "" dec al ; From FCB to DOS disk number mov bx, buffer1 ; ofs mov cx, 1 ; Read first root directory sector int 25h ; DOS - ABSOLUTE DISK READ (except DOS 4.0/COMPAQ DOS 3.31 >32M partitn) ; AL = drive number (0=A, 1=B, etc), DS:BX = Disk Transfer Address (buffer) ; CX = number of sectors to read, DX = first relative sector to read ; Return: CF set on error pop ax jnb short CheckDir_directly ; If direct read fails --- just continue, ; supposing, that disk is just unknown (and managed by some driver, for example) jmp short CheckSysFiles_aft ; =================================================================================== nop ; Check if first directory entries are IBMBIO and IBMDOS ; in readed first root directory sector CheckDir_directly: ; mov si, buffer1 ; ofs cmp byte [si], 0 ; 0 --- directory record unused jz short CheckIfSysFilesBothFoundNAtCorrectPlace cld cmp byte [si], 0E5h ; 'е' ; Directory entry -- erased jz short local_check_IBMDOS ; NExt directory entry mov di, aIbmbioCom ; "IBMBIO COM" ; ofs mov cx, 11 repe cmpsb jnz short printNoRoomForSystem_2 dec byte [ds:IBMBIO_position_not_correct] local_check_IBMDOS: ; mov si, (buffer1+20h) ; NExt directory entry ; ofs cmp byte [si], 0 jz short CheckIfSysFilesBothFoundNAtCorrectPlace cmp byte [si], 0E5h ; 'е' jz short CheckIfSysFilesBothFoundNAtCorrectPlace mov di, aIbmdosCom ; "IBMDOS COM" ; ofs mov cx, 11 repe cmpsb printNoRoomForSystem_2: ; jnz short printNoRoomForSystem_ dec byte [ds:IBMDOS_position_not_correct] ; If found, hi and lo bytes of word ptr IBMDOS_found_flag will be 1, ; if found at correct position in root dir, both bytes of IBMDOS_position_correct ; will be equal to 0, otherwise will be 0-1 = 0FFh, ; So if not found at all or in correct position in dir -- OK CheckIfSysFilesBothFoundNAtCorrectPlace: ; ; mov ax, word [ds:IBMDOS_fount_flag] and ax, word [ds:IBMDOS_position_not_correct] jnz short printNoRoomForSystem_ CheckSysFiles_aft: ; pop ax CheckSysFiles_: ; add al, 40h ; '@' ; Disk number to letter --- source mov byte [ds:aAIbmbio_com], al ; "A:\\IBMBIO.COM" mov byte [ds:aAIbmdos_com], al ; "A:\\IBMDOS.COM" cld mov dx, aAIbmbio_com ; "A:\\IBMBIO.COM" ; ofs mov di, IBMBIO_hndlr ; ofs call GetFileSizeDateTime ; DX -- filename string ; DI -- address of file handler variable ; CF -- on error, ; If OK, Size, Time and Date are saved after the Handler ; Strange that size is saved twice -- two times lo word ; and two times hi word. ; ; Routine is the same as in 2.10 jnb short checkIBMDOS AskToInsertSysDisk_: ; ; jmp CheckIfSrcRemovable ; =================================================================================== checkIBMDOS: ; mov dx, aAIbmdos_com ; "A:\\IBMDOS.COM" ; ofs mov di, IBMDOS_hndlr ; ofs call GetFileSizeDateTime ; DX -- filename string ; DI -- address of file handler variable ; CF -- on error, ; If OK, Size, Time and Date are saved after the Handler ; Strange that size is saved twice -- two times lo word ; and two times hi word. ; ; Routine is the same as in 2.10 jb short AskToInsertSysDisk_ mov cx, sp sub cx, 0B32h ; Approximation of code+stack size? mov [ds:PutativeMaxFreeMem], cx call ReadFilesToMem ; Tries to read IBMBIO and IBMDOS to memory. ; Read as much as fits. Sets CF in case of errors, ; clears otherwise. ; ; Same as in 2.10 jb short AskToInsertSysDisk_ ; Now checking files at target disk root ; Set target driver letter to system filenames strings and search mask mov al, byte [ds:FCBs_Copy] ; "" add al, 40h ; '@' mov byte [ds:aAIbmbio_com], al ; "A:\\IBMBIO.COM" mov byte [ds:aAIbmdos_com], al ; "A:\\IBMDOS.COM" mov byte [ds:anyFileMask], al ; "A:\\*.*" mov ah, 4Eh ; 'N' mov dx, anyFileMask ; "A:\\*.*" ; ofs mov cx, 10110b ; Hidden + system + dir, any cobination of, inclusive int 21h ; DOS - 2+ - FIND FIRST ASCIZ (FINDFIRST) ; CX = search attributes (bits 0 and 5 ignored) ; DS:DX -> ASCIZ filespec ; (drive, path, and wildcards allowed) ; 7 pending deleted files (Novell DOS, OpenDOS) ; 6 unused ; 5 archive ; 4 directory ; 3 volume label. ; 2 system ; 1 hidden ; 0 read-only ; http://www.ctyme.com/intr/rb-2803.htm jnb short someFilesFound ; ; No files, check for volume label mov ah, 4Eh ; 'N' mov dx, anyFileMask ; "A:\\*.*" ; ofs ; For search attributes other than 08h, all files ; with at MOST the specified combination of hidden, ; system, and directory attributes will be returned. ; Under DOS 2.x, searching for attribute 08h (volume ; label) will also return normal files, while under ; DOS 3.0+ only the volume label (if any) will be returned.. mov cx, 1000b ; Vol Label int 21h ; DOS - 2+ - FIND FIRST ASCIZ (FINDFIRST) ; CX = search attributes ; DS:DX -> ASCIZ filespec ; (drive, path, and wildcards allowed) jb short noneFilesFound jmp printNoRoomForSystem ; =================================================================================== ; ; If some files found --- try to find IBMBIO.COM ; IF not found --- exit. someFilesFound: ; mov dx, aAIbmbio_com ; "A:\\IBMBIO.COM" ; ofs mov cx, 111b mov ah, 4Eh int 21h ; DOS - 2+ - FIND FIRST ASCIZ (FINDFIRST) ; CX = search attributes ; DS:DX -> ASCIZ filespec ; (drive, path, and wildcards allowed) jnb short someFilesFound_checkIBMDOS printNoRoomForSystem_: ; ; jmp printNoRoomForSystem ; =================================================================================== ; ; Otherwise --- check for IBMDOS.COM someFilesFound_checkIBMDOS: ; mov dx, aAIbmdos_com ; "A:\\IBMDOS.COM" ; ofs mov ah, 4Eh int 21h ; DOS - 2+ - FIND FIRST ASCIZ (FINDFIRST) ; CX = search attributes ; DS:DX -> ASCIZ filespec ; (drive, path, and wildcards allowed) jb short printNoRoomForSystem_ ; ; Here we, if none files were found, or are some files found, ; and both IBMxxx.COM are found too noneFilesFound: ; mov dx, aAIbmbio_com ; "A:\\IBMBIO.COM" ; ofs mov cx, 0 ; Clear attributes of IBMBIO.COM mov ax, 4301h int 21h ; DOS - 2+ - SET FILE ATTRIBUTES ; DS:DX -> ASCIZ file name ; CX = file attribute bits mov dx, aAIbmdos_com ; "A:\\IBMDOS.COM" ; ofs mov cx, 0 ; Clear attributes of IBMDOS.COM mov ax, 4301h int 21h ; DOS - 2+ - SET FILE ATTRIBUTES ; DS:DX -> ASCIZ file name ; CX = file attribute bits ; (Re)create IBMxxx.COM with correct attributes --- sys+hid+r/o mov dx, aAIbmbio_com ; "A:\\IBMBIO.COM" ; ofs mov cx, 111b mov ah, 3Ch int 21h ; DOS - 2+ - CREATE A FILE WITH HANDLE (CREAT) ; CX = attributes for file ; DS:DX -> ASCIZ filename (may include drive and path) jb short printNoRoomForSystem_3 mov [ds:TargetIBMBIO_hndlr], ax mov dx, aAIbmdos_com ; "A:\\IBMDOS.COM" ; ofs mov ah, 3Ch int 21h ; DOS - 2+ - CREATE A FILE WITH HANDLE (CREAT) ; CX = attributes for file ; DS:DX -> ASCIZ filename (may include drive and path) jb short printNoRoomForSystem_3 mov [ds:TargetIBMDOS_hndlr], ax push ds mov ah, 32h ; '2' mov dl, byte [ds:FCBs_Copy] ; "" int 21h ; DOS - 2+ internal - GET DRIVE PARAMETER BLOCK ; DL = drive number, 0 = default, 1 = A, etc. ; For DPB: cluster containing start of current directory, 0000h=root ; Why to change it?! Some internal undocumented hack? ; --- way to set root dir as current? mov word [bx+1Ch], 0 pop ds ReadNWriteNextPart: ; call WriteSysFilesFromMem ; Same as from 2.10 jb short printNoRoomForSystem_3 mov ax, [ds:IBMDOS_size_hi_1] or ax, [ds:IBMDOS_size_lo_1] or ax, [ds:IBMBIO_size_hi_1] or ax, [ds:IBMBIO_size_lo_1] jz short RestoreFilesDateNTime call ReadFilesToMem ; Tries to read IBMBIO and IBMDOS to memory. ; Read as much as fits. Sets CF in case of errors, ; clears otherwise. ; ; Same as in 2.10 jnb short ReadNWriteNextPart jmp CheckIfSrcRemovable ; Also asks to insert system disk ; =================================================================================== printNoRoomForSystem_3: ; ; jmp printNoRoomForSystem ; =================================================================================== ; Looks like dead, unreachable, code mov dx, IncompatibleSysSize_Adr ; ofs jmp short call_printf_n_exit ; =================================================================================== nop RestoreFilesDateNTime: ; mov cx, [ds:IBMBIO_time] mov dx, [ds:IBMBIO_date] mov bx, [ds:TargetIBMBIO_hndlr] mov ax, 5701h int 21h ; DOS - 2+ - SET FILE'S DATE/TIME ; BX = file handle, CX = time to be set ; DX = date to be set mov ah, 3Eh int 21h ; DOS - 2+ - CLOSE A FILE WITH HANDLE ; BX = file handle mov cx, [ds:IBMDOS_time] mov dx, [ds:IBMDOS_date] mov bx, [ds:TargetIBMDOS_hndlr] mov ax, 5701h int 21h ; DOS - 2+ - SET FILE'S DATE/TIME ; BX = file handle, CX = time to be set ; DX = date to be set mov ah, 3Eh int 21h ; DOS - 2+ - CLOSE A FILE WITH HANDLE ; BX = file handle call WriteBoot ; Writes boot with correct BPB ; For HDD takes BPB from some DOS internal tables. ; For FDD --- uses it's own collection of BPB's for different MediaID's ; Rewritten for DOS 3.0! mov dx, SystemTransferred_Adr ; ofs push dx call PrintfSeg:Printf_sub xor al, al jmp short exit_program ; =================================================================================== call_printf_n_exit: ; ; push dx ; DX contains address of message call PrintfSeg:Printf_sub mov dx, CR_LF_Adr ; ofs push dx call PrintfSeg:Printf_sub mov al, 0FFh ; Error code, -1 exit_program: ; mov ah, 4Ch int 21h ; DOS - 2+ - QUIT WITH EXIT CODE (EXIT) ; AL = exit code ; =================================================================================== ; START OF FUNCTION CHUNK FOR ReadFilesToMem local_error: ; ; pop cx ; Restore saved CX Set_CF_NExit_A0E2: ; stc Retn_A0E3: ; retn ; END OF FUNCTION CHUNK FOR ReadFilesToMem ; === SUBROUTINE ==================================================== ; Tries to read IBMBIO and IBMDOS to memory. ; Read as much as fits. Sets CF in case of errors, ; clears otherwise. ; ; Same as in 2.10 ReadFilesToMem: ; proc near mov cx, [ds:PutativeMaxFreeMem] mov bx, [ds:IBMBIO_hndlr] mov dx, buffer1 ; ofs push cx cmp [ds:IBMBIO_size_hi_1], 0 ; If size above 64k -- read max size, which can be ; disposed in free mem. ja short PRoceedToRead cmp [ds:IBMBIO_size_lo_1], cx ; If size is large than can feet in memory -- ; read max size, which can be disposed in free mem. ja short PRoceedToRead mov cx, [ds:IBMBIO_size_lo_1] ; Else read exactly file size bytes PRoceedToRead: ; ; mov ah, 3Fh int 21h ; DOS - 2+ - READ FROM FILE WITH HANDLE ; BX = file handle, CX = number of bytes to read ; DS:DX -> buffer ; ; Returns AX = number of bytes actually read jb short local_error cmp ax, cx ; Readed less than requested size? jnz short local_error add dx, ax ; Calculate first free byte address mov [ds:IBMDOS_buffer_addr], dx sub [ds:IBMBIO_size_lo_1], ax ; Calculate, how much left to read sbb [ds:IBMBIO_size_hi_1], 0 pop cx sub cx, ax ; Calculate free memory left mov bx, [ds:IBMDOS_hndlr] cmp word [ds:IBMDOS_size_hi_1], 0 ; If size above 64k -- read max size, which can be ; disposed in free mem. ja short ProceedToRead2 cmp word [ds:IBMDOS_size_lo_1], cx ; If size is large than can feet in memory -- ; read max size, which can be disposed in free mem. ja short ProceedToRead2 mov cx, word [ds:IBMDOS_size_lo_1] ; Else read exactly file size bytes ProceedToRead2: ; ; mov ah, 3Fh int 21h ; DOS - 2+ - READ FROM FILE WITH HANDLE ; BX = file handle, CX = number of bytes to read ; DS:DX -> buffer ; ; Returns AX = number of bytes actually read jb short Retn_A0E3 ; Error -- exit cmp ax, cx jnz short Set_CF_NExit_A0E2 ; Readed less than requested -- exit add dx, ax ; Calculate first free byte address mov [ds:After_IBMDOS_buffer], dx sub [ds:IBMDOS_size_lo_1], ax ; Calculate, how much left to read sbb word [ds:IBMDOS_size_hi_1], 0 clc ; Clear CF -- no error. Retn_A14C: ; retn ; ReadFilesToMem endp ; === SUBROUTINE ==================================================== ; DX -- filename string ; DI -- address of file handler variable ; CF -- on error, ; If OK, Size, Time and Date are saved after the Handler ; Strange that size is saved twice -- two times lo word ; and two times hi word. ; ; Routine is the same as in 2.10 GetFileSizeDateTime: ;proc near mov ax, 3D00h int 21h ; DOS - 2+ - OPEN DISK FILE WITH HANDLE ; DS:DX -> ASCIZ filename ; AL = access mode ; 0 - read jb short Retn_A14C stosw ; Save file handler ; Determine and save file size mov bx, ax mov ax, 4202h xor cx, cx xor dx, dx 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 xor dx, dx mov ax, 4200h int 21h ; DOS - 2+ - MOVE FILE READ/WRITE POINTER (LSEEK) ; AL = method: offset from beginning of file mov ax, 5700h int 21h ; DOS - 2+ - GET FILE'S DATE/TIME ; BX = file handle ; http://www.ctyme.com/intr/rb-2992.htm ; CX = file's time (see #01665) ; DX = file's date mov ax, cx stosw ; Store time mov ax, dx stosw ; Store date Retn_A178: ; ; WriteSysFilesFromMem+23 j ... retn ; GetFileSizeDateTime endp ; =================================================================================== printNoRoomForSystem: ; ; mov dx, NoRoomForSystem_Adr ; ofs jmp call_printf_n_exit ; === SUBROUTINE ==================================================== ; Same as from 2.10 WriteSysFilesFromMem: ; proc near mov dx, buffer1 ; ofs mov cx, [ds:IBMDOS_buffer_addr] sub cx, dx jz short ProceedToIBMDOS mov bx, [ds:TargetIBMBIO_hndlr] mov ah, 40h int 21h ; DOS - 2+ - WRITE TO FILE WITH HANDLE ; BX = file handle, CX = number of bytes to write, DS:DX -> buffer jb short Retn_A178 cmp ax, cx ; Written less than should? jnz short WriteError ProceedToIBMDOS: ; mov dx, [ds:IBMDOS_buffer_addr] mov cx, [ds:After_IBMDOS_buffer] sub cx, dx jz short Retn_A178 mov bx, [ds:TargetIBMDOS_hndlr] mov ah, 40h int 21h ; DOS - 2+ - WRITE TO FILE WITH HANDLE ; BX = file handle, CX = number of bytes to write, DS:DX -> buffer jb short Retn_A178 cmp ax, cx ; Written less than should? jz short Retn_A178 WriteError: ; stc retn ; WriteSysFilesFromMem endp ; === SUBROUTINE ==================================================== ; Writes boot with correct BPB ; For HDD takes BPB from some DOS internal tables. ; For FDD --- uses it's own collection of BPB's for different MediaID's ; Rewritten for DOS 3.0! WriteBoot: ; proc near mov ah, 32h ; '2' mov dl, byte [ds:FCBs_Copy] ; "" int 21h ; DOS - 2+ internal - GET DRIVE PARAMETER BLOCK ; DL = drive number, 0 = default, 1 = A, etc. ; DS:BX -> Drive Parameter Block (DPB) ; ; http://www.ctyme.com/intr/rb-2724.htm ; Format of DOS Drive Parameter Block: ; Offset Size Description (Table 01395) ; 00h BYTE drive number (00h = A:, 01h = B:, etc) ; 01h BYTE unit number within device driver ; 02h WORD bytes per sector ; 04h BYTE highest sector number within a cluster ; 05h BYTE shift count to convert clusters into sectors ; 06h WORD number of reserved sectors at beginning of drive ; 08h BYTE number of FATs ; 09h WORD number of root directory entries ; 0Bh WORD number of first sector containing user data ; 0Dh WORD highest cluster number (number of data clusters + 1) ; 16-bit FAT if greater than 0FF6h, else 12-bit FAT ; 0Fh BYTE number of sectors per FAT ; 10h WORD sector number of first directory sector ; 12h DWORD address of device driver header (see #01646) ; 16h BYTE media ID byte (see #01356) ; 17h BYTE 00h if disk accessed, FFh if not ; 18h DWORD pointer to next DPB ; ---DOS 2.x--- ; 1Ch WORD cluster containing start of current directory, 0000h=root, ; FFFFh = unknown ; 1Eh 64 BYTEs ASCIZ pathname of current directory for drive ; ---DOS 3.x--- ; 1Ch WORD cluster at which to start search for free space when writing ; 1Eh WORD number of free clusters on drive, FFFFh = unknown mov al, [bx+16h] ; media ID byte ; ; http://www.ctyme.com/intr/rb-2590.htm#Table1356 ; FFh floppy, double-sided, 8 sectors per track, 40 tracks (320K) ; FEh floppy, single-sided, 8 sectors per track, 40 tracks (160K) ; FDh floppy, double-sided, 9 sectors per track, 40 tracks (360K) ; FCh floppy, single-sided, 9 sectors per track, 40 tracks (180K) ; + up to DOS 3.0 (only 5.25 floppies, though ID's were used later for similar 3.5): ; FBh floppy, double-sided, 8 sectors per track, 80 tracks (640K) ; FAh floppy, single-sided, 8 sectors per track, 80 tracks (320K) ; F9h floppy, double-sided, 15/9/18 sectors per track, 80 tracks (1200K/720K/1440K - 5.25/3.5/3.5) ; See also: http://en.wikipedia.org/wiki/File_Allocation_Table#FATID push cs loc_A1C0: ; Restore DS pop ds ; assume ds:CodeSeg sub al, 0F8h ; 'ш' cbw ; AX = MediaID-0F8h, DOS 3.00 knows floppy codes up to F9h ; (according to http://en.wikipedia.org/wiki/File_Allocation_Table) mov bx, ax shl bx, 1 ; mul MediaID - 0F8h by 2, offset in table mov si, [bx+DiskBPBsPtrsTable] or si, si jnz short notHDD ; Zero is defined in table only for 0F8h code ; Looks like here we are only for HDD mov byte [MediaIDInBPB], 0F8h ; 'ш' ; Media ID xor bx, bx ; BX = 0 mov [lds_offset], bx ; 0 push ds ; Save DS lds bx, dword [lds_offset] ; assume ds:nothing mov bx, [bx] ; DS:BX = 70:0 --- IBMBIO begins here... ; Some pointer there for some data? mov al, [bx] inc bx pop ds ; Restore DS mov [ds:lds_offset], bx ; BX -- value of pointer at 70:0 points + 1... mov byte [ds:PossiblyPhysicalDiskNumber], al ; AL -- value, obtained from ; where pointer at 70:0 points + 1... int 11h ; EQUIPMENT DETERMINATION ; Return: AX = equipment flag bits ; http://www.ctyme.com/intr/rb-0575.htm rol al, 1 rol al, 1 ; Bring bits 6-7 to 0-1, ; Bits 7-6 - number of floppies installed less 1 (if bit 0 set) and al, 11b ; Mask num_flop-1 jnz short more_than_1_floppy inc al ; Some magic with disks number... more_than_1_floppy: ; inc al ; bits fdds al_val ; 00 1 2 ; 00 2 2 ; 00 3 3 ; 00 4 4 ; Accounting "virtual" B for one floppy system? mov dl, byte [ds:FCBs_Copy] ; "" sub dl, al dec dl ; Disk code from FCB (-1 for DOS numbering) ; minus floppies number ; May be this is related to some internal DOS data structures organization ; If target disk number is last floppy --- adjusts by 13, DOS2 BPB size... jz short loc_A20E add [ds:lds_offset], 13h ; Size of DOS 2.0 BPB ; Kind of second HDD detection ; and taking into account? inc byte [ds:PossiblyPhysicalDiskNumber] ; ; Copy disk BPB from some internall structure loc_A20E: ; lds si, dword [ds:lds_offset] push cs pop es mov di, BPB_Start ; Bytes per logical sector in powers of two, 512 ; ofs mov cx, 13h ; ; Size of DOS 2.0 BPB cld rep movsb ; Move byte DS:[SI] to ES:[DI] ; Looks like from some segment in IBMIO to our BPB image push cs pop ds ; Restore DS ; assume ds:CodeSeg jmp short ProceedToWrite ; =================================================================================== notHDD: ; cmp si, 0FFFFh jz short skip_writeBoot ; Move part of BPB, from byte 2 (not 0!) to 13 -- 12 bytes (DOS2 part of it) ; Index of correct BPB --- in SI, from table DiskBPBsPtrsTable push ds pop es mov di, SectorsPerCluster ; ofs mov cx, 12h cld rep movsb ; Move 12h=18 bytes from DS:[SI] to ES:[DI] ; We have prepaired correct BPB for disk ID ; (using some internal buffer for HDD BPB and our own table for floppies) ProceedToWrite: ; mov al, byte [FCBs_Copy] ; Target disk dec al ; From FCB to DOS disk number mov bx, NewBootBuffer ; Near jump and nop ; ofs xor dx, dx mov cx, dx inc cx int 26h ; DOS - ABSOLUTE DISK WRITE (except DOS 4.0/COMPAQ DOS 3.31 >32M partn) ; AL = drive number (0=A, 1=B, etc), DS:BX = Disk Transfer Address (buffer) ; CX = number of sectors to write, DX = first relative sector to write ; Return: CF set on error pop ax skip_writeBoot: ; retn ; WriteBoot endp ; === SUBROUTINE ==================================================== ; Clears buffer before and after. WaitForAnyKey: ; proc near ; mov ax, 0C08h int 21h ; DOS - CLEAR KEYBOARD BUFFER ; AL must be 01h, 06h, 07h, 08h, or 0Ah. mov ax, 0C00h int 21h ; DOS - CLEAR KEYBOARD BUFFER ; AL must be 01h, 06h, 07h, 08h, or 0Ah. ; ; (AL=00h, or any ; other not from the list above ; -- "the buffer is flushed but no input is attempted") mov dx, CR_LF_Adr ; ofs push dx call PrintfSeg:Printf_sub retn ; WaitForAnyKey endp ; =================================================================================== CantSysToNetDrv_Str db 'Cannot SYS to a Network drive',0 CantSysToNetDrv_Adr dw CantSysToNetDrv_Str ; aIncorrectDosVe db 'Incorrect DOS version',0Dh,0Ah,'$' ; InvalidDriveSpec_Str db 'Invalid drive specification',0 InvalidDriveSpec_Adr dw InvalidDriveSpec_Str InvalidParameter_Str db 'Invalid parameter',0 InvalidParameter_Adr dw InvalidParameter_Str NoRoomForSystem_Str db 'No room for system on destination disk',0 NoRoomForSystem_Adr dw NoRoomForSystem_Str IncompatibleSysSize_Str db 'Incompatible system size',0 IncompatibleSysSize_Adr dw IncompatibleSysSize_Str SystemTransferred_Str db 'System transferred',0Dh,0Ah,0 SystemTransferred_Adr dw SystemTransferred_Str NoSystemOnDefDrv_Str db 'No system on default drive',0Dh,0Ah,0 NoSystemOnDefDrv_Adr dw NoSystemOnDefDrv_Str InsertSystemDisk_Str db 'Insert system disk in drive %c',0Dh,0Ah db 'and strike any key when ready',0 InsertSystemDisk_Adr dw InsertSystemDisk_Str DriverLetter1_Adr dw DriverLetter1_Str DriverLetter1_Str db 'A',0 ; CR_LF_Str db 0Dh,0Ah,0 CR_LF_Adr dw CR_LF_Str db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 0 ; CodeSeg ends ; Displ: 7Bh, StackSeg = PSP seg + 10 + 7Bh, size = 80h ; =================================================================================== segment StackSeg StackSegBegin: db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 0 db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 16 db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 32 db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 48 db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 64 db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 80 db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 96 db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 112 StackSegEnd: ; StackSeg ends ; =================================================================================== currentDrive db 0 ; ; aAIbmbio_com db 'A:\IBMBIO.COM',0 ; ; aAIbmdos_com db 'A:\IBMDOS.COM',0 ; ; IBMDOS_fount_flag db 0 ; ; IBMBIO_fount_flag db 0 ; IBMDOS_position_not_correct db 1 ; ; IBMBIO_position_not_correct db 1 ; ; ; IBMBIO.COM data IBMBIO_hndlr dw 0 ; ; IBMBIO_size_lo_1 dw 0 ; ; IBMBIO_size_lo_2 dw 0 IBMBIO_size_hi_1 dw 0 ; ; IBMBIO_size_hi_2 dw 0 IBMBIO_time dw 0 ; IBMBIO_date dw 0 ; TargetIBMBIO_hndlr dw 0 ; ; ; ; IBMDOS.COM data IBMDOS_hndlr dw 0 ; ; IBMDOS_size_lo_1 dw 0 ; ; IBMDOS_size_lo_2 dw 0 IBMDOS_size_hi_1 dw 0 ; ; IBMDOS_size_hi_2 dw 0 IBMDOS_time dw 0 ; IBMDOS_date dw 0 ; TargetIBMDOS_hndlr dw 0 ; ; anyFileMask db 'A:\*.*',0 ; FCBs_Copy db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ; aIbmdosCom db 'IBMDOS COM' ; aIbmbioCom db 'IBMBIO COM' ; NewDefDTA db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 0 ; db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 16 db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 32 db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 48 db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 64 db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 80 db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 96 db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 112 PutativeMaxFreeMem dw 0 ; IBMDOS_buffer_addr dw 0 ; After_IBMDOS_buffer dw 0 ; lds_offset dw 0 ; lds_segment dw 70h ;================================================== ;=New boot========================================= ;================================================== NewBootBuffer db 0EBh, 29h, 90h ; 0 ; ; Near jump and nop aIbm3_0 db 'IBM 3.0' ; BPB starts at 0Bh offset (in boot sector) ; In DOS2+ it size 0x0D = 13bytes, DOS 3.0 -- 19 bytes (3.2+ --- 25, 6 bytes more) ; Details: http://en.wikipedia.org/wiki/File_Allocation_Table#BPB BPB_Start dw 200h ; ; Bytes per logical sector in powers of two, 512 SectorsPerCluster db 8 ; ; Logical sectors per cluster. dw 1 ; Count of reserved logical sectors --- ; The number of logical sectors before the first FAT in ; the file system image. At least 1. db 2 ; Number of File Allocation Tables dw 200h ; Maximum number of FAT12 or FAT16 root directory entries. dw 5103h ; Total logical sectors?!! MediaIDInBPB db 0F8h ; ш ; ; Media ID dw 8 ; Logical sectors per FAT ; Part of DOS 3.0+ BPB dw 11h ; Physical sectors per track for disks with INT 13h CHS geometry, ; e.g., 15 for a "1.20 MB" (1200 KiB) floppy. dw 4 ; Number of heads for disks with INT 13h CHS ; geometry, e.g., 2 for a double sided floppy. dw 1 ; Count of hidden sectors preceding the partition that contains this FAT volume. ; This field should always be zero on media that are not partitioned. ; This DOS 3.0 entry is incompatible with a similar entry at offset 0x01C in BPBs since DOS 3.31. ; End of DOS 3.0 BPB ; ; Are there one more DOS 3.0-specific BPB byte? ; At least, all BPB's images below have one zero byte at the end... ; ; Value 80h for HDD (which can be increased in code) hints ; that this byte is smth. similar to 0x19 byte in DOS 3.31: ; "Physical drive number (0x00 for (first) removable media, 0x80 for (first) fixed disk as per INT 13h)." PossiblyPhysicalDiskNumber db 80h ; Ђ ; ; ; Rest of the boot sector db 0, 0, 0, 0, 0, 0Fh, 0, 0, 0, 0, 1, 0, 0FAh, 33h, 0C0h, 8Eh; 0 db 0D0h, 0BCh, 0, 7Ch, 16h, 7, 0BBh, 78h, 0, 36h, 0C5h, 37h, 1Eh, 56h, 16h, 53h; 16 db 0BFh, 20h, 7Ch, 0B9h, 0Bh, 0, 0FCh, 0ACh, 26h, 80h, 3Dh, 0, 74h, 3, 26h, 8Ah; 32 db 5, 0AAh, 8Ah, 0C4h, 0E2h, 0F1h, 6, 1Fh, 89h, 47h, 2, 0C7h, 7, 20h, 7Ch, 0FBh; 48 db 0CDh,13h , 72h, 67h, 0A0h, 10h, 7Ch, 98h, 0F7h, 26h, 16h, 7Ch, 3, 6, 1Ch, 7Ch; 64 db 3, 6, 0Eh, 7Ch, 0A3h, 34h, 7Ch, 0A3h, 2Ch, 7Ch, 0B8h, 20h, 0, 0F7h, 26h, 11h; 80 db 7Ch, 8Bh, 1Eh, 0Bh, 7Ch, 3, 0C3h, 48h, 0F7h, 0F3h, 1, 6, 2Ch, 7Ch, 0BBh, 0; 96 db 5, 0A1h, 34h, 7Ch, 0E8h, 96h, 0, 0B8h, 1, 2, 0E8h, 0AAh, 0, 72h, 19h, 8Bh; 112 db 0FBh, 0B9h, 0Bh, 0, 0BEh, 0BEh, 7Dh, 0F3h, 0A6h, 75h, 0Dh, 8Dh, 7Fh, 20h, 0BEh, 0C9h; 128 db 7Dh, 0B9h, 0Bh, 0, 0F3h, 0A6h, 74h, 18h, 0BEh, 5Fh, 7Dh, 0E8h, 61h, 0, 32h, 0E4h; 144 db 0CDh, 16h, 5Eh, 1Fh, 8Fh, 4, 8Fh, 44h, 2, 0CDh, 19h, 0BEh, 0A8h, 7Dh, 0EBh, 0EBh; 160 db 0A1h, 1Ch, 5, 33h, 0D2h, 0F7h, 36h, 0Bh, 7Ch, 0FEh, 0C0h, 0A2h, 31h, 7Ch, 0A1h, 2Ch; 176 db 7Ch, 0A3h, 32h, 7Ch, 0BBh, 0, 7, 0A1h, 2Ch, 7Ch, 0E8h, 40h, 0, 0A1h, 18h, 7Ch; 192 db 2Ah, 6, 30h, 7Ch, 40h, 50h, 0E8h, 4Eh, 0, 58h, 72h, 0CFh, 28h, 6, 31h, 7Ch; 208 db 76h, 0Ch, 1, 6, 2Ch, 7Ch, 0F7h, 26h, 0Bh, 7Ch, 3, 0D8h, 0EBh, 0D9h, 8Ah, 2Eh; 224 db 15h, 7Ch, 8Ah, 16h, 1Eh, 7Ch, 8Bh, 1Eh, 32h, 7Ch, 0EAh, 0, 0, 70h, 0, 0ACh; 240 db 0Ah, 0C0h, 74h, 22h, 0B4h, 0Eh, 0BBh, 7, 0, 0CDh, 10h, 0EBh, 0F2h, 33h, 0D2h, 0F7h; 256 db 36h, 18h, 7Ch, 0FEh, 0C2h, 88h, 16h, 30h, 7Ch, 33h, 0D2h, 0F7h, 36h, 1Ah, 7Ch, 88h; 272 db 16h, 1Fh, 7Ch, 0A3h, 2Eh, 7Ch, 0C3h, 0B4h, 2, 8Bh, 16h, 2Eh, 7Ch, 0B1h, 6, 0D2h; 288 db 0E6h, 0Ah, 36h, 30h, 7Ch, 8Bh, 0CAh, 86h, 0E9h, 8Bh, 16h, 1Eh, 7Ch, 0CDh, 13h, 0C3h; 304 db 0Dh, 0Ah, 4Eh, 6Fh, 6Eh, 2Dh, 53h, 79h, 73h, 74h, 65h, 6Dh, 20h, 64h, 69h, 73h; 320 db 6Bh, 20h, 6Fh, 72h, 20h, 64h, 69h, 73h, 6Bh, 20h, 65h, 72h, 72h, 6Fh, 72h, 0Dh; 336 db 0Ah, 52h, 65h, 70h, 6Ch, 61h, 63h, 65h, 20h, 61h, 6Eh, 64h, 20h, 73h, 74h, 72h; 352 db 69h, 6Bh, 65h, 20h, 61h, 6Eh, 79h, 20h, 6Bh, 65h, 79h, 20h, 77h, 68h, 65h, 6Eh; 368 db 20h, 72h, 65h, 61h, 64h, 79h, 0Dh, 0Ah, 0, 0Dh, 0Ah, 44h, 69h, 73h, 6Bh, 20h; 384 db 42h, 6Fh, 6Fh, 74h, 20h, 66h, 61h, 69h, 6Ch, 75h, 72h, 65h, 0Dh, 0Ah, 0, 49h; 400 db 42h, 4Dh, 42h, 49h, 4Fh, 20h, 20h, 43h, 4Fh, 4Dh, 49h, 42h, 4Dh, 44h, 4Fh, 53h; 416 db 20h, 20h, 43h, 4Fh, 4Dh, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 432 db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 448 db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 464 db 55h, 0AAh ; 0 ; End of boot image ; ============================================================================================= DiskBPBsPtrsTable dw 0 ; ; Corresponds to 0F8h media ID --- Fixed DISK ; (Designated to be used for any partitioned fixed or removable media, where the geometry is defined in the BPB.) dw ID_F9_BPB_bytes2_13 ; 0F9h ; ofs dw 0FFFFh ; 0FAh - Skip write boot dw 0FFFFh ; 0FBh - Skip write boot dw ID_FC_BPB_bytes2_13 ; 0FCh ; ofs dw ID_FD_BPB_bytes2_13 ; 0FDh ; ofs dw ID_FE_BPB_bytes2_13 ; 0FEh ; ofs dw ID_FF_BPB_bytes2_13 ; 0FFh ; ofs ID_FE_BPB_bytes2_13 partialBPB_record 1, 1, 2, 40h, 140h, 0FEh, 1, 8, 1, 0 ID_FF_BPB_bytes2_13 partialBPB_record 2, 1, 2, 70h, 280h, 0FFh, 1, 8, 2, 0 ID_FC_BPB_bytes2_13 partialBPB_record 1, 1, 2, 40h, 168h, 0FCh, 2, 9, 1, 0 ID_FD_BPB_bytes2_13 partialBPB_record 2, 1, 2, 70h, 2D0h, 0FDh, 2, 9, 2, 0 ID_F9_BPB_bytes2_13 partialBPB_record 1, 1, 2, 0E0h, 960h, 0F9h, 7, 0Fh, 2, 0 ; Format: ; 00 partialBPB_record struc ; (sizeof=0x12) ; 00 sectors_per_claster db ? ; Logical sectors per cluster. ; 01 reserved_sectors dw ? ; Count of reserved logical sectors. ; 03 FATs db ? ; Number of File Allocation Tables. ; 04 root_entries dw ? ; Maximum number of FAT12 or FAT16 root directory entries ; 06 logical_sectors dw ? ; Total logical sectors ; 08 MediaID db ? ; 09 sectors_per_FAT dw ? ; Logical sectors per FAT ; 0B sectors_per_track dw ? ; DOS 3+ Physical sectors per track for disks with INT 13h CHS geometry ; 0D heads dw ? ; Number of heads for disks with INT 13h CHS geometry ; 0F hidden_sectors dw ? ; Count of hidden sectors preceding the partition that contains this FAT volume. ; 0F ; 0 for floppies --- without MBR ; 11 unknown_fld db ? ; 12 partialBPB_record ends ; Looks like all below in buffer is just a garbage. buffer1: db 8 dup(0)
Скачати код, разом із лістингом, згенерованим IDA, скомпільованим файлом та, для порівняння, оригінальним SYS.COM і "SYS.EXE", можна тут.
Алгоритм роботи
Під "кришечкою" EXE --- все та ж стара знайома, SYS.COM. В основному зосереджуся на змінах, тому, для повноти вражень, див. також: "Аналіз SYS.COM з PC-DOS 2.10". В точці входу EXE-файла, яка раныше була початком COM-файлу --- перехід до реальної точки входу, після нього --- адреса бут-сектора в коді і стрічка "SYS 1.86" --- внутрішня версія.
Спочатку код, як завжди, перевіряє версію DOS. Якщо менша за 3.00 --- виходить із повідомленням "Incorrect DOS version". Далі, на відміну від 2.10, копіює FCB із PSP у виділену область пам'яті і переставляє DTA у свій сегмент даних. Нова DTA має розмір 128 байт.
Перевірки переданого диску здійснюються як і раніше, єдине, якщо після диску є якісь літери -- умовно, ім'я файлу, повідомляє "Invalid parameter" та виходить.
Зауважте (так було і раніше, але явно я цього, здається, не писав), виклики такого типу:
Зауважте (так було і раніше, але явно я цього, здається, не писав), виклики такого типу:
sys b:\a123.bcd
sys b:/cd.efg
sys b:/cd.efg
перетравляться нормально --- все, що після '\' в FCB просто не потрапить. Повідомлення про інвалідність параметру дасть хіба щось таке:
sys b:cd.efg
Якщо диск некоректний, або цільовим використано поточний диск --- скаже "Invalid drive specification" і вийде, як і раніше.
Для друку повідомлень, крім отого, про погану версію DOS, використовується printf. Коди помилки тепер 0FFh, а не 1, як було в 2.10.
Далі використовується виклик AX=4409h/INT 21h, "IOCTL - CHECK IF BLOCK DEVICE REMOTE", якого в DOS 3.00 ще не мало б існувати. Однак, як і пишуть на сайті музею OS/2 ("DOS 3.0, 3.1, and 3.2"), мережевий редиректор вже присутній в 3.00, хоча ще й не використовувався. Код із SYS, що його використовує -- хороше підтвердження цього. Якщо виклик успішний, результат виклику тестується на біт 12, "Drive is remote". Якщо так --- повідомляє "Cannot SYS to a Network drive". Якщо не віддалений, або виклик IOCTL повернув помилку --- продовжуємо.
Наступний крок --- той же, що і в 2.10, перевірка Media ID у FAT. Якщо він не нижчий за 0F8h, на відміну від 2.10, переходить до додаткової перевірки формату диску. Інакше друкує (як на мене --- не зовсім релевантне) "No room for system on destination disk" і виходить. Що цікаво, якщо читання Media ID повернуло помилку --- переходимо зразу ж до тих перевірок, що робив і 2.10. Якщо диск невідомий, просто прямолінійно копіюватимемо, не колупаючись у внутрішній структурі?
Літеру цільового диску встановлюємо у стрічки "A:\\IBMBIO.COM" і "A:\\IBMDOS.COM" замість 'A'. Якщо існує IBMBIO.COM, встановлюємо прапорець (умовно --- IBMBIO_fount_flag), якщо існує IBMDOS.COM --- встановлюємо IBMDOS_fount_flag.
Читаємо "Drive Parameter Block", з його допомогою знаходимо перший сектор каталогу диску, читаємо його у свій буфер. Якщо читання провалилося --- продовжуємо, вважаючи, що диск невідомого формату (і керується якимось драйвером? чи це просто помилка?), інакше, продовжуємо перевірки.
Якщо перший запис порожній, перевіряємо, чи файл IBMBIO.COM було знайдено, (використовується хитрий трюк, з логічним and: IBMDOS_fount_flag and IBMDOS_position_not_correct, але, здається, про аналогічні прапорці для IBMBIO забули, або я не всю логіку зрозумів), і, якщо так --- повідомляємо помилку "No room for system on destination disk" та виходимо. Природно --- він, у цій версії DOS, має бути першим у каталозі. Однак, наступна перевірка, чи не є перший файл стертим (ознака --- перший байт = 0E5h), дещо дивна: якщо так --- переходимо до перевірки наступного запису на відповідність IBMDOS.COM. А якщо IBMBIO.COM є, а перший запис -- стертий? Чи воно тоді автоматично стертий запис використає? Якщо перший запис каталогу не є незайманим і не є стертим, перевіряється, чи відповідає він IBMBIO.COM. Якщо ні -- "No room for system on destination disk". Інакше --- аналогічна перевірка робиться і для IBMDOS.COM.
Після тих перевірок повертаємося на дорогу, вже проторену 2.10. Звертаємося до системних файлів на вихідному диску. Викликаємо GetFileSizeDateTime, яка не змінилася з 2.10 --- зберігає дати, часи та розміри, для IBMBIO.COM. Якщо помилка --- на відміну від 2.10, що зразу просить вставити системний диск, перевіряє, чи диск змінний (AX=4408h/INT 21h --- "IOCTL - CHECK IF BLOCK DEVICE REMOVABLE"), і якщо так, або виклик завершився не успішно --- просить вставити системний диск та провалюється до перевірки диску із FCB (описаної вище), інакше --- цільвоий диск фіксований, замінити неможливо, тому "No system on default drive" і виходить з кодом помилки 1.
Далі, аналогічну перевірку та читання часу-дати-розміру робить і для IBMDOS.COM. Якщо проблеми, аналогічно, просимо замінити диск і починаємо все з початку.
Якщо все в порядку --- оцінюємо, скільки у нас вільної пам'яті, та, шматками, читаємо файли у пам'ять, за допомогою функції ReadFilesToMem, яка не змінилася з часів 2.10. Якщо проблеми --- просимо вставити системний диск.
Далі, на відміну від 2.10, не чіпаємо поточну директорію цільового диску. Просто, за маскою із абсолютним шляхом, шукаємо всі файли, зі всіма атрибутами в корені цільового диску ("<target-letter>:\*.*"). Якщо знайдено файли --- йдемо до їх перевірки. Інакше, перевіряємо, чи є мітка тому. Якщо є (і тоді вона --- єдиний файл), кажемо, що "No room for system on destination disk" і виходимо. Якщо ж ні --- переходимо до запису.
Якщо якісь файли було знайдено, перевіряємо, чи є IBMBIO.COM і IBMDOS.COM. Якщо немає --- теж кажемо, що немає місця. (Дивно --- перевірки раніше мали б виявити, що їх немає. Правда, сюди ми провалюємося, також, якщо відмовилися від частини перевірок --- наприклад, MediaID невідомий, або каталог не вдалося прочитати).
Все, якщо ми продовжуємо працювати --- всі перевірки завершено. Можна записувати. Очищаємо атрибути IBMBIO.COM і IBMDOS.COM на цільовому диску. Ігноруємо помилки, (зокрема, файлів може і не бути взагалі), щоб їх можна було модифікувати. Створюємо ці файли, (або "обрізаємо", якщо вже були) із правильними атрибутами --- system+hidden+readonly. Якщо помилка під час створення --- все те ж "No room....".
Потім йде якась дивна маніпуляція з внутрішніми даними. Не зміг розшифрувати... Отримуємо адресу DPB, і в поле, що містить початок поточної директорії (0h --- коренева), записує нуль. То такий спосіб перейти в кореневу директорію?..
Після цього викликаємо WriteSysFilesFromMem, яка теж не змінилася з часв 2.10. Вона записує те, що прочитала раніше ReadFilesToMem. Якщо файли прочитані-записані ще не всі (пам'яті вмістити їх могло і не вистачити), читає та записує наступну порцію, поки файли не закінчаться. (Детальніше див. опис 2.10).
Коли запис завершено, відновлюємо дату та час файлів, як і в 2.10. Потім записуємо бут-сектор, і завершуємо, вивівши "System transferred", з кодом завершення 0.
Залишилося описати процедуру запису бут-сектора, WriteBoot. Вона радикально змінилася в 3.00. Хоча, починає із того ж -- добуває Media ID цільового диску. Відмінності починаються потім. Замість вгадувати деталі BPB, вони беруться із таблиці в кінці програми. MediaID слжуить індексом у "диспетчері", табличці DiskBPBsPtrsTable. Якщо в ній 0 --- опрацьовуємо як HDD (детальніше трішки пізніше). Якщо 0xFFFF --- пропускаємо. Інакше копіюємо 12h байт "заготивки" BPB із адреси, вказаної у відповідному елементі DiskBPBsPtrsTable у образ boot-сектора і він записується на дискету. (Образ бута за міткою NewBootBuffer, початок частини BPB, яка копіюється, починається з мітки SectorsPerCluster).
Таблиця містить опис для наступних MediaID: 0F9h (варіант 5.25", 1200Кб), 0FCh (180Кб), 0FDh (360Кб), 0FEh (160Кб), 0FFh (320Кб). Пропускатимуться Media ID, рівні 0FAh i 0FBh, які відповідають доволі загадковим форматам (див. вікі).
Якщо ж працюємо з жорстким диском, то, в образі бут-сектора, записуємо 0F8h як MediaID. Потім для чогось ліземо в системні дані. Ймовірно (судячи по коду далі), системні дані... Перевіряємо, скільки є дисководів. Далі певна, не до кінця зрозуміл мені, магія. Ймовірно, визначається номер жорсткого диску по порядку... Ймовірно, знову ж таки, так визначаємо відносне зміщення поля у внутрішніх структурах даних. Тоді, з цього гіпотетичного внутрішнього поля, копіюється той же фрагмент BPB, про який говорилося вище. Можна припустити, що поточний BPB для жорсткого диска десь там лежить... Після цього теж записуємо boot-сектор.
Таблиця містить опис для наступних MediaID: 0F9h (варіант 5.25", 1200Кб), 0FCh (180Кб), 0FDh (360Кб), 0FEh (160Кб), 0FFh (320Кб). Пропускатимуться Media ID, рівні 0FAh i 0FBh, які відповідають доволі загадковим форматам (див. вікі).
Якщо ж працюємо з жорстким диском, то, в образі бут-сектора, записуємо 0F8h як MediaID. Потім для чогось ліземо в системні дані. Ймовірно (судячи по коду далі), системні дані... Перевіряємо, скільки є дисководів. Далі певна, не до кінця зрозуміл мені, магія. Ймовірно, визначається номер жорсткого диску по порядку... Ймовірно, знову ж таки, так визначаємо відносне зміщення поля у внутрішніх структурах даних. Тоді, з цього гіпотетичного внутрішнього поля, копіюється той же фрагмент BPB, про який говорилося вище. Можна припустити, що поточний BPB для жорсткого диска десь там лежить... Після цього теж записуємо boot-сектор.
Все.
Хоча офіційно DOS 3.00 ще не підтримує мережі, але утиліта вже перевіряє, чи диск не є віддаленим, і відмовляється писати систему на такі диски.
Нарешті (!!!) справді перевіряє, чи цільовий диск підходить --- чи вільні перші два записи каталогу, чи, якщо вони зайняті, знаходиться в них те, що потрібно --- записи для IBMBIO.COM та IBMDOS.COM. Правда, додатково перевіряє, чи не відповідають перші записи стертим файлам, і якщо так -- вважає, що диск підходить. Дивує також , що якщо читання першого сектору корневого каталогу диску (для цієї перевірки), провалилося, повернуло помилку, переходить до перевіри наявності файлів звичайним FindFirst... Не бачу поки і перевірки, чи справді системні файли попадуть на початок області даних. (Чи вона є ія її пропустив?). Ну, але, все рівно --- прогрес.
Ще одна важлива відмінність --- BPB для boot-сектора цільового диску не придумується, як, фактично, робили попередні версії, а генерується на базі Media ID та спеціальної таблички в кінці програми. Деталі див. вище, в описі алгоритму. Зокрема, знає вона пару дивних форматів, для яких бут-сектор просто не зачіпає. Бут-сектор для жорстких дисків будується, здається, на базі образу BPB десь в системних даних DOS --- не береться вгадувати самостійно, умнічка.
COMMAND.COM все ще не копіюється.
На жаль, сказати більше чогось по суті мені складно --- минуло більше кварталу з того часу, як я писав дивився до коду попередніх версій, тому безпосереднього відчуття, що змінилося, що ні --- немає. А ще раз колупатися аж в таких деталях неохота.
На разі:
"Документація"
Трішки змінилися повідомлення про помилку. Зокрема, якщо після імені диску будуть якісь літери, скаже "Invalid parameter", якщо ж вказано некоректний диск, не вказано взагалі, або цільвоим використано поточний диск, скаже "Invalid drive specification" -- як і раніше.Хоча офіційно DOS 3.00 ще не підтримує мережі, але утиліта вже перевіряє, чи диск не є віддаленим, і відмовляється писати систему на такі диски.
Нарешті (!!!) справді перевіряє, чи цільовий диск підходить --- чи вільні перші два записи каталогу, чи, якщо вони зайняті, знаходиться в них те, що потрібно --- записи для IBMBIO.COM та IBMDOS.COM. Правда, додатково перевіряє, чи не відповідають перші записи стертим файлам, і якщо так -- вважає, що диск підходить. Дивує також , що якщо читання першого сектору корневого каталогу диску (для цієї перевірки), провалилося, повернуло помилку, переходить до перевіри наявності файлів звичайним FindFirst... Не бачу поки і перевірки, чи справді системні файли попадуть на початок області даних. (Чи вона є ія її пропустив?). Ну, але, все рівно --- прогрес.
Ще одна важлива відмінність --- BPB для boot-сектора цільового диску не придумується, як, фактично, робили попередні версії, а генерується на базі Media ID та спеціальної таблички в кінці програми. Деталі див. вище, в описі алгоритму. Зокрема, знає вона пару дивних форматів, для яких бут-сектор просто не зачіпає. Бут-сектор для жорстких дисків будується, здається, на базі образу BPB десь в системних даних DOS --- не береться вгадувати самостійно, умнічка.
COMMAND.COM все ще не копіюється.
Про код
Ну, про псевдо-printf --- мовчу. Може були у них якісь мотиви. Так виглядає, що вона й в інших утилітах використовується, і в тому ж CHKDSK цілком може бути справді зручною. Як би там не було, код став "дорослішим" -- виглядає солідніше, хоч і громіздкіший. Правда, почав лазити в загадкові системні дані, щоправда... --- нецікаво. :-) З іншого боку, зовсім не використовуєтьс FCB, код дещо стабілізувався --- великі шматки не змінилися з попередньої версії.На жаль, сказати більше чогось по суті мені складно --- минуло більше кварталу з того часу, як я писав дивився до коду попередніх версій, тому безпосереднього відчуття, що змінилося, що ні --- немає. А ще раз колупатися аж в таких деталях неохота.
На разі:
Дякую за увагу!
Немає коментарів:
Дописати коментар