- BORLAND/: Borland C++ 4.52 (chosen over 4.5 by byte-match: CODE/RP/CW32.LIB
is identical to 4.52's install lib). BCC32/TLINK32/TLIB/MAKE run natively on
Win11; CODE/BT/OPT.MAK is the shipped BTL4OPT.EXE's exact flag recipe
(extender = Borland PowerPack DPMI32, not Phar Lap TNT).
- restoration/source410/: the literal 1995-form reconstruction of the missing
BT game source (never mixed into CODE/). Round 1-3 state:
* 6 of 10 surviving original TUs COMPILE CLEAN under the period toolchain
(BTMSSN, BTCNSL, BTSCNRL, BTTEAM, BTL4MODE, BTL4ARND) - first builds
since 1996.
* BT_L4/BTL4APP.CPP pilot reconstruction: 12/12 functions, Fail() lands on
its binary-recorded line 400 exactly.
* BT/BTCNSL.HPP: console wire IDs recovered from the binary's ctors
(Killed=9, Damaged=10, ScoreUpdate=13, DeathWithoutHonor=15 [T1];
TeamScore=12 flagged [T4]).
* MUNGA/: 8 engine-header backfills back-dated from the BT412 WinTesla tree
(VDATA numbering decomp-verified; AUDREND's OpenAL-era virtual removed -
the period compiler is the drift detector).
* Tooling: backdate.py (WinTesla->1995 header transform), compile410.sh
(per-TU verification sweep under authentic OPT.MAK flags).
* README: corrected roadmap - MECH.HPP is the capstone grown with the mech
TU reconstructions; BTREG.CPP green = the header-family milestone.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
449 lines
12 KiB
NASM
449 lines
12 KiB
NASM
;[]------------------------------------------------------------[]
|
|
;| C0W.ASM -- Start Up Code For Windows |
|
|
;[]------------------------------------------------------------[]
|
|
|
|
;
|
|
; C/C++ Run Time Library - Version 6.5
|
|
;
|
|
; Copyright (c) 1991, 1994 by Borland International
|
|
; All Rights Reserved.
|
|
;
|
|
|
|
locals
|
|
|
|
__C0__ = 1
|
|
include RULES.ASI
|
|
|
|
ASSUME CS:_TEXT, DS:DGROUP
|
|
|
|
public __acrtused ;satisfy MS for now
|
|
__acrtused equ 0
|
|
IFNDEF __DPMI16__
|
|
public __WINMAINCALL ; used internally
|
|
ENDIF
|
|
public __INITAPPCALLED ; used internally
|
|
|
|
IFNDEF __DPMI16__
|
|
extrn WINMAIN:DIST
|
|
ENDIF
|
|
|
|
extrn INITAPP:far
|
|
extrn INITTASK:far
|
|
extrn FATALEXIT:far
|
|
extrn WAITEVENT:far
|
|
extrn LOCKSEGMENT:far
|
|
extrn UNLOCKSEGMENT:far
|
|
extrn GETWINFLAGS:far
|
|
IFDEF __DPMI16__
|
|
extrn _main:DIST
|
|
extrn _OkToInitWindowsRTL:DIST
|
|
extrn __abort:DIST
|
|
ENDIF
|
|
extrn _exit:DIST
|
|
extrn __exit:DIST
|
|
extrn __exitbuf:DIST
|
|
extrn __exitfopen:DIST
|
|
extrn __exitopen:DIST
|
|
extrn __setupio:near ;required!
|
|
extrn __ExceptInit:DIST
|
|
|
|
public __DestructorCount ;Offset to global destructor count
|
|
__DestructorCount EQU 10H
|
|
public __Exception_list ;Offset to global exception list
|
|
__Exception_list EQU 14H
|
|
NULL segment
|
|
db 16 dup (0) ;Windows
|
|
db 4 dup (0) ;destructor count
|
|
db 2 dup (0) ;exception list
|
|
db 4 dup (0) ;exception vptr
|
|
db 6 dup (0) ;reserved
|
|
db 2 dup (0) ;VBX control jump vector
|
|
;MUST be at SS:20h
|
|
db 2 dup (0) ;reserved
|
|
ends
|
|
|
|
_CVTSEG segment
|
|
public __RealCvtVector
|
|
__RealCvtVector label word
|
|
ends
|
|
|
|
_SCNSEG segment
|
|
public __ScanTodVector
|
|
__ScanTodVector label word
|
|
ends
|
|
|
|
_FPSEG segment
|
|
public __FPVector
|
|
__FPVector dd 0
|
|
ends
|
|
|
|
_DATA segment
|
|
public _errno
|
|
_errno dw 0
|
|
public __protected
|
|
__protected dw 0
|
|
public __8086
|
|
__8086 dw 0
|
|
public __8087
|
|
__8087 dw 0
|
|
public __psp
|
|
__psp dw 0
|
|
public __hInstance
|
|
__hInstance dw 0
|
|
public __hPrev
|
|
__hPrev dw 0
|
|
public __pszCmdline
|
|
__pszCmdline dw 0
|
|
public __cmdShow
|
|
__cmdShow dw 0
|
|
public __version
|
|
__version label word
|
|
public __osversion
|
|
__osversion label word
|
|
public __osmajor
|
|
__osmajor db 0
|
|
public __osminor
|
|
__osminor db 0
|
|
public __osmode ;Used for OS/2 protected mode by MS,
|
|
__osmode db 0 ; currently set to 0 under Windows
|
|
public __WinAllocFlag ;Used by malloc to get additional flags
|
|
__WinAllocFlag dw 0 ; to pass to GlobalAlloc (for DLL use)
|
|
public __LockWIN87EM ;Used do lock down WIN87EM to avoid
|
|
__LockWIN87EM dw 1 ; DLL unload ordering problem
|
|
public __abend
|
|
__abend dw 0 ;Signals abnormal end, don't run destructors etc.
|
|
|
|
IFDEF __DPMI16__
|
|
extrn __C0environ:dword
|
|
extrn __C0argv:dword
|
|
extrn __C0argc:word
|
|
ENDIF
|
|
|
|
CopyRight db 'Borland C++ - Copyright 1994 Borland Intl.',0
|
|
IFDEF __DPMI16__
|
|
DPMI16Error_s db 'This DPMI16 application was linked incorrectly and'
|
|
db ' will fail.',10,13,'$'
|
|
ENDIF
|
|
ends
|
|
|
|
_TEXT segment
|
|
|
|
Main proc near
|
|
|
|
;Windows initialization. Sets up registers and stack.
|
|
|
|
IFDEF __DPMI16__
|
|
mov ax, seg DGROUP
|
|
mov ds, ax
|
|
ENDIF
|
|
|
|
push bx cx es
|
|
xor ax,ax
|
|
push ax
|
|
call __ExceptInit
|
|
pop ax
|
|
pop es cx bx
|
|
|
|
;INITTASK returns:
|
|
; 'Failure:
|
|
; AX = zero if it failed
|
|
; Success:
|
|
; AX = 1
|
|
; CX = stack limit
|
|
; DX = cmdShow parameter to CreateWindow
|
|
; ES:BX = -> DOS format command line (ES = PSP address)
|
|
; SI = hPrevinstance
|
|
; DI = hinstance
|
|
call INITTASK
|
|
or ax,ax
|
|
jnz @@OK
|
|
jmp @@Fail
|
|
@@OK: mov word ptr ss:[__Exception_list], -1
|
|
mov __psp,es
|
|
mov word ptr __pszCmdline,bx
|
|
mov __hPrev,si
|
|
mov __hInstance,di
|
|
mov __cmdShow,dx
|
|
|
|
IF LDATA EQ false
|
|
mov ax,-1
|
|
push ax
|
|
call LOCKSEGMENT
|
|
ENDIF
|
|
|
|
;Clear _BSS, uninitialized data area
|
|
|
|
IFNDEF __HUGE__
|
|
xor ax, ax
|
|
push ds
|
|
pop es
|
|
mov di,offset DGROUP:BeginBSS
|
|
mov cx,offset DGROUP:EndBSS
|
|
sub cx,di
|
|
cld
|
|
rep
|
|
stosb
|
|
ENDIF
|
|
|
|
;Init the Windows App
|
|
|
|
xor ax,ax
|
|
push ax
|
|
call WAITEVENT
|
|
push __hInstance
|
|
call INITAPP
|
|
__INITAPPCALLED:or ax,ax
|
|
jnz @@InitOK
|
|
jmp @@Fail
|
|
@@InitOK:
|
|
|
|
;Determine DOS version
|
|
|
|
mov ah, 30h
|
|
int 21h
|
|
mov __version, ax ; save minor and major revision
|
|
|
|
;Determine whether we are in protected mode
|
|
|
|
call GETWINFLAGS
|
|
test ax,1 ; WF_PMODE = 1
|
|
jz @@realmode ; Note: GETWINFLAGS returns a long,
|
|
; so if WF_PMODE changed it could be
|
|
; in the high word.
|
|
mov __protected, 8 ; Eight is for convenience.
|
|
@@realmode:
|
|
|
|
;Test for 8086/8087 presence
|
|
|
|
test ax,0400h ; WF_8087 = 0x0400
|
|
jz @@no8087
|
|
mov __8087, 1
|
|
@@no8087:
|
|
and ax,08h+04h+02h ; WF_CPU486|WF_CPU386|WF_CPU286
|
|
shr ax,1 ; Convert to 4 or 2 or 1 or 0
|
|
test ax,0004h ; Have 4, 486 done
|
|
jnz @@NoAdjust
|
|
or ax,ax ; Have 0, 8086 done
|
|
jz @@NoAdjust
|
|
inc ax ; Have 2 or 1, need 3 or 2
|
|
@@NoAdjust:
|
|
mov __8086,ax ; Set CPU Type
|
|
|
|
|
|
IFDEF __DPMI16__
|
|
;
|
|
; Here we attempt to prevent an early death caused by linking
|
|
; the libs in the wrong order
|
|
;
|
|
call _OkToInitWindowsRTL
|
|
or ax, ax
|
|
jnz DPMI16Error
|
|
ENDIF
|
|
|
|
;Call our initialization functions, including C++ static constructors.
|
|
|
|
mov ax,ds
|
|
mov es,ax
|
|
mov si,offset DGROUP:InitStart ;si = start of table
|
|
mov di,offset DGROUP:InitEnd ;di = end of table
|
|
call Initialize
|
|
|
|
IFDEF __DPMI16__
|
|
;Set up and call _main for DPMI16 application
|
|
|
|
push word ptr [__C0environ+2]
|
|
push word ptr [__C0environ]
|
|
push word ptr [__C0argv+2]
|
|
push word ptr [__C0argv]
|
|
push [__C0argc]
|
|
call _main
|
|
ELSE
|
|
;Set up and call WinMain for Windows application
|
|
push __hInstance
|
|
push __hPrev
|
|
push __psp
|
|
push word ptr __pszCmdline
|
|
push __cmdShow
|
|
__WINMAINCALL: call WINMAIN
|
|
ENDIF
|
|
push ax ; Push return value
|
|
|
|
cmp __abend, 0
|
|
jne @@abnormalexit
|
|
call _exit ; Normal exit
|
|
@@abnormalexit: call __exit ; Abnormal exit, don't call destructors etc.
|
|
|
|
IFDEF __DPMI16__
|
|
DPMI16Error:
|
|
mov ah, 9
|
|
mov dx, offset DPMI16Error_s
|
|
int 21h
|
|
call __abort
|
|
ENDIF
|
|
;---------------------------------------------------------------------------
|
|
; _cleanup() call all #pragma exit cleanup routines.
|
|
; _checknull() check for null pointer zapping copyright message
|
|
; _terminate(int) exit program with error code
|
|
; _restorezero() restore interrupt vectors
|
|
;
|
|
; These functions are called by exit(), _exit(), _cexit(),
|
|
; and _c_exit().
|
|
;---------------------------------------------------------------------------
|
|
|
|
; Call cleanup routines
|
|
|
|
__cleanup PROC DIST
|
|
PUBLIC __cleanup
|
|
|
|
mov ax,ds
|
|
mov es,ax
|
|
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. NO-OP on Windows.
|
|
|
|
__checknull PROC DIST
|
|
PUBLIC __checknull
|
|
ret
|
|
__checknull ENDP
|
|
|
|
; Restore grabbed interrupt vectors. NO-OP on Windows.
|
|
|
|
__restorezero PROC DIST
|
|
PUBLIC __restorezero
|
|
ret
|
|
__restorezero ENDP
|
|
|
|
; Exit to DOS
|
|
|
|
__terminate PROC DIST
|
|
PUBLIC __terminate
|
|
|
|
IF LDATA EQ false
|
|
mov ax,-1
|
|
push ax
|
|
call UNLOCKSEGMENT
|
|
ENDIF
|
|
mov bp,sp
|
|
mov al,[bp+cPtrSize]
|
|
mov ah,4ch ;exit
|
|
int 21h
|
|
__terminate ENDP
|
|
|
|
@@Fail: mov al,0ffh
|
|
push ax
|
|
call _exit
|
|
|
|
mov ah,4ch ;exit
|
|
int 21h
|
|
endp
|
|
|
|
; Return default data segment in AX
|
|
|
|
__GetDGROUP PROC FAR
|
|
PUBLIC __GetDGROUP
|
|
mov ax, ss
|
|
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
|
|
|
|
ends
|
|
|
|
end Main
|