"Fossies" - the Fresh Open Source Software archive 
Member "netboot-0.10.2/mknbi-mgl/first/first-dos.S86" of archive netboot-0.10.2.tar.gz:
/*
* first-dos.S86 - MGL DOS loader
*
* Copyright (C) 2003-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: first-dos.S86,v 1.8 2007/01/06 18:31:37 gkminix Exp $
*
*====================================================================
*/
#define DEF_HEAP_SIZE
#define DEF_STACK_SIZE
#include <common.i86>
#include <system/bios.i86>
#include <dos/general.i86>
#include <dos/exec.i86>
#include <net/ethernet.i86>
#include <net/bootp.i86>
#include "headers/runtime.i86"
.file __FILE__
.line __LINE__
/*
*====================================================================
*
* Definition of heap and stack size
*/
/* Define some minimal stack */
__stack_size = 8192
/* Heap is just required to hold the BOOTP record */
__heap_size = 4096
/*
*====================================================================
*
* Definitions local to this module
*/
#define READSIZE 512 /* size of one chunk of disk data */
#define RD_DATASIZE 53 /* size of DOS ramdisk info struct */
#define RD_XMSHANDLE 49 /* offset to BOOTP XMS handle */
#define RD_XMSSIZE 51 /* offset to BOOTP size */
#define MGL_SIZE 128 /* number of kB to allocate for MGL */
.line __LINE__
/*
*====================================================================
*
* Define the text segment.
*/
.text
.extern prcrlf # externals from runtime library
.extern prnstr
.extern prnchr
.extern chkcpu
.extern brk
.extern sbrk
.global _start
_start: jmp 1f
/*
* EXE file name to use when we are unable to find a valid file
* name in the environment. This can be caused by various debuggers.
* This name will be filled in by the MGL compiler.
*/
exenam: .space 14
/*
* Initialize the data and BSS segments
*/
1: cld
push ds
mov ax,ss # SS is set to the data segment
mov ds,ax # by the linker script
mov es,ax
mov di,offset __bss_start
mov cx,offset __bss_end
sub cx,di
inc cx # clear out the BSS segment
shr cx,1
xor ax,ax
rep stosw
pop word ptr [pspseg] # save PSP segment
mov [stktop],sp # save top of stack
/*
* Print program title and copyright information
*/
mov si,offset id1msg
call prnstr
mov al,0x20
call prnchr
mov si,offset id2msg
call prnstr
call prcrlf
mov si,offset id3msg
call prnstr
call prcrlf
/*
* Check for correct DOS version, and that we are running on
* a 386+ cpu.
*/
mov ax,0x3000
xor bx,bx
int INT_DOS # get DOS version
xchg al,ah
cmp ax,0x0300 # has to be at least 3.0
jae 2f
mov si,offset doserr
4: call prnstr # print error message
call prcrlf
xor ah,ah
int INT_DOS # terminate the program
lret
2: call chkcpu # check if running on correct CPU
or si,si
jnz 4b
/*
* Determine the amount of memory required for this loader program, and
* reset the memory block size accordingly.
*/
push es
mov ax,[stktop]
add ax,0x000F
shr ax,4 # compute end segment
mov bx,ss
add bx,ax
mov ax,[pspseg] # compute total size of program
sub bx,ax
mov es,ax
inc bx
mov ax,0x4A00
int INT_DOS # readjust program memory block size
pop es
jnc 3f
mov si,offset interr
jmp 8f
/*
* Read the BOOTP buffer either from a DOS disk file, or from the XMS
* memory space provided by the netboot ramdisk driver.
*/
3: call getbtp
or si,si
jnz 8f
/*
* Read the MGL program out of this program file
*/
call getmgl
or si,si
jnz 8f
/*
* Call the MGL runtime module
*/
mov si,offset exemsg
call prnstr
call prcrlf
mov si,offset rtinfo
lcall [mglptr] # call MGL program
xor al,al # return with exit code 0
jmp 9f
/*
* Eventually print an error message and return to DOS
*/
8: call prnstr # print error message
call prcrlf
mov al,0x01 # return with exit code 1
9: mov ah,0x4C # if called from DOS, terminate the
int INT_DOS # program
lret
/*
*====================================================================
*
* Read the BOOTP/DHCP information from a DOS file
* Input: None
* Output: SI - Pointer to error message (zero if no error)
* Registers changed: AX, BX, CX, DX, SI
*/
getbtp:
call getfilename # get file name from command line
or dx,dx # check if file name specified
jz 2f
mov si,offset dflmsg # check if file name is DEFAULT
call chkfname
jnc 7f # set default BOOTP info
mov si,offset pxemsg # check if file name is PXE
call chkfname
jnc 8f # leave BOOTP info empty
mov si,offset xmsmsg # check if file name is XMS
call chkfname
jc 1f
call xmsget # get BOOTP info from XMS
jnc 8f
mov si,offset xmserr # return with error message
jmp 9f
1: call fileget # get BOOTP info from file
jmp 9f
2: call xmsget # try to get BOOTP info from XMS
jnc 8f
7: mov ax,offset btpdef # setup default BOOTP info
mov word ptr [btpptr + 0],ax
mov word ptr [btpptr + 2],ds
mov word ptr [btplen],BOOTP_MIN_SIZE
8: xor si,si
9: ret
/*
*====================================================================
*
* Get name of BOOT/DHCP file from command line. We assume that the
* filename is the first and only argument to the program.
* Input: None
* Output: DX - Offset to filename within PSP (zero if no name)
* Registers changed: AX, CX, DX, SI
*/
getfilename:
cld
push ds
xor dx,dx # DX gets the address of the filename
mov ds,[pspseg] # put pointer to command line into DS:SI
mov si,_I(psp_cmdlsize)
lodsb # get length of command line
movzx cx,al
jcxz 9f # check if command line empty
1: lodsb
cmp al,0x0D # check for end of command line
je 9f
cmp al,0x20 # loop until first non-blank found
je 2f
cmp al,0x09
jne 3f
2: loop 1b
jmp 9f # no file name found
3: mov dx,si
dec dx
4: lodsb
cmp al,0x0D # check for end of command line
je 5f
cmp al,0x20
je 5f # loop until blank or tab found
cmp al,0x09
je 5f
loop 4b
inc si
5: dec si
mov byte ptr [si],0 # mark end of filename string
9: pop ds
ret
/*
*====================================================================
*
* Compare a file name against a pattern
* Input: DX - offset to file name within PSP segment
* SI - offset to pattern
* Output: Carry flag set if file name matches pattern
* Registers changed: AX, SI, DI
*/
chkfname:
cld
push es
mov es,[pspseg]
mov di,dx
1: lodsb # load next character from pattern
scasb # compare it with file name
jne 8f
or al,al # check if at end of string
jz 9f
jmp 1b
8: stc # set carry if strings not equal
9: pop es
ret
/*
*====================================================================
*
* Read the BOOTP/DHCP information from a DOS file
* Input: DX - offset to file name within PSP segment
* Output: SI - offset to error message
* Registers changed: AX, BX, CX, DX, SI
*/
fileget:
push es
mov es,[pspseg]
call openfile # open file
pop es
mov si,offset opberr
jc 9f
xor ax,ax # determine pointer to disk buffer
call sbrk
mov word ptr [btpptr + 0],ax
mov word ptr [btpptr + 2],ds
mov word ptr [btplen],0
1: mov cx,READSIZE
mov ax,cx
call sbrk # advance heap pointer for next read
mov si,offset memerr
jc 8f
add word ptr [btplen],cx # increase size of BOOTP buffer
mov dx,ax
call readfile # read next sector from file
mov si,offset rdberr
jc 8f
cmp ax,READSIZE
je 1b # continue until end of file
xor si,si
8: push si
call closefile # close input file
pop si
or si,si
jz 9f # free memory in case of error
mov ax,word ptr [btpptr + 0]
call brk
9: ret
/*
*====================================================================
*
* Read the BOOTP/DHCP information from an XMS memory block
* Input: None
* Output: Carry flag set if error
* Registers changed: EAX, BX, CX, DX, SI
*/
xmsget:
push es
/*
* When running under DOS loaded by a netboot bootrom, the BOOTP
* information is stored in an XMS memory block if the nbramdrv
* driver has been used. First we have to check if the driver is
* indeed installed, and then read the information block.
*/
mov dx,offset rddev
mov ax,0x3D00 # open ramdisk controlling device
int INT_DOS # for reading
jc 8f # no ramdisk controller
mov bx,ax
push bx
mov dx,offset rddata
mov cx,RD_DATASIZE # read data from DOS ramdisk driver
mov ax,0x4402
int INT_DOS
pop bx
pushf
push ax
mov ah,0x3E # close file handle
int INT_DOS
pop ax
popf
jc 8f # check if read error
cmp ax,RD_DATASIZE # check if we got enough data
jne 8f
mov ax,rddata[RD_XMSHANDLE]
or ax,ax # the handle has to be correct
jz 8f
mov ax,rddata[RD_XMSSIZE]
or ax,ax # the size has to be correct
jz 8f
/*
* Now try to find an XMS controller and get its function dispatcher
* address.
*/
mov ax,0x4300
int INT_XMS # check if XMS services are available
cmp al,0x80
jne 8f
mov ax,0x4310
int INT_XMS # get XMS entry address
mov word ptr [xmsadr + 0],bx
mov word ptr [xmsadr + 2],es
mov ah,0x0E
mov dx,rddata[RD_XMSHANDLE]
lcall [xmsadr] # check if XMS handle is valid
or ax,ax
jz 8f
mov ax,rddata[RD_XMSSIZE]
add ax,0x0F # compute number of kB for
shr ax,4 # BOOTP information
cmp ax,dx # check if XMS block is large enough
ja 8f
/*
* Finally copy the BOOTP information block onto the heap
*/
mov ax,rddata[RD_XMSHANDLE]
mov [xmssrc],ax # save source handle
mov ax,rddata[RD_XMSSIZE]
inc ax # make BOOTP size even
and eax,0x0000FFFE
mov [xmslen],eax # save copy size
call sbrk # advance heap pointer
jc 8f # check if we have enough memory
mov word ptr [xmsdst + 0],ax
mov word ptr [xmsdst + 2],ds
mov ah,0x0B
mov si,offset xmsemm
lcall [xmsadr] # copy BOOTP information out of
or ax,ax # extended memory
jz 7f # check if error
mov eax,[xmslen]
mov [btplen],ax # save size of BOOTP info
mov eax,[xmsdst]
mov [btpptr],eax # save pointer to BOOTP info
clc
jmp 9f
7: mov ax,[xmsdst + 0] # in case of error, restore the
call brk # heap pointer
8: stc # no BOOTP record found
9: pop es
ret
/*
*====================================================================
*
* Read the MGL program from the program file
* Input: None
* Output: SI - Offset to error message
* Registers changed: EAX, BX, CX, DX, SI, DI
*/
getmgl:
push es
mov bx,(MGL_SIZE * 1024) / 16
mov ah,0x48 # allocate enough memory for reading
int INT_DOS # the MGL program
mov si,offset prgerr
jc 9f
mov word ptr [mglptr + 0],0 # save pointer to MGL program
mov word ptr [mglptr + 2],ax
mov word ptr [readseg],ax
mov word ptr [readcount],MGL_SIZE
call getprogname # get name of program file
mov si,offset noperr
jc 8f
call openfile # open program file
mov si,offset opperr
jc 8f
mov eax,file_size_value
call seekfile # seek to beginning of MGL program
mov si,offset skperr
jc 7f
1: mov es,[readseg] # get current read segment
xor dx,dx
mov cx,1024
call readfile # read next 1kB from file
mov si,offset rdperr
jc 7f
cmp ax,1024 # check if at end of file
jb 2f
shr ax,4
add word ptr [readseg],ax
dec word ptr [readcount]
jnz 1b # continue with next 1kB
mov si,offset prgerr # check if MGL program is too
jmp 7f # large for our buffer
2: mov es,word ptr [mglptr + 2]
mov di,es:rtdata_id[RT_DATAOFS]
mov si,offset rtid
mov cx,offset rtidend
sub cx,si # check if ID string in MGL
repe cmpsb # program is correct
mov si,offset inverr
jne 7f
xor si,si
7: push si
call closefile # close file
pop si
or si,si
jz 9f
8: mov es,word ptr [mglptr + 2]
mov ah,0x49 # release memory in case of error
int INT_DOS
9: pop es
ret
/*
*====================================================================
*
* Get the name of program .exe file
* Input: None
* Output: ES:DX - Pointer to file name
* Carry flag set if error
* Registers changed: AX, CX, DX, DI, ES
*/
getprogname:
cld
mov es,[pspseg]
mov es,es:[psp_env] # get segment of program environment
xor di,di
1: xor al,al
mov cx,0xFFFF # scan to end of environment string
repne scasb
jne 7f # error if no end found
mov al,es:[di] # check if at end of environment
or al,al
jnz 1b # continue with next string
mov ax,es:[di + 1]
or ax,ax # check if string count not zero
jz 7f
cmp ax,256 # check for unreasonably long string
jae 7f
lea dx,[di + 3] # determine address of program file
add di,ax # name
cmp byte ptr [di + 3],0 # check if name end with a zero
jne 7f
clc
jmp 9f
7: mov al,cs:[exenam] # check if a default EXE file name
or al,al # has been filled in
jz 8f
mov ax,cs
mov es,ax # set default EXE file name
mov dx,offset exenam
clc
jmp 9f
8: stc
9: ret
/*
*====================================================================
*
* Open a DOS file
* Input: ES:DX - Pointer to filename
* Output: Carry flag set if error
* Registers changed: AX
*/
openfile:
push si
mov si,offset lodmsg
call prnstr
push ds
mov ax,es
mov ds,ax
mov si,dx # print name of file to open
call prnstr
mov ax,0x3D00 # open file using DOS
int INT_DOS
pop ds
jc 9f
mov [handle],ax # save file handle
9: pushf
call prcrlf
popf
pop si
ret
/*
*====================================================================
*
* Read one sector from input file
* Input: ES:DX - Pointer to destination buffer
* CX - Size of destination buffer
* Output: AX - Number of bytes actually read
* Carry flag set if error
* Registers changed: AX, BX, CX, DX
*/
readfile:
mov bx,[handle] # get file handle into BX
or bx,bx # check if file opened correctly
jz 8f
push ds
mov ax,es
mov ds,ax
mov ax,0x3F00
int INT_DOS # call DOS to read next sector
pop ds
jmp 9f
8: stc # return with error
9: ret
/*
*====================================================================
*
* Seek within file
* Input: EAX - File position from the beginning
* Output: Carry flag set if error
* Registers changed: EAX, BX, CX, DX
*/
seekfile:
mov bx,[handle] # get file handle into BX
or bx,bx # check if file opened correctly
jz 8f
mov dx,ax
shr eax,16 # put 32-bit file offset into CX:DX
mov cx,ax
mov ax,0x4200
int INT_DOS # call DOS to position within file
jmp 9f
8: stc
9: ret
/*
*====================================================================
*
* Close BOOTP/DHCP file
* Input: None
* Output: None
* Registers changed: AX, BX
*/
closefile:
mov bx,[handle]
or bx,bx # check that the file handle is
jz 9f # valid
mov ax,0x3E00
int INT_DOS # call DOS to close the file
mov word ptr [handle],0
9: ret
/*
*====================================================================
*
* Read-only Data area
*/
_rodata
/* Copyright message */
id1msg: .asciz "DOS MGL loader"
id2msg: .asciz VERSION
id3msg: .asciz COPYRIGHT
/* ID string for MGL runtime module */
rtid: .ascii RT_IDSTRING
rtidend:
.byte 0
/* Name of DOS ramdisk character device */
rddev: .asciz "RAMCNTR0"
/* Special BOOTP "file" names */
xmsmsg: .asciz "xms"
pxemsg: .asciz "pxe"
dflmsg: .asciz "default"
/* General runtime messages */
lodmsg: .asciz "Loading "
exemsg: .asciz "Executing MGL program"
/* Error messages */
doserr: .asciz "Invalid DOS version"
interr: .asciz "Internal memory error"
memerr: .asciz "Not enough memory for BOOTP/DHCP file"
xmserr: .asciz "Unable to read BOOTP/DHCP info from XMS"
opberr: .asciz "Unable to open BOOTP/DHCP file"
rdberr: .asciz "Unable to read from BOOTP/DHCP file"
noperr: .asciz "Unable to determine program file name"
opperr: .asciz "Unable to open program file"
skperr: .asciz "Unable to find MGL program in executable"
rdperr: .asciz "Unable to read from program file"
prgerr: .asciz "Not enough memory for MGL program"
inverr: .asciz "Invalid MGL program"
/*
*====================================================================
*
* Data area
*/
.data
/* Structure used to pass various parameters to the runtime module */
rtinfo: .word 0 # no flags set
btplen: .word 0 # size of BOOTP record
btpptr: .long 0 # far pointer to BOOTP record
.long 0 # pointer to !PXE or PXENV+ structure
/* XMS EMM structure */
xmsemm:
xmslen: .long 0 # number of bytes to move
xmssrc: .word 0 # source handle
.long 0 # source offset
.word 0 # destination handle
xmsdst: .long 0 # destination offset
/* Default BOOTP/DHCP record */
btpdef: .byte BOOTP_OP_REPLY # indicate reply packet
.byte 1 # hardware type
.byte ETH_ALEN # ethernet address length
.byte 0 # hop count
.long 0 # transaction ID
.word 0 # time since client booted
.word 0 # flags
.byte 127,0,0,1 # client IP address
.byte 127,0,0,1 # your (client) IP address
.byte 127,0,0,2 # server IP address
.byte 0,0,0,0 # gateway IP address
.fill BOOTP_CHADDR_SIZE # client hardware address
.ascii "server" # server host name
.fill BOOTP_SNAME_SIZE - 6
.fill BOOTP_FILE_SIZE # boot file name
.long BOOTP_VM_RFC1048 # options header
.byte VEND_RFC_MSGTYPE # DHCP message type
.byte 1 # length of option
.byte VEND_DHCP_ACK # ACK message type
.byte VEND_RFC_HNAME # client host name
.byte 7 # length of option
.asciz "client" # default client name
.byte VEND_RFC_END # end of vendor options
.space BOOTP_MIN_SIZE + btpdef - .
/*
*====================================================================
*
* BSS segment
*/
.bss
.global stktop # required for i386 library
.lcomm stktop,2 # pointer to top of stack
.lcomm mglptr,4 # pointer to MGL program
.lcomm pspseg,2 # DOS PSP segment
.lcomm handle,2 # BOOTP/DHCP file handle
.lcomm readseg,2 # current read segment
.lcomm readcount,2 # current read counter
.lcomm xmsadr,4 # XMS handler address
.lcomm rddata,RD_DATASIZE # DOS ramdisk data space
/*
*====================================================================
*/
.end