;******************************************************************************
;            (C) Copyright 1992-1995, Radius, Inc. and Atari Corp.
; Secret and confidential                                   All rights reserved
;******************************************************************************
; This source code and any compilation or derivative thereof is the joint
; property of Radius, Inc. and Atari Corp.  This code is provided pursuant
; to a Software License Agreement and/or Software Development Contract.  This
; code is the proprietary information of Radius, Inc. and Atari Corp. and is
; confidential in nature.  Its use and dissemination by any party other than
; Atari Corp. are strictly limited by the confidential information provisions
; of the Agreement or Contract referenced above.
;******************************************************************************
; 100 25 Jan 96 JPE     Initial revision.
; 101 16 Feb 96 JPE     Use frame limit in HYPER structure. Modified logic
;                       for loading third leadin. Logic added to prevent
;                       branching to unloaded leadin. Added logic to support
;                       automatic play modes.
;******************************************************************************

        .nlist
        .include    'params.inc'
        .include    'offset.inc'
        .list

;==============================================================================
;                               Navigate
;==============================================================================

Navigate::
        movem.l a0-a1/d0,-(sp)          ; Save registers

        tst     initLoad                ; Initial load?
        beq.s   GetCurrent              ; No, get current element

        clr     initLoad                ; Clear flag
        clr     branchPending           ; Clear flag
        movea.l pList,a0                ; Current element
        bra     SetPending              ; Set pending element

GetCurrent:
        movea.l pList,a0                ; Current element
        btst.b  #bLEADOUT,attrib(a0)    ; Leadout?
        beq.s   CurrentAttrib           ; Skip if not

        move.l  pPath,pendingPath       ; Mark location on path record

CurrentAttrib:
        move.b  attrib(a0),d0           ; Get attributes
        btst.l  #bLOOP,d0               ; Loop?
        bne.s   LoopElement             ; Yes, handle it

        btst.l  #bLEADIN,d0             ; Leadin?
        bne.s   LeadinElement           ; Yes, handle it

        bra     NormalCase              ; Handle normal case

LoopElement:
        tst     autoPilot               ; On automatic pilot?
        beq.s   LoopNormal              ; Skip if not

        bsr     NavigatePath            ; Get leadin from path record
        bra     SetPending              ; Set pending element

LoopNormal:
        move.l  a0,baseAddress          ; Save for use in path record
        lea     fwdLink(a0),a0          ; Skip to forward links
        move.l  a0,linkTable            ; Pointer to link table
        move.l  a0,pLink                ; Pointer to first branch
        adda    (a0),a0                 ; Add offset
        move    #1,leadinCnt            ; Initialize leadin count
        move    #1,branchPending        ; Set branch pending flag
        lea     branchTable,a1          ; Start of branch table
        clr.l   (a1)+                   ; Table[0] = 0
        clr.l   (a1)+                   ; Table[1] = 0
        clr.l   (a1)                    ; Table[2] = 0
        bra     SetPending              ; Set pending element

LeadinElement:
        tst     autoPilot               ; On automatic pilot?
        bne.s   NormalCase              ; Yes, treat normally

        tst     branchPending           ; Is a branch pending?
        beq.s   NormalCase              ; No, treat normally

        tst     optionIndex             ; Option selected?
        bge.s   MaybeBranch             ; Yes, try to start new branch

        cmpi    #1,leadinCnt            ; Done just one leadin?
        beq.s   NextLeadin              ; Yes, second is no-brainer

        cmpi    #3,leadinCnt            ; Finished 3 leadins?
        beq     NavigateExit            ; Yes, return null pointer

        cmpi    #3,loopIndex            ; Started final loop yet?
        bne     NavigateExit            ; No, return null pointer

        tst     loadDefault             ; OK to load default leadin?
        beq     NavigateExit            ; No, return null pointer

NextLeadin:
        addq    #1,leadinCnt            ; Increment counter
        movea.l pLink,a0                ; Recall place in link table
        lea     2(a0),a0                ; Advance to next entry
        move.l  a0,pLink                ; Remember where we are now
        adda    (a0),a0                 ; Add offset
        bra.s   SetPending              ; Set pending element

MaybeBranch:        
        lea     branchTable,a0          ; Base of branch table
        move    optionIndex,d0          ; Get option index
        lsl     #2,d0                   ; Index by longs
        tst.l   0(a0,d0)                ; Is branch valid?
        beq.s   NextLeadin              ; No, first load the leadin

        movea.l 0(a0,d0),a0             ; Start of branch
        bsr     RecordPath              ; Add entry to path record
        clr     branchPending           ; Clear branch pending flag

NormalCase:
        btst.b  #bSPECIAL,attrib(a0)    ; Special case?
        beq.s   NotSpecial              ; No, just use forward link

        bsr     NavigateSpecial         ; Handle special case
        bra.s   SetPending              ; Set pending element

NotSpecial:
        lea     fwdLink(a0),a0          ; Skip to link
        adda    (a0),a0                 ; Next element

SetPending:
        lea     ctrlList,a1             ; List of unresolved controls
        clr     ctrlCount               ; Clear counter
        clr     ctrlPending             ; Clear pending control flag
        tst     branchPending           ; Is branch pending?
        bne.s   TestControl             ; Yes, don't trash base address!

        move.l  a0,baseAddress          ; May use in path record!

TestControl:
        btst.b  #bCONTROL,attrib(a0)    ; Is this a control element?
        beq.s   FilmPending             ; No, we have a film

        tst     autoPilot               ; On automatic pilot?
        beq.s   UsePlaylistC            ; No, use playlist

        bsr     NavigatePath            ; Use path record
        bra.s   FilmPending             ; Film is guaranteed

UsePlaylistC:
        move    #1,ctrlPending          ; Flag pending control
        bsr     NavigateControl         ; Handle control element
        tst.l   d0                      ; Unresolved element?
        beq.s   TestControl             ; No, look for another control

        move.l  d0,(a1)+                ; Add element to list
        addq    #1,ctrlCount            ; Increment counter
        cmpi    #MAX_CTRL,ctrlCount     ; Exceeded maximum number?
        ble.s   TestControl             ; No, look for another control

        illegal                         ; Trap error

FilmPending:
        tst     ctrlPending             ; Did we bump into a CONTROL?
        beq.s   CheckLeadout            ; Skip if not

        clr     ctrlPending             ; Clear flag
        tst     branchPending           ; Is a branch pending?
        bne.s   CheckLeadout            ; Yes, don't add to path record!!

        bsr     RecordPath              ; Add entry to path record

CheckLeadout:
        tst     huntLeadout             ; Is hunt leadout flag set?
        beq.s   CopyPending             ; No, use this film

        btst.b  #bLEADOUT,attrib(a0)    ; Is this a leadout?
        beq     CurrentAttrib           ; No, find first one

        clr     huntLeadout             ; Clear flag

CopyPending:
        move.l  a0,pPendingList         ; Pending element
        tst     branchPending           ; Is loop branch pending?
        beq.s   NavigateExit            ; Exit if not

        lea     branchTable,a1          ; Base of branch table
        move    leadinCnt,d0            ; Get leadin count
        subq    #1,d0                   ; Make 0-relative
        lsl     #2,d0                   ; Index by longs
        move.l  a0,0(a1,d0)             ; Add entry to branch table

NavigateExit:
        movem.l (sp)+,d0/a0-a1          ; Restore registers
        rts

;------------------------------------------------------------------------------
;                           NavigateSpecial
;------------------------------------------------------------------------------
;   Input:  a0 = address of current playlist element
;
;   Output: a0 = address of next playlist element, according to the rule
;                specified by fRule(a0)
;------------------------------------------------------------------------------

NavigateSpecial:
        move.l  d0,-(sp)                ; Save register

        clr     d0                      ; Get a clean word
        move.b  fRule(a0),d0            ; Get rule number
        lsl     #2,d0                   ; Index by longs
        jsr     navFilmTbl(pc,d0)       ; Call branch table

        move.l  (sp)+,d0                ; Restore register
        rts

navFilmTbl:
        bra.w   TrapBogus               ; 0: undefined!
        bra.w   SkipNavigate            ; 1: SKIP
        bra.w   HyperNavigate           ; 2: HYPER

TrapBogus:
        illegal                         ; Trap bogus rule/type

;------------------------------------------------------------------------------
;                                 SKIP
;------------------------------------------------------------------------------
;   If the fadeOut flag is not set, the first of the two fan-outs is taken;
;   otherwise, the second fan-out is taken.
;------------------------------------------------------------------------------

SkipNavigate:
        lea     skFwdLink(a0),a0        ; Forward link
        adda    (a0),a0                 ; Next playlist entry
        rts

;------------------------------------------------------------------------------
;                                HYPER
;------------------------------------------------------------------------------

HyperNavigate:
        move.l  d0,-(sp)                ; Save register

        tst     autoPilot               ; On automatic pilot?
        beq.s   UsePlaylistH            ; No, use playlist

        bsr     NavigatePath            ; Use path record
        bra.s   HyperExit               ; Exit

UsePlaylistH:
        move.l  a0,baseAddress          ; Set path record base address
        move    hyClickCnt(a0),d0       ; Get click count
        cmp     hyClickThr(a0),d0       ; Met or exceeded threshold?
        bge.s   HyperLinkA              ; Yes, set link A

        move.l  frameNum,d0             ; Current frame number
        cmp.l   hyEndFrm(a0),d0         ; Beyond limit?
        bgt.s   HyperLinkB              ; Yes, set link B

        clr.l   d0                      ; Get a zero
        move.l  d0,a0                   ; Return null value
        bra.s   HyperExit               ; Exit

HyperLinkA:
        lea     hyFwdLinkA(a0),a0       ; Use Link A
        bra.s   SetHyperLink            ; Get linked element

HyperLinkB:
        lea     hyFwdLinkB(a0),a0       ; Use Link B

SetHyperLink:
        adda    (a0),a0                 ; Next element
        btst.b  #bCONTROL,attrib(a0)    ; Did we land on a control?
        bne.s   HyperExit               ; Yes, don't add to path record!

        bsr     RecordPath              ; Add entry to path record

HyperExit:
        move.l  (sp)+,d0                ; Restore register
        rts

;------------------------------------------------------------------------------
;                           NavigateControl
;------------------------------------------------------------------------------
;   Input:  a0 = address of current playlist element
;
;   Output: a0 = address of next playlist element, according to the rule
;                specified by cType(a0)
;           d0 = address of unresolved CONTROL element, or null
;------------------------------------------------------------------------------

NavigateControl:
        clr     d0                      ; Get a clean word
        move.b  cType(a0),d0            ; Get type number
        lsl     #2,d0                   ; Index by longs
        jsr     navCtrlTbl(pc,d0)       ; Call branch table
        rts

navCtrlTbl:
        bra.w   TrapBogus               ; 0: undefined!
        bra.w   BurnNavigate            ; 1: BURN
        bra.w   CountNavigate           ; 2: COUNT
        bra.w   SwitchNavigate          ; 3: SWITCH
        bra.w   CensorNavigate          ; 4: CENSOR
        bra.w   RelayNavigate           ; 5: RELAY
        bra.w   ChangeNavigate          ; 6: CHANGE

;------------------------------------------------------------------------------
;                                 BURN
;------------------------------------------------------------------------------

BurnNavigate:
        movem.l d3/a1/a3,-(sp)          ; Save registers

        lea     buVectors(a0),a1        ; Start of vector data
        move    buPathRec(a0),d3        ; Path record
        bsr     MatchVectors            ; Find best match
        move    d3,buBurnMask(a0)       ; Set burn mask
        move.l  a0,d0                   ; Unresolved control
        movea.l a3,a0                   ; Next element

        movem.l (sp)+,d3/a1/a3          ; Restore registers
        rts

;------------------------------------------------------------------------------
;                                COUNT
;------------------------------------------------------------------------------

CountNavigate:
        move.l  a1,-(sp)                ; Save register

        clr     d0                      ; Get a clean word
        move.b  cFanOut(a0),d0          ; Number of fanouts
        subq    #1,d0                   ; Make 0-relative
        cmp     coCount(a0),d0          ; Compare running count
        ble.s   CountLink               ; Use limit if >=

        move    coCount(a0),d0          ; Otherwise, use running count

CountLink:
        movea.l a0,a1                   ; Copy starting address
        lea     coFwdLink(a0),a0        ; Start of forward links
        add     d0,d0                   ; Two bytes per link
        adda    d0,a0                   ; Target link
        adda    (a0),a0                 ; Next element
        move.l  a1,d0                   ; Unresolved control

        move.l  (sp)+,a1                ; Restore register
        rts

;------------------------------------------------------------------------------
;                                SWITCH
;------------------------------------------------------------------------------

SwitchNavigate:
        movem.l d3/a1/a3,-(sp)          ; Save registers

        lea     swVectors(a0),a1        ; Start of vector data
        clr     d3                      ; Path record (all 0's)
        bsr     MatchVectors            ; Find best match
        movea.l a3,a0                   ; Next element
        clr.l   d0                      ; No unresolved control

        movem.l (sp)+,d3/a1/a3          ; Restore registers
        rts

;------------------------------------------------------------------------------
;                                CENSOR
;------------------------------------------------------------------------------

CensorNavigate:
        move.l  a1,-(sp)                ; Save register

        lea     ceFwdLink1(a0),a0       ; Start of link data
        movea.l a0,a1                   ; Make a copy
        bsr     GetPCIndex              ; Get parental control index
        lsl     #1,d0                   ; Two bytes per link
        lea     0(a0,d0),a0             ; Target link
        tst     (a0)                    ; Null link?
        bne.s   CensorLink              ; No, use it

FindNonZero:
        tst     (a1)                    ; Null link?
        bne.s   UseDummy                ; No, use as dummy

        lea     2(a1),a1                ; Next link
        bra.s   FindNonZero             ; Look for a connected link

UseDummy:
        movea.l a1,a0                   ; Use first nonzero link

CensorLink:
        adda    (a0),a0                 ; Next element
        clr.l   d0                      ; No unresolved control

        move.l  (sp)+,a1                ; Restore register
        rts

;------------------------------------------------------------------------------
;                                RELAY
;------------------------------------------------------------------------------

RelayNavigate:
        move.l  a1,-(sp)                ; Save register

        lea     reCounter(a0),a1        ; Link to associated counter
        adda    (a1),a1                 ; Counter address
        clr     d0                      ; Get a clean word
        move.b  cFanOut(a1),d0          ; Use fanout for threshold
        cmp     coCount(a1),d0          ; Count saturated?
        ble.s   RelayLink2              ; Yes, set link 2

        lea     reFwdLink1(a0),a0       ; Use link 1
        bra.s   SetRelayLink            ; Get linked element

RelayLink2:
        lea     reFwdLink2(a0),a0       ; Use link 2

SetRelayLink:
        adda    (a0),a0                 ; Next element
        clr.l   d0                      ; No unresolved control

        move.l  (sp)+,a1                ; Restore register
        rts

;------------------------------------------------------------------------------
;                                CHANGE
;------------------------------------------------------------------------------

ChangeNavigate:
        clr.l   d0                      ; No unresolved control
        rts

;------------------------------------------------------------------------------
;                             MatchVectors
;------------------------------------------------------------------------------
;   Input:  a0 = address of current element
;           a1 = base of vector array
;           d3 = path record
;
;   Output: a3 = address of linked element whose associated psychometric
;                values most closely match player's current profile.
;           d3 = match mask
;
;   Local:  a2 = pointer to link array
;           d0 = Manhattan distance
;           d1 = running minimum distance
;           d2 = counter
;           d4 = counter at minimum
;           d5 = unburned link counter
;------------------------------------------------------------------------------

MatchVectors:
        movem.l d0-d2/d4-d5/a1-a2,-(sp) ; Save registers

        move.l  #-1,d1                  ; Maximize minimum distance
        clr.b   d2                      ; Initialize counter
        clr     d5                      ; Unburned links
        clr     d4                      ; Get a clean word
        move.b  cFanOut(a0),d4          ; Number of fanouts
        mulu    #10,d4                  ; 10 bytes per vector
        lea     0(a1,d4),a2             ; Start of link array

MatchLoop:
        btst.l  d2,d3                   ; Has link been burned?
        bne.s   NextVector              ; Yes, skip match

        addq    #1,d5                   ; Increment unburned link count
        bsr.s   ManhattanDist           ; Compute distance
        cmp.l   d0,d1                   ; Compare with prior minimum
        bcs.s   NextVector              ; Skip if not less

        move.l  d0,d1                   ; Update minimum
        movea.l a2,a3                   ; Remember associated link
        move.b  d2,d4                   ; Remember associated count

NextVector:
        lea     10(a1),a1               ; Next vector
        lea     2(a2),a2                ; Next link
        addq.b  #1,d2                   ; Increment counter
        cmp.b   cFanOut(a0),d2          ; Finished list?
        bne.s   MatchLoop               ; Loop if not

        adda    (a3),a3                 ; Next element
        clr     d3                      ; Get a clean word
        cmpi    #1,d5                   ; Only one unburned link?
        beq.s   MatchExit               ; Yes, leave it

        bset.l  d4,d3                   ; Mask for matched link

MatchExit:
        movem.l (sp)+,d0-d2/d4-d5/a1-a2 ; Restore registers
        rts

;------------------------------------------------------------------------------
;                            ManhattanDist
;------------------------------------------------------------------------------
;   Input:  a1 = address of target pyschometric vector
;
;   Output: d0 = Manhattan distance between target vector and player's
;                psychometric profile.
;------------------------------------------------------------------------------

ManhattanDist:
        movem.l d1-d2/a0-a1,-(sp)       ; Save registers

        lea     profile,a0              ; Address of player's profile
        clr.l   d0                      ; Clear accumulated value
        clr.l   d1                      ; Clear working register
        moveq   #5-1,d2                 ; Count 5 components

DistLoop:
        move    (a0)+,d1                ; Get player's component
        sub     (a1)+,d1                ; Subtract target component
        bge.s   AccumAbs                ; Accumulate if positive

        neg     d1                      ; Flip sign if negative

AccumAbs:
        add.l   d1,d0                   ; Accumulate absolute value
        dbra    d2,DistLoop             ; Loop until done

        movem.l (sp)+,d1-d2/a0-a1       ; Restore registers
        rts

;------------------------------------------------------------------------------
;                              RecordPath
;------------------------------------------------------------------------------

RecordPath:
        movem.l a0-a1,-(sp)             ; Save registers

        movea.l pPath,a1                ; Pointer to path record
        suba.l  baseAddress,a0          ; Relative playlist address
        move    a0,(a1)+                ; Add entry to path record
        cmpa.l  #pathEnd,a1             ; Reached end of storage?
        bne.s   MarkEnd                 ; Continue if not

        illegal                         ; Trap overrun

MarkEnd:
        move    #$eeee,(a1)             ; Mark end of path
        move.l  a1,pPath                ; Update pointer

        movem.l (sp)+,a0-a1             ; Restore registers
        rts

;------------------------------------------------------------------------------
;                             NavigatePath
;------------------------------------------------------------------------------

NavigatePath:
        move.l  a1,-(sp)                ; Save register

        movea.l pPath,a1                ; Pointer to path record
        adda    (a1)+,a0                ; Target film in playlist
        move.l  a1,pPath                ; Update pointer

        move.l  (sp)+,a1                ; Restore register
        rts

;==============================================================================
;                            GetCensorMask
;==============================================================================

GetCensorMask::
        movem.l d1-d2/a0-a1,-(sp)       ; Save registers

        lea     fwdLink(a0),a0          ; Start of forward links
        clr     d1                      ; Clear counter
        clr     d2                      ; Clear mask

CensorLoop:
        movea.l a0,a1                   ; Copy link address
        adda    (a1),a1                 ; Go to linked element
        btst.b  #bCONTROL,attrib(a1)    ; Control element?
        beq.s   CensorNext              ; Skip if not

        cmpi.b  #CENSOR,cType(a1)       ; CENSOR element?
        bne.s   CensorNext              ; Skip if not

        bsr     GetPCIndex              ; Get parental control index
        lsl     #1,d0                   ; Two bytes per link
        tst     ceFwdLink1(a1,d0)       ; Null link?
        bne.s   CensorNext              ; Skip if not

        bset.l  d1,d2                   ; Set mask bit

CensorNext:
        lea     2(a0),a0                ; Next link
        addq    #1,d1                   ; Increment counter
        cmpi    #3,d1                   ; Done all three links?
        bne.s   CensorLoop              ; Repeat loop if not

        move    d2,d0                   ; Return mask in d0

        movem.l (sp)+,d1-d2/a0-a1       ; Restore registers
        rts

;------------------------------------------------------------------------------
;                              GetPCIndex
;------------------------------------------------------------------------------

GetPCIndex:
        move    minViolence,d0          ; Get violence control
        lsl     #1,d0                   ; Shift left one bit
        or      minSex,d0               ; Merge sex control
        rts

;==============================================================================
;                             ResolveCtrl
;==============================================================================
;   Resolves associated BURN or COUNT control elements after a film segment
;   has been played.
;
;   Input:  a2 = address of buffer control block
;==============================================================================

ResolveCtrl::
        movem.l d0-d1/a1/a3,-(sp)       ; Save registers

        move    resolveCnt(a2),d0       ; # of unresolved elements
        beq.s   ResolveExit             ; If zero, nothing to do

        lea     resolveList(a2),a1      ; Where pointers are stored

ResolveLoop:
        movea.l (a1)+,a3                ; Pointer to control element
        btst.b  #bCONTROL,attrib(a3)    ; Is it a really a control?
        bne.s   ResolveType             ; Yes, get type

        illegal                         ; Oops, trap error

ResolveType:
        cmpi.b  #BURN,cType(a3)         ; BURN element?
        beq.s   ResolveBurn             ; Yes, handle it

        cmpi.b  #COUNT,cType(a3)        ; COUNT element?
        beq.s   ResolveCount            ; Yes, handle it

        illegal                         ; Trap error

ResolveBurn:
        move    buBurnMask(a3),d1       ; Get burn mask
        or      d1,buPathRec(a3)        ; Set bit in path record
        bra.s   ResolveNext             ; Resolve next element

ResolveCount:
        addq    #1,coCount(a3)          ; Increment count variable

ResolveNext:
        subq    #1,d0                   ; Decrement counter
        bne.s   ResolveLoop             ; Loop until done

ResolveExit:
        movem.l (sp)+,d0-d1/a1/a3       ; Restore registers
        rts

;==============================================================================
;                            ResetPlaylist
;==============================================================================

ResetPlaylist::
        movem.l d0/a0-a1,-(sp)          ; Save registers

        lea     playList,a0             ; Start of playlist
        lea     playListEnd,a1          ; End of playlist

ResetNext:
        cmpa.l  a0,a1                   ; Reached end of list?
        beq.s   ResetExit               ; Yes, we're done

        move.b  attrib(a0),d0           ; Get attribute field
        clr.b   history(a0)             ; Clear history field
        btst.l  #bSPECIAL,d0            ; Special film element?
        beq.s   TryControl              ; No, look for control

        bsr.s   ResetSpecial            ; Yes, handle it
        bra.s   ResetNext               ; Do next element

TryControl:
        btst.l  #bCONTROL,d0            ; Control element?
        beq.s   ResetNormal             ; No, do normal film

        bsr.s   ResetControl            ; Yes, handle it
        bra.s   ResetNext               ; Do next element

ResetNormal:
        btst.l  #bLOOP,d0               ; Loop?
        bne.s   ResetLoop               ; Yes, handle it

        lea     nlpDataEnd(a0),a0       ; Move playlist pointer
        bra.s   ResetNext               ; Do next element

ResetLoop:
        lea     lpDataEnd(a0),a0        ; Move playlist pointer
        bra.s   ResetNext               ; Do next element

ResetExit:
        movem.l (sp)+,d0/a0-a1          ; Restore registers
        rts

;------------------------------------------------------------------------------
;                             ResetSpecial
;------------------------------------------------------------------------------
;   Resets playlist structure for special film and returns pointer to next
;   element on playlist.
;
;   Input:  a0 = address of current playlist element
;
;   Output: a0 = address of next playlist element, according to the rule
;                specified by fRule(a0)
;------------------------------------------------------------------------------

ResetSpecial:
        move.l  d0,-(sp)                ; Save register

        clr     d0                      ; Get a clean word
        move.b  fRule(a0),d0            ; Get rule number
        lsl     #2,d0                   ; Index by longs
        jsr     resetFilmTbl(pc,d0)     ; Call branch table

        move.l  (sp)+,d0                ; Restore register
        rts

resetFilmTbl:
        bra.w   TrapBogus               ; 0: undefined!
        bra.w   SkipReset               ; 1: SKIP
        bra.w   HyperReset              ; 2: HYPER

;------------------------------------------------------------------------------
;                                 SKIP
;------------------------------------------------------------------------------

SkipReset:
        move.l  d0,-(sp)                ; Save register

        clr     skPathRec(a0)           ; Clear path record
        move    skFanIn(a0),d0          ; Number of fan-ins
        add     d0,d0                   ; Two bytes per fan-in
        lea     skBackLink(a0,d0),a0    ; Start of fan-in data

        move.l  (sp)+,d0                ; Restore register
        rts                             ; Exit

;------------------------------------------------------------------------------
;                                HYPER
;------------------------------------------------------------------------------

HyperReset:
        clr     hyClickCnt(a0)          ; Clear click counter
        lea     hyDataEnd(a0),a0        ; Next playlist entry
        rts

;------------------------------------------------------------------------------
;                             ResetControl
;------------------------------------------------------------------------------
;   Resets playlist structure for control element and returns pointer to next
;   element on playlist.
;
;   Input:  a0 = address of current playlist element
;
;   Output: a0 = address of next playlist element, according to the rule
;                specified by cType(a0)
;------------------------------------------------------------------------------

ResetControl:
        move.l  d0,-(sp)                ; Save register

        clr     d0                      ; Get a clean word
        move.b  cType(a0),d0            ; Get type number
        lsl     #2,d0                   ; Index by longs
        jsr     resetCtrlTbl(pc,d0)     ; Call branch table

        move.l  (sp)+,d0                ; Restore register
        rts

resetCtrlTbl:
        bra.w   TrapBogus               ; 0: undefined!
        bra.w   BurnReset               ; 1: BURN
        bra.w   CountReset              ; 2: COUNT
        bra.w   SwitchReset             ; 3: SWITCH
        bra.w   CensorReset             ; 4: CENSOR
        bra.w   RelayReset              ; 5: RELAY
        bra.w   ChangeReset             ; 6: CHANGE

;------------------------------------------------------------------------------
;                                 BURN
;------------------------------------------------------------------------------

BurnReset:
        move.l  d0,-(sp)                ; Save register

        clr     buPathRec(a0)           ; Clear path record
        clr     buBurnMask(a0)          ; Clear burn mask
        clr     d0                      ; Get a clean word
        move.b  cFanOut(a0),d0          ; Number of fan-outs
        mulu    #12,d0                  ; Twelve bytes per fan-out
        lea     buVectors(a0,d0),a0     ; Next playlist entry

        move.l  (sp)+,d0                ; Restore register
        rts

;------------------------------------------------------------------------------
;                                COUNT
;------------------------------------------------------------------------------

CountReset:
        move.l  d0,-(sp)                ; Save register

        clr     coCount(a0)             ; Clear accumulated count
        clr     d0                      ; Get a clean word
        move.b  cFanOut(a0),d0          ; Number of fan-outs
        add     d0,d0                   ; Two bytes per fan-out
        lea     coFwdLink(a0,d0),a0     ; Next playlist entry

        move.l  (sp)+,d0                ; Restore register
        rts

;------------------------------------------------------------------------------
;                                SWITCH
;------------------------------------------------------------------------------

SwitchReset:
        move.l  d0,-(sp)                ; Save register

        clr     d0                      ; Get a clean word
        move.b  cFanOut(a0),d0          ; Number of fan-outs
        mulu    #12,d0                  ; Twelve bytes per fan-out
        lea     swVectors(a0,d0),a0     ; Next playlist entry

        move.l  (sp)+,d0                ; Restore register
        rts

;------------------------------------------------------------------------------
;                                CENSOR
;------------------------------------------------------------------------------

CensorReset:
        lea     ceDataEnd(a0),a0        ; Next playlist entry
        rts

;------------------------------------------------------------------------------
;                                RELAY
;------------------------------------------------------------------------------

RelayReset:
        lea     reDataEnd(a0),a0        ; Next playlist entry
        rts

;------------------------------------------------------------------------------
;                                CHANGE
;------------------------------------------------------------------------------

ChangeReset:
        lea     chDataEnd(a0),a0        ; Next playlist entry
        rts

;------------------------------------------------------------------------------
;   Declare uninitialized storage.
;------------------------------------------------------------------------------

        .bss

branchTable:    ds.l    3
linkTable:      ds.l    1
baseAddress:    ds.l    1
pLink:          ds.l    1
ctrlPending:    ds.w    1

ctrlList::      ds.l    MAX_CTRL
branchPending:: ds.w    1
ctrlCount::     ds.w    1
loadDefault::   ds.w    1

;------------------------------------------------------------------------------
;   Declare externals.
;------------------------------------------------------------------------------

        .extern autoPilot
        .extern fadeOut
        .extern frameNum
        .extern huntLeadout
        .extern initLoad
        .extern leadinCnt
        .extern loopIndex
        .extern minSex
        .extern minViolence
        .extern optionIndex
        .extern pathEnd
        .extern pendingPath
        .extern pList
        .extern playList
        .extern playListEnd
        .extern pPath
        .extern pPendingList
        .extern profile
        .extern segActive
        .extern segCount
