!---------------------------------------------------------------------------
!! This is a bootblock to load an execute a standalone program from an
!! MSDOS filesystem on a floppy disk.
!!
!! The file loaded is called 'BOOTFILE.SYS' and is loaded at address $7C00.
!---------------------------------------------------------------------------
! The filename can be changed at install time by following the label
! boot_name, because the file is loaded at address $7C00 it can be the
! image of a boot block (eg LILO). It's also checked for the magic number
! associated with a Linux-8086 standalone executable and this is used if
! it's found.
!
! There are a number of assumptions made by the code concerning the layout
! of the msdos files system:
!
! 1) All of the first 12 bit FAT must be on the first track.
! 2) The FAT must be 12 bit or 16 bit. (known at install time)
! 3) The value of the hidden sectors field must be zero
!
! All these are true for mtools created floppies on normal PC drives.
!
! In addition this now has a compile time option for FAT16 partitions.
! TODO: Auto detect disk type
!       FAT32
!
!---------------------------------------------------------------------------
ORGADDR=$0500

use16

! Some configuration values
LOADSEG=	$7C0
export fatbits
fatbits=12		! Set to 12 or 16 (16 for LS-120 disks)

export heads
heads=0			! This can be 0,1 or 2. 0 is dynamic.

export harddisk
 if fatbits=12
harddisk=0
 else
harddisk=1		! Allow for hard disks, but will only work with 
 endif			! disks formatted >= MSDOS 4.0

!---------------------------------------------------------------------------
! Absolute position macro, fails if code before it is too big.
macro locn
  if *-start>?1
   fail! *-start>?1
  endif
  .blkb	?1 + start-*
mend

org ORGADDR
start:
include sysboot16.s

org dos_sysid
   .ascii "DOSFS"	! System ID

!---------------------------------------------------------------------------
! Data into the temp area, 30 clear bytes
org floppy_temp
root_count:	.blkw	1

!---------------------------------------------------------------------------
  locn(codestart-start)

  cld			! Assume _nothing_!
  mov	bx,#$7C00	! Pointer to start of BB.
  xor	ax,ax		! Segs all to zero
  mov	ds,ax
  mov	es,ax
  mov	ss,ax
  mov	sp,bx		! SP Just below BB
  mov	cx,#$100	! Move 256 words
  mov	si,bx		! From default BB
  mov	di,#ORGADDR	! To the correct address.
  rep
   movsw
  jmpi	cont,#0		! Set CS:IP correct.
cont:
  sti			! Let the interrupts back in.

! DONT Need to fix BPB for fd0 to correct sectors (Like linux bootblock does)
! as we only ever read one sector at a time.

! For each sector in root dir
! For each dir entry
! If entry name is == boot_name
! then load and run

  ! First dir = dos_fatlen*dos_nfat+dos_resv
  mov	ax,[dos_fatlen]
  mov	dl,[dos_nfat]
  xor	dh,dh
  mul	dx
  add	ax,[dos_resv]
  mov	di,ax
  ! DI is sector number of first root dir sector.

  mov	ax,[dos_nroot]
  mov	[root_count],ax
  add	ax,#15
  mov	cl,#4
  shr	ax,cl
  add	ax,di
  mov	bp,ax		! bp is first data sector.

nextsect:
  call	linsect
  mov	ax,#$0201
  int	$13
  jc	floppy_error

! ... foreach dir entry
  mov	si,bx
nextentry:

  ! TODO: Test attributes for !dir !label here ?

! test entry name
  push	si
  push	di
  mov	di,#boot_name
  mov	cx,#11
  rep
   cmpsb
  pop	di
  pop	si
  jne	bad_entry

  mov	ax,[si+26]	! Cluster number of start of file
  test	ax,ax		! Make sure we have a block.
  jnz	load_system

bad_entry:
  add	si,#32		! skip to next entry
  cmp	si,#512
  jnz	nextentry

  inc	di		! Load next sector
  sub	[root_count],#16
  jp	nextsect
  jmp	no_system

!---------------------------------------------------------------------------
! Convert a cluster number in DI into a CHS in CX:DX
linclust:
  mov	ax,di
  sub	ax,#2
  mov	dl,[dos_clust]
  xor	dh,dh
  mul	dx
  add	ax,bp		! Add sector number of first data sector.
  adc   dx,#0
  jmp	linsect2

!
! This function converts a linear sector number in DI 
! into a CHS representation in CX:DX
!
linsect:
  mov	ax,di
linsect1:
  xor	dx,dx
linsect2:
  ! Add partition offset in.
 if fatbits =16
  add   ax,[dos_hidden]
  adc   dx,[dos_hidden+2]
 endif

  div	[dos_spt]
  inc	dx
  mov	cl,dl		! Sector num
  xor	dx,dx		! Drive 0
 if heads =2
  shr	ax,#1		! Assume dos_heads == 2
  adc	dh,#0		! Head num
 endif
 if heads =0
  div	[dos_heads]
  xchg	dh,dl
 endif
  mov	ch,al		! Cylinder bits 0-7
 if heads =0
  sar   ax,#1		! Cylinder bits 8&9
  sar   ax,#1
  and   al,#$C0
  or    cl,al
 endif
 if harddisk
  mov	dl,[dos4_phy_drive]
 endif
  ret

!---------------------------------------------------------------------------
! Error processing.
no_system:
floppy_error:

  mov	si,#error_msg
no_boot:		! SI now has pointer to error message
  lodsb
  cmp	al,#0
  jz	EOS
  mov	bx,#7
  mov	ah,#$E		! Can't use $13 cause that's AT+ only!
  int	$10
  jmp	no_boot
EOS:
  xor	ax,ax
  int	$16
  jmpi	$0,$FFFF	! Reboot.

!---------------------------------------------------------------------------
! This loads the boot program 1 sector at a time, funny thing is it actually
! loads at exactly the same speed as loading a track at a time, at least on
! my slow old 286/8Mhz. Oh well, we need the space so if you're worried just
! tell superformat to give you an interleave of 2 (-i 2).
!

load_system:
 if fatbits =12
  push	ax		! Save cluster we're loading

! 1) Load FAT
  ! This assumes all of FAT1 is on the first track, this is normal
  mov	ax,[dos_fatlen]
  mov	ah,#2
  mov	bx,#fat_table
  mov	cx,[dos_resv]	! Fat starts past bootblock
  inc	cx
  xor	dx,dx		! Head zero
  int	$13
  jc	floppy_error
  pop	di		! Cluster to start load.
 else
  mov	di,ax
 endif

  mov	ax,#LOADSEG
  mov	es,ax

  ! load whole cluster
next_cluster:

  mov	si,[dos_clust]	! How big is a cluster
  and	si,#255
  call	linclust	! Find it's physical address

next:
  mov	ax,#$0201
  xor	bx,bx
  int	$13		! Load 1 sector at a time
  jc	floppy_error

  mov	ax,es
  add	ax,#512/16
  mov	es,ax

  cmp	cl,[dos_spt]	! Does cluster straddle tracks ?
  jnz	sectok
  mov	cl,#0
 if heads=2
  inc	dh
  cmp	dh,#2		! Does cluster straddle cylinders ?
  jnz	sectok
  xor	dh,dh
 endif
 if heads=0
  inc	dh
  cmp	dh,[dos_heads]	! Nb low byte only.
  jnz	sectok
  xor	dh,dh
 endif
  inc	ch
sectok:
  inc	cl
  dec	si
  jnz	next

  call	next_fat
  jc	next_cluster
  jmp	maincode

next_fat:
 if fatbits =12
  mov	ax,di
  shr	ax,#1
  pushf			! Save if odd number
  add	di,ax
  mov	di,fat_table[di]
  popf
  jnc	noshift		! Odd in high nibbles.
  mov	cl,#4
  shr	di,cl
noshift:
  and	di,#$FFF
  cmp	di,#$FF0	! FFF is EOF, clear carry flag.
  			! FF0+ are badblocks etc.
 endif

 if fatbits =16
  mov	ax,di
  ! load fat sector AH (if it's not already loaded)

  xchg	ah,al
  xor	ah,ah
  add	ax,[dos_resv]

  cmp	ax,[fatsect]
  jz	got_fsect
  mov	[fatsect],ax

  call	linsect1

  push	es
  mov	bx,#fat_table
  xor	ax,ax
  mov	es,ax
  mov	ax,#$0201
  int	$13 
  pop	es
  jnc	got_fsect
  br	floppy_error

got_fsect:
  ! Load di with cluster number in di
  and	di,#$FF
  shl	di,#1
  mov	di,fat_table[di]
  cmp	di,#$FFF0
 endif

  ret

!---------------------------------------------------------------------------
! File is now loaded, execute it.
maincode:
  mov	bx,#7
  mov	ax,#$0E + ':
  int	$10		! Marker printed to say bootblock succeeded.

  xor	dx,dx		! DX=0 => floppy drive
 if harddisk
  mov	dl,[dos4_phy_drive]
 endif
  push	dx		! CX=0 => partition offset = 0
  mov	si,[dos_spt]	! SI=Sectors per track

  mov	bx,#LOADSEG
  mov	ds,bx		! DS = loadaddress
  xor	di,di		! Zero
  mov	ax,[di]
  cmp	ax,#0x0301	! Right magic ?
  jnz	bad_magic	! Yuk ...
  mov	ax,[di+2]
  and	ax,#$20		! Is it split I/D ?
  jz	impure		! No ...
  mov	cl,#4
  mov	ax,[di+8]
  shr	ax,cl
impure:
  pop	cx		! Partition offset.
  inc	bx
  inc	bx		! bx = initial CS
  add	ax,bx
  mov	ss,ax
  mov	sp,[di+24]	! Chmem value
  mov	ds,ax

  ! AX=ds, BX=cs, CX=X, DX=X, SI=X, DI=0, BP=X, ES=X, DS=*, SS=*, CS=*

bad_magic:
  push	bx		! jmpi	0,#LOADSEG+2
  push	di
  retf

!---------------------------------------------------------------------------
! initilised data

fatsect:
  .word	0

error_msg:
  .asciz "\r\nError during initial boot\r\nPress a key:"

export boot_name
boot_name:
 .ascii "BOOTFILESYS"
name_end:
!        NNNNNNNNEEE

locn(510)
  .word $AA55	! This is a floppy so it should not need the magic _but_
  		! the debian MBR requires the magic even on floppies

fat_table:	! This is the location that the fat table is loaded.
		! Note: The fat must be entirely on track zero if 12 bit.
!---------------------------------------------------------------------------
