bootsect.asm
kolibrios-nextgen_kolibrios-kernel/kernel/sec_loader/trunk/boot/cdfs/bootsect.asm
; Copyright (c) 2008-2009, diamond
; All rights reserved.
;
; Redistribution and use in source and binary forms, with or without
; modification, are permitted provided that the following conditions are met:
;       * Redistributions of source code must retain the above copyright
;       notice, this list of conditions and the following disclaimer.
;       * Redistributions in binary form must reproduce the above copyright
;       notice, this list of conditions and the following disclaimer in the
;       documentation and/or other materials provided with the distribution.
;       * Neither the name of the <organization> nor the
;       names of its contributors may be used to endorse or promote products
;       derived from this software without specific prior written permission.
;
; THIS SOFTWARE IS PROVIDED BY Alexey Teplov aka <Lrz> ''AS IS'' AND ANY
; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
; DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
;*****************************************************************************

        jmp     far 0:real_start
; special text
org $+0x7C00
real_start:
; initialize
        xor     ax, ax
        mov     ss, ax
        mov     sp, 0x7C00
        mov     ds, ax
        mov     es, ax
        cld
        sti
        mov     [bootdrive], dl
; check LBA support
        mov     ah, 41h
        mov     bx, 55AAh
        int     13h
        mov     si, aNoLBA
        jc      err_
        cmp     bx, 0AA55h
        jnz     err_
        test    cl, 1
        jz      err_
; get file system information
; scan for Primary Volume Descriptor
        db      66h
        push    10h-1
        pop     eax
pvd_scan_loop:
        mov     cx, 1
        inc     eax
        mov     bx, 0x1000
        call    read_sectors
        jnc     @f
fatal_read_err:
        mov     si, aReadError
err_:
        call    out_string
        mov     si, aPressAnyKey
        call    out_string
        xor     ax, ax
        int     16h
        int     18h
        jmp     $
@@:
        push    ds
        pop     es
        cmp     word [bx+1], 'CD'
        jnz     pvd_scan_loop
        cmp     word [bx+3], '00'
        jnz     pvd_scan_loop
        cmp     byte [bx+5], '1'
        jnz     pvd_scan_loop
; we have found ISO9660 descriptor, look for type
        cmp     byte [bx], 1    ; Primary Volume Descriptor?
        jz      pvd_found
        cmp     byte [bx], 0xFF ; Volume Descriptor Set Terminator?
        jnz     pvd_scan_loop
; Volume Descriptor Set Terminator reached, no PVD found - fatal error
        mov     si, no_pvd
        jmp     err_
pvd_found:
        add     bx, 80h
        mov     ax, [bx]
        mov     [lb_size], ax
; calculate number of logical blocks in one sector
        mov     ax, 800h
        cwd
        div     word [bx]
        mov     [lb_per_sec], ax
; get location of root directory
        mov     di, root_location
        movzx   eax, byte [bx+1Dh]
        add     eax, [bx+1Eh]
        stosd
; get memory size
        int     12h
        mov     si, nomem_str
        cmp     ax, 71000h / 400h
        jb      err_
        shr     ax, 1
        sub     ax, 60000h / 800h
        mov     [size_rest], ax
        mov     [free_ptr], 60000h / 800h
; load path table
; if size > 62K => it's very strange, avoid using it
; if size > (size of cache)/2 => avoid using it too
        mov     ecx, [bx+4]
        cmp     ecx, 0x10000 - 0x800
        ja      nopathtable
        shr     ax, 1
        cmp     ax, 0x20
        jae     @f
        shl     ax, 11
        cmp     cx, ax
        ja      nopathtable
@@:
; size is ok, try to load it
        mov     [pathtable_size], cx
        mov     eax, [bx+12]
        xor     edx, edx
        div     dword [lb_per_sec]
        imul    dx, [bx]
        mov     [pathtable_start], dx
        add     cx, dx
        call    cx_to_sectors
        xor     bx, bx
        push    6000h
        pop     es
        call    read_sectors
        jc      nopathtable
; path table has been loaded
        inc     [use_path_table]
        sub     [size_rest], cx
        add     [free_ptr], cx
nopathtable:
; init cache
        mov     ax, [size_rest]
        mov     [cache_size], ax
        mov     ax, [free_ptr]
        mov     [cache_start], ax
; load secondary loader
        mov     di, secondary_loader_info
        call    load_file
        test    bx, bx
        jnz     noloader
; set registers for secondary loader
        mov     ah, [bootdrive]
        mov     al, 'c'
        mov     bx, 'is'
        mov     si, callback
        jmp     far [si-callback+secondary_loader_info] ; jump to 1000:0000

noloader:
        mov     si, aKernelNotFound
        jmp     err_

read_sectors:
; es:bx = pointer to data
; eax = first sector
; cx = number of sectors
        pushad
        push    ds
do_read_sectors:
        push    ax
        push    cx
        cmp     cx, 0x7F
        jbe     @f
        mov     cx, 0x7F
@@:
; create disk address packet on the stack
; dq starting LBA
        db      66h
        push    0
        push    eax
; dd buffer
        push    es
        push    bx
; dw number of blocks to transfer (no more than 0x7F)
        push    cx
; dw packet size in bytes
        push    10h
; issue BIOS call
        push    ss
        pop     ds
        mov     si, sp
        mov     dl, [cs:bootdrive]
        mov     ah, 42h
        int     13h
        jc      diskreaderr
; restore stack
        add     sp, 10h
; increase current sector & buffer; decrease number of sectors
        movzx   esi, cx
        mov     ax, es
        shl     cx, 7
        add     ax, cx
        mov     es, ax
        pop     cx
        pop     ax
        add     eax, esi
        sub     cx, si
        jnz     do_read_sectors
        pop     ds
        popad
        ret
diskreaderr:
        add     sp, 10h + 2*2
        pop     ds
        popad
        stc
out_string.ret:
        ret

out_string:
; in: ds:si -> ASCIIZ string
        lodsb
        test    al, al
        jz      .ret
        mov     ah, 0Eh
        mov     bx, 7
        int     10h
        jmp     out_string

aNoLBA          db      'The drive does not support LBA!',0
aReadError      db      'Read error',0
no_pvd          db      'Primary Volume Descriptor not found!',0
nomem_str       db      'No memory',0
aPressAnyKey    db      13,10,'Press any key...',13,10,0

load_file:
; in: ds:di -> information structure
;       dw:dw   address
;       dw      limit in 4Kb blocks (0x1000 bytes) (must be non-zero and not greater than 0x100)
;       ASCIIZ  name
; out: bx = status: bx=0 - ok, bx=1 - file is too big, only part of file was loaded, bx=2 - file not found
; out: dx:ax = file size (0xFFFFFFFF if file not found)
; parse path to the file
        lea     si, [di+6]
        mov     eax, [cs:root_location]
        cmp     [cs:use_path_table], 0
        jz      parse_dir
; scan for path in path table
        push    di
        push    6000h
        pop     es
        mov     di, [cs:pathtable_start]        ; es:di = pointer to current entry in path table
        mov     dx, 1   ; dx = number of current entry in path table, start from 1
        mov     cx, [cs:pathtable_size]
pathtable_newparent:
        mov     bx, dx  ; bx = number of current parent in path table: root = 1
scan_path_table_e:
        call    is_last_component
        jnc     path_table_scanned
scan_path_table_i:
        cmp     word [es:di+6], bx
        jb      .next
        ja      path_table_notfound
        call    test_filename1
        jc      .next
@@:
        lodsb
        cmp     al, '/'
        jnz     @b
        jmp     pathtable_newparent
.next:
; go to next entry
        inc     dx
        movzx   ax, byte [es:di]
        add     ax, 8+1
        and     al, not 1
        add     di, ax
        sub     cx, ax
        ja      scan_path_table_i
path_table_notfound:
        pop     di
        mov     ax, -1
        mov     dx, ax
        mov     bx, 2   ; file not found
        ret
path_table_scanned:
        movzx   eax, byte [es:di+1]
        add     eax, [es:di+2]
        pop     di
parse_dir:
; eax = logical block, ds:di -> information structure, ds:si -> file name
; was the folder already read?
        push    di ds
        push    cs
        pop     ds
        mov     [cur_desc_end], 2000h
        mov     bx, cachelist
.scan1:
        mov     bx, [bx+2]
        cmp     bx, cachelist
        jz      .notfound
        cmp     [bx+4], eax
        jnz     .scan1
.found:
; yes; delete this item from the list (the following code will append this item to the tail)
        mov     di, [bx]
        push    word [bx+2]
        pop     word [di+2]
        mov     di, [bx+2]
        push    word [bx]
        pop     word [di]
        mov     di, bx
        jmp     .scan
.notfound:
; no; load first sector of the folder to get its size
        push    eax
        push    si
        mov     si, 1
        call    load_phys_sector_for_lb_force
        mov     bx, si
        pop     si
        pop     eax
        jnc     @f
; read error - return
.readerr:
        pop     ds
.readerr2:
        pop     di
        mov     ax, -1
        mov     dx, ax
        mov     bx, 3
        ret
@@:
; first item of the folder describes the folder itself
; do not cache too big folders: size < 64K and size <= (total cache size)/2
        cmp     word [bx+12], 0
        jnz     .nocache
        mov     cx, [cache_size]        ; cx = cache size in sectors
        shr     cx, 1                   ; cx = (cache size)/2
        cmp     cx, 0x20
        jae     @f
        shl     cx, 11
        cmp     [bx+10], cx
        ja      .nocache
@@:
; we want to cache this folder; get space for it
        mov     cx, [bx+10]
        call    cx_to_sectors
        jnz     .yescache
.nocache:
        push    dword [bx+10]
        pop     dword [cur_nocache_len]
        call    lb_to_sector
        push    ds
        pop     es
        pop     ds
.nocache_loop:
        push    eax
        mov     dx, 1800h
        call    scan_for_filename_in_sector
        mov     cx, dx
        pop     eax
        jnc     .j_scandone
        sub     cx, bx
        sub     word [es:cur_nocache_len], cx
        sbb     word [es:cur_nocache_len+2], 0
        jb      .j_scandone
        ja      @f
        cmp     word [es:cur_nocache_len], 0
        jz      .j_scandone
@@:
        mov     cx, 1
        inc     eax
        push    es
        mov     bx, 1000h
        call    read_sectors
        pop     es
        jc      .readerr2
        jmp     .nocache_loop
.j_scandone:
        jmp     .scandone
.yescache:
        push    bx
        mov     bx, [cachelist.head]
.freeloop:
        cmp     cx, [size_rest]
        jbe     .sizeok
@@:
; if we are here: there is not enough free space, so we must delete old folders' data
; N.B. We know that after deleting some folders the space will be available (size <= (total cache size)/2).
; one loop iteration: delete data of one folder
        pusha
        mov     dx, [bx+10]
        mov     es, dx          ; es = segment of folder data to be deleted
        xor     di, di
        mov     ax, [bx+8]
        add     ax, 0x7FF
        rcr     ax, 1
        shr     ax, 10
        push    ax
        shl     ax, 11-4        ; get number of paragraphs in folder data to be deleted
        mov     cx, [cache_size]
        add     cx, [cache_start]
        push    ds
        push    ax
        add     ax, dx
        mov     ds, ax
        pop     ax
        shl     cx, 11-4
        sub     cx, dx          ; cx = number of paragraphs to be moved
        push    si
        xor     si, si
; move cx paragraphs from ds:si to es:di to get free space in the end of cache
@@:
        sub     cx, 1000h
        jbe     @f
        push    cx
        mov     cx, 8000h
        rep movsw
        mov     cx, ds
        add     cx, 1000h
        mov     ds, cx
        mov     cx, es
        add     cx, 1000h
        mov     es, cx
        pop     cx
        jmp     @b
@@:
        add     cx, 1000h
        shl     cx, 3
        rep movsw
        pop     si
        pop     ds
; correct positions in cache for existing items
        mov     cx, 80h
        mov     di, 8400h
.correct:
        cmp     [di+10], dx
        jbe     @f
        sub     [di+10], ax
@@:
        add     di, 12
        loop    .correct
; some additional space is free now
        pop     ax
        add     [size_rest], ax
        sub     [free_ptr], ax
; add cache item to the list of free items
        mov     dx, [bx]
        mov     ax, [free_cache_item]
        mov     [bx], ax
        mov     [free_cache_item], bx
        mov     bx, dx
; current iteration done
        popa
        jmp     .freeloop
.sizeok:
        mov     [cachelist.head], bx
        mov     word [bx+2], cachelist
; allocate new item in cache
        mov     di, [free_cache_item]
        test    di, di
        jz      .nofree
        push    word [di]
        pop     [free_cache_item]
        jmp     @f
.nofree:
        mov     di, [last_cache_item]
        add     [last_cache_item], 12
@@:
        pop     bx
        push    si di
;       mov     [di+4], eax     ; start of folder
        scasd
        stosd
        push    ax
        mov     ax, [free_ptr]
        shl     ax, 11-4
        mov     [di+10-8], ax
        mov     es, ax
        pop     ax
        add     [free_ptr], cx
        sub     [size_rest], cx
; read folder data
; first sector is already in memory, 0000:bx
        pusha
        mov     cx, [bx+10]
        mov     [di+8-8], cx    ; folder size in bytes
        mov     si, bx
        xor     di, di
        mov     cx, 0x1800
        sub     cx, si
        rep movsb
        pop     ax
        push    di
        popa
; read rest of folder
        mov     esi, dword [lb_per_sec]
        add     eax, esi
        dec     si
        not     si
        and     ax, si
        mov     si, word [bx+10]
        mov     bx, di
        pop     di
        sub     si, bx
        jbe     @f
        mov     [cur_limit], esi
        call    read_many_bytes
        pop     si
        jnc     .scan
        jmp     .readerr
@@:
        pop     si
.scan:
; now we have required cache item; append it to the end of list
        mov     bx, [cachelist.tail]
        mov     [cachelist.tail], di
        mov     [di+2], bx
        mov     word [di], cachelist
        mov     [bx], di
; scan for given filename
        mov     es, [di+10]
        mov     dx, [di+8]
        pop     ds
        xor     bx, bx
        call    scan_for_filename_in_sector
.scandone:
        push    cs
        pop     es
        mov     bx, 2000h
        cmp     bx, [es:cur_desc_end]
        jnz     filefound
j_notfound:
        jmp     path_table_notfound
filefound:
@@:
        lodsb
        test    al, al
        jz      @f
        cmp     al, '/'
        jnz     @b
@@:
        mov     cl, [es:bx+8]
        test    al, al
        jz      @f
; parse next component of file name
        test    cl, 2   ; directory?
        jz      j_notfound
        mov     eax, [es:bx]
        pop     di
        jmp     parse_dir
@@:
        test    cl, 2   ; directory?
        jnz     j_notfound      ; do not allow read directories as regular files
; ok, now load the file
        pop     di
        les     bx, [di]
        call    normalize
        movzx   esi, word [di+4]        ; esi = limit in 4K blocks
        shl     esi, 12         ; esi = limit in bytes
        push    cs
        pop     ds
        mov     [cur_limit], esi
        mov     di, 2000h
loadloop:
        and     [cur_start], 0
.loadnew:
        mov     esi, [cur_limit]
        mov     eax, [cur_start]
        add     esi, eax
        mov     [overflow], 1
        sub     esi, [di+4]
        jb      @f
        xor     esi, esi
        dec     [overflow]
@@:
        add     esi, [di+4]     ; esi = number of bytes to read
        mov     [cur_start], esi
        sub     esi, eax
        jz      .loadcontinue
        xor     edx, edx
        div     dword [lb_size] ; eax = number of logical blocks to skip,
        mov     [first_byte], dx; [first_byte] = number of bytes to skip in 1st block
        cmp     byte [di+10], 0
        jnz     .interleaved
        add     eax, [di]
; read esi bytes from logical block eax to buffer es:bx
        call    read_many_bytes.with_first
        jc      .readerr3
.loadcontinue:
        mov     [cur_chunk], di
        add     di, 11
        cmp     di, [cur_desc_end]
        jae     @f
        cmp     [cur_limit], 0
        jnz     loadloop
@@:
        mov     bx, [overflow]
.calclen:
; calculate length of file
        xor     ax, ax
        xor     dx, dx
        mov     di, 2000h
@@:
        add     ax, [di+4]
        adc     dx, [di+6]
        add     di, 11
        cmp     di, [cur_desc_end]
        jb      @b
        ret
.interleaved:
        mov     [cur_unit_limit], esi
        push    esi
; skip first blocks
        movzx   ecx, byte [di+9]        ; Unit Size
        movzx   esi, byte [di+10]       ; Interleave Gap
        add     si, cx
        mov     edx, [di]
@@:
        sub     eax, ecx
        jb      @f
        add     edx, esi
        jmp     @b
@@:
        add     ecx, eax        ; ecx = number of logical blocks to skip
        lea     eax, [ecx+edx]  ; eax = first logical block
        pop     esi
.interleaved_loop:
; get number of bytes in current file unit
        push    eax
        movzx   eax, byte [di+9]
        sub     ax, cx
        imul    eax, dword [lb_size]
        cmp     eax, esi
        ja      .i2
.i1:
        xchg    esi, eax
.i2:
        pop     eax
        sub     [cur_unit_limit], esi
        push    eax
; read esi bytes from logical block eax to buffer es:bx
        call    read_many_bytes.with_first
        pop     eax
        jnc     @f
.readerr3:
        mov     bx, 3
        jmp     .calclen
@@:
        mov     esi, [cur_unit_limit]
        test    esi, esi
        jz      .loadcontinue
        movzx   ecx, byte [di+9]        ; add Unit Size
        add     cl, byte [di+10]        ; add Interleave Gap
        adc     ch, 0
        add     eax, ecx
        xor     cx, cx
        mov     [first_byte], cx
        jmp     .interleaved_loop

cx_to_sectors:
        add     cx, 7FFh
        rcr     cx, 1
        shr     cx, 10
        ret

is_last_component:
; in: ds:si -> name
; out: CF set <=> current component is not last (=> folder)
        push    si
@@:
        lodsb
        test    al, al
        jz      @f
        cmp     al, '/'
        jnz     @b
        stc
@@:
        pop     si
        ret

test_filename1:
; in: ds:si -> filename, es:di -> path table item
; out: CF set <=> no match
        pusha
        mov     cl, [es:di]
        add     di, 8
        jmp     test_filename2.start
test_filename2:
; in: ds:si -> filename, es:bx -> directory item
; out: CF set <=> no match
        pusha
        mov     cl, [es:bx+32]
        lea     di, [bx+33]
.start:
        mov     ch, 0
@@:
        lodsb
        test    al, al
        jz      .test1
        cmp     al, '/'
        jz      .test1
        call    toupper
        mov     ah, al
        mov     al, [es:di]
        call    toupper
        inc     di
        cmp     al, ah
        loopz   @b
        jnz     .next1
; if we have reached this point: current name is done
        lodsb
        test    al, al
        jz      .ret
        cmp     al, '/'
        jz      .ret
; if we have reached this point: current name is done, but input name continues
; so they do not match
        jmp     .next1
.test1:
; if we have reached this point: input name is done, but current name continues
; "filename.ext;version" in ISO-9660 represents file "filename.ext"
; "filename." and "filename.;version" are also possible for "filename"
        cmp     byte [es:di], '.'
        jnz     @f
        inc     di
        dec     cx
        jz      .ret
@@:
        cmp     byte [es:di], ';'
        jnz     .next1
        jmp     .ret
.next1:
        stc
.ret:
        popa
        ret

toupper:
; in: al=symbol
; out: al=symbol in uppercase
        cmp     al, 'a'
        jb      .ret
        cmp     al, 'z'
        ja      .ret
        sub     al, 'a'-'A'
.ret:
        ret

scan_for_filename_in_sector:
; in: ds:si->filename, es:bx->folder data, dx=limit
; out: CF=0 if found
        push    bx
.loope:
        push    bx
.loop:
        cmp     bx, dx
        jae     .notfound
        cmp     byte [es:bx], 0
        jz      .loopd
        test    byte [es:bx+25], 4      ; ignore files with Associated bit
        jnz     .next
        call    test_filename2
        jc      .next
        push    ds es di
        push    es
        pop     ds
        push    cs
        pop     es
        mov     di, [es:cur_desc_end]
        movzx   eax, byte [bx+1]
        add     eax, [bx+2]
        stosd   ; first logical block
        mov     eax, [bx+10]
        stosd   ; length
        mov     al, [bx+25]
        stosb   ; flags
        mov     ax, [bx+26]
        stosw   ; File Unit size, Interleave Gap size
        mov     [es:cur_desc_end], di
        cmp     di, 3000h
        pop     di es ds
        jae     .done
        test    byte [es:bx+25], 80h
        jz      .done
.next:
        add     bl, [es:bx]
        adc     bh, 0
        jmp     .loop
.loopd:
        mov     ax, bx
        pop     bx
@@:
        add     bx, [cs:lb_size]
        jz      .done2
        cmp     bx, ax
        jb      @b
        jmp     .loope
.notfound:
        stc
.done:
        pop     bx
.done2:
        pop     bx
        ret

lb_to_sector:
        xor     edx, edx
        div     dword [lb_per_sec]
        ret

load_phys_sector_for_lb_force:
; in: eax = logical block, ds=0
; in: si=0 - accept 0 logical blocks, otherwise force read at least 1
; out: 0000:1000 = physical sector data; si -> logical block
; out: eax = next physical sector
; out: CF=1 if read error
; destroys cx
; this procedure reads 0-3 or 1-4 logical blocks, up to the end of physical sector
        call    lb_to_sector
        or      si, dx
        jnz     @f
        mov     si, 1800h
        jmp     .done
@@:
        mov     si, 1000h
        imul    dx, [lb_size]
        add     si, dx
        mov     cx, 1
        push    es bx
        push    ds
        pop     es
        mov     bx, 1000h
        call    read_sectors
        pop     bx es
        inc     eax
.done:
        ret

normalize:
; in: es:bx = far pointer
; out: es:bx = normalized pointer (i.e. 0 <= bx < 0x10)
        push    ax bx
        mov     ax, es
        shr     bx, 4
        add     ax, bx
        mov     es, ax
        pop     bx ax
        and     bx, 0x0F
        ret

read_many_bytes:
        and     [first_byte], 0
read_many_bytes.with_first:
; read esi bytes from logical block dx:ax to buffer es:bx
; out: CF=1 <=> disk error
        push    di
; load first physical sector
        push    bx si
        mov     si, [first_byte]
        call    load_phys_sector_for_lb_force
        jnc     @f
        pop     si bx
.ret:
        pop     di
        ret
@@:
        add     si, [first_byte]
        mov     ecx, 1800h
        sub     cx, si
        mov     ebx, esi
        pop     bx
        sub     ebx, ecx
        jnc     @f
        add     cx, bx
        xor     ebx, ebx
@@:
        pop     di
        sub     [cur_limit], ecx
        rep movsb
        mov     esi, ebx
        mov     bx, di
        call    normalize
; load other physical sectors
; read esi bytes from physical sector eax to buffer es:bx
        test    esi, esi
        jz      .ret
        push    esi
        add     esi, 0x7FF
        and     si, not 0x7FF
        cmp     esi, [cur_limit]
        jbe     .okplace
.noplace:
        sub     esi, 800h
.okplace:
        shr     esi, 11 ; si = number of sectors
        mov     cx, si
        jz      @f
        call    read_sectors
@@:
        pop     esi
        jc      .ret
        movzx   ecx, cx
        add     eax, ecx
        shl     ecx, 11
        sub     [cur_limit], ecx
        sub     esi, ecx
        jc      .big
        jz      .nopost
        push    bx es
        push    ds
        pop     es
        mov     bx, 1000h
        mov     cx, 1
        call    read_sectors
        pop     es di
        jc      .ret2
        mov     cx, si
        mov     si, 1000h
        sub     word [cur_limit], cx
        sbb     word [cur_limit+2], 0
        rep movsb
        mov     bx, di
        call    normalize
.nopost:
        clc
.ret2:
        pop     di
        ret
.big:
        mov     ax, es
        sub     ax, 80h
        mov     es, ax
        add     bx, 800h
        add     bx, si
        call    normalize
        sub     [cur_limit], esi
        jmp     .nopost

; Callback function for secondary loader
callback:
; in: ax = function number; only function 1 is defined for now
        dec     ax
        jz      callback_readfile
        dec     ax
        jz      callback_continueread
        stc     ; unsupported function
        retf

callback_readfile:
; function 1: read file
; in: ds:di -> information structure
;       dw:dw   address
;       dw      limit in 4Kb blocks (0x1000 bytes) (must be non-zero and not greater than 0x100)
;       ASCIIZ  name
; out: bx=0 - ok, bx=1 - file is too big, only part of file was loaded, bx=2 - file not found, bx=3 - read error
; out: dx:ax = file size (0xFFFFFFFF if file was not found)
        call    load_file
        clc     ; function is supported
        retf

callback_continueread:
; function 2: continue to read file
; in: ds:di -> information structure
;       dw:dw   address
;       dw      limit in 4Kb blocks (0x1000 bytes) (must be non-zero and not greater than 0x100)
; out: bx=0 - ok, bx=1 - file is too big, only part of file was loaded, bx=3 - read error
; out: dx:ax = file size
        les     bx, [di]
        call    normalize
        movzx   esi, word [di+4]        ; si = limit in 4K blocks
        shl     esi, 12                 ; bp:si = limit in bytes
        push    cs
        pop     ds
        mov     [cur_limit], esi
        mov     di, [cur_chunk]
        call    loadloop.loadnew
        clc     ; function is supported
        retf

secondary_loader_info:
        dw      0, 0x1000
        dw      0x30000 / 0x1000
        db      'kord/loader',0
aKernelNotFound db      'Fatal error: cannot load the secondary loader',0

align 2
cachelist:
.head           dw      cachelist
.tail           dw      cachelist
free_cache_item dw      0
last_cache_item dw      0x8400

use_path_table  db      0
bootdrive       db      ?
align 2
lb_size         dw      ?       ; Logical Block size in bytes
                dw      0       ; to allow access dword [lb_size]
lb_per_sec      dw      ?       ; Logical Blocks per physical sector
                dw      0       ; to allow access dword [lb_per_sec]
free_ptr        dw      ?       ; first free block in cache (cache block = sector = 0x800 bytes)
size_rest       dw      ?       ; free space in cache (in blocks)
cache_size      dw      ?
cache_start     dw      ?
pathtable_size  dw      ?
pathtable_start dw      ?
root_location   dd      ?
cur_desc_end    dw      ?
cur_nocache_len dd      ?
cur_limit       dd      ?
cur_unit_limit  dd      ?
overflow        dw      ?
cur_chunk       dw      ?
first_byte      dw      ?
cur_start       dd      ?

;times 83FEh-$  db      0
        db      43h
; just to make file 2048 bytes long :)
        db      'd' xor 'i' xor 'a' xor 'm' xor 'o' xor 'n' xor 'd'

        dw      0xAA55          ; this is not required for CD, but to be consistent...
