
page	,132;
title	Non-Contiguous IBMBIO Loader (MSLOAD)
;==============================================================================
;REVISION HISTORY:
;AN000 - New for DOS Version 4.00 - J.K.
;AC000 - Changed for DOS Version 4.00 - J.K.
;AN00x - PTMs for DOS Version 4.00 - J.K.
;==============================================================================
;AN001; - P1820 New Message SKL file				   10/20/87 J.K.
;AN002; - D381	For SYS.COM, put the version number		   01/06/88 J.K.
;==============================================================================
;JK, 1987 -
;     For DOS 4.00, MSLOAD program has been changed to allow:
;	  1. 32 bit calculation,
;	  2. Reading a FAT sector when needed, instead of reading the whole FAT
;	     sectors at once.  This will make the Boot time faster, and eliminate
;	     the memory size limitation problem,
;	  3. Solving the limitation of the file size (29 KB) of IBMBIO.COM,
;	  4. Adding the boot error message.  Show the same boot error message
;	     and do the same behavior when the read operation of IBMBIO.COM
;	     failes as the MSBOOT program, since MSLOAD program is the
;	     extention of MSBOOT program.
;

IF1
	%OUT ASSEMBLING: Non-Contiguous IBMBIO Loader (MSLOAD)
	%OUT

ENDIF


DSKADR	= 1Eh * 4		;ROM bios diskette table vector position

bootseg 	segment at 0h


       org	7C00h
Boot_Sector	label	byte
bootseg 	ends


dosloadseg	segment at 70h
       org	00h
IBMBIO_Address	label   byte

dosloadseg	ends


cseg		segment public para 'code'
		assume	cs:cseg,ds:nothing,es:nothing,ss:nothing

include MSload.inc
include Bootform.inc		;AN000; Extended bpb, boot record defintion.
include versiona.inc		;AN001; Version number for SYS.COM

sec9	equ	522h		;;** 8/3/87 DCL

BIOOFF		equ	700h
;
org	0h

start:
	jmp Save_Input_Values
SYS_Version	dw	EXPECTED_VERSION ;AN001; From VERSIONA.INC file
Mystacks	dw	64 dup (0)	;AN000; local stack
MyStack_ptr	label	word

;local data
Number_Of_Heads 	dw	0
Size_Cluster		dw	0
Start_Sector_L	      dw      0
Start_Sector_H	      dw      0 		;J.K.
Temp_H			dw	0		;J.K. For 32 bit calculation
Temp_Cluster		dw	0		;J.K. Temporary place for cluster number
Last_Fat_SecNum 	dw	-1		;Fat sector number starting from the first fat entry.
Sector_Count		dw	0
Number_Of_FAT_Sectors	dw	0
Hidden_Sectors_L	dw	0
Hidden_Sectors_H	dw	0		;J.K.
Sector_Size		dw	0
Reserved_Sectors	dw	0
Last_Found_Cluster	dw	0
Next_BIO_Location	dw	0
First_Sector_L		dw	0
First_Sector_H		dw	0		;J.K.
Drive_Lim_L		dw	0		;J.K. Max. number of sectors
Drive_Lim_H		dw	0		;J.K.
Sectors_Per_Track	dw	0
Drive_Number		db	0
FAT_Size		db	0
Media_Byte		db	0
EOF			db	0
Org_Rom_Disktable	dd	0
FAT_Segment		dw	0
Sectors_Per_Cluster	db	0

subttl	Save Input Values
page
;***********************************************************************
;  Save_Input_Values
;***********************************************************************
;
; Input:     none
;
;   DL = INT 13 drive number we booted from
;   CH = media byte
;   BX = First data sector (low) on disk (0-based)
;   DS:SI = Original ROM BIOS DISKETTE Parameter table.
;J.K. 6/2/87 If an extended Boot Record, then AX will be the First data sector
;J.K. high word.  Save AX and set First_Sector_H according to AX if it is an
;J.K. extended boot record.
;   AX = First data sector (High) on disk ;
; Output:
;
;   BX = first data sector on disk
;
;   Media_Byte = input CH
;   Drive_Number = input DL
;   First_Sector_L = input BX
;   First_Sector_H = input AX, if an extended Boot record.;J.K.
;   Drive_Lim_L = maximum sector number in this media ;J.K.
;   Drive_Lim_H = high word of the above
;   Hidden_Sectors_L = hidden secotrs
;   Hidden_Sectors_H
;   Reserved_Sectors = reserved sectors
;   Sectors_Per_Track = Sectors/track
;   Number_Of_Heads = heads/cylinder
;
;   DS = 0
;   AX,DX,SI destroyed
;
; Calls:     none
;-----------------------------------------------------------------------
;Function:
; Save input information and BPB informations from the boot record.
;
;----------------------------------------------------------------------
Save_Input_Values:


	mov	First_Sector_L,bx	   ;AC000;
	mov	media_Byte,ch
	mov	Drive_Number,dl
	mov	word ptr Org_Rom_Disktable, si
	push	ds
	pop	word ptr Org_Rom_Disktable+2
	xor	cx,cx				;Segment 0
	mov	ds,cx
	assume	ds:Bootseg

	push	es				;;** DCL 8/3/87
	mov	es,cx				;;** DCL 8/3/87
	assume	es:Bootseg			;;** DCL 8/3/87

	MOV	SI,WORD PTR DS:DSKADR	;			  ARR 2.41
	MOV	DS,WORD PTR DS:DSKADR+2 ; DS:SI -> CURRENT TABLE  ARR 2.41

	MOV	DI,SEC9 		; ES:DI -> NEW TABLE	  ARR 2.41
	MOV	CX,11	   ; taken from ibmboot.asm		  ARR 2.41
	CLD				;
	REP	MOVSB			; COPY TABLE		  ARR 2.41
	PUSH	ES			;			  ARR 2.41
	POP	DS			; DS = 0		  ARR 2.41

	MOV	WORD PTR DS:DSKADR,SEC9 ;			  ARR 2.41
	MOV	WORD PTR DS:DSKADR+2,DS ; POINT DISK PARM VECTOR TO NEW TABLE
	pop	es			;;** DCL 8/3/87
	assume	es:nothing

	mov	cx,Boot_Sector.EXT_BOOT_BPB.EBPB_BYTESPERSECTOR      ;AN000;
	mov	cs:Sector_Size, cx				     ;AN000;
	mov	cl,Boot_Sector.EXT_BOOT_BPB.EBPB_SECTORSPERCLUSTER   ;AN000;
	mov	cs:Sectors_Per_Cluster, cl			     ;AN000;
	mov	cx,Boot_Sector.EXT_BOOT_BPB.EBPB_SECTORSPERTRACK     ;Get Sectors per track
	mov	cs:Sectors_Per_Track,cx
	mov	cx,Boot_Sector.EXT_BOOT_BPB.EBPB_HEADS		     ;Get BPB heads per cylinder
	mov	cs:Number_Of_Heads,cx
	mov	cx,Boot_Sector.EXT_BOOT_BPB.EBPB_SECTORSPERFAT	     ;Get sectors per FAT
	mov	cs:Number_Of_FAT_Sectors,cx
	mov	cx,Boot_Sector.EXT_BOOT_BPB.EBPB_RESERVEDSECTORS     ;Get Reserved Sectors
	mov	cs:Reserved_Sectors,cx
	mov	cx,word ptr Boot_Sector.EXT_BOOT_BPB.EBPB_HIDDENSECTOR	 ;Get hidden sectors
	mov	cs:Hidden_Sectors_L,cx
	mov	cx, Boot_Sector.EXT_BOOT_BPB.EBPB_TOTALSECTORS	     ;AN000;
	mov	cs:Drive_Lim_L, cx				     ;AN000;

;J.K. First of all, check if it the boot record is an extended one.
;J.K. This is just a safe guard in case some user just "copy" the 4.00 IBMBIO.COM
;J.K. to a media with a conventional boot record.

	cmp	Boot_Sector.EXT_BOOT_SIG, EXT_BOOT_SIGNATURE		;AN000;
	jne	Relocate						;AN000;
	mov	cs:First_Sector_H, AX					;AN000; start data sector (high)
	mov	ax,word ptr Boot_Sector.EXT_BOOT_BPB.EBPB_HIDDENSECTOR+2  ;AN000;
	mov	cs:Hidden_Sectors_H,ax					  ;AN000;
	cmp	cx, 0							;AN000; CX set already before (=Totalsectors)
	jne	Relocate						;AN000;
	mov	ax, word ptr Boot_Sector.EXT_BOOT_BPB.EBPB_BIGTOTALSECTORS   ;AN000;
	mov	cs:Drive_Lim_L, ax					     ;AN000;
	mov	ax, word ptr Boot_Sector.EXT_BOOT_BPB.EBPB_BIGTOTALSECTORS+2 ;AN000;
	mov	cs:Drive_Lim_H, ax					  ;AN000;
subttl	Relocate
page
;
;***********************************************************************
;  RELOCATE
;***********************************************************************
;
; Notes:
;
;   Relocate the loader code to top-of-memory.
;
; Input:     none
;
; Output:    Code and data relocated.
;	     ax,cx,si,di destroyed
;
; Calls:     none
;-----------------------------------------------------------------------
; Copy code from Start to Top of memory.
;
; The length to copy is Total_length
;
; Jump to relocated code
;-----------------------------------------------------------------------
;
Relocate:
	assume	ds:nothing
	cld				;AN000;
	xor	si,si			;AN000;
	mov	di,si			;AN000;
;SB34LOAD000****************************************************************
;SB  Determine the number of paragraphs (16 byte blocks) of memory.
;SB	This involves invoking the memory size determination interrupt,
;SB	which returns the number of 1K blocks of memory, and then
;SB	converting this to the number of paragraphs.
;SB	Leave the number of paragraphs of memory in AX.

	int	12h			;get system memory size in Kbytes
	mov	cl,6			;
	shl	ax,cl			;memory size in paragraphs
;SB34LOAD000****************************************************************
	mov	cl,4			;AN000;
	mov	dx, cs:Sector_Size	;AN000;
	shr	dx,cl			;AN000;
	inc	dx			;AN000;
	sub	ax, dx			;AN000;
	mov	cs:Fat_Segment, ax	;AN000;This will be used for FAT sector
	mov	dx, offset total_length ;AN000;
	shr	dx, cl			;AN000;
	inc	dx			;AN000;
	sub	ax, dx			;AN000;
	mov	es, ax			;AN000;es:di -> place be relocated.
	push	cs			;AN000;
	pop	ds			;AN000;ds:si -> source
	mov	cx, offset total_length ;AN000;
	rep	movsb			;AN000;

	push	es			;AN000;
	mov	ax, offset Setup_stack	;AN000;
	push	ax			;AN000;massage stack for destination of cs:ip
Dumbbb	proc	far			;AN000;
	ret				;AN000;
Dumbbb	endp				;AN000;


;	 push	 cs				 ;Set up ds segreg
;	 pop	 ds
;	 xor	 ax,ax				 ;Set up ES segreg
;	 mov	 es,ax
;
;	 assume  es:bootseg,ds:cseg
;
;	 mov	 si,offset Start		 ;Source
;	 mov	 di,offset Relocate_Start	 ;Target
;	 mov	 cx,Relocate_Length		 ;Length
;	 rep	 movsb				 ;Go do it
;	 jmp	 far ptr Relocate_Start



subttl	Setup Stack
page
;***********************************************************************
;  Setup_Stack
;***********************************************************************
;
; Input:     none
;
; Output:
;
;   SS:SP set
;   AX destroyed
;-----------------------------------------------------------------------
; First thing is to reset the stack to a better and more known place.
;
; Move the stack to just under the boot record and relocation area (0:7C00h)
;
; Preserve all other registers
;----------------------------------------------------------------------

Setup_Stack:
	assume	ds:nothing, es:nothing, ss:nothing
;	 CLI				 ;Stop interrupts till stack ok
	mov	ax,cs
	MOV	SS,AX			;Set up the stack to the known area.
	mov	sp, offset MyStack_Ptr
;	 MOV	 SP,7C00h - 50		 ;Leave room for stack frame
;	 MOV	 BP,7C00h - 50		 ;Point BP as stack index pointer
;	 STI

subttl	Find_Cluster_Size
page
;***********************************************************************
;  Find_Cluster_Size
;***********************************************************************
;
; Input:     BPB information in loaded boot record at 0:7C00h
;
; Output:
;
;	DS = 0
;	AX = Bytes/Cluster
;	BX = Sectors/Cluster
;	SI destroyed
; Calls:     none
;-----------------------------------------------------------------------
;
; Get Bytes/sector from BPB
;
; Get sectors/cluster from BPB
;
; Bytes/cluster = Bytes/sector * sector/cluster
;----------------------------------------------------------------------
Find_Cluster_Size:

;For the time being just assume the boot record is valid and the BPB
;is there.

	xor	ax,ax				;Segment 0
	mov	ds,ax

	assume ds:bootseg

	mov	ax,Boot_Sector.EXT_BOOT_BPB.EBPB_BYTESPERSECTOR    ;AC000;Get BPB bytes/sector
	xor	bx,bx
	mov	bl,Boot_Sector.EXT_BOOT_BPB.EBPB_SECTORSPERCLUSTER ;AC000;Get sectors/cluster
	mul	bx				;Bytes/cluster
	mov	cs:Size_Cluster,ax		;Save it


subttl	Determine FAT size
page
;***********************************************************************
;  Determine_FAT_Size
;***********************************************************************
;
; Notes:
;
;   Determine if FAT is 12 or 16 bit FAT. 12 bit FAT if floppy, read MBR
;   to find out what system id byte is.
;
; Input:
;
; Output:
;
;   cs:Fat_Size = FAT12_bit or FAT16_bit
;   All other registers destroyed
;
;----------------------------------------------------------------------
Determine_FAT_Size:
	mov	cs:FAT_Size,FAT12_bit		;AN000;Assume 12 bit fat
	mov	dx, cs:Drive_Lim_H		;AN000;
	mov	ax, cs:Drive_Lim_L		;AN000;
	sub	ax, cs:Reserved_Sectors 	;AN000;
	sbb	dx, 0				;AN000;now, dx;ax = available total sectors
	mov	bx, cs:Number_Of_FAT_Sectors	;AN000;
	shl	bx, 1				;AN000;2 FATs
	sub	ax, bx				;AN000;
	sbb	dx, 0				;AN000;now, dx;ax = tatal sectors - fat sectors
	mov	bx, Boot_Sector.EXT_BOOT_BPB.EBPB_ROOTENTRIES ;AN000;
	mov	cl, 4				;AN000;
	shr	bx, cl				;AN000;Sectors for dir entries = dir entries / Num_DIR_Sector
	sub	ax, bx				;AN000;
	sbb	dx, 0				;AN000;
	xor	cx, cx				;AN000;
	mov	cl, Boot_Sector.EXT_BOOT_BPB.EBPB_SECTORSPERCLUSTER ;AN000;
	push	ax				;AN000;
	mov	ax, dx				;AN000;
	xor	dx, dx				;AN000;
	div	cx				;AN000;
	mov	cs:Temp_H, ax			;AN000;
	pop	ax				;AN000;
;J.K. We assume that cx > dx.
	div	cx				;AN000;
	cmp	ax, 4096-10			;AN000;
;	 jb	 Determine_First_Cluster	 ;AN000;
	jb	Read_In_FirstClusters
	mov	cs:FAT_Size, FAT16_Bit		;AN000;16 bit fat

;	 cmp	 cs:Media_Byte,0F8h		 ;Is it floppy
;	 jne	 FAT_Size_Found 		 ;Yep, all set
;	 mov	 cs:Logical_Sector,0		 ;Got hardfile, go get MBR
;	 xor	 ax,ax
;	 mov	 es,ax
;	 mov	 di,offset Relocate_Start
;	 mov	 cs:Sector_Count,1
;	 call	 Disk_Read
;	 mov	 si,offset Relocate_Start+1C2h
;	 mov	 cx,4
;	 xor	 ax,ax
;	 mov	 ds,ax
;Find_Sys_Id:
;	 mov	 cs:FAT_Size,FAT12_bit		     ;Assume 12 bit fat
;	 cmp	 byte ptr [si],1
;	 je	 FAT_Size_Found
;	 mov	 cs:FAT_Size,FAT16_bit		     ;Assume 12 bit fat
;	 cmp	 byte ptr [si],4
;	 je	 Fat_Size_Found
;	 add	 si,16
;	 loop	 Find_Sys_Id
;	;xxxxxxxxxxxxxxxxxxxxxxxxxx error
;FAT_Size_Found:


subttl Read_In_FirstClusters
page
;***********************************************************************
;  Read_In_FirstClusters
;***********************************************************************
;
; Notes: Read the start of the clusters that covers at least IBMLOADSIZE
;	 fully.  For example, if sector/cluster = 2, and IBMLOADSIZE=3
;	 then we are going to re-read the second cluster to fully cover
;	 MSLOAD program in the cluster boundary.
;
; Input:
;   IBMLOADSIZE - Make sure this value is the same as the one in
;		  MSBOOT program when you build the new version!!!!!
;
;   Sectors_Per_Cluster
;   Size_Cluster
;   First_Sector_L
;   First_Sector_H
;
; Output: MSLOAD program is fully covered in a cluster boundary.
;	  AX = # of clusters we read in so far.
;
; Calls:     Disk_Read
; Logic:
;	AX; DX = IBMLOADSIZE / # of sector in a cluster.
;	if DX = 0 then Ok. (MSLOAD is in a cluster boundary.)
;      else		   (Has to read (AX+1)th cluster to cover MSLOAD)
;	read (AX+1)th cluster into the address after the clusters we
;	read in so far.
;-----------------------------------------------------------------------

Read_In_FirstClusters:
	mov	ax, IBMLOADSIZE 			;AN000;
	div	cs:Sectors_Per_Cluster			;AN000;
	cmp	ah, 0					;AN000;
	je	Set_Next_Cluster_Number 		;AN000;
	xor	ah, ah					;AN000;
	push	ax					;AN000;
	mov	cx, cs:First_Sector_L			;AN000;
	mov	cs:Start_Sector_L, cx			;AN000;
	mov	cx, cs:First_Sector_H			;AN000;
	mov	cs:Start_Sector_H, cx			;AN000;
	mul	cs:Sectors_Per_Cluster			;AN000; Now, AX=# of sectors
	add	cs:Start_Sector_L, ax			;AN000;
	adc	cs:Start_Sector_H, 0			;AN000;
	pop	ax					;AN000;
	push	ax					;AN000;
	mov	di, BIOOFF				;AN000;
	mul	cs:Size_Cluster 			;AN000;AX = # of bytes read in before this cluster
	add	di, ax					;AN000;
	xor	ax, ax					;AN000;
	mov	es, ax					;AN000;
	mov	al, cs:Sectors_Per_Cluster		;AN000;
	mov	cs:Sector_Count, ax			;AN000;
	call	Disk_Read				;AN000;
	pop	ax					;AN000;
	inc	ax					;AN000;# of clusters read in so far.

subttl Set_Next_Cluster_Number
page
;***********************************************************************
;  Set_Next_Cluster_Number
;***********************************************************************
;
; Notes: Set LAST_Found_Cluster for the next use.
;	 Last_Found_Cluster is the cluster number we are in now.
;	 Since cluster number is 0 based and there are 2 clusters int
;	 the beginning of FAT table used by the system, we just add
;	 1 to set Last_Found_Cluster.
;
; Input:
;   AX = # of clusters read in so far.
;
; Output:
;
;   cs:Last_Found_Cluster
;
; Calls:     none
;------------------------------------------------------------------
Set_Next_Cluster_Number:
	inc	ax			      ;AN000; For Last_Found_Cluster
	mov	cs:Last_Found_Cluster,ax      ;2 is the first data cluster number(0 based)

subttl	Read In FAT
page
;***********************************************************************
;  Read_In_FAT
;***********************************************************************
;
; Notes:
;
;   Reads in the entire FAT at 800:0. This gives the relocated portion
;   of this loader a maximum size of 768 bytes (8000 - 7D00).
;   With 64 KB memory system, this can support maximum size of FAT to
;   be 32 KB.  We assumes that the system memory size be 128 KB, if
;   the system has a big media with the total fat size bigger than
;   32 KB.
;
; Input:     none
;
; Output:
;
;   ES = 0
;   All sectors destroyed
;
; Calls:  READ DISK
;-----------------------------------------------------------------------
; Get number of sectors in FAT
;
; Set ES:DI to 800:0
;
; Read in the sectors
;
;----------------------------------------------------------------------
;Read_In_FAT:
;	 mov	 ax,cs:Number_Of_FAT_Sectors   ;Get sectors/FAT
;	 mov	 cs:Sector_Count,ax	       ;Number of sectors to read
;	 mov	 ax,cs:Hidden_Sectors_L        ;Hidden+Reserved = start of FAT sector
;	 mov	 dx,cs:Hidden_Sectors_H        ;AN000;
;	 add	 ax,cs:Reserved_Sectors
;	 adc	 dx, 0
;	 mov	 cs:Start_Sector_L,ax	     ;AC000;Save it, setup for disk read
;	 mov	 cs:Start_Sector_H,dx	     ;AN000;
;	 mov	 di, 800h			 ;AC000;
;	 mov	 es, di 			 ;AC000;
;	 xor	 di, di 			 ;AC000;
;	 assume  es:nothing
;	 call	 Disk_Read
;
subttl	Keep Loaded BIO
page
;***********************************************************************
;  KEEP LOADED BIO
;***********************************************************************
;
; Notes:
;
;   Determine how much of IBMBIO was loaded in when the loader was loaded
;   by the boot record (only the portion that is guaranteed to be contiguous)
;
; Input:
;
;   cs:Last_Found_Cluster = number of clusters used for loader+2
;
; Output:
;	ES=70h
;	DI = Next offset to load IBMBIO code
;	AX,BX,CX,DX,SI destroyed
;
;	cs:Next_BIO_Location = DI on output
;	cs:Last_Cluster = last cluster loaded
;
; Calls:     none
;-----------------------------------------------------------------------
;Number of clusters loaded+2 is in cs:Last_Found_Cluster
;
;Multiply cluster * cluster size in bytes to get total loaded for MSLOAD
;
;Subtract TOTAL_LOADED - LOADBIO_SIZE to get loaded IBMBIO in last cluster
;
;Relocate this piece of IBMBIO down to 70:0
;
;----------------------------------------------------------------------
Keep_Loaded_BIO:
	push	ds
	mov	ax,cs:Last_Found_Cluster	;Point to last cluster loaded
	sub	ax,1				;Get number of clusters loaded
	mul	cs:Size_Cluster 		;Get total bytes loaded by
						;This is always < 64k, so
						;lower 16 bits ok
	sub	ax,LoadBio_Size 		;Get portion of IBMBIO loaded
	mov	cx,ax				;Save length to move
	mov	ax,70h				;Segment at 70h
	mov	ds,ax
	mov	es,ax
	mov	si,offset Total_Length		;Point at IBMBIO
	mov	di,0				;Point at 70:0
	rep	movsb				;Relocate this code
	mov	cs:Next_Bio_Location,di 	;Save where to load next
	pop	ds

subttl Get Contiguous Clusters
page
;***********************************************************************
;  Get_Contiguous_Clusters
;***********************************************************************
;
; Notes: Go find clusters as long as they are contiguous
;
;
; Input:
;
;   cs:Next_BIO_Location
;   cs:
;
;
; Output:
;
;
; Calls: Get_Next_FAT_Entry
;-----------------------------------------------------------------------
;
;Set cs:Sector_Count to Sectors per cluster
;
;Call Get_Next_FAT_Entry to get next cluster in file
;
;Call Check_for_EOF
;
;IF (NC returned)
;
;   {Call Get_Next_FAT_Entry
;
;    IF (New cluster is contig to old cluster)
;	{Add sectors per cluster to cs:Sector_Count
;
;	 Call Check_For_EOF
;
;	 IF (NC returned)
;
;
;----------------------------------------------------------------------
Get_Contiguous_Cluster:
	xor	ah,ah
	mov	al,cs:Sectors_Per_Cluster	;Assume we will get one cluster
	mov	cs:Sector_Count,ax
	push	cs:Sector_Count
	call	Get_Next_Fat_Entry	;Go get it in AX
	pop	cs:Sector_Count
	mov	cs:Last_Found_Cluster,ax ;Update the last one found
	cmp	cs:EOF,END_OF_FILE
	je	GO_IBMBIO

;	 je	 GOTO_IBMBIO
;Got_Contig_Clusters:

	xor	dx,dx			;AN000;
	sub	ax,2			;Zero base the cluster
	xor	ch,ch
	mov	cl,cs:Sectors_Per_Cluster ;Get sectors per cluster
	mul	cx			;Get how many
	add	ax,cs:First_Sector_L	;AC000;See where the data sector starts
	adc	dx,cs:First_Sector_H	;AN000;
	mov	cs:Start_Sector_L,ax	;AC000;Save it
	mov	cs:Start_Sector_H,dx	;AN000;
	mov	di,cs:Next_Bio_Location ;Get where to put code
	push	cs:Sector_Count 	;Save how many sectors
	mov	ax,dosloadseg		;Get area to load code
	mov	es,ax
	call	Disk_Read
	pop	ax			;Get back total sectors read in
;	jc	##########
	mul	cs:Sector_Size		;AC000;Get number of bytes we loaded
;	 mul	 Boot_Sector.ByteSec
	add	cs:Next_Bio_Location,ax ;Point to where to load next
	jmp	Get_Contiguous_Cluster

subttl	GOTO IBMBIO
page
;***********************************************************************
;  GOTO_IBMBIO
;***********************************************************************
;
; Notes:
;
;  Set up required registers for IBMBIO, then jump to it (70:0)
;
; Input:     none
;
;   cs:Media_Byte = media byte
;   cs:Drive_Number = INT 13 drive number we booted from
;   cs:First_Sector_L = First data sector on disk (Low) (0-based)
;   cs:First_Sector_H = First data sector on disk (High)
;
; Output:
;
;   Required by MSINIT
;   DL = INT 13 drive number we booted from
;   CH = media byte
;   BX = First data sector on disk (0-based)
;   AX = First data sector on disk (High)
;   DI = Sectors/FAT for the boot media.
;
; Calls:     none
;-----------------------------------------------------------------------
;
; Set up registers for MSINIT then do Far Jmp
;
;----------------------------------------------------------------------
GO_IBMBIO:
	mov	ch,cs:Media_Byte	;Restore regs required for MSINT
	mov	dl,cs:Drive_Number	;Physical Drive number we booted from.
	mov	bx,cs:First_Sector_L	;AC000;
	mov	ax,cs:First_Sector_H	;AN000; AX will be the First data sector (High)
;J.K. Don't need this information any more.
;	mov	di,cs:Number_Of_FAT_Sectors ;AN000
	jmp	far ptr IBMBIO_Address


subttl	Disk Read
page
;***********************************************************************
; Disk_Read
;***********************************************************************
;
; Notes:
;
;  Read in the cs:Sector_Count number of sectors at ES:DI
;
;
; Input:     none
;
;   DI = Offset of start of read
;   ES = Segment of read
;   cs:Sector_Count = number of sectors to read
;   cs:Start_sector_L = starting sector (Low)
;   cs:Start_sector_H = starting sector (High)
;   Following is BPB info that must be setup prior to call
;   cs:Number_Of_Heads
;   cs:Number_Of_Sectors
;   cs:Drive_Number
;   cs:Sectors_Per_Track
;
; Output:
;
;   AX,BX,CX,DX,SI,DI destroyed
;-----------------------------------------------------------------------
; Divide start sector by sectors per track
; The remainder is the actual sector number, 0 based
;
; Increment actual sector number to get 1 based
;
; The quotient is the number of tracks - divide by heads to get the cyl
;
; The remainder is actual head, the quotient is cylinder
;
; Figure the number of sectors in that track, set AL to this
;
; Do the read
;
; If Error, Do RESET, then redo the INT 13h
;
; If successful read, Subtract # sectors read from Sector_Count, Add to Logical
; Sector, add #sectors read * Sector_Size to BX;
;
; If Sector_Count <> 0 Do next read
;----------------------------------------------------------------------
Disk_Read:

;
; convert a logical sector into Track/sector/head.  AX has the logical
; sector number
;
DODIV:
	MOV	cx,5			;5 retries

Try_Read:
	PUSH	cx			;Save it
	MOV	AX,cs:Start_Sector_L	;AC000; Get starting sector
	mov	dx, cs:Start_Sector_H	;AN000;
;	 XOR	 DX,DX
	push	ax			;AN000;
	mov	ax, dx			;AN000;
	xor	dx, dx			;AN000;
	DIV	word ptr cs:Sectors_Per_Track
	mov	cs:Temp_H, ax	      ;AN000;
	pop	ax			;AN000;
	div	word ptr cs:Sectors_Per_Track  ;AN000;[temp_h];AX = track, DX = sector number
	MOV	bx,cs:Sectors_Per_Track ;Get number of sectors we can read in
	sub	bx,dx			;this track
	mov	si,bx
	cmp	cs:Sector_Count,si    ;Is possible sectors in track more
	jae	Got_Length		;than what we need to read?
	mov	si,cs:Sector_Count    ;Yes, only read what we need to
Got_Length:
	INC	DL			; sector numbers are 1-based
	MOV	bl,dl			;Start sector in DL
	mov	dx, cs:Temp_H	      ;AN000;now, dx;ax = track
;	 XOR	 DX, DX
	push	ax			;AN000;
	mov	ax, dx			;AN000;
	xor	dx, dx			;AN000;
	DIV	word ptr cs:Number_Of_Heads  ;Start cyl in ax,head in DL
	mov	cs:Temp_h, ax	      ;AN000;
	pop	ax			;AN000;
	div	word ptr cs:Number_of_Heads ;AN000;now [temp_h];AX = cyliner, dx = head
;J.K. At this moment, we assume that Temp_h = 0, AX <= 1024, DX <= 255
	MOV	DH,DL
;
; Issue one read request.  ES:BX have the transfer address, AL is the number
; of sectors.
;
	MOV	CL,6
	SHL	AH,CL			;Shift cyl high bits up
	OR	AH,BL			;Mix in with sector bits
	MOV	CH,AL			;Setup Cyl low
	MOV	CL,AH			;Setup Cyl/high - Sector
	mov	bx,di			;Get back offset
	MOV	DL,cs:Drive_Number    ;Get drive
	mov	ax,si			;Get number of sectors to read (AL)

	MOV	AH,2			;Read
	push	ax			;Save length of read
	push	di
; Issue one read request.  ES:BX have the transfer address, AL is the number
; of sectors.
	INT	13H
	pop	di
	pop	ax
	pop	cx			;Get retry count back
	jnc	Read_OK
	mov	bx,di			;Get offset
	xor	ah,ah
	push	cx
	mov	dl,cs:Drive_Number
	push	di
	int	13h
	pop	di
	pop	cx
;	 loop	 Try_Read		 ;AC000;
       ;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx error
	dec	cx			;AN000;
	jz	Read_Error		;AN000;
	jmp	Try_Read		;AN000;
Read_Error:				;AN000;
	jmp	ErrorOut		;AN000;

Read_OK:
	xor	ah,ah			;Mask out read command, just get # read
	sub	cs:Sector_Count,ax	;Bump number down
	jz	Read_Finished
	add	cs:Start_Sector_L,ax	;AC000;Where to start next time
	adc	cs:Start_Sector_H, 0	;AN000;
	xor	bx,bx			  ;Get number sectors read
	mov	bl,al
	mov	ax,cs:Sector_Size	;Bytes per sector
	mul	bx			  ;Get total bytes read
	add	di,ax			  ;Add it to offset
	jmp	DODIV
Read_Finished:
	RET

subttl	GET NEXT FAT ENTRY
page
;***********************************************************************
;  GET_NEXT_FAT_ENTRY
;***********************************************************************
;
; Notes:
;
;   Given the last cluster found, this will return the next cluster of
;   IBMBIO. If the last cluster is (F)FF8 - (F)FFF, then the final cluster
;   of IBMBIO has been loaded, and control is passed to GOTO_IBMBIO
;   MSLOAD can handle maximum FAT area size of 64 KB.
;
; Input:
;
;    cs:Last_Found_Cluster
;    cs:Fat_Size
;
; Output:
;
;   cs:Last_Found_Cluster (updated)
;
; Calls:  Get_Fat_Sector
;-----------------------------------------------------------------------
; Get Last_Found_Cluster
;
; IF (16 bit FAT)
;    {IF (Last_Found_Cluster = FFF8 - FFFF)
;	 {JMP GOTO_IBMBIO}
;     ELSE
;	{Get offset by multiply cluster by 2}
;
; ELSE
;    {IF (Last_Found_Cluster = FF8 - FFF)
;	 {JMP GOTO_IBMBIO}
;     ELSE
;	{Get offset by	- multiply cluster by 3
;
;	 Rotate right to divide by 2
;
;	 IF (CY set - means odd number)
;	    {SHR 4 times to keep high twelve bits}
;
;	 ELSE
;	    {AND with 0FFFh to keep low 12 bits}
;	}
;    }
;
;
;----------------------------------------------------------------------
Get_Next_FAT_Entry:

	push	es				;AN000;
	mov	ax, cs:FAT_Segment		;AN000;
	mov	es, ax				;AN000; es-> Fat area segment
	assume	es:nothing

	mov	cs:EOF,End_Of_File		;Assume last cluster
	mov	ax,cs:Last_Found_Cluster	;Get last cluster
	cmp	cs:Fat_Size,FAT12_bit
	jne	Got_16_Bit
	mov	si, ax				;AN000;
	shr	ax, 1				;AN000;
	add	si, ax				;AN000; si = ax*1.5 = ax+ax/2
	call	Get_Fat_Sector			;AN000;
	jne	Ok_cluster			;AN000;
	mov	al, byte ptr es:[bx]		;AN000;
	mov	byte ptr cs:Temp_cluster, al	;AN000;
	inc	si				;AN000;
	call	Get_Fat_Sector			;AN000;read next FAT sector
	mov	al, byte ptr es:[0]		;AN000;
	mov	byte ptr cs:Temp_cluster+1, al	    ;AN000;
	mov	ax, cs:Temp_cluster		    ;AN000;
	jmp	short Even_Odd			;AN000;
Ok_cluster:					;AN000;
	mov	ax, es:[bx]			;AN000;
Even_Odd:					;AN000;

;	 xor	 bx,bx
;	 mov	 bl,3				 ;Mult by 3
;	 mul	 bx
;	 shr	 ax,1				 ;Div by 2 to get 1.5
;	 mov	 si,ax				 ;Get the final buffer offset
;	 mov	 ax,[si]+8000h			 ;Get new cluster

	test	cs:Last_Found_Cluster,1 	;Was last cluster odd?
	jnz	Odd_Result			;If Carry set it was odd
	and	ax,0FFFh			;Keep low 12 bits
	jmp	short Test_EOF			;

Odd_Result:
	 mov	cl,4				;AN000;Keep high 12 bits for odd
	 shr	ax,cl
Test_EOF:
	 cmp	 ax,0FF8h			 ;Is it last cluster?
	 jae	 Got_Cluster_Done		 ;Yep, all done here
	 jmp	short Not_Last_CLuster

Got_16_Bit:
	shl	ax,1				;Multiply cluster by 2
	mov	si,ax				;Get the final buffer offset
	call	Get_Fat_Sector			;AN000;
	mov	ax, es:[bx]			;AN000;
;	 mov	 ax,[si]+8000h			;Get new cluster
	cmp	ax,0FFF8h
	jae	Got_Cluster_Done

Not_Last_Cluster:
	mov	cs:EOF,not END_OF_FILE		  ;Assume last cluster

Got_Cluster_Done:
	pop	es
	ret


Get_Fat_Sector	proc	near
;Function: Find and read the corresponding FAT sector into ES:0
;In). SI = offset value (starting from FAT entry 0) of FAT entry to find.
;     ES = FAT sector segment
;     cs:Sector_Size
;Out). Corresponding FAT sector read in.
;      BX = offset value of the corresponding FAT entry in the FAT sector.
;      CX destroyed.
;      Zero flag set if the FAT entry is splitted, i.e. when 12 bit FAT entry
;      starts at the last byte of the FAT sector.  In this case, the caller
;      should save this byte, and read the next FAT sector to get the rest
;      of the FAT entry value.	(This will only happen with the 12 bit fat).

	push	ax			;AN000;
	push	si			;AN000;
	push	di			;AN000;
	push	dx			;AN000;
	xor	dx, dx			;AN000;
	mov	ax, si			;AN000;
	mov	cx, cs:Sector_Size	;AN000;
	div	cx			;AN000;ax = sector number, dx = offset
	cmp	ax, cs:Last_Fat_SecNum	;AN000;the same fat sector?
	je	GFS_Split_Chk		;AN000;don't need to read it again.
	mov	cs:Last_Fat_SecNum, ax	;AN000;
	push	dx			;AN000;
	xor	dx, dx			;AN000;
	add	ax, cs:Hidden_Sectors_L ;AN000;
	adc	dx, cs:Hidden_Sectors_H ;AN000;
	add	ax, cs:Reserved_Sectors ;AN000;
	adc	dx, 0			;AN000;
	mov	cs:Start_Sector_L, ax	;AN000;
	mov	cs:Start_Sector_H, dx	;AN000;set up for Disk_Read
	mov	cs:Sector_Count, 1	;AN000;1 sector
	xor	di, di			;AN000;
	call	Disk_Read		;AN000;
	pop	dx			;AN000;
	mov	cx, cs:Sector_Size	;AN000;
GFS_Split_Chk:				;AN000;
	dec	cx			;AN000;now, cx= sector size - 1
	cmp	dx, cx			;AN000;if the last byte of the sector, then splitted entry.
	mov	bx, dx			;AN000;Set BX to DX
	pop	dx			;AN000;
	pop	di			;AN000;
	pop	si			;AN000;
	pop	ax			;AN000;
	ret				;AN000;
Get_Fat_Sector	endp			;AN000;


Errorout:				;AN000;
	push	cs			;AN000;
	pop	ds			;AN000;
	mov	si, offset Sysmsg	;AN000;
	call	write			;AN000;
;SB34LOAD001****************************************************************
;SB  Wait for a keypress on the keyboard. Use the BIOS keyboard interrupt.
;SB	2 LOCS

	xor	ah,ah
	int	16h			;read keyboard
;SB34LOAD001****************************************************************

;SB34LOAD002****************************************************************
;SB  We have to restore the address of the original rom Disk parameter table
;SB  to the location at [0:DSKADR].  The address of this original table has been
;SB  saved previously in 0:Org_Rom_DiskTable and 0:Org_Rom_Disktable+2.
;SB  After this table address has been restored we can reboot by
;SB  invoking the bootstrap loader BIOS interrupt.

	xor	bx, bx
	mov	ds, bx
	les	bx, dword ptr ds:Org_Rom_DiskTable
	mov	si, DSKADR
	mov	word ptr ds:[si], bx	;restore offset
	mov	word ptr ds:[si+2], es	;restore segment
	int	19h			;reboot
;SB34LOAD002****************************************************************

Write	proc  near			;show error messages
;In) DS:SI -> ASCIIZ string.

	lodsb				;AN000;
	or	al, al			;AN000;
	jz	Endwr			;AN000;
;SB34LOAD003****************************************************************
;SB  Write the character in al to the screen.
;SB    Use Video service 'Write teletype to active page' (ROM_TELETYPE)
;SB    Use normal character attribute
	mov	ah, ROM_TELETYPE
	mov	bl, 7			;"normal" attribute ?
	int	10h			;video write
;SB34LOAD003****************************************************************
	jmp	Write			;AN000;
Endwr:					;AN000;
	ret				;AN000;
Write	endp
;

;include MSbtmes.inc	       ;AN000;
include MSbio.cl1	       ;AN001;

Relocate_Length   equ  $ - start
Total_Length label byte
LoadBIO_Size	equ	$ - Start

cseg	ends
	end	start
