неділю, 25 вересня 2022 р.

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

Наступна версія DOS, 3.10, вийшла 2 квітня 1985. Однак, згідно інформації музею OS/2, анонсована вона була разом із 3.00, і, фактично, являла собою обіцянку доробити те, що до випуску 3.0, який мав співпасти із виходом IBM PC/AT, не встигли.  Зокрема, нарешті довели до ладу мережевий редіркетор. По великому рахунку, DOS API з часів 3.1 змінювалося мало. Із важливих для SYS.COM аспектів --- з'явилася підтримка 720Кб 3.5'' дисків. 
 
Ех, я починав теж із 720Кб, правда нестандартних -- 5-дюймових, дисковід Пошука-1 принципово не вмів 1.2Мб читати-писати, а на 800 Кб заїкався, хоча по сектору за раз, руками з DEBUG.COM  --- читав. Штатним форматом був 360Кб, але в 1995 то було зовсім мало, а 720Кб, при всій нестандартності для 5-дюймовок, особливих проблем не створював. На крайній випадок, 800.COM вже був популярним.
 
Ремарка: цей текст був написаний майже 10 років тому, довести до кінця тоді не вистачило натхнення, а зараз вже шкода часу. Тому публікую як є, із мінімальними правками, менш завершеним, ніж інші пости цієї серії.


SYS.COM з 3.10 дуже схожий на той, що в 3.00. Теж "Converted"-EXE, з тим самим (побайтово) декодером, із тим самим printf (теж побайтово), тому можна перейти безпосередньо до коду.

Код нижче --- умовно рекомпілювабельний, із тими ж обмовками, що і для попередньої версії --- EXE-файл дещо відмінний, і я не перевіряв, чи всі ті відмінності є синонімічними, тобто наслідком використання не тих еквівалентних інструкцій чи просто зміни положення сегментів у файлі через емуляцію розміру заголовка. 

Завантажити код, разом із скомпільованим файлом та, для порівняння, оригінальним SYS.COM і "SYS.EXE", можна тут. Аналіз -- після коду.

; ===================================================================================
; 	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   :	38C64BCE77519747A2B419C43200DACF


; Modified to compile by fasm and commented by Indrekis, indrekis2.blogspot.com

; File Name   :	PC-DOS_3.10\"Decoded" SYS.COM
; Format      :	EXE

		include		'partialBPBrecord.inc'
		;.8086
		format MZ

		entry 		CodeSeg:CodeSegStart
		;stack 		80h	
		stack 		StackSeg:StackSegEnd
		; heap 		0 	; To obtain this value in MZ header
;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
		db 0, 0, 0, 0		; 16
EndOfPrintfOutBuffer db	   0		
		db  15h
		db    0
; Printf --- To	the last one byte same as from 3.10, so	do not comment here


; === 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 ; Here is a coincidence, which confuses IDA
					; Jump displ. == PaddingSize offset
		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 ; padding symbols
		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
                                        ; 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
		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 (orignal) 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

; ===================================================================================
; CodeSeg is PSP + 37h,	in example PSP seg = 9AEh (Test	run in DosBox :)
; So to	find CodeSeg --	use (9AE+37h)*10h = 9E50h offset from the start
;
; Seg: 9E50h
; Size -- until StackSeg, PSP seg+10h+80h
; Base,	paragraphs, 9E5h
; 16 bit
; ===================================================================================
                        ; segment byte public ''
                        ; Segment type: Regular
        		; assume cs:CodeSeg
        		; assume es:nothing, ss:nothing, ds:nothing
segment 	CodeSeg 
CodeSegStart:				
		jmp	short EntryPoint2
; ===================================================================================
		dw  NewBootBuffer	; NewBoot addr
; Version string had not changed -- same as for the 3.00!
aSys1_86	db 'SYS 1.86'
; ===================================================================================

EntryPoint2:				; 
		push	ax		; Save AX --- disk status
; Then check for DOS version. Exit if older than 3.10 or younger than 3.10
; Strange way to check equality, though...
; 3.00 Checked only, if	not older
		mov	ah, 30h
		int	21h		; DOS -	GET DOS	VERSION
					; Return: AL = major version number (00h for DOS 1.x)
		xchg	ah, al
		cmp	ax, 30Ah
		jb	short WrongDOSVersion
		cmp	ax, 30Ah
		ja	short WrongDOSVersion
		pop	ax		; Restore AX
;
; Copy initial FCB from	PSP to dedicated area

		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
		; 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, aIncorrectDosVersion	; ofs "Incorrect DOS version\r\n$"
		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
; ===================================================================================

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
; Function call	is new compared	to 3.10	-- was inline code
		call	CheckIfRemovable ; Check if disk is fixed or remote, sets CF, otherwise	clears CF
					; BL --	disk number (in	DOS, not FCB, scheme)
					; Sets CF, if fixed or remote and cannot be changed
					;
		jnb	short AskToInsertSysDisk
		mov	dx, NoSystemOnDefDrv_Adr
		push	dx
		call	PrintfSeg:Printf_sub ; Address of the pointer to the string --- in stack.
					; IF there are other data, pointers to them are	after the pointer to string.
		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 ; Address of the pointer to the string --- in stack.
					; IF there are other data, pointers to them are	after the pointer to string.
		call	WaitForAnyKey	; Clears buffer	before and after.
		xor	al, al		; Restore AL --	00 means correct first FCB (we have
					; already checked).
; This 4 moves are new to 3.10
; 3.00 rely on values in prog. image, so, may be second	attempt	would be
; problematic?

Continue1:				; 
		mov	byte [IBMDOS_fount_flag], 0
		mov	byte [IBMBIO_fount_flag], 0
		mov	byte [IBMDOS_position_correct], 1
		mov	byte [IBMBIO_position_correct], 1
;
		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
		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.
		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
; 3.00 checks only 12-th bit --	"is remote"
		test	dx, 1200h	; Check	if remote (bit 12), or
					; "Direct I/O not allowed" (bit	9)
					; Last bit check - new to 3.10
		jz	short ioctl_err_or_driver_is_not_remote
		mov	dx, CantSysToNetDrv_Adr
		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
; Disk reset --	new in 3.10
		mov	ah, 0Dh
		int	21h		; DOS -	DISK RESET
		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/3.10 -- too.
		jnb	short AdditionalChecksForKnownDisk ; 
		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_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_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_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 and 3.00
		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 and 3.00
		jb	short AskToInsertSysDisk_
		mov	cx, sp
		sub	cx, 0B94h	; 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 and 3.00
		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
					; 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 and 3.00
		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 and 3.00
		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. In 3.10 added 720Kb 3.25'' floppy support.
		mov	dx, SystemTransferred_Adr	; ofs
		push	dx
		call	PrintfSeg:Printf_sub ; Address of the pointer to the string --- in stack.
					; IF there are other data, pointers to them are	after the pointer to string.
		xor	al, al
		jmp	short exit_program
; ===================================================================================

call_printf_n_exit:			;
					;
		push	dx		; DX contains address of message
		call	PrintfSeg:Printf_sub ; Address of the pointer to the string --- in stack.
					; IF there are other data, pointers to them are	after the pointer to string.
		mov	dx, CR_LF_Adr	; ofs
		push	dx
		call	PrintfSeg:Printf_sub ; Address of the pointer to the string --- in stack.
					; IF there are other data, pointers to them are	after the pointer to string.
		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

locret_A0F9:				; 
		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 and 3.00

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 locret_A0F9 ; 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.

locret_A162:				; 
		retn
; ReadFilesToMem	endp ; sp =  2


; === 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 and 3.00

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 locret_A162
		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

locret_A18E:				; 
					; WriteSysFilesFromMem+23j ...
		retn
; GetFileSizeDateTime endp

; ===================================================================================

printNoRoomForSystem:			; 
					; 
		mov	dx, NoRoomForSystem_Adr
		jmp	call_printf_n_exit

; === SUBROUTINE ====================================================

; Same as from 2.10 and	3.00

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 locret_A18E
		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 locret_A18E
		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 locret_A18E
		cmp	ax, cx		; Written less than should?
		jz	short locret_A18E

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. In 3.10 added 720Kb 3.25'' floppy support.

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
		pop	ds		; Restore DS
		; assume ds:CodeSeg
		sub	al, 0F8h ; 'ш'  ; AX = MediaID-0F8h,
					; According to http://en.wikipedia.org/wiki/File_Allocation_Table
					; DOS 3.00 knows floppy	codes up to F9h,
					; DOS 3.10 should know codes up	to F8h.
		cbw
		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_A224
		add	[ds:lds_offset], 14h ; Size of DOS 2.0 BPB+1
					; Kind of second HDD detection
					; and taking into account?
		inc	byte [ds:PossiblyPhysicalDiskNumber]
;
; Copy disk BPB	from some internall structure

loc_A224:				;
		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
; New in 3.10 --- account for two possible 0F9h	MediaID:
;  ==> 3.5-inch	Double sided, 80 tracks	per side, 9 sectors per	track (720 KiB)	(since DOS 3.2)[9]
;  3.5-inch Double sided, 80 tracks per	side, 18 sectors per track (1440 KiB) (DOS 3.2 only)[9]
;  ==> 5.25-inch Double	sided, 80 tracks per side, 15 sectors per track	(1200 KiB, known as "1.2 MB") (since DOS 3.0)[9]
;  ( http://en.wikipedia.org/wiki/File_Allocation_Table#BPB )
;  Wiki	is wrong, looks	like 3.10 knows	about 720Kb 3.5	fdd

		cmp	si, 0FFFEh
		jnz	short putBPBinBootImage
		mov	ah, 36h	; '6'
		mov	dl, byte [FCBs_Copy] ; Driver code
		int	21h		; DOS -	2+ - GET DISK SPACE
					; DL = drive code (0 = default,	1 = A, 2 = B, etc.)
					; Return:
					; AX = FFFFh if	invalid	drive
					; else
					; AX = sectors per cluster
					; BX = number of free clusters
					; CX = bytes per sector
					; DX = total clusters on drive
		mov	bx, dx		; BX --- total clusters	on drive
		mul	bx		; BX*AX	-> DX:AX
					; So DX:AX - sectors number
		sub	ax, 0		; ?
		cmp	ax, ID_F9_720Kb_BPB_bytes2_13.logical_sectors ;	sectors_in_720Kb_fdd --
					; field	from ID_F9_720Kb_BPB_bytes2_13
		mov	si, ID_F9_720Kb_BPB_bytes2_13
		jb	short putBPBinBootImage
		mov	si, ID_F9_1200Kb_BPB_bytes2_13
; 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

putBPBinBootImage:			; 
		push	ds
		pop	es
		mov	di, SectorsPerCluster ; Logical sectors per cluster.
		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 ; sp =  2


; === 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 ; Address of the pointer to the string --- in stack.
					; IF there are other data, pointers to them are	after the pointer to string.
		retn
; WaitForAnyKey	endp ; sp = -2

; ===================================================================================

; Check	if disk	is fixed or remote, sets CF, otherwise clears CF
; BL --	disk number (in	DOS, not FCB, scheme)
; Sets CF, if fixed or remote and cannot be changed
;

CheckIfRemovable: ; proc near		
		push	ax		; Save AX
		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)
		jnb	short not_error_4408_IOCTL
		mov	ax, 4409h	; 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+)
		int	21h		; DOS -	2+ - IOCTL -
		jb	short DiskFixedOrRemote
		test	dx, 1000h	; 12-th	bit set	in mask	--- remote driver
		jnz	short DiskFixedOrRemote
		jmp	short driver_is_not_remote ; Clear CF --- disk is removable
;===================================================================================
		nop

not_error_4408_IOCTL:			; 
		test	ax, 1		; Check	if fixed
		jnz	short DiskFixedOrRemote

driver_is_not_remote:			; 
		clc			; Clear	CF --- disk is removable
		pop	ax		; Restore AX
		retn
;===================================================================================

DiskFixedOrRemote:			; 
		stc
		pop	ax
		retn
; CheckIfRemovable endp

;===================================================================================
CantSysToNetDrv_Str db 'Cannot SYS to a Network drive',0
CantSysToNetDrv_Adr dw CantSysToNetDrv_Str		; 
aIncorrectDosVersion 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
; CodeSeg		ends

; Displ: 80h, StackSeg = PSP seg + 10 +	80h, size = 80h
; ===================================================================================

segment StackSeg		


StackSegBegin:
; Segment type:	Regular
; StackSeg	segment	byte public ''
		;assume cs:StackSeg
		;assume es:nothing, ss:nothing, ds:nothing
        	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, 8Ch, 0Fh, 0AEh, 9, 6, 3; 112
StackSegEnd:
; StackSeg	ends

; This CodeSeg should have the same base as CodeSeg above!
; ===================================================================================

; Segment type:	Regular
; CodeSeg		segment	byte public ''
		; assume cs:CodeSeg
		; org 610h
		; assume es:nothing, ss:nothing, ds:nothing
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_correct	db 1		; 
					;
IBMBIO_position_correct	db 1		;
					;
; IBMBIO.COM data
IBMBIO_hndlr	dw 0			; 
					; 
IBMBIO_size_lo_1 dw 0			; 
					; 
		dw 0
IBMBIO_size_hi_1 dw 0			; 
					; 
		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			; 
					; 
		dw 0
IBMDOS_size_hi_1 dw 0			;
					; 
		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
		db 0, 0, 0, 0, 0, 0, 0,	0, 0, 0, 0, 0, 0, 0, 0,	0; 16
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_1		db 'IBM  3.1'
; 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,	3.1 entry is incompatible with a similar
					; entry	at offset 0x01C	in BPBs	since DOS 3.31.
; End of DOS 3.00-3.10 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 0FFFEh		; 0F9h - (change from 3.00)
					; Wiki says: http://en.wikipedia.org/wiki/File_Allocation_Table#BPB
					;
					; 3.5-inch Double sided, 80 tracks per side, 9 sectors per track (720 KiB) (since DOS 3.2)
					; 3.5-inch Double sided, 80 tracks per side, 18	sectors	per track (1440	KiB) (DOS 3.2 only)
					; 5.25-inch Double sided, 80 tracks per	side, 15 sectors per track (1200 KiB, known as "1.2 MB") (since	DOS 3.0)
					;
					; According to code above, 3.10	already
					; knows	3.5 for	720Kb case, but	not 1440Kb
		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_1200Kb_BPB_bytes2_13 partialBPB_record 1, 1, 2, 0E0h, 960h, 0F9h, 7, 0Fh, 2, 0
ID_F9_720Kb_BPB_bytes2_13 partialBPB_record 2,	1, 2, 70h, 5A0h, 0F9h, 3, 9, 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		2 dup(0)


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

Перевірка версії на початку строгіша, ніж в 3.00 -- версія має бути рівно 3.10 (AX=030Ah). Однак, при тому, стрічка внутрішньої версії, "SYS 1.86" не змінилася. Після перевірки версії все продовжується, як в 3.00, із декількома дрібними відмінностями: в результатах "IOCTL - CHECK IF BLOCK DEVICE REMOTE" дивляться не тільки на біт віддаленого пристрою, але й на "Direct I/O not allowed" --- не робиться SYS на диски, в яких хоч один із тих бітів встановлено. Після виклику IOCTL Disk reset.

Перевірка, чи вихідний диск змінний, якщо на ньому не було необхідних файлів, винесено в окрему процедуру, CheckIfRemovable. Ця процедура, крім перевірки за допомогою AX=4408h/IN 21h, "IOCTL - CHECK IF BLOCK DEVICE REMOVABLE", перевіряє, чи диск є віддаленим. Однак, якщо перший IOCTL (AX=4408h) повернув помилку, але наступний IOCTL "IOCTL - CHECK IF BLOCK DEVICE REMOTE" (AX=4409h) завершився успішно, і диск не є віддаленим, вважає його змінним. (?! Це воно на що його перевіряє?)

Далі все відбувається, як і в 3.00. Процедури GetFileSizeDateTime, ReadFilesToMem, WriteSysFilesFromMem, не змінилися.

Дещо відрізняється WriteBoot. Якщо в таблиці DiskBPBsPtrsTable є код 0FFFEh, обробка трохи інша. Насправді, такий код поставлено у відповідність Media ID = 0F9h. Він може відповідати трьом різним форматам, із яких DOS 3.10 знає два, 1200Кб/5.25'' та 720Кб/3.5''. Для цього код, за допомогою AH=36h/INT 21h (GET DISK SPACE), визначає розмір дискети, і, якщо він рівний 720Кб, бере відповідну табличку BPB, інакше автоматом вибирає 1200Кб. 
 
В коді зникло подвійне збереження розміру файлів, яке дивувало в попередніх версіях, але в пам'яті місце під них залишилося.

На цьому відмінності від попередньої версії закінчуються.


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

В порівнянні із попередньою версією більш строгою стала перевірка версії DOS -- запуск дозволяється тільки, якщо версія 3.10. Змінність диску перевіряється трішки ґрунтовніше, але мені не до кінця зрозуміла ціль цієї перевірки...

Додано підтримку 720Кб 3.5'' дискет, хоча і не до кінця послідовну, всі дискети із Media ID = 0F9h вважаються або 720Кб, або 1200Кб, хоча в майбутньому з'явиться ще третій варіант, 1440Кб/3.5'', на якому ця версія SYS.COM напартачить. Якраз ілюстрація, чому контроль версій утиліт був строгим.

Що цікаво, офіційна документація, DOS_3.10_Reference_Feb85, стверджує (7-178):
Не зауважив, як би код міг враховувати переданий йому шлях... Шляхи вихідних файлів, так виглядає, прописані жорстко. Є пара загадкових фрагментів коду, які працюють з внутрішніми даними DOS, але і припущення, що вони за це відповідають, звучить неправдоподібно --- див. код вище. Гіпотеза -- хтось із OEM міг цим користувати. Або прибріхують, або я чогось недозрозумів. :-)

Це, мабуть, і все.

Про код

Особливо додати тут немає чого --- код дуже схожий на той, що в 3.00. Ну, ще один шматок виділили в окрему процедуру, можливо --- в наслідок ускладнення його функцій.

На разі ---
Дякую за увагу!

1 коментар: