понеділок, 10 червня 2013 р.

Аналіз SYS.COM з PC-DOS 2.00

Після 1.10, в березні 1983, "наступила" версія 2.00. Фактично, нова операційна система. З'явилася підтримка директорій, засоби роботи з файлами у стилі UNIX (завдяки чому зразу застаріли FCB), підтримка жорстких дисків та нових форматів дискет. З'явилася можливість завантажувати драйвери пристроїв, резидентні програми, купа нових внутрішніх і декілька --- зовнішніх, команд. Крім набору засобів роботи з файлами та резидентами, з'явилися системні виклики для керування динамічною пам'яттю.

Подивимося як це все вплинуло на нашу жертву.


Для кращого розуміння --- див. попередні статті серії. Код -- сумісний з Flat Assembler. 

Коментарі --- англійською, щоб й іноземні колеги мали шанс зрозуміти, однак англійську я знаю відверто слабенько, тому буду вдячний за зауваження та виправлення. :-)

;  This file is generated by The Interactive Disassembler (IDA)     
;  Copyright (c) 2010 by Hex-Rays SA, <support@hex-rays.com>     
;     Licensed to: Freeware version       
;
; Modified to compile by fasm and commented by Indrekis, indrekis2.blogspot.com
;
; Input MD5   : ED2306A7FCCEEB053801ACD5E2F0EACE

; File Name   : PC-DOS_2.00\SYS.COM
; Format      : MS-DOS COM-file
; Base Address: 0h Range: 100h-680h Loaded length: 580h

IBMBIO_DTA  = 5666h

  ;.8086
  use16
  org 100h

  include "my_fcb_2b.inc"

start:
  jmp short EntryPoint1


  db  8Eh ; Ћ  ; ; code for mov es, word ptr [bp+di] ?
  db    3

; ===========================================================================
; 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.00
  mov ah, 30h
  int 21h  ; DOS - GET DOS VERSION
     ; Return: AL = major version number (00h for DOS 1.x)
  xchg ah, al  ; Now AX=High:Low version
  cmp ax, 136h ; Why this number, instead of obvious 200h?
     ; May be some development version of DOS 2.0 had
     ; internal versions starting from 1.54?
  jb short WrongDOSVersion
  cmp ax, 200h
  jbe short DosVersionOK ; If version is newer, than 2.00, exit too.

WrongDOSVersion:   
  push cs  ; Restore DS, but why?
  pop ds
  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
; ===========================================================================

AnyArgIsInvalid:   
  mov dx, aInvalidParamet ; "Invalid parameter$"
  jmp PrintMsgNExit
; ===========================================================================

BadDriveLetter:    
  mov dx, aInvalidDriveSp ; "Invalid drive specification$"
  jmp PrintMsgNExit
; ===========================================================================

AskToInsertSysDisk:   
  mov al, [currentDrive]
  add al, 40h  ; Disk number (1 - A, 2 - B, ...) to disk letter.

loc_134:
  mov [drive_letter], al
  mov dx, aInsertSystemDi ; "Insert system disk in drive "
  mov ah, 9
  int 21h  ; DOS - PRINT STRING
     ; DS:DX -> string terminated by "$"
  mov ax, 0C08h
  int 21h  ; DOS - CLEAR KEYBOARD BUFFER
     ; AL must be 01h, 06h, 07h, 08h, or 0Ah.
  xor al, al  ; Restore AL -- 00 means correct first FCB (we have
     ; already checked).

EntryPoint2CorrectVer:   
  cmp byte ptr ds:5Dh, 20h ; PSP: 5Ch-6Bh (16 bytes) -- Unopened Standard FCB 1
     ; So here will be any symbol, entered after [a-z][:][\]
  jnz short AnyArgIsInvalid
  cmp al, 0FFh ;   AL = 00h if first FCB has valid drive letter, FFh if not
     ; ( 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 ptr 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
; Put current driver letter in the strings, where
; it will be used in messages.
  mov dl, al
  add al, 40h  ; 41h -- code for 'A', we have disk numbers, starting from,
     ; so to convert number to letter, should add 40h.
  mov byte [currentDriveChar1], al
  mov byte [currentDriveChar2], al ; ":/"
; DOS have directories, so determine where we are now
  mov si, CurDirPath ; ""
  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
;
; currentDriveChar2 after previous code containt driver letter + "\:",
; ended with 0 (C-string), for example, "a:\", so we change directory to
; source disk root, (where sys files exists).
  mov dx, currentDriveChar2 ; ":/"
  mov ah, 3Bh
  int 21h  ; DOS - 2+ - CHANGE THE CURRENT DIRECTORY (CHDIR)
     ; DS:DX -> ASCIZ directory name (may include drive)
;
; Open system files.
  mov ah, 0Fh
  mov dx, FCBext_1
  int 21h  ; DOS - OPEN DISK FILE
     ; DS:DX -> FCB
     ; Return: AL = 00h file found, FFh file not found
  or al, al
  jnz short AskToInsertSysDisk
  mov dx, FCBext_2
  mov ah, 0Fh
  int 21h  ; DOS - OPEN DISK FILE
     ; DS:DX -> FCB
     ; Return: AL = 00h file found, FFh file not found
  or al, al
  jnz short AskToInsertSysDisk
; We have found IBMBIO.COM and IBMDOS.COM files. (No check for COMMAND.COM)
  mov ah, 1Ah
  mov dx, IBMBIO_DTA
  int 21h  ; DOS - SET DISK TRANSFER AREA ADDRESS
     ; DS:DX -> disk transfer buffer
;
; Both FCBs are opened, so we can set record size
  mov ax, 1
  mov word [FCBext_1.RecordSize], ax
  mov word [FCBext_2.RecordSize], ax
  
; Read IBMBIO
  mov bx, FCBext_1
  mov cx, 4000h ; Actual IBMBIO.COM size -- 4608d=9*512, less than 4000h.
  call ReadFile ; On entry BX -- FCB, DTA is set
     ; Saves after the FCB file date, file time and number
     ; of readed bytes (recors, but recors size=1)
; IBMBIO readed, proceeding to IBMDOS
  mov dx, IBMDOS_DTA
  mov ah, 1Ah
  int 21h  ; DOS - SET DISK TRANSFER AREA ADDRESS
     ; DS:DX -> disk transfer buffer
  mov bx, FCBext_2
  mov cx, 5000h
  call ReadFile ; On entry BX -- FCB, DTA is set
     ; Saves after the FCB file date, file time and number
     ; of readed bytes (recors, but recors size=1)
; IBMDOS readed.
;
; Both files readed, their date, time and size saved.
;

  mov al, [ds:5Ch] ; Target disk from PSP FCB1
  mov [FCBext_2.Driver], al ; Set for FCB2
  mov [FCBext_1.Driver], al ; Set for FCB1
  mov [FCBext_forFind.Driver], al
; Set letter for taget disk
  add al, 40h
; currentDriveChar2 after previous code containt driver letter + "\:",
; ended with 0 (C-string), for example, "a:\", so we change directory to
; target disk root.
  mov byte [currentDriveChar2], al ; ":/"
  mov dx, currentDriveChar2 ; ":/"
  mov ah, 3Bh
  int 21h  ; DOS - 2+ - CHANGE THE CURRENT DIRECTORY (CHDIR)
     ; DS:DX -> ASCIZ directory name (may include drive)
; Return DTA to default address
  mov ah, 1Ah
  mov dx, 80h  ; Default DTA in PSP.
  int 21h  ; DOS - SET DISK TRANSFER AREA ADDRESS
     ; DS:DX -> disk transfer buffer
; Search files by mask *.* (== ????????.???)
  mov ah, 11h
  mov dx, FCBext_forFind
  int 21h  ; DOS - SEARCH FIRST USING FCB
     ; DS:DX -> FCB
  or al, al
  jnz short noFilesAtTarget ; Jump, if no files found at disk root.
; Search for IBMBIO.COM on target disk
  mov dx, FCBext_1
  mov ah, 0Fh
  int 21h  ; DOS - OPEN DISK FILE
     ; DS:DX -> FCB
     ; Return: AL = 00h file found, FFh file not found
; If there are files at target disk root, and
; none of them has name "IBMBIO.COM" -- tell that
; there is no room for system
  or al, al
  jnz short noRoomForSys
; If IBMDOS.COM exists, open it
  mov dx, FCBext_2
  mov ah, 0Fh
  int 21h  ; DOS - OPEN DISK FILE
     ; DS:DX -> FCB
     ; Return: AL = 00h file found, FFh file not found
; If there are files at target disk root, and
; no of them has name "IBMDOS.COM" -- tell that
; there is no room for system
  or al, al
  jnz short noRoomForSys
  mov bx, dx  ; ; What is the purpose of this move?
; If IBMDOS exists on target -- delete it.
  mov ah, 13h
  int 21h  ; DOS - DELETE FILE via FCB
     ; DS:DX -> FCB with filename field filled with
     ; template for deletion ('?' wildcard allowed, but not '*')
     ; Return: AL = 00h file found, FFh file not found
; Write system files to target

noFilesAtTarget:   
  mov dx, IBMBIO_DTA
  mov bx, FCBext_1
  call WriteSysFile ; At call BX -- unopened tagret extended FCB, size,
     ; date and time is saved after the FCB,
     ; DX -- DTA adress
  mov dx, IBMDOS_DTA
  mov bx, FCBext_2
  call WriteSysFile ; At call BX -- unopened tagret extended FCB, size,
     ; date and time is saved after the FCB,
     ; DX -- DTA adress
  mov dx, aSystemTransfer ; "System transferred$"

WriteMsgWriteBootNExit:   
  mov ah, 9
  int 21h  ; DOS - PRINT STRING
     ; DS:DX -> string terminated by "$"
; Restore old path for source disk.
  mov dx, currentDriveChar1
  mov ah, 3Bh
  int 21h  ; DOS - 2+ - CHANGE THE CURRENT DIRECTORY (CHDIR)
     ; DS:DX -> ASCIZ directory name (may include drive)
  call WriteBoot ; ; Writes boot with correct BPB for 8-track FDD's
     ; ; Possibly attempt to automatically fix boot for old, (1.XX),
     ; ; incompatible floppies.
  int 20h  ; DOS - PROGRAM TERMINATION
     ; returns to DOS--identical to INT 21/AH=00h
; ===========================================================================
; Here we fall in case of errors, which occured before
; we have changed current directory.

PrintMsgNExit:    
  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
; ===========================================================================
; At time of this error, default path has been changed -- need to restore.
; So jumpint to WrtieMsgWriteBootNExit

noRoomForSys:    
  mov dx, aNoRoomForSyste ; "No room for system on destination disk$"
  jmp short WriteMsgWriteBootNExit


; ===========================================================================
; This code is unused!
  mov dx, aIncompatibleSy ; "Incompatible system size$"
  jmp short WriteMsgWriteBootNExit


virtual at bx
        bxExtFCB_t ExtFCB_t 0,"        ","   ",0,0,0 ; To cheat fasm, needs exactly 8/3 spaces
       ; In fact, values are ignored.
end virtual


; ============== subroutine =================================================
; On entry BX -- FCB, DTA is set
; Saves after the FCB file date, file time and number
; of readed bytes (recors, but recors size=1)

; Procedure is exactly the same, as in PC-DOS 1.10

ReadFile:
  mov ah, 27h
  mov dx, bx
  int 21h  ; DOS - RANDOM BLOCK READ
     ; DS:DX -> FCB
     ; CX = number of records to be read
; After the FCB we have variables IBM???_date, IBM???_time, IBM???_readed_size
  mov [bx+30h], cx
  mov ax, word [bxExtFCB_t.FileDate] ;word prt [bx+ExtFCB_t.FileDate]
  mov [bx+2Ch], ax
  mov ax, word [bxExtFCB_t.FileTime] ;word ptr [bx+ExtFCB_t.FileTime]
  mov [bx+2Eh], ax
  retn
;ReadFile 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, [FCBext_1.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
; If disk is single-sided --- write boot for it
  test al, 1  ; For known codes, last bit=1 for double-sided
  jz short DoWriteBoot
; Update boot image for double-sided disk
  mov bx, FCBext_forFind.DirectRecord ; Also points to beginning of new boot.
  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, [FCBext_1.Driver]
  dec al  ; FCB disk number to DOS disk number
  mov bx, FCBext_forFind.DirectRecord ; Also points to beginning of new boot.
  xor dx, dx
; Write new boot

loc_280:
  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  ; Original flags are left on stack by int 26h,
     ; must be popped by caller.

WriteBootExit:    
  retn
;WriteBoot endp ; sp =  2


; ============== subroutine =================================================
; Unlike SYS 1.XX, now we have some error checking during writes.
; At call BX -- unopened tagret extended FCB, size,
; date and time is saved after the FCB,
; DX -- DTA adress

WriteSysFile:
  mov ah, 1Ah
  int 21h  ; DOS - SET DISK TRANSFER AREA ADDRESS
     ; DS:DX -> disk transfer buffer
  mov dx, bx
  mov ah, 16h
  int 21h  ; DOS - CREATE A DISK FILE
     ; DS:DX -> FCB
  xor ax, ax
  mov word [bxExtFCB_t.DirectRecord], ax
  mov word [bxExtFCB_t.DirectRecord+2], ax
  inc ax
  mov word [bxExtFCB_t.RecordSize], ax
  mov ah, 28h
  mov cx, [bx+30h] ; Readed file size
  int 21h  ; DOS - RANDOM BLOCK WRITE
     ; DS:DX -> FCB
     ; CX = number of records to be written
     ; if zero, truncate file to current random file position
  cmp al, 1  ; AL=01h: disk full or file read-only
  jz short noRoomForSys
  mov ax, [bx+2Ch] ; Set target file date and time, saved from source file
  mov word [bxExtFCB_t.FileDate], ax
  mov ax, [bx+2Eh]
  mov word [bxExtFCB_t.FileTime], ax
  mov ah, 10h
  int 21h  ; DOS - CLOSE DISK FILE
     ; DS:DX -> FCB
     ; Return: AL = 00h directory update successful
     ; FFh file not found in directory
  retn
; WriteSysFile endp

; ===========================================================================
  db    0
currentDriveChar2 db 0,':/',0           
currentDriveChar1 db 0  
  db  3Ah ; :
  db  2Fh ; /
CurDirPath 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
     
currentDrive db 0  

FCBext_1 ExtFCB_t 0,"IBMBIO  ", "COM", 0,7,0
;FCBext_1 db 0FFh   ; ExtMarker      
;  db 5 dup(0)  ; Reserved1
;  db 7   ; Attribute
;  db 0   ; Driver
;  db 'I', 'B', 'M', 'B', 'I', 'O', 2 dup(' '); FileName
;  db 'C', 'O', 'M'        ; FileExt
;  db 2 dup(0)  ; CurBlock
;  db 2 dup(0)  ; RecordSize
;  db 4 dup(0)  ; FileSize
;  db 2 dup(0)  ; FileDate
;  db 2 dup(0)  ; FileTime
;  db 8 dup(0)  ; Reserved2
;  db 0   ; CurRecord
;  db 4 dup(0)  ; DirectRecord
IBMBIO_date dw 0
IBMBIO_time dw 0
IBMBIO_readed_size dw 0

FCBext_2 ExtFCB_t 0,"IBMDOS  ", "COM", 0,7,0
;FCBext_2 db 0FFh   ; ExtMarker 
;  db 5 dup(0)  ; Reserved1
;  db 7   ; Attribute
;  db 0   ; Driver
;  db 'I', 'B', 'M', 'D', 'O', 'S', 2 dup(' '); FileName
;  db 'C', 'O', 'M'        ; FileExt
;  db 2 dup(0)  ; CurBlock
;  db 2 dup(0)  ; RecordSize
;  db 4 dup(0)  ; FileSize
;  db 2 dup(0)  ; FileDate
;  db 2 dup(0)  ; FileTime
;  db 8 dup(0)  ; Reserved2
;  db 0   ; CurRecord
;  db 4 dup(0)  ; DirectRecord
IBMDOS_date dw 0
IBMDOS_time dw 0
IBMDOS_readed_size dw 0

; 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.
;
; 1Eh=11110b -- hidden, system, volume lable, directory
FCBext_forFind ExtFCB_t_DR 0,"????????", "???", 0,1Eh,0, 0EBh, 2Ch, 90h, 49h ; <= 4 last - bytes of boot
;FCBext_forFind db 0FFh   ; ExtMarker 
;  db 5 dup(0)  ; Reserved1
;  db 1Eh   ; 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 0EBh, 2Ch, 90h, 49h ; DirectRecord  
  
; First 4 bytes of boot are intersected with FCB for find!
NewBoot_minus_first_4_bytes db 42h, 4Dh, 20h, 20h, 32h, 2Eh, 30h, 0, 2, 2, 1, 0, 2; 0
  db 40h, 0, 40h, 1, 0FEh, 1, 0, 8, 0, 1, 0, 0, 0, 0, 0; 13
  db 0Ah, 0DFh, 2, 25h, 2, 9, 2Ah, 0FFh, 50h, 0F6h, 0, 2; 28
  db 0CDh, 19h, 0FAh, 33h, 0C0h, 8Eh, 0D0h, 0BCh, 0, 7Ch; 40
  db 8Eh, 0D8h, 0A3h, 7Ah, 0, 0C7h, 6, 78h, 0, 21h, 7Ch; 50
  db 0FBh, 0CDh, 13h, 73h, 3, 0E9h, 95h, 0, 0Eh, 1Fh, 0A0h; 61
  db 10h, 7Ch, 98h, 0F7h, 26h, 16h, 7Ch, 3, 6, 1Ch, 7Ch; 72
  db 3, 6, 0Eh, 7Ch, 0A3h, 3, 7Ch, 0A3h, 13h, 7Ch, 0B8h; 83
  db 20h, 0, 0F7h, 26h, 11h, 7Ch, 5, 0FFh, 1, 0BBh, 0, 2; 94
  db 0F7h, 0F3h, 1, 6, 13h, 7Ch, 0E8h, 7Eh, 0, 72h, 0B3h; 106
  db 0A1h, 13h, 7Ch, 0A3h, 7Eh, 7Dh, 0B8h, 70h, 0, 8Eh, 0C0h; 117
  db 8Eh, 0D8h, 0BBh, 0, 0, 2Eh, 0A1h, 13h, 7Ch, 0E8h, 0B6h; 128
  db 0, 2Eh, 0A0h, 18h, 7Ch, 2Eh, 2Ah, 6, 15h, 7Ch, 0FEh; 139
  db 0C0h, 32h, 0E4h, 50h, 0B4h, 2, 0E8h, 0C1h, 0, 58h, 72h; 150
  db 38h, 2Eh, 28h, 6, 20h, 7Ch, 76h, 0Eh, 2Eh, 1, 6, 13h; 161
  db 7Ch, 2Eh, 0F7h, 26h, 0Bh, 7Ch, 3, 0D8h, 0EBh, 0CEh; 173
  db 0Eh, 1Fh, 0CDh, 11h, 0D0h, 0C0h, 0D0h, 0C0h, 25h, 3; 183
  db 0, 75h, 1, 40h, 40h, 8Bh, 0C8h, 0F6h, 6, 1Eh, 7Ch, 80h; 193
  db 75h, 2, 33h, 0C0h, 8Bh, 1Eh, 7Eh, 7Dh, 0EAh, 0, 0, 70h; 205
  db 0, 0BEh, 0C9h, 7Dh, 0E8h, 2, 0, 0EBh, 0FEh, 2Eh, 0ACh; 217
  db 24h, 7Fh, 74h, 4Dh, 0B4h, 0Eh, 0BBh, 7, 0, 0CDh, 10h; 228
  db 0EBh, 0F1h, 0B8h, 50h, 0, 8Eh, 0C0h, 0Eh, 1Fh, 2Eh; 239
  db 0A1h, 3, 7Ch, 0E8h, 43h, 0, 0BBh, 0, 0, 0B8h, 1, 2; 249
  db 0E8h, 58h, 0, 72h, 2Ch, 33h, 0FFh, 0B9h, 0Bh, 0, 26h; 261
  db 80h, 0Dh, 20h, 26h, 80h, 4Dh, 20h, 20h, 47h, 0E2h, 0F4h; 272
  db 33h, 0FFh, 0BEh, 0DFh, 7Dh, 0B9h, 0Bh, 0, 0FCh, 0F3h; 283
  db 0A6h, 75h, 0Eh, 0BFh, 20h, 0, 0BEh, 0EBh, 7Dh, 0B9h; 293
  db 0Bh, 0, 0F3h, 0A6h, 75h, 1, 0C3h, 0BEh, 80h, 7Dh, 0E8h; 303
  db 0A6h, 0FFh, 0B4h, 0, 0CDh, 16h, 0F9h, 0C3h, 1Eh, 0Eh; 314
  db 1Fh, 33h, 0D2h, 0F7h, 36h, 18h, 7Ch, 0FEh, 0C2h, 88h; 324
  db 16h, 15h, 7Ch, 33h, 0D2h, 0F7h, 36h, 1Ah, 7Ch, 88h; 334
  db 16h, 1Fh, 7Ch, 0A3h, 8, 7Ch, 1Fh, 0C3h, 2Eh, 8Bh, 16h; 344
  db 8, 7Ch, 0B1h, 6, 0D2h, 0E6h, 2Eh, 0Ah, 36h, 15h, 7Ch; 355
  db 8Bh, 0CAh, 86h, 0E9h, 2Eh, 8Bh, 16h, 1Eh, 7Ch, 0CDh; 366
  db 13h, 0C3h, 0, 0, 0Dh, 0Ah, 4Eh, 6Fh, 6Eh, 2Dh, 53h; 376
  db 79h, 73h, 74h, 65h, 6Dh, 20h, 64h, 69h, 73h, 6Bh, 20h; 387
  db 6Fh, 72h, 20h, 64h, 69h, 73h, 6Bh, 20h, 65h, 72h, 72h; 398
  db 6Fh, 72h, 0Dh, 0Ah, 52h, 65h, 70h, 6Ch, 61h, 63h, 65h; 409
  db 20h, 61h, 6Eh, 64h, 20h, 73h, 74h, 72h, 69h, 6Bh, 65h; 420
  db 20h, 61h, 6Eh, 79h, 20h, 6Bh, 65h, 79h, 20h, 77h, 68h; 431
  db 65h, 6Eh, 20h, 72h, 65h, 61h, 64h, 79h, 0Dh, 0Ah, 0; 442
  db 0Dh, 0Ah, 44h, 69h, 73h, 6Bh, 20h, 42h, 6Fh, 6Fh, 74h; 453
  db 20h, 66h, 61h, 69h, 6Ch, 75h, 72h, 65h, 0Dh, 0Ah, 0; 464
  db 69h, 62h, 6Dh, 62h, 69h, 6Fh, 20h, 20h, 63h, 6Fh, 6Dh; 475
  db 30h, 69h, 62h, 6Dh, 64h, 6Fh, 73h, 20h, 20h, 63h, 6Fh; 486
  db 6Dh, 30h, 0, 0, 0, 0, 0, 0, 0, 55h, 0AAh; 497
  
aIncorrectDosVe db 'Incorrect DOS version',0Dh,0Ah,'$' 
aInvalidDriveSp db 'Invalid drive specification$' 
aInvalidParamet db 'Invalid parameter$' 
;
; String contains disk letter, adjusted in code,
; so consists of 3 variables.
aInsertSystemDi db 'Insert system disk in drive ' 
drive_letter db 41h   
aAndStrikeAnyKe db 0Dh,0Ah
  db 'and strike any key when ready',0Dh,0Ah,'$'
;
aNoRoomForSyste db 'No room for system on destination disk$'
     
; Unused string
aIncompatibleSy db 'Incompatible system size$' 
aSystemTransfer db 'System transferred$' 
IBMDOS_DTA db    6   
; Trash ?
  db 0C4h ; Д
  db  2Dh ; -
  db 0EBh ; л
  db 0D5h ; Х
  db  3Ch ; <
  db  0Eh
  db  75h ; u
  db  0Dh
  db  83h ; ѓ
  db  3Eh ; >
  db 0C1h ; Б
  db  2Dh ; -
  db    0
  db  75h ; u
  db 0DBh ; Ы
  db 0FEh ; ю
  db    6
  db 0C2h ; В
  db  2Dh ; -
  db 0EBh ; л
  db 0C4h ; Д
  db  3Ch ; <
  db  0Fh
  db  75h ; u
  db 0D1h ; С

Розмір --- 1 408 байт, сильно розрослася. Хоча, 512 з них --- новий бут-сектор, і в коді трохи сміття, тому реальне зростання не таке велике. Але є.

Скачати код, разом із лістингом, згенерованим IDA, скомпільованим файлом та, для порівняння, оригінальним SYS.COM, можна тут. Скомпільований код, з точністю до опкодів-синонімів, тотожній оригінальному.

До речі, придумав швидкий спосіб перевіряти відмінні опкоди на "синонімічність". Шукати по таблицях довго, незручно, вразливо до помилок. Однак, якщо дизасемблювати новий файл, (за допомогою IDA), можна звіряти команди за адресами, де байти оригінального та скомпільованого файлів відмінні. Сильно допомагає в цьому вкладка HexView-A.


Алгоритм роботи


Нова версія, перш за все, зберігає AX (нагадуємо, AL містить 00, якщо в першому FCB з PSP коректний диск), перевіряє версію DOS. Якщо версія менша за 1.54 (1.36h) або більша за 2.00, вийти, повідомивши "Incorrect DOS version". Інакше продовжити. DOS 1.XX охоплюється цією перевіркою, так як виклик "AH=31h/INT 21h", "GET DOS VERSION" в ньому не існує, а для таких викликів DOS поверне 0. Між початком програми та перевіркою версії DOS є два загадкових байти, які ніби ніде не використовуються...

UPDATE 06-2013: Подібні байти є і в пізніших версіях SYS.COM, але лише аналізуючи FORMAT.COM (PC-DOS 1.00), яка теж містить ці загадкові байти, виявив що це -- адреса копії бут-сектора в пам'яті (не в файлі -- там вона менша на 100h). Принаймні, для всіх перевірених SYS.COM, дане правило виконується, після стрибка до точки входу вони містять адресу boot-сектора, яка в коді ніде не використовується.

Версія 1.54 в перевірці показує, що існували внутрішні версії, які були ще 1.XX, але вже підтримували нові системні виклики.

Якщо версія прийнятна -- відновлює AX, перевіряє, чи диск в першому FCB коректний, чи крім диску в першому аргументі нічого немає, і, нарешті, чи не пробуємо ми робити SYS на диск, з якого запустилися, точно так, як і в DOS 1.10. Якщо ні, зберігаємо код поточного диску в currentDrive.

Далі перетворюємо код диску в його літеру (додавши 40h --- див. коментар в коді) і зберігаємо її в дві стрічки, одну довгу (літера диску+":/"+64 порожніх байти), одну коротку (літера диску+":/"+\0 -- символ кінця С-стрічки). Після цього зберігаємо поточний шлях (отриманий викликом AH=47h/INT 21h, "GET CURRENT DIRECTORY") у нулі великої стрічки, яка, таким чином, міститиме повний поточний шлях на момент запуску програми. Потім --- перейти за шляхом, вказаним у короткій стрічці, тобто --- в корінь поточного диску.

Наступні кроки абсолютно такі ж, як і для DOS 1.10 --- відкрити IBMBIO.COM i IBMDOS.COM, використовуючи FCB, задані в коді, якщо їх немає --- попросити вставити системний диск. Мітка AskToInsertSysDisk знаходиться перед перевіркою коректності диску в FCB-1, ефективно запускаючи всі дії програми, окрім перевірки версії, заново. Далі, абсолютно так само, як і раніше, читаються обидва файли, (єдине, що читати максимум 4000h і 5000h відповідно, проти 8000h і 2000h в попередній версії). Якщо успішно, як і раніше, переставляємо в FCB диск на цільовий. Код запиту системного диску не змінився. Код читання файлу теж --- він так само зберігає дату, час і розмір файлу після його FCB.

Далі програма змінює поточний каталог цільового диску на кореневий (використовуючи згадану вище коротку стрічку, але змінивши літеру диску).

Подальший код дещо змінився. DTA переставляється на область для DTA по замовчуванню в PSP, шукається, чи є хоч якісь файли на диску (маска -- "*.*"). Пошук здійснюється за допомогою ще одного FCB. Якщо немає -- перейти до запису системних файлів. Якщо якісь файли є --- спробувати відкрити IBMBIO.COM (використовуючи його FCB, із зміненим раніше цільовим диском). Якщо такого не знайдено, повідомити "No room for system on destination disk" і вийти, інакше перевірити, чи є IBMDOS.COM, за його відсутності --- дати це ж повідомлення і завершити програму. В DOS 1.10 аналогічні дії робилися хитріше --- звірялися розміри файлів... Якщо обидва файли знайдено, IBMDOS.COM видаляється, (тільки він) і провалюємося до запису. Функції запису передаються адреси DTA (в DX) і FCB файлу (в BX) --- вона більш цілісна (ІМХО :), ніж у попередній версії, сама переставляє DTA. Крім того, вона перевіряє, чи записано стільки ж байт, скільки було прочитано, якщо менше --- повідомляє те ж "No room for system on destination disk".

На цьому попередня версія закінчувалася, повідомивши "System transferred". Однак наша робить ще дещо. Після виводу повідомлення, відновлює вихідну поточну директорію поточного диску, потім викликає ще одну функцію, яку я умовно назвав WriteBoot.

Ця функція дивиться на Media ID з FAT цільового диску, якщо він відповідає 8-доріжковим дискетам, перезаписує у них boot! Якщо дискета одностороння, береться образ boot-сектора, збережений в коді, якщо двостороння, його BPB спершу коректується. УВАГА! boot-сектор перезаписується, навіть якщо ситуація "No room for system on destination disk" чи "Incompatible system size". Я так розумію, це спроба тихенько зробити старі, до-BPB, дискети сумісними.

Код boot-сектора я дизасемблювати не став, просто лінь, але з тим проблем не мало б бути. Та й, здається, десь в мережі його дизасемблювання траплялося. Однак, якщо цікаво --- пишіть, зробимо! В кінці трохи загадкового коду, який не використовується ніяк, напевне сміття якесь... Також є код виводу ""Incompatible system size", який теж ніде не використовується.

"Документація"

На відміну від попередньої версії, не порівнює розміри системних файлів на вихідному цільовому дисках. Але більш важливі відмінності наступні:
  • Визначається поточна директорія диску, з якого запускали програму, зберігається, відновлюється перед завершенням. 
  • На 8-секторних цільових дискетах записується новий boot-сектор, навіть якщо систему перенести не вдалося.


Про код

Код став ще трішки чіткішим (ІМХО, знову ж таки), а так --- все той же. Далі нас чекає SYS.COM з DOS 2.10.

А поки:

 Дякую за увагу!

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

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