Rev 1558 | Blame | Compare with Previous | Last modification | View Log | RSS feed
; WMINICRT - minimal runtime for (Open) Watcom C to generate DOS .COM files
;
; The default behaviour of the runtime is:
; - sets up a stack of 400H bytes
; - releases additional memory beyond stack to DOS
; - panics on initialization, if not enough memory for stack size
; - panics if running out of stack during execution
;
; WASM definitions:
; STACKSIZE=<value> define stack size other than 400h
; NOSTACKCHECK do not assemble __STK function
; STACKSTAT remember the minimum SP, maintained by __STK__
; and exported via _stack_low_.
;
; To build an executable without build- and linker scripts run:
; wasm startup.asm
; wcc -0 -bt=dos -ms -os -zl your_c_file.c
; wlink system dos com file startup,your_c_file
; To change the stack size, add -DSTACKSIZE=<value> to the wasm call
;
; To compile without stack checking:
; a) compile this startup file with wasm -DNOSTACKCHECK startup.asm
; b) call the C compiler with -s flag
;
; To build a debug version of your program:
; wasm -d1 startup.asm
; wcc -bt=dos -ms -d2 -zl your_c_file.c
; wlink system dos com debug all option map file startup,your_c_file
;
; TODO:
; - display stack statistics on program termination if STACKSTAT is enabled
; - proper Makefiles
; - many more (optional) things while keeping it small :-)
.8086
IFNDEF STACKSIZE
STACKSIZE = 400h
ENDIF
DGROUP group _TEXT,_DATA,CONST,CONST2,_BSS,_STACK
extrn "C",main : near
public _cstart_, _small_code_
_TEXT segment word public 'CODE'
org 100h
_small_code_ label near
_cstart_ proc
; DOS puts the COM program in the largest memory block it can find
; and sets SP to the end of this block. On top of that, it reserves
; the entire memory (not only the process' block) to the program, which
; makes it impossible to allocate memory or run child processes.
; for this reasons it is beneficial to resize the memory block we occupy
; into a more reasonable value
@verify_stack_size:
cmp sp,offset DGROUP:_stack_end_
ja @resize_mem
mov dx,offset @memerr
jmp _panic_
@memerr db 'MEMERR$'
; step 2: resize our memory block to sp bytes (ie. sp/16 paragraphs)
@resize_mem:
mov sp,offset DGROUP:_stack_end_
IFDEF STACKSTAT
mov [_stack_low_],sp
ENDIF STACKSTAT
mov ah,4ah
mov bx,sp
add bx,0fh
shr bx,1
shr bx,1
shr bx,1
shr bx,1
int 21h
; clear _BSS to be ANSI C conformant
mov di,offset DGROUP:_BSS
mov cx,offset DGROUP:_STACK
sub cx,di
shr cx,1
xor ax,ax
cld
rep stosw
call main
mov ah,4ch
int 21h
_cstart_ endp
_panic_ proc
; output error message in DX, then terminate with FF
mov ah,9
int 21h
mov ax,4cffh ; terminate if not enough stack space
int 21h
_panic_ endp
IFNDEF NOSTACKCHECK
public __STK
__STK proc
; ensures that we have enough stack space left. the needed bytes are
; given in AX. panics if stack low.
sub ax,sp ; subtract needed bytes from SP
neg ax ; SP-AX = -(AX-SP)
cmp ax,offset DGROUP:_STACK - 2 ; -2 is to compensate for __STK ret addr
jae @l1 ; enough stack => return, else panic
int 3 ; trap into debugger
mov dx,offset @stkerr
add sp,200h ; make sure we have enough stack to call DOS
jmp _panic_
@stkerr db 'STKERR$'
@l1:
IFDEF STACKSTAT ; update lowest stack pointer if statistics enabled
cmp [_stack_low_],ax
jbe @r
mov [_stack_low_],ax
ENDIF STACKSTAT
@r:
ret
__STK endp
ENDIF
_DATA segment word public 'DATA'
_DATA ends
CONST segment word public 'DATA'
CONST ends
CONST2 segment word public 'DATA'
CONST2 ends
_BSS segment word public 'BSS'
IFDEF STACKSTAT
public _stack_low_
_stack_low_: dw 1 dup(?)
ENDIF STACKSTAT
_BSS ends
; stack definition, available memory is checked at runtime
; defined as segment so that linker may detect .COM size overflow
_STACK segment para public 'TINY_STACK'
public _stack_end_
db STACKSIZE dup(?)
_stack_end_:
_STACK ends
_TEXT ends
end _cstart_