; BOOTEASY, version 1.7.
; Developed from BOOTANY, with many improvements.
; The main idea was to simplify the installation
; procedure and to remove unnecessary partition type switching.
; Second hard disk switching added.
; Author: Serge Vakulenko, <vak@kiae.su>

F1_scancode     equ     59		; scancode of 'F1' key
Enter_scancode	equ	28		; scancode of 'Enter' key
Timeout		equ     5		; wait up to 5 seconds for reply

StdBase		equ     7c00h		; address where DOS loads us
Base		equ	600h		; address where we rewrite itself
Partab		equ	1beh		; partition table offset
NumDisks	equ	475h		; number of disk drives (BIOS data area)

BootIndicator   equ	0		; partition record: boot indicator
BeginHead       equ	1		; first partition sector: head number
BeginSector     equ	2		; sector and cylinder
SystemId        equ	4		; system type

; -------------------------------

dummy		segment at 0
		assume  cs:dummy
		org	Base		; address where we rewrite itself
BaseEntry:
		org	StdBase		; address where DOS loads us
StdBaseEntry:
dummy		ends

; -------------------------------

code		segment 
		assume  cs:code, ds:code, es:code, ss:code
		org     0
Boot:
;
;               Setup the stack and segment registers
;
		xor	AX,AX		; Zero register
		mov	ES,AX		; ES := 0
		mov	DS,AX		; DS := 0
		mov     SS,AX           ; disables intrs up to the next command
		mov	SP,StdBase	; SP at 7c00
;
;               DOS loads this pgm at 0000:7C00. Any boot routine
;               we call also expects to execute there so the first
;               exercise is to move this code somewhere else.
;
		cld			; Clear direction
		mov	SI,SP		; Copy from 7c00h...
		mov	DI,Base		; ...to 600h...
		mov	CX,256		; ...512 bytes, or 256 words
		repne	movsw		; Move itself to new location

		jmp	FAR PTR BaseEntry+(Entry-Boot)

; -------------------------------
;
;               A valid function key was depressed (or defaulted)
;               Attempt to boot the corresponding partition.
;
Load:
		mov	DX,BP		; restore drive number
		pop	AX		; key '1'..'5'
		mov     Base+default,AL	; save function key number
		cmp	AL,'5'
		je	SwitchDrive
		mov	AH,16
		mul	AH		; AX = key * 16
		add	AX,Base+Partab-'1'*16 ; subtract '1'
		mov	SI,AX
;
;               Check if the partition is empty.
;
		cmp	BYTE PTR SystemId[SI],0
		je      Menu		; Empty - display menu again
		cmp	BYTE PTR SystemId[SI],05h
		je      Menu		; DOS extended type - never load

		mov     BYTE PTR BootIndicator[SI],80h ; Mark partition as bootable
		call	SaveBoot
;
;               Read in and validate the partition's boot sector.
;
		mov     DH,BeginHead[SI] ; head from partition table
		mov     CX,BeginSector[SI]
		jmp	loadboot
;
;               Read in the boot sector from second disk.
;
SwitchDrive:
		call	SaveBoot
		mov     CX,0001h	; cylinder 0, sector 1
		xor	DL,CL		; (DL ^= 1) switch drive
loadboot:
		mov	BX,StdBase	; ES already == 0
		mov     AX,0201h	; function, # of sectors
		int     13h		; read system boot record
		jc      Menu		; exit if error
		cmp     WORD PTR 510[BX],0aa55h ; test signature
		jne     Menu		; reprompt if invalid
;
;		Jump to secondary boot.
;		DL now contains boot disk number (80h or 81h);
;		ES:SI contains the address of partition table
;		entry to boot.
;
		jmp	FAR PTR StdBaseEntry

; -------------------------------
;
;		The main entry to the boot
;
Entry:
		cmp	DL,81h		; DL contains current drive number
		je      driveok		; is it valid?
		mov	DL,80h		; no - use the default value
driveok:	mov	BP,DX		; save the drive number
		inc	DX		; 80h -> 81h, 81h -> 82h
		xor	DL,80h+'3'	; 80h -> '2', 81h -> '1'
		mov	Base+diskNum,DL
;
;               Display the menu
;
Menu:
		mov     DI,Base+Partab	; set index
		mov     CX,4		; set loop count
		mov	Base+key,'1'	; set key number in message
		xor	DH,DH		; count of partitions
menuloop:
		mov     BYTE PTR BootIndicator[DI],CH ; Clear active flag
		mov	AL,SystemId[DI]
		cmp	AL,0		; unused partition?
		je	next
		cmp	AL,5		; extended DOS partition?
		je	next
		inc	DH		; increment partition count

		lea     SI,Base+FkeyMsg	; get msg addr
		call    Output

		lea	SI,Base+nameTable-2
nameloop:
		inc	SI
		inc	SI
		mov	BX,[SI]
		or	BH,BH
		je	endnameloop
		xor     BH,SystemId[DI]
		jne	nameloop
endnameloop:
		lea	SI,Base+namtab[BX]
		call	Output
next:
		add	DI,16		; next entry address
		inc	Base+key
		loop	menuloop

		cmp	BYTE PTR DS:NumDisks,2 ; is the second disk present?
		je	have2disks	; have disk 2

		lea     SI,Base+defaultMsg ; prepare 'Default' message
		or	DH,DH		; no disk 2; do we have valid partitions?
		jne	prompt		; several partitions, wait for reply

		int	18h		; no partitions, load ROM basic
		jmp	Menu		; repeat, if no ROM basic
have2disks:
		lea     SI,Base+FkeyMsg	; print 'F5'
		call    Output		; now SI points to "disk 2\nDefault..."
prompt:
		call    Output		; print 'Default' message
reprompt:
		xor     AH,AH		; GetTickCount
		int     1ah		; BiosTimerService
		mov     BX,DX           ; lo-order tick count
		add     BX,192*Timeout/10 ; timeout value in ticks
;
;               Get the reply
;
waitkey:
		mov     AH,1		; keyboard status
		int     16h		; keybd bios service
		mov     AH,0            ; GetTickCount
		jnz     reply		; jump if reply
		int     1ah		; BiosTimerService
		cmp     DX,BX           ; check for timeout
		jb      waitkey		; wait for scancode
loaddefault:
		mov     AL,Base+default	; prior system id
		jmp     testkey		; boot default system
reply:
		int     16h             ; AH=0, keybd bios service
		mov     AL,AH		; Copy to AL
		cmp	AL,Enter_scancode
		je	loaddefault
		add     AL,'1'-F1_scancode ; Turn into index
testkey:
		cmp     AL,'1'		; max Function key
		jb	reprompt	; Invalid code check
		cmp     AL,'5'		; max Function key
		jnbe    reprompt	; if not F1..F5, branch
		push	AX

		lea	SI,Base+newLine	; new line
		lea	BX,Base+Load
		push	BX		; call Output; jmp Load

; -------------------------------
;
;		Output line [SI] to screen, end of line marked with 80h
;
Output:
		cld			; reset direction flag
		lodsb			; load argument from string
		push	AX		; save byte
		and     AL,7fh		; insure valid character
		mov     AH,14		; write tty
		int     10h		; bios video service
		pop	AX		; restore byte
		test    AL,80h		; test for end of string
		jz      Output		; do until end of string
		ret			; return to caller

; -------------------------------
;
;		Save boot block default partition settings
;
SaveBoot:
		push	SI
		mov     AX,0301h	; write sector
		mov	BX,Base		; ES already == 0
		mov     CX,0001h	; cylinder 0, sector 1
		xor     DH,DH		; drive #, head 0
		int     13h		; replace boot record
		pop	SI
		mov     Base+default,'?' ; reset default
		ret

; -------------------------------

newLine		db	13,10+80h
FkeyMsg         db      13,10,'F'
key             db      '0 . . .',' '+80h,'disk '
diskNum		db	'1'
defaultMsg	db	13,10,10,'Default: F'
default         db      '?',' '+80h

nameTable	db      dos     -namtab,   1
		db      dos     -namtab,   4
		db      dos     -namtab,   6
		db      hpfs    -namtab,   7
		db      os2     -namtab,  0Ah
		db      unix    -namtab,  63h
		db      novell  -namtab,  64h
		db      novell  -namtab,  65h
		db      minix   -namtab,  80h
		db      linux   -namtab,  81h
		db      linux   -namtab,  82h
		db      linux   -namtab,  83h
		db      amoeba  -namtab,  93h
		db      freebsd -namtab, 0A5h
		db      bsdi    -namtab,  9fh
		db      pcix    -namtab,  75h
		db      cpm     -namtab,  52h
		db      cpm     -namtab, 0dbh
		db      venix   -namtab,  40h
		db      dossec  -namtab, 0F2h
		db      noname  -namtab,   0

namtab:
dos             db      'Do','s'+80h
hpfs            db      'Hpf','s'+80h
os2             db      'Os','2'+80h
unix            db      'Uni','x'+80h
novell          db      'Novel','l'+80h
minix           db      'Mini','x'+80h
linux           db      'Linu','x'+80h
amoeba          db      'Amoeb','a'+80h
freebsd         db      'FreeBS','D'+80h
bsdi            db      'BSD','i'+80h
pcix            db      'Pci','x'+80h
cpm             db      'Cp','m'+80h
venix           db      'Veni','x'+80h
dossec          db      'Dosse','c'+80h
noname		db	'?','?'+80h

used            equ     $ - Boot
		db      (1beh - used + 1) dup (0) ; error if code too big
		db      63 dup (0)	; clear rest of record
		dw	0aa55h		; magic

code		ends
		end
