cseg    segment para public 'code'
org     100h

; This program is used to load an EXE file faster than DOS.
; To use it, enter 'RUN prog', where prog is a valid EXE program.
; The file may be any size, but for programs less than 64K,
; making a COM file using EXETOCOM.BIN as a header works faster.

recct   equ 504                 ; max recs per read

run     proc far
        assume cs:cseg,ds:cseg,ss:nothing,es:nothing

        mov sp,offset estak     ; point to internal stack
        call p100               ; initialize
        call p200               ; open file
        jmp p300                ; copy this prog to hi memory
p010:   call p400               ; read exe file to lo memory
        jmp p600                ; relocate and execute prog

p050:   mov ah,9                ; print error message
        int 21h
        mov ax,oseg
        push ax
        xor ax,ax
        push ax
        ret 0                   ; terminate

p100    proc near               ; initialize
        mov dx,offset copyr
        mov ah,9                ; print copyright message
        int 21h

        mov ax,ds
        mov oseg,ax             ; save original seg
        add ax,10h
        mov nseg,ax             ; save new prog seg
        mov si,2
        mov bx,[si]             ; get top of memory from psp
        mov topmem,bx

        mov ax,offset endprog
        add ax,15
        and ax,0fff0h
        mov eprog,ax            ; point to end of prog
        mov cl,4
        shr ax,cl
        mov bx,ds
        add ax,bx
        mov dtads,ax            ; point to start dta

        mov si,5ch              ; point to 1st fcb
        mov di,offset fcb       ; & work fcb
        mov cx,37
        rep movsb               ; copy it

        mov di,offset fcb+9     ; make sure extension is 'EXE'
        mov si,offset exe
        mov cx,3
        repnz movsb             ; copy 'EXE' to extension

                                ; juggle fcbs in psp
        mov si,81h              ; point to command line in psp
        mov di,5ch              ; point to first fcb in psp
        mov ax,2901h
        int 21h                 ; parse first file name (prog to execute)

                                ; rearrange command line
        mov di,81h              ; point to start of command line
        mov bx,80h
        mov ch,0
        mov cl,[bx]             ; get length of command line
        add cx,di
        sub cx,si               ; new command line length
        mov [bx],cl             ; save it
        inc cl                  ; copy carriage return too!
        repnz movsb             ; move command line down

        mov si,offset fcbfill   ; fill fcbs with blanks
        mov di,offset 5ch
        mov cx,16
        rep movsb               ; clear first fcb
        mov si,offset fcbfill
        mov di,offset 6ch
        mov cx,16
        rep movsb               ; clear second fcb
        mov si,81h              ; get command line back
        mov di,5ch              ; point to first fcb
        mov ax,2901h
        mov bx,offset drives
        int 21h                 ; parse first parm
        cmp al,1                ; error?
        jbe p110                ; no
        mov [bx],al             ; set error in lo byte

p110:   mov di,6ch
        mov ax,2901h
        int 21h                 ; parse second parm
        cmp al,1                ; error?
        jbe p120                ; no
        mov [bx+1],al           ; set error in hi byte

p120:   ret
p100    endp

p200    proc near               ; open file
        mov dx,offset fcb
        mov ah,0fh
        int 21h
        and al,0ffh             ; error?
        jz p210                 ; no
        mov dx,offset errmsg1
        jmp p050

p210:   mov si,offset fcb+16    ; get file size
        mov ax,[si]             ; lower part
        mov cl,7
        shr ax,cl               ; divide by 128
        mov bx,ax               ; hold in bx
        mov ax,[si+2]           ; upper part of file size
        mov cl,9
        sal ax,cl               ; divide by 128
        add ax,bx               ; total file size divided by 128
        mov norecs,ax           ; save as norecs

        mov ax,128
        mov si,offset fcb+14    ; set recsize = 128
        mov [si],ax
        mov al,0                ; zero 5 bytes in fcb
        mov di,offset fcb+32
        mov cx,5
        repnz stosb             ; clear fcb bytes 32 - 36

        mov ax,norecs
        mov cl,3
        sal ax,cl               ; # of pghs for prog
        add ax,dtads            ; top of prog (pghs)
        mov bx,topmem           ; top of memory
        cmp bx,ax               ; is there room?
        jae p220                ; yes
        mov dx,offset errmsg4
        jmp p050

p220:   ret
p200    endp

p300:                           ; read exe header
        mov cx,4
        call p500               ; read 4 recs
        mov si,eprog
        mov ax,[si]             ; get EXE signature
        cmp ax,5a4dh            ; valid exe file?
        jz p310                 ; yes
        mov dx,offset errmsg2
        jmp p050

p310:                           ; check stack placement
        mov ax,[si+10h]         ; get value for sp
        mov cl,4
        shr ax,cl               ; convert to pghs
        add ax,nseg             ; add base seg
        add ax,[si+0eh]         ; and sp offset
        mov bx,topmem           ; get top of memory
        cmp bx,ax               ; is there room for the stack?
        jae p315                ; yes
        mov dx,offset errmsg4
        jmp p050

p315:   mov ax,[si+8]           ; get header size (pghs)
        mov cl,3
        shr ax,cl               ; divide by 8 to get 128-byte recs
        sub ax,4                ; already read
        mov cx,ax
        jcxz p320               ; no more header
        call p500               ; read rest of header

p320:   mov ax,norecs           ; recs left
        mov cl,3
        sal ax,cl               ; convert to pghs
        add ax,nseg             ; plus start point gives copy destination
        mov bx,dtads            ; end of header
        cmp ax,bx               ; is start point above header?
        jae p325                ; yes
        mov ax,bx               ; no - change copy destination

p325:   mov es,ax               ; copy prog here
        sub bx,oseg             ; minus psp gives pghs to copy
        mov cl,3
        sal bx,cl               ; words to copy
        mov cx,bx
        xor di,di
        xor si,si
        rep movsw               ; copy it

        mov di,offset p330
        push es
        push di
        ret 0                   ; jump to new copy

p330:   push es
        pop ds                  ; set ds=es=new cs
        mov ax,es
        mov ss,ax               ; set ss=es
        jmp p010

p400    proc near               ; read file
        mov ax,nseg
        mov dtads,ax
p410:   mov cx,norecs           ; # of recs left
        sub cx,recct            ; 63K worth?
        ja p420                 ; yes
        add cx,recct            ; no - read remainder
        jmp p430

p420:   mov cx,recct            ; read 63K
p430:   call p500               ; read file

p440:   mov cx,norecs           ; any more to read?
        jcxz p450               ; no
        jmp p410                ; read next block

p450:   ret
p400    endp

p500    proc near               ; read file - cx contains recs to read
        push ds
        mov bx,dtads
        mov ds,bx               ; new ds
        mov dx,0
        mov ah,1ah
        int 21h                 ; set dta
        pop ds
        mov dx,offset fcb
        mov ah,27h
        int 21h                 ; random block read
        and al,0ffh             ; error?
        jz p510                 ; no
        mov dx,offset errmsg3
        jmp p050

p510:   mov ax,cx               ; adjust dta for next read
        sub norecs,ax
        mov cl,3
        sal ax,cl               ; 8 pghs/rec
        add dtads,ax
        ret
p500 endp

p600:   mov ax,nseg             ; relocate and execute it
        mov si,eprog
        mov bx,si
        add bx,[si+18h]         ; first relocation item
        mov cx,[si+6]           ; # of items
        jcxz p620               ; none
p610:   mov dx,[bx+2]           ; get code seg
        add dx,ax               ; add start seg
        mov es,dx               ; in es
        mov di,[bx]             ; set instruction in es
        add es:[di],ax          ; add start seg
        add bx,4                ; do another
        loop p610               ; done?

p620:                           ; pass control to exe prog
        mov ax,nseg             ; new prog seg
        add ax,[si+0eh]         ; plus ss offset gives new ss
        cli
        mov ss,ax
        mov sp,[si+10h]
        sti

        mov ax,nseg             ; new prog seg
        add ax,[si+16h]         ; plus cs offset gives new cs
        mov bx,[si+14h]         ; new ip
        push ax
        push bx
        mov bx,drives           ; get drive status

        mov ax,oseg             ; set up ds & es
        mov ds,ax
        mov es,ax

        mov ah,1ah              ; reset dta
        mov dx,80h
        int 21h
        mov ax,bx               ; drive status in ax

        ret 0                   ; far return to exe prog

copyr   db 'RUN - Copyright 1983 Data Base Decisions',10,13,'$'
errmsg1 db 'Program not found',7,10,13,'$'
errmsg2 db 'Header not found ',7,10,13,'$'
errmsg3 db 'Unable to load program',7,10,13,'$'
errmsg4 db 'Not enough memory',7,10,13,'$'

oseg    dw 0                    ; original data seg
nseg    dw 0                    ; start of new prog
topmem  dw 0                    ; top of memory
norecs  dw 0                    ; # of 128 byte recs in file
dtads   dw 0                    ; dta data seg
drives  dw 0                    ; drive status for fcbs
exe     db 'EXE'                ; file extension
fcb     db 37 dup(0)            ; work fcb
eprog   dw 0                    ; end of this prog
stak    db 16 dup('stack   ')   ; temp stack
estak   db 0
fcbfill db 0,11 dup(' '),4 dup(0)       ; fcb filler

endprog equ this word

run     endp
cseg    ends
end     run
