"Fossies" - the Fresh Open Source Software archive 
Member "netboot-0.10.2/bootrom/loader/rom.S86" of archive netboot-0.10.2.tar.gz:
/*
**************************************************************************
*
* Boot-ROM-Code to load an operating system across a TCP/IP network.
*
* Module: rom.S86
* Purpose: Decompress the rom image and copy everything into place
* Entries: Called by BIOS at offset 0x0003, getbyte, putbyte
*
**************************************************************************
*
* Copyright (C) 1995-2007 Gero Kuhlmann <gero@gkminix.han.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
* 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.
*
* $Id: rom.S86,v 1.12 2007/01/06 18:30:47 gkminix Exp $
*
**************************************************************************
*/
#include <common.i86>
#include <romconfig.i86>
#include <system/bootrom.i86>
#include <system/bios.i86>
#include <system/pnp.i86>
#include <system/pci.i86>
#include <system/pmm.i86>
#include <pxe/loader.i86>
#include <pxe/romid.i86>
#include "ldram.i86"
.file __FILE__
.line __LINE__
/*
**************************************************************************
*
* Equates for this module:
*/
#define BBSWAIT 3 /* number of seconds to wait */
#define COMPADR FLOPMEM /* segment for compressed image */
#define LOADADR FLOPEND /* segment to load the boot code */
#define LOADEND OSENDMEM /* end segment of this boot code */
#define BOOTLOAD 0x7C00 /* boot load area in segment 0 */
#define MINSTK 0x2000 /* Minimum stack pointer */
#if (LOADEND - LOADADR) < 0x1000
# error Invalid boot loader size
#endif
/*
**************************************************************************
*
* Definitions for accessing the high memory area using the BIOS
*/
#define GDTSRCLEN 0x0010 /* source seg length */
#define GDTSRCADR 0x0012 /* source seg linear address */
#define GDTSRCRIGHT 0x0015 /* source seg access rights */
#define GDTSRCHIADR 0x0017 /* source seg linear addr high byte */
#define GDTDESTLEN 0x0018 /* destination seg length */
#define GDTDESTADR 0x001A /* destination seg linear address */
#define GDTDESTRIGHT 0x001D /* destination seg access rights */
#define GDTDESTHIADR 0x001F /* dest seg linear addr high byte */
#define GDTSTRUCTLEN 0x0030 /* length of GDT BIOS structure */
#define GDTACCESS 0x93 /* segment access rights */
/*
**************************************************************************
*
* Definitions used to setup the PnP option ROM expansion header as
* defined per PnP BIOS Specification, Version 1.0A.
*
* Vendor and Device ID: This is only required for PnP compatible operating
* systems to load the necessary drivers. For the bootrom, we use 0 for
* both values. If it really is required, makerom can add it lateron.
*/
#define PNP_VENDID 0 /* vendor ID not used */
#define PNP_DEVID 0 /* device ID not used */
/*
* Device Indicators:
* Bit 7 = 1 --> ROM supports DDIM (if selected)
* Bit 6 = 0 --> ROM may not be shadowed in RAM
* Bit 5 = 0 --> ROM is not read cacheable
* Bit 4 = 1 --> ROM is only required if this device is selected for boot
* Bit 3 = 0 --> reserved
* Bit 2 = 1 --> this is an IPL device
* Bit 1 = 0 --> not an input device
* Bit 0 = 0 --> not an output device
*/
#ifdef NODDIM
# define PNP_DEVIND 0b00010100 /* device indicators */
#else
# define PNP_DEVIND 0b10010100 /* device indicators */
#endif
/*
* Device Type Codes: These are used by the BIOS to prioritize the boot
* devices. According to the specs, the base type value should be 2
* to identify a network controller. However, some older BIOS (mainly
* Award) are not able to detect and boot from anything with a base
* type value larger than 1 (disk controller). This is why we use a
* value of 0 here.
*/
#define PNP_BASETYPE 0
#define PNP_SUBTYPE 0
#define PNP_IFTYPE 0
/*
**************************************************************************
*
* Definitions used to setup the PCI data structure.
*
* Device Indicator: Set to 0x80 if this is the last PCI structure within
* the physical ROM.
*/
#define PCI_DEVIND 0x80 /* device indicator */
/*
* Code type: We are only able to run on x86 Intel architecture.
*/
#define PCI_CODETYPE 0
/*
* Device Type Codes: These specify the type of interface this bootrom
* is controlling.
*/
#define PCI_BASETYPE 2 /* network interface controller */
#define PCI_SUBTYPE 0 /* ethernet */
#define PCI_IFTYPE 0 /* unused */
/*
* Revision level of code/data: This is just the major/minor version
* number of the netboot bootrom.
*/
#define PCI_REVISION ((VER_MAJOR * 100) + VER_MINOR)
/*
**************************************************************************
*
* Definitions for calling PMM functions. All memory blocks have to have
* a unique signature. We dont use a number as described in the PMM
* specification because that would mean to compute it at runtime, and
* that requires additional memory. Instead, we simply set it to a value
* which hopefully no one else uses.
*/
#define PMM_RAMSIG 0x6E62676B /* signature of PMM memory block */
#define PMM_FLAGS 0x02 /* flags for memory allocation */
.line __LINE__
/*
**************************************************************************
*
* The physical rom starts here. At the beginning there is some
* miscellaneous data.
*/
.text
.global _start
.global getbyte
.global putbyte
.extern inflate
.word 0xAA55 # rom signature
romlen: .byte 0 # rom size (filled in by checksum prg)
_start: jmp romstart # the BIOS calls us here
infadr: .word DECOMPMEM # this is for debugging purposes
/*
* The following variables contain the program version number and some
* other useful values. Dont change the order of any of these values as
* the binary patch program depends on them. Especially, the pointer to
* the PnP structure has to be at offset 0x001A as per the BBS spec.
* Note on the UNDI rom structure pointer: this structure is only required
* in the header for using the UNDI driver from the BIOS (called early
* UNDI API usage in Intels PXE specification). Since this is optional
* according to the PXE specification, and we dont support it, there is
* no need to have a UNDI rom structure here. We are just running as a
* normal IPL, so the structure offset is zero here.
*/
.org DATAOFS
.byte VER_MAJOR # major version number
.byte VER_MINOR # minor version number
.long 0 # serial number
botvec: .word 0 # offset to bootrom interrupt vector
romsiz: .long 0 # compressed rom size
.word 0 # reserved
.word 0 # pointer to UNDI rom structure
pciofs: .word pcihdr # pointer to PCI data structure
pnpofs: .word pnphdr # pointer to PnP expansion header
.asciz NBROMID # romcheck searches for this string
botflg: .word 0 # boot flags
mannam: .asciz MANUFACTURER # manufacturer name
prdnam: .asciz PRODUCT # product name
copyrt: .ascii ", "
.asciz COPYRIGHT # copyright message
crlf: .byte 0x0D,0x0A,0
/*
* Define the PCI data structure to make the netboot bootrom compatible
* with the PCI local bus specification 2.1. This header is actually only
* needed when the bootrom is physically located on the PCI network card.
* Note that the order of the class codes has to be reversed compared to
* what the spec says.
*/
.align 16
pcihdr: .ascii PCI_SIGNATURE # signature for PCI data structure
.word 0 # vendor ID (filled in later)
.word 0 # device ID (filled in later)
.word 0 # ptr to vital product data (unused)
.word PCI_HDRSIZE # length of PCI data structure
.byte 0 # structure revision
.byte PCI_BASETYPE # base device type code
.byte PCI_SUBTYPE # device subtype code
.byte PCI_IFTYPE # device programming interface
.word 0 # rom image length (filled in later)
.word PCI_REVISION # version number of code/data
.byte PCI_CODETYPE # code type
.byte PCI_DEVIND # device indicator
.word 0 # reserved
/*
* Define the expansion header to make the netboot bootrom Plug and Play
* compatible according to the Plug and Play BIOS Specification 1.0A. This
* will allow the system BIOS to select whether to boot from the network or
* not, and we dont have to redirect interrupt 18h or 19h by ourselves.
* Note that the order of the class codes has to be reversed compared to
* what the spec says.
*/
.align 16
pnphdr: .ascii PNP_SIGNATURE # signature for PnP expansion header
.byte 0x01 # structure revision 0.1
.byte 2 # length of header in 16-byte-blocks
.word 0 # pointer to next header (0 = none)
.byte 0 # reserved
.byte 0 # checksum (filled in later)
.word PNP_VENDID # compressed manufacturer code
.word PNP_DEVID # product number and revision, device ID
.word mannam # ptr to manufacturer string
.word prdnam # ptr to product name
.byte PNP_BASETYPE # base device type code
.byte PNP_SUBTYPE # device subtype code
.byte PNP_IFTYPE # device programming interface
.byte PNP_DEVIND # device indicators
.word 0 # boot connection vector (unused)
.word 0 # disconnect vector (unused)
.word dorom # bootstrap entry vector
.word 0 # reserved
.word 0 # static resource info vector (unused)
/*
* Define the new stack pointer
*/
stkptr: .word (LOADEND - LOADADR) * 16 - 2
.word LOADADR
/*
* These variables are writable when we are using DDIM. They hold the
* values required to resize the ROM and to handle interrupt 0x15.
*/
#ifndef NODDIM
oldi15: .long 0xDEADBEEF # old interrupt 0x15
newsiz: .long 0 # new extended memory size
newcks: .byte 0 # new ROM checksum
#endif
.line __LINE__
/*
**************************************************************************
*
* Now start with the actual boot rom code. The startup code just installs
* a new bootrom interrupt vector, and then returns to the BIOS. This is
* necessary so that the BIOS gets a chance to also initialize other installed
* roms. The bootrom vector is used for the ROM BASIC in original IBM PCs
* and gets called by the BIOS whenever there is no bootable disk in any
* of the drives.
* In case we have a PnP capable BIOS, we dont have to redirect the bootrom
* vector here, as the BIOS will call us lateron with a far call, not an
* interrupt. If the BIOS is PnP compatible, this routine gets called with
* the PCI PFA in AX or the PnP CSN in BX. Save these values for later use.
* Also, if we can write into this code we are running under the Device
* Driver Initialization Model (DDIM). In that case, we move the compressed
* bootrom image into extended memory and can therefore reduce the bootrom
* image accordingly. This way, more space is provided for other option ROMs
* in the system. See the BIOS boot specification and PXE specification for
* a further explanation on DDIM.
* Unfortunately, neither the BBS nor the PXE spec say anything about what
* registers are allowed to get changed by this routine. In addition, some
* BIOS set the stack to 0:0400 instead of 0:7C00, which leaves us about 64
* bytes of stack space. We therefore have to be very careful about stack
* space usage.
*/
romstart:
/*
* First save all registers onto the stack and then switch stacks if
* necessary.
*/
call setstk # set new stack pointer
push ds
push es
/*
* Check if we can write into the bootrom memory area. We can only use
* CX here. All other registers have to remain unchanged.
*/
mov cx,0x00FF
xchg cl,cs:[romlen] # set rom length to 0xFF
cmp cl,cs:[romlen] # check if we could write into
je 1f # the ROM area
mov cs:[romlen],cl # restore rom length value
or ch,RAMFL_WRITE
/*
* If another boot rom already occupied the ram data area, we have to
* return to the BIOS without any further initialization. It is not
* possible to run two instances of the bootrom. Remember not to change
* any general-purpose register. Setting the rom size byte to zero is
* enough. If the rom size is zero, we dont need to compute a bootrom
* checksum (so dont set RAMFL_CHKSUM), and there is no need to setup
* the rom size value in the PCI header.
*/
1: push ax
call checkram # check if we have been initialized
pop ax
jc 3f
7: test ch,RAMFL_WRITE # if the bootrom is writable, set the
jz 2f # rom size to zero
mov byte ptr cs:[romlen],0
mov word ptr cs:[pciofs],0
mov word ptr cs:[pnpofs],0
and ch,_NOT(RAMFL_CHKSUM)
2: xor ax,ax # not a boot device
jmp 9f # return to BIOS
/*
* Setup our private data area within the interrupt table. The checksum
* gets written at the end of this routine.
*/
3: push cx
mov si,RAM_START
mov cx,RAM_SIZE / 2
4: mov word ptr [si],0 # fill RAM area with zeroes
add si,2
loop 4b
pop cx
mov word ptr ds:ram_id[RAM_START],RAM_SIGNATURE
mov word ptr ds:ram_pnport[RAM_START],dx
mov dx,ax
/*
* Check for the PnP installation structure. With this we just check if
* we have been called according to the BBS.
*/
call checkpnp # check for PnP installation structure
jc 5f
mov word ptr ds:ram_pnpstruct[RAM_START + 0],di
mov word ptr ds:ram_pnpstruct[RAM_START + 2],es
test word ptr cs:[botflg],ROMFLG_NOBBS
jnz 6f
or ch,RAMFL_BBS
jmp 6f
/*
* We dont have a PnP BIOS, so we have to redirect the necessary interrupts. Also
* there is no PCI PFA or PnP CSN available, so set them both to 0xFFFF.
*/
5: mov dx,0xFFFF
mov bx,dx
mov word ptr ds:ram_pnport[RAM_START],0
6: mov word ptr ds:ram_pnpcsn[RAM_START],bx
mov word ptr ds:ram_pcipfa[RAM_START],dx
mov byte ptr ds:ram_flags[RAM_START],ch
/*
* In case we have no BBS support, we can remove the BEV from the PnP
* header. This device is not an IPL device in that case. Only change
* the PnP header if the bootrom is writable. In addition, we need to
* setup the bootrom interrupt vector.
*/
test ch,RAMFL_BBS
jnz 1f
mov si,cs:[botvec]
mov ax,ds
mov es,ax
mov di,_I(ram_oldint) + RAM_START
mov bx,offset int18 # set new bootrom interrupt
call intset
test ch,RAMFL_WRITE
jz 1f
push ds
mov ax,cs
mov ds,ax
mov si,offset pnphdr
and byte ptr pnphdr_devind[si],0xFB # remove IPL bit
mov word ptr pnphdr_bev[si],0 # set BEV to zero
movzx32 ecx,byte ptr pnphdr_size[si]
shl ecx,4
call dochksum # compute checksum
sub byte ptr pnphdr_chksum[pnphdr],ah
pop ds
or byte ptr ds:ram_flags[RAM_START],RAMFL_CHKSUM
/*
* We can now handle DDIM support. For this, we copy the compressed bootrom
* image into extended memory. If we can use PMM, we can simply copy the
* compressed bootrom image into the space provided by the PMM allocation
* routine. Only if no PMM BIOS is available, use interrupt 0x15 for copying.
* In that case, we also have to redirect interrupt 0x15 so that it will
* return the size of extended memory less than what we used for the bootrom
* copy. When using DDIM, we are actually running in RAM, so we can write
* into variables stored in the code segment. After redirecting the interrupt,
* set the new ROM size.
* The PMM specification states that when the Boot Entry Vector is called
* lateron, no PMM services are available anymore. However, the extended
* memory contents should be left untouched by PMM so we can still use it,
* even though its not quite clean, but unfortunately, the PXE specification
* requires the use of DDIM and PMM if available.
*/
1:
#ifndef NODDIM
test byte ptr ds:ram_flags[RAM_START],RAMFL_WRITE
jz 8f
or byte ptr ds:ram_flags[RAM_START],RAMFL_DDIM
mov ebx,dword ptr cs:[romsiz]
or ebx,ebx # get size of compressed rom image
jz 0f
add ebx,0x03FF # and convert it into kB
shr ebx,10
mov di,cs # compute linear address of compressed
shl edi,4 # bootrom image
add edi,offset __text_end + 0x000F
and edi,0x000FFFF0
call findpmm # find PMM entry point
or eax,eax
jz 5f # check if PMM entry point found
push PMM_FLAGS # push flags
data32 push PMM_RAMSIG # push memory block signature
shl ebx,6 # convert size into paragraphs
push ebx # push size of memory block
push PMM_ALLOCATE # push PMM function code
push cs
push offset 2f # push return address
push eax # push PMM entry point
lret # call PMM
2: add sp,12 # cleanup stack
mov si,dx
shl esi,16
mov si,ax # check if we got a valid memory block
or esi,esi # in case the memory allocation routine
jz 0f # returned with an error we cant divert
cmp esi,0xFFFFFFFF # to using int 0x15 because thats pro-
je 0f # hibited when PMM is available
mov dword ptr ds:ram_extadr[RAM_START],esi
mov ebx,cs:[romsiz] # get size of compressed rom image
add ebx,0x0003 # convert it into number of double-
shr ebx,2 # words
xor ax,ax
mov ds,ax # reload big linear segment into DS
3: dec bx # we have to do the copying routine
js 4f # by hand (instead of using movsd)
addr32 mov eax,[edi + ebx * 4] # because movsd just uses si/di, not
addr32 mov [esi + ebx * 4],eax # esi/edi.
jmp 3b
4: or byte ptr ds:ram_flags[RAM_START],RAMFL_PMM
jmp 7f
5: call getext
sub eax,ebx # compute new extended memory size
jae 6f
0: and byte ptr ds:ram_flags[RAM_START],_NOT(RAMFL_DDIM)
jmp 8f
6: push eax
mov ebx,eax # compute linear address of new bootrom
shl ebx,10 # image copy
add ebx,0x00100000
mov dword ptr ds:ram_extadr[RAM_START],ebx
mov edx,edi
mov ecx,cs:[romsiz] # get size of compressed rom image
call copy_highmem # copy image into extended memory
pop eax
jc 0b
mov cs:[newsiz],eax # save new extended memory size
mov ax,cs
mov es,ax
mov di,offset oldi15 # set new interrupt
mov si,INT_MISC * 4
mov bx,offset int15
call intset
7: mov dx,offset __text_end + 0x07FF
and dx,0xF800
shr dx,9 # compute new bootrom size
mov cs:[romlen],dl # set new rom length
mov cs:pcihdr_romlength[pcihdr],dx
or byte ptr ds:ram_flags[RAM_START],RAMFL_CHKSUM
#endif
/*
* Finally compute the checksum of the RAM data area, the checksum of the
* bootrom (if necessary) and return to the caller.
*/
8: mov si,RAM_START
mov ecx,RAM_SIZE # compute new ram area checksum
call dochksum # (requires 4 stack bytes)
sub byte ptr ds:ram_chksum[RAM_START],ah
xor ax,ax
mov ch,ds:ram_flags[RAM_START]
test ch,RAMFL_BBS
jz 9f # check if this device is an IPL
mov ax,0x0020 # device according to BBS
9: test ch,RAMFL_CHKSUM
jz 0f # check if the bootrom contents has
push ax # been changed
mov cx,cs
mov ds,cx
movzx32 ecx,byte ptr [romlen]
shl ecx,9
xor si,si
call dochksum # compute checksum for the bootrom
sub [newcks],ah
pop ax
0: pop es
pop ds
call rststk # restore the stack and registers
lret
#ifndef NODDIM
.line __LINE__
/*
**************************************************************************
*
* New 0x15 interrupt. It has been redirected to report the new reduced
* size of extended memory when using DDIM, but not PMM.
*/
int15: pushf
cmp ah,0x88 # check if we have to report the
je 4f # amount of free extended memory
cmp ax,0xE801
je 2f
cmp ax,0xE820
je 1f
cmp ax,0xE881
je 5f
popf
ljmp cs:[oldi15] # jump to old interrupt handler
/*
* Functions E820 and E881 are not supported
*/
1: popf
mov ah,0x86 # return with error: function not
stc # supported
jmp 9f
/*
* Function E801 can return the amount of memory above 16 MB
*/
2: mov ax,cs:[newsiz + 0]
xor bx,bx # check if less than 15 MB
cmp dword ptr cs:[newsiz],15 * 1024
jbe 3f
mov bx,cs:[newsiz + 0]
mov ax,cs:[newsiz + 2]
sub bx,15 * 1024 # compute amount of memory above 16 MB
sbb ax,0
shrd bx,ax,6 # compute number of 64 kB blocks
mov ax,15 * 1024 # set amount of memory below 16 MB
3: mov cx,ax
mov dx,bx
jmp 8f
/*
* Function E881 can also return the amount of memory above 16 MB
*/
5: mov eax,cs:[newsiz]
xor ebx,ebx
cmp eax,15 * 1024 # check if less than 15 MB
jbe 6f
mov ebx,15 * 1024
sub eax,ebx # compute amount of memory above 16 MB
shr eax,6 # compute number of 64 kB blocks
xchg eax,ebx
6: mov ecx,eax
mov edx,ebx
jmp 8f
/*
* Function 88 returns the amount below 16 MB
*/
4: mov ax,15 * 1024 # return a maximum of 15 MB
cmp dword ptr cs:[newsiz],15 * 1024
jae 8f
mov ax,cs:[newsiz + 0] # return new memory size
8: popf
clc
/*
* Return to caller with carry flag set properly
*/
9: push bp
mov bp,sp # put carry flag onto the stack
rcr word ptr [bp + 6],1
rol word ptr [bp + 6],1
pop bp
iret
#endif
.line __LINE__
/*
**************************************************************************
*
* Handle a redirected bootstrap interrupt vector.
*/
int18: cli
xor ax,ax
mov ss,ax # set new stack
mov sp,BOOTLOAD
sti
cmp word ptr cs:[botvec],INT_BOOTSTRAP * 4
jne 1f
xor dx,dx
int INT_DISK # reset the disk system
mov cx,5 # loop counter
mov bx,BOOTLOAD # put loading address into ES:BX
mov es,dx
3: push cx
mov ax,0x0201
mov cx,0x0001 # try to load first sector of first
int INT_DISK # track on floppy drive A
pop cx
jnc 4f # if error, we dont have a floppy
loop 3b # try up to 5 times
jmp 1f
4: cld
mov di,bx
mov al,byte ptr es:[di] # check that the boot sector doesnt
mov cx,0x0080 # contain only one value
repe scasb
jcxz 1f # boot sector is empty so try network
call checkram # check if data area valid
jc 6f
mov eax,dword ptr ds:ram_oldint[RAM_START]
mov dword ptr ds:[INT_BOOTSTRAP * 4],eax
6: mov word ptr ds:ram_id[RAM_START],0 # make data area invalid
#ifndef NODDIM
mov ax,cs
shl eax,16
mov ax,offset int15 # check if the interrupt 15h
cmp dword ptr ds:[INT_MISC * 4],eax # vector has been changed
jne 5f
mov eax,cs:[oldi15]
mov dword ptr ds:[INT_MISC * 4],eax # restore it
#endif
5: xor di,di
ljmp 0,BOOTLOAD # call floppy boot sector
1: push cs
call dorom # start IPL process
or ax,ax # check error code
jnz 2f
/*
* If the bootrom failed, try to start the system using the BIOS. This method
* should only be used if we dont have BBS.
*/
bootfail:
mov si,offset keymsg
call prnstr # wait for key press
xor ax,ax
int INT_KBD
2: mov ax,BIOS_SEG # try to start the system using the BIOS
mov ds,ax
mov word ptr ds:[BIOS_RSTFLAG],0
int INT_BOOTSTRAP
jmp 2b
.line __LINE__
/*
**************************************************************************
*
* Start the bootrom. This is called UNDI IPL in the PXE specification.
* While the code above is just for the BIOS interface, now decompress
* the bootrom and start the game.
*
* Input: none
* Output: if network boot successful, routine does not return
* if network boot unsusccessful:
* if stack cannot get restored, routine does not return
* otherwise:
* ax = 0 --> loader error
* ax = 1 --> user abort
*/
dorom:
/*
* First we have to switch the stack, because we need quite some amount
* of stack space. Unfortunately, some BIOS incorrectly set the top of
* stack to 0:0400 instead of 0:7C00, and we would delete the interrupt
* vector table in that case.
*/
call setstk # set new stack
push ds # save all segment registers
push es
push fs
push gs
/*
* Print the startup message
*/
cld
mov si,offset prdnam
call prnstr # print bootrom startup message
mov si,offset copyrt
call prnstr
call prcrlf
/*
* Check that the data area has not been tampered, and mark the data area
* as invalid so that we can restart the bootrom through a warm boot if
* necessary.
* Then restore all interrupts which have been redirected previously. The
* data area, which resides in the interrupt vector table, doesnt have to
* be restored. If we cant restore the 0x15 interrupt vector its not such a
* problem because this code will remain in ROM and the vector will therefore
* remain valid. But we cant reclaim the used memory in that case.
*/
call checkram # check data area
setc bl # remember status for later
mov word ptr ds:ram_id[RAM_START],0 # make data area invalid
mov si,cs:[botvec]
mov ecx,dword ptr ds:ram_oldint[RAM_START]
or bl,bl # restore with either the orig
jz 1f # or default vector (if the ram
cmp si,INT_BOOTSTRAP * 4 # area has been destroyed)
jne 2f
mov ecx,BIOS_BOOT_SEG * 65536 + BIOS_BOOT_OFS
1: jecxz 2f
mov dword ptr [si],ecx # restore it
2: mov dx,cs # save CS into DX
#ifndef NODDIM
mov ax,dx
shl eax,16
mov ax,offset int15 # check if the interrupt
cmp dword ptr ds:[INT_MISC * 4],eax # vector has been changed
jne 3f
mov ecx,cs:[oldi15]
mov dword ptr ds:[INT_MISC * 4],ecx # restore it
#endif
3: mov si,offset datmsg # print error message if ram
or bl,bl # area destroyed
jnz error
/*
* Check if the user really wants to start the netboot process. If not we
* can simply return to the BIOS. Note that we should not change DX here.
* Also note that we need to store the timeout value onto the stack since
* we are still running within a ROM (even when using DDIM the BIOS has
* turned us read-only by now). When we got started using BBS, the BIOS
* already cared for selecting the boot device, so there is no need to
* ask the user again.
*/
test byte ptr ds:ram_flags[RAM_START],RAMFL_BBS
jnz 4f
mov ax,cs:[botflg]
test ax,ROMFLG_ASKNET
jz 4f
push ebx # make space on stack for timeout value
mov bp,sp # BP points to timeout value
and ax,ROMFLG_ASKTIME
jz 7f # check if no timeout
#if ROMFLG_SHIFT_ASKTIME > 0
shr ax,ROMFLG_SHIFT_ASKTIME
#endif
call settimeout # set keyboard timeout
mov si,offset askmsg # print message
call prnstr
6: mov ah,0x01 # check if keystroke available
int INT_KBD
jnz 7f
call chktimeout # check if timeout reached
jc 6b
mov al,0x4E # assume 'N' if timeout reached
jmp 8f
7: xor ah,ah # get keystroke
int INT_KBD
8: pop ebx # restore stack
push ax
call prnchr # print entered character
call prcrlf
pop ax
cmp al,0x59 # check for 'Y'
je 4f
cmp al,0x79 # check for 'y'
je 4f
cmp al,0x5A # check for 'Z' (e.g. 'Y' on German kbd)
je 4f
cmp al,0x7A # check for 'z'
je 4f
mov ax,1 # set return error code
jmp romend # return to BIOS
/*
* Copy this code into RAM so that we can use variables with the decompression
* code. Also we save the ROM address.
*/
4: push ds
mov bx,LOADADR
mov cx,offset __text_end + 0x000F
and cx,0xFFF0
shr cx,2
mov ds,dx
mov es,bx # move boot loader into lower ram
xor si,si
xor di,di
rep movsd
cmp dx,MEMEND # check if we are running within a
jb 5f # physical ROM
mov es:[romseg],dx # save physical ROM segment
5: push es
push offset 1f # jump to new copy
lret
/*
* We are now in RAM. If a Flash EPROM loader is installed, copy it into
* RAM as well.
*/
1: test word ptr cs:[botflg],ROMFLG_FLOADER
jz 2f # check if flash loader installed
mov ds,dx
mov cx,[si + FLOADSIZEOFS]
shr cx,1 # copy flash loader from ROM into
rep movsw # RAM at the end of this bootrom
pop ds # loader
jmp 4f # skip DDIM checking
2: pop ds
/*
* Copy the compressed image from extended memory and save the values necessary
* to access the compressed image.
*/
#ifndef NODDIM
test byte ptr ds:ram_flags[RAM_START],RAMFL_DDIM
jz 3f
mov edx,dword ptr ds:ram_extadr[RAM_START]
mov ebx,COMPADR * 16
mov ecx,cs:[romsiz] # copy the compressed bootrom image
call copy_highmem # out of extended memory
mov si,offset cpymsg
jc error
xor si,si # let DX:SI point to new compressed
mov dx,COMPADR # image copy
#endif
3: mov word ptr cs:[inptr + 0],si
mov word ptr cs:[inptr + 2],dx
mov ax,cs:[infadr] # set pointer for decompressed bootrom
shl eax,16 # image
mov dword ptr cs:[outptr],eax
/*
* Check that we have a PnP BIOS and find the PnP BIOS entry point structure.
* If we have been called according to the BBS, we already know the address
* of the PnP BIOS entry structure. Otherwise, we have to scan the BIOS memory
* area.
* If we dont have a PnP read port number already, call the PnP BIOS to read
* the configuration information which contains the read port number.
*/
4: test byte ptr ds:ram_flags[RAM_START],RAMFL_BBS
jnz 6f
mov word ptr ds:ram_pnpstruct[RAM_START + 0],0
mov word ptr ds:ram_pnpstruct[RAM_START + 2],PNPE_STRUCT_SEG
5: les di,ds:ram_pnpstruct[RAM_START]
call checkpnp
jnc 6f
inc word ptr ds:ram_pnpstruct[RAM_START + 2]
jnz 5b
6: cmp word ptr ds:ram_pnport[RAM_START],0
jne 7f
les di,ds:ram_pnpstruct[RAM_START]
mov ax,es
or ax,di # check if we have a PnP BIOS at all
jz 7f
sub sp,PNP_CONF_SIZE # make space on stack for config struct
mov bp,sp
push word ptr es:pnpe_real_data[di]
push ss
push bp # push far pointer to config struct
push PNP_FUNC_GET_CONFIG # push PnP BIOS opcode
lcall es:pnpe_real_entry[di] # call PnP BIOS
mov dx,[bp + PNP_CONF_DATA_PORT]
add sp,PNP_CONF_SIZE + 8 # restore stack
cmp ax,PNP_RET_SUCCESS # check for errors
jne 7f
mov ds:ram_pnport[RAM_START],dx
/*
* If we are running on a PCI device, but havent been called according to
* the BBS, we have to determine the PCI bus/dev/func number (PFA) in order
* to pass valid values to the bootrom.
*/
7: mov bx,word ptr ds:ram_pcipfa[RAM_START]
call checkpci
mov si,offset pcimsg # print error message if device not
jc error # found
mov word ptr ds:ram_pcipfa[RAM_START],bx
/*
* Some network cards offer a flash EPROM for the bootrom. However, various
* cards dont map the full flash EPROM into the processor space, but just a
* small window (8kB or 16kB are common) from the start of the flash EPROM.
* In those cases, the flash programmer can choose to put the compressed
* bootrom image into the non-visible flash EPROM area. It then adds an
* access routine at the end of this bootrom loader and puts a flag into
* the bootrom header. We check this flag, and if set call it with BX = PCI
* PFA and DX = PnP CSN. It returns an offset to the get-byte routine in AX.
* If AX is returned zero, the flash EPROM could not get accessed.
* Otherwise, we can then use the get-byte routine to decompress the bootrom
* image. If the flash EPROM loader vector is zero, there is no flash EPROM
* access driver available, and we can simply decompress the bootrom image
* as it is.
*/
4: mov dx,word ptr ds:ram_pnpcsn[RAM_START]
push ds
mov ax,cs
mov ds,ax # set segment registers
mov es,ax
cli
mov di,ss
mov si,sp
mov ss,ax # the flash loader and decompression
mov sp,0xFFFE # codes need the stack to be within
push di # the current code segment
push si
sti
mov di,offset __text_end + 0x000F
and di,0xFFF0
test word ptr [botflg],ROMFLG_FLOADER
jz 2f # check if flash loader installed
mov si,offset romsiz
call di # call flash init routine
or ax,ax # check for error
mov si,offset flsmsg # print error message
jz 3f
mov [getfls],ax # save offset to get-byte routine
2: call inflate # decompress the block
or al,al # check for error
mov si,offset fmtmsg # print error message
jnz 3f
xor si,si # no error
3: cli
pop ax
pop bx
mov ss,bx # restore stack
mov sp,ax
sti
pop ds # restore data segment
or si,si
jnz error # print error message
/*
* Print the bootrom BIOS options. Dont change DX here!
*/
#ifndef NOPRINT
mov si,offset optmsg # print options message
call prnstr
mov ch,byte ptr ds:ram_flags[RAM_START]
test ch,RAMFL_BBS + RAMFL_DDIM + RAMFL_PMM
mov si,offset nonmsg # print 'none'
jz 4f
test ch,RAMFL_BBS # check for BBS calling sequence
jz 2f
mov si,offset bbsmsg # print BBS message
call prnstr
test ch,RAMFL_DDIM + RAMFL_PMM
jz 5f
call prncom # print comma
2: test ch,RAMFL_DDIM # check for DDIM option
jz 5f
mov si,offset dimmsg # print DDIM message
call prnstr
test ch,RAMFL_PMM # check for PMM option
jz 5f
call prncom # print comma
mov si,offset pmmmsg # print PMM message
4: call prnstr
5: call prcrlf
call prcrlf
#endif
/*
* Prepare the kernel loader structure. We dont pass an address for the
* UNDI ROMID structure.
*/
sub sp,bc_loader_size
mov bp,sp
mov ax,word ptr ds:ram_pcipfa[RAM_START]
mov o_bcl_ax[bp],ax
mov ax,word ptr ds:ram_pnpcsn[RAM_START]
mov o_bcl_bx[bp],ax
mov ax,word ptr ds:ram_pnport[RAM_START]
mov o_bcl_dx[bp],ax
mov eax,ds:ram_pnpstruct[RAM_START]
mov o_bcl_pnp_ptr[bp],eax
mov dword ptr o_bcl_undi_romid[bp],0
/*
* Finally start the bootrom image loader, called base code loader in the
* PXE specification. But instead of using the loader entry point specified
* in the ROMID structure, we are using our own entry point. This way we
* dont need to check for a valid ROMID structure.
* The loader gets a far pointer to the loader structure as a parameter on
* the stack.
*
* Set the return address so that the bootrom can return to us savely. How-
* ever, we can only have a return address if this code has NOT been started
* off a floppy, and it has to return into physical ROM, not our own RAM
* copy.
*/
push ss # push far pointer to loader struct
push bp
mov ax,cs:[romseg]
push ax # push ROM segment
or ax,ax # check if we are running off a
jz 1f # floppy
mov ax,offset bbsret # use BBS return address
test cl,RAMFL_BBS # check if we are running under BBS
jnz 1f
mov ax,offset nobbs # use non-BBS return address
1: push ax # push return offset
mov ax,cs:[infadr]
mov es,ax # push address of kernel loader
push ax
mov si,es:[KRNROMIDOFS]
push word ptr es:o_kr_loadofs[si]
lret # jump to kernel loader
/*
* If the bootrom returns, we cant simply get back to the BIOS since we
* dont know if the old BIOS stack is still valid. Therefore, depending on
* whether we have BBS or not, use interrupts to get back to the BIOS.
* Note that these routines can only be called within a physical ROM. If
* we have been started from a floppy, the bootrom is never allowed to
* return.
*
* With BBS, we can use the BOOTFAIL interrupt after a short pause. Other-
* wise just get back using the BOOTSTRAP interrupt.
*/
bbsret: call dowait
int INT_BOOTFAIL # get back to BBS BIOS
nobbs: jmp bootfail
/*
* If we have some sort of error, print an error message and return
* to the BIOS.
*/
error: push si
mov si,offset errmsg
call prnstr
pop si
call prnstr # print error message
call prcrlf
xor ax,ax # set return error code
mov ds,ax
test byte ptr ds:ram_flags[RAM_START],RAMFL_BBS
jz romend
call dowait
romend: pop gs
pop fs
pop es
pop ds # restore all registers
call rststk # restore stack
lret # return to BIOS
.line __LINE__
/*
**************************************************************************
*
* Messages:
*/
errmsg: .asciz "Loader error: "
datmsg: .asciz "data"
fmtmsg: .asciz "format"
cpymsg: .asciz "copy"
flsmsg: .asciz "flash"
pcimsg: .asciz "PCI ID"
keymsg: .asciz "Press a key to continue"
#ifndef NOPRINT
optmsg: .asciz "BIOS options: "
nonmsg: .asciz "none"
bbsmsg: .asciz "BBS"
dimmsg: .asciz "DDIM"
pmmmsg: .asciz "PMM"
#endif
askmsg: .asciz "Boot from network (Y/N)? "
.line __LINE__
/*
**************************************************************************
*
* Wait some seconds when using BBS to let the user see an error message
* Input: none
* Output: none
* Registers changed: BP
*/
dowait: push ax
push bp
push ebx
mov al,BBSWAIT
mov bp,sp
call settimeout
1: call chktimeout
jc 1b
add sp,4
pop bp
pop ax
ret
.line __LINE__
/*
**************************************************************************
*
* Print a string onto the console screen
* Input: CS:SI - Pointer to string
* Output: none
* Registers changed: AX, SI
*/
prcrlf: mov si,offset crlf # print CR/LF
jmp prnstr # jmp is shorter than "mov al,cs:[si]"
1: call prnchr # send character to screen
inc si
prnstr: mov al,cs:[si] # get next character
or al,al
jnz 1b
ret
.line __LINE__
/*
**************************************************************************
*
* Print a character onto the console screen
* Input: AL - Character to print
* Output: none
* Registers changed: AX
*/
#ifndef NOPRINT
prncom: mov al,0x2C # print a comma
#endif
prnchr: push bx
mov ah,0x0E # BIOS command for character output
mov bx,0x0007 # screen page and colors
int INT_VIDEO # send character to screen
pop bx
ret
.line __LINE__
/*
**************************************************************************
*
* Save all general registers onto the stack and make sure that we
* have at least 7 kB of stack space available.
* Input: none
* Output: none
* Registers changed: SS, SP
*/
setstk:
push 0
push ebp
mov bp,sp # put EDX onto the stack and exchange
xchg edx,[bp + 4] # it with the return address
shr edx,16
push ecx # push registers onto the stack
push ebx
push eax
mov ax,ss
mov bx,sp
or ax,ax # check if current stack pointer is
jnz 1f # below 0000:2000
cmp bx,MINSTK
jae 1f
cli
mov ss,ax # set new stack right at the beginning
mov sp,BOOTLOAD # of the boot sector load area
sti
1: push esi # save index registers
push edi
push ax # push old stack pointer
push bx
push dx # push return address
push ds
mov ds,ax
mov eax,[bx + 0] # restore all registers from old
mov edx,[bx + 16] # stack
mov ecx,[bx + 8]
mov ebx,[bx + 4]
pop ds
ret
.line __LINE__
/*
**************************************************************************
*
* Restore all general registers from stack, and restore the stack pointer
* Input: none
* Output: none
* Registers changed: All registers except EAX
*/
rststk:
pop dx # get return address
pop cx # get old stack pointer
pop bx # get old stack segment
pop edi # get old index registers
pop esi
cli
mov ss,bx # restore old stack
mov sp,cx
sti
pop ebx # get general registers from old stack
pop ebx
pop ecx
shl edx,16
mov bp,sp # exchange EDX value on stack with
xchg edx,[bp + 4] # return address, and restore EDX
pop ebp
add sp,2
ret
.line __LINE__
/*
**************************************************************************
*
* Check that we have a correct PCI network device
* Input: BX - PCI PFA (or 0xFFFF if unknown)
* Output: BX - PCI PFA
* Carry flag set if error
* Registers changed: EAX, EBX
*/
checkpci:
push ecx
push edx
push esi
push edi
mov ax,cs:pcihdr_vendorid[pcihdr]
or ax,cs:pcihdr_deviceid[pcihdr]
jnz 1f # check if we have a PCI device
mov bx,0xFFFF # return unknown PFA if not a PCI device
jmp 9f # carry will be cleared by 'or'
1: push bx
xor edi,edi
mov ax,PCI_FUNC_INST_CHECK # check if PCI functions supported
int INT_PCI
pop bx
jc 9f
or ah,ah
jnz 8f
mov si,cs:[botflg]
and si,ROMFLG_DEVNUM # if request device number is zero,
jz 3f # use default search action
#if ROMFLG_SHIFT_DEVNUM > 0
shr si,ROMFLG_SHIFT_DEVNUM
#endif
dec si # otherwise search for numbered device
jmp 2f
3: cmp bx,0xFFFF
je 2f # check that we dont have a PFA already
mov di,PCI_REG_VENDOR_ID
mov ax,PCI_FUNC_READ_WORD # read PCI vendor ID
int INT_PCI
jc 2f # if error, the known PFA is incorrect
cmp cx,cs:pcihdr_vendorid[pcihdr]
jne 2f # check for valid vendor ID
mov di,PCI_REG_DEVICE_ID
mov ax,PCI_FUNC_READ_WORD # read PCI device ID
int INT_PCI
jc 2f # if error, the known PFA is incorrect
cmp cx,cs:pcihdr_deviceid[pcihdr]
je 9f # check for valid device ID
2: mov ax,PCI_FUNC_FIND_DEV
mov cx,cs:pcihdr_deviceid[pcihdr]
mov dx,cs:pcihdr_vendorid[pcihdr]
int INT_PCI # search for specified device
jc 9f
or ah,ah
jz 9f
8: stc
9: pop edi
pop esi
pop edx
pop ecx
ret
.line __LINE__
/*
**************************************************************************
*
* Check that the interrupt table contains our data structure
* Input: none
* Output: DS - segment of interrupt table
* Carry set if data structure not found
* Registers changed: AX, SI, DS
*/
checkram:
push ecx
xor ax,ax
mov ds,ax
cmp word ptr ds:ram_id[RAM_START],RAM_SIGNATURE
jne 8f
mov si,RAM_START
mov ecx,RAM_SIZE # the data signature has to be valid
call dochksum # and the checksum has to be correct
or ah,ah # this will clear the carry bit
jz 9f
8: stc # return with error
9: pop ecx
ret
.line __LINE__
/*
**************************************************************************
*
* Check for $PnP installation structure
* Input: ES:DI - pointer to installation structure
* Output: Carry flag set if pointer does not show to a PnP structure
* Registers changed: AX
*/
checkpnp:
push ecx
mov cx,es # check if we have a pointer to a
or cx,di # system BIOS PnP installation check
jz 8f # structure
cmp dword ptr es:pnpe_sig[di],PNPE_SIGNATURE
jne 8f
push ds
push si
mov cx,es # compute checksum of PnP installation
mov ds,cx # check structure
mov si,di
movzx32 ecx,byte ptr pnpe_length[si]
call dochksum
pop si
pop ds
or ah,ah # if its ok, then we have a PnP BIOS
jz 9f
8: stc
9: pop ecx
ret
.line __LINE__
/*
**************************************************************************
*
* Compute checksum of a given memory area
* Input: DS:SI - pointer to memory area
* ECX - size of memory area
* Output: AH - checksum
* Registers changed: AX, ECX, SI
*/
dochksum:
cld
push ds
push dx
xor ah,ah
jecxz 9f # check if anything to do at all
1: cmp si,0x8000
jb 2f
mov dx,ds
add dx,0x0800 # dont let the SI register overrun
mov ds,dx
sub si,0x8000
2: lodsb
add ah,al # add all bytes together
addr32 loop 1b
9: pop dx
pop ds
ret
#ifndef NODDIM
.line __LINE__
/*
**************************************************************************
*
* Copy into and out of extended memory.
* Input: EDX - source linear address
* EBX - destination linear address
* ECX - number of bytes to copy
* Output: Carry flag set if error
* Registers changed: EAX, EBX, ECX, EDX
*/
copy_highmem:
push bp
push es
push si
inc ecx
shr ecx,1 # convert byte count into word count
1: push ecx
cmp ecx,0x00008000 # copy a maximum of 0x8000 words at one
jbe 2f # time
mov cx,0x8000
2: mov si,GDTSTRUCTLEN # get space for GDT on stack
sub sp,si
mov bp,sp
3: dec si
mov byte ptr [bp + si],0 # fill GDT with zeroes
jnz 3b
/*
* Setup source segment address in GDT
*/
mov word ptr [bp + GDTSRCADR + 0],dx
ror edx,16
mov byte ptr [bp + GDTSRCADR + 2],dl
mov byte ptr [bp + GDTSRCHIADR],dh
ror edx,16
mov word ptr [bp + GDTSRCLEN],0xFFFF
mov byte ptr [bp + GDTSRCRIGHT],GDTACCESS
/*
* Setup destination segment address in GDT
*/
mov word ptr [bp + GDTDESTADR + 0],bx
ror ebx,16
mov byte ptr [bp + GDTDESTADR + 2],bl
mov byte ptr [bp + GDTDESTHIADR],bh
ror ebx,16
mov word ptr [bp + GDTDESTLEN],0xFFFF
mov byte ptr [bp + GDTDESTRIGHT],GDTACCESS
/*
* Actually copy the memory using the BIOS
*/
mov ax,ss # let ES:SI point to the GDT
mov es,ax # CX is already set with the number
mov si,bp # of words to copy
mov ah,0x87
clc
int INT_MISC # call the BIOS to do the copy
lahf
add sp,GDTSTRUCTLEN # restore stack
shl ecx,1
add ebx,ecx # set address to next block
add edx,ecx
pop ecx # restore copy count
sahf
jc 9f # check for error
sub ecx,0x00008000
ja 1b # continue with next block to copy
clc # return without error
9: pop si
pop es
pop bp
ret
#endif
#ifndef NODDIM
.line __LINE__
/*
**************************************************************************
*
* Find POST Memory Manager entry point
* Input: none
* Output: EAX - PMM entry point, zero if not found
* Registers changed: EAX, CX, SI
*/
findpmm:
push ds
mov ax,PMM_STARTSEG - 1 # start scanning for PMM structure
1: inc ax
jz 8f # check if at end of memory
mov ds,ax
cmp dword ptr ds:[pmm_signature],PMM_SIGNATURE
jne 2f
xor si,si
movzx32 ecx,byte ptr ds:[pmm_length]
call dochksum # check if checksum is correct
or ah,ah
jnz 2f
mov eax,dword ptr ds:[pmm_entry]
or eax,eax
jnz 9f # the entry point should not be zero
2: mov ax,ds
jmp 1b # continue with next segment
8: xor eax,eax # PMM entry not found
9: pop ds
ret
#endif
#ifndef NODDIM
.line __LINE__
/*
**************************************************************************
*
* Determine amount of extended memory
* Input: none
* Output: EAX - amount of extended memory in kB
* Registers changed: EAX
*/
getext:
push ebx
push ecx
push edx
mov ax,0xE801 # try 16-bit version
int INT_MISC
jc 3f
mov cx,ax # some BIOS return incorrect
or cx,bx # values
jnz 1f
mov ax,cx
mov bx,dx
1: cwde # clear upper word
and ebx,0xFFFF
shl ebx,6 # multiply by 64
add eax,ebx # and add to amount below 16M
jmp 7f
3: mov ah,0x88 # try very old method
clc
int INT_MISC # call BIOS to get amount of
jnc 4f # extended memory in kB
xor ax,ax # no extended memory found
4: cwde
7: pop edx
pop ecx
pop ebx
ret
#endif
.line __LINE__
/*
**************************************************************************
*
* Set timer timeout in 1 second intervalls
* Input: AL - Timeout value
* BP - Pointer to 4-byte save space
* Output: none
* Registers changed: AX
*/
settimeout:
push cx
push dx
mov ah,18 # compute number of timer ticks
mul ah
push ax
xor ah,ah
int INT_TIME
pop ax
add dx,ax
adc cx,0
mov word ptr [bp + 0],dx
mov word ptr [bp + 2],cx
pop dx
pop cx
ret
.line __LINE__
/*
**************************************************************************
*
* Check if timeout value reached
* Input: BP - Pointer to 4-byte save space
* Output: Carry flag set if timeout not reached
* Registers changed: AX
*/
chktimeout:
push cx
push dx
xor ah,ah
int INT_TIME
cmp cx,word ptr [bp + 2]
jne 9f
cmp dx,word ptr [bp + 0]
9: pop dx
pop cx
ret
.line __LINE__
/*
**************************************************************************
*
* Set new interrupt. Dont delete the old vector if it has been saved
* already.
* Input: DS:SI - pointer to interrupt vector
* CS:BX - pointer to new handler routine
* ES:DI - pointer to old vector repository
* Output: none
* Registers changed: EAX
*/
intset:
pushf
cli
mov ax,cs
shl eax,16
mov ax,bx
cmp dword ptr [si],eax
je 9f
xchg eax,dword ptr [si]
mov dword ptr es:[di],eax
9: popf
ret
.line __LINE__
/*
**************************************************************************
*
* Return the next byte from the compressed image.
* Input: none
* Output: AL - next byte
* Zero flag set if end of file, AL is zero in that case
* Changed registers: AX
*/
getbyte:
jmp word ptr [getfls] # call get-byte routine
readbyte:
cmp dword ptr [romsiz],0 # check if at end of file
jz 9f
pushf # remember zero flag
push si
push ds
lds si,[inptr]
lodsb # get next byte from compressed
pop ds # image
test si,0x000F
jnz 1f
shr si,4 # if offset is on paragraph
add word ptr [inptr + 2],si # boundary, increase segment
xor si,si # and reset offset
1: mov word ptr [inptr + 0],si
dec dword ptr [romsiz]
pop si
popf # restore zero flag
9: ret
.line __LINE__
/*
**************************************************************************
*
* Write a byte to the decompressed image.
* Input: AL - output byte
* Output: none
* Registers changed: none
*/
putbyte:
push di
push es
les di,[outptr]
stosb # write byte into output buffer area
test di,0x000F
jnz 1f
shr di,4 # adjust segment
add word ptr [outptr + 2],di
xor di,di
1: mov word ptr [outptr + 0],di
pop es
pop di
ret
.line __LINE__
/*
**************************************************************************
*
* Define the data segment (we dont have a seperate data segment)
*/
outptr: .word 0 # ptr into decompressed memory
.word DECOMPMEM
inptr: .long 0 # ptr into compressed memory
romseg: .word 0 # physical ROM segment
getfls: .word readbyte # get-byte routine
/*
**************************************************************************
*/
.end