;
; $VER: cres_OpenUtil.a 1.0 (1.11.98)
;
; OpenUtil.library custom replacement for the SAS-C cres.o startup module.
;
; Author: Oliver "Bloodrock" Lange <bloodrock@bloody.in-berlin.de>
;
; SAS/C is (c) 1993 SAS Institute, Inc, Cary, NC USA
;
;-----------------------------------------------------------------------------
;
; Description:
;
; Two years ago, i made this module for private use, but with the additional
; OpenUtil stuff you'll maybe find it useful. I'll explain it in detail:
;
; cres_OpenUtil.o was made in order to minimize startup code size for use with
; AmigaOS-only applications; therefor the ANSI-C libcode ballast is gone,
; including argc/argv, so your main() function is now simply declared as
;
; void main(void);
;
; Hint: to avoid errors, best compile using the NoStdIo option.
; In general, ANSI-C isn't often needed with OpenUtil.library..
;
; The rest (all but ANSI-C) is still supported. Refer to the list below.
;
; In addition, all Library Bases (as stored in OpenUtilBase) are initialized
; ready for use, including OpenUtilBase (V2+).
;
; Because OpenUtil.library requires at least V39/68020, you can always except
; AmigaOS 3.0 and at least a 68020 CPU being installed, else main() doesn't
; get executed. Note: this module returns -1 (file not executable) if there's
; no AmigaOS at least V37.
;
; You may also completely exclude stdlib.h if you declare the exit() function
; yourself by either including "cres_OpenUtil.h" or adding the following
; define in your source code:
;
;   extern void exit (long ReturnCode);
;
;-----------------------------------------------------------------------------
;
; To compile the startup module, use:
;
; sc:c/asm -u -oLIB:cres_OpenUtil.o cres_OpenUtil.a
;
;-----------------------------------------------------------------------------
;
; Like cres.o, the compiled executables are resident-able. Your application
; doesn't need to support this, so you can always use this startup module,
; but if you want to make your program re-entrant, some rules again:
;
; - Don't compile with the __saveds option anywhere. For example, if you need
;   Hooks, pass the stuff needed by the hook (or just your DataSpace register)
;   as Hook function parameter or in h_Data.
;
;   With the following code you can replace all __saveds statements and then
;   compile resident:
;
;     void main(void)
;     {
;       // Init Hooks:
;
;       MyHook->h_Data = (void *)getreg(12); // REG_A4
;
;       AndSoOn;
;     }
;
;     long MyHookEntry(MyArgs)            // without __saveds
;     {
;       // very first action:
;
;       long OldA4 = getreg(12);          // remember current a4
;       putreg (12, (long)Hook->h_Data);  // set a4 to our DS address
;
;       // now:
;
;       DoMyStuff;
;
;       // very last action:
;
;       putreg(12,OldA4);                 // restore initial a4
;       return(TRUE);
;     }
;
; - Always compile using the SMALLDATA option
;
; - Don't write to __far data (if you don't really know what you do) because
;   far data gets shared with the other running instances of your application.
;
; - Use #pragmas instead of amiga.lib whenever possible, since amiga.lib
;   uses absolute addressing.
;
;-----------------------------------------------------------------------------
;
; Supported variables
; -------------------
;
;   __startup        ; startup function (first byte in the first code Segment)
;   __fpinit         ; Compiler FPU initializers, Constructors
;   @main            ; App's main() function
;   __fpterm         ; Terminators (incl. FPU)
;   _XCEXIT          ; exit() entry; also called by StackCheck & other Compiler services
;   @_XCEXIT         ; (same), used with Parameters=REGISTER
;   _ONEXIT          ; exit trap function (if the Compiler generated one)
;
;   __base           ; StackBase, used with StackCheck
;   __stack          ; defined by the application: required Stack size in bytes (long)
;   _StackPtr        ; startup StackPtr, needed for good exit()
;   _WBenchMsg       ; Workbench Startup Message or NULL = run from CLI (Shell)
;   _ProgramName     ; Program Name (as named on disk / from Shell)
;   __curdir         ; current directory Lock (read only)
;   DOSBase          ; Library Bases as defined in libraries/OpenUtil.h
;   SysBase          ; for fast access using exec/exec_sysbase_pragmas.h
;
; New Variables:     ; all new stuff is free for use by the application (read/write)
;
;   _ThisTask        ; Application's Process structure
;
;   _OpenUtilName    ; if one needs it :)
;
;   AslBase          ; Library bases as defined in libraries/OpenUtil.h
;   CxBase           ;
;   DiskfontBase     ;
;   ExpansionBase    ;
;   GadToolsBase     ;
;   GfxBase          ;
;   IconBase         ;
;   IFFParseBase     ;
;   IntuitionBase    ;
;   KeymapBase       ;
;   LayersBase       ;
;   LocaleBase       ;
;   OpenUtilBase     ; (V2+)
;   UtilityBase      ;
;
;   ConsoleBase      ; Device Library bases as defined in libraries/OpenUtil.h
;   InputBase        ;
;   TimerBase        ;
;
;=============================================================================

        include "exec/libraries.i"
        include "exec/memory.i"
        include "dos/dosextens.i"
        include "workbench/startup.i"

        include "exec/exec_lib.i"
        include "dos/dos_lib.i"

        include "libraries/OpenUtil.i"

;-----------------------------------------------------------------------------

call    Macro
                jsr     _LVO\1(a6)
        EndM

;-----------------------------------------------------------------------------

        XDEF    __startup
        XDEF    _XCEXIT
        XDEF    @_XCEXIT

        XDEF    _ThisTask
        XDEF    _OpenUtilName

        XDEF    AslBase
        XDEF    CxBase
        XDEF    DiskfontBase
        XDEF    DOSBase
        XDEF    ExpansionBase
        XDEF    GadToolsBase
        XDEF    GfxBase
        XDEF    IconBase
        XDEF    IFFParseBase
        XDEF    IntuitionBase
        XDEF    KeymapBase
        XDEF    LayersBase
        XDEF    LocaleBase
        XDEF    OpenUtilBase
        XDEF    SysBase
        XDEF    UtilityBase

        XDEF    ConsoleBase
        XDEF    InputBase
        XDEF    TimerBase

;-----------------------------------------------------------------------------

        xref    @main
        xref    _ONEXIT
        xref    __fpinit
        xref    __fpterm

        xref    __stack
        xref    __base
        xref    _StackPtr

        xref    __curdir
        xref    _ProgramName
        xref    _WBenchMsg

        xref    LinkerDB            ; Data/BSS hunk (absolute hunk start)

        xref    RESLEN              ; resident: Number of BYTEs in Data + BSS
        xref    RESBASE             ; resident: a4 LinkerDB offset (0 or $8000)
        xref    NEWDATAL            ; resident: Number of LONGs in Data

;-----------------------------------------------------------------------------

        OFFSET  0
        ds.b    4                   ; Stack Map
        ds.b    13*4                ;   (savereg)
        ds.b    4                   ;   (stackbtm)

;-----------------------------------------------------------------------------

        section text,code

__startup:
        lea     LinkerDB,a4         ; original base (read only until allocated a copy)
        move.l  4.w,a6              ; a6 = SysBase
        move.l  #RESBASE,d4         ; d4 = RESBASE
        move.l  __stack(a4),d7      ; d7 = __stack
        moveq   #0,d5               ; d5 = _WBenchMsg

        sub.l   a1,a1
        call    FindTask
        move.l  d0,a3               : a3 = _ThisTask
        move.l  pr_CLI(a3),d1
        lsl.l   #2,d1
        move.l  d1,a2               ; a2 = CLI (or NULL)
        beq.s   GetStack_WB         ; checks d1 (lsl.l)

GetStack_CLI:
        move.l  cli_DefaultStack(a2),d6 ; d6 = current Stack Size (=CommandStack)
        lsl.l   #2,d6                   ; numLongs -> numBytes
        bra.s   CheckV37
GetStack_WB:
        lea     pr_MsgPort(a3),a0
        call    WaitPort
        lea     pr_MsgPort(a3),a0
        call    GetMsg
        move.l  d0,d5               ; d5 = _WBenchMsg
        move.l  sp,d6
        sub.l   TC_SPLOWER(a3),d6   ; d6 = current Stack Size

CheckV37:
        cmp.w   #37,LIB_VERSION(a6) ; at least V37 ? OpenUtil.library will display an error
        bhs.s   CalcAllocSize       ; requester if below V39, so if we have V37, let it roll..
        moveq   #-1,d7              ; "file not executable" on Kick 1.3 and below
        bra.w   ReplyWBench
       ;-----   -----------

CalcAllocSize:                      ; d6 = current Stack Size
        moveq   #0,d3               ; d3 = Flag: new Stack needed ?

        move.l  #RESLEN,d0
        cmp.l   d7,d6               ; __stack
        bcc.s   AllocBSS

        move.l  d7,d6               ; __stack
        add.l   d6,d0               ; increase size of mem for new stack
        moveq   #1,d3               ; set flag

AllocBSS:
        moveq   #MEMF_PUBLIC,d1
        bset    #MEMB_CLEAR,d1
        call    AllocVec            ; Allocate BSS (+ needed Stack)
        tst.l   d0
        bne.s   BSSMemOK

OutOfMem:
        moveq   #RETURN_FAIL,d7
        bra.w   ReplyWBench         ; d5 = _WBenchMsg !
       ;-----   -----------

BSSMemOK:
        move.l  d0,a0
CopyData:
        move.l  #NEWDATAL,d1
        sub.l   d4,a4               ; - RESBASE

CData_Loop:
        move.l  (a4)+,(a0)+
        subq.l  #1,d1
        bne.s   CData_Loop

        move.l  d0,d1               ; MemStart
GenRelocTable:
        move.l  (a4)+,d2            ; NumRelocs
        beq.s   InitBSS
GenRel_Loop:
        move.l  d1,a0
        add.l   (a4)+,a0            ; a0 now has add of reloc
        add.l   (a0),d0
        move.l  d0,(a0)
        move.l  d1,d0               ; restore offset
        subq.l  #1,d2
        bne.s   GenRel_Loop

InitBSS:
        move.l  d1,a4               ; set up new base register
        add.l   d4,a4               ; + RESBASE
        move.l  d5,_WBenchMsg(a4)
        move.l  a3,_ThisTask(a4)
        move.l  a6,SysBase(a4)
        move.l  sp,_StackPtr(a4)

OpenLibs:
        lea     OUName(PC),a1
        move.l  a1,_OpenUtilName(a4)
        moveq   #2,d0               ; minumum required OpenUtil.library version
        call    OpenLibrary
        move.l  d0,OpenUtilBase(a4)
        bne.s   SetupData
        moveq   #RETURN_FAIL,d7
        bra.w   FreeBSS
       ;-----   -------

SetupData:
        move.l  d0,a0
        lea     AslBase(a4),a1
        lea     OUBaseOffsetTab(pc),a5
        moveq   #0,d0
        moveq   #17,d1
SD_SetLibs:
        move.b  (a5)+,d0
        move.l  0(a0,d0.w),(a1)+    ; Setup Library Bases
        subq.b  #1,d1
        bne.s   SD_SetLibs

        move.l  a2,d0               ; CLI ?
        beq.s   SetupData_WB
        move.l  cli_CommandName(a2),d0
        lsl.l   #2,d0
        addq.l  #1,d0
        move.l  d0,_ProgramName(a4)
        move.l  pr_CurrentDir(a3),__curdir(a4)
        bra.s   SetStack

SetupData_WB:
        move.l  d5,a0               ; _WBenchMsg
        move.l  sm_ArgList(a0),d0   ; Startup->ArgList ? Else no __curdir and _ProgramName..
        beq.s   SetStack

        move.l  d0,a0
        move.l  wa_Name(a0),_ProgramName(a4)
        move.l  wa_Lock(a0),d1
        move.l  DOSBase(a4),a6
        call    DupLock
        move.l  d0,__curdir(a4)
        move.l  d0,d1
        call    CurrentDir
        move.l  SysBase(a4),a6

SetStack:
        tst.b   d3                  ; New Stack needed ?
        beq.s   SetBase
SwapStack:
        move.l  a4,d0
        sub.l   d4,d0               ; - RESBASE
        add.l   #RESLEN,d0
        add.l   d7,d0               ; __stack
        move.l  d0,d1
        lea     MyStack+stk_Pointer(a4),a0  ; end of StackSwapStruct
        move.l  d0,(a0)             ; stk_Pointer
        move.l  d1,-(a0)            ; stk_Upper
        sub.l   d7,d1               ; __stack
        move.l  d1,-(a0)            ; stk_Lower (beg of StackSwapStruct)
        call    StackSwap

SetBase:
        moveq   #127,d1
        addq.l  #1,d1               ; allow for parms overflow
        add.l   sp,d1
        sub.l   d6,d1               ; get top of stack
        move.l  d1,__base(a4)       ; save for stack checking

       ;save the current stack. We'll reset it to here to run the autoterminators.

        lea     NewStack+stk_Pointer(a4),a0
        move.l  sp,(a0)             ; stk_Pointer
        move.l  sp,-(a0)            ; stk_Upper
        move.l  d1,-(a0)            ; stk_Lower
        call    StackSwap

LaunchInitializers:
        jsr     __fpinit(pc)        ; launch auto initializers
        tst.l   d0
        bne.s   shutdown            ; NULL or RETURN_CODE

;-----------------------------------------------------------------------------

        jsr     @main(pc)           ; call your main () entry point
        moveq   #0,d0               ; main() may return void (only exit() has a return code)
        bra.s   shutdown
_XCEXIT:                            ; exit() called if Parameters=STACK is used
        move.l  4(sp),d0
@_XCEXIT:                           ; exit() called if Parameters=REGISTER
;-----------------------------------------------------------------------------

shutdown:
        move.l  d0,d7               ; d7 = main ReturnCode
        move.l  SysBase(a4),a6
        lea     NewStack(a4),a0
        move.l  (a0),__base(a4)     ; stk_Lower
        call    StackSwap           ; destructors get full stack even after stack overflow etc.

        move.l  _ONEXIT(a4),d0
        beq.s   CallDestructors
        move.l  d0,a0
        jsr     (a0)

CallDestructors:
        jsr     __fpterm(pc)        ; shut down auto initializers

        move.l  SysBase(a4),a6
        lea     MyStack(a4),a0
        tst.l   (a0)                ; stk_Lower
        beq.s   RestoreSP
        call    StackSwap
RestoreSP:
        move.l  _StackPtr(a4),sp    ; restore Stack

        move.l  _WBenchMsg(a4),d5   ; keep it in order to free A4 first
        beq.s   CloseLibs
        move.l  __curdir(a4),d1
        beq.s   CloseLibs           ; tested because if NULL, there may be no DOSBase
        move.l  DOSBase(a4),a6
        call    UnLock
        move.l  SysBase(a4),a6

CloseLibs:
        move.l  OpenUtilBase(a4),d0
        beq.s   FreeBSS
        move.l  d0,a1
        call    CloseLibrary
FreeBSS:
        move.l  a4,a1
        sub.l   #RESBASE,a1
        call    FreeVec
ReplyWBench:
        tst.l   d5
        beq.s   SetRC
        call    Forbid
        move.l  d5,a1
        call    ReplyMsg            ; reply Workbench Startup Message
SetRC:
        move.l  d7,d0               ; ReturnCode
TheEnd:
        rts

;-----------------------------------------------------------------------------

OUName: OPENUTILNAME

OUBaseOffsetTab:
        dc.b    OU_AslBase
        dc.b    OU_CxBase
        dc.b    OU_DiskfontBase
        dc.b    OU_DOSBase
        dc.b    OU_ExpansionBase
        dc.b    OU_GadToolsBase
        dc.b    OU_GfxBase
        dc.b    OU_IconBase
        dc.b    OU_IFFParseBase
        dc.b    OU_IntuitionBase
        dc.b    OU_KeymapBase
        dc.b    OU_LayersBase
        dc.b    OU_LocaleBase
        dc.b    OU_UtilityBase
        dc.b    OU_ConsoleBase
        dc.b    OU_InputBase
        dc.b    OU_TimerBase

        cnop    0,4                 ; currently, we're already LONG-aligned

;=============================================================================

        section __MERGED,BSS

MyStack:        ds.b    StackSwapStruct_SIZEOF ; not for use by the application
NewStack:       ds.b    StackSwapStruct_SIZEOF ;

_ThisTask:      ds.l    1
_OpenUtilName:  ds.l    1

OpenUtilBase    ds.l    1
SysBase         ds.l    1

AslBase         ds.l    1           ; array [17]
CxBase          ds.l    1
DiskfontBase    ds.l    1
DOSBase         ds.l    1
ExpansionBase   ds.l    1
GadToolsBase    ds.l    1
GfxBase         ds.l    1
IconBase        ds.l    1
IFFParseBase    ds.l    1
IntuitionBase   ds.l    1
KeymapBase      ds.l    1
LayersBase      ds.l    1
LocaleBase      ds.l    1
UtilityBase     ds.l    1
ConsoleBase     ds.l    1
InputBase       ds.l    1
TimerBase       ds.l    1

;-----------------------------------------------------------------------------
        end
