ReC98/libs/BorlandC/c0.asm

583 lines
20 KiB
NASM

; *Not* the original file, but an edit to turn it into an includable slice.
; Changes include:
; * removal of RULES.ASI to eliminate redundancy
; * data declarations being moved to a separate file
; * removal of the segment declarations (for obvious reasons)
; * additional NOPs before far calls to ensure binary compatibility to the
; original builds of the PC-98 Touhou games.
;[]------------------------------------------------------------[]
;| C0.ASM -- Start Up Code for DOS |
;[]------------------------------------------------------------[]
;
; C/C++ Run Time Library - Version 6.02
;
; Copyright (c) 1987, 1993 by Borland International
; All Rights Reserved.
;
SUBTTL Start Up Code
PAGE
;/* */
;/*-----------------------------------------------------*/
;/* */
;/* Start Up Code */
;/* ------------- */
;/* */
;/*-----------------------------------------------------*/
;/* */
PSPHigh equ 00002h
PSPEnv equ 0002ch
PSPCmd equ 00080h
public __AHINCR
public __AHSHIFT
__AHINCR equ 1000h
__AHSHIFT equ 12
IFDEF __NOFLOAT__
MINSTACK equ 128 ; minimal stack size in words
ELSE
MINSTACK equ 256 ; minimal stack size in words
ENDIF
;
; At the start, DS and ES both point to the segment prefix.
; SS points to the stack segment except in TINY model where
; SS is equal to CS
;
IFDEF __TINY__
ORG 100h
ENDIF
PUBLIC STARTX
STARTX PROC NEAR
; Save general information, such as :
; DGROUP segment address
; DOS version number
; Program Segment Prefix address
; Environment address
; Top of far heap
IFDEF __TINY__
mov dx, cs ; DX = GROUP Segment address
ELSE
mov dx, DGROUP ; DX = GROUP Segment address
ENDIF
mov cs:DGROUP@@, dx ;
mov ah, 30h
int 21h ; get DOS version number
mov bp, ds:[PSPHigh]; BP = Highest Memory Segment Addr
mov bx, ds:[PSPEnv] ; BX = Environment Segment address
mov ds, dx
mov _version@, ax ; Keep major and minor version number
mov _psp@, es ; Keep Program Segment Prefix address
mov _envseg@, bx ; Keep Environment Segment address
mov word ptr _heaptop@ + 2, bp
;
; Save several vectors and install default divide by zero handler.
;
call SaveVectors
; Count the number of environment variables and compute the size.
; Each variable is ended by a 0 and a zero-length variable stops
; the environment. The environment can NOT be greater than 32k.
mov ax, _envseg@
mov es, ax
xor ax, ax
mov bx, ax
mov di, ax
mov cx, 07FFFh ; Environment cannot be > 32 Kbytes
cld
@@EnvLoop:
repnz scasb
jcxz InitFailed ; Bad environment !!!
inc bx ; BX = Nb environment variables
cmp es:[di], al
jne @@EnvLoop ; Next variable ...
or ch, 10000000b
neg cx
mov _envLng@, cx ; Save Environment size
mov cx, dPtrSize / 2
shl bx, cl
add bx, dPtrSize * 4
and bx, not ((dPtrSize * 4) - 1)
mov _envSize@, bx ; Save Environment Variables Nb.
; Determine the amount of memory that we need to keep
IFDEF _DSSTACK_
mov dx, ds
ELSE
mov dx, ss
ENDIF
sub bp, dx ; BP = remaining size in paragraphs
IF LDATA
mov di, seg __stklen
mov es, di
mov di, es:__stklen ; DI = Requested stack size
ELSE
mov di, __stklen ; DI = Requested stack size
ENDIF
;
; Make sure that the requested stack size is at least MINSTACK words.
;
cmp di, 2*MINSTACK ; requested stack big enough ?
jae AskedStackOK
mov di, 2*MINSTACK ; no --> use minimal value
IF LDATA
mov es:__stklen, di ; override requested stack size
ELSE
mov __stklen, di ; override requested stack size
ENDIF
AskedStackOK label near
IFDEF _DSSTACK_
add di, offset DGROUP: edata@
jb InitFailed ; DATA segment can NOT be > 64 Kbytes
ENDIF
IF LDATA EQ false
add di, __heaplen
jb InitFailed ; DATA segment can NOT be > 64 Kbytes
ENDIF
mov cl, 4
shr di, cl ; $$$ Do not destroy CL $$$
inc di ; DI = DS size in paragraphs
cmp bp, di
IF LDATA EQ false
jb InitFailed ; Not enough memory
cmp __stklen, 0
je ExpandDS ; Expand DS up to 64 Kb
cmp __heaplen, 0
jne ExcessOfMemory ; Much more available than needed
ExpandDS label near
mov di, 1000h
cmp bp, di
ja ExcessOfMemory ; Enough to run the program
mov di, bp
jmp short ExcessOfMemory ; Enough to run the program
ELSE
jnb ExcessOfMemory ; Much more available than needed
ENDIF
; All initialization errors arrive here
InitFailed label near
if LDATA
nop ; Touhou PC-98 compatibility
endif
call __abort
; Return to DOS the amount of memory in excess
; Set far heap base and pointer
ExcessOfMemory label near
mov bx, di
add bx, dx
mov word ptr _heapbase@ + 2, bx
mov word ptr _brklvl@ + 2, bx
mov ax, _psp@
sub bx, ax ; BX = Number of paragraphs to keep
mov es, ax ; ES = Program Segment Prefix address
mov ah, 04Ah
push di ; preserve DI
int 021h ; this call clobbers SI,DI,BP !!!!!!
pop di ; restore DI
shl di, cl ; $$$ CX is still equal to 4 $$$
cli ; req'd for pre-1983 88/86s
mov ss, dx ; Set the program stack
mov sp, di
sti
IFNDEF _DSSTACK_
mov ax, seg __stklen
mov es, ax
mov es:__stklen, di ; If separate stack segment, save size
ENDIF
IFNDEF __HUGE__
; Reset uninitialized data area
xor ax, ax
mov es, cs:DGROUP@@
mov di, offset DGROUP: bdata@
mov cx, offset DGROUP: edata@
sub cx, di
cld
rep stosb
ENDIF
; If default number of file handles have changed then tell DOS
cmp __nfile, 20
jbe @@NoChange
cmp _osmajor@, 3 ; Check for >= DOS 3.3
jb @@NoChange
ja @@DoChange
cmp _osminor@, 1Eh
jb @@NoChange
@@DoChange:
mov ax, 5801h ; Set last fit allocation
mov bx, 2
int 21h
jc @@BadInit
mov ah, 67h ; Expand handle table
mov bx, __nfile
int 21h
jc @@BadInit
mov ah, 48h ; Allocate 16 bytes to find new
mov bx, 1 ; top of memory address
int 21h
jc @@BadInit
inc ax ; Adjust address to point after block
mov word ptr _heaptop@ + 2, ax
dec ax ; Change back and release block
mov es, ax
mov ah, 49h
int 21h
jc @@BadInit
mov ax, 5801h ; Set first fit allocation
mov bx, 0
int 21h
jnc @@NoChange
@@BadInit:
if LDATA
nop ; Touhou PC-98 compatibility
endif
call __abort
@@NoChange:
; Prepare main arguments
xor bp,bp ; set BP to 0 for overlay mgr
IFNDEF __TINY__
push bp
nop ; Touhou PC-98 compatibility
call __ExceptInit
pop ax
ENDIF
mov es, cs:DGROUP@@
mov si,offset DGROUP:InitStart ;si = start of table
mov di,offset DGROUP:InitEnd ;di = end of table
call Initialize
; ExitCode = main(argc,argv,envp);
IF LDATA
push word ptr __C0environ+2
push word ptr __C0environ
push word ptr __C0argv+2
push word ptr __C0argv
ELSE
push word ptr __C0environ
push word ptr __C0argv
ENDIF
push __C0argc
call _main
; Flush and close streams and files
push ax
if LDATA
nop ; Touhou PC-98 compatibility
endif
call _exit
;---------------------------------------------------------------------------
; _cleanup() call all #pragma exit cleanup routines.
; _checknull() check for null pointer zapping copyright message
; _terminate(int) exit program with error code
;
; These functions are called by exit(), _exit(), _cexit(),
; and _c_exit().
;---------------------------------------------------------------------------
; Call cleanup routines
__cleanup PROC DIST
PUBLIC __cleanup
mov es, cs:DGROUP@@
push si
push di
mov si,offset DGROUP:ExitStart
mov di,offset DGROUP:ExitEnd
call Cleanup
pop di
pop si
ret
__cleanup ENDP
; Check for null pointers before exit
__checknull PROC DIST
PUBLIC __checknull
IF LDATA EQ false
IFNDEF __TINY__
push si
push di
mov es, cs:DGROUP@@
xor ax, ax
mov si, ax
mov cx, 16
ComputeChecksum label near
add al, es:[si]
adc ah, 0
inc si
loop ComputeChecksum
sub ax, CheckSum
jz @@SumOK
mov dx, offset DGROUP:NullCheck
pushDS_
push dx
if LDATA
nop ; Touhou PC-98 compatibility
endif
call ___ErrorMessage
pop dx
popDS_
@@SumOK: pop di
pop si
ENDIF
ENDIF
ret
__checknull ENDP
; Exit to DOS
__terminate PROC DIST
PUBLIC __terminate
mov bp,sp
mov ah,4Ch
mov al,[bp+cPtrSize]
int 21h ; Exit to DOS
__terminate ENDP
STARTX ENDP
SUBTTL Vector save/restore & default Zero divide routines
PAGE
;[]------------------------------------------------------------[]
;| |
;| Interrupt Save/Restore routines and default divide by zero |
;| handler. |
;| |
;[]------------------------------------------------------------[]
ZeroDivision PROC FAR
mov dx, offset DGROUP:ZeroDivMSG
pushDS_
push dx
if LDATA
nop ; Touhou PC-98 compatibility
endif
call ___ErrorMessage
pop dx
popDS_
mov ax, 3
push ax
if LDATA
nop ; Touhou PC-98 compatibility
endif
call __exit ; _exit(3);
ZeroDivision ENDP
;--------------------------------------------------------------------------
; savevectors()
;
; Save vectors for 0, 4, 5 & 6 interrupts. This is for extended
; signal()/raise() support as the signal functions can steal these
; vectors during runtime.
;--------------------------------------------------------------------------
SaveVectors PROC NEAR
push ds
; Save INT 0
mov ax, 3500h
int 021h
mov word ptr _Int0Vector@, bx
mov word ptr _Int0Vector@+2, es
; Save INT 4
mov ax, 3504h
int 021h
mov word ptr _Int4Vector@, bx
mov word ptr _Int4Vector@+2, es
; Save INT 5
mov ax, 3505h
int 021h
mov word ptr _Int5Vector@, bx
mov word ptr _Int5Vector@+2, es
; Save INT 6
mov ax, 3506h
int 021h
mov word ptr _Int6Vector@, bx
mov word ptr _Int6Vector@+2, es
;
; Install default divide by zero handler.
;
mov ax, 2500h
mov dx, cs
mov ds, dx
mov dx, offset ZeroDivision
int 21h
pop ds
ret
SaveVectors ENDP
;--------------------------------------------------------------------------
; _restorezero() puts back all the vectors that SaveVectors took.
;
;NOTE : TSRs must BE AWARE that signal() functions which take these
; vectors will be deactivated if the keep() function is executed.
; If a TSR wants to use the signal functions when it is active it
; will have to save/restore these vectors itself when activated and
; deactivated.
;--------------------------------------------------------------------------
__restorezero PROC DIST
PUBLIC __restorezero
IFDEF __HUGE__
push ds
mov ds, cs: DGROUP@@
ENDIF
push ds
mov ax, 2500h
lds dx, _Int0Vector@
int 21h
pop ds
push ds
mov ax, 2504h
lds dx, _Int4Vector@
int 21h
pop ds
push ds
mov ax, 2505h
lds dx, _Int5Vector@
int 21h
pop ds
IFNDEF __HUGE__
push ds
ENDIF
mov ax, 2506h
lds dx, _Int6Vector@
int 21h
pop ds
ret
ENDP
;------------------------------------------------------------------
; Loop through a startup/exit (SE) table,
; calling functions in order of priority.
; ES:SI is assumed to point to the beginning of the SE table
; ES:DI is assumed to point to the end of the SE table
; First 64 priorities are reserved by Borland
;------------------------------------------------------------------
PNEAR EQU 0
PFAR EQU 1
NOTUSED EQU 0ffh
SE STRUC
calltype db ? ; 0=near,1=far,ff=not used
priority db ? ; 0=highest,ff=lowest
addrlow dw ?
addrhigh dw ?
SE ENDS
Initialize proc near
@@Start: mov ax,100h ;start with lowest priority
mov dx,di ;set sentinel to end of table
mov bx,si ;bx = start of table
@@TopOfTable: cmp bx,di ;and the end of the table?
je @@EndOfTable ;yes, exit the loop
cmp es:[bx.calltype],NOTUSED;check the call type
je @@Next
mov cl, es:[bx.priority] ;move priority to CX
xor ch, ch
cmp cx,ax ;check the priority
jae @@Next ;too high? skip
mov ax,cx ;keep priority
mov dx,bx ;keep index in dx
@@Next: add bx,SIZE SE ;bx = next item in table
jmp @@TopOfTable
@@EndOfTable: cmp dx,di ;did we exhaust the table?
je @@Done ;yes, quit
mov bx,dx ;bx = highest priority item
cmp es:[bx.calltype],PNEAR ;is it near or far?
mov es:[bx.calltype],NOTUSED;wipe the call type
push es ;save es
je @@NearCall
@@FarCall: call DWORD PTR es:[bx.addrlow]
pop es ;restore es
jmp short @@Start
@@NearCall: call WORD PTR es:[bx.addrlow]
pop es ;restore es
jmp short @@Start
@@Done: ret
endp
Cleanup proc near
@@Start: mov ah,0 ;start with highest priority
mov dx,di ;set sentinel to end of table
mov bx,si ;bx = start of table
@@TopOfTable: cmp bx,di ;and the end of the table?
je @@EndOfTable ;yes, exit the loop
cmp es:[bx.calltype],NOTUSED;check the call type
je @@Next
cmp es:[bx.priority],ah ;check the priority
jb @@Next ;too low? skip
mov ah,es:[bx.priority] ;keep priority
mov dx,bx ;keep index in dx
@@Next: add bx,SIZE SE ;bx = next item in table
jmp @@TopOfTable
@@EndOfTable: cmp dx,di ;did we exhaust the table?
je @@Done ;yes, quit
mov bx,dx ;bx = highest priority item
cmp es:[bx.calltype],PNEAR ;is it near or far?
mov es:[bx.calltype],NOTUSED;wipe the call type
push es ;save es
je @@NearCall
@@FarCall: call DWORD PTR es:[bx.addrlow]
pop es ;restore es
jmp short @@Start
@@NearCall: call WORD PTR es:[bx.addrlow]
pop es ;restore es
jmp short @@Start
@@Done: ret
endp
;------------------------------------------------------------------
; The DGROUP@ variable is used to reload DS with DGROUP
PubSym@ DGROUP@, <dw ?>, __PASCAL__
; __MMODEL is used to determine the memory model or the default
; pointer types at run time.
public __MMODEL
__MMODEL dw MMODEL