


                      HOW TO BUILD APPLICATIONS FOR SIM
                      =================================



This documentation should give the interested user an overview of how to do
selfmade applications for SIM and how to use it as built-in debugger. It is
not complete by now. Because SIM is not finished, there may be additional
features later.




Entry Points
~~~~~~~~~~~~

SIM  basically  has  a number of public entry points which activate it.  They
all are located at specific positions at the start of the SIM binary:


        o simb_EJSR: A subroutine call entry point. SIM expects the address
          where to continue on the actual stack. This is the simplest way to
          invoke SIM. A sample assembly language code would look like this:


                ...
                pea     ReturnPoint(pc)         ;next PC
                move.l  SIMStart(pc),-(sp)      ;start address of SIM
                addq.l  #simb_EJSR,(sp)
                rts
            ReturnPoint:
                ...


        o simb_EException: A direct entry point for Exec's trap handler.
          Expects to find a longword containing the vector number and an
          exception frame on the stack. The address of this entry can simply
          be written in the TC_TRAPCODE field of a tash structure:


                ...
                move.l  4.w,a6
                move.l  ThisTask(a6),a0
                move.l  SIMStart(pc),a1
                lea     simb_EExceptions(a1),a1
                move.l  a1,TC_TRAPCODE(a0)
                ...


        o simb_EVectors: This field contains the offset from the start of SIM
          to a list of exception entry points. This allows to catch only
          exceptions of a certain type. Simply add this offset to the start
          address of SIM, add the vector number multiplied by four and write
          the result to the proper place in the vector base. A sample code
          to catch only TRACE exceptions would look like this:


                ...
                move.l  SIMStart(pc),a0
                add.l   simb_EVectors(a0),a0
                lea     9*4(a0),a0
                bsr     Get_VBR_To_D0
                move.l  d0,a1
                move.l  a0,9*4(a1)
                ...


        o simb_EServer: An entry point for a debugger. Contrarily to the
          other entries, SIM uses the context which is stored in the simb_D*
          variables. The register set when calling this entry is lost.
          A sample call that invokes SIM with a context to debug a program
          at $70000:


                ...
            ;Set length of filled structure (rest will be zeroed up to
            ;simb_Text) and name of server.
                move.l  SIMStart(pc),a0
                move.l  #simb_DQuitPC-simb_DLength,simb_DLength(a0)
                move.l  #"TEST",simb_DName(a0)

            ;Clear registers d0-a6.
                moveq   #14,d0
                lea     simb_DRegisters(a0),a1
            Clear:
                clr.l   (a1)+
                dbra    d0,Clear

            ;Use current USP (assuming that this is called in user mode),
            ;return address must be on the stack.
                move.l  a7,simb_DUSPreg(a0)

            ;Use current ISP and MSP, therefore set these two to 0.
                clr.l   simb_DISPreg(a0)
                clr.l   simb_DMSPreg(a0)

            ;Start at $70000, all bits cleared in SR. Then start SIM.
                move.l  #$70000,simb_DPCreg(a0)
                clr.w   simb_DSRreg(a0)
                jmp     simb_EServer(a0)
                ...




Initialisation
~~~~~~~~~~~~~~

Before SIM can be called throught any of these entry points, four things
must be done first:


        o simb_Plane and simb_Backup must be set correctly. simb_Plane must
          point to an $a000 bytes long space in *GRAPHICS* memory. simb_Backup
          can either be 0 or it must point to another $a000 bytes long space of
          memory. If backup is 0, SIM puts its display at the address stored
          in simb_Plane and the contents there are lost. If backup is not 0,
          SIM first copies the $a000 bytes there to the address stored in
          simb_Backup before it sets up its display at simb_Plane. It copies
          the memory back when it exits. A sample setup for an allocated display
          would look like this:
        

                ...
                move.l  4.w,a6
                move.l  #$a000,d0
                moveq   #3,d1
                jsr     _LVOAllocMem(a6)
                tst.l   d0
                beq     ERROR
                move.l  SIMStart(pc),a0
                move.l  d0,simb_Plane(a0)
                clr.l   simb_Backup(a0)
                ...


        o simb_WMStart and simb_WMLength must be set to the start and the
          length of a chunk of memory SIM can use for work memory. If none
          is used, clear both fields. The work memory is used to store
          symbols defined from within SIM, external history, etc.

        o simb_VBR must contain the address of the current vector base.
          This is important, otherwise SIM will not work. The following
          code does this under regular OS:


                ...
                bsr     Get_VBR_To_D0
                move.l  SIMStart(pc),a0
                move.l  d0,simb_VBR(a0)
                ...

            Get_VBR_To_D0:
                move.l  4.w,a6
                btst    #AFB_68010,AttnFlags+1(a6)
                beq.s   M68000
                lea     GetVBR(pc),a5
                jmp     _LVOSupervisor(a6)
            GetVBR:
                movec   vbr,d0
                rte
            M68000:
                moveq   #0,d0
                rts

        o A call to simb_EInit should be done if this is not a fresh copy
          of SIM. If a tool eg. uses a resident copy of SIM which has
          previously been used, it can check if it is still okay. The
          function returns 0 in d0 if this copy is not okay, else -1.




The Debug Server Feature
~~~~~~~~~~~~~~~~~~~~~~~~

Additionally to the entry points SIM also allows to return the current context
to another program. This is done with the 'Quit' command. SIM stores the current
registers into the appropriate simb_D* variables and loads the simb_DQuit*
variables into the appropriate registers and jumps to the address stored
in simb_DQuitPC. The program that starts at that address can then read the
registers from SIM again. The four bytes before the new PC must contain
the longword 'OKAY', which is used to check if SIM can actually exit there.


A sample code that can be used to abort any program that is debugged under
normal OS:

                ...
                move.l  SIMStart(pc),a0

            ;Set PC and SR (user mode)
                pea     QuitFunction(pc)
                move.l  (sp)+,simb_DQuitPC(a0)
                clr.w   simb_DQuitSR(a0)

            ;Use the same stack pointers and DMACON/INTENA settings.
                clr.l   simb_DQuitUSP(a0)
                clr.l   simb_DQuitISP(a0)
                clr.l   simb_DQuitMSP(a0)
                clr.l   simb_DQuitDMA(a0)
                ...

                dc.b    "OKAY"
            QuitFunction:

            ;Remember SIMs address which may have changed.
                lea     SIMStart(pc),a0
                move.l  a6,(a0)

            ;Ask if user really want's to abort, eg. with a requester.
            ;Function returns a nonzero value if okay, in which case
            ;a clean up function does the rest.
                bsr     Ask_If_Quit_Okay
                tst.l   d0
                bne     CleanUp

            ;Return control to SIM. The simb_D* fields are still set.
            ;Only the simb_DLength is cleared by SIM and must therefore
            ;be set again.
                move.l  SIMStart(pc),a0
                move.l  #simb_DQuitPC-simb_DLength,simb_DLength(a0)
                jmp     simb_EServer(a0)





Symbols
~~~~~~~

SIM manages a symbol list. It is devided into an external part which can
be furnished by whatever invoked SIM and an internal part which will be
located in SIMs work memory and contains the symbols defined by the user
while working with SIM.

The buildup of a symbol list is described in the sim.i include file.
A sample source that stores symbols in a new list:

        
        ; IN:
        ;       D0  Value of the symbol.
        ;       D1  Length of symbol name in chars.
        ;       A0  First byte of symbol name.
        ;       A1  Previous symbol list or 0 when called for the first time.
        ;
        ; OUT:
        ;       D0  0 if no more memory, else symbol list
        ;
        StoreSymbol:
                movem.l d1-a6,-(sp)
                move.l  d1,d2
                addq.l  #8,d1           ;7 bytes for length, next and value
                and.w   #$fffe,d1       ;plus round up to even
                move.l  d0,d6
                move.l  a1,d7
        
        ;Check if there already is a symbol list and that the symbol will fit.
                beq.s   NewNode
                cmp.l   ssym_Free(a1),d1
                bls.s   Store
        
        ;Allocate and init a new node.
        NewNode:
                bsr     Get_Two_KB_Of_Memory
                beq.s   Failure
                exg.l   d0,a1
                move.l  d0,ssym_Next(a1)
                clr.l   ssym_NumOf(a1)
                move.l  #ssym_First,ssym_Used(a1)
                move.l  #$800,ssym_Size(a1)
                clr.l   ssym_Highest(a1)
                move.l  #-1,ssym_Lowest(a1)
                move.l  #$800-ssym_First,ssym_Free(a1)
        ;Change this one to use a unique key such as eg. the program start address.
                move.l  #"YAHO",ssym_Key(a1)
        
        ;Copy symbol name and refresh the node.
        Store:  addq.l  #1,ssym_NumOf(a1)
                move.l  a1,a2
                add.l   ssym_Used(a1),a2
                add.l   d1,ssym_Used(a1)
                sub.l   d1,ssym_Free(a1)
                move.l  d6,(a2)+
                move.w  d1,(a2)
                subq.w  #4,(a2)+
                move.b  d2,(a2)+
        
        CName:  move.b  (a0)+,(a2)+
                subq.b  #1,d2
                bne.s   CName
        
                cmp.l   ssym_Highest(a1),d6
                bls.s   NoHigher
                move.l  d6,ssym_Highest(a1)
        NoHigher:
                cmp.l   ssym_Lowest(a1),d6
                bhs.s   NoLower
                move.l  d6,ssym_Lowest(a1)
        NoLower:
                move.l  a1,d0
        
        ;Return.
        Done:   movem.l (sp)+,d1-a6
                rts
        
        ;No memory.
        Failure:
                moveq   #0,d0
                bra.s   Done
                
        
        ;Get 2Kb of memory.
        Get_Two_KB_Of_Memory:
                movem.l d1-a6,-(sp)
                move.l  4.w,a6
                move.l  #$800,d0
                moveq   #1,d1
                jsr     -198(a6)
                movem.l (sp)+,d1-a6
                rts



After a symbol list has been generated, it can be added to SIM by writing
its first node start address to simb_Symbols:

                ...
                move.l  SIMStart(pc),a0
                move.l  SIMSymbolList(pc),simb_Symbols(a0)
                ...


