mirror of
https://github.com/KolibriOS/kolibrios.git
synced 2025-09-12 05:26:00 -04:00

When the extended primary loader for CD was introduced in 2008-2009 it used to load a relatively small secondary loader. Later the secondary loader was integrated into the kernel, hence the primary loader had to load the kernel which is 100kB+. The EDD BIOS specification allows to read less than 0x80 sectors at once using Int 0x13 0x42 service. For a 512-byte sector this is less than 0x10000 bytes which is a 64kB limit. The same limit of 64kB is reached on a CD with only 0x20 sectors, because the sector size is 2048 bytes here. This commit changes the maximum number of sectors that can be read by the loader at once from 0x7f to 0x20, i.e. reading is done in 64kB blocks now.
1026 lines
28 KiB
NASM
1026 lines
28 KiB
NASM
; 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
|
|
; read no more than 64kB at once, i.e. 0x20 sectors
|
|
cmp cx, 0x20
|
|
jbe @f
|
|
mov cx, 0x20
|
|
@@:
|
|
; 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 0x20)
|
|
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 'kernel.mnt',0
|
|
aKernelNotFound db 'Fatal error: cannot load the kernel',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 83FCh-$ 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...
|