home *** CD-ROM | disk | FTP | other *** search
- page 60, 132
- name tcpp10
- title Turbo C++ 1.0 ROMable Startup Code, Version 1.00
-
- ;
- ; Embedded System Startup Code for Turbo C++ 1.0
- ; Copyright (C) 1989, 1990 Paradigm Systems. All rights reserved.
- ;
- ; Assemble with MASM/TASM as follows:
- ;
- ; masm tcpp10 /mx /d__MDL__ [ /dTDREM ] ;
- ; tasm tcpp10 /mx /d__MDL__ [ /dTDREM ]
- ;
- ; where
- ; __MDL__ is one of the following:
- ;
- ; __s__ Small memory model
- ; __c__ Compact memory model
- ; __m__ Medium memory model
- ; __l__ Large memory model
- ; __h__ Huge memory model
- ;
- ; If no memory model is specified, the small memory model is
- ; assumed and a warning is issued.
- ;
- ; TDREM should be defined if the application will be debugged with
- ; the Paradigm Turbo Debugger Interface.
- ;
-
- INCLUDE startup.inc ; Macros and other assembly langauge defintions
- INCLUDE tcpp10.inc ; Compiler-specific defintions
-
- IFDEF ??Version ; Check for TASM
- NOWARN PDC ; Disable pass 1 warnings
- ENDIF ; ??Version
- IF1
- IFDEF TDREM
- %out Building TDREM-compatible startup code
- ENDIF ; TDREM
- ENDIF ; IF1
- IFDEF ??Version ; Check for TASM
- WARN PDC ; Turn warnings back on
- ENDIF ; ??Version
-
- subttl Segment ordering/alignment section
- page
- ;
- ; Segment and group declarations. The order of these declarations is
- ; used to control the order of segments in the .EXE file since the
- ; Microsoft and Borland linkers copy segments in the same order they
- ; are encountered in the object files.
- ;
- ; Make sure that this startup module is specified first when linking
- ; the application. Requires MASM 5.10 or TASM 1.0 (or later) to
- ; assemble.
- ;
- DefSeg begtext, para, public, CODE, <> ; Start of CODE class
- DefSeg _text, word, public, CODE, <>
-
- ;
- ; The following segments are used to hold the linked initializers
- ; and terminators. These permit automatic initialization of code
- ; without startup source modification and should be located in ROM.
- ; Terminators are not implemented since an application will normally
- ; not return to the startup code but are supported for run-time library
- ; compatibility.
- ;
- DefSeg BEGINIT, para, public, INITDATA, IGROUP ; Start of initializers
- DefSeg _INIT_, word, public, INITDATA, IGROUP ; Initializers
- DefSeg _INITEND_, byte, public, INITDATA, IGROUP
- DefSeg _EXIT_, word, public, EXITDATA, IGROUP ; Terminators
- DefSeg _EXITEND_, byte, public, EXITDATA, IGROUP
-
- IFNDEF TDREM ; ROMable code
- ;
- ; The following segments are used to mark the place for ROM copies
- ; of initialized data for use by the startup code.
- ;
- DefSeg romdata, para, public, ROMDATA, <>
- DefSeg erdata, para, public, ENDROMDATA, <>
- ENDIF ; !TDREM
-
- DefSeg begdata, para, public, DATA, DGROUP
- DefSeg _data, word, public, DATA, DGROUP
- IF @DataSize NE MM_HUGE ; BSS class definition
- DefSeg _bss, word, public, BSS, DGROUP
- DefSeg _bssend, word, public, BSSEND, DGROUP
- ELSE ; HUGE_BSS class definition
- DefSeg hugebss, para, public, HUGEBSS, <>
- DefSeg hbssend, word, public, HUGEBSSEND, <>
- ENDIF ; MM_HUGE
-
- ;
- ; Two stack segments are declared, one for emulators that expect
- ; a segment called STACK and the TC++ _STACK segment.
- ;
- IF @DataSize EQ MM_NEAR ; Stack is part of DGROUP
- DefSeg stack, para, public, STACK, DGROUP
- DefSeg _stack, para, stack, STACK, DGROUP
- ELSE ; Stack is independent of DGROUP
- DefSeg stack, para, public, STACK, <>
- DefSeg _stack, para, stack, STACK, <>
- ENDIF ; MM_NEAR
-
- IFDEF TDREM ; TDREM code
- ;
- ; This segment marks the start of the saved copy of initialized
- ; data. The copy of the initialized data will be copied here for
- ; restoring on restarts.
- ;
- DefSeg backup, para, public, BACKUP, <> ; Initialized data copy
- ENDIF ; TDREM
-
- ;
- ; The far heap is defined as a convienence and identifies the
- ; start of RAM available for general purpose use.
- ;
- DefSeg farheap, para, public, FARHEAP, <>
-
- ;
- ; Make up the correct external references that depend on the
- ; memory model and inform the user which code and data memory
- ; models have been selected.
- ;
- ExtProc main ; Turbo C language entry point
- IF @CodeSize EQ MM_HUGE ; Huge memory model support
- extrn PSBP@ : far ; Huge pointer subtraction helper
- ENDIF ; MM_HUGE
-
- subttl Startup code section
- page
- _text segment
- assume cs:_text
-
- BegProc _startup, far ; Turbo C 2.0 startup code entry point
- cli ; Disable interrupts
- cld ; Make sure the direction is forward
-
- IFDEF TDREM
- ;
- ; Check if the segment registers have been setup by DOS
- ;
- mov ax, ds ; See if DOS setup DS
- or ax, ax ; Check if the PSP is zero
- jz embedded ; Continue with the embedded code
-
- push cs ; Display a DOS error message
- pop ds
- assume ds:_text
- mov dx, offset errmsg
- mov ah, 09h
- int 21h
-
- mov ah, 04ch ; Exit before doing something harmful
- int 21h
-
- errmsg db "Embedded application: DOS not supported", 0ah, 0dh, '$'
-
- embedded:
- ENDIF ; TDREM
-
- ;
- ; Set up the stack according to the memory model. The default Borland
- ; model is to have the stack after the heap in the small and medium
- ; memory models.
- ;
- IF @DataSize EQ MM_NEAR
- mov ax, dgroup ; Get the frame number for the group
- mov ss, ax ; Load in the stack segment register
- mov sp, offset dgroup:tos ; And the offset within the group
- assume ss:dgroup
- ELSE
- mov ax, stack ; Get the frame number for the segment
- mov ss, ax ; Load in the stack segment register
- mov sp, offset tos ; And the offset within the segment
- assume ss:stack
- ENDIF ; @DataSize
-
- IFNDEF TDREM ; ROM-based initialization code
-
- IF @DataSize NE MM_HUGE ; Small, medium, compact and large models
- ;
- ; Prepare the segment registers for initialization. The initialized
- ; data is assumed to have been located in the class ROMDATA with begins
- ; with the segment of the same name.
- ;
- mov ax, dgroup ; The destination segment for DGROUP
- mov es, ax
- mov ax, romdata ; The source segment
- mov ds, ax
- assume ds:romdata, es:dgroup
-
- ;
- ; Copy DGROUP from its position in ROM to the target address in RAM
- ;
- mov si, offset dgroup:idata ; Get the starting offset of the DATA segment
- mov di, si ; Same offset, different segment
- mov cx, offset dgroup:bdata ; Get the ending offset (start of BSS)
- sub cx, di ; Subtract the two for a length
- shr cx, 1 ; Convert to words
- jcxz $$1 ; Skip if zero length
- rep movsw ; Copy initialized data from ROM to RAM
- $$1:
-
- ELSE ; Huge model initialized data
-
- ;
- ; Copy the DATA class. This class can span multiple segments in the
- ; huge model so the copying from ROM to RAM must be iterated as many
- ; times as necessary. We start by computing the class length with the
- ; help of the Turbo C run-time library.
- ;
- mov cx, _data ; Get the destination address
- mov bx, offset idata
- mov dx, hugebss ; Get the end address
- mov ax, offset bhdata
- call PSBP@ ; Length returned in DX:AX
-
- mov bx, _data ; Get the destination address in RAM
- mov es, bx ; Address via ES:DI
- mov di, offset idata
-
- mov bx, romdata ; Point to the copy in ROM
- mov ds, bx ; Address via DS:SI
- mov si, di
- assume ds:nothing, es:nothing
-
- ;
- ; Get the size of the class and perform the copy
- ;
- or dx, dx ; Test if less than a segment
- jz $$3 ; Skip if less than 64K
-
- $$2:
- mov cx, 08000h ; Move 64KB
- rep movsw ; Copy initialized data from ROM to RAM
- mov cx, ds ; Get the source segment
- add cx, 1000h ; Adjust for the 64K just transfered
- mov ds, cx ; Save it back
- mov cx, es ; Get the destination segment
- add cx, 1000h ; Adjust for the 64K just transfered
- mov es, cx ; Save it back
- dec dx ; Bump the count
- jnz $$2 ; Repeat for all complete segments
-
- $$3:
- mov cx, ax ; Get the remaining count
- shr cx, 1 ; Convert to words
- jcxz $$4 ; Skip if zero
- rep movsw ; Copy the remaining data
- $$4:
- ENDIF ; MM_HUGE
-
- ELSE ; Initialization code for TDREM
- ;
- ; Copy the initialized to safety if not done all ready
- ;
- mov ax, dgroup ; Setup DGROUP addressability
- mov ds, ax
- assume ds:dgroup
-
- cmp SAVEDATA, 0 ; Check if saved
- jne do_init ; Skip to the initialization
-
- ;
- ; Come here the first time thru to save a copy of the initialized
- ; data.
- ;
- mov SAVEDATA, 1 ; Prevent further saves to BACKUP
-
- IF @DataSize NE MM_HUGE ; Small, medium, compact and large models
- ;
- ; The first pass requires that a copy of the initialized data be made
- ; and stored in the BACKUP class.
- ;
- mov ax, backup ; Where to store the data
- mov es, ax
- mov di, 0 ; ES:DI for destination address
- mov si, offset dgroup:idata ; DS:SI for the source address
- mov cx, offset dgroup:bdata
- sub cx, si ; Compute the length of the initialized data
- shr cx, 1 ; Convert from bytes to words
- jcxz $$5 ; Skip if zero
- rep movsw ; Copy the data to be restored later
- jmp short $$5
-
- ;
- ; Come here when the initialized data must be restored from the copy
- ; stored in the BACKUP class.
- ;
- do_init:
- mov ax, backup ; Source address is DS:SI
- mov ds, ax
- xor si, si
-
- mov ax, dgroup ; Desintation address is ES:DI
- mov es, ax
- mov di, offset dgroup:idata
-
- mov cx, offset dgroup:bdata ; Find the end of the initialized data
- sub cx, di ; Compute the length
- shr cx, 1 ; Convert from bytes to words
- jcxz $$5 ; Skip if zero
- rep movsw ; Restore the previously saved data
- $$5:
-
- ELSE ; Huge memory model
- ;
- ; Huge model initialized data save/restore from BACKUP. This is more
- ; complicated since the initialized data spans multiple segments.
- ;
- mov cx, _data ; Get the class base address
- mov bx, offset idata
- mov dx, hugebss ; Get the class end address
- mov ax, offset bhdata
- call PSBP@ ; Class length in DX:AX
-
- mov bx, _data ; Get the class base address
- mov ds, bx
- mov si, offset idata ; DS:SI source address
-
- mov bx, backup ; Destination of the copy
- mov es, bx
- xor di, di
- jmp short do_copy ; Do the copy
-
- ;
- ; Come here when initialized data must be restored from the copy
- ; to the destination.
- ;
- do_init:
- mov cx, _data ; Get the class base address
- mov bx, offset idata
- mov dx, hugebss ; Get the class end address
- mov ax, offset bhdata
- call PSBP@ ; Class length in DX:AX
-
- mov bx, _data ; Get the class base address
- mov es, bx
- mov di, offset idata ; ES:DI destination address
-
- mov bx, backup ; DS:SI source address in BACKUP
- mov ds, bx
- xor si, si
- assume ds:nothing, es:nothing
-
- do_copy:
- or dx, dx ; Test if less than a segment
- jz $$7 ; Skip if less than 64K
-
- $$6:
- mov cx, 08000h ; Move 64KB
- rep movsw ; Copy initialized data
- mov cx, ds ; Get the source segment
- add cx, 1000h ; Adjust for the 64K just transfered
- mov ds, cx ; Save it back
- mov cx, es ; Get the destination segment
- add cx, 1000h ; Adjust for the 64K just transfered
- mov es, cx ; Save it back
- dec dx ; Bump the count
- jnz $$6 ; Repeat for all complete segments
-
- $$7:
- mov cx, ax ; Get the remaining count
- shr cx, 1 ; Convert from bytes to words
- jcxz $$8 ; Skip the copy if zero
- rep movsw ; Copy the remaining data
- $$8:
- ENDIF ; MM_HUGE
- ENDIF ; !TDREM
-
- ;
- ; Setup the segment registers for Turbo C
- ;
- mov ax, dgroup ; Address DGROUP via DS
- mov ds, ax
- mov es, ax
- assume ds:dgroup, es:dgroup
-
- IF @DataSize NE MM_HUGE
- ;
- ; Zero out the BSS area (not present in the huge memory model)
- ;
- xor ax, ax ; Use a zero fill pattern
- mov di, offset dgroup:bdata ; Get the start of the BSS class
- mov cx, offset dgroup:edata ; Get the end address
- sub cx, di ; Subtract the two for a length
- shr cx, 1 ; Convert to the number of words
- jcxz $$9 ; Skip if zero
- rep stosw ; Zero out the BSS
- $$9:
- ELSE
- ;
- ; Zero out the HUGEBSS class. This class can span multiple segments so
- ; the zeroing of memory must be iterated as many times as necessary.
- ; Start by making a normalized pointer to the end of class.
- ;
- mov dx, hbssend ; Get the ending adress
- mov ax, offset ehdata
- mov cx, hugebss ; Get the starting address
- mov bx, offset bhdata
- call PSBP@ ; Compute the class size
- mov bx, ax ; DX:BX is the length
-
- mov ax, hugebss ; Get the starting address of HUGEBSS
- mov es, ax
- assume es:nothing
- mov di, offset bhdata ; Address via ES:DI
-
- xor ax, ax ; Use a zero fill pattern
- or dx, dx ; Test if less than a segment
- jz $$11 ; Skip if less than 64K
- $$10:
- mov cx, 08000h ; Max operation is 64KB bytes
- rep stosw ; Zero out the far BSS
- mov cx, es ; Get the destination segment
- add cx, 1000h ; Adjust for the 64K just transfered
- mov es, cx ; Save it back
- dec dx ; Bump the count
- jnz $$10 ; Repeat for all complete segments
-
- $$11:
- mov cx, bx ; Get the remaining count
- shr cx, 1 ; Convert to words
- jcxz $$12 ; Skip if zero
- rep stosw ; Zero out the remaining far BSS
- $$12:
- ENDIF ; @DataSize
-
- ;
- ; Call all of the linked-in initializers in the _INIT_ segment. These
- ; are a series of pointers to routines which are guaranteed to
- ; execute before main() is called. The advantage of this approach is
- ; that the initialization will be made by virtue of the code being
- ; linked in, without any modification to the startup code.
- ;
- ; This implementation permits the initializers to be kept in ROM,
- ; even though there is no ordering of the initializers. The register
- ; use is as follows:
- ; SI - initializers start
- ; DI - initializers end
- ; CX - number of initializers not processed
- ; BX - current initializer
- ; AH - current priority
- ;
- mov si, offset igroup:InitStart ; Start of initializers
- mov di, offset igroup:InitEnd ; End of initializers
- mov ax, igroup
- mov es, ax
- assume es:nothing
-
- mov ax, di
- sub ax, si
- sub dx, dx
- mov cx, SIZE InitRec
- idiv cx
- mov cx, ax ; Number of initializers in the table
- jcxz InitDone ; Skip if nothing to process
- sub ah, ah ; Start with the highest priority
-
- PriTop:
- mov bx, si ; Point to the start of the table
-
- NextInit:
- cmp bx, di ; Check if this pass is done
- jae PriBottom ; Process the next priority level
- cmp es:[bx.pri], ah ; Compare against the current priority
- jne TableBottom ; Not our turn to execute
-
- push es
- push ax
- push bx
- push cx
- cmp es:[bx.ctype], MM_NEAR ; Is it near or far?
- je @@NearCall
- call dptr es:[bx.foff] ; Call the initializer
- jmp short $$14
- @@NearCall:
- call wptr es:[bx.foff] ; Call the initializer
- $$14:
- pop cx
- pop bx
- pop ax
- pop es
- dec cx ; Decrement the initializer count
- jz InitDone ; Exit if done
-
- TableBottom:
- add bx, SIZE InitRec ; Move to the next initializer
- jmp NextInit
-
- PriBottom:
- inc ah ; Process the next priority
- jmp PriTop
- InitDone:
-
- ;
- ; Call the C language entry point - initialization complete.
- ;
- sti ; Enable interrupts
- call main ; Call the Turbo C main()
-
- ;
- ; Program terminate is simulated by using a breakpoint with a
- ; CS:IP being both zero for TDREM. Also return the exit code in AX.
- ; Most embedded applications should not return.
- ;
- IFNDEF TDREM ; ROMable code
- jmp _startup ; Restart the application
- ELSE ; TDREM code
- and ax, 7fh ; Mask for TDREM codes
- push ax
- call pptr exit ; Call the TDREM exit handler
- ENDIF ; TDREM
-
- EndProc _startup
-
- ;
- ; This routine handles getting the error code from the caller
- ; and terminating the application.
- ;
- public __exit
- __exit proc
- IFNDEF TDREM ; ROMable code
- jmp _startup ; Application dependent
- ELSE
- mov bp, sp ; Prepare the stack frame
- IF @CodeSize EQ MM_NEAR
- mov ax, [bp+2] ; After IP
- ELSE
- mov ax, [bp+4] ; After CS:IP
- sub ah, ah ; Zero the upper byte of the exit code
- ENDIF ; MM_NEAR
-
- xor bx, bx ; Make the interrupt vector table addressable
- mov es, bx
-
- pushf ; Push the PSW
- push bx ; IP
- push bx ; CS
- jmp dptr es:[000ch] ; Call the break handler
- ENDIF ; TDREM
- __exit endp
-
- BegProc exit ; C language exit()
- IF @CodeSize NE MM_NEAR
- pop cx ; Get rid of CS
- ENDIF ; MM_NEAR
- pop cx ; Clear IP (leave the exit code)
- IF @CodeSize NE MM_NEAR
- call fptr __exit ; Call the exit routine
- ELSE
- call nptr __exit ; Call the exit routine
- ENDIF ; MM_NEAR
- EndProc exit
-
- BegProc _exitclean
- IF @CodeSize NE MM_NEAR
- pop cx ; Get rid of CS
- ENDIF ; MM_NEAR
- pop cx ; Clear IP (leave the exit code)
- IF @CodeSize NE MM_NEAR
- call fptr __exit ; Call the exit routine
- ELSE
- call nptr __exit ; Call the exit routine
- ENDIF ; MM_NEAR
- EndProc _exitclean
-
- BegProc abort ; C language abort()
- mov ax, TDE_ABORT ; Abort error code
- push ax
- call exit ; Call the exit routine
- EndProc abort
-
- ;
- ; This is code given control when Turbo C detects a stack overflow.
- ; Stack overflow checking is enabled by the -N compiler option.
- ;
- public OVERFLOW@
- OVERFLOW@ proc
- mov ax, TDE_STKOVL ; TDREM stack overflow error code
- push ax
- call exit
- OVERFLOW@ endp
-
- _text ends
-
- subttl Data declarations
- page
- ;
- ; Run-time libraries and other modules can insert initializers in this
- ; segment. Initializers are executed by priority, and by link order when
- ; the same priority.
- ;
- _INIT_ segment
- InitStart label dword
- _INIT_ ends
- _INITEND_ segment
- InitEnd label dword
- db ?
- _INITEND_ ends
-
- IFNDEF TDREM
- romdata segment ; Segment placeholder for initialized data
- public ridata
- ridata label byte ; Mark the start of the ROM data area where
- ; the ROM copy of initialized data is stored
- romdata ends
- erdata segment ; ROMDATA class end marker
- db 16 dup (?) ; Force the next segment to a new paragraph
- erdata ends
- ENDIF ; !TDREM
-
- _data segment
- public idata
- idata label byte ; Mark the start of the _DATA segment
- IFDEF TDREM
- SAVEDATA dw 0 ; Restart flag (1 if restarted)
- ENDIF ; TDREM
- _data ends
-
- IF @DataSize NE MM_HUGE ; No BSS in the huge memory model
- _bss segment
- public bdata ; Mark the start of the BSS class
- bdata label byte ; and the end of the DATA class
- _bss ends
- _bssend segment ; BSS class end marker
- public edata
- dw ? ; Give a fixed size for alignment
- edata label byte ; Mark the end of the BSS class
- _bssend ends
- ELSE ; Must be huge memory model
- hugebss segment
- public bhdata ; Mark the start of the HUGEBSS class
- bhdata label byte ; and the end of the DATA class
- hugebss ends
- hbssend segment
- public ehdata
- ehdata label byte ; Mark the end of the HUGEBSS class
- db 16 dup (?) ; Force the next segment to a new paragraph
- hbssend ends
- ENDIF ; @DataSize
-
- IFDEF TDREM ; TDREM code
- backup segment ; Copy of initialized data
- db ?
- backup ends
- ENDIF ; TDREM
-
- _stack segment
- public tos ; Make the TOS visible to debuggers and ICE
- IFDEF STKSIZE
- db STKSIZE dup (?) ; Setup the stack
- ELSE
- db 2048 dup (?) ; Declare the stack size (in words)
- %out No stack size specified: stack size set to 2KB
- ENDIF
- even ; Force word alignment
- tos label word ; Define the top of stack (the initial SP value)
- _stack ends
-
- end _startup ; Program entry point