
                ; .model tiny, basic
                .386c ;.486p
                .387  ;.487


boot            segment byte public 
		assume cs:boot, ds:boot, ss:boot

                org     0h

BASE          EQU  7c00h
OldMBROrigin  EQU  7C00h
NewMBROrigin  EQU  0600h

bootstrap       proc    far                ; 0:7c00
abjmpcode:      jmp     short abbootcode
                nop   ; 90h
BPB:

abbootcode:
              CLI                    ; Disable interrupts
              XOR  CX,CX             ; Stack grows downwards from
              MOV  SS,CX             ; SS:SP = 0:7C00h
              MOV  SP, OldMBROrigin
              MOV  SI,SP             ; SI = 7C00h
              STI

BeginMain:
              ; show boot menu txt
              mov  si, offset BASE+bootmenutext
              call cs:tty 
              STI                    ; Enable interrupts
              MOV  DH,'9'            ; Progress bar will start from '9' char

Usermenu:
              MOV  AH,1              ; Get keyboard status
              INT  16h
              JZ   NotPressed        ; Is key pressed?
              MOV  AH,0              ; If yes, then read it
              INT  16h
              CMP  AL,0Dh            ; Is Enter pressed?
              JE   BootDefaultOS     ; Enter activates booting default OS
              CMP  AL,1Bh            ; Esc code interrupts timer
                                     ; (patched from installation: Esc/Space)
              JNE  TestKey           ; Go to checking of OS number choice key

                               
TestKey:      MOV  AL,AH             ; Move scan-code to AL instead of ASCII
                                     ; code (could be filled with NOP opcodes
                                     ; from installation program)
              SUB  AL,'1'            ; convert ASCII/scan code to OS number
                                     ; (patched from installation)
              CMP  AL,9              ; Check maximum available OS number to boot
                                     ; (patched from installation)
              JB   KeyPressed
NotPressed:   MOV  AX,18             ; How often to output a progress bar symbols
                                     ; (patched from installation)
                                     ; ThisValue*10*55ms=DefaultBootTimeout
              CMP  CX,AX             ; Is not it a time to output a symbol
                                     ; of progress bar?
              JB   usermenu         
              SUB  CX,AX
              MOV  AH,0Eh            ; Teletype mode for int 10h
              MOV  AL,DH             ; Symbol to output for int 10h
              MOV  AL,'*'            ; Symbol to output for int 10h
              call cs:_tty

              DEC  DH                ; Switch to next symbol
              CMP  DH,'0'            ; Check if time is out
              JAE  usermenu       
BootDefaultOS:
              MOV  AL,0              ; If timeout has occured, set default OS
                                     ; (patched from installation)

; Store currently chosen OS number to be used as a default one at next boot
KeyPressed:

; Get sectors offset of chosen operating system
              CBW                    ; convert byte to word, ax = os number
              SHL  AX,2              ; convert number to offset in a table
              ADD  SI,AX             ; SI points to proper element in offsets
                                     ; table
              MOV  BP,[SI]          ; load offset in sectors of desired OS
              INC  BP               ; Check "skip boot" menu item
              JNZ  BootAttempt
              INT  18h               ; Tell BIOS, boot attempt has been failed
                                     ; User has chosen not to boot

; Set desired partition to active and hide remaining partitions
; we modify partition in the old origin since it would be saved to MBR
BootAttempt:  DEC  BP

              MOV  offset SI, base+PartitionTable
              MOV  CX,4              ; number of records in partition table
ProcessPartitionRecord:
              MOV  AL,0              ; inactive flag
              CMP  BP,[SI+8]        ; check relative sectors value
              JNE  SetActiveFlag
              MOV  AL,80h            ; hard disk 0 (active flag)
SetActiveFlag:
              MOV  [SI],AL           ; set partition (in)active flag
              MOV  AL,[SI+4]         ; load partition system id byte
              AND  AL,0EFh           ; ignore HIDDEN bit
              CMP  AL,01h
              JE   ModifyPartitionVisibility
              CMP  AL,04h
              JE   ModifyPartitionVisibility
              CMP  AL,06h
              JE   ModifyPartitionVisibility
              CMP  AL,07h
              JE   ModifyPartitionVisibility
              CMP  AL,0Bh
              JE   ModifyPartitionVisibility
              CMP  AL,0Ch
              JE   ModifyPartitionVisibility
              CMP  AL,0Eh
              JNE  SkipVisibility
ModifyPartitionVisibility:
              CMP  EBP,[SI+8]        ; check relative sectors value
              JE   WriteVisibilityFlag
                                     ; make bootable partition visible
              OR   AL,10h            ; make other partitions invisible
WriteVisibilityFlag:
              MOV  [SI+4],AL         ; save updated system id byte
SkipVisibility:
              ADD  SI,10h            ; next record in partition table
              LOOP ProcessPartitionRecord

; Write modified partition table to MBR (we assume DL still contains the
; number of drive since we have not overwritten it till this place)
              MOV  SI,DevAdrPkt
              MOV  AX,4300h          ; Extended write, no verify
              INT  13h               ; we do not check for errors in order
                                     ; to save space in MBR

; Load boot sector of OS (we use extended int13h)
              TEST EBP,EBP           ; Take care about next drive if requested
                                     ; if EBP=0
              JNE  ReadBR
              INC  DL                ; Next disk please
ReadBR:       MOV  [SI+8],EBP        ; save offset (relative sectors)
              MOV  AH,42h            ; Extended read
              INT  13h
              JNC  CheckSignature
UseMBR:       MOV  DL,80h            ; Try to use default boot disk
              XOR  EBP,EBP           ; Use MBR in case of error
              JMP  ReadBR            ; Try again

; Check for signature in boot record
CheckSignature:
              CMP  WORD [OldMBROrigin+510],0AA55h
              JNE  UseMBR            ; Load MBR again instead of invalid BR
              JMP  OldMBROrigin      ; Execute OS boot loader (DL is a disk
                                     ; number what could be important for the
                                     ; new BR/MBR being loaded)

; Timer interrupt routine is called by int8 every 55msec (approx 1/18sec)
; We modify the value in new origin to prevent saving non-zero value back to MBR
; The first opcode here could be patched from installation (with 0CFh - IRET to
; disable timed boot (at installation time it is patched in actual master boot
; record which is saved to HDD, but at run-time the same is done with the
; Esc/Space keypress in the copy of MBR to prevent saving the modified code
; back to MBR)
TimerHandler: INC  CX                ; This opcode is patched from installation
                                     ; to prevent loading of default OS by
                                     ; timeout. CX initially is equal to 0
              IRET

; ===========================================================================
_tty    proc near
; ===========================================================================
        push ax
        push cx
        push dx
        push bx
        mov  ah, 0eh
        mov  bl,7
        int  10h
        pop  bx
        pop  dx
        pop  cx
        pop  ax
        ret
_tty    endp

; ===========================================================================
tty     proc near
; ===========================================================================
        push ax
        cld
_tty1:  lodsb             ; write ascii-z string to display using bios
        test  al, al
        jz    short _ttyx
        cmp   al, 0ffh
        jne   short _tty2
        mov   ax, 0e0ah
        int   10h
        mov   al, 0dh
_tty2:
        call  _tty
        jmp   short _tty1
_ttyx:
        pop  ax
        ret
tty     endp

; ***********************************************************
; Here goes some data stored in MBR
; ***********************************************************

; Device address packet which is used in extended read operation
; (which should be performed in order to work around a problem
; with booting an operating system located higher than 1024 cyl.)
DevAdrPkt:    DB    10h       ; Size of this structure in bytes, shall be 10h
              DB    0         ; Reserved field, should be set to 0
              DB    1         ; Number of blocks to transfer, we need 1 sector
              DB    0         ; Reserved field, should be set to 0
              DW    OldMBROrigin
                              ; Address of transfer buffer (offset)
              DW    0         ; Address of transfer buffer (segment)
              DD    0         ; This double word and the next one (DQ) mean
              DD    0         ; starting logical block address in form of
                              ; (Cyl*MaxHead+SelHead)*MaxSector+SelSector-1
                              ; initially it (DQ 0) points to MBR

; Text for user menu (patched from installation, real data should reflect
; user's boot menu text). The default entry is marked with "*" sign (">"
; could also be used)
BootMenuText: DB    0Ah       ; This LF is used to avoid boot menus visually
                              ; flowing together into one solid block of text
                              ; f.ex. when choosing a corrupted menu entry
              db 138 dup(20h)
                              ; It is a maximum number of characters for
                              ; user's boot menu text when 9 partitions are
                              ; used. For example 2 partitions will result
                              ; in 138+(9-2)*4=166 availble characters
              DB    0

; Table representing offsets of bootable partitions. (patched from
; installation, real offsets should be written by the installation program)
OffsetsTable:
              dd 9 dup(0)

; DiskSignature (or so-called NT Drive Serial Number) for Microsoft Windows
; These bytes should not be overwritten since it breaks boot process under
; Windows Vista
DiskSignature:
              DD 0
              db 2 dup(0)

; Partition table (patched from installation, should be overwritten with
; actual data at installation time)
PartitionTable:
              db 4 * 16 dup(0)

bootstrap       endp

CodeSize        EQU $-abjmpcode          ; org boot+200h-2
padding         db (512-CodeSize-2) dup(0)
bootsignature   db 55h,0aah
BB:             org boot+200h-2    ; make sure it is at the end

boot            ends

		end     abjmpcode
