Skip to main content

  | मौसम…

411 समाचार
Public archive edition · Attributed source reporting with original dates · AI drafts are excluded

411 REFERENCE · Wikibooks

Oberon/BootManager.Asm

Reference material by Wikibooks contributors. This retains its source identity and is not 411 news reporting.

; 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