|
|
1.1 root 1: page ,132
2: TITLE crt0 - C start up routine
3: ;***
4: ;crt0.asm - C start up routine
5: ;
6: ; Copyright (c) 1985-1987, Microsoft Corporation. All rights reserved.
7: ;
8: ;Purpose:
9: ; How startup works in a few words -
10: ;
11: ; The startup and termination is performed by a few modules
12: ;
13: ; crt0.asm DOS 2.x/3.x specific init/term
14: ; crt0msg.asm DOS 2.x/3.x error messages
15: ; (winstart.asm) Windows specific init/term (not included)
16: ;
17: ; crt0dat.asm remainder of shared DOS 3.x init/term
18: ;
19: ; ************* IMPORTANT *****************************************
20: ;
21: ; If the user reassembles this module, he will need to link using the
22: ; /DOSSEG switch or run the the DOSSEG.EXE program on crt0.obj, i.e.
23: ;
24: ; dosseg crt0.obj
25: ;
26: ; See the C documentation for more information about the /DOSSEG switch.
27: ;
28: ; All assembler modules must be assembled with the /mx switch, i.e.
29: ;
30: ; masm crt0/mx;
31: ;
32: ;*******************************************************************************
33:
34:
35: ?DF= 1 ; this is special for c startup
36: include version.inc
37: .xlist
38: ifdef _QC ;[5] move includes out of version
39: include \sl\qc\src\include\kernel.inc ;[5]
40: include \sl\qc\src\include\kmac.inc ;[5]
41: CEDEF macro name, num, msg ;;[8]
42: name equ num ;;[8]
43: endm ;;[8]
44: include \sl\qc\src\include\criterr.inc ;[8]
45: endif ;_QC ;[5]
46:
47: include cmacros.inc
48: include msdos.inc
49: include brkctl.inc
50: .list
51:
52: ifdef _QC ;[2]
53: extrn $Exec_Shell:FAR ;[9]
54: extrn $GlobalAlloc:FAR ;[2]
55: extrn $GlobalLock:FAR ;[2]
56: extrn $GlobalUnlock:FAR ;[2]
57: extrn _AddString:FAR ;[2]
58: endif ;_QC ;[2]
59:
60: page
61: ;===========================================================================
62: ;
63: ; Segment definitions
64: ;
65: ; The segment order is essentially the same as in XENIX.
66: ; This module is edited after assembly to contain a dosseg comment
67: ; record for the linker.
68: ;
69: ;===========================================================================
70:
71: createSeg _TEXT, code, word, public, CODE, <>
72: createSeg C_ETEXT,etext, word, public, ENDCODE,<>
73:
74: createSeg _DATA, data, word, public, DATA, DGROUP
75: createSeg STACK, stack, para, stack, STACK, DGROUP
76:
77: defGrp DGROUP ; define DGROUP
78:
79: codeOFFSET equ offset _TEXT:
80: dataOFFSET equ offset DGROUP:
81:
82: page
83:
84: public __acrtused ; trick to force in startup
85: __acrtused = 9876h ; funny value not easily matched in SYMDEB
86:
87: extrn __acrtmsg:abs ; trick to pull in startup messages
88:
89:
90: ifndef _QC
91: sBegin stack
92: assumes ds,data
93: db 2048 dup (?) ; default stack size
94: sEnd
95: endif ;_QC
96:
97: page
98:
99: ifndef _QC
100: externP main ; C main program
101: endif ;_QC
102:
103: externP exit ; exit ( code )
104:
105: if sizeC
106: extrn __exit:far ; _exit ( code) (cmacros name conflict)
107: else
108: extrn __exit:near
109: endif
110:
111: sBegin data
112:
113: extrn _edata:byte ; end of data (start of bss)
114: extrn _end:byte ; end of bss (start of stack)
115:
116: externW _psp ; psp:0 (paragraph #)
117: externW __argc
118: externDP __argv
119: externDP environ
120:
121: ifdef _QC
122: externW _QChdata ; High water mark of data space
123: globalW _QCldata,0 ; begining of DGroup (low water mark)
124:
125: extrn STKHQQ:WORD
126:
127: extrn __QCrtcsip:DWORD ;[2]
128: endif ;_QC
129:
130: ; these are used by DOS C memory management (not used in Windows)
131:
132: globalW _asizds,0 ; DS size (in bytes)
133: globalW _atopsp,0 ; top of stack (heap bottom)
134: globalW _aexit_rtn,<codeoffset __exit> ; NEAR pointer
135:
136: labelW <PUBLIC,_abrktb> ; segment table for brkctl
137: dw ?
138: dw DGROUP
139: db (MAXSEG-1) * (size segrec) dup (?)
140:
141: labelW <PUBLIC,_abrktbe>
142: globalW _abrkp,<dataoffset _abrktb>
143:
144: sEnd
145:
146: page
147:
148:
149: externP _cinit ; run-time initializers
150:
151: ifdef _QC
152: externP _NMSG_TEXT ; pascal - find error message text
153: endif
154: externP _NMSG_WRITE ; pascal - write error message to stdout
155: externP _FF_MSGBANNER ; pascal - error message banner
156: ; (includes FORTRAN $DEBUG info)
157:
158: externP _setargv ; process command line arguments
159: externP _setenvp ; process environment
160: externP _nullcheck ; check for null assignment
161:
162:
163: sBegin code
164: assumes cs,code
165:
166: ifdef _QC
167:
168: public $F_PSP ; Segment of the fake PSP for QC
169: $F_PSP dw 0 ; Must be in CODE segment for recovery.
170: FirstTime db 1; ;[10] Flag to catch when the shell startup
171: ;[10] code is executed.
172:
173: assumes ds,DGROUP ; DGROUP is setup and set by QC kernel
174: assumes es,nothing
175: else ;_QC
176: assumes ds,nothing
177: endif ;_QC
178:
179: page
180: ;***
181: ;_astart - start of all C programs
182: ;
183: ;Purpose:
184: ; Startup routine to initialize C run-time environment
185: ;
186: ;Entry:
187: ;
188: ;Exit:
189: ; Exits to DOS via exit().
190: ;
191: ;Uses:
192: ;
193: ;Exceptions:
194: ;
195: ;*******************************************************************************
196:
197: labelNP <PUBLIC,_astart> ; start address of all "C" programs
198:
199: ifndef _QC
200: ; check MS-DOS version for 2.0 or later
201:
202: callos VERSION ; AX must be preserved until later
203: cmp al,2 ; check for version 2 or later
204: jae setup ; yes - continue with setup
205: int 20h ; DOS 1.0 exit program
206:
207: setup:
208: endif ;_QC
209:
210: ifdef _QC
211: mov cs:[$F_PSP],es ; Save segment of fake PSP
212:
213: mov di,ds ; di = DGROUP
214: mov si,es:[PSP_EOM_SEG] ; get max. paragraph
215: else ;_QC
216: mov di,DGROUP
217: mov si,ds:[DOS_MAXPARA] ; get max. paragraph
218: endif ;_QC
219:
220: sub si,di ; si = # para in data area
221: cmp si,1000h ; if more than 64K
222: jb setSP
223:
224: mov si,1000H ; use full 64K (-16)
225:
226: setSP:
227:
228: cli ; turn off interrupts
229: mov ss,di ; SS = DGROUP
230: add sp,dataoffset _end-2 ; 2 for _asizds limit
231: sti ; turn interrupts back on
232: jnc SPok
233:
234: ifndef IBMC20
235: push ss ; establish DS=DGROUP for
236: pop ds ; _FF_MSGBANNER -> _NMSG_WRITE -> _NMSG_TEXT
237: endif ; IBMC20
238: ifdef _QC ;[6]
239: mov es,cs:[$F_PSP] ;[9] find 'fake' PSP
240: mov es:[PSP_CRITERR], CE_EXEC; [9] Indicate an error to kernel
241: jmp Far Ptr $Exec_Shell ; [9]
242:
243: else ;_QC ;[6]
244: call _FF_MSGBANNER ; for "\r\nrun-time error " banner
245: xor ax,ax ; stack overflow error message (0)
246: push ax
247: call _NMSG_WRITE
248: mov ax,DOS_terminate shl 8 + 255
249: callos ; terminate process with 255
250: endif ;_QC ;[6]
251:
252: SPok:
253: assumes ss,data
254:
255: and sp,not 1 ; make even (if not)
256: mov [_abrktb].sz,sp ; top DS free location
257: mov [_atopsp],sp ; save top of stack
258:
259: ifdef _QC
260: mov bx,sp ; Setup up a EOS flag on stack.
261: sub bx,es:[PSP_STK_SIZE]
262: add bx,2+256 ;[1]add stack "slop" to keep 256 bytes
263: mov [STKHQQ],bx
264: mov Word Ptr [bx],5252h
265: endif ;_QC
266:
267: mov ax,si ; si = # paragraphs
268: mov cl,4
269: shl ax,cl
270: dec ax
271: mov [_asizds],ax ; save DS size - 1 (in bytes)
272:
273: ifdef _QC
274: mov [__psp],es
275:
276: mov [__QCldata],di ; Low end of memory for pointer checking
277:
278: mov es,di ; es = DGROUP
279: push es ;[7] save DGROUP
280:
281: mov ax,1 ;[7] Now look for high end
282: call $GlobalLock ;[7] handle table
283: mov es,ax ;[7]
284: mov bx,0 ;[7]
285: mov cx,128 ;[7] 128 entries in table
286: mov ax,0 ;[7] current high
287:
288: ast1a: ;[7]
289: cmp ax,es:[bx] ;[7] is this high
290: jae ast1b ;[7] no
291: mov ax,es:[bx] ;[7] yes - new high
292: ast1b:
293: add bx,2 ;[7] move to next entry
294: loop ast1a ;[7] loop until done
295:
296: mov [_QChdata],ax ;[7] save as top of memory.
297: pop es ;[7] restore es=DGROUP
298: assumes es,DGROUP
299:
300: else ;_QC
301:
302: ; release extra space to DOS
303:
304: add si,di ; si = DGROUP + # para in DGROUP
305: mov ds:[DOS_MAXPARA],si ; fix psp:2
306: mov bx,es ; bx = PSP base
307: sub bx,si ; bx = - # para used
308: neg bx
309: callos setmem ; set memory block size
310: mov [_psp],ds ; save psp:0
311:
312: ; zero data areas (_BSS and c_common)
313:
314: push ss
315: pop es
316: assumes es,data
317:
318: cld ; set direction flag (up)
319: mov di,dataOFFSET _edata ; beginning of bss area
320: mov cx,dataOFFSET _end ; end of bss area
321: sub cx,di
322: xor ax,ax
323: rep stosb ; zero bss
324:
325: ; C segmentation conventions set up here (DS=SS and CLD)
326:
327: push ss ; set up initial DS=ES=SS, CLD
328: pop ds
329: assumes ds,data
330: endif ;_QC
331:
332: ; do necessary initialization BEFORE command line processing!
333:
334: call _cinit ; shared by DOS and Windows
335:
336: push ss
337: pop ds ; ds = DGROUP
338: assumes ds,data
339:
340: ; process command line and environment
341:
342: ifdef _QC ;[10]
343: test [FirstTime],1 ;[10] Is this the first time?
344: jz setenv2 ;[10] No - do the environment
345: mov es,[_psp] ;[11] look at fake psp to see if
346: test es:[PSP_KERNEL_FLGS],PSP_KF_BATCH;[11] in batch mode?
347: jz setenv1 ;[11] yes - skip parsing env
348: setenv2:
349: call _setenvp ;[10] crack enviornment
350: setenv1: ;[10]
351: mov [FirstTime],0 ;[10] Never the first time again
352: else ;_QC ;[10]
353: call _setenvp ; crack environment
354: endif ;_QC ;[10]
355: call _setargv ; crack command line
356:
357: ; call main and exit
358:
359: xor bp,bp ; mark top stack frame for SYMDEB
360:
361: if sizeD
362: push word ptr [environ+2] ; the environment is not always in DS
363: endif
364: push word ptr [environ]
365:
366: if sizeD
367: push word ptr [__argv+2] ; the arguments are not always in DS
368: endif
369: push word ptr [__argv]
370:
371: push [__argc] ; argument count
372:
373: ifdef _QC
374: mov es,[__psp]
375: call Dword Ptr es:[PSP_MAIN_ENTRY]
376: else ;_QC
377: call main ; main ( argc , argv , envp )
378: endif ;_QC
379:
380: ; use whatever is in ax after returning here from the main program
381:
382: push ax
383: call exit ; exit (AX)
384: ; _exit will call terminators
385:
386: page
387: ifdef _QC
388: labelFP <PUBLIC,_QCnptr_error>
389:
390: ; [2]
391: ; We know we were called far, so to get the 'best guess' cs:ip, we
392: ; just set __QCrtcsip by looking at our return address.
393: ; [2]
394:
395: mov ds,cs:[$F_PSP] ;[4] get PSP
396: mov ax,ds:[PSP_DGROUP_SEG] ;[4] get DGROUP
397: mov ds, ax ;[4] ds = DGROUP
398:
399: pop word ptr [__QCrtcsip] ;[2] Get offset
400: pop word ptr [__QCrtcsip+2] ;[2] Get segment
401:
402: mov ax,12 ;Illegal Near pointer encountered.
403: jmp short _amsg_exit
404:
405: labelFP <PUBLIC,_QCfptr_error>
406:
407: ; [2]
408: ; We know we were called far, so to get the 'best guess' cs:ip, we
409: ; just set __QCrtcsip by looking at our return address.
410: ; [2]
411:
412: mov ds,cs:[$F_PSP] ;[4] get PSP
413: mov ax,ds:[PSP_DGROUP_SEG] ;[4] get DGROUP
414: mov ds, ax ;[4] ds = DGROUP
415:
416: pop word ptr [__QCrtcsip] ;[2] Get offset
417: pop word ptr [__QCrtcsip+2] ;[2] Get segment
418:
419: mov ax,13 ;Illegal Far pointer encountered.
420: jmp short _amsg_exit
421:
422: labelFP <PUBLIC,_Break_error>
423:
424: ; [2]
425: ; We know we were called far, so to get the 'best guess' cs:ip, we
426: ; just set __QCrtcsip by looking at our return address.
427: ; [2]
428:
429: mov ds,cs:[$F_PSP] ;[4] get PSP
430: mov ax,ds:[PSP_DGROUP_SEG] ;[4] get DGROUP
431: mov ds, ax ;[4] ds = DGROUP
432:
433: pop word ptr [__QCrtcsip] ;[2] Get offset
434: pop word ptr [__QCrtcsip+2] ;[2] Get segment
435:
436: mov ax,14 ;Control BREAK encountered.
437: jmp short _amsg_exit
438: endif ;_QC
439:
440: ;***
441: ;_amsg_exit, _cintDIV - Fast exit fatal errors
442: ;
443: ;Purpose:
444: ; Exit the program with error code of 255 and appropriate error
445: ; message. cintDIV is used for integer divide by zero, amsg_exit
446: ; is for other run time errors.
447: ;
448: ;Entry:
449: ; AX = error message number (amsg_exit only).
450: ;
451: ;Exit:
452: ; calls exit() [cintDIV] or indirect through _aexit_rtn [amg_exit].
453: ;
454: ;Uses:
455: ;
456: ;Exceptions:
457: ;
458: ;*******************************************************************************
459:
460: labelNP <PUBLIC,_cintDIV>
461:
462: ifndef IBMC20
463: assumes ds,nothing
464: assumes ss,data
465:
466: endif ; IBMC20
467:
468: ifdef IBMC20
469: ; _NMSG_WRITE will reestablish ds = DGROUP
470: else
471: ; _cintDIV establishes ds = DGROUP
472: endif ; IBMC20
473:
474: ifdef _QC ;[3]
475: ; [3]
476: ; We got here from an interrupt so the stack looks like:
477: ; flags
478: ; seg
479: ; off
480: ; So to get the CS:IP where it occurred, we just pop the seg and off from
481: ; the stack (we don't need to get back...)
482: ; [3]
483:
484: mov ds,cs:[$F_PSP] ;[4] get PSP
485: mov ax,ds:[PSP_DGROUP_SEG] ;[4] get DGROUP
486: mov ds, ax ;[4] ds = DGROUP
487:
488: pop word ptr [__QCrtcsip] ;[3] Offset
489: pop word ptr [__QCrtcsip+2] ;[3] Segment
490: else
491: ifndef IBMC20
492: mov ax, DGROUP ; reestablish DS=DGROUP
493: mov ds, ax
494: endif ; IBMC20
495: endif ;_QC ;[3]
496:
497: mov ax,3 ; Integer divide by zero interrupt
498: mov [_aexit_rtn],codeoffset _exit ; call high-level exit()
499: ; to cause file buffer flushing
500:
501: labelNP <PUBLIC,_amsg_exit>
502:
503: ifdef _QC ;[2]
504: push si ;[2] Is this necessary?
505: push di ;[2] Is this necessary?
506: mov si, ax ;[2] Save message number
507: endif ;_QC ;[2]
508:
509: push ax ; message number for _NMSG_WRITE
510: call _FF_MSGBANNER ; run-time error message banner
511: call _NMSG_WRITE ; write error message to stdout
512:
513: ifdef _QC ;[2]
514: mov es, cs:[$F_PSP] ;[2] Find 'fake' PSP
515: mov es:[PSP_CRITERR], 255 ;[2] Indicate error to kernel
516:
517: mov ax, es:[PSP_ERRTAB_HDL] ;[2] Find error table handle
518: or ax, ax ;[2] Does it exist?
519: jnz _QC_0 ;[2] Yes, don't allocate
520:
521: mov ax, 26 * size error_struct ;[2]
522: xor dx, dx ;[2]
523: xor cx, cx ;[2]
524: call $GlobalAlloc ;[2]
525: or ax, ax ;[8] Check to see if we got it
526: jz _QC_Fail ;[8] Allocation failed
527:
528: _QC_0:
529: mov di, ax ;[2] Save handle
530: call $GlobalLock ;[2] Handle should be in AX
531: mov es, ax ;[2] Get seg of memory block
532:
533: mov ax, si ;[2] Get error number
534: or ax, 8000h ;[2] Set high bit in case 0
535: mov es:[es_ecode], ax ;[2]
536: mov ax, word ptr __QCrtcsip ;[2]
537: mov es:[es_oln], ax ;[2] Save IP (kludge)
538: mov ax, word ptr __QCrtcsip + 2 ;[2]
539: mov es:[es_ob], ax ;[2] Save CS (kludge)
540:
541: push es ;[2]
542:
543: mov ax,255 ;[2] 'run-time error' msg
544: push ax ;[2]
545: call _NMSG_TEXT ;[2] Fetch msg
546: push ax ;[2]
547: call _AddString ;[2] Add to strings heap
548: add sp, 2 ;[2] C-calling convention
549: cmp ax, 0FFFFh ;[8] Error?
550: jnz _QC_2 ;[8] No, continue
551:
552: pop es ;[8]
553: jmp short _QC_Fail ;[8]
554:
555: _QC_2: ;[8]
556: xchg si, ax ;[2] Save offset in heap
557: push ax ;[2] Retrieve message number
558: call _NMSG_TEXT ;[2] Return AX -> msg
559: push ax ;[2]
560: call _AddString ;[2] Add string to heap
561: add sp, 2 ;[2] AddString is C-calling
562:
563: pop es ;[2]
564: cmp ax, 0FFFFh ;[8] Error in AddString?
565: jz _QC_Fail ;[8] Yes, fail
566: mov es:[es_obString], si ;[2] Save offset of msg
567:
568: mov es, cs:[$F_PSP] ;[2]
569: mov es:[PSP_ERRTAB_HDL], di ;[2]
570:
571: mov ax, di ;[2] Retrieve handle
572: call $GlobalUnlock ;[2]
573: jmp short _QC_1 ;[8] Exit cleanly
574:
575: _QC_Fail: ;[8] Failure code
576: mov es, cs:[$F_PSP] ;[8]
577: mov es:[PSP_CRITERR], CE_NOMEM ;[8] Note true error
578:
579: _QC_1: ;[8] Exit code
580: pop di ;[2] Necessary?
581: pop si ;[2] Necessary?
582: endif ;_QC ;[2]
583:
584: assumes ds,data
585:
586: mov ax,255
587: push ax
588: if sizeC
589: push cs ; _exit is compiled far
590: ; but called near
591: endif
592: call word ptr [_aexit_rtn] ; _exit(255) ordinarily
593: ; (or exit(255) for div by 0)
594: ; NEAR routine pointer
595:
596: sEnd
597: end _astart ; start address
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.