;
; GRDB
;
; Copyright(c) LADsoft
;
; David Lindauer, camille@bluegrass.net
;
; LOADER.ASM
;
; Function : COM/EXE file loader
;


	.model small
	.386
	public	MakeEmptyProg, userbasepsp, filelen,loadfile,loadcommand,exeflag
	public  ParseProgName,exestats,LoadProgram
	public	lastcs,lastip,lastexe

include eenv.inc
include emtrap.inc
include edump.inc
include edos.inc
include ememory.inc
include eprints.inc
include einput.inc

headstruct struc
sig	dw	?  	; EXE signature- we exit if not 'MZ'
mlength	dw	?	; length modulo 512
pages	dw	?	; length in pages, rounded UP
relocs	dw	?	; Number of relocation items
headsize dw	?	; Size of header in paragraphs
			; We will later assume this is less than 1000h
			; paragraphs but you probably should fix that
minalloc dw	?	; Minimum memory needed
maxalloc dw	?	; Maximum memory desired
dispss	dw	?	; starting stack
basesp	dw	?	;
checksum dw	?   	; We'll ignore the checksum.  Dos ignores it too
			; if it is zero
baseip dw	?       ; Starting CS:IP
dispcs	dw	?
relocofs dw	?	; Offset to relocation table
overlay dw	?	; I never saw a program with the overlay field set
			; so I don't know what happens here...
headstruct ends
	.data
loadfile	db	80 DUP (?)	;file name
loadcommand	db	128 DUP (?)	;associated command
filelen	dd	0			;length of file
handle	dw	0			;IO handle
exeflag	db	0			;is it EXE loaded as EXE
header	headstruct <>			;EXE file header
userbasepsp dw	0			;their PSP
relox	dd	?			;current relocation value
lastexe db	0			;was last an EXE file
lastip	dd	0			;last CS:IP from header
lastcs	dw	0

	.code
;
; clear FCBs in PSP
;
InitFCB	PROC
	push	es
	mov	es,[userbasepsp]
	sub	al,al
	stosb
	mov	cx,11
	mov	al,20h
	rep	stosb
	pop	es
	ret
InitFCB	ENDP
;
; create an empty program area
;
MakeEmptyProg PROC
	mov	[filelen],0
	mov	[exeflag],0
	push	si		; refresh env
	call	CopyEnv
	pop	si
	jc	nomem
	mov	bx,-1		; minimum emem needed
	mov	ah,48h
	int	21h
	cmp	al,8
	jnz	nomem
	cmp	bx,200h
	jc	nomem	
	push	bx      	; space for their PSP
	mov	ah,48h
	int	21h
	mov	[userbasepsp],ax
	mov	[userpsp],ax
	mov	dx,ax		; create it
	mov	ah,26h
	int	21h
	mov	fs,[psp]
	mov	ax,fs:[2]
	mov	fs,[userbasepsp]
	mov	fs:[2],ax
	push	es		; now create a dummy file table
	mov	es,[userbasepsp]
	mov	di,18h
	mov	al,1
	stosb
	stosb
	stosb
	dec	al
	stosb
	inc	al
	inc	al
	stosb
	mov	al,0ffh
	mov	cx,10h
	rep	stosb
	pop	es
	call	SetDebugPSP	; put us back at ours now
	pop	ax
	cmp	ax,1000h        ;initial SP
	mov	bx,0ffeeh
	jnc	meok
	movzx	ebx,ax
	shl	bx,4
	sub	bx,18
meok:
	mov	[RegdumpEIP],0	; init all regs to defaults
	mov	[RegdumpEAX],0
	mov	[RegdumpEBX],0
	mov	[RegdumpECX],0
	mov	[RegdumpEDX],0
	mov	[RegdumpESP],ebx
	mov	[RegdumpEBP],0
	mov	[RegdumpESI],0
	mov	[RegdumpEDI],0
	mov	ax,[userbasepsp]
	mov	[RegdumpCS],ax
	mov	[RegdumpDS],ax
	mov	[RegdumpES],ax
	mov	[RegdumpFS],ax
	mov	[RegdumpGS],ax
	mov	[RegdumpSS],ax
	mov	[indexseg],ax
	mov	[RegdumpEIP],100h
	mov	[index],100h
	mov	word ptr [RegdumpFLAGS],202h ; flags
	mov	word ptr fs:[bx],0
	mov	fs,[userbasepsp]	; set up env seg
	mov	bx,[TgtPgmEnvSeg]
	mov	fs:[2ch],bx
	mov	ax,[userbasepsp]	; tag the arena entries
	mov	bx,ax
	dec	bx
	call	tagarena
	mov	ax,[userbasepsp]
	mov	bx,[TgtPgmEnvSeg]
	dec	bx
	call	tagarena
	mov	di,5ch			; set up FCBs
	call	InitFCB
	mov	di,6ch
	call	InitFCB
	clc
nomem:
	ret
MakeEmptyProg ENDP
;
; throw a file name into an FCB, just for the old days :)
;
tofcb	PROC
	call	WadeSpace
	sub	al,al
	cmp	byte ptr [si+1],':'
	jnz	nodrive
	lodsw
	sub	al,'a'-1
nodrive:
	stosb
	push	si
fxlp:
	lodsb
	cmp	al,':'
	jz	tfcberr
	cmp	al,'\'
	jz	tfcberr
	cmp	al,'/'
	jz	tfcberr
	cmp	al,','
	jz	fxdn
	cmp	al,' '
	jz	fxdn
	or	al,al
	jz	fxdn
	jnz	fxlp
fxdn:
	xchg	[esp],si
	mov	cx,8
mvlp:
	lodsb
	cmp	al,'.'
	jz	dodot
	cmp	si,[esp]
	jnc	tfcbx
	and	al,0dfh
	stosb
	loop	mvlp
wade1:
	lodsb
	cmp	al,'.'
	jz	dodot
	cmp	si,[esp]
	jnc	tfcbx
	jmp	wade1
dodot:
	mov	cl,3
	sub	di,5
	and	di,NOT 7
	add	di,13
ddl:
	lodsb
	cmp	si,[esp]
	jnc	tfcbx
	and	al,0dfh
	stosb
	loop	ddl
tfcbx:
	pop	si
	test	byte ptr [si],0ffh
	jz	tfcberr
	clc
	ret
tfcberr:
	stc
	ret
	
tofcb	ENDP
;
; put a command line in user psp
;
SetUserCommand	PROC
	push	es
	mov	es,[userbasepsp]
	mov	al,1
	mov	cx,80h
	mov	di,80h
	rep	stosb
	mov	di,81h
	mov	si,offset loadcommand
	mov	byte ptr es:[80h],0
	mov	cx,126
cml_lp:
	lodsb
	or	al,al
	jz	cml_dn
	stosb
	
	inc	byte ptr es:[80h]
	loop	cml_lp
cml_dn:
	mov	al,0dh
	stosb
	mov	si,offset loadcommand
	mov	di,05ch
	call	tofcb
	jc	comdone
	mov	di,06ch
	call	tofcb
comdone:
	pop	es
	ret
	
SetUserCommand	ENDP
;
; parse the program name/command line as set in a W/L command
;
ParseProgName	PROC
	lodsb
	cmp	al,' '
	jz	ParseProgName
	dec	si
	mov	es:[loadfile],0
	mov	es:[loadcommand],0
	mov	di,offset loadfile
ppnlp:
	lodsb
	cmp	al,' '
	jz	ppngot
	cmp	al,13
	jz	ppcgot
	or	al,al
	jz	ppngot
	stosb
	jmp	ppnlp
ppngot:
	mov	al,0
	stosb
	dec	si
	mov	di,offset loadcommand
ppclp:
	lodsb
	cmp	al,13
	jz	ppcgot
	or	al,al
	jz	ppcgot
	stosb
	jmp	ppclp
ppcgot:
	dec	si
	mov	al,0
	stosb
	push	ds
	push	es
	pop	ds
	push	si
	mov	si,offset loadfile
	call	QualifyName
	pop	si
	pop	ds
	ret	
ParseProgName	ENDP
;
; main program loader
;
LoadProgram	PROC
	mov	[lastexe],0		; assume com
	push	ax			; unload prog
	call	UnLoadProgram
	pop	ax
	or	ax,ax
	jnz	lcm
	call	LoadExe			; EXE load
	jmp	lpfin
lcm:
	call	LoadCom			; COM load
lpfin:
	jc	lpnomod
	mov	word ptr [RegdumpECX],ax	; set stats
	mov	word ptr [RegdumpECX+2],0
	shr	eax,16
	mov	[RegdumpEBX],eax
	call	SetUserCommand
	mov	si,offset loadfile	; tag arean with prog name
	mov	ax,[userbasepsp]
	mov	bx,ax
	dec	bx
	call	tagarena
	mov	ax,[userbasepsp]
	mov	bx,[TgtPgmEnvSeg]
	dec	bx
	call	tagarena
	call	SetEnvName
	clc
lpnomod:
	ret
LoadProgram	ENDP
;
; com file load, just grab the data
;
LoadCom	PROC
	mov ax,3d00h			; Open the file
	mov dx,offset loadfile
	int 21h
	mov bx,ax
	jc failure
LoadCom2	PROC
	mov	ax,4200h
	sub	cx,cx
	sub	dx,dx
	int	21h
	jc	failure
	mov	si,[userbasepsp]
	
	push	ds
	add	si,10h
rdlp:
	mov	ds,si
	mov ax,3f00h			; Read the file
	mov cx,8000h
	sub	dx,dx
	int 21h
	jc failure2
	add	si,800H
	movzx	eax,ax
	add	es:[filelen],eax
	cmp	ax,8000H
	jz	rdlp
	clc

failure2 PROC
	pop	ds
failure PROC
	pushf
	mov ax,3e00h			; close the file
	int 21h
	popf
	jnc	loadok
	mov	[filelen],0
	mov	[exeflag],0
loadok:
	mov	eax,[filelen]
	ret
failure ENDP
failure2 ENDP
LoadCom2 ENDP
LoadCom	ENDP
;
; exe file load
;
LoadEXE	PROC
	mov	si,offset loadfile
ls_lp1:
	lodsb
	or	al,al
	jz	LoadCom
	cmp	al,'.'
	jnz	ls_lp1
	mov	eax,[si]
	and	eax,0ffffffh
	cmp	eax,"exe"
	jnz	LoadCom
;
; Open the file
;
	mov ax,3d00h			; Open the file
	mov dx,offset loadfile
	int 21h
	mov bx,ax
	mov [handle],ax
	jc failure
;
; Read the basic part of the header
;
	mov ax,3f00h			; Read the file
	mov dx,offset header
	mov cx,1ch
	int 21h
	jc failure

	mov ax,[header.sig]		; Fail if not an EXE
	cmp ax,"ZM"
	jnz LoadCom2
	mov	[exeflag],1
;
; Load exe file
	movzx ecx,[header.pages]		; calculate size of prog
	dec     cx
	shl	ecx,9
	movzx  	eax,[header.mlength]
	add	ecx,eax
	movzx	eax,[header.headsize]
	shl	eax,4
	sub	ecx,eax
	jc	failure
	mov	[filelen],ecx
	mov	ax,[userbasepsp]
	mov	fs,ax
	add	ax,[header.minalloc]
	add	ax,10h
	cmp	fs:[2],ax
	jc	failure

	mov	ax,fs
	dec	ax
	mov	fs,ax
	mov	ax,fs:[3]
	cmp	ax,[header.maxalloc]
	jbe	nomemresize
	mov	bx,[header.maxalloc]
	push	es
	mov	es,[userbasepsp]
	mov	ah,4ah
	int	21h
	pop	es
nomemresize:
	mov	si,[userbasepsp]
	add	si,10h
	
	mov	dx,[header.headsize]
	shl	dx,4
	push ecx				; point at program data
	sub cx,cx
	mov ax,4200h
	mov bx,cs:[handle]
	int 21h
	pop ecx
	jc failure

	push	ds
redlp:
	push	ecx
	mov	ds,si
	mov ax,3f00h			; Read the file
	mov cx,8000h
	sub	dx,dx
	int 21h
	jc failure2
	pop	ecx
	add	si,800h
	sub	ecx,8000h
	jnc	redlp
	pop	ds



	test cs:[header.relocs],-1   	; get out if no relocs
	jz lfexit
	mov	si,[userbasepsp]
	add	si,10h
	sub cx,cx
	mov dx,cs:[header.relocofs]	; position to start of reloc table
	mov ax,4200h
	int 21h
	jc failure

	mov cx,[header.relocs]		; Get number of relocs
	mov bx,[handle]
relolp:
	push cx				; Load a reloc
	mov cx,4
	mov dx, offset relox
	mov ah,3fh
	int 21h
	pop cx
	jc failure

	add word ptr [relox+2],si	; Adjust to phys address
	lfs di,[relox]			; Readjust the reloc
	add fs:[di],si
	loop relolp			; continue till done
lfexit:
	mov	ax,[userbasepsp]
	add	ax,10h
	add	[header.dispcs],ax
	add	[header.dispss],ax
	movzx	eax,[header.baseip]
	mov	[RegdumpEIP],eax
	movzx	eax,[header.basesp]
	mov	[RegdumpESP],eax
	mov	ax,[header.dispcs]
	mov	[RegdumpCS],ax
	mov	ax,[header.dispss]
	mov	[RegdumpSS],ax
	mov	ah,3eh
	int	21h

	mov	[lastexe],1
	mov	eax,[RegdumpEIP]
	mov	[lastip],eax
	mov	ax,[RegdumpCS]
	mov	[lastcs],ax
	mov	eax,[filelen]
	movzx	ebx,[header.headsize]
	shl	ebx,4
	add	eax,ebx
	sub	eax,512
	clc
	ret
LoadEXE	ENDP
;
; used by status screen to display EXE status
;
ExeStats PROC
	test	[filelen],-1
	jz	noexe
	PRINT_MESSAGE	<13,10,"File length: ">
	mov	eax,[filelen]
	call	PrintDWord
	test    [exeflag],0ffh
	jz	noexe
	mov	cx,[userbasepsp]
	add	cx,10h
	PRINT_MESSAGE	<13,10,"CS:IP = ">
	mov	ax,[header.dispcs]
	call	printword
	mov	dl,':'
	call	PutChar
	mov	ax,[header.baseip]
	call	printword
	PRINT_MESSAGE	<13,10,"SS:SP = ">
	mov	ax,[header.dispss]
	call	printword
	mov	dl,':'
	call	PutChar
	mov	ax,[header.basesp]
	call	printword
	PRINT_MESSAGE	<13,10,"minalloc: ">
	mov	ax,[header.minalloc]
	call	printword
	PRINT_MESSAGE	<" maxalloc: ">
	mov	ax,[header.maxalloc]
	call	printword
	PRINT_MESSAGE	<13,10,"relocs: ">
	mov	ax,[header.relocs]
	call	printword

noexe:
	ret
ExeStats ENDP
	end