home *** CD-ROM | disk | FTP | other *** search
- ;_ c.asm Fri Feb 12 1988 Modified by: Walter Bright */
- ; Copyright (C) 1985-1988 by Northwest Software
- ; All rights reserved.
- ; Written by Walter Bright
- ; C startup file
-
- ;I8086T equ 1 ;defined to create a .COM version (CT.OBJ)
-
- DOSSEG ;have linker fix ordering of segments
- .286C ;disable automatic FWAIT generation
-
- ;*********************************************
-
- ; Determine which memory model we are assembling for. For .COM files,
- ; force S model.
- ifdef I8086T
- I8086S equ 1
- else
- ifndef I8086S
- ifndef I8086M
- ifndef I8086C
- ifndef I8086L ;if none of the memory models are defined
- I8086S equ 1 ;default to S model
- endif
- endif
- endif
- endif
- endif
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ; Macros specific to each memory model in an attempt to make it easier
- ; to write memory model independent code.
- ; P Offset on BP to first argument on stack
- ; SPTR 1 if small data model
- ; LPTR 1 if large pointers (large data)
- ; LCODE 1 if large code model
- ; SIZEPTR # of bytes in a pointer
- ; func Declare a function as NEAR or FAR
-
- ;;;;;;;;;;;;;; SMALL MEMORY MODEL ;;;;;;;;;;;;;;;;;
-
- ifdef I8086S
-
- P equ 4 ; Offset of start of parameters on the stack frame
- SPTR equ 1
- LPTR equ 0
- LCODE equ 0
- SIZEPTR equ 2 ; Size of a pointer
-
- func macro name
- name proc near
- endm
-
- endif
-
- ;;;;;;;;;;;;;;;;; MEDIUM MEMORY MODEL ;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- ifdef I8086M
-
- P equ 6 ; Offset of start of parameters on the stack frame
- SPTR equ 1
- LPTR equ 0
- LCODE equ 1
- SIZEPTR equ 2
-
- func macro name
- name proc far
- endm
-
- endif
-
- ;;;;;;;;;;;;;;;;; COMPACT MEMORY MODEL ;;;;;;;;;;;;;;
-
- ifdef I8086C
-
- P equ 4 ; Offset of start of parameters on the stack frame
- SPTR equ 0
- LPTR equ 1
- LCODE equ 0
- SIZEPTR equ 4
-
- func macro name
- name proc near
- endm
-
- endif
-
- ;;;;;;;;;;;;;;;; LARGE MEMORY MODEL ;;;;;;;;;;;;;;;;;;;
-
- ifdef I8086L
-
- P equ 6 ; Offset of start of parameters on the stack frame
- SPTR equ 0
- LPTR equ 1
- LCODE equ 1
- SIZEPTR equ 4
-
- func macro name
- name proc far
- endm
-
- endif
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ; Other more or less useful macros
-
- callm macro func
- if LCODE
- call far ptr func
- else
- call near ptr func
- endif
- endm
-
- .if macro arg1,cond,arg2,lbl
- cmp arg1,arg2
- j&cond lbl
- endm
-
- bdos macro func
- ifnb <func>
- mov AH,func
- endif
- int 21h
- endm
-
- .retf macro val ;force assembler to build a far return
- ifnb <val>
- db 0CAh
- dw val
- else
- db 0CBh
- endif
- endm
-
- public __BASE,__exit,__dos,_errno
- public __datapar,__pastdata,__progpar,__heapbottom
- public __psp,__doserrno,__oserr,__chkstack,__chkstk,__osmajor,__osminor
- public __8087
- if LPTR
- public __totalpar
- endif
-
- extrn __stack:word ;default stack size
- extrn __okbigbuf:word
- if LCODE
- extrn _exit:far, _sbrk:far, _free:far, __main:far, __entry:far
- else
- extrn _exit:near, _sbrk:near, _free:near, __main:near, __entry:near
- endif
-
- public __acrtused
- __acrtused equ 1234 ;cause linker to pull in this module
-
- ARGMAX = 32 ;max # of command line args
- CR = 13
- LF = 10
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;The code segment must be lower in memory than the data segment
- ;(so we can add to the top of the data segment).
-
- if LCODE
- C_TEXT segment word public 'CODE'
- assume CS:C_TEXT
- else
- _TEXT segment word public 'CODE'
- assume CS:_TEXT
- endif
-
- ifdef I8086T
- ;Note that this is not 100h! This is because the DOSSEG directive
- ;causes LINK to move everything up 10h bytes! Why, I dunno.
- org 0F0h
-
- start:
- jmp begin
- else
- mov DX,offset DGROUP:nullfp ;NULL function pointer
- jmp fatmsg
- endif
-
- if LCODE
- C_TEXT ends
- else
- _TEXT ends
- endif
-
- ;Define a segment so we can find the end of the code
- C_ETEXT segment word public 'ENDCODE'
- C_ETEXT ends
-
- ifdef I8086T
- CGROUP group _TEXT,C_ETEXT ;code must all fit in 1 segment
- endif
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ; Set up segments for data
-
- ;Segment so we can find the start of DGROUP
-
- NULL segment para public 'BEGDATA' ;note PARAGRAPH alignment
- NULL ends
-
- ;Regular initialized data goes in _DATA
-
- _DATA segment word public 'DATA'
- _DATA ends
-
- ;Constant data, such as switch tables, go here.
-
- CONST segment word public 'CONST'
- CONST ends
-
- ;Segment for uninitialized data. This is set to 0 by the startup code,
- ;so it does not consume room in the executable file.
-
- _BSS segment word public 'BSS'
- _BSS ends
-
- ;Segment to provide an initial stack so DOS can fire up the program.
- ;We'll set up our own stack later. Use the space later for the
- ;program arguments.
- ;Our stack in large data models is in its own segment, not part of
- ;DGROUP. This is different than MSC.
-
- ifdef I8086T
- STACK segment word public 'STACK' ;no stack for .COM files
- bssend equ $
- else
- STACK segment para stack 'STACK'
- endif
-
- cmdline equ $ ;where the command line will be placed.
- ifndef I8086T
- org $+128 ;a 128 byte stack is necessary to fire
- ;up .EXE programs, even if a CLI is the
- ;first instruction!
- dummy equ $
- endif
- STACK ends
-
-
- ;Stuff all these segments into one group so they can call be accessed by DS
- ; DOSSEG should do this, but who trusts it?
- DGROUP group NULL,_DATA,CONST,_BSS,STACK ;data segment
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;Form the start of DGROUP
-
- NULL segment
-
- ;for programs that dereference NULL string pointers
- db 'ERROR: NULL pointer', 0
-
- ;Put baggage here where it can be safely stomped by NULL pointer
- ;assignments. Should do a checksum of this to detect these.
- db 'Zortech C 4.00 library, Copyright (C) 1988 '
- ifdef I8086S
- db 'S'
- endif
- ifdef I8086M
- db 'M'
- endif
- ifdef I8086C
- db 'C'
- endif
- ifdef I8086L
- db 'L'
- endif
- db ', written by Walter Bright',0
-
- NULL ends
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ; Initialized data globals
-
- _DATA segment
-
- ;These symbols are created by the linker (DOSSEG switch)
- extrn _edata:byte ;first location in first BSS segment
- ifndef I8086T
- extrn _end:byte ;first location in first STACK segment
- endif
-
- prgnam db 0 ;dummy program name (null string)
- argc dw ? ;number of args
- argv dw offset DGROUP:prgnam ;which is the program name
- if LPTR
- dw seg DGROUP
- endif
- db (ARGMAX+1)*SIZEPTR dup (0) ;the rest of the arguments
- ;(this area doubles as the
- ;initial stack, so no initialized
- ;data here!)
- ;The +1 is so argv[] is always
- ;followed by a NULL.
- __BASE dw ? ;pointer to stack overflow check word
- ;(if this word changes, we have a stack
- ;overflow)
- __datapar dw ? ;# of paragraphs currently in data segment
- ; (max is 0FFFh)
- __pastdata dw ? ;address of 1 past data segment
- __heapbottom dw ? ;lowest address in heap (used to detect
- ; free() errors)
- __progpar dw ? ;# of paragraphs in PSP + code segment
- if LPTR
- __totalpar dw ? ;total # of paragraphs in program
- endif
-
- __8087 dw -1 ;1 means we have an 8087 on board
- __psp dw ? ;segment of program segment prefix
- __osmajor label byte ;MSC compatibility
- __dos db ? ;MS-DOS major version number
- __osminor label byte ;MSC compatibility
- db ? ;MS_DOS minor version number
- __oserr label word ;Lattice C compatibility
- __doserrno label word ;DOS error number (for compatibility
- ; with MSC). It is the same as errno.
- _errno dw 0 ;global error number
- ovrflw db CR,LF,'Stack Overflow',CR,LF,'$'
- nomem db CR,LF,'Not enough memory',CR,LF,'$'
- badstk db CR,LF,'Bad stack size parameter',CR,LF,'$'
- baddos db CR,LF,'DOS 1.xx not supported',CR,LF,'$'
- badcmd db CR,LF,'Only 32 args allowed',CR,LF,'$'
- nullfp db CR,LF,'NULL function pointer',CR,LF,'$'
-
- even
- _DATA ends
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ; Program segment prefix:
- ; 00h int 20h instruction
- ; 02h top of memory in paragraphs
- ; 80h number of bytes in command line
- ; 81h the command line
- ; Input (for .EXE files):
- ; DS,ES segment of program segment prefix
- ; CS segment of prog (CS = DS + 10h)
- ; IP offset of $start from prog
- ; SS right after end of stack segment
- ; SP 100h (the stack grows negative)
-
- if LCODE
- C_TEXT segment word 'CODE'
- assume cs:C_TEXT
- else
- _TEXT segment word 'CODE'
- assume cs:_TEXT
- endif
-
- assume DS:DGROUP
-
- ifdef I8086T
- begin:
- ;Monkey around because the assembler won't give us
- ;the data segment start. We have to compute it.
- cli
- mov AX,offset CGROUP:C_ETEXT
- add AX,15 ;round up to next paragraph
- mov CX,4
- shr AX,CL ;convert offset to paragraphs
- mov CX,CS
- add AX,CX ;now AX = segment of DGROUP
- else
- start: ;program entry point
- cli ;turn off till we get stack fixed up
- mov AX,seg DGROUP ;start of data segment (in paragraphs)
- endif
-
- fninit ;initialize 8087 (no WAITs, so do it early)
-
- mov DS,AX ;now we have the correct data segment
- mov __psp,ES ;save segment of PSP
- mov BX,AX
- sub BX,__psp ;# of paragraphs in PSP + code
- mov __progpar,BX ;and save that
-
- mov BX,ES:2 ;top of memory (in paragraphs)
- sub BX,AX ;BX = # of paragraphs we have in data seg
- .if BX b 1000h, L10 ;if less than 64k
- mov BX,0FFFh ;round down
- L10: mov DX,AX
- add DX,BX
- inc DX ;DX = # of paragraphs of code + data + round
- mov CL,4
- shl BX,CL
- mov SS,AX ;set SS to data segment
- mov SP,BX ;set SP to that too
-
- ;See if we have enough stack space before we turn interrupts back on.
- sub BX,offset DGROUP:cmdline ;max of allocated addresses
- .if BX a 512, L11 ;should be enough for the moment
- mov DX,offset nomem
- jmp fatmsg
-
- L11: sti
-
- ;Figure out what version of MS-DOS we're running under.
- bdos 30h
- mov word ptr __dos,AX
- .if AL ae 2, L7 ;if 2.00 or later
- mov DX,offset baddos
- jmp fatmsg
-
- L7:
-
- ;Transfer the command line to cmdline, as the PSP is not
- ;necessarilly in the data segment.
-
- mov AX,ES
- mov DS,AX ;DS points to PSP
- mov AX,SS
- mov ES,AX ;ES points to data segment
-
- mov SI,80h
- cld
- lodsb ;AL = # of bytes in command line
- xor AH,AH
- mov CX,AX ;# of bytes to copy
- mov DI,offset DGROUP:cmdline
- rep movsb ;transfer to stack
- xor AL,AL
- stosb ;and a terminating 0
-
- ;Now set DS to be the same as ES and SS
-
- mov AX,SS
- mov DS,AX
-
- ;Round up DI, which will form the bottom of the stack
- inc DI
- and DI,0FFFEh
- mov __BASE,DI
- if SPTR
- mov word ptr [DI],55AAh ;stack overflow check word
- endif
-
- ifndef INTONLY
- fnstsw __8087 ;store status word
- ;(do it early to be sure it is done
- ;by the time we read __8087)
- endif
-
- ;Set up argc and argv
-
- mov SI,offset DGROUP:cmdline ;SI -> start of command line
- mov BX,SIZEPTR ;number of args * SIZEPTR
- L1: lodsb ;get char from command line
- .if AL ne '=', L5
- .if __stack e 0, L5 ;if ignore '=nnnn' command
- call set_stack ;set _stack
- L5: or AL,AL ;end of command line?
- jz L2 ;yes, done
- mov DX,9*256 + ' '
- .if AL e ' ', L1
- .if AL e 9, L1 ;eat spaces and tabs
- .if AL e '"', L22
- .if AL ne "'", L21
- L22: mov DL,AL
- mov DH,AL
- inc SI ;point past the ' or "
-
- L21: .if BX be ARGMAX*SIZEPTR, L20
- mov DX,offset badcmd
- jmp fatmsg
-
- L20: dec SI ;address of start of parameter
- mov argv[BX],SI ;store in argv
- if LPTR
- mov argv+2[BX],DS
- endif
- add BX,SIZEPTR ;next slot in argv
- L4: lodsb ;get char of parameter
- or AL,AL ;done?
- jz L2 ;yes
- .if AL e DL, L3 ;end of parameter
- .if AL ne DH, L4 ;not end of parameter
- L3: xor AL,AL
- mov -1[SI],AL ;terminate parameter with a 0
- jmp L1 ;look for next parameter
-
- L2: shr BX,1 ;get arg count
- if LPTR
- shr BX,1
- endif
- mov argc,BX ;and put in argc
-
- ;Determine if we have an 8087, 80287, or 80387
-
- xor BX,BX ;assume no NDP
- test __8087,0B8BFh
- jnz L6 ;no 8087
- inc BX ;could be 8087, 80287 or 80387
- and byte ptr __8087,07Fh ;turn off interrupt mask bit
- fldcw __8087
- fdisi ;disable interrupts (works on 8087 only)
- fstcw __8087
- fwait
- test byte ptr __8087,80h ;see if bit is back on
- jnz L6 ;yes, then 8087
- inc BX ;287 or 387
- or byte ptr __8087,0BFh ;disable interrupts, mask exceptions
- and __8087,0EFFFh ;turn off infinity bit
- fldcw __8087
- fld1
- fldz
- fdivp ST(1),ST ;divide by 0 to get infinity
- fld ST
- fchs ;create +infinity and -infinity
- fcompp ;and see if they're the same
- fstsw AX
- or __8087,08000h ;turn on infinity bit
- fldcw __8087
- sahf
- jz L6 ;equal, so it's a 287
- inc BX ;BX = 3 for 80387
- L6: mov __8087,BX ;set flag
-
- ;Set up stack boundaries
-
- ;__stack = min(_stack,512)
-
- mov BX,__stack
- or BX,BX ;if __stack was special value of 0
- jnz L14
- mov BX,02000h ;then use this as the stack size
- L14: .if BX ae 512, L8 ;make sure at least 512 bytes
- mov BX,512
- L8: mov __stack,BX
-
- if SPTR
- add BX,__BASE ;add base of stack to stack size
- jc outofmemory
- else
- mov BX,__BASE
- endif
- add BX,2 + 15 ;base word + round up to paragraph
- jc outofmemory
- and BX,0FFF0h
- mov __pastdata,BX
- mov __heapbottom,BX
- mov CL,4
- shr BX,CL
- mov __datapar,BX ;# of paragraphs in data segment
- add BX,__progpar ;total size of program in paragraphs
- if LPTR
- mov __totalpar,BX
- endif
- mov ES,__psp ;segment of start of program
- cli ;SP could point to unallocated memory
- bdos 4Ah ;resize memory
- push DS
- pop ES ;restore ES
- jnc L9
- sti
-
- outofmemory:
- mov DX,offset nomem
- jmp fatmsg
-
- L9:
- if SPTR
- mov SP,__heapbottom ;new top of stack
- sti
- .if __okbigbuf e 0, L12
- ;Attempt to grow our data segment to 64k
- mov AX,0FFF0h
- sub AX,__pastdata
- push AX
- call _sbrk
- pop BX ;dump argument off stack
- .if AX e -1, L13 ;error
- add AX,2 ;skip over byte count
- push AX
- call _free ;add to memory pool
- pop BX ;clean stack
- jmp short L12
-
- L13: mov __okbigbuf,0 ;big buffers are not ok, we're short on memory
- L12:
- else
- mov __okbigbuf,0 ;no big buffers for large data model
- push __stack
- call _sbrk ;grow data segment by size of stack wanted
- pop CX ;fix stack
- .if AX e -1, outofmemory
-
- cli ;while we fiddle with the stack
- mov ES,DX
- mov BX,AX
- mov SP,ES:[BX] ;set just past end of allocated block
- add SP,16 ;one paragraph more
- dec DX ;but compensate by decrementing segment
- mov SS,DX ;this is so stack overflow checks work better
- sti
-
- mov __heapbottom,SP ;set top of stack
- mov __BASE,16 ;set address of check word
- mov word ptr SS:16,55AAh ;stack check word
- endif
-
- call getargv0 ;determine argv[0] if possible
-
- ;Clear uninitialized data segment (UDATA)
- push DS
- pop ES
- ifdef I8086T
- mov CX,offset DGROUP:bssend
- else
- mov CX,offset DGROUP:_end
- endif
- mov DI,offset DGROUP:_edata ;start of uninitialized data
- sub CX,DI ;CX = number of bytes to clear
- jcxz L23 ;no uninitialized data
- xor AL,AL
- rep stosb ;clear it out
- L23:
-
-
- xor BP,BP ;so debuggers can find last stack frame
- call __entry ;perform static constructors
-
-
- ;Call _main(argc,argv)
-
- if LPTR
- push DS ;segment of argv
- endif
- mov AX,offset DGROUP:argv
- push AX
- push argc
- call __main ;call C _main(argc,argv)
- push AX ;save exit status for _exit
- callm __chkstack ;see if stack overflowed
- call _exit ;return to MS-DOS
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ; Determine argv[0] if possible.
-
- getargv0 proc near
- .if __osmajor b 3, GA1 ;not possible for old versions of DOS
-
- ;Determine start of argv0
- mov ES,__psp
- mov ES,ES:2Ch ;get segment of environment string
- xor DI,DI
- xor AL,AL ;looking for terminating 0
- mov CX,0FFFFh
- cld
- GA2: repne scasb
- scasb ;2 bytes of 0?
- jnz GA2 ;no, more environment
- add DI,2 ;ES:DI -> argv0
- if LPTR
- mov argv,DI
- mov argv+2,ES
- else ;SPTR
- ;Need to copy to a location accessible by DS. Use the stack.
- ;Determine length needed in CX.
- mov SI,DI ;save offset
- mov CX,0FFFFh
- repne scasb ;look for terminating 0
- neg CX
- and CX,0FFFEh ;CX is count rounded up to next word
- pop BX ;BX = return address from this function
- sub SP,CX ;allocate room for argv0
- mov DI,SP
- push ES
- pop DS ;SI:DS -> argv0
- push SS
- pop ES ;ES:DI -> stack buffer
- rep movsb ;transfer to stack
- push SS
- pop DS ;restore DS
- mov argv,SP ;set pointer to it
- ;Note at this point, ES==DS
- jmp BX
- endif
- GA1: ret
- getargv0 endp
-
- ;;;;;;;;;;;;;;
- ; Set __stack.
- ; Input:
- ; SI -> start of parameter
- ; Returns:
- ; AL = char past end of number
- ; SI -> past AL
- ; Do not destroy BX
-
- set_stack proc near
- mov DI,10
- xor CX,CX ;accumulate result in CX
- S1: lodsb ;get next char of paramter
- or AL,AL ;end of command line?
- jz S2 ;yes
- .if AL e ' ', S2
- .if AL e 9, S2 ;if end of parameter
- sub AL,'0'
- js err
- cbw ;AH = 0
- .if AX ae DI, err ;AL is not a digit
- xchg AX,CX
- mul DI
- jc err ;integer overflow
- add CX,AX ;CX = CX*10 + AX
- jnc S1 ;no error
-
- err: mov DX,offset badstk
- jmp short fatmsg
-
- S2: mov __stack,CX ;store result in __stack
- ret
- set_stack endp
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ; Terminate execution of C program.
- ; Input:
- ; 2[SP] error code (ignored if not MS-DOS 2.00)
-
- func __exit
- bdos 30h ;get DOS version number
- .if AL b 2, E1 ;if pre-DOS 2.00
-
- mov BP,SP
- mov AL,P-2[BP] ;AL = error code
- bdos 04Ch ;Terminate a process (Exit)
-
- E1: push __psp
- xor AX,AX
- push AX ;push &(PSP:0)
- .retf ;far return to DOS
- __exit endp
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ; Check and see if stack overflowed.
- ; This can be called at any time by the application code. It is useful
- ; to place a call to this at selected points when stack overflow checking
- ; is turned off.
- ; Returns:
- ; # of bytes left in stack
-
- func __chkstack
- mov BX,__BASE
- if SPTR
- .if <word ptr [BX]> ne 55AAh, XCOVF
- else
- .if <SS:word ptr [BX]> ne 55AAh, XCOVF
- endif
- mov AX,SP
- sub AX,BX
- jbe XCOVF
- .if SP a __heapbottom, XCOVF
- ret
- __chkstack endp
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ; Stack frame generator.
- ; Called at entry to each function when stack overflow checking
- ; is turned on.
-
- func __chkstk
- pop DX ;get offset of return addr
- if LCODE
- pop CX ;get segment
- endif
- sub SP,AX ;create space for local variables
- jbe XCOVF ;overflowed
- mov BX,__BASE
- if SPTR
- .if <word ptr [BX]> ne 55AAh, XCOVF
- else
- .if <SS:word ptr [BX]> ne 55AAh, XCOVF
- endif
- if SPTR
- .if SP ae __heapbottom, XCOVF
- endif
- if LCODE
- push CX
- push DX
- ret
- else
- jmp DX ;return to caller
- endif
- __chkstk endp
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ; Stack overflow jumps here.
-
- XCOVF: mov DX,offset ovrflw
- ; jmp short fatmsg
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ; Print out fatal error message and abort.
- ; Input:
- ; DS:DX -> message
-
- fatmsg: bdos 9
- mov AX,1 ;error exit code
- push AX
- call __exit ;abort
- ;never reached
-
- if LCODE
- C_TEXT ends
- else
- _TEXT ends
- endif
-
- end start
-