; Boot Record for the MOOSE.
; Copyright (c) François-René "Faré" Bân Rideau Dang-Vu 1994

; Read the info/BIOS-API.nfo file

; * Load the remaining of the boot sequence and jumps to it.
; * Leaves space for DOS data fields or a partition table, so the sector
;  can be the first of a FAT filesystem.
; * In case of failure, the user must reboot the machine (?)
;  [[ If enough space, get a parameter line from the keyboard, main memory
;  (with checksum) or the sector itself ]]

; * Get main and extended memory size from BIOS
; * Loads the loader itself as a .COM file on a segment as high as possible,
;  whose length is LOADER_SEG_SIZE.
; * save it in the stack and

; * All that in pure 16 bit code, as the BIOS (real or emulated) may
; or not be 32-bit clean.


; Greatly inspired from the Linux boot sector by Linus Torvalds
; (boot/bootsect.S in the Linux 1.0 sources), and the sample boot sector
; included in the FDFORMAT utility by Christoph H. Hochstätter
; (fdboot.asm in the fdformat 1.18 package); also disassembled a MS-DOS 5
; boot sector.
; other info from various DOS books (only one had complete useful information,
; but I don't remember which; it was neither the Bible, nor Duncan's book).

Ideal	; I can't bear Microsoftish things.
P8086

Include "../../../M_Magic.h"

;;; Defines that may change.
SYS_SIZE	 =  07C0h	; Total system (loader+api+kernel) size in para
				; For use by LILO or LOADLIN.
LOADER_SEG_SIZE	 =  0F00h	;(60KB) ; size in para for the loader segment.
				; here I assumed there were more than 128 KB of
				; main memory :-)
LOADER_SECTS	 =  4			; size of the loader in sectors.
LOADER_OFFSET	 =  100h		; load the loader as a .COM file.
KERNEL_SECTS	 =  15			; Size of Kernel+API to load.
CONTIGUOUS	 =  1			; put API+Kernel just after previous.
LOAD_ARG	 =  ('O'+'S'+'L')*256	; just scratch.
__OEM_ID__	EQU 'MOOSE-01'		; nice name, isn't it ?
__DISK_TYPE__	EQU <1440KB_Floppy>	; also (HIDDEN_)1722KB_floppy


;;; Defines that won't change
LOADER_STACK_TOP =  LOADER_SEG_SIZE*16+offset M_LenFDREAD
				; SP in the same segment.
BOOT_OFFSET	=	7C00h		; Where the BIOS loads the boot code
LOG_SECT_SIZE	=	9		; Size of sectors (low-level standard)
SECT_SIZE	=   1 shl LOG_SECT_SIZE	; 512



ifidn __DISK_TYPE__,<1440KB_Floppy>

;;; Defaults for a standard 1.4MB bootdisk
RESERVED_SECTS	=	1+LOADER_SECTS	; loader is in reserved sectors.

D_T_NUM_SECT	=	2880		; 1.4 MB floppy
SECT_P_CLU	=	1		; we use only a few files, so
					; let's reduce the FAT overhead.
NUMBER_FATS	=	2		; Macro-Shit DOS needs 2
ROOTDIR_ENTRIES	=	0E0h		; 32, 2 sectors
MEDIA_DESCRPTR	=	0F0h		; High Density Floppy
SECTS_PER_FAT	=	9		; Compute yourself !
SECTS_IN_TRACK	=	18		; Yes, sir
NUMBER_HEADS	=	2		; 2 sides !
D_HIDDEN_SECTS	=	0		; no partitioning on floppies
LOAD_MODE	=	'D'		; see below
COMMANDLINE_OFS =	8192


elseifidn __DISK_TYPE__,<HIDDEN_1722KB_Floppy>
;; (1722-1440)=282, so that VirusMOOSE shouldn't be larger than that.
;; That's 564 sectors, including all the Windows worming code !
;; Of course, we can compress.
;;   The problem is with the user seeing what's happening, so we must go
;; multithreading real soon, so we cangive the hand back to the user;
;; then, if a reboot is requested, we run the bootstrap loader in
;; VM86 mode, with a no I/O mode, until we get some int 13h call with the
;; right value [Argh ! The BIOS uses hardware interrupts, so that'll be
;; difficult to tune]
endif
;;;

;;; Automatic definition after the remaining stuff.
if D_T_NUM_SECT LT 10000h
  T_NUM_SECT	=	D_T_NUM_SECT
  ;; Linux-ish Command-line interface
  ;; D_T_NUM_SECT	=	(0A33Fh OR (COMMANDLINE_OFS SHL 16))
else
  T_NUM_SECT	=	0
endif


; LOAD_MODE is:
; 'D' for loading a DOS file as the API+Kernel
; 'H' for using a 1722 KB disk's hidden sectors as the Loader+API+Kernel
; 'C' for using contiguous directories just after the boot record and the
;     loader itself.
; 'L' is set directly by the loader.
if CONTIGUOUS
 RESERVED_SECTS	=	1+LOADER_SECTS+KERNEL_SECTS
 LOAD_MODE	=	'C'
 LOAD_ARG	=	1+LOADER_SECTS
endif

;;; CUSTOM:
LOAD_MODE = 'C'
;RESERVED_SECTS = 36
NUMBER_FATS = 2
SECTS_PER_FAT = 2
SECT_P_CLU = 8
ROOTDIR_ENTRIES = 32

macro	jmps	dest
	jmp	short dest
endm


DEBUG_INT_13 = 0
IF DEBUG_INT_13

DEBUGINT = 15h
Macro	BreakPoint
	;push ax
	;mov ax,00FFh
 ;Int DEBUGINT
	;pop ax
EndM	BreakPoint
P286N
Macro	INT_13
   ;; Debug code
	;pusha
	;les di,[dword 13h*4]
	;call PutAddr
	;call CRLF
	;popa
   ;; Do call
	;;int 13h	; usual definition
	pushf
	cli
	call [dword 13h*4]
EndM	INT_13

ELSE
Macro	BreakPoint
EndM	BreakPoint
Macro 	INT_13
	Int 13h
EndM	INT_13
ENDIF

model tiny
Segment	BOOTSEG page public 'bootrec'
	assume cs:BOOTSEG,ds:BOOTSEG
		org	0h	; actually, BIOS jumps to 0:7C00, but I could
				; not link properly without putting org 0h for
				; a .BIN or org 100h for a .COM. Thus, Beware
				; to add BOOT_OFFSET to every reference in this
				; segment.

Proc		boot	near
		cld			; let's use this opcode space !
		jmps	over_tables	; leave space for DOS disk info.

;;; DOS info. table
		org 03h
OEM_Id		db	__OEM_ID__	; 03h - Whatever name you like.
		org 0Bh
bytes_per_sect	dw	SECT_SIZE	; 0Bh - 512 is the low-level standard.
sectors_per_clu	db	SECT_P_CLU	; 0Dh - do not fragment too much.
reserved_sect	dw	RESERVED_SECTS	; 0Eh - boot record is among them.
num_FATs	db	NUMBER_FATS	; 10h
entries_rootdir	dw	ROOTDIR_ENTRIES	; 11h
total_num_sect	dw	T_NUM_SECT	; 13h - 1.4 MB.
media_descr	db	MEDIA_DESCRPTR	; 15h - 3.5" DS HD floppy.
sect_per_FAT	dw	SECTS_PER_FAT	; 16h - follows from the above
sect_per_track	dw	SECTS_IN_TRACK	; 18h - number of sectors per track
num_heads	dw	NUMBER_HEADS	; 1Ah - number of heads
hidden_sect	dd	D_HIDDEN_SECTS	; 1Ch - number of hidden sectors.
d_total_n_sect	dd	D_T_NUM_SECT	; 20h - defined for HD if > 65535
;		dw	0		; ??? dunno if needed. MS has it.

;;; MOOSE Loading Parameters
		org	24h		; This won't budge.
LoadMode	db	LOAD_MODE	;
LoadArg		dw	LOAD_ARG	;
BIOS_DL		db	00h		; On what disk is the loader ?

; Here is a list of BIOS parameters for sectors to load, ended with
;; an entry where AL=0 (DH and CX will be loaded)

;; First entry
BIOS_CX		dw	0002h		; CH:cyl&0xFF, CL:sect+((cyl>>8)<<6)
BIOS_AL		db	LOADER_SECTS	; Int 13h parameters to load the LOADER
BIOS_DH		db	00h		; head
;; End Marker
		dd	0
		dd	3 dup (?)	; up to seven chunks, all in all.
;;; Strings

sBooting	db	13,10,'Booting'
;		db	' '
;sMOOSE		db	'MOOSE'
sdots		db	'.'
		db	0
;sReboot 	db	13,10,'Hit any key to reboot',0
;;; Globals
;;_Putc		dw	offset cPutc + BOOT_OFFSET
;;Putc		equ	[_Putc+BOOT_OFFSET]
Putc		equ	cPutc
Macro	PUTC	char
	ifnb	<char>
	  mov	al,char
	endif
	call	Putc
EndM	PUTC

; Notes:
; * hidden_sect is number of sectors on physical disk before boot sector.
; * d_total_n_sect is used for partitions with more than 65535 sectors;
;  total_num_sect is then null. For some hard disks, set it even if <10000h.


;;;;;;;;;;;;;;;;;;;;;;;;;; Actual boot code ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

  over_tables:		; interrupts are disabled just in case of.

   ;;; First of all, Init cs and ds to 0000h
		;PUTC	'X'
		xor	ax,ax
		mov	ds,ax
		push	ax
		mov	bx,offset Here_I_am + BOOT_OFFSET
		push	bx
		retf


Proc		Here_I_am	near
   ;;; Show that we got there.
	; Int 10h doesn't work under console mode dosemu, but
	; works under tty mode dosemu, because of VGA ROM !!!
	; A 'RETF' only ROM has it work (without cursor, but it works !)

	; Init text mode, cursor appearance, cursor position
	if 0
		;PUTC	'Y'
		mov	ax,0003h	; 80x25, 16 colors
		int	10h
		mov	ah,01h		; cursor appearance
		mov	cx,0B0Ch
		int	10h
		mov	ah,02h		; cursor position in page 0
		mov	bh,00h
		mov	dx,0000h
		int	10h
	endif
		mov	si,offset sBooting + BOOT_OFFSET
		call	Puts

   ;;; Init space for .COM loader.
	; Get Top of conventional memory in AX
		int	12h
		mov	cx,0406h		; put size in para into ax
		shl	ax,cl
		sub	ax,LOADER_SEG_SIZE	; get LOADER SEG address.
		mov	es,ax

	if 0
	;;; allow debugging
		;mov [word ds:DEBUGINT*4],offset NewIntDebug-M_OFFSET_FDREAD
		;mov [word ds:DEBUGINT*4+2],es
	endif

	; Init stack for .COM loader
		cli				; DI, for 8088's
		mov	ss,ax
		mov	sp,LOADER_STACK_TOP
		sti				; EI back.
	; push near return address to 0 & put "INT 19h" reboot instruction at 0
	; so that retn LOADER termination will reboot the computer
		cld	;(just in case int 12h destroyed it)
		xor	di,di
		push	di
		mov	ax,19CDh
		stosw
	; Init command line arguments
		mov	[byte es:80h],99h	; line length >= 128, hehe !
		mov	si,BOOT_OFFSET+02h	; move the DOS info table
		mov	cx,3Fh			; move 63 words (=126 bytes)
		rep	movsw			; di=2 already

   ;;; Install the FDRead utility.
	; save the old int 13h handler address
		mov	ax, [word 13h*4]
		mov	[old13_ofs+ BOOT_OFFSET], ax
		mov	ax, [word 13h*4+2]
		mov	[old13_seg+ BOOT_OFFSET], ax
	; move the new handler to its working address (top of memory)
		mov	si, offset NewInt13h + BOOT_OFFSET
		mov	di, LOADER_STACK_TOP
		mov	cx, LenFDREAD	; I could't divide it by 2 !!!
		cld		; (int 13 and int 10 may have spoilt it)
		rep	movsb		; so no movsw
	; write the new address
		mov	[word 13h*4], LOADER_STACK_TOP
		mov	[word 13h*4+2], es

   ;;; Load the rest of the loader into the .COM segment.
     ;* Note that if we use less than say 62 KB, that memory top is divisible
     ;* by 64KB, and that BIOS hook programs use less than 2K, we won't cross
     ;* a DMA 64KB boundary, so that everything is fine
	; Reset the Floppy disk controller (MS and linux do it, so I do too !)
		xor	ax,ax
		mov	dl,[BIOS_DL+BOOT_OFFSET]
		INT_13
	; Init the buffer to the .COM file.
		mov	bx,0100h	;(es:bx points to the .COM entry point)
	; Push it as far return address (so that retf'll later jumps into it)
		push	es
		push	bx
	; Init registers for loading the LOADER (hehe)
		mov	si,offset BIOS_CX+BOOT_OFFSET
	; Do Load the LOADER, chunk by chunk

Do_Load:	mov	al,'.'		; print that a chunk is being processed
		call	Putc
		cld			; just in case
		lodsw
		mov	cx,ax
		lodsw
		mov	dh,ah
		mov	dl,[BIOS_DL+BOOT_OFFSET]
		or	al,al
		jz	Done
		push	si
		push	ax
		mov	ah,2
		INT_13		; no error checking: if the boot sector was
				; successfully loaded, why should an I/O
				; problem occur ?
		pop	ax		; length
		pop	si
		;xor	ah,ah		; not needed: SECTOR_SIZE >= 256
		mov	cl,LOG_SECT_SIZE ; sector to bytes
		shl	ax,cl
		add	bx,ax
		jmps	Do_Load

	; jump into it
Done:
	;	xor	ax,ax		; Init .COM registers !
	;	mov	cx,bx
	;	dec	ch
	;	xor	bx,bx
	;	xor	dx,dx
	;	xor	si,si
	;	xor	di,di

		mov	cx,16
		mov	di,100h
		push	es
		pop	ds
		retf

;;; DIE for debugging
; die:		call	Beep
;		jmps	die

EndP		Here_I_am
;;;;;;;;;;;;;;;;; Int 13h handler from the FDFORMAT package ;;;;;;;;;;;;;;;;;;;

align 2
Proc		NewInt13h	far

M_OFFSET_FDREAD	=	$-LOADER_STACK_TOP
; hack, hack, hack, hack and hack...

   ;;; Only modify behavior for functions Read(2), Write(3), Verify(4), when
   ;;; a floppy is used.
IF 0
		push	es
		push	ax
		mov	ax,0B800h
		mov	es,ax
		mov	[word es:0],0F40h
		pop	ax
		pop	es
ENDIF
		BreakPoint
		;call	DumpRegs
		or	ah,ah		; reset ?
		jz	donothing	; yes: exit
		cmp	ah,4		; above Read, Write, Verify ?
		ja	donothing	; No: exit
		cmp	dl,3		; floppy ?
		ja	donothing	; No: exit
   ;;; Save registers
		BreakPoint
		push	ax	; request
		push	bx	; buffer
		push	ds	; user's data seg
		xor	bx,bx
		mov	ds,bx
   ;;; Disk-Parameter Table in DS:BX, offset 4: max. num of sectors per track
		lds	bx,[ds:78h]
		mov	al,[ds:bx+4]
		mov	[cs:oldsec-M_OFFSET_FDREAD],al	; old in oldsec
		mov	[byte ds:bx+4],1Bh		; new value: 25
   ;;; Do not modify track 0
		BreakPoint
	;	or	ch,ch			;Track 0 ?
	;	jz	exit			;Yes, exit
   ;;; Restore registers
		pop	ds	; user's ds
		pop	bx	; buffer
		pop	ax	; request
		BreakPoint
   ;;; call the old Int 13h handler.
		push	ax	; save request
		pushf		; push flags to call an interrupt routine
		push	cs
		call	near jmp_old13
   ;;; On error, analyze.
		jnc	okexit		;No problem, so exit
		pop	ax		;restore ax
		push	ds		;save ds & bx
		push	bx
		mov	bx,40h		;init ds to BIOS-Data Segment...
		mov	ds,bx
		mov	bl,90h		;Beginning of the Drive-Table
		add	bl,dl		;Offset des Laufwerks (?) ; dl=disk#
		cmp	ch,43			;Track>43 ?
		ja	nodstep			;Ja,dann niemals DSTEP
		xor	[byte ptr ds:bx],20h	;invert the Stepper Bit
		jmp	short stepend		;End the Stepping
nodstep:	and	[byte ptr ds:bx],0dfh	;Kein Double-Stepping
stepend:	pop	bx			;restore ds, bx
		pop	ds
		jmps	endrout2		;Routine zu Ende

exit:		pop	ds			;restore ds,bx,ax
		pop	bx
		pop	ax
endrout2:	pushf
	;;; call old int 13h handler
endrout:	push	cs
		call	jmp_old13
		;was: call [dword cs:offset old13-M_OFFSET_FDREAD]
okexit:	
	;;; restore max. sects/track
		push	bx
		push	ds
		xor	bx,bx
		mov	ds,bx
		lds	bx,[ds:78h]	;Disk-Parameter Table in DS:BX
		mov	[byte ds:bx+4],1Bh
		org	$-1		; hack, hack, hack
oldsec		db	?
		pop	ds
		pop	bx
	;;; Return to the caller
		inc	sp		;pop off the request
		inc	sp
		retf	2		;Exit, dropping original Flags

donothing:

;;; Hack, hack, hack
jmp_old13:	BreakPoint
		jmp	far 0:0
		org	$-4	; hacking, always hacking... :-)
label	old13		dword
	old13_ofs	dw	?	; Offset
	old13_seg	dw	?	; Segment


NewIntDebug:	iret
		align 2
FDRD_BLen	dw	LenFDREAD-4
		dd	MAGIC_MARKER

M_LenFDREAD	=	offset NewInt13h - offset $
LenFDREAD	=	offset $ - offset NewInt13h
EndP		NewInt13h



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Subroutines ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Proc	Puts
; Adapted from MicroSoft's own boot sector
; DS:SI=ASCIIZ string to print
; AX spoilt, SI points just after the string-ending zero, 7 put into BX.
	lodsb
	or	al,al
	jz	Return
	call	Putc
	jmps	Puts
EndP	Puts

IF 0
Proc	Beep
; echo a BEEP to the the Console
	mov	al,7
;	jmps	cPutc
EndP	Beep
ENDIF

; Outputs a character to console
; spoils AX and DI, keeps si
Proc	cPutc
	push	bx
	push	si
	push	di
	push	es
	mov	ah,0Eh	; function Teletype
	mov	bx,7	; attribute
	int	10h	; Video BIOS call
	pop	es
	pop	di
	pop	si
	pop	bx
Return:
	retn
EndP	cPutc

IF 0
Proc	CRLF	near
	mov	al,13
	call	Putc
	mov	al,10
	jmp	Putc
EndP	CRLF
Proc	Space
	mov al,' '
	jmp Putc
EndP	Space
ENDIF

IF 0
Proc	DumpRegs
	push ss
	push ds
	push es
	push cs
	pusha
	call CRLF
	mov di,sp
	sub di,10
	mov cx,20
	call PutWZone
	call CRLF
	popa
	pop es
	pop es
	pop ds
	pop ss
	ret
EndP

Proc	PutWZone
	mov ax,[es:di]
	inc di
	inc di
	push cx
	push di
	call	PutHexWord
	call	Space
	pop di
	pop cx
	loop PutWZone
	retn
EndP	PutWZone

Proc	PutZone
	mov al,[es:di]
	inc di
	push cx
	push di
	call	PutHexByte
	call	Space
	pop di
	pop cx
	loop PutZone
	retn
EndP	PutZone

Proc	PutAddr
;; Prints the contents of es:di
	push	di
	mov	ax,es
	call	PutHexWord
	mov	al,':'
	call	Putc
	pop	di
	mov	ax,di
;	jmp	PutHexWord
EndP	PutAddr

; Prints the contents of AX in hexa.
; saves AX, puts 4 into CL
Proc	PutHexWord	near
	xchg	al,ah
	call	PutHexByte
	xchg	al,ah
;	jmp	PutHexByte
EndP	PutHexWord

; Prints the contents of AL in hexa.
; saves AX, puts 4 into CL, 7 into BX
Proc	PutHexByte	near
	push	cx
	mov	cl,4
	rol	al,cl
	call	PutHexNybble
	rol	al,cl
	pop	cx
;	jmp	PutHexNybble
EndP	PutHexByte
; Prints to BIOS console the hexa nybble in low AL
; saves AX, puts 7 into BX
Proc	PutHexNybble	near
	push	ax
	and	al,0Fh
	add	al,'0'
	cmp	al,0Ah+'0'
	jb	do_putnyb
	add	al,'A'-'0'-0Ah
 do_putnyb:
	call	Putc
	pop	ax
	retn
EndP	PutHexNybble
ENDIF

IF 0
Proc	Getc
	xor	ah,ah
	int	16h
	retn
EndP	Getc

Proc	sPutc
	stosb
	retn
EndP	sPutc

; From Linux:
; * This procedure turns off the floppy drive motor, so
; * that we enter the kernel in a known state, and
; * don't have to worry about it later.
Proc	kill_motor
	push dx
	mov dx,03f2h
	xor al,al
	out dx,al
	pop dx
	retn
EndP	kill_motor
ENDIF
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; THE END: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;; Space for The Partition table (?)
;		org 1BEh
;		dw 40h dup (0)
;;; Linux kernel signature:
		org 1F1h		; just before 200h (=512)
loadersects	db	LOADER_SECTS
root_flags	dw	1
kernel_size	dw	SYS_SIZE	; size in para for LILO/LOADLIN
swap_dev	dw	0
ram_size	dw	0
vid_mode	dw	-1
root_dev	dw	0200h
;;; Bootable sector signature:
boot_flag	dw	0AA55h		; beware the order: 55,AA

EndP boot
;;; The boot sector is exactly 200h (512) bytes long.
EndS BOOTSEG

End boot
