Oberon/BootManager.Asm
< Oberon
; 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