|
|
Microsoft OS/2 SDK 12-15-1987
page ,132 TITLE crt0 - C start up routine ;*** ;crt0.asm - C start up routine ; ; Copyright (c) 1985-1987, Microsoft Corporation. All rights reserved. ; ;Purpose: ; How startup works in a few words - ; ; The startup and termination is performed by a few modules ; ; crt0.asm DOS 2.x/3.x specific init/term ; crt0msg.asm DOS 2.x/3.x error messages ; (winstart.asm) Windows specific init/term (not included) ; ; crt0dat.asm remainder of shared DOS 3.x init/term ; ; ************* IMPORTANT ***************************************** ; ; If the user reassembles this module, he will need to link using the ; /DOSSEG switch or run the the DOSSEG.EXE program on crt0.obj, i.e. ; ; dosseg crt0.obj ; ; See the C documentation for more information about the /DOSSEG switch. ; ; All assembler modules must be assembled with the /mx switch, i.e. ; ; masm crt0/mx; ; ;******************************************************************************* ?DF= 1 ; this is special for c startup include version.inc .xlist ifdef _QC ;[5] move includes out of version include \sl\qc\src\include\kernel.inc ;[5] include \sl\qc\src\include\kmac.inc ;[5] CEDEF macro name, num, msg ;;[8] name equ num ;;[8] endm ;;[8] include \sl\qc\src\include\criterr.inc ;[8] endif ;_QC ;[5] include cmacros.inc include msdos.inc include brkctl.inc .list ifdef _QC ;[2] extrn $Exec_Shell:FAR ;[9] extrn $GlobalAlloc:FAR ;[2] extrn $GlobalLock:FAR ;[2] extrn $GlobalUnlock:FAR ;[2] extrn _AddString:FAR ;[2] endif ;_QC ;[2] page ;=========================================================================== ; ; Segment definitions ; ; The segment order is essentially the same as in XENIX. ; This module is edited after assembly to contain a dosseg comment ; record for the linker. ; ;=========================================================================== createSeg _TEXT, code, word, public, CODE, <> createSeg C_ETEXT,etext, word, public, ENDCODE,<> createSeg _DATA, data, word, public, DATA, DGROUP createSeg STACK, stack, para, stack, STACK, DGROUP defGrp DGROUP ; define DGROUP codeOFFSET equ offset _TEXT: dataOFFSET equ offset DGROUP: page public __acrtused ; trick to force in startup __acrtused = 9876h ; funny value not easily matched in SYMDEB extrn __acrtmsg:abs ; trick to pull in startup messages ifndef _QC sBegin stack assumes ds,data db 2048 dup (?) ; default stack size sEnd endif ;_QC page ifndef _QC externP main ; C main program endif ;_QC externP exit ; exit ( code ) if sizeC extrn __exit:far ; _exit ( code) (cmacros name conflict) else extrn __exit:near endif sBegin data extrn _edata:byte ; end of data (start of bss) extrn _end:byte ; end of bss (start of stack) externW _psp ; psp:0 (paragraph #) externW __argc externDP __argv externDP environ ifdef _QC externW _QChdata ; High water mark of data space globalW _QCldata,0 ; begining of DGroup (low water mark) extrn STKHQQ:WORD extrn __QCrtcsip:DWORD ;[2] endif ;_QC ; these are used by DOS C memory management (not used in Windows) globalW _asizds,0 ; DS size (in bytes) globalW _atopsp,0 ; top of stack (heap bottom) globalW _aexit_rtn,<codeoffset __exit> ; NEAR pointer labelW <PUBLIC,_abrktb> ; segment table for brkctl dw ? dw DGROUP db (MAXSEG-1) * (size segrec) dup (?) labelW <PUBLIC,_abrktbe> globalW _abrkp,<dataoffset _abrktb> sEnd page externP _cinit ; run-time initializers ifdef _QC externP _NMSG_TEXT ; pascal - find error message text endif externP _NMSG_WRITE ; pascal - write error message to stdout externP _FF_MSGBANNER ; pascal - error message banner ; (includes FORTRAN $DEBUG info) externP _setargv ; process command line arguments externP _setenvp ; process environment externP _nullcheck ; check for null assignment sBegin code assumes cs,code ifdef _QC public $F_PSP ; Segment of the fake PSP for QC $F_PSP dw 0 ; Must be in CODE segment for recovery. FirstTime db 1; ;[10] Flag to catch when the shell startup ;[10] code is executed. assumes ds,DGROUP ; DGROUP is setup and set by QC kernel assumes es,nothing else ;_QC assumes ds,nothing endif ;_QC page ;*** ;_astart - start of all C programs ; ;Purpose: ; Startup routine to initialize C run-time environment ; ;Entry: ; ;Exit: ; Exits to DOS via exit(). ; ;Uses: ; ;Exceptions: ; ;******************************************************************************* labelNP <PUBLIC,_astart> ; start address of all "C" programs ifndef _QC ; check MS-DOS version for 2.0 or later callos VERSION ; AX must be preserved until later cmp al,2 ; check for version 2 or later jae setup ; yes - continue with setup int 20h ; DOS 1.0 exit program setup: endif ;_QC ifdef _QC mov cs:[$F_PSP],es ; Save segment of fake PSP mov di,ds ; di = DGROUP mov si,es:[PSP_EOM_SEG] ; get max. paragraph else ;_QC mov di,DGROUP mov si,ds:[DOS_MAXPARA] ; get max. paragraph endif ;_QC sub si,di ; si = # para in data area cmp si,1000h ; if more than 64K jb setSP mov si,1000H ; use full 64K (-16) setSP: cli ; turn off interrupts mov ss,di ; SS = DGROUP add sp,dataoffset _end-2 ; 2 for _asizds limit sti ; turn interrupts back on jnc SPok ifndef IBMC20 push ss ; establish DS=DGROUP for pop ds ; _FF_MSGBANNER -> _NMSG_WRITE -> _NMSG_TEXT endif ; IBMC20 ifdef _QC ;[6] mov es,cs:[$F_PSP] ;[9] find 'fake' PSP mov es:[PSP_CRITERR], CE_EXEC; [9] Indicate an error to kernel jmp Far Ptr $Exec_Shell ; [9] else ;_QC ;[6] call _FF_MSGBANNER ; for "\r\nrun-time error " banner xor ax,ax ; stack overflow error message (0) push ax call _NMSG_WRITE mov ax,DOS_terminate shl 8 + 255 callos ; terminate process with 255 endif ;_QC ;[6] SPok: assumes ss,data and sp,not 1 ; make even (if not) mov [_abrktb].sz,sp ; top DS free location mov [_atopsp],sp ; save top of stack ifdef _QC mov bx,sp ; Setup up a EOS flag on stack. sub bx,es:[PSP_STK_SIZE] add bx,2+256 ;[1]add stack "slop" to keep 256 bytes mov [STKHQQ],bx mov Word Ptr [bx],5252h endif ;_QC mov ax,si ; si = # paragraphs mov cl,4 shl ax,cl dec ax mov [_asizds],ax ; save DS size - 1 (in bytes) ifdef _QC mov [__psp],es mov [__QCldata],di ; Low end of memory for pointer checking mov es,di ; es = DGROUP push es ;[7] save DGROUP mov ax,1 ;[7] Now look for high end call $GlobalLock ;[7] handle table mov es,ax ;[7] mov bx,0 ;[7] mov cx,128 ;[7] 128 entries in table mov ax,0 ;[7] current high ast1a: ;[7] cmp ax,es:[bx] ;[7] is this high jae ast1b ;[7] no mov ax,es:[bx] ;[7] yes - new high ast1b: add bx,2 ;[7] move to next entry loop ast1a ;[7] loop until done mov [_QChdata],ax ;[7] save as top of memory. pop es ;[7] restore es=DGROUP assumes es,DGROUP else ;_QC ; release extra space to DOS add si,di ; si = DGROUP + # para in DGROUP mov ds:[DOS_MAXPARA],si ; fix psp:2 mov bx,es ; bx = PSP base sub bx,si ; bx = - # para used neg bx callos setmem ; set memory block size mov [_psp],ds ; save psp:0 ; zero data areas (_BSS and c_common) push ss pop es assumes es,data cld ; set direction flag (up) mov di,dataOFFSET _edata ; beginning of bss area mov cx,dataOFFSET _end ; end of bss area sub cx,di xor ax,ax rep stosb ; zero bss ; C segmentation conventions set up here (DS=SS and CLD) push ss ; set up initial DS=ES=SS, CLD pop ds assumes ds,data endif ;_QC ; do necessary initialization BEFORE command line processing! call _cinit ; shared by DOS and Windows push ss pop ds ; ds = DGROUP assumes ds,data ; process command line and environment ifdef _QC ;[10] test [FirstTime],1 ;[10] Is this the first time? jz setenv2 ;[10] No - do the environment mov es,[_psp] ;[11] look at fake psp to see if test es:[PSP_KERNEL_FLGS],PSP_KF_BATCH;[11] in batch mode? jz setenv1 ;[11] yes - skip parsing env setenv2: call _setenvp ;[10] crack enviornment setenv1: ;[10] mov [FirstTime],0 ;[10] Never the first time again else ;_QC ;[10] call _setenvp ; crack environment endif ;_QC ;[10] call _setargv ; crack command line ; call main and exit xor bp,bp ; mark top stack frame for SYMDEB if sizeD push word ptr [environ+2] ; the environment is not always in DS endif push word ptr [environ] if sizeD push word ptr [__argv+2] ; the arguments are not always in DS endif push word ptr [__argv] push [__argc] ; argument count ifdef _QC mov es,[__psp] call Dword Ptr es:[PSP_MAIN_ENTRY] else ;_QC call main ; main ( argc , argv , envp ) endif ;_QC ; use whatever is in ax after returning here from the main program push ax call exit ; exit (AX) ; _exit will call terminators page ifdef _QC labelFP <PUBLIC,_QCnptr_error> ; [2] ; We know we were called far, so to get the 'best guess' cs:ip, we ; just set __QCrtcsip by looking at our return address. ; [2] mov ds,cs:[$F_PSP] ;[4] get PSP mov ax,ds:[PSP_DGROUP_SEG] ;[4] get DGROUP mov ds, ax ;[4] ds = DGROUP pop word ptr [__QCrtcsip] ;[2] Get offset pop word ptr [__QCrtcsip+2] ;[2] Get segment mov ax,12 ;Illegal Near pointer encountered. jmp short _amsg_exit labelFP <PUBLIC,_QCfptr_error> ; [2] ; We know we were called far, so to get the 'best guess' cs:ip, we ; just set __QCrtcsip by looking at our return address. ; [2] mov ds,cs:[$F_PSP] ;[4] get PSP mov ax,ds:[PSP_DGROUP_SEG] ;[4] get DGROUP mov ds, ax ;[4] ds = DGROUP pop word ptr [__QCrtcsip] ;[2] Get offset pop word ptr [__QCrtcsip+2] ;[2] Get segment mov ax,13 ;Illegal Far pointer encountered. jmp short _amsg_exit labelFP <PUBLIC,_Break_error> ; [2] ; We know we were called far, so to get the 'best guess' cs:ip, we ; just set __QCrtcsip by looking at our return address. ; [2] mov ds,cs:[$F_PSP] ;[4] get PSP mov ax,ds:[PSP_DGROUP_SEG] ;[4] get DGROUP mov ds, ax ;[4] ds = DGROUP pop word ptr [__QCrtcsip] ;[2] Get offset pop word ptr [__QCrtcsip+2] ;[2] Get segment mov ax,14 ;Control BREAK encountered. jmp short _amsg_exit endif ;_QC ;*** ;_amsg_exit, _cintDIV - Fast exit fatal errors ; ;Purpose: ; Exit the program with error code of 255 and appropriate error ; message. cintDIV is used for integer divide by zero, amsg_exit ; is for other run time errors. ; ;Entry: ; AX = error message number (amsg_exit only). ; ;Exit: ; calls exit() [cintDIV] or indirect through _aexit_rtn [amg_exit]. ; ;Uses: ; ;Exceptions: ; ;******************************************************************************* labelNP <PUBLIC,_cintDIV> ifndef IBMC20 assumes ds,nothing assumes ss,data endif ; IBMC20 ifdef IBMC20 ; _NMSG_WRITE will reestablish ds = DGROUP else ; _cintDIV establishes ds = DGROUP endif ; IBMC20 ifdef _QC ;[3] ; [3] ; We got here from an interrupt so the stack looks like: ; flags ; seg ; off ; So to get the CS:IP where it occurred, we just pop the seg and off from ; the stack (we don't need to get back...) ; [3] mov ds,cs:[$F_PSP] ;[4] get PSP mov ax,ds:[PSP_DGROUP_SEG] ;[4] get DGROUP mov ds, ax ;[4] ds = DGROUP pop word ptr [__QCrtcsip] ;[3] Offset pop word ptr [__QCrtcsip+2] ;[3] Segment else ifndef IBMC20 mov ax, DGROUP ; reestablish DS=DGROUP mov ds, ax endif ; IBMC20 endif ;_QC ;[3] mov ax,3 ; Integer divide by zero interrupt mov [_aexit_rtn],codeoffset _exit ; call high-level exit() ; to cause file buffer flushing labelNP <PUBLIC,_amsg_exit> ifdef _QC ;[2] push si ;[2] Is this necessary? push di ;[2] Is this necessary? mov si, ax ;[2] Save message number endif ;_QC ;[2] push ax ; message number for _NMSG_WRITE call _FF_MSGBANNER ; run-time error message banner call _NMSG_WRITE ; write error message to stdout ifdef _QC ;[2] mov es, cs:[$F_PSP] ;[2] Find 'fake' PSP mov es:[PSP_CRITERR], 255 ;[2] Indicate error to kernel mov ax, es:[PSP_ERRTAB_HDL] ;[2] Find error table handle or ax, ax ;[2] Does it exist? jnz _QC_0 ;[2] Yes, don't allocate mov ax, 26 * size error_struct ;[2] xor dx, dx ;[2] xor cx, cx ;[2] call $GlobalAlloc ;[2] or ax, ax ;[8] Check to see if we got it jz _QC_Fail ;[8] Allocation failed _QC_0: mov di, ax ;[2] Save handle call $GlobalLock ;[2] Handle should be in AX mov es, ax ;[2] Get seg of memory block mov ax, si ;[2] Get error number or ax, 8000h ;[2] Set high bit in case 0 mov es:[es_ecode], ax ;[2] mov ax, word ptr __QCrtcsip ;[2] mov es:[es_oln], ax ;[2] Save IP (kludge) mov ax, word ptr __QCrtcsip + 2 ;[2] mov es:[es_ob], ax ;[2] Save CS (kludge) push es ;[2] mov ax,255 ;[2] 'run-time error' msg push ax ;[2] call _NMSG_TEXT ;[2] Fetch msg push ax ;[2] call _AddString ;[2] Add to strings heap add sp, 2 ;[2] C-calling convention cmp ax, 0FFFFh ;[8] Error? jnz _QC_2 ;[8] No, continue pop es ;[8] jmp short _QC_Fail ;[8] _QC_2: ;[8] xchg si, ax ;[2] Save offset in heap push ax ;[2] Retrieve message number call _NMSG_TEXT ;[2] Return AX -> msg push ax ;[2] call _AddString ;[2] Add string to heap add sp, 2 ;[2] AddString is C-calling pop es ;[2] cmp ax, 0FFFFh ;[8] Error in AddString? jz _QC_Fail ;[8] Yes, fail mov es:[es_obString], si ;[2] Save offset of msg mov es, cs:[$F_PSP] ;[2] mov es:[PSP_ERRTAB_HDL], di ;[2] mov ax, di ;[2] Retrieve handle call $GlobalUnlock ;[2] jmp short _QC_1 ;[8] Exit cleanly _QC_Fail: ;[8] Failure code mov es, cs:[$F_PSP] ;[8] mov es:[PSP_CRITERR], CE_NOMEM ;[8] Note true error _QC_1: ;[8] Exit code pop di ;[2] Necessary? pop si ;[2] Necessary? endif ;_QC ;[2] assumes ds,data mov ax,255 push ax if sizeC push cs ; _exit is compiled far ; but called near endif call word ptr [_aexit_rtn] ; _exit(255) ordinarily ; (or exit(255) for div by 0) ; NEAR routine pointer sEnd end _astart ; start address
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.