
; Copyright 2004-2005 Christopher Evans
; Website: http://nxdos.sourceforge.net/
;                                                                           ;
;       This program is copyrighted, but may be freely distributed          ;
;       under the terms of the Free Software Foundation's GNU General       ;
;       Public License v2 (or later.)  See the file COPYING for the         ;
;       legalities.  If you did not receive a copy of COPYING, you          ;
;       may request one from the Free Software Foundation, Inc.,            ;
;       59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.           ;
;       ABSOLUTELY NO WARRANTY -- use it at your own risk!                  ;
;                                                                           ;


              .286c
              .287

code            segment byte public
                assume  cs:code, ds:code
		org	100h
load            proc    far

start:          jmp     initialize
include         \nxdos\src\toolbox.asm
                _compile
initialize:

       call cs:main

exitptr:
       call dosexit
       int 20h      

load            endp

_stack proc far
     db 0C4h   ; word aligning the stack
               ; Internal stack
     dw 128 DUP ('?')
_stack endp

errmsg db 10,13,"failed.",10,13,'$'
failure proc far

     push cs
     pop  ds
     mov  dx,offset errmsg
     call doswrite
     mov  al, 1
     call dosexit
     ret
failure endp

prepare_env proc far
;
; Prepare an empty environment.  COMMAND.COM copies the environment of
; the parent to a new hunk of memory and throws some data in after it;
; We just make an empty environment
;
        mov  bx, 1                ; need one paragraph
        mov  ah, 48h
        int  21h
        jnc  newenv
             call failure
newenv:
        mov  es, ax               ; Zero it out
        sub  di, di
        sub  ax, ax
        mov  cx, 8
        rep  stosw
        ret
prepare_env endp

allocate_allmem proc far
; Allocate useable memory as indicated by header
;
        mov  bx, [maxalloc]     ; find out how much mem
        push bx
        mov  ah, 48h
        int  21h
        jnc  finish
        pop  ax
        cmp  bx, [minalloc]
        jb   aalfail
        push bx                 ; Allocate it
        mov  ah, 48h
        int  21h
finish:
        mov  es, ax
        pop  bx                 ; returning size of block              
        jnc  aalmem
aalfail:
             call failure
aalmem:
        ret
allocate_allmem endp

prepare_psp proc far
; Initialize the new PSP
;
; Note: this should handle most DOS .COM files.  Of course it
; makes little sense to go to all this trouble if we aren't running DOS
; programs :).   BTW this should let them run, but it may keep them from
; important information such as command lines and environments
        push ax                 ; start by zeroing it out
        mov  cx, 128
        sub  ax, ax
        sub  di, di
        rep  stosw
        pop  ax

        mov  word ptr es:[2ch], ax        ; Pointer to our environment

        mov  word ptr es:[0], 20cdh       ; null return to DOS
        mov  ax, es
        add  ax, bx
        mov  word ptr es:[2], ax          ; Next free block (usually A000)
        mov  byte ptr es:[5], 9ah         ; CALLF
;
; Now we grab stuff from the original PSP.  Usually DOS initializes
; this stuff with values out of the appropriate vectors but I got lazy...
;
        mov  si, 6                        ; vectors 21h-24h copy from old PSP
        mov  di, 6
        mov  cx, 8
        rep  movsw

        mov  ax, ds:[16h]
;
; This next field would typically be set to point at itself or
; the parent which did the spawning; I set it to the parent of
; this loader program
;
        mov  word ptr es:[16h], ax        ; Common parent
        mov  byte ptr es:[18h], 1         ; Handle table
        mov  byte ptr es:[19h], 1
        mov  byte ptr es:[1ah], 1
        mov  byte ptr es:[1bh], 0
        mov  byte ptr es:[1ch], 2
        mov  di, 1dh                     ; blank out unused entries (handles)
        mov  al, 0ffh                                                
        mov  cx, 15
        rep  stosb
        mov  word ptr es:[32h], 20        ; 20 handles available
        mov  word ptr es:[34h], 18h       ; Point at handle table
        mov  word ptr es:[36h], ES        ;
        .386
        mov  dword ptr es:[38h], -1       ; reserved
        .286c
        mov  word ptr es:[50h], 21CDh     ; dummy DOS call
        mov  byte ptr es:[52h], 0CBH
;
; The rest of this stuff has to be modified if you put in command line support
;
        mov  di, 5dh                      ; Now blank out the FCB file names
        mov  al, 20h
        mov  cx, 8
        rep  stosb
        mov  di, 6dh
        mov  cx, 8
        rep  stosb
        mov  word ptr es:[80h], 0D00h     ; Indicate no command line
        ret
prepare_psp endp

rename_arena proc far
; Reclassify the memory for the PSP and ENV as being owned by the PSP
; If we don't do this the memory may not get freed up... which typically
; results in a lockup.
;
        push es
        push es
        mov  ax, es:[2ch]
        dec  ax
        mov  es, ax
        pop  ax
        mov  es:[1], ax
        dec  ax
        mov  es, ax
        pop  ax
        mov  es:[1], ax
        mov  es, ax
        ret
rename_arena endp

load_program proc far
; Load the program into memory
;
        mov ax, [pages]           ; calculate size of prog
        dec ax
        sub cx, cx
        mov cl, ah
        mov ah, al
        mov al, 0
        add ax, ax
        adc cx, cx
        add ax, [mlength]
        adc cx, 0

        mov dx, es                       ; Point at the load address
        add dx, 10h
        mov ds, dx
        push cx
        push ax
        push ds

        mov dx, cs:[headsize]     ; finish adjusting size
        shl dx, 4
        sub ax, dx
        sbb cx, 0
;
; At this point we probably OUGHT to check that the program will fit
; in the allocated memory, just in case someone hand-built an EXE header
; and got it wrong...
;
        push cx                         ; point at program data
        push ax
        sub  cx, cx
        mov  ax, 4200h
        mov  bx, cs:[handle]
        int  21h
        pop  ax
        pop  cx
        jnc  llll
             call failure
llll:
        mov  bx, cs:[handle]
ldlp:
        or   cx, cx                        ; Less than 64K left?
        jz   last                         ; Yep, load it
        sub  ax, 8000h                    ; Otherwise we load 32K worth
        sbb  cx, 0                        ;
        push cx
        push ax
        mov  cx, ax
        sub  dx, dx
        mov  ax, 3f00h
        int  21h
        pop  ax
        pop  cx
        jnc  llll1
             call failure
llll1:
        mov  ax, ds
        add  ax, 800h
        mov  ds, ax
        jmp  ldlp
last:
        mov  cx, ax                       ; Load the remainder
        mov  ax, 3f00h
        sub  dx, dx
        int  21h
        jnc  llll2
        call failure
llll2:
        pop  ds
        pop  cx
        pop  ax
        sub  cx, 200h                     ; adjust length the way DOS does it
        sbb  ax, 0
        ret
load_program endp


load_relocs proc far
; Load the relocs
        test cs:[relocs], -1      ; get out if no relocs
        jz   lfexit
        sub  cx, cx
        mov  dx, cs:[relocofs]    ; position to start of reloc table
        mov  ax, 4200h
        int  21h
        jnc  relolp1
             call failure
relolp1:
        mov  dx,ds                ; DX = start of prog
        push cs
        pop  ds
        mov  cx, [relocs]         ; Get number of relocs
        mov  bx, [handle]
relolp:
        push cx                   ; Load a reloc
        push dx
        mov  cx, 4
        mov  dx, offset relox
        mov  ah, 3fh
        int  21h
        pop  dx
        pop  cx
        jnc  reloclp2
             call failure
reloclp2:
        add  word ptr [relox+2], dx       ; Adjust to phys address
        push ds
        lds  si, [relox]                  ; Readjust the reloc
        add  [si], dx
        pop  ds
        loop relolp                       ; continue till done
lfexit:
        mov  ds, dx
        ret
load_relocs endp

; ===========================================================================
main proc far
; ===========================================================================
;
; EXE file loader portions from EXELOAD by David Lindauer, March 25, 1997
;
;  This should run MOST EXE files, if not all of them.  The only
;  especially tricky part is marking the newly allocated memory
;  segments with an appropriate owner so that the memory will be
;  released properly when the program exits.
;
;  Note: there are some aspects of DOS compatability I haven't taken
;  care of here; for example the program name should show up right after
;  the environment with a fully qualified path; and it should show up
;  again right after the command line without the path.  I may have missed
;  other stuff as well...
;

     mov  sp, offset [_stack + 128 + 1]

     mov  bx, ProgramEnd               ; Free up unused memory
     add  bx, 15
     shr  bx, 4
     mov  ah, 4ah
     int  21h


     mov  dx, offset query             ; Get the file name
     mov  ah, 9
     int  21h
     ;
     mov  dx, offset response
     mov  ah, 10
     int  21h
     mov  bh, 0
     mov  bl, [response+1]
     mov  [bx+response+2], 0

     mov  ax, 3d00h                    ; Open the file
     mov  dx, offset response+2
     int  21h
     mov  bx, ax
     mov  [handle], ax
     jnc  readexehdr
     call failure
readexehdr:
     mov  ax, 3f00h                    ; Read the file
     mov  dx, offset header
     mov  cx, 1ch
     int  21h
     jnc  chkhdr
     call failure
chkhdr:
     mov  ax, sig                      ; Fail if not an EXE
     cmp  ax, 'ZM'
     jz   makeenv
     call failure
makeenv:
     call prepare_env                ; Memory initialize
     push es
     call allocate_allmem
     pop  ax
     call prepare_psp
     call rename_arena               ; rename the arena entries
     call load_program                       ; load prog
     push cx
     push ax
     push es
     mov  ax,ds                       ; adjust header SS and CS
     add  cs:[dispss], ax
     add  cs:[dispcs], ax
     call load_relocs
     mov  ax, 3e00h                    ; close the file
     mov  bx, cs:[handle]
     int  21h
     jnc  stage2
     call failure
stage2:
     mov  es, cs:[2ch]                 ; free our environment
     mov  ah, 49h
     int  21h
     push cs                         ; Free our program seg
     pop  es
     mov  ah,49h
     int  21h
; Inform DOS that the active PSP has changed.
     pop  es
     mov  bx,es                       ; Inform DOS about the new PSP
     mov  ds,bx
     mov  ah,50h
     int  21h
     pop  bx
     pop  cx
          call debugregisters
     cli                             ; Set stack pointer to new process
     mov  ss, cs:[dispss]
     mov  sp, cs:[basesp]
     sti
     jmp  dword ptr cs:[baseip]      ; branch to process
     ret
main endp

relox    dd ?
handle   dw ?
query    db 10, 13, 'Load: ','$'
response db 40,0,40 DUP (?)

; Header struct.  You need to read 1ch bytes to get this info
header:
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...
HEADERSTRUCT_SIZE EQU $ - header

eop:

code            ends

ProgramEnd      EQU $-01h

		end	start
