;	SCCSID = @(#)sys.asm	1.12 85/10/06
TITLE	MS-DOS SYS Program
; SYS - Copies system programs IBMBIO.COM/IO.SYS and IBMDOS.COM/MSDOS.SYS
;   1.6     05/21/82	Added rev number message
;   1.61    06/04/82	Allow SYS to blank disk TimP at SCP
;   1.70    06/30/82	NON contiguous DOS allowed on 2.00 IBM.  Allows SYS to
;			1.0 1.1 disks.
;   1.71    07/02/82	Put in CHDIRs to make sure everything done in root dir.
;   1.80    04/26/83	MZ make sys work in small machines; use full 2.0
;			system calls
;   1.81    07/22/83	ARR Added check in IBM version for valid FAT ID on
;			destination because of IBM problem with SYSing to
;			unformatted disks which are really formatted.  Prints
;			NoDest message for ridic IBM reasons, should have a
;			better message.
;   1.82    08/12/83	ARR ZIBOed again.  Mark fails to check for errors on
;			his file I/O.  Results in SYS saying system
;			transferred when it hasn't been.
;   1.83    09/08/83	EKE Added code to quit if sys called on a drive across
;			a net.
;   1.84    09/09/83	CHRISP grabbed against his will to make this stupid
;			program write out a boot sector
;   1.85    10/18/83	NP Printf to print messages and it's now an .EXE file
;   1.86    11/8/83	MZ fix hard file output of boot sector
;   1.87    5/1/84	MZ make sys prompt for system disk in default drive.
;   3.20    11/9/84	RS make sys write out a correct boot sector for the
;			version of DOS. It grabs the boot sector off the system
;			disk and inserts the correct BPB.
;			Uses IOCTL Get Device Parms to get BPB for a Hard drive.
;   3.20    08/5/85	RS Allow FAT ID byte of 0F0H for 'strange' media
;			layouts. These come about because of /T /N switches in
;			FORMAT.
;   3.20    09/16/85	Incorporate tables of filenames to allow system files
;			used by other OEMs to be SYSed onto disks formatted on
;			other MSDOS systems. Allows the flexibility to replace
;			system files for one OEM by system files for another.
;
;
;   3.30    06/04/86	MT removes initial check for IBMBIO and DOS - not needed
;			because of later search of dirs
;   3.30    06/16/86	MT only force part of IBMBIO contig - do this by assuming
;			contig part smaller than 1.10 BIOS
;   3.30    06/16/86	MT Check diks space for enough room to install BIO and DOS
.xlist
.xcref
	include version.inc
	INCLUDE DOSSYM.INC   ;also defines version of dos
	include ioctl.INC
.cref
.list

;IBMJAPVER    EQU	FALSE		; this and the following are mutually
;IBMVER	      EQU	TRUE		; exclusive
;IBMCOPYRIGHT EQU 	FALSE

printf_code segment public
	extrn	printf:far
printf_code ends

CODE	SEGMENT PARA PUBLIC
CODE	ENDS

CONST	SEGMENT BYTE PUBLIC
	EXTRN	BIOSNamesTable:byte, DOSNamesTable:byte
	EXTRN	SourceBIOSFile:byte, SourceDOSFile:byte
CONST	ENDS

cstack	segment stack
	db  278 + 80h dup (?)		; 278 == IBM's ROM requirements
cstack	ends

DATA	SEGMENT BYTE PUBLIC

DG	GROUP	CODE,DATA,CONST,cstack

	EXTRN	BADDRV_ptr:byte, BADPARM_ptr:byte, GETSYS_ptr:byte
	EXTRN	SYSDRV:byte, NODEST_ptr:byte, BADSIZ_ptr:byte, DONE_ptr:byte
	EXTRN	BADVER:byte, Crlf_ptr:byte, NoSYS_PTR:byte, GetTarg_ptr:byte
	EXTRN	TargDrv:byte

	EXTRN	NotNetM_ptr:byte


DEFALT	DB	0

	IF IBMCOPYRIGHT
BIOSName    DB	"A:\IBMBIO.COM",0
DOSName     DB	"A:\IBMDOS.COM",0

SourceBIOSName	  DB  "A:\IBMBIO.COM",0
SourceDOSName	  DB  "A:\IBMDOS.COM",0
	ELSE
BIOSName    DB	"A:\IO.SYS",0
DOSName     DB	"A:\MSDOS.SYS",0

SourceBIOSName	  DB  "A:\IO.SYS",0
SourceDOSName	  DB  "A:\MSDOS.SYS",0
	ENDIF

IBMBIO_LOW  DW	0			;length of IBMBIO on disk
IBMBIO_HIGH DW	0
IBMDOS_LOW  DW	0			;length of old IBMDOS on disk
IBMDOS_HIGH DW	0

SIZE_OLD_HIGH DW  0
SIZE_OLD_LOW  DW  0

NEWBIO_SIZE_LOW DW 0
NEWBIO_SIZE_HIGH DW 0
NEWDOS_SIZE_LOW DW 0
NEWDOS_SIZE_HIGH DW 0


Need_Clusters	dw	0
Bytes_Per_Cluster dw	0
Number_Free_Clusters dw 0


;*** WARNING ***
; KEEP THE FOLLOWING ITEMS IN THE EXACT ORDER BELOW!!!
DOSEntFree  DB	1
BIOSEntFree DB	1

BIOSInFH    DW	?			; file handle of source BIOS
BIOSLenLow  DW	2 DUP (?)		; 32-bit length of BIOS
BIOSLenHigh DW	2 DUP (?)		; 32-bit length of BIOS
BIOSTime    DW	2 DUP (?)		; place to store time of BIOS write
BIOSOutFH   DW	?			; fh of BIOS destination

DOSInFH     DW	?			; file handle of source DOS
DOSLenLow   DW	2 DUP (?)		; 32-bit length of DOS
DOSLenHigh  DW	2 DUP (?)		; 32-bit length of DOS
DOSTime     DW	2 DUP (?)		; place to store time of DOS write
DOSOutFH    DW	?			; fh of DOS destination


AllName     DB "A:\*.*",0

;******************************************
;FCB SNOT

fcb_copy	db	32 dup(?)

	IF IBMCOPYRIGHT
FCBDOS	DB  "IBMDOS  COM"
FCBBIO	DB  "IBMBIO  COM"
	ELSE
FCBDOS	DB  "MSDOS   SYS"
FCBBIO	DB  "IO      SYS"
	ENDIF

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

DOSFile_Offset	dw	0		; Index for system file names in Tables

DirEnt	DB	80h DUP (?)
cbBuf	    DW	?			; number of bytes in buffer
pDOS	    DW	?			; offset of beginning of DOS in buffer
pDOSEnd     DW	?			; offset of end of DOS in buffer

BIOSEG	EQU	70H			; STARTING LOCATION OF BIOS
BPBLEN	EQU	19

BPB	LABEL	DWORD			; POINTER TO HARDFILE BPB
	DW	0000
	DW	BIOSEG

public boot
BOOT	DB	0,0,0			 ; SKIP THE THREE BYTE JUMP
	DB	"MSDOS3.3"
SECSIZ	DW	512			; SECTOR SIZE
SECALL	DB	1			; SECTOS PER ALLOCATION UNIT
RESNUM	DW	1			; RESERVED SECTORS
FATNUM	DB	2			; NUMBER OF FATS
DIRNUM	DW	64			; NUMBER OF DIRECTORY ENTRIES
SECNUM	DW	1*8*40			; NUMBER OF SECTORS
FATID	DB	0FEH
FATSIZE DW	1
SECLIM	DW	8
HDLIM	DW	1
HIDDEN	DW	0
PHYDRV	DB	0
	DB	510-($-boot) dup (?)
	DB	55H,0AAH		 ; MAGIC WORD
REORG	LABEL	BYTE
	ORG	BOOT
	INCLUDE ..\BOOT\BOOT.INC
	ORG	REORG
FATID_TABLE	LABEL	WORD
	DW	0			; F8	FIXED DISK
	DW	-2			; F9
	DW	-1			; FA
	DW	-1			; FB
	DW	OFFSET DG:BPB91 	; FC
	DW	OFFSET DG:BPB92 	; FD
	DW	OFFSET DG:BPB81 	; FE
	DW	OFFSET DG:BPB82 	; FF

; BIOS PARAMETER BLOCKS FOR THE VARIOUS MEDIA

BPB81	DB	1
	DW	1
	DB	2
	DW	64
	DW	1*8*40
	DB	0FEH
	DW	1
	DW	8
	DW	1
	DW	0
	DB	0

BPB82	DB	2
	DW	1
	DB	2
	DW	112
	DW	2*8*40
	DB	0FFH
	DW	1
	DW	8
	DW	2
	DW	0
	DB	0

BPB91	DB	1
	DW	1
	DB	2
	DW	64
	DW	1*9*40
	DB	0FCH
	DW	2
	DW	9
	DW	1
	DW	0
	DB	0

BPB92	DB	2
	DW	1
	DB	2
	DW	112
	DW	2*9*40
	DB	0FDH
	DW	2
	DW	9
	DW	2
	DW	0
	DB	0

BPB96T	DB	1			; mjb001
	DW	1
	DB	2
	DW	224
	DW	2*15*80
	DB	0f9H
	DW	7
	DW	15
	DW	2
	DW	0
	DB	0
BPBSIZ	=	$-BPB96T

BPB325	DB	2			; mjb001
	DW	1
	DB	2
	DW	70h
cSec325 DW	2*9*80
	DB	0f9H
	DW	3
	DW	9
	DW	2
	DW	0
	DB	0



;
; Following structure used by Generic IOCTL call Get Device Parameters to get
; the BPB of a hard disk. It 'overflows' into area of BUF.
;
DeviceParameters	a_DeviceParameters	<0,DEV_HARDDISK>

BUF	LABEL	BYTE			; beginning of area for file reads

DATA	ENDS

CODE	SEGMENT PUBLIC PARA

	ASSUME	CS:DG,DS:NOTHING,ES:NOTHING,SS:CSTACK

start:
	JMP	SHORT CheckVersion

	DW	OFFSET DG:BOOT

HEADER	DB	"SYS 3.30"

CheckVersion:
	PUSH	AX			; save drive letter validity
	MOV	AH,GET_VERSION
	INT	21H			; get dos version

    cmp ax,expected_version
    jne gotbaddos

;	XCHG	AH,AL			; Turn it around to AH.AL
;	CMP	AX,DOSVER_LOW		; is it too low?
;	JNE	GOTBADDOS		; yes, error

	pop	ax
ASSUME	DS:NOTHING,ES:NOTHING
	push	cs
	pop	es
ASSUME	ES:DG
	mov	si,5ch
	mov	di,offset dg:fcb_copy
	mov	cx,32
	rep	movsb
	push	cs
	pop	ds
ASSUME	DS:DG
	MOV	DX,OFFSET DG:DirEnt
	MOV	AH,SET_DMA
	INT	21h
	jmp	sys

GOTBADDOS:
	push	cs
	pop	ds
ASSUME	DS:DG
	MOV	DX,OFFSET DG:BADVER	; message to dump
	mov	ah,std_con_string_output
	int	21h
	push	es
	xor	ax,ax
	push	ax

foo	proc	far
	ret			    ; Must use this method, version may be < 2.00
foo	endp

ERR0:
ASSUME	DS:DG,ES:DG
	MOV	DX,OFFSET DG:BADPARM_ptr    ; no drive letter
	JMP	xerror

ERR1:
ASSUME	DS:DG,ES:DG
	MOV	DX,OFFSET DG:BADDRV_ptr     ; drive letter invalid
	JMP	xerror

;
; We do not have a disk that has an available system in the root.  See if the
; media is removable and if so, ask the user for a replacement.  If the media
; is not removable, then die gracefully.
;
ERR2:
ASSUME	DS:DG,ES:DG
	MOV	AH,GET_DEFAULT_DRIVE	;Will find out the default drive
	INT	21H			;Default now in AL
	MOV	BL,AL
	INC	BL			; A = 1
	Call	IsRemovable
	jnc	DoPrompt
	MOV	DX,OFFSET DG:NoSys_PTR
	PUSH	DX
	CALL	PRINTF
	MOV	AX,4C01h
	INT	21h
DoPrompt:
	MOV	AL,DEFALT		; get default drive number
	ADD	AL,'A'-1                ; turn into letter
	MOV	SYSDRV,AL
	MOV	DX,OFFSET DG:GETSYS_ptr
	push	dx
	call	printf
	CALL	GetKeystroke		; wait for him to type simething
	XOR	AL,AL			; valid drive spec now...
SYS:
ASSUME	DS:DG,ES:DG
	mov	DOSEntFree,1
	mov	BIOSEntFree,1
	CMP	BYTE PTR fcb_copy+1," " ; Was file specified?
	JNZ	ERR0			; yes, no files are allowed -> error
	CMP	AL,-1			; Invalid drive spec?
	JZ	ERR1			; yes, must have valid drive -> error
	CMP	BYTE PTR fcb_copy,0	; No drive specified?
	JZ	ERR1			; yes, cannot sys to default drive error
	MOV	AH,GET_DEFAULT_DRIVE	; Get default drive
	INT	21H
	INC	AL			; turn from phys drive to logical drive
	MOV	DEFALT,AL		; save it for possible printing
	CMP	BYTE PTR fcb_copy,AL	; did he specify the default drive?
	JZ	ERR1			; yes, default drive not allowed

;------Code to quit if sys called on a drive across a net-----------------------
CheckNet:
	MOV	BL,BYTE PTR fcb_copy	;   x = IOCTL (getdrive, Drive+1);
	MOV	AX,(IOCTL SHL 8) + 9
	INT	21H
	JC	NotNet
	TEST	DX,1200H		; if(x & 0x1000)(redirected or shared)
	JZ	NotNet
	MOV	DX,OFFSET DG:NotNetM_ptr    ;	    Fatal ("Can't do over net");
	JMP	xerror

;-------Load up BIOS and DOS at this point ---------------------------------
NotNet:
	MOV	AL,DEFALT
	ADD	AL,'A'-1                ; turn into letter
	MOV	SourceBIOSName,AL	; twiddle source name
	MOV	SourceDOSName,AL	; twiddle source name
	CLD
	MOV	DX,OFFSET DG:SourceBIOSName   ; source name
	MOV	CX,7
	MOV	AH,4Eh
	INT	21h
	JC	ERR2J
	MOV	AX,WORD PTR DG:DIRENt+26
	MOV	WORD PTR NEWBIO_SIZE_LOW,AX
	MOV	AX,WORD PTR DG:DIRENt+28
	MOV	WORD PTR NEWBIO_SIZE_HIGH,AX
	MOV	DX,OFFSET DG:SourceBIOSName   ; source name
	MOV	DI,OFFSET DG:BIOSInFH	; pointer to block of data
	CALL	OpenFile
	JNC	NotERR2
ERR2J:
	JMP	Err2			; not found, go and try again

NotERR2:
	MOV	DX,OFFSET DG:SourceDOSName   ; source name
	MOV	CX,7
	MOV	AH,4Eh
	INT	21h
	JC	ERR2J
	MOV	AX,WORD PTR DG:DIRENt+26
	MOV	WORD PTR NEWDOS_SIZE_LOW,AX
	MOV	AX,WORD PTR DG:DIRENt+28
	MOV	WORD PTR NEWDOS_SIZE_HIGH,AX
	MOV	DX,OFFSET DG:SourceDOSName    ; source of DOS
	MOV	DI,OFFSET DG:DOSInFH	; pointer to block of data
	CALL	OpenFile		; Look for DOS
	JC	ERR2J			; not there, go ask for a system disk
	MOV	CX,SP			; get lowest available spot
	SUB	CX,0200h+(OFFSET DG:BUF); leave room for all sorts of things
	MOV	cbBuf,CX		; store length away
	CALL	FillMem 		; load up memory with files
	JC	ERR2J			; Some kind of error reading system


CheckDest:
;------------Check for "valid" destination-----------------------------------
	MOV	AH,0Dh
	INT	21h
	MOV	AL,BYTE PTR fcb_copy
	DEC	AL
	MOV	BX,OFFSET DG:buf	; Temp space
	MOV	DX,1			; Sector 1 (first sec of FAT)
	MOV	CX,DX			; One sector
	INT	25H			; Read Fat sector
	POP	AX			; Flags
	JC	PromptDest		; Issue prompt to insert Destination
					;   disk
	CMP	BYTE PTR [BUF],0F8H
	JAE	OKFAT
;---------Treat 0F0H as a valid 'IBM' FAT ID byte--------------------------------
	cmp	BYTE PTR [BUF],0F0H
	je	OKFAT
	JMP	ERR3


PromptDest:
	push	ax

	mov	al,Byte ptr fcb_copy	   ; Get destination ID
	ADD	AL,'A'-1                ; turn into letter
	MOV	TargDrv,AL

	MOV	DX,OFFSET DG:GetTarg_ptr
	push	dx
	call	printf

	CALL	GetKeystroke		; wait for him to type simething

	pop	ax
	jmp	CheckDest

;-------The Fat appears to be OK... See if DOS/Bios there-----------------




OKFAT:
;
;Set up drive letter in destignation filespecs
;

	MOV	AL,BYTE PTR fcb_copy
	ADD	AL,'A'-1                ; convert to letter
	MOV	BIOSName,AL		; point names at destination drive
	MOV	DOSName,AL

;
; Read in first sector of directory
;
; The reason that we do the direct read of the directory is the find first/next
; or search first/next do an exclusive search for volume labels. By using these
; calls, there is no way to determine if a volume label occupies the first
; location in the directory. Hence we get sleazy and read the directory directly
; (no pun intended) to get this info. While its in there, go ahead and grab the file
; sizes. -MT
;
GetDir:
       MOV     DL,Byte PTR fcb_copy
       MOV     AH,Get_DPB
       PUSH    DS
       INT     21h

; DS:BX point to DPB

	MOV	DX,[BX.dpb_dir_sector]
	POP	DS
	MOV	AL,BYTE PTR fcb_copy
	DEC	AL
	MOV	BX,OFFSET DG:BUF	; Temp space
	MOV	CX,1			; Sector 1 (first sec of FAT)
	INT	25H			; Read Fat sector
	POP	AX			; Flags
	JNC	Skip

;
;NOTE!!!! This next jump is actually a bug left over from the MS days - if the
;dir is not read successfully then the SYS should be canned at that point -
;however, up thru 3.20 this would just jump to the actual sys. To make things
; a little better I jump to an error - this needs to be DCR'd for a real error
;

	JMP	Err3
Skip:

;
; Now see if the first two directory entries are available... First check
; for being free:
;
	MOV	SI,OFFSET DG:BUF
	CMP	BYTE PTR [SI],0 	; empty dir?
	JE	Get_Space		; yes, go check for conflicts
	CLD
	CMP	BYTE PTR [SI],0E5h
	JE	SecondFree
;
; The first entry is not free.	See if the BIOS is there
;
	MOV	DI,OFFSET DG:FCBBIO
	MOV	CX,11
	REP	CMPSB
	JNE	Err3jj			; not BIOS
	DEC	BIOSEntFree

; added by dcl 8/24/86 - most cases solution

	mov	ax,0002 		; is ibmbio allocated the 1st cluster?
	cmp	ax,word ptr DG:BUF+26	; that being cluster 2???
	JNE	Err3jj			; not cluster 2 - error; go display

; end added by dcl 8/24/86

;
; IBM change - MT 6/16/86
; Get the size of the file for IBMBIO
;
	MOV	ax,word ptr DG:BUF+28
	MOV	word ptr IBMBIO_Low,ax
	MOV	ax,word ptr DG:BUF+30
	MOV	word ptr IBMBIO_High,ax

; Check the second entry
;
 SecondFree:
	MOV	SI,OFFSET DG:BUF+32
	CMP	BYTE PTR [SI],0 	; empty dir?
	JE	Get_Space		; yes, go check for conflicts
	CMP	BYTE PTR [SI],0E5h    ; how about second?
	JE	Get_Space
;
; This entry is not free.  Check it for IBMDOS
;
	MOV	DI,OFFSET DG:FCBDOS
	MOV	CX,11
	REP	CMPSB
Err3jj: JNZ	Err3j1			; not DOS
	DEC	DOSEntFree
;
; IBM change - MT 6/16/86
; Get the file size for IBMDOS
;
	MOV	ax,word ptr DG:BUF+60
	MOV	word ptr IBMDOS_Low,ax
	MOV	ax,word ptr DG:BUF+62
	MOV	word ptr IBMDOS_High,ax

;
; We now have the following:
;   Whether or not there is a DOS/BIOS on the disk
;   Whether or not the appropriate entry is free
; We are guaranteed that if the entry is free, then it has the correct file in


;
;IBM CHANGE - MT 6/16/86
;
; Here we make some VERY IMPORTANT assumptions.
;
;1) If IBMBIO exists on the disk currently, we assume it is in the correct
;   place, i.e. at the front of the data area and contiguous.
;2) The stub loader portion of IBMBIO is less than 2048 bytes long. This
;   number comes about by assuming we will never overlay anything smaller
;   than 1920 bytes (DOS 1.10 IBMBIO size). This can be expanded to 2048 if
;   we also assume the smallest possible cluster length is 512 bytes.
;
; Therefore, if we have an empty disk or IBMBIO exists, then we have enough
; contiguous room to install the portion of IBMBIO that requires itself to
; be contiguous.


;
;IBM CHANGE - MT 6/16/86
;
;See if enough free space for IBMBIO and IBMDOS
;

GET_SPACE:
	MOV	AH,36H			;Get free space
	MOV	DL,byte ptr fcb_copy	;Get the drive letter
	INT	21h

;16 bit math okay here, no danger of overflow

	MUL	CX			;Get bytes/cluster
					;Result left in AX
	PUSH	AX			;Save it for later
	MOV	Bytes_Per_Cluster,AX
	MOV	Number_Free_Clusters,BX

;Low result in AX, High result in DX
;Get IBMBIO size

	MOV	AX,IBMBIO_Low
	MOV	DX,IBMBIO_High

	CALL	Get_Clusters
	ADD	Number_Free_Clusters,AX

;Low result in AX, High result in DX
;Get IBMDOS size

	MOV	AX,IBMDOS_Low
	MOV	DX,IBMDOS_High
	CALL	Get_Clusters
	ADD	Number_Free_Clusters,AX


;Find the total size of new DOS and BIOS

	MOV	AX,NEWBIO_Size_Low
	MOV	DX,NEWBIO_Size_High

	CALL	Get_Clusters

Got_NEW_BIO_Clusters:
	MOV	Need_Clusters,AX

	MOV	AX,NEWDOS_Size_Low
	MOV	DX,NEWDOS_Size_High
	CALL	Get_Clusters
	ADD	AX,Need_Clusters

;Now see if there is enough room for all of it on the disk

	CMP	AX,Number_Free_Clusters
	JBE	PUT_SYS

ERR3J1:
	JMP	ERR3

;Go do the sys
PUT_SYS:
	CMP	BIOSEntFree,0
	JNE	GetRidDOS
	MOV	DX,OFFSET DG:BIOSName	; who to change mode
	MOV	CX,0			; undo attributes
	MOV	AX,(ChMod SHL 8) + 1	; set the attributes
	INT	21h
	MOV	DX,OFFSET DG:BIOSName
	MOV	AX,(UNLINK SHL 8)
	INT	21H			; Delete BIOS file
GetRidDOS:
	CMP	DOSEntFree,0
	JNE	CreateBIOS
	MOV	DX,OFFSET DG:DOSName	; who to change mode
	MOV	CX,0			; undo attributes
	MOV	AX,(ChMod SHL 8) + 1	; set the attributes
	INT	21h
	MOV	DX,OFFSET DG:DOSName
	MOV	AX,(UNLINK SHL 8)
	INT	21H				; Delete DOS file
CreateBIOS:
	MOV	DX,OFFSET DG:BIOSName	  ; destination of BIOS
	MOV	CX,7				; fancy attributes
	MOV	AH,Creat			; make a new one
	INT	21h
	JC	err3j2
	MOV	BIOSOutFH,AX			; save handle
	MOV	DX,OFFSET DG:DOSName		; destination of DOS
	MOV	AH,Creat			; make a new one
	INT	21h
	JC	err3j2
	MOV	DOSOutFH,AX		; save handle
	PUSH	DS
	MOV	AH,GET_DPB
	MOV	DL,BYTE PTR fcb_copy	; Target drive
	INT	21H
	MOV	[BX.dpb_next_free],0	; Reset Allocation to start of disk
					;  so BIOS goes in right place!
	POP	DS
Copy:
	CALL	DumpMem 		; flush out memory
	JC	ERR3J2			; Disk full or some other error
	MOV	AX,DOSLenHigh		; more DOS?
	OR	AX,DOSLenLow		; more low dos
	OR	AX,BIOSLenHigh		; more high BIOS
	OR	AX,BIOSLenLow		; more low BIOS
	JZ	AllDone 		; nope, all done
	CALL	FillMem 		; reload world
	JNC	Copy
	JMP	ERR2			; Start over again

ERR3J2:
	JMP	ERR3

ERR4:
	MOV	DX,OFFSET DG:BADSIZ_ptr
	JMP	xerror

AllDone:
	MOV	CX,BIOSTime		; get time and date
	MOV	DX,BIOSTime+2
	MOV	BX,BIOSOutFH		; where to stuff the time
	MOV	AX,(File_Times SHL 8) + 1
	INT	21h
	MOV	AH,Close
	INT	21h
	MOV	CX,DOSTime		; get time and date
	MOV	DX,DOSTime+2
	MOV	BX,DOSOutFH		; where to stuff the time
	MOV	AX,(File_Times SHL 8) + 1
	INT	21h
	MOV	AH,Close
	INT	21h

	CALL	PUTBOOT 		; copy the boot sector also

	MOV	DX,OFFSET DG:DONE_ptr	; all finished message
	push	dx
	call	printf
	XOR	AL,AL			; ok error code
	jmp	short rexit

xerror:
	push	dx
	call	printf
	mov	dx,offset dg:crlf_ptr
	push	dx
	call	printf
	mov	al,0ffh
rexit:
	MOV	AH,EXIT 		; bye and return error code
	INT	21h

POPRET:
	POP	CX
SETERR:
	STC
	return


Get_Clusters:
;Round up to the next cluster size

	MOV	BX,Bytes_Per_Cluster	;Bytes/cluster
	DIV	BX			;If there is a remainder in DX, then
					;we have another cluster to round up
	CMP	DX,0
	JE	Got_CLUSTERS
	INC	AX			;Round up to next cluster
Got_Clusters:
	RET


FillMem:
	MOV	CX,cbBuf		; get length of buffer
	MOV	BX,BIOSInFH		; get bios source handle
	MOV	DX,OFFSET DG:BUF+512	; point to beginning of buffer
					; past area to read in boot rec
	PUSH	CX			; save away total length
	CMP	BIOSLenHigh,0		; > 64K to read?
	JA	UseCX			; use CX
	CMP	BIOSLenLow,CX		; more left to read?
	JA	UseCX			; use CX
	MOV	CX,BIOSLenLow		; move new
UseCX:
	MOV	AH,Read
	INT	21h			; read in what we can
	JC	POPRET			; Error
	CMP	AX,CX			; Did we get it all?
	JNZ	POPRET			; No, error
	ADD	DX,AX			; update pointer for DOS Read
	MOV	pDOS,DX 		; point to beginning of DOS
	SUB	BIOSLenLow,AX		; decrement remaining
	SBB	BIOSLenHigh,0		; do 32 bit
	POP	CX			; get original length
	SUB	CX,AX			; this much is left
	MOV	BX,DOSInFH		; get bios source handle
	CMP	DOSLenHigh,0		; > 64K to read?
	JA	UseCXDOS		; use CX
	CMP	DOSLenLow,CX		; more left to read?
	JA	UseCXDOS		; use CX
	MOV	CX,DOSLenLow		; move new
UseCXDOS:
	MOV	AH,Read
	INT	21h			; read in what we can
	retc				; error
	CMP	AX,CX
	JNZ	SETERR			; Didn't read it all
	ADD	DX,AX			; update pointer for DOS Read
	MOV	pDOSEnd,DX		; point to End of dos DOS
	SUB	DOSLenLow,AX		; decrement remaining
	SBB	DOSLenHigh,0		; do 32 bit arithmetic
	CLC
	return

OpenFile:
	MOV	AX,(OPEN SHL 8) + 0	; open for reading only
	INT	21H			; Look for BIOS
	retc				; not found, go and try again
	STOSW				; stash away handle
	MOV	BX,AX			; get ready for seeks
	MOV	AX,(LSeek SHL 8) + 2	; seek relative to eof
	XOR	CX,CX			; zero offset
	XOR	DX,DX			; zero offset
	INT	21h			; get offsets
	STOSW				; save low part of size
	STOSW				; save low part of size
	MOV	AX,DX
	STOSW				; save high part of size
	STOSW				; save high part of size
	XOR	DX,DX			; zero offset
	MOV	AX,(LSeek SHL 8) + 0	; seek relative to beginning
	INT	21h
	MOV	AX,(File_Times SHL 8) + 0
	INT	21h			; get last write times
	MOV	AX,CX
	STOSW				; save time
	MOV	AX,DX
	STOSW				; save date
	return

ERR3:
	MOV	DX,OFFSET DG:NODEST_ptr
	JMP	xerror

DumpMem:
	MOV	DX,OFFSET DG:BUF+512	; get offset of bios start
	MOV	CX,pDOS 		; beginning of next guy
	SUB	CX,DX			; difference is length
	JZ	DumpDos 		; no bios to move
	MOV	BX,BIOSOutFH		; where to output
	MOV	AH,Write
	INT	21h			; wham
	retc				; error
	CMP	AX,CX			; Did it work?
	JNZ	WRERR			; No
DumpDos:
	MOV	DX,pDOS 		; beginning of dos
	MOV	CX,pDOSEnd		; end of dos
	SUB	CX,DX			; difference is length
	retz				; if zero no write
	MOV	BX,DOSOutFH		; where to output
	MOV	AH,Write
	INT	21h			; wham
	retc				; error
	CMP	AX,CX			; Did it work?
	retz				; Yes, carry clear
WRERR:
	STC
	return



;
; Output a boot sector apporpriate to the supposed version of DOS.  We do this
; by reading in the boot sector for the drive and getting the BPB from there.
; This works fine until we get a 1.x diskette, when we have to grab the media
; descriptor byte and select the correct BPB using that.
;
public putboot
PUTBOOT:
	mov	ax,dg			  ; initialize DS.
	mov	ds,ax
	mov	al,byte ptr fcb_copy	  ; target drive 1-based
	dec	al			  ; convert to 0-based
	mov	dx,0000H		  ; get relative sector 0
	call	Read_Sec
	jc	Do_Media_Byte
	cmp	word ptr DG:[Buf+1FEH], 0AA55H	       ; Is it a 1.x disk?
	jnz	Do_Media_Byte
PB0:
	mov	si,(offset DG:Buf)+13	  ; Set up SI to point to 'SECALL' in
					  ; the BPB just read in.
	jmp	PB7
;
; We were unable to use the BPB in the boot sector, so we must now get a
; valid BPB by examining the media descriptor byte.
;
Do_Media_Byte:
	MOV	AH,GET_DPB
	MOV	DL,BYTE PTR fcb_copy	; Target drive
	INT	21H
	MOV	AL,[BX.dpb_media]	; Media byte
	PUSH	CS
	POP	DS
	SUB	AL,0F8H
	CBW
	MOV	BX,AX			; BX has offset into bpb table
	SHL	BX,1			; Word table, mult by 2
	MOV	SI,[BX+FATID_TABLE]	; get pointer to bpb
	OR	SI,SI			; hardfile?
	JNZ	PB3			; nope.

;
; We have a hard drive. We can use the IOCTL Get Device Parameters to get the
; BPB for the hard drive.
;
	mov	bl,byte ptr fcb_copy	; Drive number
	mov	dx,offset DeviceParameters
	mov	ah,IOCTL
	mov	al,GENERIC_IOCTL
	mov	ch,RAWIO
	mov	cl,Get_Device_Parameters
	int	21H
	mov	bx,dx			; ds:bx-> DeviceParameters
	lea	si,byte ptr [bx].DP_BPB ; SI -> BPB returned
	inc	si
	inc	si			; si-> secall in BPB

PB3:	CMP	SI,-1			; weird internal error?
	JZ	PB9
	CMP	SI,-2			; new multi-value FAT ID?
	JNZ	PB7			; nope.
;
; We may have a FAT ID of F9.  This may represent TWO different media in two
; incompatible drives.	We do a get free space to determine which.
;
	MOV	AH,Get_Drive_freespace
	MOV	DL,BYTE PTR FCB_copy
	INT	21h
	MOV	BX,DX
	MUL	BX			; compute total sectors
	CMP	AX,cSec325
	MOV	SI,OFFSET DG:BPB325
	JB	PB7
	MOV	SI,OFFSET DG:BPB96T

PB7:	PUSH	DS			; COPY IN THE NEW STUFF
	POP	ES
	MOV	DI,OFFSET DG:SECALL
	MOV	CX,BPBSIZ
	CLD
	REP	MOVSB

PB8:
	cmp	dg:FATID,0F8H			; Hard drive?
	jnz	not_hard
; NOTE:
; The physical hard drive number is placed in the third byte from the end in the
; boot sector in DOS 3.2. This is a change from the previous DOS versions.
;
	mov	byte ptr dg:[boot+1FDH],80H	; set physical hard drive number
not_hard:
	MOV	AL,BYTE PTR fcb_copy
	DEC	AL
	MOV	BX,OFFSET DG:BOOT	; Boot sector
	XOR	DX,DX			; Sector 0
	MOV	CX,DX
	INC	CX			; One sector
	INT	26H			; Write out 8 sector boot sector
	jnc	pb9
PB9:	POP	AX			; Flags
	RET

;
; Read_Sec reads the sector and track specified in DX from the drive in AL.
; The sector is read into location Buf. Carry set if error.
;

READ_SEC:
	push	ax			; save registers
	mov	cx,0001H
	mov	bx,Offset DG:Buf	; temp. buffer
	int	25H
	pop	ax			; Pop flags pushed by Int 25
Read_Ret:
	pop	ax
	ret



GetKeystroke:
	MOV	AX,(Std_CON_Input_Flush SHL 8) + Std_CON_Input_No_Echo
	INT	21H
	MOV	AX,(Std_CON_Input_Flush SHL 8) + 0
	INT	21H
	mov	dx,offset dg:crlf_ptr
	push	dx
	call	printf
	return

;
; Determine if the drive indicated in BX is removable or not.
;
;   Inputs:	BX has drive (0=default, 1=A)
;   Outputs:	Carry clear => removable
;		Carry set   => non-removable
;
;   Registers modified: none
;
IsRemovable:
	SaveReg <AX>
	mov	AX,(IOCTL SHL 8) OR 8	    ; do a media check
	INT	21H
	jnc	CheckRemove
	MOV	AX,(IOCTL SHL 8) OR 9	    ; Is it a net drive
	INT	21H
	jc	NotRemove		    ; Yipe! Say non-removable
	TEST	DX,1000H
	jnz	NotRemove		    ; Is NET drive. Say non-removable.
	JMP	IsRemove
CheckRemove:
	TEST	ax,1
	jnz	NotRemove
IsRemove:
	CLC
	RestoreReg  <AX>
	return
NotRemove:
	STC
	RestoreReg  <AX>
	return

;
; Moves the name at the current locations in the system file name tables into
; the relevant system variables for later use.
; Preserves all registers (except flags).
;
MoveNames:
	push	bx
	push	cx
	push	di
	push	si
	cld
	mov	bx,word ptr DOSFile_Offset
	mov	si,offset DG:BIOSNamesTable
	add	si,bx
	mov	di,offset DG:BIOSName
	add	di,3			; skip over drive,colon,back-slash
	mov	cx,11
	repe	movsb
	mov	si,offset DG:DOSNamesTable
	add	si,bx
	mov	di,offset DG:DOSName
	add	di,3			; skip over drive,colon,back-slash
	mov	cx,11
	repe	movsb
	pop	si
	pop	di
	pop	cx
	pop	bx
	ret

;
; Set up filename in FCB variables for searching directory entries.
;
SetUpFCB:
	push	si
	push	di
	push	bx
	mov	si,offset DG:BIOSNamesTable
	mov	bx,word ptr DOSFile_Offset
	add	si,bx
	mov	di,offset DG:FCBBIO
	call	MoveToFCB
	mov	si,offset DG:DOSNamesTable
	mov	bx,word ptr DOSFile_Offset
	add	si,bx
	mov	di,offset DG:FCBDOS
	call	MoveToFCB
	pop	bx
	pop	di
	pop	si
	ret

;
; Move ASCIZ filename to FCB-type filename from [SI] to [DI].
;
MoveToFCB:
	xor	cx,cx
NextChar:
	lodsb
	cmp	al,'.'
	jz	Fill_Blanks
	stosb
	inc	cx
	jmp	short NextChar

Fill_Blanks:
	mov	al,' '
	cmp	cx,8
	jge	See_Ext
	stosb
	inc	cx
	jmp	short Fill_Blanks

See_Ext:
	mov	cx,3
Next_Ext:
	lodsb
	stosb
	dec	cx
	jnz	Next_Ext
	RET

;
; Gets source file names from SourceBIOSFile and SourceDOSFile and puts them
; into the ASCIZ strings SourceBIOSName and SourceDOSName.
;
GetSourceNames:
	push	si
	push	di
	push	cx
	mov	si,offset DG:SourceBIOSFile
	mov	di,offset DG:SourceBIOSName
	add	di,3			; skip over drive,colon,back-slash
	mov	cx,12
	repe	movsb
	mov	si,offset DG:SourceDOSFile
	mov	di,offset DG:SourceDOSName
	add	di,3			; skip over drive,colon,back-slash
	mov	cx,12
	repe	movsb
	pop	cx
	pop	di
	pop	si
	ret



CODE	ENDS
	END	START
