В листопаді того ж, 1983 року, IBM випустила DOS 2.10. Нічого особливо нового в ній не було --- поточний апдейт, але подивимося детальніше до нутрощів (хоч і на тривіальному прикладі).
Для кращого розуміння --- див. попередні статті серії. Код -- сумісний з Flat Assembler.
; This file is generated by The Interactive Disassembler (IDA) ; 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 fasm and commented by Indrekis, indrekis2.blogspot.com ; ; Input MD5 : 60077C698CB4907A2B1C8022BA16F98D ; ; File Name : PC-DOS_2.10\SYS.COM ; Format : MS-DOS COM-file ; Base Address: 0h Range: 100h-790h Loaded length: 690h ;.8086 use16 org 100h include "my_fcb_2b.inc" start: jmp short EntryPoint1 ; Save AX ; =========================================================================== db 8Ah db 5 aVers1_82 db 'Vers 1.82' ; =========================================================================== ; 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 ) EntryPoint1: push ax ; Save AX ; Then check for DOS version. Exit if older than 1.54 ; or newer than 2.10 mov ah, 30h int 21h ; DOS - GET DOS VERSION ; Return: AL = major version number (00h for DOS 1.x) xchg ah, al cmp ax, 136h jb short WrongDOSVersion cmp ax, 20Ah jbe short DosVersionOK WrongDOSVersion: mov dx, aIncorrectDosVe ; "Incorrect DOS version\r\n$" 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 ; =========================================================================== DosVersionOK: pop ax ; Restore initial AX, with default FCBs disk states. jmp short EntryPoint2CorrectVer ; PSP: 5Ch-6Bh (16 bytes) -- Unopened Standard FCB 1 ; So here will be any symbol, entered after [a-z][:][\] ; =========================================================================== ArgIsInvalid: mov dx, aInvalidParamet ; "Invalid parameter" mov cx, [InvalidParametSize] jmp ExitWithCode1 ; =========================================================================== BadDriveLetter: mov dx, aInvalidDriveSp ; "Invalid drive specification" mov cx, [InvalidDriveSpSize] jmp ExitWithCode1 ; =========================================================================== AskToInsertSysDisk: mov al, [currentDrive] add al, 40h mov [aDriverLetter1], al mov dx, aInsertSystemDi ; "Insert system disk in drive " mov cx, [InsertSystemDiskFullSize] mov bx, 2 ; Standard Error Device handler (STDERR) ; http://stanislavs.org/helppc/file_handles.html mov ah, 40h int 21h ; DOS - 2+ - WRITE TO FILE WITH HANDLE ; BX = file handle, CX = number of bytes to write, DS:DX -> buffer call WaitForAnyKey ; Clears buffer before and after. xor al, al ; Restore AL -- 00 means correct first FCB (we have ; already checked). EntryPoint2CorrectVer: 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 ArgIsInvalid 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 ; ; 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 jz short BadDriveLetter ; Check FAT on destination disk push ax mov al, [ds:5Ch] ; ; Target driver number dec al ; Adjust from FCB numbering to DOS mov bx, ReadedIBMDOS mov dx, 1 mov cx, dx ; Read one sector, starting from first 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 25h pop ax ; Restore AX jb short CheckSysFiles cmp [ReadedIBMDOS], 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 jnb short CheckSysFiles jmp noRoomForSys ; =========================================================================== CheckSysFiles: add al, 40h ; Disk number to letter mov byte [aAIbmbio_com], al ; "A:\\IBMBIO.COM" mov byte [aAIbmdos_com], al ; "A:\\IBMDOS.COM" cld mov dx, aAIbmbio_com ; "A:\\IBMBIO.COM" mov di, IBMBIO_hndlr ; Try to open both system files. ; Determine their size, time and date. Save them ; both with file handlers. In case of error -- ; ask to insert system disk 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. ; locContinuerOrError: jb short AskToInsertSysDisk mov dx, aAIbmdos_com ; "A:\\IBMDOS.COM" mov di, IBMDOS_hndlr 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. ; jb short AskToInsertSysDisk ; Both files exist and successfully opened mov cx, sp sub cx, 98Ah ; Approximation of code+stack size? mov [PutativeMaxFreeMem], cx ; Possibly contains approximation of free mem, ; above code, below stack call ReadFilesToMem jb short locContinuerOrError ; IS too far for direct jump? mov al, [ds:5Ch] ; Get target drive mov byte [FCBext_forFind.Driver], al push ax ; Current dir of target disk is saved. ; No cd of source disk, as version from 2.00, ; just use X:\<filename> with correct disk letter. ; (2.00 were using FCB's, so had no choice.) mov dl, al mov si, CurDirBuffer ; "" mov ah, 47h int 21h ; DOS - 2+ - GET CURRENT DIRECTORY ; DL = drive (0=default, 1=A, etc.) ; DS:SI points to 64-byte buffer area ; ; http://www.ctyme.com/intr/rb-2933.htm ; returned path does not include a drive ; or the initial backslash pop ax ; Set target disk current dir to root add al, 40h mov byte [DiskRootDir], al ; "X:\\" mov [CurDirDisk], al ; "X:\" -- beginning for next Dir Buffer push ax mov dx, DiskRootDir ; "X:\\" mov ah, 3Bh int 21h ; DOS - 2+ - CHANGE THE CURRENT DIRECTORY (CHDIR) ; DS:DX -> ASCIZ directory name (may include drive) pop ax mov byte [aAIbmbio_com], al ; "A:\\IBMBIO.COM" mov byte [aAIbmdos_com], al ; "A:\\IBMDOS.COM" mov byte [anyFileMask], al ; "A:\\*.*" ; Search for any files on target disk ; Attributes dir, sys, hid are inclusive --- search ; for ordinary files + for files with any of that attributes. mov ah, 11h mov dx, FCBext_forFind int 21h ; DOS - SEARCH FIRST USING FCB ; DS:DX -> FCB push ax ; Restore current disk current dir mov dx, CurDirDisk ; "X:\" -- beginning for next Dir Buffer mov ah, 3Bh int 21h ; DOS - 2+ - CHANGE THE CURRENT DIRECTORY (CHDIR) ; DS:DX -> ASCIZ directory name (may include drive) ; Restore search files results and continue pop ax or al, al jz short SomeFilesFoundOnTrg mov ah, 11h mov [FCBext_forFind.Attribute], 8 ; ; So we searching for volume lable. ; ; (AFAIK, vol. cannot be accessed without ; ; FCB in DOS 2) mov dx, FCBext_forFind int 21h ; DOS - SEARCH FIRST USING FCB ; DS:DX -> FCB ; ; For extended FCBs with search attribute 08h, ; the volume label (if any) will be returned ; even if the current directory is not the root dir. or al, al jnz short no_any_files ; Check for other file types? jmp noRoomForSys ; =========================================================================== SomeFilesFoundOnTrg: mov dx, aAIbmbio_com ; "A:\\IBMBIO.COM" mov cx, 7 mov ah, 4Eh int 21h ; DOS - 2+ - FIND FIRST ASCIZ (FINDFIRST) ; CX = search attributes ; DS:DX -> ASCIZ filespec ; (drive, path, and wildcards allowed) ; http://www.ctyme.com/intr/rb-2977.htm ; ; Under DOS 2.x, searching for attribute 08h ; (volume label) will also return normal files jnb short CheckForIBMDOS loc224_noRoomForSys: jmp noRoomForSys ; =========================================================================== CheckForIBMDOS: mov dx, aAIbmdos_com ; "A:\\IBMDOS.COM" 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 loc224_noRoomForSys no_any_files: mov dx, aAIbmbio_com ; "A:\\IBMBIO.COM" mov cx, 0 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" mov cx, 0 mov ax, 4301h int 21h ; DOS - 2+ - SET FILE ATTRIBUTES ; DS:DX -> ASCIZ file name ; CX = file attribute bits mov dx, aAIbmbio_com ; "A:\\IBMBIO.COM" mov cx, 7 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) mov [TargetIBMBIO_hndlr], ax mov dx, aAIbmdos_com ; "A:\\IBMDOS.COM" 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) mov [TargetIBMDOS_hndlr], ax ReadNWriteNextPart: call WriteSysFilesFromMem jb short loc27b_noRoomForSys mov ax, [IBMDOS_size_hi_1] or ax, [IBMDOS_size_lo_1] or ax, [IBMBIO_size_hi_1] or ax, [IBMBIO_size_lo_1] jz short RestoreFilesDateNTime call ReadFilesToMem jnb short ReadNWriteNextPart jmp AskToInsertSysDisk ; =========================================================================== loc27b_noRoomForSys: jmp noRoomForSys ; =========================================================================== ; Unused code! mov dx, aIncompatibleSy ; "Incompatible system size" mov cx, [IncompatibleSySize] jmp short ExitWithCode1 ; =========================================================================== nop RestoreFilesDateNTime: mov cx, [IBMBIO_time] mov dx, [IBMBIO_date] mov bx, [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, [IBMDOS_time] mov dx, [IBMDOS_date] mov bx, [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 8-track FDD's ; Possibly attempt to automatically fix boot for old, (1.XX), ; incompatible floppies. mov dx, aSystemTransfer ; "System transferred" mov cx, [SystemTransferSize] xor al, al PrnMsgNExitWithCode: push ax mov bx, 2 ; Standard Error Device handler - can be redirected (STDERR) ; http://stanislavs.org/helppc/file_handles.html 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 ax mov ah, 4Ch int 21h ; DOS - 2+ - QUIT WITH EXIT CODE (EXIT) ; AL = exit code ; =========================================================================== ExitWithCode1: mov al, 1 ; Exit code -- 1 (error) jmp short PrnMsgNExitWithCode ;start endp ; =========================================================================== RestoreStackNExit: pop cx Set_CF_NExit: stc Retn_2D1: retn ; ============== subroutine ================================================= ; Tries to read IBMBIO and IBMDOS to memory. ; Read as much as fits. Sets CF in case of errors, ; clears otherwise. ReadFilesToMem: mov cx, [PutativeMaxFreeMem] ; Possibly contains approximation of free mem, ; above code, below stack mov bx, [IBMBIO_hndlr] mov dx, ReadedIBMDOS push cx cmp [IBMBIO_size_hi_1], 0 ; If size above 64k -- read max size, which can be ; disposed in free mem. ja short PRoceedToRead cmp [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, [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 ; ; http://www.ctyme.com/intr/rb-2783.htm ; Returns AX = number of bytes actually read jb short RestoreStackNExit ; Error -- exit cmp ax, cx ; Readed less than file size jnz short RestoreStackNExit add dx, ax ; Calculate first free byte address mov [IBMDOS_buffer_addr], dx sub [IBMBIO_size_lo_1], ax ; Calculate, how much left to read sbb [IBMBIO_size_hi_1], 0 pop cx sub cx, ax ; Calculate free memory left mov bx, [IBMDOS_hndlr] cmp [IBMDOS_size_hi_1], 0 ; If size above 64k -- read max size, which can be ; disposed in free mem. ja short PRoceedToRead2 cmp [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, [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_2D1 ; Error -- exit cmp ax, cx jnz short Set_CF_NExit ; Readed less than file size -- exit add dx, ax ; Calculate first free byte address mov [After_IBMDOS_buffer], dx sub [IBMDOS_size_lo_1], ax ; Calculate, how much left to read sbb [IBMDOS_size_hi_1], 0 clc ; Clear CF -- no error. Retn_33A: 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. ; GetFileSizeDateTime: mov ax, 3D00h int 21h ; DOS - 2+ - OPEN DISK FILE WITH HANDLE ; DS:DX -> ASCIZ filename ; AL = access mode ; 0 - read ; Returns AX = file handle (or err code, if CF=1) jb short Retn_33A 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 ; Restore file pointer 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 retGetFileSizeDateTime: retn ;GetFileSizeDateTime endp ; =========================================================================== noRoomForSys: mov dx, aNoRoomForSyste ; "No room for system on destination disk" mov cx, [NoRoomForSysteSize] jmp ExitWithCode1 ; ============== subroutine ================================================= WriteSysFilesFromMem: mov dx, ReadedIBMDOS mov cx, [IBMDOS_buffer_addr] sub cx, dx jz short ProceedToIBMDOS mov bx, [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 retGetFileSizeDateTime cmp ax, cx ; Written less than should jnz short WriteError ProceedToIBMDOS: mov dx, [IBMDOS_buffer_addr] mov cx, [After_IBMDOS_buffer] sub cx, dx jz short retGetFileSizeDateTime mov bx, [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 retGetFileSizeDateTime cmp ax, cx ; Written less than should? jz short retGetFileSizeDateTime WriteError: stc retn ;WriteSysFilesFromMem endp ; ============== subroutine ================================================= ; Writes boot with correct BPB for 8-track FDD's ; Possibly attempt to automatically fix boot for old, (1.XX), ; incompatible floppies. WriteBoot: mov ah, 32h mov dl, [ds:5Ch] ; Target driver 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 mov al, [bx+16h] ; media ID byte ; ; http://www.ctyme.com/intr/rb-2590.htm#Table1356 ; FFh floppy, double-sided, 8 sectors per track (320K) ; FEh floppy, single-sided, 8 sectors per track (160K) ; FDh floppy, double-sided, 9 sectors per track (360K) ; FCh floppy, single-sided, 9 sectors per track (180K) ; See also: http://en.wikipedia.org/wiki/File_Allocation_Table#FATID push cs ; Restore spoiled DS pop ds cmp al, 0FEh ; If less -- exit from proc. ; Known ID's, less than FEh are that for 9-track FDD's jb short WriteBootExit test al, 1 ; For known codes, last bit=1 for double-sided jz short DoWriteBoot ; Update boot image for double-sided disk mov bx, NewBoot mov word [bx+11h], 70h ; BPB -- root entries mov word [bx+13h], 280h ; BPB -- NumSectors inc byte [bx+15h] ; BPB -- MediaType inc word [bx+1Ah] ; NumberOfHeads DoWriteBoot: mov al, [ds:5Ch] ; Target driver dec al ; FCB disk number to DOS disk number mov bx, NewBoot xor dx, dx mov cx, dx inc cx ; One sector, starting from sector 0 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 ; Original flags are left on stack by int 26h, ; must be popped by caller. WriteBootExit: retn ;WriteBoot endp ; ============== subroutine ================================================= ; Clears buffer before and after. WaitForAnyKey: 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") retn ;WaitForAnyKey endp ; =========================================================================== aIncorrectDosVe db 'Incorrect DOS version',0Dh,0Ah,'$' aInvalidDriveSp db 'Invalid drive specification' InvalidDriveSpSize dw 1Bh aInvalidParamet db 'Invalid parameter' InvalidParametSize dw 11h aNoRoomForSyste db 'No room for system on destination disk' NoRoomForSysteSize dw 26h ; Unused aIncompatibleSy db 'Incompatible system size' IncompatibleSySize dw 18h aSystemTransfer db 'System transferred' SystemTransferSize dw 12h aInsertSystemDi db 'Insert system disk in drive ' aDriverLetter1 db 41h aAndStrikeAnyKe db 0Dh,0Ah db 'and strike any key when ready',0Dh,0Ah InsertSystemDiskFullSize dw 3Eh currentDrive db 0 aAIbmbio_com db 'A:\IBMBIO.COM',0 aAIbmdos_com db 'A:\IBMDOS.COM',0 ; ; IBMBIO.COM data IBMBIO_hndlr dw 0 IBMBIO_size_lo_1 dw 0 IBMBIO_size_lo_2 dw 0 ; Just copy of IBMBIO_size_lo_1... IBMBIO_size_hi_1 dw 0 IBMBIO_size_hi_2 dw 0 ; Just copy of IBMBIO_size_hi_1... 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 ; Just copy of IBMDOS_size_lo_1... IBMDOS_size_hi_1 dw 0 IBMDOS_size_hi_2 dw 0 ; Just copy of IBMDOS_size_hi_1... IBMDOS_time dw 0 IBMDOS_date dw 0 TargetIBMDOS_hndlr dw 0 ; ; anyFileMask db 'A:\*.*',0 DiskRootDir db 'X:\',0 CurDirDisk db 58h, 3Ah, 5Ch ; "X:\" -- beginning for next Dir Buffer ; 64 bytes CurDirBuffer 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,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 ; 0x01 file is read only ; 0x02 hidden file ; 0x04 system file ; 0x08 a special entry containing the disk's volume label ; 0x10 The entry describes a subdirectory. ; 0x20 This is the archive flag. ; 0x40 Not used; must be set to 0. ; 0x80 Not used; must be set to 0. ; ; 16h -- hidden + system + dir(?) FCBext_forFind ExtFCB_t 0,"????????", "???", 0,16h,0 ;FCBext_forFind db 0FFh ; ExtMarker ; ; ; db 5 dup(0) ; Reserved1 ; db 16h ; Attribute ; db 0 ; Driver ; db 8 dup('?') ; FileName ; db 3 dup('?') ; 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 db 0 db 0 db 0 db 0 db 0 PutativeMaxFreeMem dw 0 ; Possibly contains approximation of free mem, ; above code, below stack IBMDOS_buffer_addr dw 0 After_IBMDOS_buffer dw 0 NewBoot db 0EBh, 2Ch, 90h, 49h, 42h, 4Dh, 20h, 20h, 32h, 2Eh, 30h, 0, 2, 2, 1, 0; 0 db 2, 40h, 0, 40h, 1, 0FEh, 1, 0, 8, 0, 1, 0, 0, 0, 0, 0; 16 db 0Ah, 0DFh, 2, 25h, 2, 9, 2Ah, 0FFh, 50h, 0F6h, 0Fh, 2, 0CDh, 19h, 0FAh, 33h; 32 db 0C0h, 8Eh, 0D0h, 0BCh, 0, 7Ch, 8Eh, 0D8h, 0A3h, 7Ah, 0, 0C7h, 6, 78h, 0, 21h; 48 db 7Ch, 0FBh, 0CDh, 13h, 73h, 3, 0E9h, 95h, 0, 0Eh, 1Fh, 0A0h, 10h, 7Ch, 98h, 0F7h; 64 db 26h, 16h, 7Ch, 3, 6, 1Ch, 7Ch, 3, 6, 0Eh, 7Ch, 0A3h, 3, 7Ch, 0A3h, 13h; 80 db 7Ch, 0B8h, 20h, 0, 0F7h, 26h, 11h, 7Ch, 5, 0FFh, 1, 0BBh, 0, 2, 0F7h, 0F3h; 96 db 1, 6, 13h, 7Ch, 0E8h, 7Eh, 0, 72h, 0B3h, 0A1h, 13h, 7Ch, 0A3h, 7Eh, 7Dh, 0B8h; 112 db 70h, 0, 8Eh, 0C0h, 8Eh, 0D8h, 0BBh, 0, 0, 2Eh, 0A1h, 13h, 7Ch, 0E8h, 0B6h, 0; 128 db 2Eh, 0A0h, 18h, 7Ch, 2Eh, 2Ah, 6, 15h, 7Ch, 0FEh, 0C0h, 32h, 0E4h, 50h, 0B4h, 2; 144 db 0E8h, 0C1h, 0, 58h, 72h, 38h, 2Eh, 28h, 6, 20h, 7Ch, 76h, 0Eh, 2Eh, 1, 6; 160 db 13h, 7Ch, 2Eh, 0F7h, 26h, 0Bh, 7Ch, 3, 0D8h, 0EBh, 0CEh, 0Eh, 1Fh, 0CDh, 11h, 0D0h; 176 db 0C0h, 0D0h, 0C0h, 25h, 3, 0, 75h, 1, 40h, 40h, 8Bh, 0C8h, 0F6h, 6, 1Eh, 7Ch; 192 db 80h, 75h, 2, 33h, 0C0h, 8Bh, 1Eh, 7Eh, 7Dh, 0EAh, 0, 0, 70h, 0, 0BEh, 0C9h; 208 db 7Dh, 0E8h, 2, 0, 0EBh, 0FEh, 2Eh, 0ACh, 24h, 7Fh, 74h, 4Dh, 0B4h, 0Eh, 0BBh, 7; 224 db 0, 0CDh, 10h, 0EBh, 0F1h, 0B8h, 50h, 0, 8Eh, 0C0h, 0Eh, 1Fh, 2Eh, 0A1h, 3, 7Ch; 240 db 0E8h, 43h, 0, 0BBh, 0, 0, 0B8h, 1, 2, 0E8h, 58h, 0, 72h, 2Ch, 33h, 0FFh; 256 db 0B9h, 0Bh, 0, 26h, 80h, 0Dh, 20h, 26h, 80h, 4Dh, 20h, 20h, 47h, 0E2h, 0F4h, 33h; 272 db 0FFh, 0BEh, 0DFh, 7Dh, 0B9h, 0Bh, 0, 0FCh, 0F3h, 0A6h, 75h, 0Eh, 0BFh, 20h, 0, 0BEh; 288 db 0EBh, 7Dh, 0B9h, 0Bh, 0, 0F3h, 0A6h, 75h, 1, 0C3h, 0BEh, 80h, 7Dh, 0E8h, 0A6h, 0FFh; 304 db 0B4h, 0, 0CDh, 16h, 0F9h, 0C3h, 1Eh, 0Eh, 1Fh, 33h, 0D2h, 0F7h, 36h, 18h, 7Ch, 0FEh; 320 db 0C2h, 88h, 16h, 15h, 7Ch, 33h, 0D2h, 0F7h, 36h, 1Ah, 7Ch, 88h, 16h, 1Fh, 7Ch, 0A3h; 336 db 8, 7Ch, 1Fh, 0C3h, 2Eh, 8Bh, 16h, 8, 7Ch, 0B1h, 6, 0D2h, 0E6h, 2Eh, 0Ah, 36h; 352 db 15h, 7Ch, 8Bh, 0CAh, 86h, 0E9h, 2Eh, 8Bh, 16h, 1Eh, 7Ch, 0CDh, 13h, 0C3h, 0, 0; 368 db 0Dh, 0Ah, 4Eh, 6Fh, 6Eh, 2Dh, 53h, 79h, 73h, 74h, 65h, 6Dh, 20h, 64h, 69h, 73h; 384 db 6Bh, 20h, 6Fh, 72h, 20h, 64h, 69h, 73h, 6Bh, 20h, 65h, 72h, 72h, 6Fh, 72h, 0Dh; 400 db 0Ah, 52h, 65h, 70h, 6Ch, 61h, 63h, 65h, 20h, 61h, 6Eh, 64h, 20h, 73h, 74h, 72h; 416 db 69h, 6Bh, 65h, 20h, 61h, 6Eh, 79h, 20h, 6Bh, 65h, 79h, 20h, 77h, 68h, 65h, 6Eh; 432 db 20h, 72h, 65h, 61h, 64h, 79h, 0Dh, 0Ah, 0, 0Dh, 0Ah, 44h, 69h, 73h, 6Bh, 20h; 448 db 42h, 6Fh, 6Fh, 74h, 20h, 66h, 61h, 69h, 6Ch, 75h, 72h, 65h, 0Dh, 0Ah, 0, 69h; 464 db 62h, 6Dh, 62h, 69h, 6Fh, 20h, 20h, 63h, 6Fh, 6Dh, 30h, 69h, 62h, 6Dh, 64h, 6Fh; 480 db 73h, 20h, 20h, 63h, 6Fh, 6Dh, 30h, 0, 0, 0, 0, 0, 0, 0, 55h, 0AAh; 496 ; Also --- starting address for IBMDOS.COM ReadedIBMDOS db 1Ah db 1Ah db 1Ah db 1Ah db 1Ah db 1Ah
Скачати код, разом із лістингом, згенерованим IDA, скомпільованим файлом та, для порівняння, оригінальним SYS.COM, можна тут. Скомпільований код, з точністю до опкодів-синонімів, тотожній оригінальному.
Алгоритм роботи
Хоча оновлення від 2.00 до 2.10 було скромним, однак внутрішньо SYS.COM змінився дуже сильно. Більшість викликів до FCB-сервісів замінені на аналогічні нові виклики, що працюють з номерами (handles) файлів.
На початку --- традиційний перехід до перевірки версії, після якої знаходиться два байти --- адреса копії бут-сектора в пам'яті та стрічка "Vers 1.82", очевидно, внутрішня версія SYS.COM. Після неї --- перевірка версії DOS. Мінімальна припустима -- та ж, що і раніше, 1.54 (1.36h), максимальна -- 2.10 ( 2.0Ah). Якщо версія невірна, виводиться "Incorrect DOS version", програма завершується. Ця стрічка все ще виводиться за допомогою старого AH=09h/INT 21h. Очевидно, щоб показати коректне повідомлення, навіть якщо запущено з-під старого DOS.
Далі йде звична перевірка коректності цільового диску та спроби запису на той же, з якого читаємо. Однак, після неї, читається перший сектор диску. Очікується, що в ньому знаходиться FAT! В цьому (гіпотетичному) FAT перевіряється перший байт, який мав би відповідати Media ID. Якщо він менший, ніж 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, виводиться дещо загадкове "No room for system on destination disk". Очевидно, це спроба не перезаписувати системні файли на не-FAT дисках.
І повідомлення про відсутність місця, і згадане "Invalid drive specification", виводяться тепер за допомогою файлового виклику AH=40h/INT 21h, "WRITE TO FILE WITH HANDLE", з BX=2, "Standard Error Device handler". Програма завершується з кодом завершення, рівним 1, за допомогою виклику AH=4Ch/INT 21h. Взагалі, це єдиний код повернення для всіх помилок. Забігаючи наперед, якщо програма завершилася успішно, повертається нуль.
Якщо Media ID влаштовує, програма кладе літеру поточного диску замість "A" в дві стрічки, "A:\IBMBIO.COM" і "A:\IBMBIO.COM", що закінчуються нулем, та намагається відкрити їх і визначити розмір, дату та час файлу за допомогою функції GetFileSizeDateTime, яка приймає в DX адресу стрічки з іменем, в DI -- адресу, куди покласти номер відкритого файлу. Розмір, час і дата зберігаються зразу після нього. Що дивно, розмір зберігається хитро -- спочатку дві копії молодшого слова, потім дві копії старшого... Для повідомлення про помилку функція встановлює прапорець переносу -- CF.
Працює GetFileSizeDateTime так. Намагається відкрити файл, (за допомогою AX=3D00h/INT 21h, " OPEN DISK FILE WITH HANDLE", доступ на читання). Якщо не вдалося -- завершується (передаючи вже встановлений викликом INT 21h прапорець CF). Інакше --- зберігає номер файлу, викликом AH=4202h/INT 21h, "MOVE FILE READ/WRITE POINTER (LSEEK)", переміщає файловий вказівник в кінець. Отримане в DX:AX значення відповідає положенню вказівника відносно початку, і буде рівним розміру файлу. Його зберігає після номера (чомусь двічі старше і двічі молодше слово!). (Нагадаю, stosw автоматично пересуватиме DI). Далі файловий вказівник переміщається на початок, і за допомогою AH=57/INT 21h, "GET FILE'S DATE/TIME" читається та зберігається дата і час файлу. Явної перевірки помилок цих викликів не робиться, однак якщо котрийсь із них поверне CF, код, що викликав GetFileSizeDateTime, прореагує, як і на інші помилки читання системних файлів з поточного диску, проханням вставити системний диск: "Insert system disk in drive ".
Ймовірно, двічі зберігати розмір планувалося для того, щоб один із них залишався незмінним, а інший -- змінювався, вказуючи, скільки ще залишилося записати (див. нижче). Спочатку двічі молодше слово, потім двічі -- старше, ймовірно, зроблено для простоти -- stosw так зручніше.
Процедура обробки цього прохання, "Insert system disk in drive", знаходиться перед точкою входу, тому після неї програма ефективно почнеться з початку (ну, крім перевірки версії DOS --- немає сенсу її повторювати). Для виводу повідомлення використовується те ж AH=40h/INT 21h, "WRITE TO FILE WITH HANDLE", з BX=2, "Standard Error Device handler". Очікування "any key" здійснюється спеціальною підпрограмкою, WaitForAnyKey, що чекає на натискання, потім очищає буфер (чого не робили попередні версії).
Далі програма поводиться хитріше, ніж її попередники. Зроблено так, що якщо файли IBMBIO.COM i IBMDOS.COM не вміщаються в пам'ять, то читання і запис здійснюються шматками, які поміщаються. Для оцінки доступної пам'яті від вершини стеку, SP, віднімається 98Ah. (Цілком раціональна оцінка.). Далі читається, скільки влізло, функцією ReadFilesToMem, яку детальніше розглянемо пізніше. Якщо були помилки --- просимо вставити системний диск. Інакше зберігає поточну директорію цільового диску. Директорія поточного не змінюється --- звертається до файлів з абсолютним шляхом. (Версія 2.00, працюючи за допомогою FCB, особливого вибору не мала, мусила змінювати і там і там.) На цільовому диску здійснюється перехід в корінь та пошук з допомогою FCB за маскою "*.*", після чого поточний шлях відновлюється.
Якщо на ньому жодних файлів не знайдено, шукаємо також мітку диску (вона завжди шукається в корені, тому неважливо, що директорія вже може бути не поточною). Якщо в корені немає жодних "повноцінних" файлів, але є мітка диску, повідомляємо, що на диску немає місця і завершуємо. (Ймовірно, пов'язано із тим, що тоді перші записи каталогу вважаються зайнятими.). Інакше переходимо до переносу системи.
Якщо якісь файли таки знайдені, перевіряємо, чи є поміж них IBMBIO.COM і IBMDOS.COM, якщо є, вважається, що вони правильно розташовані, і переходиться до переносу системи. Якщо ж немає --- все те ж "немає місця для системи".
Перенос системи починається із того, що обом системним файлам (є там вони, чи ні :), очищаються атрибути, помилки не перевіряються --- якщо файлів немає, ось і добре, а якщо є, вони перестануть бути захищеними від запису. Далі обидва файли створюються (або обрізаються, якщо вже існували), викликом AH=3Ch/INT 21h ("CREATE OR TRUNCATE FILE WITH HANDLER"), з атрибутами "системні", "приховані", "тільки для читання". Перевірка помилок не здійснюється.
Далі, власне, головний цикл програми. Прочитана раніше порція записується. Звіряється, чи є ще не прочитані байти системних файлів. Якщо є --- читається та записується наступна порція. Якщо в процесі читання помилка, просимо вставити системний диск. Якщо помилка під час запису -- повідомляємо, що недостатньо місця і завершуємо. Запис здійснюється WriteSysFilesFromMem, читання все тією ж ReadFilesToMem. Розглянемо їх детальніше.
ReadFilesToMem перевіряє, чи є ще непрочитані байти в IBMBIO, якщо є -- читає, але не більше, ніж є вільної пам'яті. Для підрахунку використовується визначений раніше розмір. Прочитана кількість кожного разу від нього віднімається. Якщо прочитано менше, ніж є пам'яті, тобто ми прочитали весь залишок файлу (навіть якщо нуль байт, весь файл прочитано ще раніше) --- перемістити буфер для IBMDOS на початок вільної області. (Поки читається IBMBIO, цей буфер буде вказувати на кінець вільної пам'яті. Зроблено це елегантним трюком --- він вказує на адресу, що рівна початку буферів + розмір прочитаного.) Далі повторити аналогічну процедуру для IBMDOS. Ефективно, на кожному циклі читання буде читатися спершу IBMBIO, потім IBMDOS, шматками, які поміщаються в пам'ять.
WriteSysFilesFromMem працює дзеркально. Подивившись початок буферу для IBMDOS, (який знаходиться після буферу для IBMBIO), визначає, чи залишилося ще щось записувати в IBMBIO. Якщо залишилося --- записуємо. Далі переходимо до запису IBMDOS, якщо є ще що записувати. Якщо на котромусь етапі записалося не все, що планували --- повідомляємо про помилку прапорцем CF. (Вихід, якщо помилки не було, здійснюємо "чужим" retn... І чого автори так люблять цей трюк? Невже вся причина в дальнобійності, точніше в не-дальнобійності, коротких умовних переходів?).
Після цього відновлюємо дату та час системних файлів.
Наступний етап -- відновлення boot-сектора для старих, 8-секторних, дискет, така ж, як і раніше. Аж після цього друкується "System transferred". На цьому роботу програми закінчено.
Все ще присутній шматок коду, що виводить "Incompatible system size", який теж не використовується.
"Документація"
На відміну від попередньої версії, навіть не змінює поточну директорію вихідного диску, але зате відновлює поточну для цільового.
Не хитрує із стиранням-не стиранням системних файлів на цільовому. Якщо вони є, очищає їх атрибути, після чого просто обрізає. Якщо ж немає --- створює.
Дві найбільш важливі зміни:
Помітну частину програми переписано, все, крім пошуку Volume Label, реалізовано з використанням сучасних викликів, які працюють із номерами файлів. (З міткою диску альтернатив не було, DOS 2.XX не вмів шукати її без використання FCB. )
Нарешті додано менш-більш систематичну обробку помилок. В файлі менше сміття. Прибрано перекриття FCB для пошуку і нового boot-сектора.
Наступним кроком мав би бути SYS.COM з DOS 2.11. Однак, з ним "все складно". Слідкуйте за новинами ;-)
А поки:
Не хитрує із стиранням-не стиранням системних файлів на цільовому. Якщо вони є, очищає їх атрибути, після чого просто обрізає. Якщо ж немає --- створює.
Дві найбільш важливі зміни:
- Програма працюватиме на системах із малим об'ємом пам'яті, читаючи порціями, рівними доступному об'єму. Всі попередні в таких умовах вмирали б в тяжких муках.
- Додано обробку багатьох типових помилкових ситуацій, хоч повідомлення часто будуть загадковими.
Про код
Помітну частину програми переписано, все, крім пошуку Volume Label, реалізовано з використанням сучасних викликів, які працюють із номерами файлів. (З міткою диску альтернатив не було, DOS 2.XX не вмів шукати її без використання FCB. )
Нарешті додано менш-більш систематичну обробку помилок. В файлі менше сміття. Прибрано перекриття FCB для пошуку і нового boot-сектора.
Наступним кроком мав би бути SYS.COM з DOS 2.11. Однак, з ним "все складно". Слідкуйте за новинами ;-)
А поки:
Дякую за увагу!
Немає коментарів:
Дописати коментар