
                .286c
                .287 

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

        org 0

boot:

jmp     START

OEM_ID                db 'QUASI-OS'
BytesPerSector        dw 0200h
SectorsPerCluster     db 01h
ReservedSectors       dw 0001h
TotalFATs             db 02h
MaxRootEntries        dw 00E0h
TotalSectorsSmall     dw 0B40h
MediaDescriptor       db 0F0h
SectorsPerFAT         dw 0009h
SectorsPerTrack       dw 0012h
NumHeads              dw 0002h
HiddenSectors         dd 00000000h
TotalSectorsLarge     dd 00000000h
DriveNumber           db 00h
Flags                 db 00h
Signature             db 29h
VolumeID              dd 0FFFFFFFFh
VolumeLabel           db 'QUASI  BOOT'
SystemID              db 'FAT12   '


START:
; code located at 0000:7C00, adjust segment registers
     cli
     mov     ax, 07C0h
     mov     ds, ax
    ; mov     es, ax
    ; mov     fs, ax
    ; mov     gs, ax
; create stack
     mov     ax, 0000h
     mov     ss, ax
     mov     sp, 0FFFFh
     sti
; post message
     mov     si, offset msgLoading
     call    DisplayMessage
LOAD_ROOT:
; compute size of root directory and store in "cx"
     xor     cx, cx
     xor     dx, dx
     mov     ax, 0020h                         ; 32 byte directory entry
     mul     WORD [MaxRootEntries]               ; total size of directory
     div     WORD [BytesPerSector]               ; sectors used by directory
     xchg    ax, cx
; compute location of root directory and store in "ax"
     mov     al, BYTE [TotalFATs]                ; number of FATs
     mul     WORD [SectorsPerFAT]                ; sectors used by FATs
     add     ax, WORD [ReservedSectors]          ; adjust for bootsector
     mov     WORD [datasector], ax               ; base of root directory
     add     WORD [datasector], cx
; read root directory into memory (7C00:0200)
     mov     bx, 0200h                         ; copy root dir above bootcode
     call    ReadSectors
; browse root directory for binary image
     mov     cx, WORD [MaxRootEntries]           ; load loop counter
     mov     di, 0200h                         ; locate first root entry
LOOP1:
     push    cx
     mov     cx, 000Bh                         ; eleven character name
     mov     si, offset ImageName                       ; image name to find
     push    di
rep  cmpsb                                       ; test for entry match
     pop     di
     je      LOAD_FAT
     pop     cx
     add     di, 0020h                         ; queue next directory entry
     loop    LOOP1
     jmp     FAILURE
LOAD_FAT:
; save starting cluster of boot image
     mov     si, offset msgCRLF
     call    DisplayMessage
     mov     dx, WORD [di + 001Ah]
     mov     WORD [cluster], dx                  ; file's first cluster
; compute size of FAT and store in "cx"
     xor     ax, ax
     mov     al, BYTE [TotalFATs]                ; number of FATs
     mul     WORD [SectorsPerFAT]                ; sectors used by FATs
     mov     cx, ax
; compute location of FAT and store in "ax"
     mov     ax, WORD [ReservedSectors]          ; adjust for bootsector
; read FAT into memory (7C00:0200)
     mov     bx, 0200h                          ; copy FAT above bootcode
     call    ReadSectors
; read image file into memory (0050:0000)
     mov     si, offset msgCRLF
     call    DisplayMessage
     mov     ax, 0050h
     mov     es, ax                              ; destination for image
     mov     bx, 0000h                          ; destination for image
     push    bx
LOAD_IMAGE:
     mov     ax, WORD [cluster]                  ; cluster to read
     pop     bx                                  ; buffer to read into
     call    ClusterLBA                          ; convert cluster to LBA
     xor     cx, cx
     mov     cl, BYTE [SectorsPerCluster]        ; sectors to read
     call    ReadSectors
     push    bx
; compute next cluster
     mov     ax, WORD [cluster]                  ; identify current cluster
     mov     cx, ax                              ; copy current cluster
     mov     dx, ax                              ; copy current cluster
     shr     dx, 0001h                          ; divide by two
     add     cx, dx                              ; sum for (3/2)
     mov     bx, 0200h                          ; location of FAT in memory
     add     bx, cx                              ; index into FAT
     mov     dx, WORD [bx]                       ; read two bytes from FAT
     test    ax, 0001h
     jnz     .ODD_CLUSTER
.EVEN_CLUSTER:
     and     dx, 0000111111111111b               ; take low twelve bits
    jmp     _DONE
.ODD_CLUSTER:
     shr     dx, 0004h                          ; take high twelve bits
_DONE:
     mov     WORD [cluster], dx                  ; store new cluster
     cmp     dx, 0FF0h                          ; test for end of file
     jb      LOAD_IMAGE
DONE:
     mov     si, offset msgCRLF
     call    DisplayMessage
     push    WORD 0050h
     push    WORD 0000h
     retf
FAILURE:
     mov     si, offset msgFailure
     call    DisplayMessage
     mov     ah, 00h
     int     16h                                ; await keypress
     int     19h                                ; warm boot computer

;*************************************************************************
; PROCEDURE DisplayMessage
; display ASCIIZ string at "ds:si" via BIOS
;*************************************************************************
DisplayMessage:
     lodsb                                       ; load next character
     or      al, al                              ; test for NUL character
     jz      DONE1
     mov     ah, 0Eh                            ; BIOS teletype
     mov     bh, 00                            ; display page 0
     mov     bl, 07h                            ; text attribute
     int     10h                                ; invoke BIOS
     jmp     DisplayMessage
DONE1:
     ret

;*************************************************************************
; PROCEDURE ReadSectors
; reads "cx" sectors from disk starting at "ax" into memory location "es:bx"
;*************************************************************************
ReadSectors:
.MAIN:
     mov     di, 0005h                          ; five retries for error
        pusha
        mov  ah, 0eh
        mov  al, '.'
        mov  bl,7
        int  10h
        popa
.SECTORLOOP:
     push    ax
     push    bx
     push    cx
     call    LBACHS
     mov     ah, 02h                            ; BIOS read sector
     mov     al, 01h                            ; read one sector
     mov     ch, BYTE [absoluteTrack]            ; track
     mov     cl, BYTE [absoluteSector]           ; sector
     mov     dh, BYTE [absoluteHead]             ; head
     mov     dl, BYTE [DriveNumber]              ; drive
     int     13h                                ; invoke BIOS
     jnc     .SUCCESS                            ; test for read error
     xor     ax, ax                              ; BIOS reset disk
     int     13h                                ; invoke BIOS
     dec     di                                  ; decrement error counter
     pop     cx
     pop     bx
     pop     ax
     jnz     .SECTORLOOP                         ; attempt to read again
     int     18h
.SUCCESS:
     mov     si, offset msgProgress
     call    DisplayMessage
     pop     cx
     pop     bx
     pop     ax
     add     bx, WORD [BytesPerSector]           ; queue next buffer
     inc     ax                                  ; queue next sector
     loop    .MAIN                               ; read next sector
     ret

;*************************************************************************
; PROCEDURE ClusterLBA
; convert FAT cluster into LBA addressing scheme
; LBA = (cluster - 2) * sectors per cluster
;*************************************************************************
ClusterLBA:
     sub     ax, 0002h                          ; zero base cluster number
     xor     cx, cx
     mov     cl, BYTE [SectorsPerCluster]        ; convert byte to word
     mul     cx
     add     ax, WORD [datasector]               ; base data sector
     ret

;*************************************************************************
; PROCEDURE LBACHS
; convert "ax2; LBA addressing scheme to CHS addressing scheme
; absolute sector = (logical sector / sectors per track) + 1
; absolute head   = (logical sector / sectors per track) MOD number of heads
; absolute track  = logical sector / (sectors per track * number of heads)
;*************************************************************************
LBACHS:
     xor     dx, dx                              ; prepare dx:ax for operation
     div     WORD [SectorsPerTrack]              ; calculate
     inc     dl                                  ; adjust for sector 0
     mov     BYTE [absoluteSector], dl
     xor     dx, dx                              ; prepare dx:ax for operation
     div     WORD [NumHeads]                     ; calculate
     mov     BYTE [absoluteHead], dl
     mov     BYTE [absoluteTrack], al
     ret

absoluteSector db 00
absoluteHead   db 00
absoluteTrack  db 00

datasector  dw 0000
cluster     dw 0000
ImageName   db 'LOADER  BIN'
msgLoading  db 0Dh, 0Ah, 'Loading Boot Image ', 0Dh, 0Ah, 00
msgCRLF     db 0Dh, 0Ah, 00
msgProgress db '.', 00
msgFailure  db 0Dh, 0Ah, 'ERROR : Press Any Key to Reboot', 00

CodeSize        EQU $-boot      
padding         db (512-CodeSize-2) dup(0)
bootsignature   db 55h,0aah
BB:             org boot+200h-2 

quasios         ends

                end     boot
;*************************************************************************
