Oberon/BootManager.Asm

From Wikibooks, open books for an open world
Jump to navigation Jump to search
; Bluebottle Boot Manager
;
; Written by A.L. Fischer using Network Assembler syntax for use with
; the Bluebottle assembler PCAAMD64.Mod
;	PCAAMD64.Assemble BootManager.Asm ~
; This Boot Manager uses the int 13h extensions for
; accessing blocks in LBA mode in order to break the 8.4GB barrier,
; if that access method is supported by the BIOS and the hard drive,
; and uses the C-H-S disk access mode otherwise.
; In Oberon, what is otherwise named "sector" is called "block",
; in relation with LBA = Logical Block Address
; The 1st block of a volume is named MBR and that of a partition PBR.

; Reference: BIOS Enhanced Disk Drive Specification
;      Version 3.0 - Rev 0.9b - June 24, 1999. Phoenix Technologies Ltd.
;      http://www.t13.org/project/d1386r0.pdf
; http://web.inter.nl.net/hcc/J.Steunebrink/bioslim.htm

; 2007-04-13 afi Started
;	Adaptation from original BootManager.Asm
; 2007-06-24 afi completed first version
; 2007-11-12 afi converted text from Turbo to Network Assembler
; The date of the current version is located at bmdate and displayed
; in the title line of the boot menu at startup.
; --------------------
; Infrastructure and implementation procedure:
;
; 1 - edit this text
; 2 - assemble by executing: PCAAMD64.Assemble BootManager.Asm ~
;	to obtain BootManager.Bin
; 3 - Split BootManager.Bin into BootManagerMBR.Bin and BootManagerTail.Bin
; --------------------
; (0) central combined CHS / LBA support.
;       All of it must be located in block with LBA=0 (MBR)
; (1) added for warning when a partition has no valid PBR
; (2) added for using 1 or 2 digits depending on the # of partitions
;	Up to 12 partitions per volume can be handled
; (3) added for inspecting the graphic controller capabilities
;	incorporated to (15) - previously done with enter 'v'
;	procedure inspectvbe
;	VESA BIOS Extensions 2.0 (VBE)
;	http:www.geocities.com/SiliconValley/Park/8933.vesa.html
; (4) added for collecting disk drive information - proc driveinfo
; (5) added for displaying VBE information or not - now part of (15)
; (6) added for providing a better disk error diagnostic
;	as soon as the complete BootManager is loaded
; (7) added for scanning PCI bus
; (8) added for presenting a survey of installed drives
;       Nowadays justified by the appearance of external mobile drives
;       with USB or Firewire interface resulting in an ever changing
;       drive configuration
; (9) was diskette support, now removed
; (10) added for indicating which volume is currently explored (underline)
; (11) void
; (12) was diskette support, now removed
; (13) show drive interface: IDE, FDD, CD-ROM, USB when available
; (14) skip non-accessible (USB) drives
; (15) display PC hardware information - enter 'h' - inspectvbe/inspectpci
; (16) GUI text restructured - not marked in code
; (17) diskette support entirely removed. Presence of FDD now ignored.
;	In 2007, PCs are very seldomly equipped with a diskette drive.
;	All PCs, without exception support external USB devices having
;	many thousand times superior storage capacity.
;	If no FDD is installed, it should be disabled in the BIOS setup.
; (18) toggle between 25 (default) and 50 lines on screen - enter 'l'
; (20) announce OS start
; (21) display CPU information with cpuid
; (22) switch/swap the current MBR and that of another volume - enter 's'
; (23) add some persistance to the message line
; (24) display partition sizes
; (25) toggle store/do NOT store selected volume and partition - enter 'b'
;  Refer to Dual/Multi Booting With Vista - www.multibooters.co.uk/mbr.html
; --------------------
CPU P4
	bits 16
; define filler character
filler: equ 'U'	; Unused byte

; BIOS reads the first physical block (MBR) of the boot device and places
; it into memory at location
biosloc: equ 7c00h

; The initial part of the contained program (up to entry01) has the
; duty of freeing the area at biosloc by relocating it to memory
; at location 0000:0600 (newloc below).
; The rest of the program will read and place the PBR
; at the same location biosloc as if it had been read by the BIOS.

newloc: equ 0600h	; address of the area in which
;	the MBR will be relocated

MBRreloc: equ 0200h	; (22) address of the area in
;	which the MBR is temporarily saved for a possible swapping

CR:	equ 13	; Carriage Return 0ch
LF:	equ 10	; Line Feed 0ah
blocksize:	equ 512	; bytes per block
signID:	equ 0aa55h	; signature
bmLBA:	equ 1	; LBA of stage 2
; Changing bmLBA might be required to locate the bulk of the
; IPL bootmanager elsewhere than immediatly after the MBR
; as is suggested here with the value 1.

; The boot manager is split into two parts:
;	- the MBR of the volume: stage1 contained in block LBA=0
;	- the rest, comprising bmsize blocks, in a free area of the volume

bmsize:	equ 12	; stage2 size in blocks
; the block with LBA= 12 is used to memorize the last user selection 
; defaultd and defaultp
lbalen:	equ 21	; length of an lbalist entry
partlen:	equ 16	; length of a partition entry

; offset (in blocks) of the first partition with respect to the volume beginning
;partoffset:	equ 2048	; on a HD formatted with Vista
;partoffset:	equ 63	; on a HD or USB memory stick
;partoffset:	equ 32	; on a CD-ROM

maxretry:	equ 5	; max. number of read/write retries

; --------------------------------------------------------------------
; LBA 0 - MBR - Master Boot Record

	org 0
; WARNING: Do not alter dl until call inspectdisk is over
entry:	cli	; disable interrupts
; NOTE: No justification yet for the presence of this "cli" but
; in LILO one can read "NT 4 blows up if this is missing".
	xor ax, ax	; clear
	mov ss, ax	; set stack segment to 0000
	mov sp, biosloc	; set stack pointer
	sti	; enable interrupts
	push ax	;
	pop es	; es now 0000
	push ax	;
	pop ds	; ds now 0000
	cld	; clear direction flag
	mov si, biosloc + entry01	; set si
	mov di, newloc + entry01	; relocation address
	push ax	; 0000
	push di	; newloc + offset entry01
	mov cx, blocksize - (entry01-entry)	; set count
	rep movsb	; move MBR from 0000:7c00 to 0000:0600
	retf	; jump to 0000:newloc + entry01

entry01:	call savembr
	mov [newloc + currdrive], dl	; note from which drive it all started
	call inspectdisk

; read rest of boot manager - bmsize blocks in one operation
	mov bp, newloc + baseadd	; lba = 1 for accessing the 2nd block
	call readblock	; read all blocks in one go
	mov si, biosloc	; set si
	mov di, newloc + blocksize	; relocation address
	mov cx, bmsize*blocksize	; set count for bmsize blocks
	rep movsb	; move from 0000:7c00 to 0000:0800

; The area extending from entry to this point may be used to store data
; as soon as execution passed this point.
; Imperative condition: it must be at least 3*16 bytes long to contain
; 3 partition table entries of an extended partition - see at isext label
; --------------------------------

; At this point, the entire boot manager is loaded into memory (bmloaded).
; An eventual disk operation error can be diagnosed correctly and
; reported on the display console.

	mov [newloc + bmloaded], 1	; (6) set indicator to TRUE

; Collect the information on the installed drives (drive numbers ordered
; by index) for deciding what to tell the user about the environment
; Takes place only once at the very beginning
	call cpuinfo
	call driveinfo	; (4)
	jmp word newmbr

; Jump to the main processing located at the end of this block .

; All the procedures which appear below and up to the label newmbr
; must imperatively be located within the block with LBA=0 because
; they are all needed for getting the remaining blocks LBA=1 to bmsize

; ===== procedure: read a block with the specified LBA number (0)
; NOTE: (0) starts at label readblock. What comes before is used later
; use the LBA directly or convert that value to CHS
; in : bp = point to lba in table entry
; out : dl = drive number ; bx = data buffer address
readplus:

; has the function of tracing the partition types detected - optional
	call info	; info about the current entry, set bp

; Up to maxretry attempts, with at each failed attempt a disk reset.
; The registers are conditioned first to launch an extended read and
; if LBA mode is not supported, a standard read in prepared.
; A discriminator is also placed immediately before the int 13h instruction
; to trim the operation code in ah, al.
readblock:	mov di, maxretry	; number of tries

readtry:
; set up dap for an extended read/write 13h disk operation
; the Device Address Packet (dap) is constructed on the fly by pushing
; the parts on the stack.
; the structure of the dap is:
;dap	db 10h	; dap size (fixed)
;	db 0	; reserved (fixed)
;num	dw bmsize	; number of blocks to transfer (fixed)
;boff	dw biosloc	; address of transfer buffer - offset
;bseg	dw 0	; address of transfer buffer - segment
;sec0	dd ?	; starting absolute block address
;sec1	dd 0	; remaining 32 bits of address
; it is easiest to set up the dap first because the values to assign are
; available straight away.
; a volatile dap has the disadvantage that it must be rebuilt for a read retry

	mov ax, word [bp]	; dx:ax = lba number of block to read
	mov dx, word [bp+2]
; now leave dx:ax untouched until the next test of the lba indicator !
; NOTE:	pushd 0 is an "Illegal instruction" for tasm in 16-bit mode
	push byte 0	; remaining 16 bits
	push byte 0	; remaining 16 bits
; NOTE:	push dword [bp]	; starting absolute block address
; replaced by the next 2 lines because ax and dx are set already
	push dx
	push ax
	push es	; address of transfer buffer - segment
	push biosloc	; address of transfer buffer - offset
	push byte bmsize	; read bmsize blocks (fixed)
	push byte 10h	; 00h reserved - DAP is 16 bytes long (fixed)
	mov si, sp	; give access to the dap ds:si = dap

; test if the drive supports extended int 13h extensions, i.e. LBA mode
; and trim to standard read if lba is not supported
	cmp [newloc + lbaindic], 0	; test the lba indicator
	jne short uselba

; convert lba (dx:ax = lba number of block to read) to CHS values
; see http://homepage2.nifty.com/cars/misc/chs2lba.html
	mov bx, word [newloc + blocks]	; blocks per track (spt)
	div bx	; remainder in dx (lba MOD spt)
	inc dx	; correct block number
	mov cx, dx	; move it in cx
	xor dx, dx	; clear
; dx:ax = quotient of lba DIV spt
	mov bx, word [newloc + heads]	; number of heads
	div bx	; remainder in dx
; at this point, the register values are:
;   ax = cylinder number
;   dx = head number
;   cx = block number
; place the obtained values in the registers for the upcoming standard read
	mov ch, al	; lsb of cylinder number
	shl ah, 6	; 2 msb of cylinder number
	or cl, ah	; merge to form cylinder number
	mov dh, dl	; head number
	mov bx, biosloc	; data buffer offset
	mov ax, 0200h + bmsize	; function 02h, read bmsize blocks
	jmp short chsmode

uselba:	mov ah, 42h	; ready for extended read

chsmode:	mov dl, [newloc + currdrive]	; current drive number
	int 13h	; call BIOS
	mov [newloc + errcode], ah	; (6) stow the error code away
	lea sp, [si + 10h]	; load original offset value in sp
	jnc short int13ok	; carry flag not set: success, proceed

; failure is signalled by carry and ah value <> :, perform a disk reset
	xor ax, ax	; reset disk system
	int 13h	; call BIOS
	dec di	; does not clear carry
	jnz readtry	; retry counter > 0, retry
	cmp [newloc + bmloaded], 0	; (6)
	jne near readerr	; (6) display errormsg with error code
	mov si, newloc + errormsg
	call writestring	; issue msg - call WILAstring not
		; allowed yet (not yet in memory)
; when the attempt to read the rest of the boot manager fails, nothing else
; can be attempted except bringing the machine to a grinding halt !!!
hang:	jmp short hang	; halt the system !!! endless loop

int13ok:	mov di, biosloc + sign	; point to the signature
; For each block read note the signature
	mov ax, word [di]	; (1) get it
	mov [newloc + mbrsign], ax	; (1) make a note
; Later on if the block is a PBR its validity will be assessed.
good:	ret

; messages

errormsg:	db 10,'Disk error '	; must remain in the 1st block
errorgui:	db '??',CR,LF,0	; (6) errcode translated for GUI

currdrive:	db 0h	; current drive # as returned by BIOS in dl
lbaindic:	db 0	; lba indicator: 0 = lba not supported
mbrsign:	dw 0	; (1) mbr signature (55AA)
		; saved for TUI
; 3 fields storing data obtained by int 13h, funct 08h (get drive parameters)
; heads and blocks are used to convert an LBA value to CHS
cyls:	dw 0	; number of cylinders
heads:	dw 0	; maximum head number
blocks:	dw 0	; maximum block number
chainadd:	dd 0	; lba of chained extended
flag:	db 1fh	; flag/mask for testing bits in the BIOS
;		; shift status byte at address 0040h:0017h
mbrinproc:	db 0	; bit 0 = 1 new mbr being processed,
		;               force interaction
		; bit 7 = 1 write LBA 12 lock
		; after having swapped mbr
digindic:	db 2	; (2) no. of digits indic. (1 = one, 2 = two)
bmloaded:	db 0	; (6) bit 0 = 1 bootmanager loaded
errcode:	db 0	; (6) disk operation error code

; ===== procedure: write one character at the current cursor location and
; advance the cursor (display operating in text mode)
; on entry: al = character
; mod: none
writechar:
	push ax
;	push bx
; NOTE: commented because invalid in text mode. Found everywhere though.
; Setting the attribute in text mode is done as shown in proc setred
;	mov bx, 7	; screen attributes: white text on black bg
	mov ah, 0eh	; 
	int 10h	; display character on the screen
;	pop bx
	pop ax
	ret

; ===== procedure: write a zero-terminated string
; must be located in the MBR to be able to report a disk error (message
; labelled "errormsg") while reading the rest of the boot manager (LBA=1 ...)
; in : si = pointer to zero terminated string
; mod: al,si
writestring:
nxtchar:	lodsb	; get char of message
	cmp al, 0	; is end of message ?
	jz short finish	; yes
	call writechar	; display this character
	jmp nxtchar	; proceed to next character
finish:	ret

; ===== procedure: inspect the properties of the disk/diskette drives
; with the purpose of finding out if LBA is supported and of determining
; the disk geometry for converting an LBA value to a C-H-S equivalent.

; in : dl = drive number (00h is diskette; 80h, 81h, ... is internal or
; external drives, incl. USB flash memory and others)
; On the first call, the drive number is supplied in dl by BIOS
inspectdisk:

; find out if the drive supports extended int 13h extensions
	mov [newloc + lbaindic], 0	; clear lba indicator
	cmp dl, 80h	; is it a hard disk ?
	jb short params	; no, lba not applicable to diskette
	mov bx, 55aah	; condition next BIOS call
	mov ah, 41h	; check extension present
	int 13h	; call BIOS
	jc short params	; carry, LBA not supported
	cmp bx, signID	; confirm lba extensions present
	jne short params	; no
	test cx, 01h	; fixed disk access subset - is LBA ready ?
	jz short params	; no
	mov [newloc + lbaindic], cl	; save lba level indicator
	ret	; omit what follows

; get number of heads and blocks in view of a needed conversion
; from LBA to CHS in the procedure readblock
; the number of cylinders is evaluated for presenting on the display
; dl has still the correct value

params:	push es	; save to protect its value
	mov ah, 08h	; get disk drive parameters
	int 13h	; call BIOS
	xor ax, ax	; clear 16 bits
	mov al, dh	; max. # of heads (0-based)
	inc ax	; effective # of heads
	mov[newloc + heads], ax	; save it
	mov al, cl	; number of blocks per track (0 .. 63)
	and ax, 3fh	; mask cylinder bits
	mov [newloc + blocks], ax	; save number of blocks
	xchg ch, cl	; 8 MLB of number of cylinders
	shr ch, 6	; 2 MSB of do.
	inc cx
	mov [newloc + cyls], cx	; save number of cylinders
	pop es	; restore es
	ret

; ===== procedure: save the MBR just read in a save area (22)
savembr:
	mov si, biosloc	; 
	mov di, MBRreloc	; 
	mov cx, blocksize	; (22) set count
	rep movsb	; (22) save MBR
	ret

; code stretch used for testing and now commented out:
tracesec:
; test code: display the beginning of the block just read
;	mov cx, 40	;
;	mov si, biosloc
;disnxt:	lodsb	; get char of message
;	mov bh, al
;	call char2hex	; display this character
;	loop disnxt	; proceed to next character
;	ret

; The code between the labels newmbr and bmdate may be moved
; to a block with LBA > 0 when more space is needed for essential
; processing which must take place in the MBR (LBA = 0).

; Process the partition table entries of a new MBR
newmbr:
	call inspectmbr
	mov [newloc + digindic], 2	; assume 2
	mov si, newloc + lbaval	; point to lba in first table entry

; process the four partition table entries of the MBR now relocated
next2:	mov ebx, [si]	; note base lba of partition
	cmp ebx, byte 0	; is this entry empty ?
	je near donext	; yes, skip entry
	cmp [newloc + lbaindic], 0
	jne short lbaready	; ignore limit
	cmp ebx, 16450560	; 1024 * 255 * 63
		; max. lba value accessible with c-h-s addressing
	ja near donext	; cannot access > 8.4GB, skip entry
lbaready:	mov [newloc + baseadd], ebx	; save lba of extended partition
	mov [newloc + chainadd], ebx	; save lba of chained extended
	call addlba	; collect in list
	push si
	call readplus	; read the corresponding block
		; either of primary or extended partition
	cmp [bp-4], 05h	; is it for a DOS extended partition ?
	je short isext	; yes, 
	cmp byte [bp-4], 0fh	; is it for a WIN extended partition LBA ?
	jne near notext	; no, skip

; process 2 first table entries of an extended partition
; 1 - save these 2 entries in the 32 bytes at the beginning of newloc
; 2 - the first one points to a logical drive
; 3 - the second one, can be empty or point to the next extended partition
isext:	add word [newloc + lbaslot], byte lbalen	; next lba entry address

	jmp short nxtsect	; continue in next block

; Free space
	db 'UUUUUUUUUUUUUUUUU'	;17 bytes

; By design, the remaining data in block must imperatively be located here.
; NOTE: fillto was a macro used to force locating some values at the
; specified offset. The macro disppaeared but the offsets are imperative.
;	fillto 1a3h	; imperative location !!!
	db '*'	; Visible reference point
bmdate:	db '13.03.2008 - '

; By design, the remaining data in block must imperatively be located here.
;	fillto 1b0h	; imperative location !!!
bbmid:	db 'BBM '	; (12) unique 'BBM ' identifier
baseadd:	db bmLBA,0,0,0	; lba of block where the bulk
;	Field also stores the lba of an extended partition during the processing.

; Windows 2000, XP and Vista disk signature!
; When a drive has either of these OSes installed, the four bytes at offset
; 1b8h through 1bbh will/might be overwritten with that disk signature. One
; observes a high tendency for the 1st and 3rd and for the 2nd and 4th bytes
; to be the same, as in the examples: 96 e9 96 e9 or a8 e1 a8 e1

 ;	fillto 1b8h	; imperative location !!!
dsignat:	db 66h,0dh,67h,0dh,0,0	; Vista disk signature
; These four bytes at 1b8h MUST BE TAKEN OVER from the Windows Vista install.
; See: http://www.multibooters.co.uk/mbr.html
;	Dual/Multi Booting With Vista

; The partition table in the MBR has four 16-byte entries.
; The data in this area originates from:
;	either the MBR of the boot volume
;	or the MBR of the volume accessed by selecting another volume
;	in the list - when entering 'n' or 'p'.  Processing starts at label extract:

; When only the boot manager resides on a volume (thus not partitioned)
; this area should contain all 0's as defined below.

;	fillto 1beh	; imperative location !!!
partab:	dd 0
partyp:	dd 0	;
lbaval:	dd 0	; 1st lba of partition
	dd 0	; partition size in blocks
	dd 0,0,0,0	;
	dd 0,0,0,0	;
	dd 0,0,0,0	;

sign:	dw signID	; MBR signature

; --------------------------------------------------------------------
; LBA 1 - contains the boot manager continuation i.e. stage2

nxtsect:
isext2:	mov si, biosloc + partab	; point to first entry in new block
	mov di, newloc	; point to free space in relocated MBR
	mov cx, 3 * partlen	; was mov cx,32
	rep movsb	; move 2 ext. part. entries to save area
	mov si, newloc + 8	; point to first entry lba in extended
	mov ebx, [newloc + chainadd]	; retrieve base lba of partition
	add [si], ebx	; add relative lba
	mov ebx, [si]	; absolute lba
	call addlba	; collect in list
	call readplus	; read block corresponding to entry 1
	call lbatxte	; collect the OS name in the table

	mov si, newloc + 24	; point to 2nd entry lba in extended
	cmp dword [si], byte 0	; is the chain finished ?
	jz short donexte	; yes

; At this point the information about the partitions is in the lbalist table

	mov ebx, [newloc + baseadd]	; retrieve base LBA of extended
	add [si], ebx	; add relative LBA
	mov ebx, [si]	; absolute LBA of this chained extended
	mov [newloc + chainadd], ebx	; lba of chained extended
	call readplus	; read block corresponding to entry 2
	jmp isext2

notext:	call lbatxt	; collect the OS name in the table
donexte:	pop si
donext:	add si, byte partlen	; point to next table entry lba

; there are 4 partition table entries terminated with 0aa55h.
	cmp word [si - 8], signID	; signature in there is end of table ?
	jne word next2	; no, process next entry
	cmp [newloc + flag], 0	; any bit set in flag (mask)?
	je short direct	; no, boot directly
	push ds
	xor ax, ax
	mov ds, ax
	mov al, [417h]	; shift status byte in BIOS low address 0040h:0017h
				; 7	Insert locked
				; 6	Caps Lock locked
				; 5	Num Lock locked
				; 4	Scroll Lock locked
				; 3	Alt key is pressed
				; 2	Ctrl key is pressed
				; 1	Left Shift is pressed
				; 0	Right Shift is pressed
; NOTE: the same information can be collected with int 16h, function ah = 02h
; Same technique for halting the boot process as in the OBL boot loader.
	pop ds
	test al, [newloc + flag]	; flag mask tests if any bit is set
	jnz short interact	; stop boot process for user interaction

; if no flag instructs to cause an interaction, force it if mbrinproc = 1
; i.e. 'p' or 'n' input caused to extract info from another mbr
	test [newloc + mbrinproc], 1	; is flag = 1 ?
	jnz short interact	; yes, force interaction

; boot the partition directly, thus without user intervention/interaction
	cmp [newloc + defaultd], 0	; is default <> 0 ? e.g. 80h ?
	je short interact	; no, force interaction

; access the correct hard disk
	mov cl, [newloc + curindex]	; index of drive currently analyzed
	call getdrvno	; set dl = drive number with that index
	cmp dl, [newloc + defaultd]	; is current hd also the default ?
	je short locatep	; yes, locate the partition to boot
	inc cl	; next drive index
	jmp word extract2	; extract the info from the partition table
; without halting the boot process for a user interaction

; access the correct partition
locatep:	mov al, dl	; 
	call announce	; (20) optional, may be commented out
	mov bx, newloc + lbadesc
	mov ax, [newloc + defaultp]
direct:
nxtos:	cmp ax, word [bx]
	je near startdirec	; digit pair found, now start os
	add bx, byte lbalen	; point to next entry
	jmp nxtos

interact:	call clearscr
; stow the MBR date in the prompt line
	mov cx, 10	; pdate is 10 bytes long
	mov si, newloc + bmdate
	mov di, newloc + pdate
	rep movsb
	call setblue23
	call WILAstring	; issue top prompt text line
	dw newloc + prompt

	call WILAstring
	dw newloc + drivelisth

	call underline	; (10) indicate selected vol.

; info displayed differs depending on the drive mode, i.e. LBA or CHS
	mov cl, [newloc + currdrive]	; current drive number
	mov al, cl
	call hex	; convert to 2 hex characters
	mov [newloc + driveno], ax	; stow away in text
	call WILAstring
	dw newloc + twolinef

	cmp [newloc + lbaindic], 0
	jne short notchs
	call WILAstring
	dw newloc + modechs

	mov ax, [newloc + cyls]	; number of cylinders
	call int2charv
	mov ax, [newloc + heads]	; number of heads
	call int2charv
	mov ax, [newloc + blocks]	; blocks per track
	mov [newloc + intval + 4], 0
	call int2charv
	mov [newloc + intval + 4], 'x'
	call WILAstring
	dw newloc + fddtail
	jmp word invitlin

notchs:	mov si, newloc + modelba
	mov ah, [newloc + lbaindic]
	or ah, 30h	; convert to decimal character
	mov [si + 4], ah	; store in text
	call writestring

; Display which kind of MBR is present on this volume
	mov si, newloc + BBMBR
	test [newloc + uvstate], 80h	; 'Foreign'
	jz short setbbm	; 
	test [newloc + uvstate], 40h	; 'GRUB'
	jnz short setgrub	; 
	test [newloc + uvstate], 20h	; 'LILO'
	jnz short setlilo	;
	test [newloc + uvstate], 10h	; 'GAG'
	jnz short setgag	;
	jmp short setforeign

setgag:	add si, MBRtxtlen	; position on 'GAG'
setlilo:	add si, MBRtxtlen	; position on 'LILO'
setgrub:	add si, MBRtxtlen	; position on 'GRUB'
setforeign:	add si, MBRtxtlen	; position on 'Foreign'
	call writestring
	jmp short settail

setbbm:	call writestring
settail:	call WILAstring
	dw newloc + BBMtail

; Display a list of OSs to choose from
; The information is extracted from the table lbadesc creating
; one line of text in lbaline per primary or logical partition
; The extended partition is not published.
nombr:	mov bx, newloc + lbadesc
; transfer the text in the entry to a line for display
nxtline:	cmp byte [bx + 4], 'x'
	jne short nxtline2
	mov ax, [bx]
	mov [newloc + locked], ax
nxtline2:	mov si, bx
	mov di, newloc + lbaline
	mov cx, 12	; text length, incl. line number
	rep movsb	; transfer text

	mov al, byte [bx + 12]	; get partition type
	call hex	; convert to 2 hex characters
	mov [newloc + lbalinep], ax	; stow away in text

	call showsize	; (24)
	call hilite
	mov si, newloc + lbaline	; 1 line partition info
; if the number of partitions is less than 10 use one digit
	mov ax, word [newloc + lbaslot]	; (2)
	sub ax, newloc + lbalist	; (2)
	cmp ax, 10*lbalen	; (2)
	jae short twodigit	; (2)
	mov byte [newloc + digindic], 1	; (2) note only 1 digit
	inc si	; (2) start in the 2nd position
twodigit:	call writestring
	add bx, byte lbalen
	cmp bx, word [newloc + lbaslot]
	jb nxtline

; Construct the user prompt line: the information displayed will vary
; depending on the number of disk drives detected
invitlin:	call WILAstring
	dw newloc + selectinit	; prompt initial

	call defaultsel
	call WILAstring	; finalize user prompting
	dw newloc + selectfin	; prompt final

	cmp [newloc + maxdrive], 1	; only 1 disk drive ?
	je short finalize	; 

; Prompt user to select the next or previous drive.
; In response, the partitions on the selected drive are enumerated.
	mov cl, [newloc + curindex]
	cmp cl, 1	; is first drive ?
	ja short notfirst	; go wait for new input
	mov si, newloc + selectn	; prompt user to select next
	jmp short writeit	; 

notfirst:	cmp cl, [newloc + maxdrive]	; is last drive ?
	jb short notlast	; 
	mov si, newloc + selectp	; prompt user to select
	jmp short writeit	; 
notlast:	mov si, newloc + select	; prompt user to select
writeit:	call writestring
	call WILAstring	; finalize user prompting
	dw newloc + selectdrv	; prompt final
finalize:
input:	and byte [newloc + mbrinproc], 0ffh - 1	; reset
	call readchar	; wait for single character input

; User response discriminator for the following cases
; 's' | 'l' | 'b' | 'h'' | 'n' | 'p' | 1-decimal digit | 2-decimal digit
	mov cl, [newloc + curindex]	; index of current drive
; CASE 's'
	cmp al, 's'	; (22)
	je near swapMBR	; (22)

; CASE 'l'
	cmp al, 'l'	; (18)
	jne short notl	; (18)
	test [newloc + uvstate], 04h	; (18)
	jnz short set25	; (18)
	or [newloc + uvstate], 04h	; (18) done
	jmp short now50	; (18)

; fill rest of block, and check if size ok
	times 2*blocksize - ($-$$) -2 db filler

	dw signID	; imperative signature to verify
; BootManager is designed to read volume MBRs and PBRs.
; It must verify that the blocks read have a valid signature.
; The second BootManager block at LBA = 1 is subjected to the same
; validation to make sure that the continuation was correctly loaded.
; --------------------------------------------------------------------
; LBA 2 - contains the boot manager continuation

set25:	and [newloc + uvstate], 0ffh - 04h	; (18)
now50:	call clearscr	; (18) 
	test [newloc + uvstate], 2	; (18)
	jnz short inspecti	; (18)
	jmp word extract	; (18)

; CASE 'b'
notl:
	cmp al, 'b'	; (25)
	jne short notb	; (25)
	test [newloc + memoriz], 01h	; (25)
	jnz short resetb	; (25) 
	or [newloc + memoriz], 01h	; (25) set indicator
	jmp word interact	; (25)
resetb:	and [newloc + memoriz], 0ffh - 01h	; (25) clear indicator
	jmp word interact	; (25)

; CASE 'h'
notb:
	cmp al, 'h'	; (15) request to display HW info ?
	jne short noth	; (15) no
	call clearscr	; (15)
inspecti:	call inspectvbe	; (3)
	call inspectpci	; (15)
	call WILAstring	; issue top prompt text line
	dw newloc + invitretn
	call readchar
	mov cl, [newloc + curindex]	; index of current drive
	jmp short extract	; (5)

; CASE 'n'
noth:	cmp al, 'n'	; request to explore next drive ?
	jne short notn	; no

notv2:	inc cl	; index of next drive to access
	cmp cl, [newloc + maxdrive]	; is above last drive ?
	ja word input	; yes, go wait for new input
	call isaccess	; (14) is that drive marked 'not accessible' ?
	je notv2	; (14) ignore it and proceed to next
	jmp short extract	; 

; CASE 'p'
notn:	cmp al, 'p'	; request to explore previous drive ?
	jne notp	; no
notn2:	dec cl	; index of previous drive to access
	cmp cl, 1	; is below first drive ?
	jb word input	; yes, go wait for new input
	call isaccess	; (14) is that drive marked 'not accessible' ?
	je notn2	; (14) ignore it and proceed to next

; extract the MBR of another volume and process it as if it had been
; loaded by the BIOS, that is, originated from the boot volume.
extract:	or byte [newloc + mbrinproc], 1
extract2:	mov [newloc + curindex], cl; note index of volume to process
	call getdrvno
	mov [newloc + currdrive], dl		; note the volume to inspect next
; restore the default text in the lbadesc table
	call restorelba

	call inspectdisk
	mov bp, newloc + baseadd	; point to pseudo table entry
	mov dword [bp], 0	; MBR on any volume has LBA=0
	call readblock	; read MBR into area at biosloc

	call savembr	; what kind of data in MBR

; move the 4 partition table entries to newloc thus replacing the current ones
	mov si, biosloc + partab	; si points to partab
	mov di, newloc + partab	; relocation address
	mov cx, 64	; set count
	rep movsb	; move partition table data
	jmp word newmbr	; go process another MBR

; CASE 'digit'
; find the LBA corresponding to the chosen partition number and save in bx
notp:
;	call writechar	; TRACE - echo user input
	cmp byte [newloc + digindic], 2	; (2)
	je short firstdig	; (2) is first digit
	mov dl, '0'	; (2) force '0' as 1st digit
; whereas the user will operate with 1 or 2 digits, the internal processing
; is always dealing with 2 digits
	jmp short isalone	; (2) this digit is alone

; find the first digit in the lbadesc table
firstdig:		; (2)
	mov bx, newloc + lbadesc
dig1try:	cmp al, [bx]
	je short digit2	; digit found, now find digit pair
	add bx, byte lbalen	; point to next entry
	cmp bx, word [newloc + lbaslot]
	jb dig1try
invresp:	call WILAstring
	dw newloc + invalid

	jmp word input	; go wait for new input

; expect 2nd digit

digit2:	mov dl, al	; save first digit
	call readchar	; wait for single character input
	call writechar	; echo user input
; find the 2-digit value in the lbadesc table
isalone:		; (2)
	xchg ah, al
	mov al, dl
	mov bx, newloc + lbadesc
dig2try:	cmp ax, word [bx]
	jne short nentry	; digit pair not found, next
	cmp dword [bx + 3], 'no P'	; 'no PBR'
	je invresp
	cmp dword [bx + 3], '|no '	; '|no PBR'
	je invresp
	jmp short startos	; digit pair found, now start os
nentry:	add bx, byte lbalen	; point to next entry
	cmp bx, word [newloc + lbaslot]
	jb dig2try
dig2ext:	jmp invresp	; invalid response
; END CASE

; Start the operating system chosen by its menu number,
; read the partition boot block, place it at the exact location where
; the BIOS would have placed it and jump to it
; i.e. chain load to the Partition Boot Record of the selected partition
; At this point, bx points to the lbadesc entry corresponding to the partition
startos:	cmp word [newloc + locked], ax	; is it the locked Extended ?
	je dig2ext	; yes, it may not be selected

; Note which drive and which partition are now selected.
; They become the defaults for the next BootManager start.
	mov word [newloc + defaultp], ax	; note partition number
	mov dl, [newloc + currdrive]	; get current drive number

	mov [newloc + defaultd], dl	; note drive number
	test [newloc + memoriz], 01h	; (25) 
	jnz short leavesel	; (25) 
	mov [newloc + defaultd], 0	; (25) force de-selection
	mov word [newloc + defaultp], '??'	; (25) force de-selection

; Save to drive configuration for the next system restart.
leavesel:	mov cx, 8*prevdlen	; (8)
	mov si, newloc + drivelist	; (8) source
	mov di, newloc + prevdlist	; (8) relocation address
	rep movsb	; (8) move
	push bx
	call writeblock	; record these values
	pop bx

; determine which partition table entry corresponds to this LBA and load
; its address in si - Needed by the interface to Damn Small Linux - DSL
startdirec:	call setsiDSL

	mov ebx, [bx-4]	; point to corresponding lba

	mov [newloc + baseadd], ebx
	mov bp, newloc + baseadd	; 
	call readblock	; read partition boot block
; test code: display the first 32 bytes of the block just read, stop and
; wait for a keyboard input
;	mov cx, 32	; 32 bytes
;	mov si, biosloc	; 
;nxtlba:	lodsb	; get char of message
;	cmp cx, 0	; is last entry ?
;	jz short read	; yes
;	mov bh, al
;	call ptyp	; display this character
;	dec cx
;	jmp nxtlba	; proceed to next character
;read:	call readchar	; wait for single character input

; Drive mapping just before transferring control to the PBR loaded at biosloc
; In the case of Bluebottle, Native Oberon and DOS, the drive
; number (80h, 81h ...) is stored in the boot block of the OS partition.
; In order to boot from a drive which is not the first as it was at
; installation time, one needs to insert the correct number before
; the boot block is exploited.
; In pratice, it suffices to modify it on the fly in memory at biosloc.
; Drive mapping for other OSs will eventually be implemented later.

	mov dl, [newloc + currdrive]	; get drive number
; and place it in DL as specified in the work assumptions - cfr. BootMan.Text
	cmp word [biosloc + 3], 'OB'	; for 'OBERON', map drive ?
; 'OBERON' is found in Oberon partitions of types: 4ch = 76, 4fh and 50h
	je short mapdrive	; do map
	cmp word [biosloc + 6], 'LI'	; 'SYSLINUX' of DSL "
	jne short mapdrive	; if DSL do not map, leave PBR as is
; Such is the case for DSL on a USB flash memory stick
	mov si, [newloc + DSLsiVal]
	jmp short nomap

; Add whichever OS needs a drive number in its PBR here!

mapdrive:	mov byte [biosloc + 24h], dl	; correct drive number on the fly
nomap:	xor dh, dh	; zero dh; dl must be passed over
	jmp 0:biosloc 	; jump to PBR
; On entry to the PBR:
;	es = 0
;	dl = is the BIOS drive number accessed
; This code is now definitively left !!

; ===== procedure: place the OS name (primary partition) in the next entry
; si : MUST be left untouched! - pointer to lba in partition table entry
lbatxt:
	mov di, word [newloc + lbaslot]
	add di, byte 7	; position of text inside slot
osname:	push si

; Now that a PBR is being processed, verify that the signature is valid.
	cmp word [newloc + mbrsign], signID	; (1) is signature present ?
	je short signgood
	mov si, newloc + notPBR	; bad signID means not a PBR
	jmp short movename

signgood:	mov si, biosloc + 3	; OS name in PBR

; The PBR has a valid signID but when the OS name contains all 0h 
; it is the sign of a Linux partition (Ubuntu, Mandriva, DSL, ....)
	cmp word [si],0 
	jne short testLILO
	mov si, newloc + linuxtxt	; substitute this to the 0s
	jmp short movename

testLILO:	cmp dword [si + 3],'LILO'
	jne short testOBER
	mov si, newloc + LILOMBR	; same shifted left
	jmp short movename

testOBER:	cmp word [si],'OB'
	jne short movename
	mov word [si + 6],'  '

; move whichever name applies to this partition to output area
movename:	mov cx, 8
	rep movsb
	pop si
	add word [newloc + lbaslot], byte lbalen	; next lba entry address
	ret

; ===== procedure: write the block with LBA = bmLBA+bmsize-1 to the volume
; thus recording the number of:
; - the selected boot disk (field defaultd) and of
; - the selected partition (fields defaultp)
; which become default values
; No write operation is performed when booting from a FD because:
; 1 - a FDD is considered as a rescue disk, not resident
; 2 - a CD-ROM emulates a FDD and cannot be written on
writeblock:

; simple tracing sequence
;	mov cx, 32	; 
;	mov si, newloc + blocksize
;nxtlba:	lodsb	; get char
;	cmp cx, 0	; is last char ?
;	jz short read	; yes
;	call char2hex	; display this character
;	dec cx
;	jmp nxtlba	; proceed to next character
;read:

	test byte [newloc + mbrinproc], 80h	; write lock on ?
	jnz short donotwrt	; yes, do not write
	inc byte [newloc + reason]	; discrimator of situation
	cmp byte [newloc + drivenbrs + 1], 80h	; is it a hard disk ?
; if the boot disk drive (first in the list) was a USB-FDD or CD-ROM, then
	jb short donotwrt	; DO NOT register the selection
	inc byte [newloc + reason]	; discrimator of situation
	test [newloc + uvstate], 80h	; is it a foreign MBR ?
	jnz short donotwrt	; DO NOT register the selection

	mov di, maxretry	; number of tries
tryw:	mov ax, 4300h	; ready for extended read
	mov si, newloc + dapw	; dap - Device Address Packet (see below)

; test if the drive supports extended int 13h extensions, i.e. LBA mode
; and trim to standard write if lba not supported
	cmp [newloc + lbaindic], 0	; test the lba indicator
	jne short uselbaw
	mov ax, 0301h	; standard write 03h, 1 block
; place the values in the registers
	mov cx, bmLBA + bmsize - 1	; write block corresponding to LBA 12
	mov dh, 0	; head 0
	mov bx, newloc + bmsize * blocksize	; data buffer offset
	jmp short uselbaw

;	times 3*blocksize - ($-$$) db filler
; --------------------------------------------------------------------
; LBA 3

uselbaw:	mov dl, 80h	; boot drive number
	int 13h	; call BIOS
	jnc short int13okw	; carry flag not set: ok, proceed
; on failure, perform a disk reset
; by experience this event was never observed but who knows !!! it
; will never happen?
	mov [newloc + errcode], ah	; (6) stow the error code away
	xor ax, ax	; reset disk
	int 13h	; call BIOS
	dec di	; does not clear carry
	jnz tryw	; retry counter > 0, retry
	jmp word showerr	; (6) display errormsg with code
; better than the next brutal instruction
; hangw:	jmp hangw	; halt the system !!! endless loop

int13okw:	call WILAstring
	dw newloc + recording
	jmp short persist

donotwrt:	call WILAstring
	dw newloc + ignoring
persist:	call persistance	; (23) add msg persistance
	ret	; 

; ===== procedure: insert size (MB/GB) in text string
; in : bx = pointer to lbadesc for this partition line
;       must be left unaltered
showsize:
	mov dword [newloc + limit], 999999
	mov eax, dword [bx + 13]	; (24) get partition size in blocks

; show volume size
showsizev:	mov cx,6	; (24) max. number of digits to produce
	mov si,newloc + sizeunit	; (24) start at least significant digit
	shr eax,11	; (24) partition size in MB
	mov word [newloc + sizeMG], 'MB'	; (24) Megabytes
	cmp eax, dword [newloc + limit]	; (24) display max. 6 digits
	jbe short sizeM	; (24) 
	shr eax, 10	; (24) DIV 1024 > size in GB
	mov word [newloc + sizeMG], 'GB'	; (24) Gigabytes
sizeM:	push bx	; (24) 
	call convert	; (24) 
	pop bx	; (24) 
	ret	; (24) 

limit:	dd 0

; ===== procedure: place the OS name (logical drive) in the next entry
; in : PBR located at biosloc
lbatxte:
	mov di, word [newloc + lbaslot]
	mov byte [di + 7], '|'
	add di, byte 8	; position of text inside slot
	jmp word osname	; treat as primary partition name

; ===== procedure: collect the information on the number of installed (4)
; drives in a group - either hard disk or floppy disk or both
; Executed only once when the MBR from the boot volume is extracted
driveinfo:
	cmp [newloc + maxdrive],0	; max. drive number determined ?
	jne short done	; yes, skip

	mov word [newloc + driveslot],newloc + drivelist
		; set the table beginning address
	mov dl,[newloc + currdrive]	; get current drive
	mov al,dl
	call hex	; convert to 2 hex characters
	mov [newloc + driveinctrl],ax	; stow away in drivelisth
	push es	; save to protect its value
	call groupinfo
	xor [newloc + currdrive],80h	; flip msb to access other group
	call groupinfo
	xor [newloc + currdrive],80h	; restore original value
	pop es	; restore es
done:	ret

; ===== procedure: collect the information on the number of installed (4)
; drives in a group - floppy disk or hard disk
; mod: ax, cx, dx, di
groupinfo:
	mov dl,[newloc + currdrive]	; get current drive
	and dl,80h	; (22) start with first in group
	mov [newloc + grouplead],dl	; (22) save group leader

; obtain the number of drives in this goup (returned in dl)
	mov ah,08h	; get disk drive parameters
	int 13h	; call BIOS
; (9) dl now contains the number of drives around that drive, but even when
; no diskette drive is installed or when it is disabled, BIOS returns dl = 1.
; In that case of non-existent diskette drive, CF = 0 means successful, but
; bx = 0 is used to decide that no diskette drive is present.
; However, booting from CD-ROM also arrives here with dl = 1 and bx = 0 .
; Must thus test if CD-ROM or not -> using value in ax as discriminator.
; When booting from USB_FDD, the BIOS volume id is 00h or 80h depending
; on the BIOS.
	cmp [newloc + currdrive],80h	; (9)
	jae short hddgrp	; (9) no problem with hdd
	cmp [newloc + maxdrive],0	; (9) first group detected is fdd
; which means that the boot volume is a CD-ROM or a USB-FDD.
	jne short exitgrp	; (9) no, forget about fdds
	mov word [newloc + fddtype],ax	; note that
	mov dl,1	; (9) consider only one device
; and ignore the remaining fdds if any exists.

hddgrp:	add [newloc + maxdrive],dl
	xor cx,cx
	mov cl,dl	; set repeat counter, dl now free
	xor ax,ax	; clear
	mov al,[newloc + grouplead]	; (22) group leader
dgroup:
	inc word [newloc + drivtaddr]
	mov di,[newloc + drivtaddr]
	mov byte [di],al	; save drive number in table

	push cx	; save repeat counter

; brief report of the drive number and parameters. One report per drive is
; in the drivelist at the address found in driveslot .
	push ax	; save drive number
	push di
	mov di,[newloc + driveslot]
	mov byte [di],26	; single character 'right arrow'
	call hex
	mov byte [di + 1],al	; most sign. in drive number
	xchg ah,al	; 
	mov byte [di + 2],al	; least sign. in drive number
	pop di
	call driveparam	; (8)
	pop ax	; restore drive number

	inc al	; next drive
	pop cx	; restore repeat counter
	add word [newloc + driveslot],byte 10
	loop dgroup
exitgrp:	ret

; ===== procedure: collect the information on the installed drives (8)
; interprete the data in the return buffer and its extensions, if any
driveparam:
	mov dl,byte [di]	; get current drive number
	cmp dl,80h	; is it among the hdds ?
	jb isfdd	; no, is floppy drive or cd-rom

; collect more information concerning hdds
	mov si,newloc + dpbuffer	; extra-muros buffer
	mov word [si],74	; data size to receive
		; see remark under dpbuffer
	mov ah,48h	; get drive parameters
	int 13h	; call BIOS
	jc short int13err	; carry denotes error

; volume geometry information - not used
;	mov al,[newloc + infflags]
;	call char2hex
;	mov ax,[si + 4]	; cylinders
;	call int2char4
;	mov ax,[si + 8]	; heads
;	call int2char4
;	mov ax,[si + 12]	; blocks per track
;	call int2char4
;	mov ax,[si + 24]	; bytes per block
;	call int2char4

; TRACE: Take a view of the first dpbuffer encountered and halt!
; Modern PCs do not return device path information,
; only PCI interface path
;	mov cx,72	;
;	mov si,newloc + dpbuffer	; extra-muros buffer
;disnxt:	lodsb	; get char of buffer
;	mov bh,al
;	call char2hex	; display in 2 hex
;	loop disnxt	; proceed to next character
;hardstop:	jmp short hardstop

	mov di,[newloc + driveslot]
	add di,byte 4

; display the volume size in the matching entry on the next line
	mov dword [newloc + limit],999
	mov eax,dword [si + 16]	; (8) volume size in blocks
	call showsizev	; (8) 
; transfer the volume size to the next driveslot
	mov cx,5	; extract only 5 bytes
	mov si,newloc + sizethous	; first digit to extract
	rep movsb	; move cx bytes

	call interfinfo	; (13) collect volume interface information
	ret	; 

; This error occurs when:
; 1 - BIOS was set up to boot from a USB drive (it chooses the first one)
; 2 - then attempt to obtain the drive parameters of another USB drive.
; That drive is not accessible in that set-up.
int13err:	mov di,[newloc + driveslot]
	add di,byte 4
	mov dword [di],'n.a.'	; mark as 'not accessible'
	ret	; exit

isfdd:
; discriminate FDD and CD-ROM by the value returned earlier in ax
; this bit of code appears superfluous: same value returned for real fdd
; or fdd emulating cd-rom
	mov di,[newloc + driveslot]	; (13) 
;	cmp word [newloc + fddtype],0030h	; (13) CD-ROM emulating FDD ?
;	jne short notcd	; (13) no, booting from FDD
;	mov dword [di + 81],'CD-R'	; (13) 80 char. line + 1
;	mov word [di + 85],'OM'	; (13) 80 char. line + 5
;	ret	; (13) 

notcd:	mov dword [di + 83],'FDD '	; (13) 80 char. line + 3
	ret	; (13)

; ===== procedure: underline the drive info line (10)
underline:
; adjust the curindex as is required after swapping MBR
	mov si,newloc + drivenbrs + 1	; 
	mov al,1
	mov cl,[newloc + currdrive]	; get current drive
trynextd:	cmp byte [si],cl
	je short valindex	; valid index obtained
	inc si
	inc al
	jmp trynextd
valindex:	mov [newloc + curindex],al	; correct curindex

	mov di,newloc + drivelistul	; destination address
	mov al,[newloc + curindex]	; current drive index
addspace:	mov cx,10
	dec al
	jz short markit
	mov si,newloc + spaces	; move 10 spaces
	rep movsb
	jmp short addspace

markit:	mov si,newloc + marks	; move 10 marks
	rep movsb
	mov byte [di],0	; add a 0-terminator

	call WILAstring	; write complete underlining string
	dw newloc + drivelistul
	ret

; ===== procedure: is current drive accessible ? (14)
; in: cl - drive index (origin is 1)
; out: carry is 1 if drive marked as non-accessible
isaccess:
	mov al,cl	; (14) index of drive to scrutinize
	cbw	; (14) convert byte to word
	mov dx,10	; (14) drivelist entry length
	mul dx	; (14) ax now contains destination location
	mov di,ax	; (14) total displacement
	add di, newloc + drivelist - 6	; (14) 10 - 4 = 6
	cmp dword [di],'n.a.'	; (14) 10 - 4 = 6
	ret	; (14)

; ===== edit the disk operation error code as errorgui in the errormsg
; Extension of the readblock/writeblock procedure (6)

showerr:	mov al,[newloc + errcode]
	call hex	; convert to 2 hex characters
	mov word [newloc + errorgui],ax	; stow away in text
	call WILAstring
	dw newloc + errormsg

	jmp word invitlin

; ===== edit the disk operation error code as errorgui in the errormsg
; Extension of the readblock procedure (6)

readerr:	mov al,[newloc + errcode]
	call hex	; convert to 2 hex characters
	mov word [newloc + errorgui],ax	; stow away in text
	call WILAstring
	dw newloc + errormsg
	cmp dl,80h	; is it a hard disk ?
	jae word input	;no, 
	call WILAstring
	dw newloc + fdderror
errinfo:	call persistance	; (23) add msg persistance
	jmp word interact

driveslot:	dw newloc + drivelist	; address of next available slot
fddtype:	dw 0	; type of fdd detected

	times 4*blocksize - ($-$$) db filler
; --------------------------------------------------------------------
; LBA 4 - data areas

modelba:	db 'LBA ?) - MBR: ',0
modechs:	db 'CHS ',0

; One line of text per partition
; number / partition type / partition size
lbaline:	db 'ij -           '
lbalinep:	db 'XY  '	; partition type in hex
	db 'thu'

; the following 5 bytes are for displaying volume size
sizethous:	db 'th'	; thousand/hundred/
sizeunit:	db 'u'	; unit digit is inserted first
sizeMG:	db 'mb'	; MB or GB
crlf:	db CR,LF,0

dfttext:	db 'Extended ',0	; default text
locked:	dw 0	; OS number which may not be selected
		; that is, the extended partition

; area in which partition data for up to 12 partitions is collected:
; each partition is completely identified by a quadruple:
;	lbalist / lbadesc / lbapartno / lbasize in no. of blocks
; each entry has lbalen bytes

lbaslot:	dw newloc + lbalist	; address of next available slot

lbalist:	dd 0	; lba of first partition block
lbadesc:	db '01 Extended '	; partition description
lbapartno:	db 0	; partition type extracted from partition table
lbasize:	dd 0	; (24) partition size in no. of blocks

	dd 0
	db '02 Extended '
	db 0
	dd 0
	dd 0
	db '03 Extended '
	db 0
	dd 0
	dd 0
	db '04 Extended '
	db 0
	dd 0

	dd 0
	db '05 Extended '
	db 0
	dd 0
	dd 0
	db '06 Extended '
	db 0
	dd 0
	dd 0
	db '07 Extended '
	db 0
	dd 0
	dd 0
	db '08 Extended '
	db 0
	dd 0

	dd 0
	db '09 Extended '
	db 0
	dd 0
	dd 0
	db '10 Extended '
	db 0
	dd 0
	dd 0
	db '11 Extended '
	db 0
	dd 0
	dd 0
	db '12 Extended '
	db 0
	dd 0

; three lines of up to 8 volumes detected to display in one go
; Each volume is allocated a 10-byte slot with the structure:
; 	1 character "right arrow"
; 	2 characters drive number 00h, 01h, 80h, 81h, ...
; 	1 space
; 	the drive size in MB or GB, or 'fdd' for a floppy diskette
;	or a CD-ROM emulating a floppy
; 	2 spaces

drivelisth:	db 'BIOS detected volumes / '
driveinctrl:	db '  '	; drive in control by MBR
	db ' is boot volume in control',CR,LF	; drive list header
drivelist:	db '          '
	db '          '
	db '          '
	db '          '
	db '          '
	db '          '
	db '          '
	db '          '
;	db CR,LF	; automatic cr+lf because full line
volinterf:	db '          '	; volume interface: ATAPI, ATA, USB
	db '          '
	db '          '
	db '          '
	db '          '
	db '          '
	db '          '
	db '          '
;	db CR,LF	; automatic cr+lf because full line
	db 0

;	times 5*blocksize - ($-$$) db filler

; --------------------------------------------------------------------
; LBA 5

maxdrive:	db 0	; max. number of hard disk detected
; 0 denotes that the max. drive number is not yet determined
drivtaddr:	dw newloc + drivenbrs	; address in table for groupinfo
drivenbrs:	db -1,-2,-2,-2,-2,-2,-2,-2,-2
;	table of (max. 8) disk drive numbers
		; position with index 0 is empty
curindex:	db 1	; index of drive currently accessed [1 .. 8]
		; for underline

; dap for an extended write 13h disk operation defined in proc writeblock
; the structure of the dap is:
dapw:	db 10h	; dap size (fixed)
	db 0	; reserved (fixed)
	dw 1	; number of blocks to transfer (fixed)
boffw:	dw newloc + bmsize * blocksize	; address of transfer buffer - offset
	dw 0	; address of transfer buffer - segment
	dd bmLBA + bmsize - 1	; starting absolute block address LBA
	dd 0	; remaining 32 bits of address

; work areas and prompt lines
spaces:	db '          '
selectn:	db '  N(ext)',0
select:	db '  N(ext) or'
selectp:	db '  P(revious)',0
selectdrv:	db ' volume'
marks:	db ' ',30,30,30,30,30,30,CR,LF,0	; (10) marks 
selectfin:	db '  L (toggle) 25/50 lines',CR,LF
	db '  H to display PC HW info',CR,LF
	db '  S MBR on selected volume is to take control as boot volume',CR,LF,0
invalid:	db ' invalid input, retry',CR,LF,0

; (25) text to appear in green or red
preselr:	db '  B (toggle) @ next boot, show this menu',CR,LF,0
preselg:	db '  B (toggle) @ next boot, start selected OS [currently v. '
dftdtext:	db '  , p. '
dftptext:	db '  ]',CR,LF,0

fdderror:	db 'Either no floppy drive is present: disable it '
	db 'in the BIOS setup,',CR,LF
	db '        or no diskette is present: insert one',CR,LF,0

;	times 6*blocksize - ($-$$) db filler
; --------------------------------------------------------------------
; LBA 6

twolinef:	db CR,LF,'Volume '
driveno:	db '   '	; displayable digit
mode:	db '(Mode ',0
grouplead:	db 0	; (22) is 80h OR 00h

wrongdid:	db 'Must access 80h',CR,LF,0

BBMtail:	db '  - Vol. signature @ 1B8h: '
signb1:	db '   '
signb2:	db '   '
signb3:	db '   '
signb4:	db '  ',CR,LF,LF,0
fddtail:	db ') Not partitioned.',CR,LF,0

; ===== procedure: inspect the data in the MBR just read
; does not alter registers
inspectmbr:

; determine the kind of boot manager it corresponds to:
;	Bluebottle, GRUB, LILO or Foreign
; A Windows MBR has no fixed identifying strign, hence Foreign
; Save that information in uvstate for later use.
	and [newloc + uvstate],0ffh - 0f0h	; clear bits 7,6,5,4
	cmp dword [MBRreloc + 17h],'GAG '	; position of GAG
	jne short notGAG
	or [newloc + uvstate],90h	; set bit 4 = GAG
	jmp short MBRok

notGAG:	cmp dword [MBRreloc + bbmid],'BBM '
	je short MBRok
	or [newloc + uvstate],80h	; set bit 7 = foreign
	cmp dword [MBRreloc + 017fh],'GRUB'	; position of GRUB
	jne short notGRUB
	or [newloc + uvstate],40h	; set bit 6 = GRUB
	jmp short MBRok
	
notGRUB:	cmp dword [MBRreloc + 06h],'LILO'	; position of LILO
	jne short MBRok
	or [newloc + uvstate],20h	; set bit 5 = LILO

MBRok:
; reveal the disk signature in the MBR: stow info in signb1 to 4
	pusha
	mov al,[MBRreloc + dsignat]
	call hex
	mov word [newloc + signb1],ax
	mov al,[MBRreloc + dsignat + 1]
	call hex
	mov word [newloc + signb2],ax
	mov al,[MBRreloc + dsignat + 2]
	call hex
	mov word [newloc + signb3],ax
	mov al,[MBRreloc + dsignat + 3]
	call hex
	mov word [newloc + signb4],ax
	popa
	ret

; ===== procedure: set the cursor to the u.l. corner and fill the screen
; with blanks on 25 or 50 lines according to bit 2 in uvstate
; References:
; http://home.earthlink.net/~danrollins/techhelp/0117.HTM and 0124.HTM
clearscr:
	push cx
; set cursor at row 0, column 0 (upper left screen corner)
	mov ah,2
	mov bh,0
	xor dx,dx
	int 10h	; call BIOS - display characters

; fill the entire screen with blanks
	mov cx,4000	; repeat write char. 80 x 50 times
	test [newloc + uvstate],04h	;
	jz short go50
	mov ax,03h	; set text mode 80 x 25 lines
	xor bx,bx
	int 10h	; call BIOS
	mov cx,2000	; repeat write char. 80 x 25 times
	jmp short blkscr
go50:	mov ax,1112h	; set text mode 80 x 50 lines
	xor bx,bx
	int 10h	; call BIOS
blkscr:	mov ax,0920h	; write blank (20h) and attribute
	mov bx,0fh	; bright white
	int 10h	; call BIOS
	pop cx
	ret

; ===== procedure: inform of default boot pre-selection (16)
; memorizing the operating system started last
defaultsel:
	cmp byte [newloc + drivenbrs + 1],80h	; is it a hard disk ?
; if the boot disk drive (first in the list) was a FD, then nothing memorized
	jb short nopresel	; no preselection
	mov al,[newloc + defaultd]
	call hex	; convert to 2 hex characters
	mov [newloc + dftdtext],ax	; stow away in text
	mov ax,[newloc + defaultp]
	mov [newloc + dftptext],ax	; stow away in text

	mov cx,68	; (25) presel text length
	test [newloc + memoriz], 01h	; (25) 
	jnz short greepresel	; (25) 
	call setred	; (25) 
	call WILAstring	; (25)
	dw newloc + preselr	; (25)
	jmp short nopresel	; (25) 
greepresel:	call setgreen	; (25) 
	call WILAstring	; (25)
	dw newloc + preselg	; (25)

nopresel:	ret

; ===== procedure: set si for DSL (tested with DSL on 62MB USB-HDD)
; Find the address of the partition table entry containing the LBA value
; found in the lbalist
; In bx: pointer to LBA value where the OS starts
setsiDSL:
	mov ax,newloc + partab	; point to first entry
;	mov ebx,[bx-4]	; point to lba in front of lbadesc
;	cmp ebx,[ax + 8]	; lba found in partition table entry
;	jne short setsiEND
	mov [newloc + DSLsiVal],ax
setsiEND:	ret

DSLsiVal:	dw 0	; si value for interface to DSL

; ===== Transfer control from the current Boot Manager to a MBR in
; another volume (retrieve the saved MBR and relocate it in biosloc
; and behave as BIOS would)
swapMBR:	mov si,MBRreloc	; (22) copy the memorized MBR
	mov di,biosloc	; (22) to the area where BIOS
		; (22) would have loaded it normally
	mov cx,blocksize	; (22) set count
	rep movsb	; (22) copy MBR

; Attempt to pass control to MBR on volume 8xh after booting from 80h.
; Does not work, except with a Bluebottle MBR.
; Otherwise only successful when passing control to a foreign MBR on 80h.
; Was tested with GRUB and Ubuntu installed on a HDD, enumerated as 81h, while
; booting from USB-HDD thus enumerated as 80h.

	cmp dword [biosloc + bbmid],'BBM '	; (22) is this a Bluebottle MBR?
	je short bluembr	; (22) this works always

; Pass control to a foreign MBR which must this time be on volume 80h
	cmp [newloc + currdrive],80h	; (22) 
	je short bluembr	; (22) this works
; boot volume does not have drive id as expected by MBR of installed OS
	call WILAstring
	dw newloc + wrongdid	; wrong drive id
	jmp word errinfo

bluembr:	call WILAstring	; (22) 
	dw newloc + swap	; (22) 
	call persistance	; (23) add msg persistance
	or byte [biosloc + mbrinproc],80h	; 
;               NOTE! > biosloc	lock writing defaultd and defaultp
	mov dl,[newloc + currdrive]	; (22) get current drive number
	jmp 0:biosloc	; (22) activate new MBR

swap:	db 'Switch MBR in 4"...',0	; (22) timed message

recording:	db CR,LF,'Recording user selection',CR,LF,0	; (22) timed message
ignoring:	db CR,LF,'Selection not recorded '
reason:	db 'A',CR,LF,0	; (22) timed message

MBRtxtlen:	equ 11
BBMBR:	db 'Bluebottle',0
FORMBR:	db 'Foreign   ',0
GRUBMBR:	db 'GRUB      ',0
LILOMBR:	db 'LILO      ',0
GAGMBR:	db 'GAG       ',0

; --------------------------------------------------------------------
; LBA 7

linuxtxt:	db 'Linux   '	; PBR with 8 x 0h at offset 3
notPBR:	db 'no PBR  '	; PBR with invalid signature

vbeinfo:	db CR,LF,LF,'VESA modes: WidthxHeightxDepth',CR,LF,0	; (3)
dimen:	db ' MB'
hyphen:	db ' - ',0

; ===== procedure: inspect the graphic controller capabilities (3)
inspectvbe:
	call WILAstring
	dw newloc + vendebx
	call WILAstring
	dw newloc + newline

; vesatab at es:di - 1KB scratch segment for receiving the VBE
;	controller information block located at the end of this program
	mov di,newloc + vesatab
	mov ax,4f00h	; return VBE controller information
	mov dword [di],'VBE2'	; "VBE2" signature
	int 10h	; call BIOS
	cmp ax,004fh
	jne near endmode

; process the data returned in the buffer
	mov cl,[di+4]	; save value before overwriting it
	mov byte [di+4],0	; now zero-terminated string at di
	mov si,di	; 
	call writestring

	mov al,[di+5]	; first digit of version number
	or al,30h	; convert to decimal character
	call writechar
	mov al,'.'	; decimal point
	call writechar
	mov al,cl	; bring back 2nd digit
	or al,30h	; convert to decimal character
	call writechar
	mov al,' '	; space
	call writechar

	mov ax,[di + 12h]	;
	shr ax,4
	call int2char4	; 
	call WILAstring
	dw newloc + dimen

; trace VBE controller information (19 bytes of code)
;	mov cx,0
;	push di
;nextsi:	mov al,[di]
;	inc di
;	push cx
;	call char2hex
;	pop cx
;	inc cx
;	cmp cx,34h
;	jb nextsi
;	pop di

; As from VESA 2.0, additional information is returned. Some of it
; is displayed now
	mov al,[di+5]	; first digit of version number
	cmp al,2	; is it 2 ?
	jb short vesa1	; no, skip what appeared later

	push ds	; save before accessing the other ds
	mov si,[di + 16h]	; of vendor string
	mov ds,[di + 18h]	; ds of vendor string
	call writestring
	pop ds	; restore

	call WILAstring
	dw newloc + hyphen

	push ds	; save before accessing the other ds
	mov si,[di + 1ah]	; of product
	mov ds,[di + 1ch]	; ds of product
	call writestring
	pop ds	; restore

	call WILAstring
	dw newloc + crlf

vesa1:	push ds	; save before accessing the other ds
	mov si,[di + 6h]	; of OEM string
	mov ds,[di + 8h]	; ds of OEM
	call writestring
	pop ds	; restore

	call WILAstring
	dw newloc + vbeinfo

; extract all modes from the mode list and display their characteristics
; coloring is used to identify the modes suitable for Bluebottle.
hasmodes:
	push ds
	mov si,[di + 0eh]	; offset of mode list
	mov ds,[di + 10h]	; ds of mode list
nxtmode:
	mov al,[si]	; get first byte
	cmp al,0ffh	; is it an end of list marker ?
	je near endmode	; yes
; inspect mode and inform only about VBE mode for which LFB is available
; A color code is used to differentiate the modes of the graphic card:
;	green: mode supported by BB
;	white: mode not supported
;	yellow: 15-bit depth mode not supported, but potentially suitable
;	Requires a module modification.
	mov ax,4f01h	; return VBE mode information
	mov cx,[si]	; for this mode
	mov di,newloc + vesatab + blocksize	; another block for storing data
	int 10h	; call BIOS
	cmp ax,004fh	; is mode supported ?
	jne short skipmode	; no
	test byte [es:di],80h	; is video mode attribute LFB available
	jz short white	; not good for Bluebottle
;	test byte [es:di],10h	; is video mode attribute graphic mode
;	jz short white	; not good for bluebottle
	cmp byte [es:di + 19h],15	; 15 bits per pixel ? not yet supported
	jne short green	; yes, good for Bluebottle
;	or byte [newloc + lfbyes],80h	; at least one mode supports LFB
	call setyellow4	; not good for bluebottle
; NOTE: currently, 15-bit modes are not supported but it would be rather easy
; to include them (as green) by modifying Displays.Mod
	jmp short white
green:	call setgreen4	; good for Bluebottle
; display the mode (1 blank and 3 hexadecimal characters)
white:	mov al,[si + 1]
	call hex
	xchg ah,al	; 
	call writechar
	mov al,[si]
	call hex
	call writechar
	xchg ah,al	; 
	call writechar
	mov al,' '	; terminate with a blank
	call writechar

; restore the program code data segment from the stack via ax
	pop ax
	push ds	; save the current ds containing
		; the VBE mode information
	mov ds,ax

	mov ax,[es:di + 12h]	; horizontal resolution in pixels
	call int2char4
	mov ax,[es:di + 14h]	; vertical resolution in pixels
	call int2char4
	mov al,[es:di + 19h]	; bits per pixel
	aam 0ah
	or ax,'00'	; make it zero ASCII-based
	xchg al,ah
	cmp al,'0'	; is first digit '0' ?
	jne short leaveit	; no
	mov al,' '	; suppress leading '0'
leaveit:	mov [newloc + here],ax	; stow away in buffer
	push si
	call WILAstring	; display it
	dw newloc + here

	pop si

; restore the VBE mode info data segment from the stack via ax
	pop ax
	push ds	; save the program code data segment
	mov ds,ax

skipmode:	add si,byte 2	; position to next mode
	jmp word nxtmode	; repeat for next mode
here:	db 0,0,'    ',0

endmode:	pop ds
	call WILAstring
	dw newloc + initinfo
	call readchar	; halt display output
	ret

; ===== procedure: convert 1 byte to two hexadecimal characters
; in : al = byte to convert
; out: al = most significant ; ah = least significant
hex:
	aam 10h	; ah := al DIV 10h
		; al := al MOD 10h
	or ax,word '00'	; make it zero ASCII-based
	xchg ah,al	; exchange in view of output order
	cmp al,'9'
	jna short hexl	; <= 9 is alright
	add al,7	; > 9 modified to 'A' .. 'F'
hexl:	cmp ah,'9'
	jna short hexh	; <= 9 is alright
	add ah,7	; > 9 modified to 'A' .. 'F'
hexh:	ret

; ===== procedure: convert an integer to decimal 4-character string
; with 0-terminator, with suppression of leading zeros
; in: ax = 16-bit value
; out:     si pointer to most significant character of zero-delimited string
int2char:
	push ax	; correction: extend ax to eax
	xor eax,eax
	pop ax
	mov cx,4	; max. number of digits to produce
	mov si,newloc + intval + 3
	call convert
	ret

intval:	db 0,0,0,0,'x',0	; 4 receiving pos, a 'x' and a delimiter

; ===== procedure: convert a long integer to decimal character string
; with 0-terminator, with suppression of leading zeroes
; in: eax = 32-bit value
;      cx = max. number of digits to produce
;      si = pointer to unit digit in result string
;       	further digits are added from right to left
; out:     si pointer to most significant character of zero-delimited string
convert:
	mov ebx,10	; conversion base 10
convertdig:	xor edx,edx	; extend eax to 64-bit edx:eax
	div ebx	; eax <- edx:eax div ebx, edx <- remainder
	or dl,'0'	; make it '0'+ ASCII-based
	mov [si],dl	; stow away in buffer
	dec si	; place right to left in buffer
	loop convertdig	; loop on count in cx

; replace leading '0' by ' '
	inc si
convert0:	cmp [si],'0'	; is this digit '0' ?
	jne short convertexit	; no, keep it
	mov [si],' '	; put a ' ' in there
	inc si	; address next digit
	jmp convert0	; repeat that
convertexit:	ret

; ===== procedure: convert an integer to a variable length string of decimal
; characters terminated with 0, suppressing the leading zeroes,
; and display it
; in: ax = 16-bit value
int2charv:
	push si
	call int2char
	call writestring	; display it
	pop si
	ret

; ===== procedure: convert an integer to a string of 4 decimal characters
; terminated with 'x' and 0, suppressing the leading zeroes,
; and display it
; in: ax = 16-bit value
int2char4:
	push si
	call int2char
	call WILAstring	; display it
	dw newloc + intval	; position at the beginning

	pop si
	ret

; ===== procedure: convert a binary integer to a 4-hexadecimal ascii string
; not used here, but good to keep
; in: ax = 16-bit value
;      di = pointer destination - 1st byte
int2hex4:
	pusha
	mov bx,10h	; conversion base is hex (15)
	mov cx,4	; number of characters
	add di,byte 3
int2hex4n:	xor dx,dx	; extend ax to 32-bit dx:ax
	div bx	; ax <- dx:ax div bx, dx <- remainder
	cmp dl,10	; is dl < 10 ?
	jb short int2hex4d	; yes, is to become a digit
	add dl,'A' - 10	; form a letter A .. F
	jmp short int2hex4l	; skip, it is a letter
int2hex4d:	or dl,'0'	; form a digit

int2hex4l:	mov [di],dl	; stow away in buffer
	dec di	; place right to left in buffer
	loop int2hex4n	; loop on count in cx
	popa
	ret

	times 8*blocksize - ($-$$) db filler
; --------------------------------------------------------------------
; LBA 8

; ===== procedure: wait until an ASCII character is received from the keyboard
; out : al = character
readchar:
el0:	mov ah,0	; wait for a keyboard event
	int 16h	; call BIOS
	cmp al,0	; is a scan code in ah ?
	je el0	; yes, ignore
	ret

lfbyes:	db 0	; at least one mode support LFB (T/F)
uvstate:	db 4	; 04h - bit 2 = 25/50 lines
		; 10h - bit 4 on = GAG
		; 20h - bit 5 on = LILO
		; 40h - bit 6 on = GRUB
		; 80h - bit 7 off = BBM in MBR / on = foreign
memoriz:	db 0	; (25) 
initinfo:	db CR,LF,'Set Init= to a green mode val'
	db 'ue and Boot=DisplayLinear',CR,LF,LF
invitretn:	db 'Press any key to continue',CR,LF,0

; ===== procedure: info about the partition type of the current entry
; in : si = pointer to lba in entry
info:
	mov bp,si	; save entry pointer used in readblock
	ret	; shortcut the remaining code
; trace code snippet, now deactivated (16 bytes)
	mov dh,[si-4]	; partition type
ptyp:	mov al,dh

; in : al = byte to display as 2 hexadecimal digits
char2hex:	call hex
	call writechar
	xchg ah,al	; 
	call writechar
	mov al,' '
	call writechar
	ret

; ===== procedure: add an lba value and partition type to the lbalist
; the address of the next free slot is available in lbaslot
; in : si: partition table entry address
;       ebx: LBA value
addlba:
;	call readchar	; used to pause between detected partitions
; NOTE: can be used to trace the progress through the partition table entries.
	mov cl,[si - 4]	; get partition type
	mov edx,[si + 4]	; (24) get partition size in blocks
	push si	; save partition table entry address
	mov si,word [newloc + lbaslot]	; next free slot address
	mov [si],ebx	; save LBA in lbalist
	mov byte [si + 16],cl	; save partition type
	mov dword [si + 17],edx	; (24) save partition size
	pop si
	ret

; ===== procedure: restore the default text in the lbadesc table
restorelba:
	mov bx,newloc + lbadesc
nxtdft:	mov si,newloc + dfttext	; 
	mov di,bx	;
	add di,byte 3
	mov cx,10	; dfttext length
	rep movsb	; restore text
	add bx, byte lbalen
	cmp bx,word [newloc + lbaslot]
	jb nxtdft

; finally, reset the lbaslot content to the first slot address
	mov word [newloc + lbaslot],newloc + lbalist
	mov word [newloc + locked],0	; none is locked
	ret

; ===== procedure: set the color attribute of 11 characters
; light red is used to warn when a partition is not bootable as is the case
; for an extende one or when the PBR has no signature signID
; in : pointer to string to display

hilite:
	cmp dword [bx + 3],'no P'	; 'no PBR'
	je short setred11
	cmp dword [bx + 3],'|no '	; '|no PBR'
	je short setred11
	cmp dword [bx + 3],'Exte'	; 'Extended'
	jne short hiliteno
setred11:	mov cx,11	; string length

setred:	push bx	; user defined string length
	mov bx,0ch	; light red

setattrib:	mov ax,0920h	; write blank and attribute
	int 10h	; call BIOS - display characters
	pop bx
hiliteno:	ret

; ===== procedure: set the color attribute of 1 character
; mod : ax, cx
setwhite:	mov cx,1	; write 1 character

	push bx	; user defined string length
	mov bx,07h	; white
	jmp short setattrib

; ===== procedure: set the color attribute of 12 characters
; mod : ax, cx
setblue23:	mov cx,23

	push bx	; user defined string length
	mov bx,1fh	; light blue
	jmp short setattrib

; ===== procedure: set the color attribute of 4 characters
; mod : ax, cx
setgreen4:	mov cx,4

setgreen:	push bx	; user defined string length
	mov bx,0ah	; light green
	jmp short setattrib

; ===== procedure: set the color attribute of 4 characters
; mod : ax, cx
setyellow4:	mov cx,4

	push bx	; user defined string length
	mov bx,0eh	; light yellow
	jmp short setattrib

; ===== procedure: inspect the PCI Bus (7)
; References:
; PCI Bus Specifications - Intel
inspectpci:
; PCI1 - Determine if a PCI BIOS is present
	mov ax,0b101h	; pci function id / pci bios present
	int 1ah	; call BIOS
	jc short nopci	; cf = 1 means 'no BIOS present'
	cmp edx,'PCI '	; edx <> 'PCI ' means 'no BIOS present'
	jne short nopci	; 
	or ah,ah
	jnz short nopci	; ah <> 0 means 'no BIOS present'
	and al,3h	; extract hardware mechanism bits 0,1
	or [newloc + hwmech],al	; transform to decimal character
	mov al,bh	; 
	or [newloc + pciversion],al	; transform to decimal character
	mov al,bl	; 
	shr al,4
	or [newloc + pciversion + 2],al	; transform to decimal character
	mov al,bl	; 
	and al,0fh
	or [newloc + pciversion + 3],al	; transform to decimal character
	mov [newloc + lastbus],cl	; save number of last PCI bus

	call WILAstring
	dw newloc + equtitle

; PCI2 - Explore bus by bus until last bus in the system
	xor bx,bx	; 0 in bh & bl
nextbus:	push bx
	call scanbus	; (15)
	pop bx
	inc bh
	cmp bh,[newloc + lastbus]
	jbe nextbus
nopci:	ret

; ===== procedure: scan the PCI bus (15)
; Inspired from Bluebottle PCITools.Mod - ReadConfigDword
; bh = bus number - range 0 .. 0ffh
; bl = devfn - range 0 .. 0feh
; dh = ismulti - dl = devfn MOD 8

scanbus:
	xor bl,bl	; 0 in devfn
	xor dx,dx	; clear
nxtdevfn:	cmp bl,0feh	; FOR devfn := 0 TO 0feh DO
	jbe short trydevfn
	ret

trydevfn:	mov dl,bl
	and dl,7	; devfn MOD 8
	cmp dl,0	; IF devfn MOD 8 = 0 THEN
	je short okdevfn
	test dh,80h	; (dh) ODD(hdrtype DIV 80h)
	jz near gonext

okdevfn:	mov di,0eh	; hdrtype offset in al
	call readpcib
	cmp dl,0	; IF devfn MOD 8 = 0 THEN
	jne short skiptest
	mov dh,al	; hdrtype in dh (prepare multi)
skiptest:	mov di,0	; vendor offset
	call readpciw	; load vendor in ax
	cmp ax,0ffffh	; IF vendor = 0ffffh THEN skip
	je near gonext

; !!! from here on, bl, bh and dh must be left untouched until gonext: is
; reached and execution returns to trydevfn: and to line (dh) above
; dl is computed at each round

	mov [newloc + venid],ax	; save vendor

; fill area with blanks - reset in preparation of collecting new infos
	mov si,newloc + devbus	; fill with blanks
	mov cx,devend - devbus	; # of blanks
insertb:	mov byte [si],' '
	inc si
	loop insertb

	mov di,newloc + manid	; insert vendor in hex there
	call int2hex4
	mov al,bh	; bus
	or al,'0'	; convert to decimal character
	mov byte [newloc + devbus],al	; stow away in text
	mov al,bl	; device & function
	shr al,3	; device
	call hex	; convert to 2 hex characters
	mov [newloc + devno],ax	; stow away in text
	mov al,bl
	and al,7	; extract function (3 LSB)
	or al,'0'	; convert to decimal character
	mov [newloc + devfunno],al	; stow away in text

; get the device but do not search it. It cannot be found anyway.
	mov di,2	; offset to device
	call readpciw	; load device in ax
	mov di,newloc + devid	; insert device in hex there
	call int2hex4
	mov ax,[newloc + venid]	; retrieve vendor

; get the device class and subclass
	mov di,0ah	; offset to class
	call readpciw	; load class and subclass in ax

	call movedescr

; discriminate the device classes
; CASE '0ch'
; discriminate the various serial bus types 00, 03, 05
serialbus:	cmp ah,0ch	; is serial bus controller ?
	jne short notserb	; 
	push ax
	mov si,newloc + serialdesc
	call movesubd
	pop ax

notsmbus:	cmp al,03h	; is USB controller ?
	je short moreusb
	jmp word classdone

moreusb:	push dx
; get USB Specification release
	mov di,60h	; offset to read USB Spec. release
	call readpcib	; read byte
	call hex
	mov [newloc + devrel],al	; stow away in text
	mov byte [newloc + devdot],'.'	; stow away in text
	mov [newloc + devrelsub],ah	; stow away in text

; get the device Programming Interface
	mov di,09h	; offset to PI
	call readpcib	; load PI in ax

; process the base address
	mov di,20h	; offset to base address
	call readpciw	; read word in ax
	and ax,0fffeh	; clear bit 0
	mov [newloc + ioadd],ax	; save IOaddr for later
	mov dx,[newloc + ioadd]	; 
	add dx,byte 10h	; USB port 1 register (base + 10h)
	mov di,newloc + devport1
	call getport

	add dx,byte 02h	; USB port 2 register (base + 12h)
	mov di,newloc + devport2
	call getport
	pop dx
	jmp classdone

;	times 9*blocksize - ($-$$) db filler
; --------------------------------------------------------------------
; LBA 9 - PCI information collection (15)

; CASE '01h'
	cmp ah,01h	; is mass storage controller ?
	jne short serialbus
	mov si,newloc + massdesc
	call movesubd
	jmp word classdone

; CASE '02h'
notserb:	cmp ah,02h	; is network controller ?
	jne short displayc
	mov si,newloc + netwdesc
	call movesubd
	jmp classdone

; CASE '03h'
displayc:	cmp ah,03h	; is display controller ?
	jne short mmedia
	mov si,newloc + dispdesc
	call movesubd
	jmp short classdone

; CASE '04h'
mmedia:	cmp ah,04h	; is multimedia ?
	jne short bridge
	mov si,newloc + mediadesc
	call movesubd
	jmp short classdone

; CASE '06h'
; discriminate the various bridge
bridge:	cmp ah,06h	; is bridge ?
	jne short classdone

	cmp al,04h	; is PCI bridge ?
	jne short notPCIb
	mov byte [newloc + ht],1
notPCIb:	mov si,newloc + bridgdesc
	call movesubd
; ENDCASE

classdone:	call WILAstring
	dw newloc + devdesc
	mov ah,dh	; hdrtype
	and ah,7fh	; hdrtype MOD 80h
	cmp byte [newloc + ht],ah
	jne short gonext
	mov di,1ah	; offset to buses + 2
	call readpcib	; read byte in ah
	cmp ah,0	; 
	je short gonext	; yes
	mov di,18h	; offset to buses
	call readpciw	; read word in ax
	cmp ax,0
	je short gonext

	pusha
	mov bh,ah
	call scanbus
	popa

gonext:	inc bl
	jmp word nxtdevfn

; ===== procedure: move class description to info line
movedescr:	push ax
	mov si,newloc + classdesc
movenextr:	sub ah,1
	cmp ah,0
	je short moveendc
	add si,word classlen
	jmp short movenextr
moveendc:	mov di,newloc + devclass
	mov cx,word classlen
	rep movsb
	pop ax
	ret

; ===== procedure: move subclass description to info line
movesubd:	cmp al,80h
	je short move80
movenexts:	cmp al,0
	je short moveends
	add si,word subclasslen
	sub al,1
	jmp short movenexts

move80:	mov si,newloc + subcother
	jmp short movevalid
moveends:	cmp byte [si],byte 0ffh
	jne short movevalid
	mov si,newloc + subcinval
movevalid:	mov di,newloc + devsubclass
	mov cx,word subclasslen
	rep movsb
	ret

; ===== procedure: read a word
; in: bh,bl
;      di = word offset
; out: ax = word read (transfered from cx)
readpciw:
	mov ax,0b109h	; read word
readpci:	int 1ah	; 
	mov ax,cx
	ret

; ===== procedure: read a byte
; in: bh,bl
;      di = byte offset
; out: al = byte read (transfered from cx)
readpcib:
	mov ax,0b108h	; read byte
	jmp readpci

; ===== procedure: get information on attached device(s)
; in: dx = IO base address
; out: ax = port status/control register contents
getport:
	in ax,dx
	and ax,0105h	; bit 8 = 1 : suspended, bit 2 = 1 : enabled
	cmp ax,0005h	; bit 0 = 1 : device present
	je short getportp	; device present
	mov byte [di],'_'
	jmp short getporta	; device absent
getportp:	mov byte [di],'*'
getporta:	ret

; ===== procedure: write a string specified by in-line address
;	call WILAstring
;	dw newloc + fieldname
; Saves one byte per instruction pair (frequently used)
;	mov si,newloc + fieldname
;	call writestring

WILAstring:
	push bp	; save bp (the caller might be using it)
	mov bp,sp	; bp now used to access the stack
	mov si,[bp + 2]	; get old IP (return address)
	mov si,[si]	; get word after call instruction
	call writestring
	add word [bp + 2], byte 2	; return addres is 2 bytes further
	pop bp	; restore
	ret

; ===== procedure: extract the current drive number given its index
; in : cl = drive index
; out : dl = drive number
getdrvno:
	mov al,cl
	cbw
	add ax,newloc + drivenbrs	;
	mov si,ax
	mov dl,[si]
	ret

; ===== procedure: announce start of selected OS
announce:		; (20)
; insert the volume number in a readable form in the msg text
; the partition number is already in the text in readable form
	pusha
	call hex
	mov [newloc + drvcharh],al
	xchg ah,al	; 
	mov [newloc + drvcharl],al
	call WILAstring
	dw newloc + launchos
	popa
	ret

; ===== procedure: add some persistance to a text line (23)
; let the message appear for a few seconds, approx. 4"
persistance:
	pusha 
	xor ah,ah	; get tick count
	int 1ah	; call BIOS - timer service in dx
	mov bx,dx	; save RTC
	add bx,byte 70	; add 70 ticks x 55 ms = 3850 ms
waittim:	xor ah,ah	; get tick count
	int 1ah	; call BIOS - timer service
	cmp dx,bx	; is tick count >= limit saved in cx
	jb waittim	; wait time not expired
	popa
	ret

; ===== procedure: collect as much drive interface information as possible
; depending on how much data was returned by BIOS in dpbuffer
interfinfo:
	mov si,newloc + dpbuffer	; extra-muros buffer
	cmp word [si],byte 30	; (13) 30 or less bytes received ?
	jbe exitIF	; (13) yes, very little info available
	mov di,[newloc + driveslot]	; (13) 
	add di,word 84	; (13) destination is next line element

	cmp word [newloc + interftype],'AT'	; (13) test ATA or ATAPI
	jne short notATA	; (13)
	mov dword [di],'IDE '
	cmp word [newloc + key],0beddh	; device path info available ?
	jne short exitIF	; no interface info
	cmp byte [newloc + primary],0	; (13)
	je short isprim	; (13)
	mov byte [di + 3],'2'	; (13)
	jmp short tstmaster	; (13)
isprim:	mov byte [di + 3],'0'	; (13)
tstmaster:	cmp byte [newloc + master],0	; (13)
	je short exitIF	; (13)
	add byte [di + 3],01h	; (13)
	jmp short exitIF	; (13)

notATA:	cmp word [newloc + interftype],'US'	; (13) test USB
	jne short exitIF	; (13)
	mov dword [di],'USB '	; (13)
exitIF:	ret	; 

; ===== procedure: display CPU info extracted with CPUID (21)
; Should be applicable to CPUs of:
;	Intel Corp. - Vendor ID 8086h
;	AMD - Vendor ID 1022h
;	Via (formerly UDT/Centaur) - Vendor ID 1106h
; Could be applicable to Transmeta - not tested
; The CPUID instruction is not known by this assembler
; hence machine code is used instead
; Executed only once when the MBR of a volume is executed
CPUID:	equ 0a20fh	; machine hard code
cpuinfo:

; References:
; Intel: How to determine whether a processor supports
;	hyper-threading
; Detecting Multi-Core Processors
; softwarecommunity.intel.com/Wiki/Processorfeaturedetection/63.htm
; 6/17/2007
; Advanced Micro Devices - CPUID Specification Rev. 2.18 Jan. 2006

; CPUID - Determine if the CPUID instruction is supported
; by checking ability to set/clear ID flag (bit 21) in EFLAGS
; http://www.delphigl.com/forum/viewtopic.php?t=4621
; google: test eflags

	pushfd	; EFLAGS on stack
	pop eax	; copy EFLAGS to eax
	mov ebx,eax	; save a copy
	xor eax, 00200000h	; flip ID bit 21 in EFLAGS
	push eax	; new value on stack
	popfd	; new value in EFLAGS
	pushfd	; EFLAGS on stack
	pop eax	; copy EFLAGS in eax
	cmp eax, ebx	; compare to see if toggled
	jnz short cpuidyes	; jmp if value changed

; CPUID not supported, one should check for Cyrix family but don't and
; assume "classic" Intel
	mov si,newloc + cpuidno
	mov di,newloc + vendebx
	mov cx,33
	rep movsb
	ret

; CPUID - Get vendor string and stow in text
cpuidyes:	xor eax,eax	; clear
	dw CPUID	; machine code
	mov [newloc + vendebx],ebx
	mov [newloc + vendecx],ecx
	mov [newloc + vendedx],edx

; CPUID - Determine family, model, stepping
	mov eax,1
	dw CPUID	; machine code

; 4 instructions suppressed - Stepping value not important
;	mov ecx,eax	; save
;	call hex
;	mov [newloc + cpus],ah	; stepping
;	mov eax,ecx	; restore

	shr eax,4
	call hex
	mov [newloc + cpuf],al	; family
	mov [newloc + cpum],ah	; model

; CPUID - Determine if deterministic cache parameters supported
	xor eax,eax	; clear
	dw CPUID	; machine code
	cmp ax,4	; supported ?
	jl short getHT	; not supported, assume single-core

; CPUID - Read deterministic cache parameters and validate
	mov eax,4
	mov ecx,0
	dw CPUID	; machine code
	push eax
	and al,1fh	; valid cache parameters read ?
	cmp al,00h	; eax[4:0] = 0 indicates invalid
	pop eax
	je short getBrand
	shr eax,14
;	add ax,31h	; add 1 and convert to printable digit
;	mov [newloc + logipack],al	; logical processors sharing a cache
	shr eax,12	; # of processor cores on this package/die
	add ax,31h	; add 1 and convert to printable digit
	mov [newloc + phycores],al

; CPUID - Determine hyperthreading when not MCP - Multi-Core
; NOTE: this only to assert that a physical package is capable of hardware HT.
; It does no imply that HT is enabled.
getHT:	mov eax,1
	dw CPUID	; machine code
	shr ebx,16

; If an IA-32 processor does not have HW HT support,
; the value in ebx[23:16] is reserved
; bl = max. number of logical processors in a physical package
; bh = APIC ID
	test edx,010000000h	; hyper-threading bit 28
	jz short getBrand
	mov byte [newloc + HTind],'Y'	; replace 'N' by 'Y'
	or bl,30h	; convert to printable digit
	mov [newloc + logiprocs],bl	; stow in text
; CODE LIMITATION: max. 8 logical processors

;	mov al,bh	; APIC ID
;	call hex	; convert to 2 hex characters
;	mov [newloc + cpuapic], ax	; stow away in text

; CPUID - Obtain the Processor Brand string if supported
getBrand:	mov eax,80000002h
	dw CPUID	; machine code
	cmp eax,byte 0	; eax = 0 : no brand string
	jz short nobrand
	mov di,newloc + brand
	call mov16char

	mov eax,80000003h
	dw CPUID	; machine code
	call mov16char

	mov eax,80000004h
	dw CPUID	; machine code
	call mov16char

; CPUID - Determine width
nobrand:	mov eax,80000000h
	dw CPUID	; machine code
	and eax,byte 0fh	;
	cmp eax,byte 0	; 
	je short not64
	mov eax,80000001h
	dw CPUID	; machine code
	test edx,20000000h	; long mode 64-bit bit 29
	jz short not64
	mov word [newloc + cpuwidth],'64'

not64:	ret

; ===== procedure: move 16 characters from 4 registers
mov16char:
	mov [di],eax
	mov [di + 4],ebx
	mov [di + 8],ecx
	mov [di + 12],edx
	add di,16
	ret

; TUI title line
prompt:	db 'Boot Manager '
pdate:	db '??/??/???? - ',0	; transferred from LBA 0

; Information repository for cpuid instructions
; LINE 1
; NOTE the register order: ebx, edx, ecx!!!
vendebx:	dd 0
vendedx:	dd 0
vendecx:	dd 0
brand:	times 48 db ' '	; 3 * 16 char fixed
; The brand string ends with 0h, thus a newline is needed

; LINE 2
newline:	db CR,LF
cpuwidth:	db '32'	; 32 or 64
	db '-bit processor  Family 0'
cpuf:	db '?'
	db 'h Model 0'
cpum:	db '?'
;	db 'h Stepping 0'
;cpus:	db '?'
	db 'h Cores: '
phycores:	db '1'	; # of cores in a physical package
	db ' Logicals: '
logiprocs:	db '1'
; max. # of logical processors in a physical package = HW capability of the
; processor as manufactured and does not necessarily equate to the # of
; logical processors enabled by the platform BIOS or operating system.

;	db ' LPSh: '
;logipack:	db '1'	; # of logical processors sharing a cache
	db ' HT: '
HTind:	db 'N'	; hyper-threading capable 'Y' / 'N'
;	db ' APIC '
;cpuapic:	db '**'
	db CR,LF,LF,0

cpuidno:	db 'not supported, classic Intel/AMD',0	; to be moved to vendebx

; adapted from PCI SIG Class Code Table - www.pcisig.com
classlen:	equ 15	; 15-character entry
classdesc:	db 'Mass Stor. Ctrl'
	db 'Network    Ctrl'
	db 'Display    Ctrl'
	db 'Multimed Device'
	db 'Memory     Ctrl'
	db 'Bridge   Device'
	db 'Communic.  Ctrl'
	db 'Base peripheral'
	db 'Input    Device'
	db 'Docking Station'
	db 'Processor      '
	db 'Serial bus Ctrl'

subclasslen:	equ 8	; 8-character entry
massdesc:	db 'SCSI    '
	db 'IDE     '
	db 'Floppy  '
	db 0ffh

netwdesc:	db 'Ethernet'
	db 0ffh

dispdesc:	db 'VGA comp'
	db 'XGA     '
	db 0ffh

mediadesc:	db 'Video   '
	db 'Audio   '
	db 'Telephon'
	db 'Audio   '
	db 0ffh

bridgdesc:	db 'Host/PCI'
	db 'PCI/ISA '
	db 'PCI/EISA'
	db 'PCI/MIC '
	db 'PCI/PCI '
	db 'PCI/PCMC'
	db 'PCI/NuBu'
	db 'PCI/Card'
	db 0ffh

serialdesc:	db 'Firewire'
	db 'ACCESS  '
	db 'SSArchit'
	db 'USB     '
	db '????    '
	db 'SMBus   '
	db 0ffh

subcinval:	db ' unknown'
subcother:	db '   other'

; References:
;  USB in DOS - "http://www.cybertrails.com/~fys/usb.htm"
;  UHCI Design Guide - Revision 1.1 - Intel
;   "ftp://download.intel.com/design/usb/uhci11d.pdf"
;  http://www.beyondlogic.org/usbnutshell/

devindex:	db 3fh,3fh
venid:	dw 0
ioadd:	dw 0
ht:	db 0
lastbus:	db 0

; PC equipment information title
equtitle:	db 'PC equipment - PCI version '
pciversion:	db '0.00'
	db ' HW mechanism '
hwmech:	db '0',CR,LF,LF
	db 'Bus Dev Fun Ven.  Dev. Class           Subclass Rel P1 P2',CR,LF,0

; one line of device description (equivalent to PCITools.Scan)
devdesc:	db ' '
devbus:	db '?'
	db '  '
devno:	db '??'
	db '   '
devfunno:	db '?'
	db '  '
manid:	db '????'
	db '  '
devid:	db '????'
	db ' '
devclass:	times classlen db '?'
	db ' '
devsubclass:	times subclasslen db '?'
	db ' '
devrel:	db '?'
devdot:	db '.'
devrelsub:	db '?'
	db '  '
devport1:	db '?'
	db '  '
devport2:	db '?'
devend:	db CR,LF,0

	times 12*blocksize - ($-$$) db filler
; --------------------------------------------------------------------
; LBA 12 - Data areas holding information for the next system boot
; CAUTION! This area might be occupied by a GRUB boot manager.
; Beware of a programming error in this Boot Manager when
; deciding to record this block by a call writeblock just 
; before reading and transferring control to the PBR.

; defaultd & defaultp contain the number of the drive and of the partitition
; selected last.
; When defaultd is zero (no default yet set), the user is prompted
; to enter values which will then become the defaults.
; Otherwise, prompting must be forced by the user.

; message issued when starting immediately the default OS designated
; by defaultd and defaultp

launchos:	db 'Starting OS on volume '
drvcharh:	db '?'
drvcharl:	db '?'
	db ' partition '
; Partition number to boot by default without user interaction:
; "01", "02" ..., the same as used by Bluebottle for numbering partitions
defaultp:	db '??'
	db ' in 4"...',0	; 4" seconds delay

; Default volume number hosting the partition to boot:
; 80h, 81h ... for hard disks (internal or external)
; value 0 means that no default was set, the boot menu is displayed.
defaultd:	db 0

selectinit:	db 10,'Enter one of the following:',CR,LF
	db '  the number of a partition hosting an OS to start (red = no OS detected)',CR,LF
	db '      Set ScrollLock to halt AOS start-up.',CR,LF,0

; drive list save area - 8 x 10 bytes
prevdlen:	equ 10
prevdlist:	db '          '
	db '          '
	db '          '
	db '          '
	db '          '
	db '          '
	db '          '
	db '          '

	times 13*blocksize - ($-$$) db filler
; --------------------------------------------------------------------
; Extra-muros buffer (placed in memory after the previous block)
; to contain the drive parameters information (8)
; Experience shows that at most 30 bytes (1eh) are returned by the BIOS call,
; instead of the documented 74 bytes, even when modern hardware is present
		; (offset decimal)
dpbuffer:	dw 74	; (0) buffer length / size of returned data
infflags:	dw 0	; (2) information flags
	dd 0	; (4) number of physical cylinders
	dd 0	; (8) number of physical heads
sectptr:	dd 0	; (12) number of physical blocks per track
sectnumb:	dd 0,0	; (16) number of physical blocks quadword
	dw 0	; (24) number of bytes per block
dpteoff:	dw 0	; (26) pointer to DPTE seg:offset
dpteseg:	dw 0

key:	dw 0	; (30) key
	db 0,0,0,0	; (32) length of device path information
bus:	db 0,0,0,0	; (36) host bus type (ASCII)
interftype:	dd 0,0	; (40) interface type (ASCII: ATA, ATAPI, SCSI ..)
interfpath:	dd 0,0	; (48) interface path
		; (56) device path - 16 bytes
master:	db 0ffh	; 0 = master, 1 = slave
primary:	db 0ffh,0,0	; 0 = primary, 1 = secondary channel
	dd 0,0,0
	db 0,0,0,0	; (72)

	dd 0,0,0,0	; (76) free space to adjust
	dd 0,0,0,0
	dd 0,0,0,0
	dd 0

drivelistul:	dd 0,0,0,0	; drivelist underline showing current drive
	dd 0,0,0,0	; 1 * 80-position screen line as sequence
	dd 0,0,0,0	; of spaces terminated with marks
	dd 0,0,0,0
	dd 0,0,0,0

	dd 0,0,0,0
	dd 0,0,0,0
	dd 0,0,0,0

	dd 0,0,0,0
	dd 0,0,0,0
	dd 0,0,0,0
	dd 0,0,0,0
	dd 0,0,0,0
	dd 0,0,0,0
	dd 0,0,0,0
	dd 0,0,0,0

	dd 0,0,0,0
	dd 0,0,0,0
	dd 0,0,0,0
	dd 0,0,0,0
	dd 0,0,0,0
	dd 0,0,0,0
	dd 0,0,0,0
	dd 0,0,0,0

;	times 14*blocksize - ($-$$) db filler
; --------------------------------------------------------------------
; Extra-muros buffer (placed in memory after the previous block)
; to contain the VBE Controller Information (3)
; obtained with a int 10h BIOS call, function 4f00h (see inspectvbe)
; 512-byte information block

vesatab:	db 'VBE2'	; 00 - VBE2.0 information request
vbevers:	dw 0	; 04
oemptr:	dd 0	; 06 - pointer to OEM info
capab:	db 0,0,0,0	; 0a
modeptr:	dd 0	; 0e
totalmem:	dw 0	; 12

; added for VBE 2.0
software:	dw 0	; 14 - VBE implementation software rev.
vendptr:	dd 0	; 16 - pointer to OEM vendor name
prodptr:	dd 0	; 1a - pointer to OEM product name
revision:	dd 0	; 1e - pointer to OEM product revision

; The buffer at address newloc + vesatab + blocksize is used
; to store VBE mode information (3)
; and disk information