; BOOTEASY, version 1.4.
; Developed from BOOTANY, with many imrovements.
; 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
		cli			; Disable interrupts
		mov	SS,AX		; SS := 0
		mov	SP,StdBase	; SP at 7c00
		sti			; Enable interrupts
;
;               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.
;
		test	BYTE PTR SystemId[SI],0ffh
		je      Menu		; Yes - display menu again

		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:
		mov	BP,DX		; DL contains current 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
		cmp	BH,SystemId[DI]
		jne	nameloop
		xor	BH,BH
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
		jnz     reply		; jump if reply
		xor     AH,AH		; GetTickCount
		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:
		xor     AH,AH		; read keyboard
		int     16h		; 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      type01-namtab, 1
		db      type01-namtab, 4
		db      type01-namtab, 6
		db      type07-namtab, 7
		db	type08-namtab, 8
		db      type09-namtab, 9
		db      type0A-namtab, 0Ah
		db      type63-namtab, 63h
		db      type64-namtab, 64h
		db      type64-namtab, 65h
		db      type81-namtab, 80h
		db      type81-namtab, 81h
		db      type82-namtab, 82h
		db      type93-namtab, 93h
		db      typeA5-namtab, 0A5h
		db      type9f-namtab, 09fh
		db      type75-namtab, 75h
		db      typedb-namtab, 52h
		db      typedb-namtab, 0dbh
		db      type40-namtab, 40h
		db      typeF2-namtab, 0F2h
		db      unknownPart-namtab, 0

namtab:
type01          db      'do','s'+80h
type07		db      'hpf','s'+80h
type08		db	'fs '
type09		db      'ai','x'+80h
type0A          db      'os','2'+80h
type63          db      'uni','x'+80h
type64          db      'novel','l'+80h
type81          db      'mini','x'+80h
type82          db      'linu','x'+80h
type93		db      'amoeb','a'+80h
typeA5          db      '386bs','d'+80h
type9f          db      'bsd','i'+80h
type75		db      'pci','x'+80h
typedb		db      'cp','m'+80h
type40		db      'veni','x'+80h
typeF2		db      'dosse','c'+80h
unknownPart	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
