home *** CD-ROM | disk | FTP | other *** search
- page 60,132
- title Start_Up3 -- Complete Start Up Code for .EXE Programs
- name START_UP3
-
- comment ÷
- Start_UP3 V1.07
- --------------------------------------------------------------------------
- NAME
- startup3 Complete start up code for .com and .exe programs
-
- SYNOPSIS
- extern Start_Up3
- or
- extern Start_Up3S
-
- See Programming Notes below for difference.
-
- DESCRIPTION
- This is the startup code for all .exe and .com assembly language
- programs. Just use the SYNOPSIS above in the main function to
- include the startup code in the .exe file from a .lib. For .com
- assembly language programs, this source code must be the first
- assembled so that this code is the linked first.
-
- This procedure parses the command line into argc and *argv[] similar
- to C. Argv[0] is the process name as in C.
-
- The environment is parced and *envp[] is made available to main.
-
- This procedure performs the following functions in addition to above
- - Initializes the following global variables:
- -- PSP, segment address of PSP
- -- DGRP, segment address of DGROUP
- -- ENVIRON, segment address of passed copy of the ENVIRONMENT
- -- NEXTPARA, segment address of next memory paragraph
- -- STACK_BOTTOM, offset of stack bottom in DGROUP
- -- OSMAJOR, integer part of OS system
- -- OSMINOR, decimal part of OS system
- -- ENV_BRK, Offset to the end of the environmental strings
- -- ENV_STR_LEN, Length of environmental strings
- - Initializes DS and ES segment registers to DGROUP
- - Shrinks memory down to size of program by releasing all memory
- above program
- - If moving the environmental strings onto the stack, will case
- DGROUP to exceed 64K bytes, abort with error message
-
- RETURNS
- If main returns to startup code. The program terminiates with the
- return code in AL.
-
- PROGRAMING NOTES
- Requires Microsoft MASM V6.11d.
-
- Written to link with any memory model. If using the Tiny Memory
- Model, this source code must the first linked because execution must
- begin in this source code.
-
- Use MEMMOD to specify the memory model.
- /dMEMMOD=TINY|SMALL|COMPACT|MEDIUM|LARGE|HUGE
-
- Written using the FORTRAN/PASCAL/BASIC calling convention so passed
- parameters are pushed in the order of appearance in the proc
- declaration.
-
- This procedure can pass argc and argv on stack two ways. The proc
- declaration in the MAIN procedure will differ depending upon the
- choice.
-
- The default requires the following proc declaration:
-
- MAIN proc ARGC:word, ARGV:ptr
-
- If SIMPLE is defined, an alternate proc declaration is required:
-
- MAIN proc ARGV:ptr, ARGC:word
-
- Here, ARGV is not a pointer to an array of pointers but the location
- of the 1st pointer.
-
- In Huge memory model, the pointers passed in ARGV are long pointers
- vice huge pointers.
-
- MEMORY REQUIREMENTS (using 286 instructions set)
- (in bytes) Tiny Small Medium Compact Large Huge
- Code: 366 395 397 403 405 405
- Code (SIMPLE): 365 394 396 401 403 403
- _Data: 0 0 0 0 0 0
- Const: 60 60 60 60 60 60
- _BBS: 14 14 14 14 14 14
- Stack: ? ? ? ? ? ?
-
- Note: The 14 bytes in _BSS are from sudata.asm and sudata3.asm
- which is used so that all startup code can reside in the same
- .lib file.
-
- The first code size is for when SIMPLE not defined. The Code
- (SIMPLE) size is for when SIMPLE is defined.
-
- Stack usage is determined by the size of and the number of arguments
- in the command line.
-
- CAUTION
- Startup3 defines a 2K byte stack. This should be enough for programs
- that make moderate use of the stack. If automatic variables are used
- extensively, more stack space should be defined in the main module
- unless using the tiny memory model. In this case, the startup code
- must be the first souce file in the build, change the size of the
- STACK_SIZE define.
-
- Linker option, NO IGNORE CASE, is not used so that the linker will
- handle the __end reference properly.
-
- EXTERNAL LIBRARIES
- sudata alib?
- sudata3 alib?
-
- EXTERNAL PROCEDURES
- main user defined
-
- INTERUPTS CALLED
- Int 21h 30h - Get Version Number
- Int 21h 4ah - Set Memory Block Size
- Int 21h 4ch - Terminate program with return code
-
- GLOBAL NAMES
- Startup_3 or Startup_3S
-
- AUTHOR
- Raymond Moon - 7 Sep 87
-
- Copyright (c) 1987, 1988, 1994, 1995, 1996 - MoonWare
- ALL RIGHTS RESERVED
-
- VERSION
- Version - Date - Remarks
- 1.00 - 7 Sep 87 - Original
- 1.01 - 13 Feb 88 - Updated to program name as argv[0]
- 1.02 - 13 Aug 88 - Updated to MASM 5.1
- 1.03 - 10 Oct 88 - Added STACK_BOTTOM & ZZ_PRGM_TOP segment
- to determine end of data.
- 1.04 - 30 Oct 94 - Moved ZZ_PRGM_TOP segment position in file.
- - Removed increment in program size in paras
- as ZZ_PRGM_TOP is aligned on a para.
- 1.05 - 20 Apr 95 - Added TINY Model capability
- - Converted to .dosseg using __end vice
- ZZ_PRGM_TOP for end of data
- 1.06 - 15 Dec 95 - Removed checking for DOS 1.X and earlier
- for adding ".asm" if DOS is earlier than
- 3.0.
- 1.06 - 29 Dec 96 - Optimized the .com coding
- ==========================================================================
- ÷ Commend End
-
- ;-----------------------------
- ; A Make the small memory model the default
-
- ifndef memmod
- memmod equ <small>
- endif
-
- ;----------------------------
- ; B Include the processor, memory model, associate ES register with
- ; DGROUP, and specify DOS segment order
-
- include procesor.inc
- % .MODEL memmod,FORTRAN
- assume es:DGROUP
- .dosseg
-
- ;----------------------------
- ; C Required includes
-
- include startup.inc
-
- ;----------------------------
- ; D Define any required equates. Change this value if a larger
- ; stack is needed in the Tiny memory model
-
- STACK_SIZE equ 2048
- STDERR equ 2
-
- ;=========================================================================
- ; DATA
- ;=========================================================================
- ; E Define storage for the various global and system variables and
- ; constants.
-
- .CONST
-
- DG_OVRFL db 'DGROUP exceeds 64K bytes'
- DG_OVRFL_LEN equ $ - DG_OVRFL
- LIM_MEM db 'Insufficient memory to expand DGROUP'
- LIM_MEM_LEN equ $ - LIM_MEM
-
- .DATA?
-
- if @Model NE 1
- .STACK STACK_SIZE ; Define a nominal stack
- endif
-
- @CurSeg ends
-
- ;----------------------------
- ; F Define __end which is defined when using .dosseg directive.
- ; Remember not to use the NO IGNORE CASE linker option.
-
- externdef __end:byte
-
- ;----------------------------
- ; G Define segment for addressing information in the PSP.
-
- PSP_SEG segment at 00h
-
- org 2h
- NEXTPARA_PTR dw ? ; Segment address of next memory para
-
- org 2ch
- ENVIRON_PTR dw ? ; Segment address of Environment
-
- org 80h
- PARM_LEN db ? ; Number of bytes in Command Line tail
- PARMS db 127 dup(?) ; Start of Command Line tail
- PSP_SEG ends
-
- ;=========================================================================
- ; CODE
- ;=========================================================================
- ; H Put the called main procedure in the proper relationship to the
- ; startup code.
-
- if @CodeSize
- extrn Main:far
- .CODE
- else
- .CODE
- extrn Main:near
- endif
-
- ;-----------------------------
- ; I Include org statement if tiny model
-
- if @Model EQ 1
- org 100h
- endif
-
- ;-----------------------------
- ; J Start the Start_Up2 code. Make it a far procedure so error return
- ; will work. If SIMPLE is defined, redefined Start_Up2 as Start_Up2S.
-
- ifdef SIMPLE
- Start_Up3 equ <Start_Up3S>
- endif
-
- % Start_Up3 proc far
-
- ;-----------------------------
- ; K First, initialize global variables. Set DS to DGROUP.
- ; Skip DGROUP code for .com(Tiny) memory models
-
- if @Model NE 1
-
- mov ax, DGROUP ; Get seg address of DGROUP
- mov ds, ax ; Initialize DS segment register
- mov DGRP, ax ; Initialize DGRP
- assume es:PSP_SEG
- else
- mov DGRP, ds ; Initialize DGRP
- endif
- mov PSP, es ; Initialize PSP
- if @Model eq 1
- assume ds:PSP_SEG
- endif
- mov bx, ENVIRON_PTR ; Get segment address of environment
- mov cx, NEXTPARA_PTR ; Get segment address of next memory
- if @Model eq 1
- assume ds:DGROUP
- endif
- mov ENVIRON, bx ; Initialize ENVIRON
- mov NEXTPARA, cx ; Initialize NEXTPARA
- mov ah, 30h ; Get DOS version number
- int 21h ; Call DOS
- mov OSMAJOR, al ; Save major version number
- mov OSMINOR, ah ; Save minor version number
-
- ;----------------------------
- ; L Combine the stack into DGROUP so that it is addressable from DGROUP.
- ; Initialize STACK_BOTTOM.
-
- lea bx, __end ; Get pointer to end of data
-
- if @Model eq 1 ; Ensure that Stack bottom at paragraph
- add bx, 15 ; Add to ensure next para if necessary
- and bx, 0fff0h ; Truncate to a paragraph boundary
- endif
-
- mov STACK_BOTTOM, bx ; Save it
-
- if @Model eq 1 ; Get stack size
- add bx, STACK_SIZE ; DI = stack size
- else
- add bx, sp ; DI = stack size
- endif
-
- mov dx, ds ; Get DGROUP segment address
- mov ss, dx ; Reset SS
- mov sp, bx ; Reset SP
-
- ;-----------------------------
- ; M Find the end of the environmental strings. Save the length for
- ; later. Ensure that length is multiple of word length. Since
- ; the string is double null terminated, the string length saved will
- ; end with one or two nulls.
-
- mov es, ENVIRON ; ES => ENVIRON seg
- xor di, di ; ES:DI => 1st char in environment
- mov cx, -1 ; Make cx arbitarily large
- mov ax, di ; AL = null
- SU1: repne scasb ; Find null
- cmp al, es:[di] ; Is next byte a null also?
- jne SU1 ; No, continue search
- inc di ; Pad string for next op
- and di, 0fffeh ; Force to an even boundary
- mov ENV_STR_LEN, di ; Save it
- add di, 15 ; Add to ensure next para if necessary
- and di, 0fff0h ; Truncate to a paragraph boundary
-
- ;----------------------------
- ; N Release all memory above program. Include room for the environmental
- ; variables by adjusting SP. Check to see if adding environmental
- ; strings increases DGROUP size to greater than 64K. If so, tell user
- ; and terminate with error return
-
- mov es, PSP ; ES => PSP
- add bx, di ; Add room for environmental variables
- jnc SU2 ; No overflow, DGROUP < 64K
- lea dx, DG_OVRFL ; DX => message
- mov cx, sizeof DG_OVRFL ; CX => # bytes
- mov bx, STDERR ; To console
- mov ah, 40h ; DOS write to file/device
- int 21h ; Call DOS
- mov al, 80h ; Error return code
- jmp SU14
- SU2: mov sp, bx ; Adjust SP
- shr bx, 4 ; Convert to #para by dividing by 16
- if @Model ne 1
- mov ax, ds ; AX => DGROUP
- sub ax, PSP ; AX = # para for code
- add bx, ax ; BX = # para in program
- endif
- mov ah, 4ah ; Request DOS set block
- int 21h ; Call DOS
- jnc SU3 ; No error
- lea dx, LIM_MEM ; Tell user, DX => message
- mov cx, sizeof LIM_MEM ; Message length
- mov bx, STDERR ; To console
- mov ah, 40h ; DOS write to file/device
- int 21h ; Call DOS
- mov al, 81h ; Error return code
- jmp SU14 ; Go to the exit code
-
- ;----------------------------
- ; O Transfer the environmental strings onto the stack. Ensure that the
- ; last string ends with a null by pushing a null word onto the stack.
-
- SU3: mov cx, ENV_STR_LEN ; CX = count
- mov di, sp ; DI => top of stack
- sub di, cx ; DI => new SP
- mov sp, di ; Make room for environmental strings
- assume es:DGROUP
- if @Model NE 1
- mov es, DGRP ; ES:DI => top of stack
- endif
- xor si, si ; SI => start of environmental strings
- mov ds, ENVIRON ; DS:SI => start of environ strings
- rep movsb ; Move them
- mov ds, es:DGRP ; Restore DS
- mov bx, di ; Save for later
- push 0 ; Push Null address
- if @DataSize
- push 0 ; Null Pointer
- endif
-
- ;----------------------------
- ; P DI points to the top of the environmental strings. Build *enpr[]
- ; by pushing pointers to the start of each string onto the stack.
- ; The last pointer in the array must be a null to signify the end of
- ; the array.
-
- mov cx, ENV_STR_LEN ; CX = count
- dec di ; Adjust DI to point to last char
- SU4: or byte ptr [di], 0 ; Is it a null?
- jnz SU5 ; No, have reached a char
- dec di ; Yes, decrement DI
- dec cx ; Account for char checked
- jmp SU4 ; Check next char
- SU5: or byte ptr [di], 0 ; Is it a null?
- jnz SU6 ; No, continue
- mov ax, di ; AX => null
- inc ax ; AX => 1st char
- if @DataSize
- push ss ; Large data models, push seg addr
- endif
- push ax ; Push offset
- SU6: dec di ; DI => next char
- loop SU5 ; Not at the end, continue
- if @DataSize
- push ss ; Large data models, push seg addr
- endif
- inc di ; Point to last char
- push di ; Push offset
- mov dx, sp ; Save the pointer to the *envp[]
-
- ;----------------------------
- ; Q See if there are any command line arguments. If not, all command
- ; line processing is skipped.
-
- if @Model NE 1
- assume es:PSP_SEG
- mov es, PSP ; ES => PSP
- endif
- xor ax, ax ; Ensure AX is null
- push ax ; Ensure null on top of stack
- mov bp, sp ; BP = top of stack
- if @Model NE 1
- cmp es:PARM_LEN, 0 ; Are there any Command Line arguments
- else
- assume ds:PSP_SEG
- cmp PARM_LEN, 0 ; Are there any Command Line arguments
- assume ds:DGROUP
- endif
- je SU7 ; Yes, go process what is on the stack
-
- ;----------------------------
- ; R There are command line arguments. Transferring the entire command
- ; line tail onto the stack.
-
- if @Model NE 1
- mov cl, es:PARM_LEN ; Get # of bytes in Command Line
- else
- assume ds:PSP_SEG
- mov cl, PARM_LEN ; Get # of bytes in Command Line
- assume ds:DGROUP
- endif
- inc cx ; increase by one
- and cx, 0feh ; Force an even count
- mov ax, sp ; Get SP
- sub ax, cx ; Subtract PARM_LEN
- mov sp, ax ; Reset SP, room on Stack
- if @Model NE 1
- lea si, es:PARMS ; Load source addr in SI
- else
- assume ds:PSP_SEG
- lea si, PARMS ; Load source addr in SI
- assume ds:DGROUP
- endif
- mov di, sp ; Load destin addr in DI
- if @Model NE 1
- mov es, DGRP ; ES => DGROUP
- assume es:DGROUP
- mov ds, PSP ; DS => PSP
- endif
- rep movsb ; Move Command Line onto the Stack
- if @Model NE 1
- mov ds, es:DGRP ; Restore DS
- endif
-
- ;----------------------------
- ; S Move the entire d:path\filename.ext onto the stack as *argv[0].
-
- SU7: mov es, ENVIRON ; ES => ENVIRON seg
- mov di, ENV_STR_LEN ; ES:DI => end of environment
- or byte ptr es:[di + 1], 0 ; Is there another null byte?
- jnz SU8 ; No, continue
- inc di ; ES:DI => true end of environment
- SU8: add di, 3 ; ES:DI => 1st char in filename
- mov si, di ; SI => 1st char in filename
- mov cx, -1 ; CX = max count
- xor al, al ; AL = null
- repne scasb ; Find the terminating null
- neg cx ; Convert to positive number
- sub cx, 2 ; Correct for starting @ -1 and neg
- and cx, 0fffeh ; Force an even count
- mov es, DGRP ; ES => STACK
- assume es:DGROUP
- mov ds, ENVIRON ; DS:SI => 1st char in filename
- sub sp, cx ; Make room for it
- mov di, sp ; ES:DI => where to put it
- rep movsb ; Move it onto the stack
- mov ds, es:DGRP ; Reset DS
-
- ;-----------------------------
- ; T Convert all blanks, not within double quotes, in the Command Line
- ; to Nul. CX is LITERAL_FLAG. Build argv[] at the same time.
-
- xor di, di ; DI set to zero, DI = arg count
- mov bx, bp ; BX points to last byte in stack
- dec bx ; Start on the second character
- mov bp, sp ; BP is the stop point
- xor cx, cx ; Clear LITERAL FLAG
- SU9: mov al, [bx] ; Get byte
- cmp al, '"' ; Is it a literal?
- jne SU10 ; No, go to next test
- inc cx ; Set LITERAL_FLAG
- and cx, 1 ; Ensure only 0, & 1 valid
- jmp short SU11 ; Continue, and blank '"'
- SU10: or al, al ; Is it a null?
- je SU12
- cmp al, ' ' ; Is it a blank?
- ja SU13 ; No, go set up to get another
- or cx, cx ; Is LITERAL_FLAG clear?
- jnz SU13 ; No, do not null blank
- SU11: mov byte ptr [bx], 0 ; Store Nul in [BX]
- SU12: or byte ptr [bx + 1], 0 ; Was the previous char null?
- jz SU13 ; Yes, do not push pointer
- if @datasize
- push ss ; Push segment address
- endif
- mov ax, bx ; AX => Null
- inc ax ; AX => 1st char
- push ax ; Push the offset
- xor cx, cx ; Clear in literal flag
- inc di ; Account for another arg
- SU13: dec bx ; BX point to next byte
- cmp bx, bp ; Are we through yet?
- jnb SU9 ; No, go one mo' 'gin
-
- ;----------------------------
- ; U Account for the last argrument.
-
- if @datasize
- push ss ; Push segment address
- endif
- inc bx ; Adjust BX to the last char
- push bx ; Push the offset
- inc di ; Account for another arg
-
- ;-----------------------------
- ; V Have finished building *envp[]. DX => envp[0], SP => argv[], and
- ; DI = argc. Create argc and argv on the stack using the FORTRAN
- ; calling convention. If SIMPLE is defined, push only argc. See
- ; documentation above for structure of proc declaration in MAIN
- ; procedure
-
- mov ax, dx ; AX = **envp
- mov dx, sp ; DX = *argv[]
- ifndef SIMPLE ; SIMPLE NOT selected
- push di ; Push argc
- if @DataSize
- push ss ; Push segment register if required
- endif
- push dx ; Put **argv onto the stack
- else ; SIMPLE selected
- push di ; Push argc
- endif
-
- ;----------------------------
- ; W The last thing to do is push envpr onto the stack
-
- if @DataSize
- push ss ; Push segment register if required
- endif
- push ax ; Push envp onto stack
-
- ;-----------------------------
- ; X call MAIN
-
- call MAIN ; Call MAIN procedure
-
- ;----------------------------
- ; Y If main returns, the program is to terminate with the control code
- ; returned in AL
-
- SU14: mov ah, 4ch ; End process
- int 21h ; Call DOS
-
- % Start_Up3 endp
-
- % end Start_Up3 ; Indicate that START_UP3 is the start
- ; of the program
-