;******************************************************************************
;            (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 08 Nov 95 JPE     Initial revision. (Module formerly "gamefilm.s".)
;                       Cleaned up joypad interface. Added volume control.
;                       Moved scoring and control of voiceover to module
;                       "auxil.s". Cleaned up saving of score and profile.
;                       Joypad activity during subtitle in non-loop segment
;                       generates increment to profile. Restricted options
;                       following restore while paused.
; 101 05 Dec 95 JPE     Separated playlist navigation from loader control.
;                       Playlist reset by computation, not from undoctored
;                       version. Replaced playlist with component list.
;                       Endpath now requires keypress to activate menu.
;                       Implement adaptive branching.
;******************************************************************************

        .nlist
        .include    'memory.inc'
        .include    'jaguar.inc'
        .include    'cd.inc'
        .include    'player.inc'
        .include    'offset.inc'
        .include    'params.inc'
        .list

CD      .equ    1                       ; 0 shortens playlist for debug

; At this point a single NPIXELSxNLINES 16-bit bitmap object list has been
; set up by STARTUP.S. The address is held in D0.

_start:
        move.l  d0,-(sp)                ; Save on stack

        bsr     Clear                   ; Clear display buffers
        jsr     CD_setup                ; Initialize CD-ROM system

        move    #$3,d0                  ; CD-ROM, double-speed
        jsr     CD_mode                 ; Set CD-ROM mode

        bsr     LoadGPU                 ; Download the GPU code
        bsr     LoadDSP                 ; Download the DSP code

; Start the DSP.

        move.l  #DRIFT_RATE,AUDIO_DRIFT ; (Default)
        move.l  #0,AUDIO_DESC
        move.l  #16,ATTEN_SHIFT         ; 0 dB attenuation
        move.l  #DSP_ENTR,D_PC          ; Load DSP program counter
        move.l  #$1,D_CTRL              ; Start the DSP

; Start the GPU.

        move.l  (sp)+,GPUOLP            ; Give the GPU the OLP.

        lea     GPU_READ,a0             ; GPU_READY flag 
        adda.l  GPUOffset,a0            ; Relocated address
        move.l  #0,(a0)                 ; Clear the ready flag
        move.l  #G_RAM+$50,G_PC         ; Load GPU program counter
        move.l  #RISCGO,G_CTRL          ; Start the GPU

; Wait for GPU to finish startup sequence, then allow external interrupts.

WaitGPU:
        move.l  (a0),d0                 ; Get ready flag
        tst.l   d0
        beq.s   WaitGPU                 ; Loop until GPU has set it

        bsr     MakeRGBTables           ; Build RGB tables for fadeout
        bsr     ClearHistory            ; Clear node/film history records
        bsr     IntInit                 ; Initialize interrupts
        bsr     InitNVM                 ; Initialize NVM setup

        move    #$6c7,VMODE             ; Start display in RGB mode

;******************************************************************************
;                     E X E C U T I V E   T A S K
;******************************************************************************

Executive:
        move.l  execEntry,a0            ; Get entry address
        jmp     (a0)                    ; Jump to it

ExecExit:
        bra     LoadMovie               ; Switch to next task

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

GameLoop:
        moveq   #0,d0
        move    d0,loopFlag             ; Clear loop flag
        move    d0,branchPending        ; Clear branch pending flag
        move    d0,ctrlEnable           ; Clear control enable flag
        move    d0,titleOn              ; Clear title on
        move    d0,rdBeforeWr           ; Clear reBeforeWr flag
        move    d0,fadeDone             ; Clear fadeDone flag
        move    d0,flmNumber            ; Clear flmNumber
        move    d0,loadEOP              ; Clear loadEOP
        move    d0,playEOP              ; Clear playEOP
        move    d0,waitReload           ; Clear wait for reload flag

        move    playback,d0             ; Playback flag
        or      remind,d0               ; Or with remind flag
        bne.s   InitMore                ; If set, don't reset score, etc.

        moveq   #0,d0
        move.l  d0,contNode             ; Clear continuation node

        tst     continue                ; Continuation?
        bne.s   InitMore                ; Yes, don't touch score

        move.l  d0,score                ; Clear player's score
        lea     profile,a0              ; Start of profile
        move    #5-1,d0                 ; Count 5 attributes

ClearProfile:
        clr     (a0)+                   ; Clear attribute
        dbra    d0,ClearProfile         ; Loop until done

InitMore:
        move    #1,loadBaseIx           ; Start with buffer 0 (1->0)
        move    #-1,nextLoadIx          ; Invalidate next index
        move    #1,initLoad             ; Set initial load flag
        move    initNodeC,nodeCount     ; Initialize node counter
        move.l  #LoadIdle,loadEntry     ; Entry to LoadMovie
        move.l  #PlayIdle,playEntry     ; Entry to PlayMovie
        move.l  initPath,pPath          ; Initialize path record pointer
        move.l  #crumbTrail+8,pLoadCrumb; Initialize pLoadCrumb
        move.l  #crumbTrail,pPlayCrumb  ; Initialize pPlayCrumb
        move.l  #$bbbbbbbb,crumbTrail   ; Tag startup for player

.if CD
        move.l  #compList,lastNode      ; Default value for last node
.else
        move.l  #compListAlt,lastNode   ; Default value for last node
.endif

        bsr     InitROM                 ; Clear ROMulator memory
        bsr     Clear                   ; Clear display buffers

        move    #NBUFFERS-1,d0          ; Loop counter
        lea     bufBlocks,a0            ; Table of buffer blocks

InitBuffers:
        movea.l (a0)+,a1                ; Get block address
        clr.l   bufLocked(a1)           ; Clear lock flag
        clr.l   bufReady(a1)            ; Clear ready flag
        dbra    d0,InitBuffers          ; Loop until done

        lea     loadCtrl,a6             ; Loader control block
        clr     loadGo(a6)              ; Clear go flag
        clr     loadAbort(a6)           ; Clear abort flag
        clr     loadRepeat(a6)          ; Clear repeat flag
        move    #1,loadDone(a6)         ; Set done flag

        lea     playCtrl,a6             ; Player control block
        clr     playGo(a6)              ; Clear go flag
        clr     playPause(a6)           ; Clear pause flag
        move    #1,playDone(a6)         ; Set done flag

        tst     warmstart               ; Is this a warm start?
        bne     ExecLoop                ; Yes, skip extra stuff

        move    #1,warmstart            ; No, set flag

        move.l  #$1000,audioGain        ; Audio gain = 1.0
        move    #SUB_VOICE,cueOption    ; Subtitles & voiceover
        move    #EASY,difficulty        ; Difficulty = EASY
        move    #FALSE,minSex           ; Don't minimize sex
        move    #FALSE,minViolence      ; Don't minimize violence
        move    #FALSE,skipAhead        ; Don't skip ahead on playback
        move    #JUMP,jogMode           ; Jump ahead on memory jog

.if CD
        move.l  #compList,pCompList     ; Initialize loader list pointer
.else
        move.l  #compListAlt,pCompList  ; Initialize loader list pointer
.endif
        move.l  #INDEX_TRACK,d1         ; Index track number (BIOS)
        bsr     SetTrackOffset          ; Set offset to film track

        lea     ctrlBlk0,a2             ; Use buffer 0
        move.l  #BUF0_BASE,bufStart(a2) ; Start of buffer
        move.l  #BUF0_END,bufEnd(a2)    ; End of buffer
        clr.l   fOffset(a2)             ; No offset for index
        move.l  #INDX_SYNC,filmSync(a2) ; Index sync

        lea     loadCtrl,a6             ; Control block
        move.l  a2,loadBCtrl(a6)        ; Pointer to control block
        move    #1,loadGo(a6)           ; Set go flag
        clr     loadDone(a6)            ; Clear done flag

FindIndex:
        tst.l   filmIndex               ; Have we found index?
        bne.s   GotIndex                ; Yes, proceed

        move.l  #FindIndex,execEntry    ; Where to go next time
        bra     ExecExit                ; Exit

GotIndex:
        movea.l filmIndex,a0            ; Source pointer
        movea.l #FILM_INDEX,a1          ; Destination pointer

CopyIndex:
        move.l  (a0)+,(a1)+             ; Copy to permanent location
        cmpa.l  #SCREEN_BASE,a1         ; HACK: later use atom size!!
        bne.s   CopyIndex

        move.l  #FILM_TRACK,d1          ; Film track number (BIOS)
        bsr     SetTrackOffset          ; Set offset to film track

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

ExecLoop:

        bra     UserCtrl                ; Joystick/subtitle functions
UserCtrlExit:                           ; Return here

        bra     LoadCtrl                ; Loader control functions
LoadCtrlExit:                           ; Return here

        bra     PlayCtrl                ; Player control functions
PlayCtrlExit:                           ; Return here

        move.l  #ExecLoop,execEntry     ; Where to go next time
        bra     ExecExit                ; Exit executive

;------------------------------------------------------------------------------
;                             ClearHistory
;------------------------------------------------------------------------------

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

        clr.l   contNode                ; Clear continuation node

        lea     nodeRecord,a0           ; Start of array
        move    #MAX_NODES-1,d0         ; Counter for elements

ClearNode:
        clr.b   (a0)+                   ; Clear one element
        dbra    d0,ClearNode            ; Loop until done

        lea     filmShowings,a0         ; Start of array
        move    #MAX_FILMS-1,d0         ; Counter for elements

ClearFilm:
        clr.b   (a0)+                   ; Clear one element
        dbra    d0,ClearFilm            ; Loop until done

        movem.l (sp)+,d0/a0             ; Restore registers
        rts

;------------------------------------------------------------------------------
;                            SetTrackOffset
;------------------------------------------------------------------------------

SetTrackOffset:
        move.l  #GAME_SESSION,d0        ; BIOS session number
        jsr     MediaGetFirst           ; Get track offset in blocks
        tst.l   d0                      ; Valid?
        bpl.s   GotTrack                ; Yes, continue

        move.l  #$00CD00CD,d0           ; Non-existent track
        illegal                         ; Trap error

GotTrack:
        addi.l  #LEADER-MARGIN,d0       ; Leader size minus seek margin
        move.l  d0,trackOffset          ; Block offset to film track
        rts

;==============================================================================
;                       U S E R   C O N T R O L
;==============================================================================

UserCtrl:
        tst     ctrlEnable              ; Are we enabled?
        beq     UserCtrlExit            ; No, wait until we are

        bsr     SampleJoy               ; Sample joypad
        bsr     VolumeCtrl              ; Adjust volume

        tst     waitReload              ; Relaunching CD after pause?
        bne     UserCtrlExit            ; Yes, avoid recursive action

        tst     gameOver                ; Is the game over?
        beq.s   CheckPause              ; No, look for pause

        bsr     EndCtrl                 ; Yes, handle it
        bra     GameLoop                ; Start another game

CheckPause:
        tst     remind                  ; Are we doing a memory jog?
        beq.s   UseKey                  ; No, use key input

        move    flmNumber,d0            ; Film we're playing
        cmp     pauseFilm,d0            ; Pause on this one?
        bne.s   CheckFade               ; No, continue

        move.l  frameNum,d0             ; Frame we're playing
        cmp.l   pauseFrame,d0           ; Pause on this one?
        bne.s   CheckFade               ; No, continue

        bra.s   GotPause                ; Yes, force a pause

UseKey:
        move    remind,d0               ; Get remind flag
        or      playback,d0             ; Or with playback flag
        bne.s   CheckFade               ; Skip if either set

        move.l  joyRise,d0              ; Keys that went active
        btst.l  #bPAUSE,d0              ; Was pause hit?
        beq.s   CheckFade               ; No, check on fadeout

GotPause:
        bsr     PauseCtrl               ; Yes, handle it

        tst     remind                  ; Jog player's memory?
        bne     GameLoop                ; Yes, start over

CheckFade:
        tst     fadeOut                 ; Is this a fadeout?
        bne     UserCtrlExit            ; Yes, exit

        bsr     AuxCtrl                 ; Handle auxiliary atom

        tst     titleOn                 ; Is subtitle on?
        beq     UserCtrlExit            ; No, nothing to do

        btst.b  #bNORMAL,joySemaphore   ; Semaphore set?
        beq     UserCtrlExit            ; No, exit

        bclr.b  #bNORMAL,joySemaphore   ; Yes, clear semaphore
        move.l  joyRise,d0              ; Keys that went active
        andi.l  #~SPECIAL,d0            ; Mask off UP, DOWN, PAUSE
        beq     UserCtrlExit            ; Exit if no others active

        tst     loopFlag                ; Are we in a loop segment?
        bne.s   JoyActive               ; Yes, just register activity

        bsr     UpdateProfile           ; No, update player profile
        bra     UserCtrlExit            ; Exit

JoyActive:
        tst     joyPress                ; Key already pressed?
        bne     UserCtrlExit            ; Yes, exit

        move    #1,joyPress             ; Otherwise, set flag
        bsr     SubtVisible             ; Check flags
        tst     d0                      ; Subtitle enabled?
        beq     UserCtrlExit            ; Exit if not

        tst     remind                  ; Doing a memory jog?
        bne     UserCtrlExit            ; Yes, don't highlight

        bsr     HiLiteSubtitle          ; Highlight subtitle
        bra     UserCtrlExit            ; Exit

;==============================================================================
;                     L O A D E R   C O N T R O L
;==============================================================================

LoadCtrl:
        lea     loadCtrl,a6             ; Control block
        tst     fadeDone                ; Just finished a fadeout?
        beq.s   CheckDone               ; No, continue

        clr     fadeDone                ; Yes, clear flag
        move    #1,loadAbort(a6)        ; Tell loader to bail out
        bra     LoadCtrlExit

CheckDone:
        tst     loadDone(a6)            ; Finished previous load?
        beq     LoadCtrlExit            ; No, continue

        tst     loadEOP                 ; End of path?
        bne     LoadCtrlExit            ; Yes, nothing more to load

        tst     branchPending           ; Is branch pending?
        beq.s   StartLoad               ; No, continue with load

        tst     optionIndex             ; Option specified?
        bpl.s   TakeBranch              ; Yes, take selected branch

        tst     passOnZero              ; Waiting to pass on option 0?
        beq     CheckSuspend            ; No, see if we're suspended

        cmpi    #3,loopIndex            ; Started fourth loop yet?
        bne     LoadCtrlExit            ; No, skip load

        clr     passOnZero              ; Yes, shut off this path
        bra.s   StartLoad               ; OK to overwrite buffer

CheckSuspend:
        tst     suspendLoad             ; Are we suspended?
        beq.s   StartLoad               ; No, continue with load

        bra     LoadCtrlExit            ; Yes, skip load

TakeBranch:
        clr     branchPending           ; Clear pending flag

StartLoad:
        bsr     GetNextComp             ; Navigate component list
        movea.l pPendingComp,a0         ; Get component address
        bsr     GetLoadBuffer           ; Get buffer to load
        tst.l   d0                      ; Did we get one?
        beq     LoadCtrlExit            ; No, skip load

        bsr     DropCrumb               ; Drop a crumb on trail
        move.l  d0,loadBCtrl(a6)        ; Move pointer to loader control
        move    #1,loadGo(a6)           ; Set go flag
        clr     loadDone(a6)            ; Clear done flag

        move.l  attributes(a0),d0       ; Get current attributes
        btst.l  #bEND_PATH,d0           ; Path ending?
        beq.s   TestPass0               ; Skip if not

        move    #1,loadEOP              ; Set flag
        bra.s   UpdatePList             ; Bail out

TestPass0:
        btst.l  #bPASS_ZERO,d0          ; Wait to pass on option 0?
        beq.s   TestBranch              ; No, check other bits

        move    #1,passOnZero           ; Set pass-on-0 flag
        bra.s   UpdatePList             ; Update playlist pointer

TestBranch:
        btst.l  #bLOOP,d0               ; Reached a branch?
        beq.s   TestSuspend             ; No, check suspend bit

        move    #1,branchPending        ; Set pending branch flag
        clr     suspendLoad             ; Clear suspend load flag
        clr     passOnZero              ; Clear pass-on-option-0 flag
        move    #-1,optionIndex         ; Reset option index

TestSuspend:
        btst.l  #bSUSPEND,d0            ; Time to suspend loading?
        beq.s   UpdatePList             ; No, update playlist pointer

SetSuspend:
        move    #1,suspendLoad          ; Suspend until branch taken

UpdatePList:
        bsr     SetNextComp             ; Save place on list

        movea.l loadBCtrl(a6),a0        ; Buffer control block
.if 1   ;----------------------------------------------------------------------
        move.l  #'LOAD',d0              ; Tag
        moveq   #0,d1                   ; Clear high word
        move    filmNumber(a0),d1       ; Record film number
        move.l  loadBCtrl(a6),d2        ; Record loader control block
        bsr     RecordEvent             ; Leave crumb for debug
.endif  ;----------------------------------------------------------------------
        bra     LoadCtrlExit

;------------------------------------------------------------------------------
;                             GetNextComp
;------------------------------------------------------------------------------

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

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

        clr     initLoad                ; Clear flag
        move.l  pCompList,pComp         ; Initialize pointer
        movea.l pComp,a0                ; Current component
        bra.s   SetPending              ; Set pending component

GetCurrent:
        movea.l pComp,a0                ; Current component
        move.l  attributes(a0),d0       ; Get attributes
        btst.l  #bLOOP,d0               ; Loop?
        bne.s   LoopComp                ; Yes handle it

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

        bra.s   NormalComp              ; Handle normal case

LoopComp:
        lea     compLink+2(a0),a0       ; Skip to link table
        move.l  a0,linkTable            ; Pointer to link table
        move.l  a0,pLink                ; Pointer to first branch
        adda    (a0),a0                 ; Add offset
        bra.s   SetPending              ; Set pending component

LeadinComp:
        tst     optionIndex             ; Option selected?
        bge.s   NewBranch               ; Yes, start new branch

        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 component

NewBranch:        
        movea.l linkTable,a0            ; Base of link table
        move    optionIndex,d0          ; Get option index
        lsl     #1,d0                   ; Index by words
        adda    d0,a0                   ; Get target link
        adda    (a0),a0                 ; Start of branch

NormalComp:
        move.l  attributes(a0),d0       ; Get attributes
        btst.l  #bADAPT,d0              ; Adaptive branch?
        beq.s   NonAdaptive             ; Skip if not

        bsr.s   AdaptiveBranch          ; Use adaptive mechanism
        bra.s   SetPending              ; Set pending component

NonAdaptive:
        lea     compLink(a0),a0         ; Skip to link
        adda    (a0),a0                 ; Next component

SetPending:
        move.l  a0,pPendingComp         ; Pending component

        movem.l (sp)+,d0/a0             ; Restore registers
        rts

;------------------------------------------------------------------------------
;                            AdaptiveBranch
;------------------------------------------------------------------------------
;   Input:  a0 = address of current component
;
;   Output: a0 = address of component whose associated psychometric values
;                most closely match player's current profile.
;------------------------------------------------------------------------------

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

        move.l  #-1,d1                  ; Maximize minimum distance
        lea     compLink(a0),a0         ; Skip to forward links
        movea.l a0,a2                   ; Make a copy
        adda    (a0)+,a2                ; End of forward link data

AdaptLoop:
        bsr.s   ManhattanDist           ; Compute distance
        cmp.l   d0,d1                   ; Compare with prior minimum
        bcs.s   NextBranch              ; Skip if not less

        move.l  d0,d1                   ; Update minimum
        movea.l a0,a1                   ; Remember place in list

NextBranch:
        lea     12(a0),a0               ; Next entry on list
        cmpa.l  a0,a2                   ; Reached end?
        bne.s   AdaptLoop               ; Loop until done

        lea     10(a1),a0               ; Link to next component
        adda    (a0),a0                 ; Next component

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

;------------------------------------------------------------------------------
;                            ManhattanDist
;------------------------------------------------------------------------------
;   Input:  a0 = 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,a1              ; Address of 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 target component
        sub     (a1)+,d1                ; Subtract player's value
        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

;------------------------------------------------------------------------------
;                             SetNextComp
;------------------------------------------------------------------------------

SetNextComp:
        move.l  pPendingComp,pComp      ; Update component pointer
        rts

;------------------------------------------------------------------------------
;                              DropCrumb
;------------------------------------------------------------------------------
;   Input:  d0 = control block address
;           a0 = component address
;------------------------------------------------------------------------------

DropCrumb:
        movem.l d1/a0,-(sp)             ; Save registers

        move.l  a0,d1                   ; Copy component address to d1
        movea.l pLoadCrumb,a0           ; Pointer to crumb trail
        move.l  d0,0(a0)                ; Control block address
        move.l  d1,4(a0)                ; Component address
        bsr     FollowTrail             ; Skip to next crumb
        move.l  #$eeeeeeee,0(a0)        ; Mark end of trail
        move.l  a0,pLoadCrumb           ; Update trail pointer

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

;------------------------------------------------------------------------------
;                            GetLoadBuffer
;------------------------------------------------------------------------------

GetLoadBuffer:
        movem.l d2/a1-a2,-(sp)          ; Save registers

        move    nextLoadIx,d0           ; Index of next buffer
        bge.s   NextValid               ; If positive, proceed

        move.l  attributes(a0),d0       ; Get film attributes
        btst.l  #bLEADIN,d0             ; Is this a leadin?
        beq.s   NotLeadin               ; Skip if not

        tst     leadinCnt               ; First leadin of set?
        bne.s   NotFirst                ; No, continue

        eori    #1,loadBaseIx           ; Toggle base buffer index

NotFirst:
        btst.l  #bSNGL_LEAD,d0          ; Single leadin?
        beq.s   NotSingle               ; No, continue

        move    loadBaseIx,d0           ; Use base buffer
        bra.s   IncCount                ; Increment counter

NotSingle:
        move    loadBaseIx,d0           ; Base buffer index
        move    leadinCnt,d2            ; Index for mini-buffer
        bsr     GetMiniIndex            ; Get mini-buffer index in d0

IncCount:
        addq    #1,leadinCnt            ; Increment leadin count
        bra.s   NextValid

NotLeadin:
        clr     leadinCnt               ; Clear leadin counter
        eori    #1,loadBaseIx           ; Toggle base buffer index
        move    loadBaseIx,d0           ; Get a copy in d0
        move    d0,nextLoadIx           ; This is our next index

NextValid:
        lsl     #2,d0                   ; Index by longs
        lea     bufBlocks,a2            ; Table of buffer control blocks
        movea.l 0(a2,d0),a2             ; Buffer we're going to use
        tst.l   bufLocked(a2)           ; Is buffer write protected?
        bne.s   LoadNotAvail            ; Skip if yes

        move    #-1,nextLoadIx          ; Invalidate next index
        lea     bufParams,a1            ; Table of parameter blocks
        movea.l 0(a1,d0),a1             ; Parameters for this buffer

        move.l  (a1)+,pNextCtrl(a2)     ; Initialize pNextCtrl
        move.l  (a1)+,bufStart(a2)      ; Initialize bufStart
        move.l  (a1)+,bufEnd(a2)        ; Initialize bufEnd
        move.l  (a1)+,bufLocked(a2)     ; Initialize locked flag

        clr.l   bufReady(a2)            ; Clear ready flag
        clr.l   playActive(a2)          ; Clear active flag

        move    filmNum(a0),filmNumber(a2)      ; Save film number
        move.l  attributes(a0),filmAttrib(a2)   ; Copy film attributes

        bsr     SetFilmParams           ; Look up parameters from index

        move.l  a2,d0                   ; Return pointer in d0
        bra.s   GetLoadExit

LoadNotAvail:
        moveq   #0,d0                   ; Return null pointer

GetLoadExit:
        movem.l (sp)+,d2/a1-a2          ; Restore registers
        rts

;------------------------------------------------------------------------------
;                             GetMiniIndex
;------------------------------------------------------------------------------

GetMiniIndex:
        movem.l d1,-(sp)                ; Save registers

        moveq   #2,d1                   ; Start with index 2
        lsl     d0,d1                   ; Base 0: d1->2; Base 1: d1->4
        move    d2,d0                   ; Branch index (0,1,2)
        andi    #1,d0                   ; Use only lsb
        add     d0,d1                   ; Odd option increments d1
        move    d1,d0                   ; Transfer to d0

        movem.l (sp)+,d1                ; Restore registers
        rts

;------------------------------------------------------------------------------
;                            SetFilmParams
;------------------------------------------------------------------------------

SetFilmParams:
        movem.l d1/a1,-(sp)             ; Save registers

        movea.l #FILM_INDEX,a1          ; Start of film index
        move    filmNumber(a2),d1       ; Get film number
        subq    #1,d1                   ; Convert to index
        lsl     #4,d1                   ; 16-byte index records
        lea     0(a1,d1),a1             ; Point to indexed entry
        move.l  $4(a1),fOffset(a2)      ; Film offset value
        move.l  $c(a1),filmSync(a2)     ; Film sync value

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

;==============================================================================
;                     P L A Y E R   C O N T R O L
;==============================================================================

PlayCtrl:
        lea     playCtrl,a6             ; Player control block
        tst     waitReload              ; Waiting for reload?
        beq.s   CheckPlayDone           ; No, continue

        move.l  CDReloadPtr,a0          ; Location at time of pause
        cmpa.l  CDWritePtr,a0           ; Gotten back where we left off?
        bge     PlayCtrlExit            ; No, wait until we do

.if 1   ;----------------------------------------------------------------------
        movem.l d0-d2,-(sp)             ; Save registers
        move.l  #'RSMP',d0              ; ASCII tag
        move.l  CDWritePtr,d1           ; Record CDWritePtr
        move.l  CDReloadPtr,d2          ; Record CDReloadPtr
        bsr     RecordEvent             ; Leave breadcrumb for debug
        movem.l (sp)+,d0-d2             ; Restore registers
.endif  ;----------------------------------------------------------------------

        clr     playPause(a6)           ; Clear flag for player
        clr     waitReload              ; Clear reload flag
        move.l  pauseTime,time          ; Reset time for player

CheckPlayDone:
        tst     playDone(a6)            ; Done?
        beq     PlayCtrlExit            ; No, exit

        tst     playEOP                 ; End of path?
        beq.s   PlayContinue            ; No, continue playing

        move    #1,gameOver             ; Yes, set flag for control
        bra     PlayCtrlExit            ; Nothing else to do

PlayContinue:
;;;;;;  HACK FOR BOARD OF DIRECTORS MEETING ... FIX IT LATER ;;;;;;
        tst     hackDelay               ; Is flag set?
        beq.s   EndHack

        move.l  hackTime,d0             ; Get timeout value
        cmp.l   time,d0                 ; Time to play?
        bgt     PlayCtrlExit            ; No, wait

        clr     hackDelay               ; Yes, clear flag and go
EndHack:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        bsr     GetPlayBuffer           ; Get buffer to play
        tst.l   d0                      ; Did we get one?
        beq     PlayCtrlExit            ; No, exit

        move.l  d0,playBCtrl(a6)        ; Move pointer to player control
        clr     playDone(a6)
        move    #1,playGo(a6)

        movea.l d0,a0                   ; Buffer control block
        cmpi    #2,filmNumber(a0)       ; Just played cavehead?
        bne.s   DontClear               ; No, don't clear screen

        bsr     Clear                   ; Clear display buffers

DontClear:
.if 1   ;----------------------------------------------------------------------
        move.l  #'PLAY',d0              ; Tag
        clr.l   d1                      ; Clear high word
        move    loopFlag,d1             ; Record loop flag
        move.l  playBCtrl(a6),d2        ; Record player control block
        bsr     RecordEvent             ; Leave crumb for debug
.endif  ;----------------------------------------------------------------------
        bra     PlayCtrlExit

;------------------------------------------------------------------------------
;                            GetPlayBuffer
;------------------------------------------------------------------------------

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

        tst     loopFlag                ; Are we in a loop?
        beq     NotLoop                 ; Skip if not

        bsr     CheckJoy                ; Check joypad
        tst     d0                      ; Any action?
        bne.s   ExitLoop                ; Yes, exit the loop

        addq    #1,loopIndex            ; Increment loop index
        cmpi    #4,loopIndex            ; Reached 4 yet?
        beq.s   ExitLoop                ; Yes, default exit

        lea     playCtrl,a0             ; Play control block
        move.l  playBCtrl(a0),d0        ; Currently playing buffer
        bra     GetPlayExit             ; Continue playing

ExitLoop:
        clr     loopFlag                ; Clear flag
        move    loopIndex,optionIndex   ; Set option index
        subq    #2,optionIndex          ; Make 2-relative for table
        bge.s   SetNode                 ; Proceed if positive

        addq    #2,optionIndex          ; Undo if negative

SetNode:
        move.l  pendingNode,lastNode    ; Last completed node
        addq    #1,nodeCount            ; Increment node count

        move    playback,d0             ; Playback flag
        or      remind,d0               ; Or with remind flag
        bne.s   ReleaseBuff             ; If set, skip path/score update

        movea.l pPath,a0                ; Pointer to path record
        move    optionIndex,d0          ; Get option index
        move.b  d0,(a0)+                ; Update path record
        move.b  #$ee,(a0)               ; Write EOF marker
        move.l  a0,pPath                ; Update pointer

        bsr     UpdateNode              ; Update decision record for node
        cmpi.b  #7,d0                   ; Is node exhausted?
        beq.s   UpdateStatus            ; Skip if yes

        tst.l   contNode                ; Prior unexhausted node?
        bne.s   UpdateStatus            ; Skip if yes

        move.l  lastNode,contNode       ; First unexhausted node
        move    nodeCount,contCount     ; Capture node count
        subq    #1,contCount            ; Back up one step
        move.l  pPath,contPath          ; Capture path pointer
        subq.l  #1,contPath             ; Back up one step

        bsr     SaveStatus              ; Save score and profile

UpdateStatus:
        bsr     UpdateScore             ; Update node-completion score
        bsr     UpdateProfile           ; Update player profile

ReleaseBuff:
        lea     playCtrl,a0             ; Play control block
        movea.l playBCtrl(a0),a0        ; Buffer just finished
        clr.l   bufLocked(a0)           ; Release it
        clr.l   bufReady(a0)            ; Clear ready flag

NotLoop:
        bsr     GetCrumb                ; Pick up next crumb from trail
        movea.l pNextCrumb,a0           ; Get address
        cmpi.l  #$eeeeeeee,0(a0)        ; Ahead of loader?
        beq.s   PlayNotAvail            ; Yes, wait for next drop

        movea.l 0(a0),a1                ; Buffer control block
        movea.l 4(a0),a0                ; Component address
        tst.l   bufReady(a1)            ; Is buffer ready?
        beq.s   PlayNotAvail            ; No, skip

        move.l  a1,d0                   ; Yes, return pointer in d0
        ori.l   #VIEWED,attributes(a0)  ; Mark segment as viewed

        move    playback,d1             ; Playback flag
        or      remind,d1               ; Or with remind flag
        bne.s   CheckNode               ; If set, skip count update

        bsr     UpdateShow              ; Update film showings count

CheckNode:
        move.l  filmAttrib(a1),d1       ; Get film attributes
        btst.l  #bNODE,d1               ; Is this a node?
        beq.s   NextEntry               ; No, continue

        move.l  a0,pendingNode          ; Yes, this node is pending

NextEntry:
        bsr     EatCrumb                ; Swallow crumb from trail

        btst.l  #bLOOP,d1               ; Is this a loop segment?
        beq.s   GetPlayExit             ; No, exit

        move    #1,loopFlag             ; Yes, set flag
        clr     joyPress                ; Clear joypad flag

        cmpi    #EASY,difficulty        ; Set to EASY mode?
        bne.s   NotEasy                 ; No, maximum of 2 loops

        clr     loopIndex               ; Clear loop index
        bra.s   GetPlayExit

NotEasy:
        move    #2,loopIndex            ; Initialize loop index to 2
        bra.s   GetPlayExit

PlayNotAvail:
        moveq   #0,d0                   ; Return null pointer

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

;------------------------------------------------------------------------------
;                              UpdateNode
;------------------------------------------------------------------------------

UpdateNode:
        movem.l d1/a0,-(sp)             ; Save registers

        lea     nodeList,a0             ; List of node addresses
        clr     d0                      ; Initialize node ID to 0
        move.l  pendingNode,d1          ; Current node address

MatchAddr:
        cmp.l   (a0)+,d1                ; Match addresses?
        beq.s   GotID                   ; Yes, got ID

        addq    #1,d0                   ; No, increment ID
        bra.s   MatchAddr               ; Try again

GotID:
        lea     nodeRecord,a0           ; Base of array
        move    optionIndex,d1          ; Get option index
        bset.b  d1,0(a0,d0)             ; Set corresponding bit
        move.b  0(a0,d0),d0             ; Return updated record

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

;------------------------------------------------------------------------------
;                              SaveStatus
;------------------------------------------------------------------------------

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

        move.l  score,contScore         ; Save for continuation

        lea     profile,a0              ; Where profile is stored
        lea     contProfile,a1          ; Backup for continuation
        moveq   #5-1,d0                 ; Count 5 attributes

SaveAttribute:
        move    (a0)+,(a1)+             ; Save for continuation
        dbra    d0,SaveAttribute        ; Loop until done

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

;------------------------------------------------------------------------------
;                              UpdateShow
;------------------------------------------------------------------------------

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

        lea     filmShowings,a1         ; Base of array
        move    filmNum(a0),d0          ; Offset
        addq.b  #1,0(a1,d0)             ; Increment counter

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

;------------------------------------------------------------------------------
;                               GetCrumb
;------------------------------------------------------------------------------

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

        movea.l pPlayCrumb,a0           ; Get current one
        cmp.l   #$bbbbbbbb,(a0)         ; Head of trail?
        bne.s   NotHead                 ; No, continue

        bsr     FollowTrail             ; Skip to first crumb
        bra.s   SetNextCrumb            ; Use this one

NotHead:
        movea.l 4(a0),a1                ; Component address
        move.l  attributes(a1),d0       ; Get attributes

        btst.l  #bLOOP,d0               ; Loop?
        bne.s   LoopCrumb               ; Yes, handle it

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

        bsr     FollowTrail             ; Skip to next crumb
        bra.s   SetNextCrumb            ; Use this one

LoopCrumb:
        bsr     FollowTrail             ; Skip to next crumb
        movea.l 4(a0),a1                ; Component address
        move.l  attributes(a1),d0       ; Get attributes
        btst.l  #bSNGL_LEAD,d0          ; Single leadin?
        bne.s   SetNextCrumb            ; Yes, use this one

        move    optionIndex,d0          ; No, get ready to skip
        bra.s   CountCrumbs             ; Jump into loop

SkipCrumb:
        bsr     FollowTrail             ; Skip to next crumb

CountCrumbs:
        subq    #1,d0                   ; Decrement counter
        bge.s   SkipCrumb               ; Loop while positive

        bra.s   SetNextCrumb            ; Use this one

LeadinCrumb:
        bsr     FollowTrail             ; Skip to next crumb
        movea.l 4(a0),a1                ; Component address
        move.l  attributes(a1),d0       ; Get attributes
        btst.l  #bLEADIN,d0             ; Another leadin?
        bne.s   LeadinCrumb             ; Yes, loop until not

SetNextCrumb:
        move.l  a0,pNextCrumb           ; Set pending crumb

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

;------------------------------------------------------------------------------
;                             FollowTrail
;------------------------------------------------------------------------------
;   Input:  a0 = current location on crumb trail
;
;   Output: a0 = next location on crumb trail
;------------------------------------------------------------------------------

FollowTrail:
        lea     8(a0),a0                ; Move to next location
        cmpa.l  #endTrail,a0            ; Reached end of trail?
        bne.s   FollowExit              ; No, exit

        lea     crumbTrail,a0           ; Yes, reset to beginning

FollowExit:
        rts

;------------------------------------------------------------------------------
;                               EatCrumb
;------------------------------------------------------------------------------

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

        movea.l pNextCrumb,a0           ; Pointer to next crumb
        move.l  a0,pPlayCrumb           ; Update master copy

        movea.l 4(a0),a1                ; Component address
        move.l  attributes(a1),d0       ; Get attributes
        btst.l  #bEND_PATH,d0           ; Path ending?
        beq.s   EatCrumbExit            ; No, continue

        move    #1,playEOP              ; Set flag

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

;==============================================================================
;                               EndCtrl
;==============================================================================

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

        clr     restoreFlag             ; Clear restore flag
        clr     continue                ; Clear continue flag
        clr     playback                ; Clear playback flag
        clr     gameOver                ; Clear game over flag
        clr     initNodeC               ; Clear node counter by default
        move.l  #pathRecord,initPath    ; Default start of path record
        move.l  #compListAlt,pCompList  ; Default loader list pointer

        tst     autoPlay                ; Is autoPlay enabled?
        bne     EndExit                 ; Yes, exit

        bsr     ClearOnScreen           ; Clear display

EndWait:
        bsr     GetAny                  ; Read joypad
        tst.l   d0                      ; Any keys pressed?
        beq.s   EndWait                 ; No, wait

        bsr     ClearJoy                ; Let joypad clear
        bsr     EndPath                 ; Display score, etc.

        cmpi    #NOTHING,d0             ; Timed out?
        beq.s   EndWait                 ; Yes, wait again

        cmpi    #RESTART,d0             ; Restart?
        beq.s   Restart                 ; Yes, do it

        cmpi    #CONTINUE,d0            ; Continue?
        beq.s   Continue                ; Yes, do it

        cmpi    #PLAYBACK,d0            ; Playback?
        beq.s   Playback                ; Yes, do it

        illegal                         ; Trap bogus return value

Restart:
.if CD
        move.l  #compList,pCompList     ; Initialize loader list pointer
.endif
        bsr     ResetCompList           ; Reset component list
        bsr     ClearHistory            ; Clear node/film history records
        bra.s   EndExit                 ; Exit to main control

Continue:
        move    #1,continue             ; Set continue flag
        move.l  contNode,pCompList      ; Initialize loader list pointer
        move    contCount,initNodeC     ; Initial counter value
        move.l  contPath,initPath       ; Where to pick up path record
        move.l  contScore,score         ; Reset score

        lea     contProfile,a0          ; Saved values
        lea     profile,a1              ; Values to be restored
        move    #5-1,d0                 ; Loop counter

CopyProfile:
        move    (a0)+,(a1)+             ; Copy one attribute
        dbra    d0,CopyProfile          ; Loop until done

        bra.s   EndExit                 ; Exit to main control

Playback:
        move    #1,playback             ; Set playback flag

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

;------------------------------------------------------------------------------
;                            ResetCompList
;------------------------------------------------------------------------------

ResetCompList:
        movem.l d0-d1/a0-a2,-(sp)       ; Save registers

        lea     compList,a0             ; Start of component list
        lea     compListEnd,a1          ; End of component list

ResetLoop:
        andi.l  #~VIEWED,attributes(a0) ; Clear VIEWED bit
        move.l  attributes(a0),d0       ; Get attribute flag
        move.l  d0,d1                   ; Make a copy
        lea     compLink(a0),a0         ; Skip to link structures
        andi.l  #LOOP+ADAPT,d1          ; Loop or adaptive branch
        beq.s   NormalFwd               ; No, continue

        adda    (a0),a0                 ; Skip variable length field
        bra.s   ResetBack               ; Reset backward links

NormalFwd:
        lea     2(a0),a0                ; Skip to backward links

ResetBack:
        lea     2(a0),a2                ; Assume simple net
        btst.l  #bCOMPLEX,d0            ; Complex net?
        beq.s   ClearBack               ; No, continue

        movea.l a0,a2                   ; Copy current pointer
        adda    (a0)+,a2                ; End of backward links

ClearBack:
        andi    #~VIEWED,(a0)+          ; Clear VIEWED bit
        cmpa.l  a0,a2                   ; Done all backward links?
        bne.s   ClearBack               ; Loop until done

        cmpa.l  a0,a1                   ; Reached end of list?
        bne.s   ResetLoop               ; No, loop again

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

;==============================================================================
;                              PauseCtrl
;==============================================================================

PauseCtrl:
        movem.l d0-d1/a0-a2,-(sp)       ; Save regsiters

        move.l  time,pauseTime          ; Save current time
        clr     msgIndex                ; Clear messgage index
        clr     pauseClear              ; Clear flag
        clr     remind                  ; Clear remind flag
        clr     restoreFlag             ; Clear restore flag

;        move    #1,d0                   ; Wait for completion
;        jsr     CD_paus                 ; Pause the CD

        lea     loadCtrl,a2             ; Loader control block
        move.l  loadBCtrl(a2),d0        ; Buffer control block
        lea     playCtrl,a2             ; Player control block
        cmp.l   playBCtrl(a2),d0        ; Are buffers the same?
        bne     LoadMsg                 ; No, proceed

        cmpi    #1,playPhase            ; Idle CD phase?
        bne.s   CheckRdWr               ; No, check read-write pointers

        bra     LoadMsg                 ; Yes, OK to pause

CheckRdWr:
        tst     rdBeforeWr              ; Read pointer before write?
        bne     LoadMsg                 ; Yes, OK to pause

.if 1   ;----------------------------------------------------------------------
        bsr     GetCDWritePtr           ; Get CD write pointer
        movem.l d0-d2,-(sp)             ; Save registers
        move.l  #'PAUS',d0              ; ASCII tag
        move.l  CDWritePtr,d1           ; Record CDWritePtr
        move.l  #$deaddead,d2           ; Dead space
        bsr     RecordEvent             ; Leave breadcrumb for debug
        movem.l (sp)+,d0-d2             ; Restore registers
.endif  ;----------------------------------------------------------------------

        lea     loadCtrl,a2             ; Loader control block
        movea.l loadBCtrl(a2),a2        ; Load buffer control block
        move.l  bufStart(a2),d0         ; Start of buffer
        subq.l  #4,d0                   ; Where CD-BIOS starts

WaitSeek:
        bsr     GetCDWritePtr           ; Get CD write pointer
        cmp.l   CDWritePtr,d0           ; Still in seek mode?
        beq.s   WaitSeek                ; Yes, wait write to become active

        jsr     CD_uread                ; Stop overwriting memory

        move.l  CDWritePtr,CDReloadPtr  ; Remember where we are
        clr.l   CDWritePtr              ; Zero until loader modifies!!
        move.l  time,pauseTime          ; Resave current time
        move    #1,waitReload           ; Set flag to delay player

        lea     loadCtrl,a2             ; Loader control block
        move    #1,loadRepeat(a2)       ; Set repeat flag

        lea     playCtrl,a2             ; Player control block
        move    #1,playPause(a2)        ; Set pause flag

LoadMsg:
        move    msgIndex,d0             ; Get message index
        lsl     #2,d0                   ; Index by longs
        lea     msgTable,a2             ; Base of table
        movea.l 0(a2,d0),a2             ; Indexed message
        
PauseTitle:
        move    #GREEN,fontColor        ; Draw in green
        cmpi    #2,msgIndex             ; Index = 2?
        bne.s   DoTitle                 ; No, continue

        tst     restoreFlag             ; Have we done a restore?
        beq.s   DoTitle                 ; No, continue

        move    #GREY34,fontColor       ; Yes, grey out title

DoTitle:
        bsr     DrawSubtitle            ; Put up subtitle
        bsr     DoBlit                  ; Copy to display 1
        bsr     DoBlit                  ; Copy to display 2

        move.l  time,d1                 ; Get current time
        addi.l  #600,d1                 ; Add 1 second

CheckTime:
        tst     pauseClear              ; Cleared initial pause?
        bne.s   GetInput                ; Yes, get input

        bsr     ClearPause              ; Check on pause key
        tst     pauseClear              ; Cleared initial press?
        beq.s   SkipInput               ; No, skip input

GetInput:
        bsr     GetAny                  ; Look for joypad key
        btst.l  #bOPTION,d0             ; Option key?
        bne.s   EnterConfig             ; Yes, do configuration

        tst     restoreFlag             ; Restore pending?
        bne.s   CheckB                  ; Skip if yes

        btst.l  #bPAUSE,d0              ; Pause key?
        bne     Resume                  ; Yes, resume

CheckB:
        btst.l  #bRIGHT,d0              ; RIGHT key?
        bne     Remind                  ; Yes, jog memory

        cmpi.l  #(1<<bB)+(1<<bC),d0     ; B and C?
        beq     Trap                    ; Yes, trap for debug

SkipInput:
        cmp.l   time,d1                 ; Time elapsed?
        bgt.s   CheckTime               ; No, wait

        bsr     EraseSubtitle           ; Erase subtitle
        bsr     DoBlit                  ; Copy to display 1
        bsr     DoBlit                  ; Copy to display 2

        addq    #1,msgIndex             ; Increment index
        cmpi    #3,msgIndex             ; Out of range?
        bne     LoadMsg                 ; No, repeat loop

        clr     msgIndex                ; Reset to zero
        bra     LoadMsg                 ; Repeat loop

PauseExit:
;        move    #1,d0                   ; Wait for completion
;        jsr     CD_upaus                ; Unpause the CD

        move.l  pauseTime,time          ; Restore time

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

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

EnterConfig:
        bsr     ClearJoy                ; Let joypad clear
        bsr     EraseSubtitle           ; Erase subtitle
        bsr     DoBlit                  ; Copy to display 1
        bsr     DoBlit                  ; Copy to display 2
        bsr     ConfigSys               ; Do system configuration

        clr     msgIndex                ; Reset message index
        bra     LoadMsg                 ; Jump back into loop

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

Resume:
        bsr     ClearJoy                ; Let joypad clear
        bsr     EraseSubtitle           ; Erase subtitle
        bsr     DoBlit                  ; Copy to display 1
        bsr     DoBlit                  ; Copy to display 2
        clr.l   joyRise                 ; Clear active key mask
        bra.s   PauseExit               ; Exit from pause

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

Remind:
        bsr     ClearJoy                ; Let joypad clear

        cmpi    #JUMP,jogMode           ; Jump ahead?
        beq.s   JumpAhead               ; Yes, do set up

        move.l  #compListAlt,pCompList  ; Initialize loader list pointer
        move.l  #pathRecord,initPath    ; Where to start path record
        clr     initNodeC               ; Zero initial counter value
        bra.s   RemindExit              ; Exit

JumpAhead:
        move.l  lastNode,pCompList      ; Initialize loader list pointer

        move    nodeCount,d0            ; Number of nodes completed
        beq.s   InitNode                ; Continue if zero

        subq    #1,d0                   ; Convert to offset

InitNode:
        move    d0,initNodeC            ; Initial counter value
        ext.l   d0                      ; Make long
        add.l   #pathRecord,d0          ; Add base of path record
        move.l  d0,initPath             ; Where to start path record

RemindExit:
        move    fadeOut,d0              ; Get fadeOut flag
        or      fadeIn,d0               ; Or with fadeIn flag
        beq.s   Remember                ; If zero, skip

        clr     fadeOut                 ; Clear fadeOut flag
        clr     fadeIn                  ; Clear fadeIn flag

        movea.l #DECOMP_S,a0            ; Start of GPU code in DRAM
        adda.l  #RGB_OFFSET,a0          ; Source in DRAM

        movea.l #G_RAM+GPU_OFFSET+RGB_OFFSET,a1 ; Destination in GPU

        move.l  #RGB_SIZE,d0            ; Byte count
        bsr     BlockMove               ; Copy RGB table to GPU SRAM

Remember:
        move    flmNumber,pauseFilm     ; Remember film number
        move.l  frameNum,pauseFrame     ; Remember frame number
        move    #1,remind               ; Set remind flag
        bra     PauseExit               ; Exit from pause

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

Trap:
        bsr     EraseSubtitle           ; Erase subtitle
        bsr     DoBlit                  ; Copy to display 1
        bsr     DoBlit                  ; Copy to display 2
        bra     PauseExit               ; Exit from pause

;------------------------------------------------------------------------------
;                              VolumeCtrl
;------------------------------------------------------------------------------

VolumeCtrl:
        movem.l d0,-(sp)                ; Save registers

        btst.b  #bVOLUME,joySemaphore   ; Semaphore set?
        beq     CheckTimer              ; No, just check timer

        bclr.b  #bVOLUME,joySemaphore   ; Yes, clear it
        move.l  joyRise,d0              ; Keys that went active
        or.l    joyFall,d0              ; Keys that went inactive
        btst.l  #bUP,d0                 ; Change on UP key?
        beq.s   CheckDown               ; No, check DOWN key

        move.l  joyRise,d0              ; Keys that went active
        btst.l  #bUP,d0                 ; Is UP key pressed?
        beq.s   UpInactive              ; Skip if not

        move    #MAX_VOL,volLimit       ; Limit at maximum
        move    #1,volDelta             ; Use positive steps
        move.l  time,volTimer           ; Arm for immediate action
        bra.s   CheckTimer

UpInactive:
        clr.l   volTimer                ; Disable timer
        bra     ExitVolume

CheckDown:
        btst.l  #bDOWN,d0               ; Change on DOWN key?
        beq.s   CheckTimer              ; No, check timer key

        move.l  joyRise,d0              ; Keys that went active
        btst.l  #bDOWN,d0               ; Is DOWN key pressed?
        beq.s   DownInactive            ; Skip if not

        move    #MIN_VOL,volLimit       ; Limit at minimum
        move    #-1,volDelta            ; Use negative steps
        move.l  time,volTimer           ; Arm for immediate action
        bra.s   CheckTimer

DownInactive:
        clr.l   volTimer                ; Disable timer
        bra.s   ExitVolume

CheckTimer:
        tst.l   volTimer                ; Is timer set?
        beq.s   ExitVolume              ; Exit if not

        move.l  time,d0                 ; Get current time
        cmp.l   volTimer,d0             ; Time to adjust volume?
        blt.s   ExitVolume              ; No, exit

        addi.l  #120,d0                 ; Yes, add 0.2 seconds
        move.l  d0,volTimer             ; Reset timer
        move    volIndex,d0             ; Get current index
        cmp     volLimit,d0             ; Reached limit?
        beq.s   ExitVolume              ; Yes, don't adjust

        add     volDelta,d0             ; No, add increment
        move    d0,volIndex             ; Save updated index
        lsl     #1,d0                   ; Word entries in table
        move    volTbl(pc,d0),d0        ; New audio gain
        andi.l  #$ffff,d0               ; Clear out high word
        move.l  d0,audioGain            ; Write back updated value

.if 0   ;----------------------------------------------------------------------
        movem.l     d2-d3,-(sp)         ; Save
        move.l      #'VOLU',d0          ; Tag
        move.l      joySamp,d1          ; d1 = joySamp
        clr.l       d2                  ; Clear high word
        move        volIndex,d2         ; d2 = volIndex
        bsr         RecordEvent         ; Leave crumb
        movem.l     (sp)+,d2-d3         ; Restore
.endif  ;----------------------------------------------------------------------

ExitVolume:
        movem.l (sp)+,d0                ; Restore registers
        rts

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

        dc.w      512,   583,   664,   756,   861,   980,  1117,  1272
        dc.w     1448,  1649,  1878,  2139,  2435,  2773,  3158,  3596
volTbl: dc.w     4096
        dc.w     4664,  5312,  6049,  6888,  7844,  8933, 10173, 11585
        dc.w    13193, 15024, 17109, 19483, 22188, 25267, 28774, 32767

;------------------------------------------------------------------------------
;   Declare storage for flags and variables.
;------------------------------------------------------------------------------

ctrlEnable:     dc.w    0
loopFlag:       dc.w    0
warmstart:      dc.w    0
autoPlay:       dc.w    0
hackDelay:      dc.w    0               ; Get rid of this turkey!
gameOver:       dc.w    0
playback:       dc.w    0
remind:         dc.w    0
continue:       dc.w    0
initNodeC:      dc.w    0
volIndex:       dc.w    0

        .long

GPUOffset:      dc.l    0
filmIndex:      dc.l    0
volTimer:       dc.l    0

execEntry:      dc.l    GameLoop
loadEntry:      dc.l    0
playEntry:      dc.l    0
initPath:       dc.l    pathRecord

bufBlocks:      dc.l    ctrlBlk0
                dc.l    ctrlBlk1
                dc.l    ctrlBlk2
                dc.l    ctrlBlk3
                dc.l    ctrlBlk4
                dc.l    ctrlBlk5

bufParams:      dc.l    params0
                dc.l    params1
                dc.l    params2
                dc.l    params3
                dc.l    params4
                dc.l    params5

NBUFFERS        .equ    (*-bufParams)/4

params0:        dc.l    ctrlBlk0        ; pNextCtrl
                dc.l    BUF0_BASE       ; bufStart
                dc.l    BUF0_END        ; bufEnd
                dc.l    1               ; bufLocked

params1:        dc.l    ctrlBlk1        ; pNextCtrl
                dc.l    BUF1_BASE       ; bufStart
                dc.l    BUF1_END        ; bufEnd
                dc.l    1               ; bufLocked

params2:        dc.l    ctrlBlk2        ; pNextCtrl
                dc.l    BUF2_BASE       ; bufStart
                dc.l    BUF2_END        ; bufEnd
                dc.l    0               ; bufLocked

params3:        dc.l    ctrlBlk3        ; pNextCtrl
                dc.l    BUF3_BASE       ; bufStart
                dc.l    BUF3_END        ; bufEnd
                dc.l    0               ; bufLocked

params4:        dc.l    ctrlBlk4        ; pNextCtrl
                dc.l    BUF4_BASE       ; bufStart
                dc.l    BUF4_END        ; bufEnd
                dc.l    0               ; bufLocked

params5:        dc.l    ctrlBlk5        ; pNextCtrl
                dc.l    BUF5_BASE       ; bufStart
                dc.l    BUF5_END        ; bufEnd
                dc.l    0               ; bufLocked

msgTable:       dc.l    pauseMsg0
                dc.l    pauseMsg1
                dc.l    pauseMsg2

pauseMsg0:      dc.b    'PRESS OPTION FOR SETUPS...',0
pauseMsg1:      dc.b    'OR RIGHT FOR REMINDER...',0
pauseMsg2:      dc.b    'OR PAUSE TO RESUME',0

.even

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

        .bss
        .long

ctrlBlk0:       ds.b    BCTRL_SIZE
ctrlBlk1:       ds.b    BCTRL_SIZE
ctrlBlk2:       ds.b    BCTRL_SIZE
ctrlBlk3:       ds.b    BCTRL_SIZE
ctrlBlk4:       ds.b    BCTRL_SIZE
ctrlBlk5:       ds.b    BCTRL_SIZE

loadCtrl:       ds.b    LCTRL_SIZE
playCtrl:       ds.b    PCTRL_SIZE

crumbTrail:     ds.b    10*8        ; Storage for 10 crumbs
endTrail:

pCompList:      ds.l    1
trackOffset:    ds.l    1
filmOffset:     ds.l    1
hackTime:       ds.l    1               ; Get rid of this turkey!!
pPath:          ds.l    1               ; Pointer to path record
pauseTime:      ds.l    1               ; Time when film is paused
pLoadCrumb:     ds.l    1
pPlayCrumb:     ds.l    1
pNextCrumb:     ds.l    1
CDReloadPtr:    ds.l    1
pauseFrame:     ds.l    1
pendingNode:    ds.l    1
pComp:          ds.l    1
linkTable:      ds.l    1
pLink:          ds.l    1
pPendingComp:   ds.l    1

loopIndex:      ds.w    1
joyPress:       ds.w    1
optionIndex:    ds.w    1
branchPending:  ds.w    1
suspendLoad:    ds.w    1
passOnZero:     ds.w    1
loadBaseIx:     ds.w    1
nextLoadIx:     ds.w    1
leadinCnt:      ds.w    1
titleOn:        ds.w    1
msgIndex:       ds.w    1
pauseClear:     ds.w    1
rdBeforeWr:     ds.w    1
loadEOP:        ds.w    1
playEOP:        ds.w    1
waitReload:     ds.w    1
pauseFilm:      ds.w    1
volDelta:       ds.w    1
volLimit:       ds.w    1
restoreFlag:    ds.w    1
initLoad:       ds.w    1

joySemaphore:   ds.b    1

        .even

;------------------------------------------------------------------------------
;   Declare externals and globals.
;------------------------------------------------------------------------------

        .extern DSP_ENTR        ; Entry to DSP code
        .extern AUDIO_DRIFT     ; Audio drift rate
        .extern AUDIO_DESC      ; Audio data description
        .extern ATTEN_SHIFT     ; Audio attenuation shift
        .extern GPU_READ        ; GPU has finished startup

        .extern Clear           ; Clear display memory
        .extern GPUOLP          ; OLP to be set by GPU

        .extern LoadDSP         ; Load DSP code
        .extern LoadGPU         ; Load GPU code
        .extern InitROM         ; Clear ROM memory
        .extern AuxCtrl
        .extern MakeRGBTables   ; Build RGB tables for fadeout

        .extern MediaGetFirst
        .extern IntInit
        .extern flmNumber
        .extern frameNum
        .extern time

        .extern LoadIdle
        .extern LoadMovie
        .extern PlayIdle
        .extern DoBlit
        .extern ConfigSys
        .extern GetAny
        .extern CheckJoy
        .extern ClearPause
        .extern EndPath

        .extern RecordEvent
        .extern DrawSubtitle
        .extern EraseSubtitle
        .extern fontColor
        .extern fadeIn
        .extern fadeOut
        .extern pathRecord
        .extern score
        .extern playPhase
        .extern fadeDone
        .extern cueOption
        .extern profile
        .extern GetCDWritePtr
        .extern CDWritePtr
        .extern CD_uread
        .extern CD_paus
        .extern CD_upaus
        .extern audioGain
        .extern difficulty
        .extern minSex
        .extern minViolence
        .extern skipAhead
        .extern jogMode
        .extern DECOMP_S
        .extern BlockMove
        .extern filmShowings
        .extern nodeRecord
        .extern contNode
        .extern contPath
        .extern contCount
        .extern contScore
        .extern contProfile
        .extern SampleJoy
        .extern joyFall
        .extern joyRise
        .extern joySamp
        .extern UpdateProfile
        .extern UpdateScore
        .extern InitNVM
        .extern HiLiteSubtitle
        .extern lastNode
        .extern nodeCount
        .extern SubtVisible
        .extern compList
        .extern compListAlt
        .extern compListEnd
        .extern ClearOnScreen
        .extern ClearJoy
        .extern nodeList

        .globl  trackOffset     ; Added (SDS)
        .globl  filmOffset
        .globl  GPUOffset
        .globl  loadCtrl
        .globl  loadEntry
        .globl  playCtrl
        .globl  playEntry
        .globl  ctrlEnable
        .globl  loopFlag
        .globl  hackDelay
        .globl  hackTime
        .globl  pPath
        .globl  playback

        .globl  _start          ; Added (SDS)

        .globl  Executive
        .globl  filmIndex
        .globl  titleOn
        .globl  loopIndex
        .globl  rdBeforeWr
        .globl  autoPlay
        .globl  joyPress
        .globl  pauseClear
        .globl  remind
        .globl  pauseFilm
        .globl  joySemaphore
        .globl  optionIndex
        .globl  restoreFlag

        .end

