;******************************************************************************
;            (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 21 Jul 95 JPE     Initial revision. Executive code for GameFilm (formerly
;                       contained in player.s).
; 101 23 Aug 95 JPE     Control of voiceover added. Fixed bug in handling of
;                       suspend load escape code immediately following loader
;                       branch table. Joypad active during all four loops.
;                       Film segments concatenated on single CD track.
; 102 15 Sep 95 JPE     Modified to work with tree-structured playlist.
; 103 11 Oct 95 JPE     Cleaned up automatic play function. Cleaned up
;                       subtitle implementation. Filled out implementation
;                       of pause function. Endpath function implemented.
; 104 24 Oct 95 JPE     Added "memory-jog" feature. Player follows crumb
;                       trail left by loader. Update node record and number
;                       of film showings. Add difficulty feature.
; 100 08 Nov 95 JPE     Initial revision. (Module formerly "gamefilm.s".)
; [cineplay.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.
; 105 08 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. Fade reset at start in
;                       case fade preceded credits.
; 106 24 Jan 96 JPE     Moved volume control, pause and endpath control to
;                       separate modules. Added movie watching mode. Reduced
;                       film attribute size to 16 bits. Clean up flags in
;                       control block. Added parental control. Eliminated
;                       SNGL_LEAD hack. Cleaned up loader control.
; 107 20 Feb 96 JPE     End of path detected from null forward link. Revised
;                       loop control logic per new specification. Add logic
;                       to prevent player crumb trail pointer from crossing
;                       over loader pointer. Force extra loop upon default at
;                       maximum difficulty. Node update now done in playlist.
; 108 22 Feb 96 JPE     Continue if game restored while paused.
; 109 11 Mar 96 HMK	Added support for CSX audio.
;
;******************************************************************************

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

; 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

.if 0
; **** INIT CSX AUDIO ****

;//	move.l	#$43434343, CSXSETCHAN          ; non-zero is mono
	move.l	#0,CSXSETCHAN			; zero is stereo(0)

; Init silence data

	move.l	#silenceBlock, a1
	move.l	a1, silencePointer
	move.l	#silenceEnd,a4
	suba.l	a1,a4
	move.l	a4, silenceSize
	
; **** End CSX   ****
.endif
        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     InitNVM                 ; Initialize NVM setup
        bsr     InitPCtrl               ; Initialize parental control
        bsr     MakeRGBTables           ; Build RGB tables for fadeout
        bsr     IntInit                 ; Initialize interrupts

        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,ctrlEnable           ; Clear control enable flag
        move    d0,segActive            ; Clear segment window flag
        move    d0,rdBeforeWr           ; Clear reBeforeWr flag
        move    d0,currFilm             ; Clear currFilm
        move    d0,loadEOP              ; Clear loadEOP
        move    d0,playEOP              ; Clear playEOP
        move    d0,waitReload           ; Clear wait for reload flag
        move.l  d0,pPendingList         ; Clear playlist pointer
        move.l  d0,pNextCrumb           ; Clear crumb trail pointer
        move.l  d0,nextBuffer           ; Clear next buffer address

        tst     autoPilot               ; On automatic pilot?
        bne.s   InitMore                ; Yes, don't reset score, etc.

        move.l  d0,contNode             ; Clear continuation node
        move.l  playListEnt,lastNode    ; Default value for last 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    #4,baseIndex            ; Start with buffer 0 (4->0)
        move    #1,initLoad             ; Set initial load flag
        move.l  #LoadIdle,loadEntry     ; Entry to LoadMovie
        move.l  #PlayIdle,playEntry     ; Entry to PlayMovie
        lea     crumbTrail,a0           ; Start of crumb trail
        move.l  a0,pPlayCrumb           ; pPlayCrumb = Trail[0]
        move.l  #$bbbbbbbb,(a0)+        ; Tag startup for player
        move.l  a0,pLoadCrumb           ; pLoadCrumb = Trail[1]

        bsr     InitROM                 ; Clear ROMulator memory
        bsr     Clear                   ; Clear display buffers
        bsr     ResetFade               ; Reset possible fade

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

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

        lea     loadCtrl,a6             ; Loader control block
        clr     loadGo(a6)              ; Clear go 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  playListEnt,pList       ; Initialize loader list pointer

        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   filmOffset              ; No offset for index
        move.l  #INDX_SYNC,filmSync     ; 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

;------------------------------------------------------------------------------
;                            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:
        move    playback,d0             ; Get playback flag
        or      vcrMode,d0              ; Or with vcrMode flag
        bne.s   UserCtrlExit            ; Skip if either set

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

        move    currFilm,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.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
        move    remind,d0               ; Get remind flag
        or      vcrMode,d0              ; Or with vcrMode flag
        or      continue,d0             ; Or with continue flag
        bne     GameLoop                ; Yes, start over

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

        bsr     AuxCtrl                 ; Handle auxiliary atom
        bsr     InputEnable             ; Do flag logic
        tst     d0                      ; Pay attention to input?
        beq     UserCtrlExit            ; No, exit

        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  #~EXCEPT,d0             ; Mask off UP, DOWN, PAUSE
        beq     UserCtrlExit            ; Exit if no others active

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

        tst     hyperFlag               ; Are we in hypermode?
        bne.s   HyperActive             ; Yes, count key presses

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

LoopActive:
        tst     optionIndex             ; Decision already made?
        bge     UserCtrlExit            ; Yes, exit

        tst     censored                ; This branch censored?
        bne     UserCtrlExit            ; Exit if yes

        move.l  segCount,d0             ; Get segment count for track
        bchg.l  #0,d0                   ; Flip bit 0
        move    d0,optionIndex          ; Set option index
        tst     subtEnable              ; Subtitle enabled?
        beq     UserCtrlExit            ; Exit if not

        bsr     HiLiteSubtitle          ; Highlight subtitle
        bra     UserCtrlExit            ; Exit

HyperActive:
        lea     playCtrl,a0             ; Player control block
        movea.l playBCtrl(a0),a0        ; Currently playing buffer
        movea.l pListAddr(a0),a0        ; Current playlist address
        addq    #1,hyClickCnt(a0)       ; Increment keypress count
        bra     UserCtrlExit            ; Exit

;------------------------------------------------------------------------------
;                             InputEnable
;------------------------------------------------------------------------------

InputEnable:
        move.l  d1,-(sp)                ; Save register

        tst     loopFlag                ; Is this a loop?
        bne.s   LoopLogic               ; Yes, do tricky logic

        move    segActive,d0            ; Otherwise, it's simple!
        bra.s   InputExit               ; We're done

LoopLogic:
        clr     d0                      ; Disable by default

        tst     segActive               ; Within segment window?
        beq     InputExit               ; No, disable

        tst     optionIndex             ; Decision still pending?
        bge.s   InputExit               ; No, disable

        move    loopIndex,d1            ; Get loop index
        cmp     minIndex,d1             ; Initial pass of loop?
        beq.s   InputExit               ; Yes, disable

        move    #1,d0                   ; Return true value

InputExit:
        move.l  (sp)+,d1                ; Restore register
        rts

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

LoadCtrl:
        lea     loadCtrl,a6             ; Control block
        tst     loadDone(a6)            ; Finished previous load?
        beq     LoadCtrlExit            ; No, continue

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

        tst.l   pPendingList            ; Valid pointer?
        bne.s   FindLoadBuff            ; Yes, go get the buffer

        bsr     Navigate                ; Navigate playlist
        tst.l   pPendingList            ; Did we get something?
        beq     LoadCtrlExit            ; Exit if not

FindLoadBuff:
        movea.l pPendingList,a0         ; Get list address
        tst.l   nextBuffer              ; Buffer already assigned?
        bne.s   WaitBuffer              ; Yes, wait for it to free up

        bsr     GetLoadBuffer           ; Get buffer to load

WaitBuffer:
        movea.l nextBuffer,a2           ; Control for next buffer
        tst     bufLocked(a2)           ; Is buffer write protected?
        bne     LoadCtrlExit            ; Skip if yes

        clr.l   nextBuffer              ; Reset buffer pointer
        movea.l nextParams,a1           ; Start of initialization block
        move.l  (a1)+,bufStart(a2)      ; Initialize bufStart
        move.l  (a1)+,bufEnd(a2)        ; Initialize bufEnd
        move    (a1)+,bufLocked(a2)     ; Initialize locked flag

        clr     bufReady(a2)            ; Clear ready flag
        clr     loadAbort(a2)           ; Clear loader abort flag
        clr     playActive(a2)          ; Clear active flag
        move.l  a0,pListAddr(a2)        ; Playlist address

        bsr     CopyCtrlLinks           ; Copy control links, if any
        bsr     DropCrumb               ; Drop a crumb on trail

        move.l  a2,loadBCtrl(a6)        ; Move pointer to loader control
        move    #1,loadGo(a6)           ; Set go flag
        clr     loadDone(a6)            ; Clear done flag

        move.b  attrib(a0),d0           ; Get current attributes
        btst.l  #bSPECIAL,d0            ; Special film (SKIP/HYPER)?
        bne.s   TestBranch              ; Yes, can't be EOF!

        tst     fwdLink(a0)             ; Path ending?
        bne.s   TestBranch              ; Skip if not

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

TestBranch:
        btst.l  #bLOOP,d0               ; Reached a loop?
        beq.s   UpdatePList             ; No, update list pointer

        move    #-1,optionIndex         ; Yes, reset option index
        bsr     GetCensorMask           ; Get mask for censored branches
        move    d0,censorMask(a2)       ; Copy to control block

UpdatePList:
        move.l  a0,pList                ; Update list pointer
        clr.l   pPendingList            ; Dispose of pending value

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

        bra     LoadCtrlExit

;------------------------------------------------------------------------------
;                            CopyCtrlLinks
;------------------------------------------------------------------------------
;   Input:  a2 = control block address
;------------------------------------------------------------------------------

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

        move    ctrlCount,d0            ; Get control count
        move    d0,resolveCnt(a2)       ; Copy to control block
        beq.s   CopyCtrlExit            ; Exit if no unresolved controls

        lea     ctrlList,a0             ; List of unresolved controls
        lea     resolveList(a2),a1      ; Destination in control block

CopyCtrlLoop:
        move.l  (a0)+,(a1)+             ; Copy control address
        subq    #1,d0                   ; Decrement counter
        bne.s   CopyCtrlLoop            ; Loop until done

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

;------------------------------------------------------------------------------
;                              DropCrumb
;------------------------------------------------------------------------------
;   Input:  a2 = control block address
;------------------------------------------------------------------------------

DropCrumb:
        move.l  a0,-(sp)                ; Save register

        movea.l pLoadCrumb,a0           ; Pointer to crumb trail
        move.l  a2,(a0)                 ; Control block address
        bsr     FollowTrail             ; Skip to next crumb
        clr.l   (a0)                    ; Mark end of trail
        move.l  a0,pLoadCrumb           ; Update trail pointer

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

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

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

        btst.b  #bLEADIN,attrib(a0)     ; Is this a leadin?
        beq.s   NotLeadin               ; Skip if not

        tst     branchPending           ; Is branch pending?
        beq.s   NotLeadin               ; No, treat leadin generically

        cmpi    #1,leadinCnt            ; First leadin?
        bne.s   NotFirst                ; No, continue

        eori    #4,baseIndex            ; Toggle base buffer index

NotFirst:
        move    baseIndex,d0            ; Base buffer index
        add     leadinCnt,d0            ; Offset by leadin count
        bra.s   BuffLookup              ; Look up buffer

NotLeadin:
        eori    #4,baseIndex            ; Toggle base buffer index
        move    baseIndex,d0            ; Get a copy in d0

BuffLookup:
        move.b  indexTbl(pc,d0),d0      ; Get index from table
        lsl     #2,d0                   ; Index by longs
        lea     bufBlocks,a1            ; Table of buffer control blocks
        move.l  0(a1,d0),nextBuffer     ; Buffer we're going to use
        lea     bufParams,a1            ; Table of parameter blocks
        move.l  0(a1,d0),nextParams     ; Parameters for this buffer

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

indexTbl:
        dc.b    0, 2, 3, 2, 1, 4, 5, 4  ; Buffer indexes

;==============================================================================
;                     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 0   ;----------------------------------------------------------------------
        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:
        bsr     LoopCtrl                ; Resolve loop, if active

        tst.l   pNextCrumb              ; Valid pointer?
        bne.s   FindPlayBuff            ; Yes, go get the buffer

        bsr     GetCrumb                ; Get next crumb from trail
        tst.l   pNextCrumb              ; Did we get one?
        beq     PlayCtrlExit            ; No, wait for loader to drop one

FindPlayBuff:
        movea.l pNextCrumb,a0           ; Get crumb trail pointer
        movea.l (a0),a1                 ; Buffer control block
        tst     bufReady(a1)            ; Is buffer ready?
        beq     PlayCtrlExit            ; No, exit

        bsr     SetPlayFlags            ; Set various flags
        bsr     EatCrumb                ; Swallow crumb from trail

        move.l  a1,playBCtrl(a6)        ; Move pointer to player control
        clr     playDone(a6)            ; Clear done flag
        move    #1,playGo(a6)           ; Set go flag
.if 1   ;----------------------------------------------------------------------
        move.l  #'PLAY',d0              ; Tag
        clr.l   d1                      ; Clear high word
        move    loopFlag,d1             ; Record loop flag
        move.l  a1,d2                   ; Record player control block
        bsr     RecordEvent             ; Leave crumb for debug
.endif  ;----------------------------------------------------------------------
        bra     PlayCtrlExit

;------------------------------------------------------------------------------
;                               LoopCtrl
;------------------------------------------------------------------------------

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

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

        tst     optionIndex             ; Has decision been made?
        bge.s   ExitLoop                ; Yes, exit the loop

        clr     loadDefault             ; Clear default leadin flag
        addq    #1,loopIndex            ; Increment loop index
        cmpi    #4,loopIndex            ; Reached 4 yet?
        bne     LoopCtrlExit            ; No, continue looping

        move    #2,optionIndex          ; Default path
        cmpi    #HARD,difficulty        ; Difficulty set to HARD?
        bne.s   ExitLoop                ; Continue if not

        move    #1,extraLoop            ; Otherwise, force extra loop

ExitLoop:
        clr     loopFlag                ; Clear flag
        move.l  pendingNode,lastNode    ; Last completed node
        move.l  pendingPath,lastPath    ; Corresponding path position

        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.l  lastPath,contPath       ; Capture path pointer

        bsr     SaveStatus              ; Save score and profile

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

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

;------------------------------------------------------------------------------
;                             SetPlayFlags
;------------------------------------------------------------------------------

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

        tst     loopFlag                ; Continuing loop?
        bne     SetFlagsExit            ; Yes, don't repeat setup!

        movea.l pListAddr(a1),a0        ; Playlist address
        move.b  attrib(a0),d0           ; Get film attributes
        btst.l  #bLEADOUT,d0            ; Is this a leadout?
        beq.s   SetFlags                ; No, continue

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

SetFlags:
        clr     hyperFlag               ; Clear hypermode by default

        btst.l  #bLOOP,d0               ; Is this a loop segment?
        bne.s   SetLoop                 ; Yes, set loop parameters

        btst.l  #bSPECIAL,d0            ; Is this a special segment?
        beq.s   SetFlagsExit            ; Exit if not

        cmpi.b  #HYPER,fRule(a0)        ; HYPER segment?
        beq.s   SetHyper                ; Yes, set hypermode parameters

        bra.s   SetFlagsExit            ; No, exit

SetLoop:
        tst     autoPilot               ; On automatic pilot?
        bne.s   SetFlagsExit            ; Yes, inhibit loop machinery

        move    #1,loopFlag             ; Yes, set flag
        clr     extraLoop               ; Clear extra loop flag
        clr     loadDefault             ; Clear default leadin flag

        lea     MinTable,a1             ; Table of minimum indexes
        move    difficulty,d0           ; Get difficulty setting

FindMin:
        cmp     0(a1),d0                ; Matched difficulty setting?
        beq.s   SetMin                  ; Yes, set starting index

        lea     4(a1),a1                ; No, advance table pointer
        bra.s   FindMin                 ; Try again

SetMin:
        move    2(a1),minIndex          ; Set minimum value
        move    2(a1),loopIndex         ; Set initial value
        bra.s   SetFlagsExit            ; Exit

SetHyper:
        tst     autoPilot               ; On automatic pilot?
        bne.s   SetFlagsExit            ; Yes, inhibit hyper machinery

        move    #1,hyperFlag            ; Set hypermode flag
        clr     hyClickCnt(a0)          ; Zero out keypress count

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

MinTable:
        dc.w    EASY,   0               ; EASY: (0,1,2,3)
        dc.w    MEDIUM, 1               ; MEDIUM: (1,2,3)
        dc.w    HARD,   2               ; HARD: (2,3)

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

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

        movea.l playBCtrl(a6),a0        ; Buffer control block
        movea.l pListAddr(a0),a0        ; Current playlist address

        move    optionIndex,d1          ; Get option index
        bset.b  d1,history(a0)          ; Set corresponding bit
        move.b  history(a0),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

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

GetCrumb:
        movem.l d0/a0-a2,-(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:
        tst     autoPilot               ; On automatic pilot?
        bne.s   NextTrail               ; Yes, just get next crumb

        movea.l (a0),a1                 ; Control block
        movea.l pListAddr(a1),a2        ; Playlist address
        move.b  attrib(a2),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

NextTrail:
        bsr     FollowTrail             ; Skip to next crumb
        tst.l   (a0)                    ; Ahead of loader?
        beq.s   GetCrumbExit            ; Yes, return null pointer

        bra.s   SetNextCrumb            ; Use this one

LoopCrumb:
        move    optionIndex,d0          ; Get optionIndex
        blt.s   SetNextCrumb            ; Reuse crumb while negative

        tst     extraLoop               ; Forced extra loop?
        bne.s   SetNextCrumb            ; Yes, reuse crumb

SkipCrumb:
        bsr     FollowTrail             ; Else, skip to next crumb
        tst.l   (a0)                    ; Ahead of loader?
        beq.s   GetCrumbExit            ; Yes, return null pointer

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

        clr     bufLocked(a1)           ; Release current buffer
        clr     bufReady(a1)            ; Clear ready flag
        bra.s   SetNextCrumb            ; Found correct leadin

LeadinCrumb:
        bsr     FollowTrail             ; Skip to next crumb
        tst.l   (a0)                    ; Ahead of loader?
        beq.s   GetCrumbExit            ; Yes, return null pointer

        movea.l (a0),a1                 ; Control block
        movea.l pListAddr(a1),a1        ; Playlist address
        btst.b  #bLEADIN,attrib(a1)     ; Another leadin?
        bne.s   LeadinCrumb             ; Yes, loop until not

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

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

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

FollowTrail:
        lea     4(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
;------------------------------------------------------------------------------
;   Updates pointer to loader crumb trail and disposes of pending crumb.
;
;   Input:  a0 = pointer to next crumb
;------------------------------------------------------------------------------

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

        move.l  a0,pPlayCrumb           ; Update master copy
        clr.l   pNextCrumb              ; Dispose of pending value

        movea.l (a0),a1                 ; Control block
        movea.l pListAddr(a1),a1        ; Playlist address
        btst.b  #bSPECIAL,attrib(a1)    ; Special film (SKIP/HYPER)?
        bne.s   EatCrumbExit            ; Yes, can't be EOF!

        tst     fwdLink(a1)             ; Path ending?
        bne.s   EatCrumbExit            ; No, continue

        move    #1,playEOP              ; Set flag

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

;------------------------------------------------------------------------------
;                              ResetFade
;------------------------------------------------------------------------------

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

        clr     fadeOut                 ; Clear fadeOut flag
        clr     fadeIn                  ; Clear fadeIn flag
        clr     fadePending             ; Clear fadePending flag
        clr     huntLeadout             ; Clear huntLeadout 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

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

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

ctrlEnable:     dc.w    0
loopFlag:       dc.w    0
warmstart:      dc.w    0
gameOver:       dc.w    0
playback:       dc.w    0
remind:         dc.w    0
continue:       dc.w    0
vcrMode:        dc.w    0
autoPilot::     dc.w    0

        .long

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

execEntry:      dc.l    GameLoop
loadEntry:      dc.l    0
playEntry:      dc.l    0
pPath::         dc.l    pathRecord      ; Pointer to path record

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    BUF0_BASE       ; bufStart
                dc.l    BUF0_END        ; bufEnd
                dc.w    1               ; bufLocked

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

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

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

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

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

;------------------------------------------------------------------------------
;   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*4        ; Storage for 10 crumbs
endTrail:

trackOffset:    ds.l    1
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
pendingPath::   ds.l    1
pList:          ds.l    1
pPendingList:   ds.l    1
nextBuffer:     ds.l    1
nextParams:     ds.l    1

loopIndex:      ds.w    1
optionIndex:    ds.w    1
baseIndex:      ds.w    1
leadinCnt:      ds.w    1
segActive:      ds.w    1
rdBeforeWr:     ds.w    1
loadEOP:        ds.w    1
playEOP:        ds.w    1
waitReload:     ds.w    1
pauseFilm:      ds.w    1
initLoad:       ds.w    1
censored:       ds.w    1
hyperFlag:      ds.w    1
minIndex:       ds.w    1
extraLoop:      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 CSXSETCHAN
	.extern silenceBlock
	.extern silencePointer
	.extern silenceSize
	.extern silenceEnd


        .extern MediaGetFirst
        .extern IntInit
        .extern currFilm
        .extern frameNum
        .extern time

        .extern LoadIdle
        .extern LoadMovie
        .extern PlayIdle
        .extern GetAny

        .extern RecordEvent
        .extern fadeIn
        .extern fadeOut
        .extern pathRecord
        .extern score
        .extern profile
        .extern CDWritePtr
        .extern difficulty
        .extern DECOMP_S
        .extern BlockMove
        .extern contNode
        .extern contPath
        .extern contScore
        .extern contProfile
        .extern SampleJoy
        .extern joyRise
        .extern UpdateProfile
        .extern UpdateScore
        .extern InitNVM
        .extern HiLiteSubtitle
        .extern lastNode
        .extern subtEnable
        .extern playListEnt
        .extern VolumeCtrl
        .extern EndCtrl
        .extern PauseCtrl
        .extern InitPCtrl
        .extern filmOffset
        .extern filmSync
        .extern Navigate
        .extern GetCensorMask
        .extern ctrlCount
        .extern ctrlList
        .extern branchPending
        .extern fadePending
        .extern huntLeadout
        .extern segCount
        .extern loadDefault
        .extern lastPath
	.extern	DSP_ARGS


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

        .globl  _start          ; Added (SDS)

        .globl  Executive
        .globl  filmIndex
        .globl  segActive
        .globl  loopIndex
        .globl  rdBeforeWr
        .globl  remind
        .globl  pauseFilm
        .globl  joySemaphore
        .globl  optionIndex
        .globl  pauseFrame
        .globl  waitReload
        .globl  CDReloadPtr
        .globl  pauseTime
        .globl  gameOver
        .globl  continue
        .globl  vcrMode
        .globl  censored
        .globl  initLoad
        .globl  leadinCnt
        .globl  pList
        .globl  pPendingList
        .globl  minIndex

        .end










