*
* C initial startup procedure under AmigaDOS
* 

        INCLUDE "exec/types.i"
        INCLUDE "exec/alerts.i"
        INCLUDE "exec/nodes.i"
        INCLUDE "exec/lists.i"
        INCLUDE "exec/ports.i"
        INCLUDE "exec/libraries.i"
        INCLUDE "exec/tasks.i"
        INCLUDE "exec/memory.i"
        INCLUDE "exec/execbase.i"
        INCLUDE "libraries/dos.i"
        INCLUDE "libraries/dosextens.i"
FUNCDEF         MACRO   * function
_LVO\1          EQU     FUNC_CNT
FUNC_CNT        SET     FUNC_CNT-6
                ENDM

FUNC_CNT        SET     LIB_NONSTD

        INCLUDE "exec/exec_lib.i"
        INCLUDE "libraries/dos_lib.i"
        INCLUDE "workbench/startup.i"

AbsExecBase EQU 4

TINY    EQU     0
callsys macro
        CALLLIB _LVO\1
        endm
        
        xdef        _XCEXIT               ; exit(code) is standard way to leave C.
        xdef         @_XCEXIT
        
        xref        LinkerDB              ; linker defined base value
        xref        _BSSBAS               ; linker defined base of BSS
        xref        _BSSLEN               ; linker defined length of BSS

        xref        __main                ; Name of C program to start with.
        xref        __fpinit              * initialize floating point
        xref        __fpterm              * terminate floating point

*        library references

        csect       _NOMERGE,0,0,0,4
        xref        _MemCleanup
        xref        __savecli
        xref        __freecli
        xref        FindTask

start:
        movem.l d1-d6/a0-a6,-(a7)
REGSIZE EQU     (6+7)*4
        lea     REGSIZE(a7),A5         * determine old stack pointer
        move.l  a0,a2                  * save command pointer
        move.l  d0,d2                  * and command length
        lea     LinkerDB,a4            * load base register

        move.l  AbsExecBase.W,a6
        move.l  a6,SysBase(A4)
        move.l  a7,_StackPtr(A4)       * Save stack ptr
        clr.l   _WBenchMsg(A4)

*------        Clear out our BSS section
        lea     _BSSBAS,a3             * get base of BSS
        moveq   #0,d1
        move.l  #_BSSLEN,d0            * get length of BSS in longwords
        bra.s   clr_lp                 * and clear for length given
clr_bss move.l  d1,(a3)+
clr_lp  dbf     d0,clr_bss

*------ get the address of our task
        move.l        ThisTask(a6),A3

*-----  clear any pending signals
        moveq        #0,d0
        move.l       #$00003000,d1
        callsys      SetSignal

*------ are we running as a son of Workbench?
        move.l       pr_CurrentDir(A3),__curdir(A4)
        tst.l        pr_CLI(A3)
        beq          fromWorkbench

*=======================================================================
*====== CLI Startup Code ===============================================
*=======================================================================

fromCLI:
*------        attempt to open DOS library:
        bsr.w        openDOS

*------ Duplicate the current directory for the called process
        move.l        __curdir(A4),D1
        move.l        DOSBase(A4),A6
        callsys       DupLock
        move.l        D0,__curdir(A4)

*------ Save the current CLI info including path for child to use
        pea            cli
        jsr            __savecli(pc)   
        addq.l         #4,a7

*------ find command name:
        move.l        pr_CLI(A3),a0
        add.l         a0,a0                        ; bcpl pointer conversion
        add.l         a0,a0
        move.l        cli_CommandName(a0),a1
        add.l         a1,a1                        ; bcpl pointer conversion
        add.l         a1,a1

*------        collect parameters:
        move.l        d2,d0                        ; get command line length
        moveq.l       #0,d1
        move.b        (a1)+,d1
        move.l        A1,D4                        ;save our pointer
        move.l        d1,d3
        add.l         d1,d1                        ; double length since we copy it twice
        add.l         d1,d0                        ; add length of command name
        addq.l        #5,d0                        ; allow for space after command        
        addq.l        #4,d0                        ; As well as for the roundup
*                          ; changed from 5 to 9 for quotes
*                                         
*------        We have the length of the command string to be copied.
*------        Allocate space for it.
*------        Round up to the nearest 4 bytes
        lsl.l         #2,d0
        add.l         memsize(A4),d0
        move.l        d0,memsize(A4)

************************************************************************
************************************************************************
******** Allocate and copy over the code to do the magic stuff *********
************************************************************************
************************************************************************

        lea           memlist(A4),A0
        move.l        AbsExecBase.W,a6
        callsys       AllocEntry                ;get a place to put the code
*        btst.l        #31,d0                     ;did we get the memory we asked for?
*        beq           cliexit                    ;no, can't do anything then
        move.l        D0,A1
        move.l        D0,-(A7)                  ;remember the chunk we allocated

*------ Now we copy the code over
        movea.l       ML_SIZE+ME_ADDR(A1),A1   ;locate our allocated block

*------        Copy over the command line to the allocated area
        lea           commandbuf-copybeg(A1),A3

*------        At this point, we have:
*                A0 - Work pointer
*                A1 - Pointer to Allocated memory block
*                A2 - Pointer to program name
*                A3 - Pointer to target buffer
*                A3 - Pointer to our process structure
*                D0 - Work register
*                D1 - Work register
*                D2 - length of command name
*                D3 - length of program name
*                D4 - Pointer to program name

        move.l        d2,argslen                 ; save length of args
        move.l        d3,proglen                 ; save length of prog
        
        movea.l       D4,A0                      ; Load program name
        move.l        D3,D0                      ;      and length
        move.b        #'"',(a3)+
        bra.s         cpy1
cpycmd: move.b        (A0)+,(A3)+                ; copy over the command name
cpy1:   dbf           d0,cpycmd
        move.b        #'"',(a3)+
        move.b        #' ',(A3)+                 ; add the space delimiter

        move.l        A2,A0                      ; Load command text
        move.l        D2,D0                      ;      and length
        bra.s         cpy2
cpylin: move.b        (A0)+,(A3)+
cpy2:   dbf           d0,cpylin
        clr.b         (A3)+

*------        Now copy over the program name
        move.l        A3,_ProgramName(A4)
        move.l        D4,A0
        move.l        D3,D0
        bra.s         cpy3
cpypgm: move.b        (A0)+,(A3)+
cpy3:   dbf           D0,cpypgm
        clr.b         (A3)                        ;don't forget trailing null on name

*------ First we want to put it into our seglist
        lea           segexit-copybeg(A1),A2
        move.l        A2,_XCEXIT+2(A4)            ;patch the jump instruction
        lea           start-4(PC),A2
   
*        Now we have:    A1 to our new fake segment
*                        A2 to the start of the real seglist
        move.l        #(copyend-copybeg)/4,(A1)+
        move.l        A1,D3
        lsr.l         #2,D3                        ;convert it to a bptr to the list
        move.l        (A2),(A1)+                   ;save our next segment pointer
        clr.l         (A2)                         ;and kill it from the chain
*------ We have the seglist

        lea           AutoLoader(PC),A2
        move.l        #(copyend-AutoLoader)-1,D0
copyit  move.b        (A2)+,(A1)+
        dbf           d0,copyit

*------        See if they want to do some I/O
        tst.l        _BackGroundIO(A4)
        beq          noio
        lea          current_window(A4),A0
        move.l       A0,D1
        move.l       #1005,D2
        move.l       DOSBase(A4),A6
        callsys      Open
        move.l       d0,_Backstdout(A4)
noio:
        move.l       AbsExecBase.w,A6

*------ If under 2.0 Flush cache
   
        moveq    #36,d0
        cmp.w    $14(a6),d0
        bgt.b    nocache
        moveq    #-1,d0
        callsys  CacheClearU

nocache:
        
        callsys        Forbid

*------ Attach the task to do the dirty work
        move.l        __procname(A4),D1        ; Name of task to start
        move.l        __priority(A4),D2        ; Priority of background task
        move.l        __stack(A4),D4           ; Stack size of created task

        move.l        DOSBase(A4),A6
        callsys       CreateProc
        move.l        A6,A3                    ; restore dosbase to close it
        tst.l         D0
        bne           ok

*------ Serious problems.  For some reason the CreateProc failed.
*------ We need to free the AlocEntry memory and go home
        move.l        (A7)+,A2
        movea.l       ML_SIZE+ME_ADDR(A2),A1   ;locate our allocated block
*------ Also must get rid of current directory
        move.l        __curdir(A4),D1
        callsys       UnLock

        move.l        A2,A0
        move.l        AbsExecBase.w,A6
        callsys       FreeEntry
        move.l        #104,D0
        bra           cliexit

ok:
*------ The task started ok, attach the memory we allocated to
*------        its task control block
        move.l        D0,A2

        move.l        (a7)+,A1                ;locate the memory we allocated
        lea           -pr_MsgPort(A2),A2
        lea           TC_MEMENTRY(A2),A0


*        ADDTAIL
        LEA           LH_TAIL(A0),A0
        MOVE.L        LN_PRED(A0),D0
        MOVE.L        A1,LN_PRED(A0)
        MOVE.L        A0,(A1)
        MOVE.L        D0,LN_PRED(A1)
        MOVE.L        D0,A0
        MOVE.L        A1,(A0)

        move.l        #0,D0

cliexit:
        move.l        AbsExecBase.w,A6
        callsys       Permit
        move.l        A3,A1
        move.l        AbsExecBase.w,A6
        callsys       CloseLibrary
        movem.l       (a7)+,d1-d6/a0-a6
        rts

*=======================================================================
*====== Workbench Startup Code =========================================
*=======================================================================

fromWorkbench:

        move.l        TC_SPLOWER(A3),__base(A4)        ; set base of stack
        add.l         #128,__base(A4)                  ;allow for parms overflow
*------ open the DOS library:
        bsr.w         openDOS

        lea           xcexit(PC),A2
        move.l        A2,_XCEXIT+2(A4)                 ;patch the jump instruction
        
*------ we are now set up.  wait for a message from our starter
        bsr.w         waitmsg
        move.l        d0,_WBenchMsg(A4)
        move.l        d0,-(SP)
*
        move.l        d0,a2                            ; get first argument
        move.l        sm_ArgList(a2),d0
        beq.s         do_cons
        move.l        DOSBase(A4),a6
        move.l        d0,a0
        move.l        wa_Lock(a0),d1
        callsys       DupLock
        move.l        d0,__curdir(A4)
        move.l        d0,d1
        callsys       CurrentDir
do_cons:
        move.l        sm_ToolWindow(a2),d1             ; get the window argument
        beq.s         do_main
        move.l        #MODE_OLDFILE,d2
        callsys       Open
        move.l        d0,stdin(A4)
        beq.s         do_main
        lsl.l         #2,d0
        move.l        d0,a0
        move.l        fh_Type(a0),pr_ConsoleTask(A3)
do_main:
        move.l        _WBenchMsg(A4),a0                ; get address of workbench message
        move.l        a0,-(a7)                         ; push argv
        pea           NULL(A4)                         ; push argc
        move.l        sm_ArgList(a0),a0                ; get address of arguments
        move.l        wa_Name(a0),_ProgramName(A4)     ; get name of program
*------ For startup from workbench, there is no reason to go through
*------ The fancy stuff to make the startup work.  Just run the
*------ Code directly
        jsr           __fpinit(PC)
        jsr           __main
        moveq.l       #0,d0
        bra.w         exit2
        

************************************************************************
************************************************************************
*************                                              *************
************* Start of code that is copied to fake segment *************
*************                                              *************
************************************************************************
************************************************************************

copybeg:
        DC.L        (copyend-copybeg)/4
next:   DC.L        0
AutoLoader:
        move.l        a7,d0
        movem.l       d1-d6/a0-a6,-(a7)
        move.l        d0,a5
        move.l        AbsExecBase.w,a6
        lea           LinkerDB,A4                ; load base register
        move.l        a7,_StackPtr(A4)

        bsr.w         openDOS
        suba.l        a1,a1

        suba.l        a1,a1
        callsys       FindTask
        move.l        d0,A3

        move.l        TC_SPLOWER(A3),__base(A4)        ; set base of stack
        add.l         #128,__base(A4)          ;allow for parms overflow

        move.l        pr_SegList(A3),a0        ;locate the segment list
        add.l         A0,A0
        add.l         A0,A0                    ;and convert to an APTR
        move.l        12(A0),A1                ;so we can access the segments
        add.l         A1,A1
        add.l         A1,A1

        move.l        (A1),seglist(A4)        ;save the remainder for later
        move.l        (A1),12(A0)             ;cut off our segment

        move.l        __curdir(A4),D1
        move.l        DOSBase(A4),a6
        callsys       CurrentDir

*------ Open Nil: and set up it's input buffer so 
*------ ReadArgs will work.
        lea           nil(pc),a0
        move.l        a0,d1
        move.l        #MODE_OLDFILE,d2
        callsys       Open
        move.l        d0,pr_CIS(a3)
        add.l         d0,d0
        add.l         d0,d0
        move.l        d0,a2
        move.l        argslen(pc),d2
        move.l        d2,fh_End(a2)
        move.l        #0,fh_Pos(a2)        
        
        move.l        AbsExecBase.w,a6
        move.l        d2,d0
        moveq.l       #0,d1
        callsys       AllocMem
        tst.l         d0
        beq.b         segexit

        move.l        d0,a0
        lea           commandbuf(pc),a1
        adda.l        proglen(pc),a1
        addq.l        #3,a1               * 3 - is for quotes and trailing space
        subq.l        #1,d2
        blt.b         donecpy
cpy:
        move.b        (a1)+,(a0)+
        dbf           d2,cpy        
            
donecpy:
        lsr.l         #2,d0
        move.l        d0,fh_Buf(a2)
        
*------ Use the cli that our creator set up for us.
        move.l        cli(pc),pr_CLI(a3)

        jsr           __fpinit
        pea           commandbuf(PC)
        jsr           __main                        ; call C entrypoint
        addq          #4,A7


*------ Unlock the directory we set up for them
segexit:
        move.l        DOSBase(A4),a6
        move.l        __curdir(A4),D1
        callsys       UnLock

        moveq.l       #0,d0                        ; set successful status
xcexit:
exit2:
        IFEQ          TINY
        move.l        _ONEXIT(A4),d0                ; exit trap function?
        beq.s         exit3
        move.l        d0,a0
        jsr           (a0)
        ENDC

exit3:
*------ These must not be PC relative calls since this code is copied
        jsr           __fpterm
        jsr           _MemCleanup                ; cleanup leftover memory alloc.


done_1c:
*------ if we ran from CLI, skip workbench cleanup:
        tst.l         _WBenchMsg(A4)
        beq.s         exitToDOS
        move.l        DOSBase(A4),a6
        move.l        stdin(A4),d1
        beq.s         done_4
        callsys       Close
done_4:

*------ return the startup message to our parent
*        we forbid so workbench can't UnLoadSeg() us
*        before we are done:
        move.l        __curdir(a4),d1
        beq.s         done_5
        callsys       UnLock

done_5:
        move.l        AbsExecBase.w,A6
        callsys       Forbid
        move.l        _WBenchMsg(A4),a1
        callsys       ReplyMsg
        bra.b         noCIS

*------ this rts sends us back to DOS:
exitToDOS:
        jsr           __freecli
        
        move.l        AbsExecBase.W,a6
        sub.l         a1,a1
        callsys       FindTask
        move.l        d0,a0
        
        move.l        pr_CIS(a0),d1      * test for our input file handle
        beq.b         noCIS

        clr.l         pr_CIS(a0)
        move.l        d1,d2
        add.l         d1,d1
        add.l         d1,d1
        move.l        d1,a2
        
*------ First free the buffer we allocated
        move.l        fh_Buf(a2),d1
        add.l         d1,d1
        add.l         d1,d1
        move.l        d1,a1
        move.l        argslen(pc),d0
        callsys       FreeMem

*------ Restore fields to 0 
        clr.l         fh_Buf(a2)
        clr.l         fh_End(a2)

*------ Close Nil:        
        move.l        d2,d1
        move.l        DOSBase(A4),a6
        callsys       Close

noCIS:
        movea.l       _StackPtr(a4),a3            ; save old stack pointer 

*------ They are done so unload them
        move.l        seglist(A4),D1
        move.l        DOSBase(A4),a6
        callsys       UnLoadSeg

        move.l        a6,a1
        move.l        AbsExecBase.w,a6
        callsys       CloseLibrary                ; close Dos library


        movea.l       a3,SP                       ; restore stack ptr
        movem.l       (a7)+,d1-d6/a0-a6
        rts

*-----------------------------------------------------------------------
*  Open the DOS library:

openDOS
                lea          DOSName(A4),A1
                moveq.l      #0,D0
                callsys      OpenLibrary
                move.l       D0,DOSBase(A4)
                beq          noDOS
                rts
*-----------------------------------------------------------------------
noDOS:
                moveq.l      #100,d0
                bra          exit2

cli:           dc.l         0
argslen:        dc.l         0
proglen:        dc.l         0
nil:            dc.b         'NIL:',0,0
                
copyend:
commandbuf:

************************************************************************
************************************************************************
**************                                            **************
************** End of code that is copied to fake segment **************
**************                                            **************
************************************************************************
************************************************************************

*-----------------------------------------------------------------------
* This routine gets the message that workbench will send to us
* called with task id in A3

waitmsg:
                lea        pr_MsgPort(A3),a0          ; our process base
                callsys    WaitPort
                lea        pr_MsgPort(A3),a0          ; our process base
                callsys    GetMsg
                rts

        csect        __MERGED,1,0,0,4

*------        These externals control the task that is started
        xref        _BackGroundIO                ; flag to indicate background task
*                                                ; wants to do I/O
        xref        __stack                      ; stack space for created task
        xref        __procname                   ; name of process to be created
        xref        __priority                   ; priority to create process at

        xref        DOSBase
        xref        MathBase
        xref        MathTransBase
*
        xdef        NULL,SysBase,_WBenchMsg
        xdef        __curdir
        xdef        _OSERR,_FPERR,_SIGFPE,_ONERR,_ONEXIT,_ONBREAK
        xdef        _SIGINT,_Backstdout
        xdef        _ProgramName,_StackPtr,__base
*
memlist        ds.b        LN_SIZE
               dc.w        1
               dc.l        MEMF_PUBLIC
memsize        dc.l        copyend-copybeg

_XCEXIT      
@_XCEXIT       dc.w        $4ef9
               dc.l        0
seglist        dc.l        0
NULL           dc.l        0                ;
__base         dc.l        0                ; base of stack
_OSERR         dc.l        0
_FPERR         dc.l        0
_SIGFPE        dc.l        0
_SIGINT        dc.l        0
_ONERR         dc.l        0
_ONEXIT        dc.l        0
_ONBREAK       dc.l        0
__curdir       dc.l        0
SysBase        dc.l        0
_WBenchMsg     dc.l        0
_StackPtr      dc.l        0
dosCmdLen      dc.l        0
dosCmdBuf      dc.l        0
stdin          dc.l        0
_Backstdout    dc.l        0                ;filehandle for I/O
_ProgramName   dc.l        0
DOSName        dc.b        'dos.library',0
current_window dc.b        '*',0
               END
