home *** CD-ROM | disk | FTP | other *** search
- page 60,132
- title Start_Up2 -- Advanced Start Up Code for .COM/.EXE Programs
- name Start_Up2
-
- comment ÷
- START_UP2 V1.06
- --------------------------------------------------------------------------
- NAME
- Start_Up2 Advanced start up code for .com and .exe programs
-
- SYNOPSIS
- extern Start_Up2
- or
- extern Start_Up2S
-
- 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 this 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.
-
- This procedure performs the following functions in addition to above
- - Initializes the following global variables:
- -- DGRP, segment address of DGROUP
- -- STACK_BOTTOM, offset to stack bottom in DGROUP
- -- PSP, segment address of PSP
- -- ENVIRON, segment address of passed copy of the ENVIRONMENT
- -- NEXTPARA, segment address of next memory paragraph
- -- OSMAJOR, integer part of OS system
- -- OSMINOR, decimal part of OS system
- - If DOS version is less than 2.0, aborts with error message
- - Initializes DS and ES segment registers to DGROUP
- - Shrinks memory down to size of program by releasing all memory
- above program
-
- RETURNS
- If main returns to startup code. The program terminiates with the
- return code in AL.
-
- PROGRAMING NOTES
- Assembled with Microsoft MASM V6.11d.
-
- Written to link with any memory model.
-
- 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: 221 242 244 245 247 247
- Code (SIMPLE): 218 239 241 241 243 243
- _Data: 0 0 0 0 0 0
- Const: 0 0 0 0 0 0
- _BBS: 12 12 12 12 12 12
- Stack: ? ? ? ? ? ?
-
- 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.
-
- Note: The 12 bytes in _BSS are from sudata.asm which is used so that
- all startup code can reside in the same .lib file.
-
- CAUTION
- Startup2 defines a 1K 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 __end properly.
-
- EXTERNAL LIBRARIES
- sudata 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_2 or Startup_2S
-
- 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
- - Removed checking for DOS Ver 1.0 and
- terminating with error message
- - Removed checking for DOS Ver 2.0 or earlier
- and substituting .asm for the filename.
- 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
-
- STACK_SIZE equ 1024
-
- ;=========================================================================
- ; DATA
- ;=========================================================================
- ; E Define the stack if required
-
- if @Model NE 1
- .STACK STACK_SIZE ; Define a nominal stack
- endif
-
- ;----------------------------
- ; 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_Up2 equ <Start_Up2S>
- endif
-
- % Start_Up2 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
- and bx, 0fff0h ; Truncate to lower paragraph
- add bx, 16 ; Add one paragraph
- 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 Release all memory above program. Calculate the size of the program
- ; in paragraphs (16 bits). BX starts with Stack Top
-
- mov cl, 4 ; Convert to #para by dividing by 16
- shr bx, cl ; Do division by bit shifting
-
- if @Model ne 1 ; Needed in non-Tiny memory models
- 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
-
- ;----------------------------
- ; N See if there are any command line arguments. If not, all command
- ; line processing is skipped and execution jumps to loading the
- ; process name onto the stack.
-
- xor dx, dx ; Ensure DX is null
- push dx ; 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 SU1 ; No, go load filename
-
- ;----------------------------
- ; O There are command line arguments. Parse them onto the stack and
- ; build ARGC and ARGV. Start this by transferring the entire command
- ; line tail onto the stack. Make room for them by moving SP.
-
- if @Model NE 1
- mov ds, PSP ; DS => PSP
- endif
- assume ds:PSP_SEG
- mov cl, PARM_LEN ; Get # of bytes in Command Line
- 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
- lea si, PARMS ; Load source addr in SI
- mov di, sp ; Load destin addr in DI
- if @Model NE 1
- mov es, DGRP ; ES => DGROUP
- endif
- assume es:DGROUP
- rep movsb ; Move Command Line onto the Stack
- if @Model NE 1
- mov ds, es:DGRP ; Restore DS
- endif
- assume ds:DGROUP
-
- ;----------------------------
- ; P Move the entire d:path\filename.ext onto the stack as *argv[0].
-
- SU1: 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
- SU2: repne scasb ; Find null
- cmp al, es:[di] ; Is next byte a null also?
- jne SU2 ; No, continue search
- 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 ; CX = length + null + 1
- dec cx ; CX = length + null
- and cx, 0feh ; 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
-
- ;-----------------------------
- ; Q Convert all blanks, not within double quotes, in the Command Line
- ; to Nulls. CX is used as IN_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
- mov bp, sp ; BP is the stop point
- xor cx, cx ; Clear IN_LITERAL_FLAG
- SU3: mov al, [bx] ; Get byte
- cmp al, '"' ; Is it a literal?
- jne SU4 ; No, go to next test
- inc cx ; Set IN_LITERAL_FLAG
- and cx, 1 ; Ensure only 0, & 1 valid
- jmp short SU5 ; Continue, and blank '"'
- SU4: cmp al,' ' ; Is it a blank?
- ja SU7 ; No, go set up to get another
- or cx, cx ; Is IN_LITERAL_FLAG clear?
- jne SU7 ; No, do not null blank
- SU5: mov byte ptr [bx], 0 ; Store Nul in [BX]
- SU6: or byte ptr [bx + 1], 0 ; Was the previous char null?
- jz SU7 ; 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
- SU7: dec bx ; BX point to next byte
- cmp bx, bp ; Are we through yet?
- jnb SU3 ; No, go one mo' 'gin
-
- ;----------------------------
- ; R 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
-
- ;-----------------------------
- ; S Have finished building *argv[]. 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
-
- ifndef SIMPLE
- mov bx, sp ; BX = **argv[]
- push di ; Push argc
- if @DataSize
- push ss ; Long pointer
- endif
- push bx ; Push **argv[]
- else
- push di ; Push argc
- endif
-
- ;-----------------------------
- ; T call MAIN
-
- SU8: call Main ; Call MAIN procedure
-
- ;----------------------------
- ; U If main returns, the program is to terminate with the control code
- ; returned in AL
-
- mov ah, 4ch ; End process
- int 21h ; Call DOS
-
- % Start_Up2 endp
-
- % end Start_Up2 ; Indicate that Start_Up2 is the start
- ; of the program
-