;--------------------------------------------------------------------
; Multipart support for trackmos
;
; In my former demo system I used a6 as a base pointer to the demo
; framework and a4 as a pointer to my demo part variables. That
; seemed a bit wasteful and so the new system uses one pointer for
; both framework AND part variables for the current part. This,
; however, means that the framework base needs to be relocated
; depending on the part because, of course, the size of the
; local variables may vary.
;
; You must start your local variable space with
;
;    STRUCTURE   PartData,fw_SIZEOF
;
; and end it with
;
;        LABEL   pd_SIZEOF
;
;--------------------------------------------------------------------
; Allocate the part variables
;
; The first thing each demo part needs to do is to call InitPart.
; I repeat: Start your code with:
;        IFD     FW_DEMO_PART
;        move.l  #pd_SIZEOF,d0
;        CALLFW  InitPart
;        ENDC
;
; Otherwise, no part variables are available via a6 and no other
; framework calls can be made (the jumping table is not available).
;
; After this call, a6 will be pointing to the new framework base
; with all your variable space cleared.
;
; Make sure that you don't free the memory allocated here until
; the end of your part! The demo framework will call
; RestoreFrameworkBase automatically at exit of your part.
;
; Flushes the caches (if available), too.
;
; In : d0 = pd_SIZEOF (part data structure size including fw_SIZEOF)
; Out: a6 = new base pointer
; Trashes: everything except a5/a6
;
fw_InitPart:
        PUTMSG  10,<"%d: InitPart(%ld)">,fw_FrameCounter-2(a6),d0
        PUSHM   d0
        bsr     fw_RestoreFrameworkBase
        POPM

        DISABLE_INTS
        move.l  fw_PartFwBase(a6),a0
        move.l  d0,fw_PartDataSize(a6)
        add.l   #-FWLVOPOS,d0
        bsr     fw_AllocFast
        lea     -FWLVOPOS(a0),a1
        PUTMSG  40,<"LVOs from %p to %p">,a1,a0
        move.l  a6,a0
        move.l  a6,a3
        move.l  a1,a6
        move.w  #(fw_SIZEOF/4)-1,d7
        PUTMSG  40,<"Copying %d LW from %p to %p">,d7,a0,a1
.copynew
        move.l  (a0)+,(a1)+
        dbra    d7,.copynew

        move.l  a6,fw_PartFwBase(a6)
        move.l  fw_PartDataSize(a6),d7
        sub.l   #fw_SIZEOF-3,d7
        lsr.l   #2,d7
        beq.s   .skipclr
        subq.w  #1,d7
        moveq.l #0,d0
.clrnew
        move.l  d0,(a1)+
        dbra    d7,.clrnew
.skipclr
        move.l  a6,a0
        lea     fw_lvo_offsets(pc),a1
.lvoloop
        move.w  (a1)+,d0
        beq.s   .lvodone
        lea     -2(a1,d0.w),a2
        move.l  a2,-(a0)
        move.w  #%0100111011111001,-(a0) ; $4ef9 jmp x.L
        bra.s   .lvoloop
.lvodone

        IF      FW_MULTITASKING_SUPPORT
        ; The task list header is now broken after copying! We need to fix it.
        lea     fw_Tasks(a6),a1
        bsr     fw_RelocateList
        bsr     fw_FixA6BaseInTaskStacks
        ENDC

        lea     fw_BasePtr(pc),a0
        move.l  a6,(a0)
        bsr     fw_FlushCaches
        ENABLE_INTS
        PUTMSG  20,<"%d: InitPart done %p">,fw_FrameCounter-2(a6),a6
        rts

;--------------------------------------------------------------------
; Restore global framework base
;
; Removes the custom framework variable space and makes the global
; one active again.
;
; Out: a6 = new base pointer
; Trashes: everything except a5/a6
;
fw_RestoreFrameworkBase:
        PUTMSG  10,<"%d: RestoreFrameworkBase(%p)">,fw_FrameCounter-2(a6),a6
        DISABLE_INTS
        move.l  fw_PartFwBase(a6),a0
        move.l  fw_PrimaryFwBase(a6),a1
        PUTMSG  40,<"Part %p, Primary %p">,a0,a1
        cmp.l   a1,a0
        beq.s   .noold
        move.l  a0,a3
        PUTMSG  40,<"Restore from %p to %p">,a0,a1
        move.l  a1,a6
        move.w  #(fw_SIZEOF/4)-1,d7
.copyold
        move.l  (a0)+,(a1)+
        dbra    d7,.copyold

        IF      FW_MULTITASKING_SUPPORT
        bsr     fw_FixA6BaseInTaskStacks
        ENDC

        move.l  a6,fw_PartFwBase(a6)
        clr.l   fw_PartDataSize(a6)

        IF      FW_MULTITASKING_SUPPORT
        ; The task list header is now broken after copying! We need to fix it.
        lea     fw_Tasks(a6),a1
        bsr.s   fw_RelocateList
        ENDC

        lea     fw_BasePtr(pc),a0
        move.l  a6,(a0)
.noold
        ENABLE_INTS
        rts

;--------------------------------------------------------------------
; Helper function to relocate a doubly-linked list
;
; When relocating the framework base structure, we need to fix the
; contents of lists, otherwise BadThings(TM) will happen.
; This function goes through the relocated list and fixes the linkage.
;
; In: a1 = ListHead
; Trashes: a0/a1
;
        IF      FW_MULTITASKING_SUPPORT
fw_RelocateList:
        move.l  LH_HEAD(a1),a0
        subq.l  #4,a0
        cmp.l   LH_TAILPRED(a1),a0
        bne.s   .nonewlist
        PUTMSG  10,<"Fixup empty list %p">,a1
        move.l  a1,LH_TAILPRED(a1)  ; fix empty list
        addq.l  #4,a1
        move.l  a1,-(a1)
        bra.s   .listfixed
.nonewlist
        PUTMSG  10,<"Fixup existing list %p">,a1
        move.l  a1,4+LN_PRED(a0)        ; fixup LN_PRED of first node (points to LH_HEAD)
        move.l  LH_TAILPRED(a1),a0  ; get last node
        addq.l  #4,a1           ; now points to LH_TAIL
        move.l  a1,LN_SUCC(a0)  ; fixup LN_SUCC of last node (pointing to LH_TAIL)
.listfixed
        rts
        ENDC

;--------------------------------------------------------------------
; Running tasks will have the old framework base stored in their
; register context. We need to fix these occurrences. Note that
; this is not completely fool-proof, but should work for 99% of the
; cases (there shouldn't be framework-relative things).
;
; In: a3 = old base
;     a6 = new base
fw_FixA6BaseInTaskStacks:
        lea     fw_Tasks(a3),a1
.loop
        TSTNODE a1,a1
        beq.s   .done
        move.l  ft_USP(a1),a0
        lea     ft_StackEnd(a1),a2
.findloop
        cmp.l   a0,a2
        beq.s   .loop
        cmp.l   (a0),a3
        bne.s   .notmatched
        PUTMSG  10,<"Replacing base at %p">,a0
        move.l  a6,(a0)
.notmatched
        addq.l  #2,a0
        bra.s   .findloop
.done   rts


fw_lvo_offsets:
FWGENLVOTABLE SET 1
        include "framework_lvos.i"
        dc.w    0
FWGENLVOTABLE SET 0
