;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; RDOS operating system
; Copyright (C) 2000, Leif Ekblad
;
; This program is free software; you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation; either version 2 of the License, or
; (at your option) any later version. The only exception to this rule
; is for commercial usage in embedded systems. For information on
; usage in commercial embedded systems, contact embedded@rdos.net
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with this program; if not, write to the Free Software
; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
;
; The author of this program may be contacted at leif@rdos.net
;
; FATBOOT.ASM
; Second stage boot-loader for FAT12 floppy
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                                                
                NAME  FatBoot

;;;;;;;;; INTERNAL PROCEDURES ;;;;;;;;;;;

.model Small

DATA_SEG = 6000h
MAX_IMAGES = 20

boot_struc      STRUC

boot_jmp                                        DB ?,?,?
boot_name                                       DB 8 DUP(?)
boot_bytes_per_sector           DW ?
boot_sectors_per_cluster        DB ?
boot_resv_sectors                       DW ?
boot_fats                                       DB ?
boot_root_dirs                          DW ?
boot_sectors16                          DW ?
boot_media                                      DB ?
boot_fat_sectors16                      DW ?
boot_sectors_per_cyl            DW ?
boot_heads                                      DW ?
boot_hidden_sectors                     DD ?
boot_sectors                            DD ?
boot_fat_sectors                        DD ?
boot_ext_flags                          DW ?
boot_fs_version                         DW ?
boot_root_cluster                       DD ?
boot_info_sector                        DW ?
boot_backup_sector                      DW ?

boot_struc              ENDS

fat_dir_struc   STRUC

fat_base                DB 8 DUP(?)
fat_ext                 DB 3 DUP(?)
fat_attrib              DB ?
fat_case                DB ?
fat_cr_time_ms  DB ?
fat_cr_time             DW ?
fat_cr_date             DW ?
fat_acc_date    DW ?
fat_cluster_hi  DW ?
fat_time                DW ?
fat_date                DW ?
fat_cluster             DW ?
fat_file_size   DD ?

fat_dir_struc   ENDS


bios_mem_type   STRUC

mem_base    DD ?, ?
mem_size    DD ?, ?
mem_type    DD ?

bios_mem_type   ENDS

_TEXT segment byte public use16 'CODE'

    .386p

; make sure the first instruction is a jump to the startup-code

    jmp Start

    extrn init:near

ReadCount               DW 0
RdosSectors                 DW 0,0
DriveNr                     DB 0
DefaultBoot         DB 0
BootSector          DD 0
FatSector           DD 0
RootSector          DD 0
DataSector          DD 0
SectorsPerFat       DD 0
CurrentCluster      DD 0
RootEntries         DW 0
SafeBoot            DB 0
SectorsPerCluster   DW 0
ImageSize           DD 0
CurrFatSector       DD 0

Tics                DD ?

OrgTimerVect        DD ?

DiscCyls            DW 1
DiscHeads           DW 1
SectorsPerCyl       DW 1

CurrEntry           DW ?
MenuEntries         DW 0
MenuArr             DW MAX_IMAGES DUP(0)
EntryCount          DW 0
EntryArr            DB MAX_IMAGES * 32 DUP(0)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;
;               NAME:                   singel_hex
;
;               DESCRIPTION:    
;
;               PARAMETERS:             AL              Value
;                                               AX              Result
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

singel_hex      PROC near
hex_conv_low:
        mov ah,al
        and al,0F0h
        rol al,1
        rol al,1
        rol al,1
        rol al,1
        cmp al,0Ah
        jb ok_low1
        add al,7
ok_low1:
        add al,30h
        and ah,0Fh
        cmp ah,0Ah
        jb ok_high1
        add ah,7
ok_high1:
        add ah,30h
        ret
singel_hex      ENDP

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;
;               NAME:                   WriteHexByte
;
;               DESCRIPTION:    Write hex byte on screen
;
;               PARAMETERS:             AL              Value
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

WriteHexByte    PROC near
        push ax
        mov ah,al
        and al,0F0h
        rol al,4
        cmp al,0Ah
        jb write_byte_low1
        add al,7
write_byte_low1:
        add al,'0'
        push ax
        push bx
        mov ah,0Eh
        mov bx,7
        int 10h
        pop bx
        pop ax
        mov al,ah
        and al,0Fh
        cmp al,0Ah
        jb write_byte_high1
        add al,7
write_byte_high1:
        add al,'0'
        push bx
        mov ah,0Eh
        mov bx,7
        int 10h
        pop bx
        pop ax
        ret
WriteHexByte    ENDP

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;
;               NAME:                   WriteHexWord
;
;               DESCRIPTION:    Write hex word on screen
;
;               PARAMETERS:             AX              Value
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

WriteHexWord    PROC near
        xchg al,ah
        call WriteHexByte
        xchg al,ah
        call WriteHexByte
        ret
WriteHexWord    ENDP

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;
;               NAME:                   WriteAsciiz
;
;               DESCRIPTION:    Write text to screen
;
;               PARAMETERS:             CS:SI           Message to write
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

WriteAsciiz     Proc near
        lods byte ptr cs:[si]
        or al,al
        jz WriteAsciizDone
        mov ah,0Eh
        mov bx,7
        int 10h
        jmp WriteAsciiz
WriteAsciizDone:
        ret
WriteAsciiz     Endp


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;
;               NAME:                   TimerInt
;
;               DESCRIPTION:    TIMER INTERRUPT
;
;               PARAMETERS:             
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

TimerInt:
    push eax
    mov eax,cs:Tics
    or eax,eax
    jz tiDone
;
    dec eax
    mov cs:Tics,eax

tiDone:
    pop eax
    jmp cs:OrgTimerVect
    

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;
;               NAME:                   ReadSector
;
;               DESCRIPTION:    Read a sector
;
;               PARAMETERS:             EDX             Sector #
;                                               DS:BX   Address of buffer
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

ReadSector      Proc near
    push cx
    mov cx,3    

rsLoop:    
    push es
        push ax
        push bx
        push cx
        push edx
;       
    mov ax,ds
    mov es,ax
    push edx
    pop ax
    pop dx
        push bx
        div cs:SectorsPerCyl
        inc dl
        mov bl,dl
        xor dx,dx
        div cs:DiscHeads
        mov bh,dl
        mov dx,ax
        mov ax,201h
        mov cl,6
        shl dh,cl
        or dh,bl
        mov cx,dx
        xchg ch,cl
        mov dl,cs:DriveNr
        mov dh,bh
        pop bx
        int 13h
;       
        pop edx
        pop cx
        pop bx
        pop ax
        pop es
        jnc rsDone
;
        push ax
        push dx
        xor ax,ax
        mov dl,cs:DriveNr
        int 13h
        pop dx
        pop ax
    loop rsLoop
;
    stc
    
rsDone:
    pop cx
        ret
ReadSector      Endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;
;               NAME:                   ReadFatSector
;
;               DESCRIPTION:    Read fat sector
;
;               PARAMETERS:             EDX             Sector #
;                                               DS:200  Address of buffer
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

ReadFatSector   Proc near
    cmp edx,cs:CurrFatSector
    clc
    je ReadFatSectorDone
;
    call ReadSector
    mov cs:CurrFatSector,edx

ReadFatSectorDone:
        ret
ReadFatSector   Endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;
;               NAME:                   InitIrq
;
;               DESCRIPTION:    Init IRQs
;
;               PARAMETERS:             DS:BX       Sector 0 buffer
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

InitIrq Proc near
    push ds
    push es
    push bx
;    
    mov ax,ds
    mov es,ax
    mov di,bx
;    
    xor ax,ax
    mov ds,ax
    mov bx,8 SHL 2
    mov eax,[bx]
    mov cs:OrgTimerVect,eax
    mov word ptr [bx],OFFSET TimerInt
    mov [bx+2],cs
;
    pop bx
    pop es
    pop ds
    ret
InitIrq Endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;
;               NAME:                   NextCluster
;
;               DESCRIPTION:    Find next cluster for FAT12
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

NextCluster     PROC near
        push ebx
        push cx
;
    mov edx,cs:CurrentCluster
        mov cx,dx
        add dx,dx
        add dx,cx
        mov cx,dx
        movzx edx,dx
        shr edx,10
        add edx,cs:FatSector
        and cx,3FFh
        clc
        rcr cx,1
        pushf
        mov bx,200h
        call ReadFatSector
        jnc ncLocked
        popf
        stc
        jmp ncDone

ncLocked:
        mov bx,cx
        add bx,200h
        popf
        jc ncHigh
        mov cl,[bx]
        inc bx
        test bx,1FFh
        jnz ncLowOk
        inc edx
        mov bx,200h
        call ReadFatSector
        jc ncDone

ncLowOk:
        mov ch,[bx]
        and cx,0FFFh
        jmp ncOk

ncHigh:
        mov ch,[bx]
        and ch,0F0h
        inc bx
        test bx,1FFh
        jnz ncHighOk
        inc edx
        mov bx,200h
        call ReadFatSector
        jc ncDone

ncHighOk:
        mov cl,[bx]
        rol cx,4
ncOk:
        movzx edx,cx
        mov cs:CurrentCluster,edx
        cmp edx,0FF8h
        cmc

ncDone:
        pop cx
        pop ebx
        ret
NextCluster     ENDP

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;
;               NAME:                   GetCurrentSector
;
;               DESCRIPTION:    Get current sector
;
;       RETURNS:        EDX     Sector
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

GetCurrentSector    Proc near
    mov edx,cs:CurrentCluster
        sub edx,2
        movzx eax,cs:SectorsPerCluster
        mul edx
        mov edx,eax
        add edx,cs:DataSector
        ret
GetCurrentSector        Endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;
;               NAME:                   CheckDirEntry
;
;               DESCRIPTION:    Check a directory entry for a RDOS image file
;
;       PARAMETERS:     DS:SI       Dir entry
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

CheckDirEntry   Proc near
    push es
    pushad
;
    mov ax,word ptr [si].fat_ext
    cmp ax,'IB'
    jne cdeDone
;
    mov al,[si].fat_ext+2
    cmp al,'N'
    jne cdeDone
;
    mov al,[si].fat_attrib
    test al,10h
    jnz cdeDone
;
    push cs:CurrentCluster
;    
    movzx edx,ds:[si].fat_cluster
    mov cs:CurrentCluster,edx
;
    call GetCurrentSector
    mov bx,400h
    call ReadSector
    mov eax,[bx]    
    cmp eax,5A1E75D4h
    jne cdeNotRdos
;
    mov cx,cs:EntryCount
    mov di,OFFSET EntryArr
    mov ax,cx
    shl ax,5
    add di,ax
    mov ax,cs
    mov es,ax
    mov cx,10h
    rep movsw        
    inc cs:EntryCount

cdeNotRdos:
    pop cs:CurrentCluster       

cdeDone:    
    popad
    pop es
    ret
CheckDirEntry   Endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;
;               NAME:                   ScanRootDir
;
;               DESCRIPTION:    Scan root directory
;
;       PARAMETERS:     DI          File to scan for
;
;       RETURNS:        EDX         Start cluster # of file
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

ScanRootDir Proc near
    push es
    push eax
    push cx
;    
    mov ax,cs
    mov es,ax
    xor si,si
    mov cx,cs:RootEntries
    mov edx,cs:RootSector
    push bx
    xor bx,bx
    call ReadSector
    pop bx

srdLoop:
    call CheckDirEntry
    add si,20h
    test si,1FFh
    jnz srdNextEntry
;
    xor si,si
    inc edx
;    
    push bx
    xor bx,bx
    call ReadSector
    pop bx

srdNextEntry:
    loop srdLoop

srdDone:
    pop cx
    pop eax
    pop es
    ret
ScanRootDir Endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;
;               NAME:                   FindLine
;
;               DESCRIPTION:    Find a specific line
;
;       PARAMETERS:     SI      Filename
;
;       RETURNS:        AX      Entry offset
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

FindLine    Proc near
    push cx
    push edx
    push di
;
    mov cx,cs:EntryCount
    mov di,OFFSET EntryArr
    or cx,cx
    jz flFail

flLoop:
    mov edx,cs:[si]
    cmp edx,cs:[di]
    jne flNext
;
    mov edx,cs:[si+4]
    cmp edx,cs:[di+4]
    jne flNext
;
    mov dword ptr cs:[di],0
    mov ax,di
    clc
    jmp flDone

flNext:
    add di,20h
    loop flLoop

flFail:
    stc

flDone:
    pop di
    pop edx
    pop cx      
    ret
FindLine    Endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;
;               NAME:                   WriteLine
;
;               DESCRIPTION:    Write one full line
;
;       PARAMETERS:     SI      Offset to text
;                       ES:DI   Screen buffer
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

WriteLine  Proc near
    push ax
    push cx
    push si
;
    mov ah,7
    mov cx,80

wlLoop:
    lods byte ptr cs:[si]
    stosw
    loop wlLoop
;
    pop si
    pop cx
    pop ax    
    ret
WriteLine   Endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;
;               NAME:                   WriteCust
;
;               DESCRIPTION:    Write a custom line
;
;       PARAMETERS:     SI      Offset to dir entry
;                       ES:DI   Screen buffer
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

cust_bl    DB '                    º  RDOS - ', 0
cust_el    DB 'º                   ', 0
cust_ext   DB '.BIN boot', 0

WriteCust   Proc near
    push cx
    push si
;
    mov ah,7
    mov cx,80

    push si
    mov si,OFFSET cust_bl

wcBeginLoop:
    lods byte ptr cs:[si]
    or al,al
    jz wcBeginDone
;    
    stosw
    loop wcBeginLoop
    
wcBeginDone:
    pop si
;
    mov cx,30

wcNameLoop:
    lods byte ptr cs:[si]
    cmp al,' '
    je wcNameDone
;
    stosw
    loop wcNameLoop            

wcNameDone:
    mov si,OFFSET cust_ext

wcExtLoop:
    lods byte ptr cs:[si]
    or al,al
    je wcExtDone
;
    stosw
    loop wcExtLoop
    jmp wcAddEnd

wcExtDone:
    mov al,' '

wcPadLoop:
    stosw
    loop wcPadLoop

wcAddEnd:
    mov si,OFFSET cust_el

wcAddLoop:
    lods byte ptr cs:[si]
    or al,al
    je wcDone
;
    stosw
    jmp wcAddLoop 

wcDone:   
    pop si
    pop cx    
    ret
WriteCust   Endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;
;               NAME:                   WriteMenu
;
;               DESCRIPTION:    Write boot-menu
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

blank_line DB '                                                                                  '
l1         DB '                                  RDOS Bootloader                                 '
l2         DB '                    ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»                     '
l4         DB '                    ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ                     '
norm_line  DB '                    º  RDOS - normal boot                   º                     '
safe_line  DB '                    º  RDOS - safe mode boot                º                     '

norm_file   DB 'RDOS    '
safe_file   DB 'SAFE    '
                                  
WriteMenu  Proc near
    push es
    pushad
;    
    mov ax,0B800h
    mov es,ax
    xor di,di
    mov si,OFFSET l1
    call WriteLine
    mov si,OFFSET l2
    call WriteLine
    mov cx,22
;
    mov cs:MenuEntries,0
    mov bx,OFFSET MenuArr
;
    mov si,OFFSET norm_file
    call FindLine
    jc wmNormOk
;    
    inc cs:MenuEntries
    dec cx
    mov si,OFFSET norm_line
    call WriteLine
    mov cs:[bx],ax
    add bx,2

wmNormOk:
    mov si,OFFSET safe_file
    call FindLine
    jc wmSafeOk
;    
    inc cs:MenuEntries
    dec cx
    mov si,OFFSET safe_line
    call WriteLine    
    mov cs:[bx],ax
    add bx,2

wmSafeOk: 
    mov dx,cs:EntryCount
    mov si,OFFSET EntryArr
    or dx,dx
    jz wmEntryDone

wmEntryLoop:
    mov al,cs:[si]
    or al,al
    jz wmEntryNext
;
    inc cs:MenuEntries
    dec cx
    mov cs:[bx],si
    add bx,2
    call WriteCust

wmEntryNext:
    add si,20h
    sub dx,1
    jnz wmEntryLoop

wmEntryDone:
    mov si,OFFSET l4
    call WriteLine
    mov si,OFFSET blank_line

wbl:
    call WriteLine
    loop wbl
;
    popad
    pop es           
    ret
WriteMenu   Endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;
;               NAME:                   MarkEntry
;
;               DESCRIPTION:    Mark a entry
;
;       PARAMETERS:     AX      Entry #
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

MarkEntry   Proc near
    push ds
    pusha
;
    add ax,2
    mov dx,2 * 80
    mul dx
    mov bx,ax
    add bx,2 * 22
    mov cx,37
;
    mov ax,0B800h
    mov ds,ax
    inc bx

meLoop:
    mov byte ptr [bx],70h
    add bx,2
    loop meLoop
;    
    popa
    pop ds
    ret
MarkEntry   Endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;
;               NAME:                   UnmarkEntry
;
;               DESCRIPTION:    Unmark a entry
;
;       PARAMETERS:     AX      Entry #
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

UnmarkEntry   Proc near
    push ds
    pusha
;
    add ax,2
    mov dx,2 * 80
    mul dx
    mov bx,ax
    add bx,2 * 22
    mov cx,37
;
    mov ax,0B800h
    mov ds,ax
    inc bx

umeLoop:
    mov byte ptr [bx],7
    add bx,2
    loop umeLoop
;    
    popa
    pop ds
    ret
UnmarkEntry   Endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;
;               NAME:                   HandleMenu
;
;               DESCRIPTION:    Handle menu
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

HandleMenu  Proc near    
    mov cs:Tics, 5 * 17
    movzx ax,cs:DefaultBoot
    mov cs:CurrEntry,ax

hmWaitKey:
    mov ah,1
    int 16h
    jnz hmWait
;
    mov eax,cs:Tics
    or eax,eax
    jne hmWaitKey
;
    jmp hmOk
        
hmWait:
    mov ax,0
    int 16h 
    cmp al,0Dh
    je hmOk
;
    cmp ax,4800h
    jne hmNotUp

hmUp:
    mov ax,cs:CurrEntry
    or ax,ax
    jz hmWait
;
    call UnmarkEntry
    dec ax
    call MarkEntry
    mov cs:CurrEntry,ax
    jmp hmWait   

hmNotUp:
    cmp ax,5000h
    jne hmWait

hmDown:
    mov ax,cs:CurrEntry
    inc ax
    cmp ax,cs:MenuEntries
    je hmWait
;
    dec ax
    call UnmarkEntry    
    inc ax
    call MarkEntry
    mov cs:CurrEntry,ax
    jmp hmWait

hmOk: 
    mov si,cs:CurrEntry
    dec si
    add si,si
    mov si,cs:[si].MenuArr
    movzx edx,cs:[si].fat_cluster
    mov cs:CurrentCluster,edx
    mov eax,cs:[si].fat_file_size
    mov cs:ImageSize,eax
    clc
    ret
HandleMenu  Endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;
;               NAME:                   ClearScreen
;
;               DESCRIPTION:    Clear screen
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                                  
ClearScreen  Proc near
    push es
    pusha
;    
    mov ax,0B800h
    mov es,ax
    xor di,di
    mov ax,0720h
    mov cx,80 * 25
    rep stosw
;
    mov ax,3
    int 10h
;
    popa
    pop es
    ret
ClearScreen Endp       

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;
;               NAME:                   GateA20
;
;               DESCRIPTION:    Enable A20 line
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

GateA20 Proc near
wait_gate1:
        in al,64h
        and al,2
        jnz wait_gate1
        mov al,0D1h
        out 64h,al
wait_gate2:
        in al,64h
        and al,2
        jnz wait_gate2
        mov al,0DFh
        out 60h,al
wait_gate3:
        in al,64h
        and al,2
        jnz wait_gate3
        xor cx,cx
gate_wait:
        inc ax
        loop gate_wait
        ret
GateA20 Endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;
;               NAME:                   InitGdt
;
;               DESCRIPTION:    Init protected mode GDT
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

source_sel      EQU 8
dest_sel        EQU 10h
flat_sel        EQU 18h

LoadGdt:
load_gdt0:
                        DW 27h
                        DD 0
                        DW 0
load_gdt_source:
            DW 0FFFFh
            DD 92000000h
            DW 0
load_gdt_dest:
            DW 0FFFFh
            DD 92300000h
            DW 0
load_gdt_flat:
                        DW 0FFFFh
                        DD 92000000h
                        DW 008Fh
load_gdt_cs:
                        DW 0FFFFh
                        DD 9A000000h
                        DW 0

InitGdt Proc near
        mov ax,cs
        movzx eax,ax
        shl eax,4
        add eax,OFFSET LoadGdt
        mov dword ptr cs:load_gdt0+2,eax
        lgdt fword ptr cs:load_gdt0
;
        mov ax,cs
        movzx eax,ax
        shl eax,4
        or dword ptr cs:load_gdt_cs+2,eax
        ret
InitGdt Endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;
;               NAME:                   MoveData
;
;               DESCRIPTION:    Move data to extended memory
;
;               PARAMETERS:             ESI             Linear source address
;                                               EDI             Linear dest address
;                                               ECX             Number of bytes to move
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

MoveData        Proc near
        push ds
        push es
        pushad
;
        mov eax,esi
        mov dword ptr cs:load_gdt_source+2,eax
        mov al,92h
        xchg al,byte ptr cs:load_gdt_source+5
        mov byte ptr cs:load_gdt_source+7,al
;
        mov eax,edi
        mov dword ptr cs:load_gdt_dest+2,eax
        mov al,92h
        xchg al,byte ptr cs:load_gdt_dest+5
        mov byte ptr cs:load_gdt_dest+7,al
        mov word ptr cs:MoveDataRmCs,cs
;
        cli
        mov eax,cr0
        or al,1
        mov cr0,eax
;
        db 0EAh
        dw OFFSET MoveDataPm
        dw 20h

MoveDataPm:
        mov ax,source_sel
        mov ds,ax
        mov ax,dest_sel
        mov es,ax
        xor esi,esi
        xor edi,edi
        rep movs byte ptr es:[edi],[esi]
;
        mov eax,cr0
        and al,NOT 1
        mov cr0,eax
;
        db 0EAh
        dw OFFSET MoveDataRm
MoveDataRmCs:
        dw 0

MoveDataRm:
        sti
        popad
        pop es
        pop ds
        ret
MoveData        Endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;
;               NAME:                   LoadAdapter
;
;               DESCRIPTION:    Load adapter into extended memory
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

LoadAdapter     Proc near
        push ds
        push esi
;    
    mov ax,cs
    mov es,ax

laClusterLoop:
    call GetCurrentSector
    mov cx,cs:SectorsPerCluster    

laSectorLoop:
    xor si,si
    push bx
    xor bx,bx
    call ReadSector
    pop bx
    jc laError
;
    push edx
    push cx
        mov esi,16 * DATA_SEG
    mov ecx,512
        call MoveData
        add edi,ecx
        pop cx
        pop edx
;
    sub cs:ImageSize,200h
    jbe laDone
;
    inc edx
    loop laSectorLoop
;
    call NextCluster
    jnc laClusterLoop
    jmp laDone
    
laError:
        mov si,OFFSET ReadError
        call WriteAsciiz
        
laDone:
        pop esi
        pop ds
        ret     
LoadAdapter     Endp

LoadAdapterDone DB 'Load adapter done',0

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;
;               NAME:                   GetRamSize
;
;               DESCRIPTION:    Get size of physical memory
;
;               RETURNS:                ECX             Number of bytes of physical memory
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

bios_buf bios_mem_type <>

GetRamSize      Proc near
    push ds
    push es
    push eax
    push ebx
    push di
;
    mov ax,cs
    mov es,ax
    mov di,OFFSET bios_buf
    xor ebx,ebx

GetRamSizeRetry:    
    mov eax,0E820h
    mov ecx,20
    mov edx,534D4150h
    int 15h
    jc GetRamSizeScan
;
    cmp eax,534D4150h
    jne GetRamSizeScan
;
    mov eax,cs:bios_buf.mem_type
    cmp eax,1
    jne GetRamSizeNext 
;
    mov ecx,cs:bios_buf.mem_base
    cmp ecx,100000h
    jb GetRamSizeNext
;
    add ecx,cs:bios_buf.mem_size
    jmp GetRamSizeExit

GetRamSizeNext:
    or ebx,ebx
    jnz GetRamSizeRetry
    
GetRamSizeScan:
    mov word ptr cs:GetRamSizeRmCs,cs
    cli
    mov eax,cr0
    or al,1
    mov cr0,eax
;
    db 0EAh
    dw OFFSET GetRamSizePm
    dw 20h

GetRamSizePm:
    mov ax,flat_sel
    mov ds,ax
    mov ebx,110000h
    mov eax,851A7EC2h

GetRamSizeLoop:
    mov [ebx],eax
    cmp eax,[ebx]
    jne GetRamSizeDone
    add ebx,1000h
    jnc GetRamSizeLoop

GetRamSizeDone:
    mov ax,source_sel
    mov ds,ax
;
    mov eax,cr0
    and al,NOT 1
    mov cr0,eax
;
    db 0EAh
    dw OFFSET GetRamSizeRm
GetRamSizeRmCs:
    dw 0

GetRamSizeRm:
    mov ecx,ebx
    sti

GetRamSizeExit: 
    pop di
    pop ebx
    pop eax
    pop es
    pop ds
    ret
GetRamSize      Endp

ReadError       db 'Cannot read rdos.bin',0Dh,0Ah,0
LoadMsg         db 0Dh,0Ah,'Loading Rdos operating system',0

InvalidDisc db 'Cannot read floppy disc', 0Dh, 0Ah, 0
BootNotFound db 'Cannot find boot image', 0Dh, 0Ah, 0
BootNoEntries db 'No image file', 0Dh, 0Ah, 0

boot_no_entry:
    mov si,OFFSET BootNoEntries
    call WriteAsciiz
    jmp part_stop

read_part_error:
    mov si,OFFSET InvalidDisc
    call WriteAsciiz

part_stop:
    jmp part_stop

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;               DESCRIPTION:    Second stage boot-loader entry point
;
;               RETURNS:            DL          Bios disc #
;                       AL          Default boot #
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    public Start

Start:
    sti
    mov cs:DriveNr,dl
    mov cs:DefaultBoot,al
    mov ax,DATA_SEG
    mov ds,ax
    xor bx,bx
    call InitIrq
    jc read_part_error
;    
    xor edx,edx
    mov cs:BootSector,edx
    xor bx,bx
    call ReadSector
        jc read_part_error
;    
    mov ax,ds:boot_sectors_per_cyl
    mov cs:SectorsPerCyl,ax
    mov ax,ds:boot_heads
    mov cs:DiscHeads,ax
    mov cs:DiscCyls,80
;    
    movzx eax,ds:boot_resv_sectors
    add eax,cs:BootSector
    mov cs:FatSector,eax
;
    movzx eax,ds:boot_fat_sectors16
    mov cs:SectorsPerFat,eax
    mov eax,cs:FatSector
;
    mov cl,ds:boot_fats
    or cl,cl
    jz read_part_error

boot_fat_adv_loop:
    add eax,cs:SectorsPerFat
    sub ds:boot_fats,1
    jnz boot_fat_adv_loop
;
    mov cs:RootSector,eax
    movzx ecx,ds:boot_root_dirs
    shr ecx,4
    add eax,ecx
;
    mov cs:DataSector,eax
    mov eax,ds:boot_root_cluster
    mov cs:CurrentCluster,eax
    mov ax,ds:boot_root_dirs
    mov cs:RootEntries,ax
    movzx ax,ds:boot_sectors_per_cluster
    mov cs:SectorsPerCluster,ax
    mov cs:EntryCount,0
    call ScanRootDir
    mov ax,cs:EntryCount
    or ax,ax
    jz boot_no_entry
;    
    call WriteMenu
;
    movzx ax,cs:DefaultBoot
    cmp ax,cs:MenuEntries
    jb LoadDefaultOk
;
    xor ax,ax

LoadDefaultOk:
    mov cs:CurrEntry,ax
    call MarkEntry
    call HandleMenu       
    call ClearScreen
        mov si,OFFSET LoadMsg
        call WriteAsciiz
        call GateA20
        call InitGdt
        call GetRamSize
        mov edi,ecx
        mov ecx,cs:ImageSize
        dec ecx
        and cx,0F000h
        add ecx,1000h
        push ecx
        pop ax
        pop ax
        sub edi,ecx
        push edi
        push ecx
        call LoadAdapter
        mov dx,3F2h
        mov al,0
        out dx,al
        pop ecx
        pop edi
        jmp init

stop:
        jmp stop

_TEXT   ends    

    END Start