home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 2000 May
/
Chip_2000-05_cd2.bin
/
dosutils
/
loadlin
/
src
/
loadlinm.asm
< prev
next >
Wrap
Assembly Source File
|
1996-03-30
|
24KB
|
785 lines
; >>> this is file LOADLINM.ASM
;============================================================================
; LOADLIN v1.6 (C) 1994..1996 Hans Lermen (lermen@elserv.ffm.fgan.de)
;
; 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.
;
; 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., 675 Mass Ave, Cambridge, MA 02139, USA.
;
;----------------------------------------------------------------------------
; Comments and bug reports are welcome and may be sent to:
; E-Mail: lermen@elserv.ffm.fgan.de
; SnailMail: Hans Lermen
; Am Muehlenweg 38
; D53424 REMAGEN-Unkelbach
; GERMANY
;
;============================================================================
;---------------------------------------------------------------------------
call_pmode_routine proc near
; NOTE: must have called get_VCPI_interface before this
; _AND_ must be already moved high, else the pagetable
; will not be aligned.
; input:
; AX = offset of protected mode routine
; The called routine will have a pointer in ebp to a pushad_struct
; of the other input registers, and all segment register point to
; High_seg
;
@@regs struc
pushAD_struc @@
@@regs ends
pushf
CLI
pushad
mov ebp,esp
; ┌───── IRET stack for return to VM86
push 0
push gs
push 0
push fs
push 0
push ds
push 0
push es
push 0
push ss
push ebp
sub sp,4 ; space for EFLAGS
push 0
push cs
push 0
push offset @@pmode_return
; └────── IRET stack for return to VM86
mov cs:pmode_return_esp,esp
lea ax,@@pmode_task
movzx eax,ax
mov dword ptr protected_mode_target,eax
mov word ptr protected_mode_target+4,g_code
movzx esi,cs:High_Seg
shl esi,4
lea si,our_CR3[si]
mov ax,0DE0Ch
int emm_int ; jumps to protected mode
; and comes here in 16-bit protected mode
@@pmode_task:
mov ax,g_data
mov ss,ax
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
mov esp,cs:pmode_return_esp
; ebp pointing to pushad_struct
mov ax, word ptr [bp].@@eax
call ax ; call the routine
; now we return to VM86 mode
cli ; to be save
mov ax,g_core ; selector for addressing first MByte
mov ds,ax
.386p
CLTS ; clear taskswitch flag
.386
mov eax,0DE0Ch
call fword ptr cs:server_vcpi_entry ; back to VM86
; and returns here in VM86 mode
@@pmode_return:
popad
popf
ret
call_pmode_routine endp
IF 0
AUXPAGE = (modul_end-code_org0) ; behind our code
ELSE
AUXPAGE = 0 ; at start of our segment, only used in protected mode
ENDIF
AUXPAGE_size = 02000h
AUXPAGE_entry = (AUXPAGE shr (12-2))
AUXPAGE_S = AUXPAGE
AUXPAGE_T = AUXPAGE+AUXPAGE_size
AUXPAGE_S_entry equ page0+AUXPAGE_entry[bx]
AUXPAGE_T_entry equ page0+(AUXPAGE_entry+(AUXPAGE_size shr (12-2)))[bx]
AUXPAGE_BITS = 3 ; writeable, present
move_anywhere_vcpi proc near
; input:
; ESI= linear source address
; EDI= linear target address
; CX= size to move, must be <= 4096 !!!
; output:
; all registers preserved
; we are in VM86 and at this point assume to have VCPI
push ax
lea ax,@@pmove
call call_pmode_routine
pop ax
ret
; this will be executed in 16-bit protected mode
@@pmove:
@@regs struc
pushAD_struc @@
@@regs ends
; save pagetable-entries
mov bx,High_Seg
shr bx,(12-4-2)
push AUXPAGE_S_entry
push AUXPAGE_S_entry+4
push AUXPAGE_T_entry
push AUXPAGE_T_entry+4
mmm macro reg,st
mov eax,[bp].@@e®
mov reg,ax
and reg,0fffh
lea reg,[reg]+AUXPAGE_&st
and ax,0f000h
or al,AUXPAGE_BITS
mov AUXPAGE_&st&_entry,eax
add eax,1000h
mov AUXPAGE_&st&_entry+4,eax
endm
mmm si,S ; map source
mmm di,T ; map target
.386p
mov eax,CR3 ; flush TLB
mov CR3,eax
.386
cld
movzx ecx,cx
ror ecx,2
rep movsd
rol ecx,2
rep movsb
; restore pagetable-entries
pop AUXPAGE_T_entry+4
pop AUXPAGE_T_entry
pop AUXPAGE_S_entry+4
pop AUXPAGE_S_entry
ret
move_anywhere_vcpi endp
;---------------------------------------------------------------
move_anywhere_INT15 proc near
; input: ESI = source linear address
; EDI = destination linear address
; CX = count
; returns:
; all registers preserved
;
@@descript struc
@@limit dw ?
@@base0 dw ?
@@base16 db ?
@@typbyte db ?
@@limit16 db ?
@@base24 db ?
@@descript ends
push_ es,eax,esi,edi,ecx
mov cs:@@src.@@base0,si
shld eax,esi,16
mov cs:@@src.@@base16,al
mov cs:@@src.@@base24,ah
mov cs:@@dest.@@base0,di
shld eax,edi,16
mov cs:@@dest.@@base16,al
mov cs:@@dest.@@base24,ah
mov cx,(1000h/2) ; force to be one page
; NOTE: INT15 moves are in WORDS,
; because we always move 1000h,
; we simple do it also for the last page
push cs
pop es
lea si,@@gdt
mov ax,08700h
int 15h
pop_ es,eax,esi,edi,ecx
ret
align qword
@@gdt @@descript <0,0,0,0,0,0>
@@descript <0,0,0,0,0,0>
@@src @@descript <0ffffh,0,0,093h,0,0>
@@dest @@descript <0ffffh,0,0,093h,0,0>
@@descript <0,0,0,0,0,0> ; BIOS CS
@@descript <0,0,0,0,0,0> ; BIOS SS
move_anywhere_INT15 endp
;============================================================================
DUMMY_XMS_BLOCK_START = 100000h
XMS_GET_VERSION = 0
XMS_QUERY_FREE = 8
XMS_ALLOC = 9
XMS_FREE = 0ah
XMS_MOVE_BLOCK = 0bh
XMS_LOCK_BLOCK = 0ch
XMS_UNLOCK_BLOCK = 0dh
xmscall macro function
mov ah,function
call cs:xms_entry
endm
check_for_XMS proc near
push_ ds,es,ax,bx
mov cs:xms_entry,0
mov cs:xms_avail,0
mov ax,4300h
INT 2fh
cmp al,80h
jnz @@ex
mov ax,4310h
INT 2fh
mov word ptr cs:xms_entry,bx
mov word ptr cs:xms_entry+2,es
xmscall XMS_GET_VERSION
cmp ax,200h
jb @@ex0
xmscall XMS_QUERY_FREE
or ax,ax
jz @@ex0
mov cs:xms_avail,ax
@@ex:
pop_ ds,es,ax,bx
cmp cs:xms_entry,0
ret
@@ex0:
mov cs:xms_entry,0
jmp @@ex
check_for_XMS endp
allocate_xms_block proc near
push_ bx,dx
cmp cs:xms_entry,0
je @@ex0
mov dx,cs:xms_avail
xmscall XMS_ALLOC
or ax,ax
je @@ex0
mov cs:xms_handle,dx
@@ex:
pop_ bx,dx
or eax,eax
ret
@@ex0:
xor eax,eax
jmp @@ex
allocate_xms_block endp
lock_xms_block proc near
; try to lock the XMS block
; (needed to get the phys.address)
push_ ax,bx,dx
mov dx,cs:xms_handle
xmscall XMS_LOCK_BLOCK
or ax,ax
je @@err
mov word ptr cs:xms_phys_addr,bx
mov word ptr cs:xms_phys_addr+2,dx
pop_ ax,bx,dx
ret
@@err:
push cs
pop ds
lea dx,@@tx
call print
jmp exit_to_dos
@@tx db 13,10,'Cannot lock XMS memory',13,10,'$'
lock_xms_block endp
free_xms_block proc near
push_ ax,bx,dx
mov dx,cs:xms_handle
xmscall XMS_UNLOCK_BLOCK
mov dx, cs:xms_handle
xmscall XMS_FREE
pop_ ax,bx,dx
ret
free_xms_block endp
move_anywhere_xms proc near
; input:
; ESI= linear source address (must be in lowmem )
; EDI= linear target address
; CX= size to move, must be <= 4096 !!! (actually _is_ always 4096)
; output:
; all registers preserved
cmp edi,DUMMY_XMS_BLOCK_START
jb move_simple
pushad
push ds
mov ecx,1000h ; force a length of one page to move
; NOTE: XMS moves have to be even,
; because we alway move 1000h,
; we simple do it also for the last page
mov @@length,ecx
sub edi,DUMMY_XMS_BLOCK_START
mov @@doffs,edi
mov di,xms_handle
mov @@dhandle,di
ror esi,4
mov word ptr @@soffs+2,si
xor si,si
rol esi,4
mov word ptr @@soffs,si
lea si,@@length
push cs
pop ds
xmscall XMS_MOVE_BLOCK
pop ds
popad
ret
@@length dd ?
@@shandle dw 0
@@soffs dd ?
@@dhandle dw ?
@@doffs dd ?
move_anywhere_xms endp
;--------------------------------------------------------------------
move_simple proc near
; input:
; ESI= linear source address
; EDI= linear target address
; CX= size to move, must be <= 4096 !!!
; output:
; all registers preserved
pushad
push_ ds,es
movzx ecx,cx
cld
ror edi,4
mov es,di
xor di,di
rol edi,4
ror esi,4
mov ds,si
xor si,si
rol esi,4
ror ecx,2
rep movsd
rol ecx,2
rep movsb
@@ex:
pop_ ds,es
popad
ret
move_simple endp
build_buffer_heap proc near
pushad
; first we setup the normal low heap
; (why not use it)
movzx eax,kernel_load_frame
shl eax,4
mov heap_ptr,eax
movzx eax,High_Seg ; NOTE: this is page aligned !
shl eax,4
mov heap_end,eax
sub eax,heap_ptr
shr eax,12
mov heap_max_pages,eax
; now we try to get some extended memory
cmp need_mapped_put_buffer,0
jz @@err
cmp cpu_type,cpu_386V86
je @@vcpi
; we check what we can use to access extended memory
call check_for_XMS
jz @@need_int15
cmp cs:xms_avail,128
jb @@need_int15
sub cs:xms_avail,64 ; leave a 64K minimum for system
call allocate_xms_block
jz @@need_int15
movzx eax,cs:xms_avail
shr eax,2 ; from 1KB to 4KB
add heap_max_pages,eax
mov high_heap_ptr,DUMMY_XMS_BLOCK_START
mov high_mem_access,USING_XMS
mov move_anywhere, offset move_anywhere_xms
@@ex:
mov eax,heap_max_pages
shl eax,12
mov load_buffer_size,eax
popad
ret
@@need_int15:
mov ax,08800h
int 15h
sub ax,64+64 ; leave HMA untouched, we need 64Kminimum
; (else we would waste more mem than winning)
jbe @@err
movzx eax,ax
shr eax,2 ; from 1KB to 4KB
add heap_max_pages,eax
mov high_heap_ptr,0110000h
mov high_mem_access,USING_INT15
mov move_anywhere, offset move_anywhere_int15
jmp @@ex
@@err:
mov high_mem_access,0
mov move_anywhere, offset move_simple
jmp @@ex
@@vcpi:
cmp have_VCPI,0
jz @@ex
push edx
mov ax,0DE03h ; get avail 4K VCPI-page
int emm_int
sub edx,(64/4) ; leave a 64K minimum for system
jb @@err
add heap_max_pages,edx
mov high_mem_access,USING_VCPI
mov move_anywhere, offset move_anywhere_vcpi
pop edx
jmp @@ex
build_buffer_heap endp
get_buffer_from_heap proc near
; output: EAX = linear address of 4K buffer, page aligned
; (if XMS, then the offset within the buffer)
; CARRY =1, if memory overflow
cmp heap_max_pages,0
jna @@err
dec heap_max_pages
mov eax,heap_ptr
cmp eax,heap_end
jnb @@high
add heap_ptr,1000h
@@ex1:
clc
@@ex:
ret
@@high:
cmp high_mem_access,USING_VCPI ; what heap are we using
je @@vcpi
mov eax,high_heap_ptr
add high_heap_ptr,1000h
jmp @@ex1
@@vcpi:
push edx
mov ax,0DE04h ; get 4K VCPI-page
int emm_int
mov eax,edx
and ax,0f000h
pop edx
jmp @@ex1
@@err:
xor eax,eax
stc
jmp @@ex
get_buffer_from_heap endp
free_extended_memory proc near
cmp need_mapped_put_buffer,0
jz @@ex
cmp high_mem_access,USING_XMS
je @@xms
cmp high_mem_access,USING_VCPI
jne @@ex
@@vcpi:
push_ eax,bx,edx
mov bx,word ptr pageadjlist.ncount
shl bx,2
@@loop:
sub bx,4
jb @@ex1
mov edx,pageadjlist.sources[bx]
cmp edx,0100000h
jb @@ex1
mov ax,0DE05h ; free 4K VCPI-page
int emm_int
jmp @@loop
@@ex1:
pop_ eax,bx,edx
@@ex:
ret
@@xms:
call free_xms_block
jmp @@ex
free_extended_memory endp
final_page_adjust_list_handling proc near
cmp need_mapped_put_buffer,0
jz @@ex
cmp high_mem_access,USING_XMS
jne @@ex
push_ eax,bx
call lock_xms_block
mov bx,word ptr pageadjlist.ncount
shl bx,2
@@loop:
sub bx,4
jb @@ex1
mov eax,pageadjlist.sources[bx]
sub eax,DUMMY_XMS_BLOCK_START
jb @@ex1
add eax,xms_phys_addr
mov pageadjlist.sources[bx],eax
jmp @@loop
@@ex1:
pop_ eax,bx
@@ex:
ret
final_page_adjust_list_handling endp
open_new_mapped_block proc near
; input:
; EDI = linear address of final target address
cmp need_mapped_put_buffer,0
jz @@ex
push_ bx,edi
mov bx,word ptr pageadjlist.number_of_blocks
shl bx,3 ; * sizeof(pblock)
and di,0f000h
mov pageadjlist.blocks.taddr[bx],edi
mov di,word ptr pageadjlist.ncount
mov pageadjlist.blocks.tstart[bx],di
mov pageadjlist.blocks.tcount[bx],0
inc pageadjlist.number_of_blocks
mov do_mapped_put_buffer,1 ; notice 'put_buffer' what to do
pop_ bx,edi
@@ex:
ret
open_new_mapped_block endp
map_high_page proc near
; input:
; EDI = linear address of source address
; because this is called strict sequentially, the target address
; is allways given by the next free entry in the pageadjlist
cmp need_mapped_put_buffer,0
jz @@ex
push_ bx,edi
mov bx,word ptr pageadjlist.ncount
shl bx,2
and di,0f000h
mov pageadjlist.sources[bx],edi
inc pageadjlist.ncount
mov bx,word ptr pageadjlist.number_of_blocks
dec bx
shl bx,3 ; * sizeof(pblock)
inc pageadjlist.blocks.tcount[bx]
pop_ bx,edi
@@ex:
ret
map_high_page endp
put_buffer proc near
; input:
; AX= count
; EDI= linear destination address (may be > 0100000h)
; DS:DX= source address in lowmem
; output:
; none, all registers preserved
; in case of memory overflow it doesn't return (jumps to exit_to_dos)
;
pushad
push_ ds,es
movzx ecx,ax
cmp cs:need_mapped_put_buffer,0
jnz @@mapped
; we can mov it normally
cld
ror edi,4
mov es,di
xor di,di
rol edi,4
mov si,dx
ror ecx,2
rep movsd
rol ecx,2
rep movsb
@@ex:
pop_ ds,es
popad
ret
@@mapped: ; do high move
call get_buffer_from_heap
jc @@err
; we don't need EDI any more, because
; if we come here we are writing
; strict sequentialy and have set the
; starting address of the block
; via open_new_mapped_block
mov edi,eax
mov si,ds
movzx esi,si
shl esi,4
movzx edx,dx
add esi,edx
call map_high_page
call move_anywhere
jmp @@ex
@@err:
pop_ ds,es
popad
DosCall DOS_CLOSE_FILE
lea dx,@@tx
call print
jmp exit_to_dos
@@tx db 13,10,'Out of memory (may be low or extended)',13,10,'$'
put_buffer endp
; ---------------------------------------------------------------
; initrd stuff
MAXPHYSMEM_FOR_INT15 = 1000000h
load_initrd proc near
cmp cs:option_initrd,0
jz @@ex
pushad
cmp need_mapped_put_buffer,0
jz @@err_unable
call get_effective_physmem
mov end_of_physmem,eax
cmp high_mem_access,USING_INT15
jne @@1
; INT15 on some BIOSes will not access above 16Mb
cmp eax,MAXPHYSMEM_FOR_INT15
jbe @@1
mov eax,MAXPHYSMEM_FOR_INT15
@@1:
mov @@end_of_physmem,eax
mov ax,DOS_OPEN_FILE shl 8
lea dx,rdimage_name
DosInt
jc @@err_open
mov fhandle,ax
mov bx,ax
call get_filesize
mov ramdisk_size,eax
neg eax
add eax,@@end_of_physmem
and ax,0f000h ; round down to full page boundary
mov ramdisk_image,eax
movzx eax,kernel_size
add ax,0ffh
mov al,0 ; round up to page boundary
shl eax,4 ; size of compressed kernel
mov ecx,eax
shl eax,1 ; estimated size of decompressed kernel
; (we assume a decompression rate of 1:2)
sub ecx,(90000h - 2000h) ; the low buffer size (for bzimages)
jb @@3
add eax,ecx ; add padding
@@3:
add eax,(100000h+2000h) ; (+ gunzip-heap), now we have end of kernel
cmp eax,ramdisk_image
jnb @@err_mem
; ok we have place
; now loading the kernel
; NOTE: needing EDI for open_new_mapped_block
mov edi,ramdisk_image
call open_new_mapped_block ; open the first block
mov bx,fhandle
mov ecx,ramdisk_size
mov print_dots,3
call read_handle ; read the ramdisk
call print_crlf
mov print_dots,0
call print_crlf
jc @@err_io
cmp eax,ecx
jnz @@err_io
; ok, all is read into memory
DosCall DOS_CLOSE_FILE
popad
@@ex:
ret
@@err:
popad
mov dx,@@errtx_addr
call print
jmp exit_to_dos
@@err_open:
mov @@errtx_addr,offset @@tx
jmp @@err
@@err_mem:
mov @@errtx_addr,offset @@txmem
jmp @@err
@@err_io:
mov @@errtx_addr,offset @@txio
jmp @@err
@@err_unable:
mov @@errtx_addr,offset @@txno
jmp @@err
@@end_of_physmem dd 0
@@tx db 13,10,"can't open image file for initrd",13,10,'$'
@@txmem db 13,10,"no place after kernel for initrd",13,10,'$'
@@txio db 13,10,"IO-error while reading initrd",13,10,'$'
@@txno db 13,10,"no support in setup for reading initrd",13,10,'$'
@@errtx_addr dw 0
load_initrd endp
get_filesize proc near
; input:
; bx = filehandle
; output:
; eax = filesize, all other registers reserved
push_ cx,dx,esi
mov ax,4201h ; seek to current position
xor cx,cx
xor dx,dx
DosInt ; ... and return current position in DX:AX
push_ ax,dx ; save it
mov ax,4202h ; seek to last position
xor cx,cx
xor dx,dx
DosInt ; ... and return EOF position in DX:AX
mov si,dx
shl esi,16
mov si,ax
pop_ dx,cx
mov ax,4200h ; seek to saved position
DosInt
mov eax,esi
pop_ cx,dx,esi
ret
get_filesize endp