;
;  gameSegments.s
;  BuGS
;
;  Created by Jeremy Rand on 2020-07-30.
;Copyright © 2020 Jeremy Rand. All rights reserved.
;

        case on
        mcopy gameSegments.macros
        keep gameSegments

gameSegments start
        using globalData
		using tileData
        
        
SEGMENT_MAX_NUM     equ 12
SEGMENT_MAX_OFFSET  equ SEGMENT_MAX_NUM*2-2

SEGMENT_STATE_NONE          equ 0
SEGMENT_STATE_EXPLODING     equ 1
SEGMENT_STATE_HEAD          equ 2
SEGMENT_STATE_POISONED_HEAD equ 3
SEGMENT_STATE_BODY          equ 4

SEGMENT_DIR_DOWN    equ 0
SEGMENT_DIR_UP      equ 1

; You would think I would need "facing up" variants here also.  When the centipede gets to the bottom of
; the screen, it starts traveling up again and when it turns, you would think it would face up.  Turns out
; the real arcade system uses "facing down" sprites even when the centipede is moving up.  So, I guess I
; will too.  I suspect that the players tend not to notice because by the time the centipede is traveling
; up, they are pretty occupied by the fact that they are now surrounded by segments.
SEGMENT_FACING_LEFT         equ 0
SEGMENT_FACING_DOWN_LEFT    equ 32
SEGMENT_FACING_DOWN         equ 64
SEGMENT_FACING_DOWN_RIGHT   equ 96
SEGMENT_FACING_RIGHT        equ 128

SEGMENT_MAX_POSITION_OFFSET     equ TILE_PIXEL_WIDTH*SEGMENT_MAX_NUM*2-2


segmentsInitLevel entry
		stz numSegments
		ldx #SEGMENT_MAX_OFFSET
		lda #SEGMENT_STATE_NONE
segmentsInitLevel_loop anop
		sta segmentStates,x
		dex
		dex
		bpl segmentsInitLevel_loop
		rtl

                
drawSegments entry
        ldx #SEGMENT_MAX_OFFSET
drawSegments_nextSegment anop
        lda segmentStates,x
        bne drawSegments_cont
        jmp drawSegments_skipSegment

drawSegments_cont anop
        phx
		cmp #SEGMENT_STATE_EXPLODING
		beq drawSegments_exploding
        cmp #SEGMENT_STATE_BODY
        bne drawSegments_head
        jsl segmentBodyJump
        bra drawSegments_handleTiles
		
drawSegments_exploding anop
		jsl segmentExplodingJump
		bra drawSegments_handleTiles
	
drawSegments_head anop
        jsl segmentHeadJump
        
; By the time we come back from segment*Jump, the X register no longer has the original 0-22
; offset.  Instead, we have a position offset which goes from 0-190.  But that is good because
; we need to use that offset to update our dirty tiles.
        
drawSegments_handleTiles anop
; We do have to handle non-game tiles.  When centipede reaches the bottom, new segements are
; added randomly in from the side.  That segment needs to be clipped so we need to use non-game
; tiles to do that clipping for us.
        _dirtyGameOrNonGameTileX segmentTileOffsetsUL
        _dirtyGameOrNonGameTileX segmentTileOffsetsUR
        _dirtyGameOrNonGameTileX segmentTileOffsetsLL
        _dirtyGameOrNonGameTileX segmentTileOffsetsLR
        
; Get our original 0-22 offset we are iterating through that we pushed earlier because we know
; it was going to get trashed.
        plx
drawSegments_skipSegment anop
        dex
        dex
        bmi drawSegments_done
        jmp drawSegments_nextSegment
        
drawSegments_done anop
        rtl
      
segmentExplodingJump entry
		lda segmentPosOffset,x
		tax
		ldy segmentFacing,x
		lda explosionJumpTable,y
		sta segmentExplodingJump_jumpInst+1
		lda explosionJumpTable+2,y
		sta segmentExplodingJump_jumpInst+3
		ldy segmentScreenOffsets,x
		
segmentExplodingJump_jumpInst anop
		jmp >leftHead1
		nop
        
segmentHeadJump entry
        lda segmentPixelOffset
        and segmentSpeed,x
        tay
        
        lda segmentPosOffset,x
        tax
        
        lda segmentFacing,x
        clc
        adc segmentSpriteOffset
        
        cpy #0
        beq segmentHeadJump_noShift
        
        tay
        lda headShiftJumpTable,y
        sta segmentHeadJump_jumpInst+1
        lda headShiftJumpTable+2,y
        sta segmentHeadJump_jumpInst+3
        
        ldy segmentScreenOffsets,x
        bra segmentHeadJump_jumpInst
        
segmentHeadJump_noShift anop
        tay
        lda headJumpTable,y
        sta segmentHeadJump_jumpInst+1
        lda headJumpTable+2,y
        sta segmentHeadJump_jumpInst+3
        
        ldy segmentScreenOffsets,x
        
segmentHeadJump_jumpInst anop
        jmp >leftHead1
        nop
        
segmentBodyJump entry
        lda segmentPixelOffset
        and segmentSpeed,x
        tay
        
        lda segmentPosOffset,x
        tax
        
        lda segmentFacing,x
        clc
        adc segmentSpriteOffset
        
        cpy #0
        beq segmentBodyJump_noShift
        
        tay
        lda bodyShiftJumpTable,y
        sta segmentBodyJump_jumpInst+1
        lda bodyShiftJumpTable+2,y
        sta segmentBodyJump_jumpInst+3
        
        ldy segmentScreenOffsets,x
        bra segmentBodyJump_jumpInst
        
segmentBodyJump_noShift anop
        tay
		lda bodyJumpTable,y
        sta segmentBodyJump_jumpInst+1
        lda bodyJumpTable+2,y
        sta segmentBodyJump_jumpInst+3
        
        ldy segmentScreenOffsets,x
        
segmentBodyJump_jumpInst anop
        jmp >leftHead1
        nop
        
updateSegments entry
		lda playerState
		cmp #PLAYER_STATE_ONSCREEN
		beq updateSegments_playerOnscreen
; Even when the player is offscreen, we need to update any segments which are exploding
		ldx #SEGMENT_MAX_OFFSET
updateSegments_explodeOnlyLoop anop
		lda segmentStates,x
		cmp #SEGMENT_STATE_EXPLODING
		bne updateSegments_explodeOnlyNext
		ldy segmentPosOffset,x
		lda segmentFacing,y
		beq updateSegments_explodeOnlyDone
		sec
		sbc #$4
		sta segmentFacing,y
		bra updateSegments_explodeOnlyNext
updateSegments_explodeOnlyDone anop
		lda #SEGMENT_STATE_NONE
		sta segmentStates,x
updateSegments_explodeOnlyNext anop
		dex
		dex
		bpl updateSegments_explodeOnlyLoop
		rtl
		
updateSegments_playerOnscreen anop
; Clear the segment mask to start.
		stz segmentTileMask+0
		stz segmentTileMask+2
		stz segmentTileMask+4
		stz segmentTileMask+6
		stz segmentTileMask+8
		stz segmentTileMask+10
		stz segmentTileMask+12
		stz segmentTileMask+14
		stz segmentTileMask+16
		stz segmentTileMask+18
		stz segmentTileMask+20
		stz segmentTileMask+22
		stz segmentTileMask+24
		stz segmentTileMask+26
		stz segmentTileMask+28
		stz segmentTileMask+30
		stz segmentTileMask+32
		stz segmentTileMask+34
		stz segmentTileMask+36
		stz segmentTileMask+38
		stz segmentTileMask+40
		stz segmentTileMask+42
		stz segmentTileMask+44
		stz segmentTileMask+46
		stz segmentTileMask+48
		stz segmentTileMask+50
		stz segmentTileMask+52
		stz segmentTileMask+54
		stz segmentTileMask+56
		stz segmentTileMask+58
		stz segmentTileMask+60
		stz segmentTileMask+62
		stz segmentTileMask+64
		stz segmentTileMask+66
		stz segmentTileMask+68
		stz segmentTileMask+70
		stz segmentTileMask+72
		stz segmentTileMask+74
		stz segmentTileMask+76
		stz segmentTileMask+78

        lda segmentPixelOffset
        inc a
        and #TILE_PIXEL_WIDTH-1
        sta segmentPixelOffset
        and #1
        beq updateSegments_skipSpriteOffset

        lda segmentSpriteOffset
        beq updateSegments_resetSpriteOffset
        sec
        sbc #$4
        bra updateSegments_spriteOffsetCont
        
updateSegments_resetSpriteOffset anop
        lda #SEGMENT_SPRITE_LAST_OFFSET
updateSegments_spriteOffsetCont anop
        sta segmentSpriteOffset

updateSegments_skipSpriteOffset anop
		lda #SEGMENT_MAX_OFFSET+2
		sta segmentEmptyOffset
        ldx #SEGMENT_MAX_OFFSET
updateSegments_nextSegment anop
        lda segmentStates,x
        bne updateSegments_cont
		stx segmentEmptyOffset
		lda segmentPosOffset,x
		beq updateSegments_emptyWrapPos
		dec a
		dec a
		sta segmentPosOffset,x
        jmp updateSegments_skipSegment
updateSegments_emptyWrapPos anop
		lda #SEGMENT_MAX_POSITION_OFFSET
		sta segmentPosOffset,x
		jmp updateSegments_skipSegment
updateSegments_cont anop
        cmp #SEGMENT_STATE_BODY
        bne updateSegments_headOrExploding
        
        lda segmentPosOffset,x
        beq updateSegments_bodyWrapPos
        dec a
        dec a
        sta segmentPosOffset,x
        bra updateSegments_bodyMarkTile
updateSegments_bodyWrapPos anop
        lda #SEGMENT_MAX_POSITION_OFFSET
        sta segmentPosOffset,x
updateSegments_bodyMarkTile anop
		phx
		tay
		ldx segmentCurrentTile,y
		ldy tileBitOffset,x
		lda tileBitMask,x
		ora segmentTileMask,y
		sta segmentTileMask,y
		plx
        jmp updateSegments_skipSegment

updateSegments_headOrExploding anop
        lda segmentPosOffset,x
        beq updateSegments_headWrapPos
        dec a
        dec a
        sta segmentPosOffset,x
        tay
        jmp updateSegments_headNoWrap
        
updateSegments_headWrapPos anop
        lda #SEGMENT_MAX_POSITION_OFFSET
        sta segmentPosOffset,x
        tay
; Copy the 0th position entry to the SEGMENT_POSITION_OFFSET_SPARE(th) entry.
; That way, Y points to the new entry for the head and Y+2 always points to
; the previous entry, even on a wrap around.
        lda segmentHorizontalDir
        sta segmentHorizontalDir+SEGMENT_MAX_POSITION_OFFSET
        lda segmentVerticalDir
        sta segmentVerticalDir+SEGMENT_MAX_POSITION_OFFSET
        lda segmentScreenOffsets
        sta segmentScreenOffsets+SEGMENT_MAX_POSITION_OFFSET
        lda segmentTileOffsetsUL
        sta segmentTileOffsetsUL+SEGMENT_MAX_POSITION_OFFSET
        lda segmentTileOffsetsUR
        sta segmentTileOffsetsUR+SEGMENT_MAX_POSITION_OFFSET
        lda segmentTileOffsetsLL
        sta segmentTileOffsetsLL+SEGMENT_MAX_POSITION_OFFSET
        lda segmentTileOffsetsLR
        sta segmentTileOffsetsLR+SEGMENT_MAX_POSITION_OFFSET
		lda segmentCurrentTile
		sta segmentCurrentTile+SEGMENT_MAX_POSITION_OFFSET
        
; We could be in exploding state and if so, we want to skip the rest of the
; code for update head segments.
		lda segmentStates,x
		cmp #SEGMENT_STATE_EXPLODING
		bne updateSegments_notExplodingWrap
		jsr updateSegmentExplodingWrap
		bra updateSegments_skipSegment
updateSegments_notExplodingWrap anop
; Important - Do facing last because we use that to index into the jump
; table for update.
        lda segmentFacing
        sta segmentFacing+SEGMENT_MAX_POSITION_OFFSET
        bra updateSegments_headCont
        
updateSegments_headNoWrap anop
; Assume no change in any of the data for now and just copy it into place.
        lda segmentHorizontalDir+2,y
        sta segmentHorizontalDir,y
        lda segmentVerticalDir+2,y
        sta segmentVerticalDir,y
        lda segmentScreenOffsets+2,y
        sta segmentScreenOffsets,y
        lda segmentTileOffsetsUL+2,y
        sta segmentTileOffsetsUL,y
        lda segmentTileOffsetsUR+2,y
        sta segmentTileOffsetsUR,y
        lda segmentTileOffsetsLL+2,y
        sta segmentTileOffsetsLL,y
        lda segmentTileOffsetsLR+2,y
        sta segmentTileOffsetsLR,y
		lda segmentCurrentTile+2,y
		sta segmentCurrentTile,y
		
; We could be in exploding state and if so, we want to skip the rest of the
; code for update head segments.
		lda segmentStates,x
		cmp #SEGMENT_STATE_EXPLODING
		bne updateSegments_notExplodingNoWrap
		jsr updateSegmentExplodingNoWrap
		bra updateSegments_skipSegment
updateSegments_notExplodingNoWrap anop
; Important - Do facing last because we use that to index into the jump
; table for update.
        lda segmentFacing+2,y
        sta segmentFacing,y
        
updateSegments_headCont anop
; This is funky.  Each "facing" value is in increments of 32 to make figuring out where
; the sprite callback is easy but it makes this update callback harder.  We divide by
; 16 to get values incrementing by 2's.  Then, we or in the speed.  The speed is 0 or 1
; so now we have values incrementing by 1 each with a unique pair of facing and speed.
; Then we multiply by two again so we have unique values incrementing by 2 and that is
; used as an index into the jump table.  That way, we jump to a subroutine which is
; unique for each facing/speed pair.
        lsr a
        lsr a
        lsr a
        lsr a
        ora segmentSpeed,x
        asl a
        stx segmentBeingUpdated
        tax
        jsr (segmentUpdateJumpTable,x)
		ldx segmentCurrentTile,y
		ldy tileBitOffset,x
		lda tileBitMask,x
		ora segmentTileMask,y
		sta segmentTileMask,y
		ldx segmentBeingUpdated
updateSegments_skipSegment anop
        dex
        dex
        bmi updateSegments_maybeAdd
        jmp updateSegments_nextSegment
updateSegments_maybeAdd anop
		lda segmentsAddEnabled
		bne updateSegments_done
		lda segmentEmptyOffset
		cmp #SEGMENT_MAX_OFFSET+2
		bge updateSegments_done
		lda numSegments
		beq updateSegments_done
		cmp #SEGMENT_MAX_NUM
		bge updateSegments_done
		lda segmentPixelOffset
		and #3
		cmp #1
		bne updateSegments_done
		jsl rand0_to_65534
		and #31
		bne updateSegments_done
		jmp updateSegmentAddSegment
updateSegments_done anop
        rtl

updateSegmentAddSegment entry
		inc numSegments
		ldx segmentEmptyOffset
		
		lda #SEGMENT_STATE_HEAD
		sta segmentStates,x
		
		lda #SEGMENT_SPEED_FAST
		sta segmentSpeed,x
		
		ldy segmentPosOffset,x
		
		lda #SEGMENT_DIR_DOWN
		sta segmentVerticalDir,y
		
		jsl rand0_to_65534
		and #$0080
		beq updateSegmentAddSegment_leftSide
		
		lda #SEGMENT_DIR_LEFT
		sta segmentHorizontalDir,y
		
		lda #SEGMENT_FACING_LEFT
		sta segmentFacing,y
		
		ldx #((GAME_NUM_TILES_WIDE*(GAME_NUM_TILES_TALL-6))-1)*SIZEOF_TILE_INFO
		
		lda tileScreenOffset,x
		sta segmentScreenOffsets,y
		
		txa
		sta segmentTileOffsetsUL,y
		sta segmentTileOffsetsLL,y
		sta segmentCurrentTile,y
		lda tileRight,x
		sta segmentTileOffsetsUR,y
		sta segmentTileOffsetsLR,y
		rtl
		
updateSegmentAddSegment_leftSide anop
		lda #SEGMENT_DIR_RIGHT
		sta segmentHorizontalDir,y

		lda #SEGMENT_FACING_RIGHT
		sta segmentFacing,y

		ldx #(GAME_NUM_TILES_WIDE*(GAME_NUM_TILES_TALL-7))*SIZEOF_TILE_INFO
		lda tileScreenOffset,x
		sec
		sbc #6
		sta segmentScreenOffsets,y
		
		txa
		sta segmentTileOffsetsUR,y
		sta segmentTileOffsetsLR,y
		sta segmentCurrentTile,y
		lda tileLeft,x
		sta segmentTileOffsetsUL,y
		sta segmentTileOffsetsLL,y
		rtl
		
updateSegmentExplodingNoWrap entry
		lda segmentFacing+2,y
		bra updateSegmentExploding_common
		
updateSegmentExplodingWrap	entry
		lda segmentFacing
updateSegmentExploding_common anop
		beq updateSegmentExploding_done
		sec
		sbc #$4
		sta segmentFacing,y
		rts
		
updateSegmentExploding_done anop
		lda #SEGMENT_STATE_NONE
		sta segmentStates,x
		rts
        

updateSegmentLeftFast entry
        tyx
        dec segmentScreenOffsets,x

        lda segmentPixelOffset
        and #3
        bne updateSegmentLeftFast_nextOffset
        lda segmentTileOffsetsUL,y
        sta segmentTileOffsetsUR,y
        sta segmentTileOffsetsLR,y
		rts
        
updateSegmentLeftFast_nextOffset anop
        cmp #1
        bne updateSegmentLeftFast_nextOffset2
        
        lda segmentTileOffsetsUL,y
        tax
        lda tileLeft,x
        sta segmentTileOffsetsUL,y
        sta segmentTileOffsetsLL,y
		sta segmentCurrentTile,y
		rts

updateSegmentLeftFast_nextOffset2 anop
        cmp #3
        beq updateSegmentLeftFast_checkDir
		rts

updateSegmentLeftFast_checkDir anop
        ldx segmentBeingUpdated
        lda segmentStates,x
        cmp #SEGMENT_STATE_POISONED_HEAD
        beq updateSegmentLeftFast_changeDir
; This is a important special case.  When we add segments on the side, we don't want a mushroom
; right on the edge or near the edge to cause a turn and make the segment head off-screen.  This
; special case is there only for fast head segments because we only add fast head segments on the
; right and left.
		ldx segmentTileOffsetsUR,y
		cpx #RHS_FIRST_TILE_OFFSET
		bge updateSegmentLeftFast_noChangeDir
        ldx segmentTileOffsetsUL,y
		cpx #LHS_FIRST_TILE_OFFSET
		bge updateSegmentLeftFast_changeDir
		lda tileType,x
		bne updateSegmentLeftFast_checkPoison
		phy
		txy
		ldx tileBitOffset,y
		lda tileBitMask,y
		ply
		and segmentTileMask,x
		bne updateSegmentLeftFast_changeDir
		ldx segmentTileOffsetsUL,y

        lda tileLeft,x
        cmp #LHS_FIRST_TILE_OFFSET
        bge updateSegmentLeftFast_changeDir
        tax
        lda tileType,x
        bne updateSegmentLeftFast_checkPoison
		phy
		txy
        ldx tileBitOffset,y
		lda tileBitMask,y
		ply
		and segmentTileMask,x
		bne updateSegmentLeftFast_changeDir
updateSegmentLeftFast_noChangeDir anop
		rts
		
updateSegmentLeftFast_checkPoison anop
        cmp #TILE_POISON_MUSHROOM1
        blt updateSegmentLeftFast_changeDir
        ldx segmentBeingUpdated
        lda #SEGMENT_STATE_POISONED_HEAD
        sta segmentStates,x
        
updateSegmentLeftFast_changeDir anop
        lda #SEGMENT_FACING_DOWN_LEFT
        sta segmentFacing,y
        lda #SEGMENT_DIR_RIGHT
        sta segmentHorizontalDir,y
        lda segmentVerticalDir,y
        beq updateSegmentLeftFast_dirDown

        lda segmentTileOffsetsUR,y
        cmp #(NUM_GAME_TILES-5*GAME_NUM_TILES_WIDE)*SIZEOF_TILE_INFO
        bge updateSegmentLeftFast_doUp
        lda #SEGMENT_DIR_DOWN
        sta segmentVerticalDir,y
        bra updateSegmentLeftFast_doDown

updateSegmentLeftFast_doUp anop
        lda segmentScreenOffsets,y
        sec
        sbc #SCREEN_BYTES_PER_ROW*2
        sta segmentScreenOffsets,y
        
        lda segmentTileOffsetsLR,y
        tax
        lda tileAbove,x
        sta segmentTileOffsetsUR,y
        
        lda segmentTileOffsetsLL,y
        tax
        lda tileAbove,x
        sta segmentTileOffsetsUL,y
		sta segmentCurrentTile,y
        rts
        
updateSegmentLeftFast_dirDown anop
        lda segmentTileOffsetsUR,y
        cmp #(NUM_GAME_TILES-GAME_NUM_TILES_WIDE)*SIZEOF_TILE_INFO
        blt updateSegmentLeftFast_doDown
; If the head segment was poisoned, it is no longer poisoned once the head is going up again.
		lda #SEGMENT_DIR_UP
		sta segmentVerticalDir,y
        ldx segmentBeingUpdated
		lda segmentStates,x
		cmp #SEGMENT_STATE_HEAD
		beq updateSegmentLeftFast_notPoisoned
        lda #SEGMENT_STATE_HEAD
        sta segmentStates,x
        bra updateSegmentLeftFast_doUp
updateSegmentLeftFast_notPoisoned anop
		stz segmentsAddEnabled
		bra updateSegmentLeftFast_doUp
        
updateSegmentLeftFast_doDown anop
        lda segmentScreenOffsets,y
        clc
        adc #SCREEN_BYTES_PER_ROW*2
        sta segmentScreenOffsets,y
        
        lda segmentTileOffsetsUR,y
        tax
        lda tileBelow,x
        sta segmentTileOffsetsLR,y
        
        lda segmentTileOffsetsUL,y
        tax
        lda tileBelow,x
        sta segmentTileOffsetsLL,y
		sta segmentCurrentTile,y
        rts

        
updateSegmentLeftSlow entry
        lda #1
        and segmentPixelOffset
        beq updateSegmentLeftSlow_skipDec
        tyx
        dec segmentScreenOffsets,x

updateSegmentLeftSlow_skipDec anop
        lda segmentPixelOffset
        bne updateSegmentLeftSlow_nextOffset
        lda segmentTileOffsetsUL,y
        sta segmentTileOffsetsUR,y
        sta segmentTileOffsetsLR,y
		lda numSegments
		cmp #1
		beq updateSegmentsLeftSlow_lastSegment
        rts
updateSegmentsLeftSlow_lastSegment anop
		ldx segmentBeingUpdated
		lda #SEGMENT_SPEED_FAST
		sta segmentSpeed,x
		rts
        
updateSegmentLeftSlow_nextOffset anop
        cmp #1
        bne updateSegmentLeftSlow_nextOffset2
        
        lda segmentTileOffsetsUL,y
        tax
        lda tileLeft,x
        sta segmentTileOffsetsUL,y
        sta segmentTileOffsetsLL,y
		sta segmentCurrentTile,y
		rts

updateSegmentLeftSlow_nextOffset2 anop
        cmp #5
        beq updateSegmentLeftSlow_checkDir
        rts

updateSegmentLeftSlow_checkDir anop
        ldx segmentBeingUpdated
        lda segmentStates,x
        cmp #SEGMENT_STATE_POISONED_HEAD
        beq updateSegmentLeftSlow_changeDir
        ldx segmentTileOffsetsUL,y
		cpx #LHS_FIRST_TILE_OFFSET
		bge updateSegmentLeftSlow_changeDir
		lda tileType,x
		bne updateSegmentLeftSlow_checkPoison
		phy
		txy
		ldx tileBitOffset,y
		lda tileBitMask,y
		ply
		and segmentTileMask,x
		bne updateSegmentLeftSlow_changeDir
		ldx segmentTileOffsetsUL,y
		
        lda tileLeft,x
        cmp #LHS_FIRST_TILE_OFFSET
        bge updateSegmentLeftSlow_changeDir
        tax
        lda tileType,x
        bne updateSegmentLeftSlow_checkPoison
		phy
		txy
		ldx tileBitOffset,y
		lda tileBitMask,y
		ply
		and segmentTileMask,x
		bne updateSegmentLeftSlow_changeDir
        rts
updateSegmentLeftSlow_checkPoison anop
        cmp #TILE_POISON_MUSHROOM1
        blt updateSegmentLeftSlow_changeDir
        ldx segmentBeingUpdated
        lda #SEGMENT_STATE_POISONED_HEAD
        sta segmentStates,x

updateSegmentLeftSlow_changeDir anop
        lda #SEGMENT_FACING_DOWN_LEFT
        sta segmentFacing,y
        lda #SEGMENT_DIR_RIGHT
        sta segmentHorizontalDir,y
        lda segmentVerticalDir,y
        beq updateSegmentLeftSlow_dirDown

        lda segmentTileOffsetsUR,y
        cmp #(NUM_GAME_TILES-5*GAME_NUM_TILES_WIDE)*SIZEOF_TILE_INFO
        bge updateSegmentLeftSlow_doUp
        lda #SEGMENT_DIR_DOWN
        sta segmentVerticalDir,y
        bra updateSegmentLeftSlow_doDown

updateSegmentLeftSlow_doUp anop
        lda segmentScreenOffsets,y
        sec
        sbc #SCREEN_BYTES_PER_ROW
        sta segmentScreenOffsets,y
        
        lda segmentTileOffsetsLR,y
        tax
        lda tileAbove,x
        sta segmentTileOffsetsUR,y
        
        lda segmentTileOffsetsLL,y
        tax
        lda tileAbove,x
        sta segmentTileOffsetsUL,y
		sta segmentCurrentTile,y
        rts
        
updateSegmentLeftSlow_dirDown anop
        lda segmentTileOffsetsUR,y
        cmp #(NUM_GAME_TILES-GAME_NUM_TILES_WIDE)*SIZEOF_TILE_INFO
        blt updateSegmentLeftSlow_doDown
; If the head segment was poisoned, it is no longer poisoned once the head is going up again.
		lda #SEGMENT_DIR_UP
		sta segmentVerticalDir,y
        ldx segmentBeingUpdated
		lda segmentStates,x
		cmp #SEGMENT_STATE_HEAD
		beq updateSegmentLeftSlow_notPoisoned
        lda #SEGMENT_STATE_HEAD
        sta segmentStates,x
        bra updateSegmentLeftSlow_doUp
updateSegmentLeftSlow_notPoisoned anop
		stz segmentsAddEnabled
		bra updateSegmentLeftSlow_doUp
        
updateSegmentLeftSlow_doDown anop
        lda segmentScreenOffsets,y
        clc
        adc #SCREEN_BYTES_PER_ROW
        sta segmentScreenOffsets,y
        
        lda segmentTileOffsetsUR,y
        tax
        lda tileBelow,x
        sta segmentTileOffsetsLR,y
        
        lda segmentTileOffsetsUL,y
        tax
        lda tileBelow,x
        sta segmentTileOffsetsLL,y
		sta segmentCurrentTile,y
        rts
        
        
updateSegmentDownLeftFast entry
		lda segmentScreenOffsets,y
		cmp #$2000-(7*SCREEN_BYTES_PER_ROW)
		bge updateSegmentDownLeftFast_notOffscreen
		lda segmentPixelOffset
		cmp #7
		beq updateSegmentDownLeftFast_moveOnScreen
		rts

updateSegmentDownLeftFast_moveOnScreen anop
		lda segmentScreenOffsets,y
		clc
		adc #2*SCREEN_BYTES_PER_ROW
		sta segmentScreenOffsets,y
		rts

updateSegmentDownLeftFast_notOffscreen anop
        lda segmentVerticalDir,y
        beq updateSegmentDownLeftFast_down
        lda segmentScreenOffsets,y
        sec
        sbc #SCREEN_BYTES_PER_ROW*2+1
        bra updateSegmentDownLeftFast_cont
        
updateSegmentDownLeftFast_down anop
        lda segmentScreenOffsets,y
        clc
        adc #SCREEN_BYTES_PER_ROW*2-1
        
updateSegmentDownLeftFast_cont anop
        sta segmentScreenOffsets,y
        
        lda segmentPixelOffset
        and #3
        beq updateSegmentDownLeftFast_nextOffset
        
        lda #SEGMENT_FACING_LEFT
        sta segmentFacing,y
        lda segmentVerticalDir,y
        beq updateSegmentDownLeftFast_tilesDown
        
        lda segmentTileOffsetsUL,y
        sta segmentTileOffsetsLL,y
        lda segmentTileOffsetsUR,y
        sta segmentTileOffsetsLR,y
		rts
        
updateSegmentDownLeftFast_tilesDown anop
        lda segmentTileOffsetsLL,y
        sta segmentTileOffsetsUL,y
        lda segmentTileOffsetsLR,y
        sta segmentTileOffsetsUR,y
        rts
        
updateSegmentDownLeftFast_nextOffset anop
        lda #SEGMENT_FACING_DOWN
        sta segmentFacing,y
        rts
        
        
updateSegmentDownLeftSlow entry
        lda segmentScreenOffsets,y
        tax
        lda #1
        and segmentPixelOffset
        beq updateSegmentDownLeftSlow_skipDec
        dex

updateSegmentDownLeftSlow_skipDec anop
        lda segmentVerticalDir,y
        beq updateSegmentDownLeftSlow_down
        txa
        sec
        sbc #SCREEN_BYTES_PER_ROW
        bra updateSegmentDownLeftSlow_cont
        
updateSegmentDownLeftSlow_down anop
        txa
        clc
        adc #SCREEN_BYTES_PER_ROW
        
updateSegmentDownLeftSlow_cont anop
        sta segmentScreenOffsets,y
        
        lda segmentPixelOffset
        cmp #4
        bne updateSegmentDownLeftSlow_nextOffset
        
        lda #SEGMENT_FACING_LEFT
        sta segmentFacing,y
        lda segmentVerticalDir,y
        beq updateSegmentDownLeftSlow_tilesDown
        
        lda segmentTileOffsetsUL,y
        sta segmentTileOffsetsLL,y
        lda segmentTileOffsetsUR,y
        sta segmentTileOffsetsLR,y
        rts
        
updateSegmentDownLeftSlow_tilesDown anop
        lda segmentTileOffsetsLL,y
        sta segmentTileOffsetsUL,y
        lda segmentTileOffsetsLR,y
        sta segmentTileOffsetsUR,y
        rts
        
updateSegmentDownLeftSlow_nextOffset anop
        cmp #7
        bne updateSegmentDownLeftSlow_done
        lda #SEGMENT_FACING_DOWN
        sta segmentFacing,y
        
updateSegmentDownLeftSlow_done anop
        rts
      
      
updateSegmentDownFast entry
        lda segmentHorizontalDir,y
        beq updateSegmentDownFast_left
        
        lda #SEGMENT_FACING_DOWN_RIGHT
        sta segmentFacing,y
        lda segmentVerticalDir,y
        beq updateSegmentDownFast_downRight
        lda segmentScreenOffsets,y
        sec
        sbc #SCREEN_BYTES_PER_ROW*2-1
        bra updateSegmentDownFast_done
        
updateSegmentDownFast_downRight anop
        lda segmentScreenOffsets,y
        clc
        adc #SCREEN_BYTES_PER_ROW*2+1
        bra updateSegmentDownFast_done
        
updateSegmentDownFast_left anop
        lda #SEGMENT_FACING_DOWN_LEFT
        sta segmentFacing,y
        lda segmentVerticalDir,y
        beq updateSegmentDownFast_downLeft
        lda segmentScreenOffsets,y
        sec
        sbc #SCREEN_BYTES_PER_ROW*2+1
        bra updateSegmentDownFast_done
        
updateSegmentDownFast_downLeft anop
        lda segmentScreenOffsets,y
        clc
        adc #SCREEN_BYTES_PER_ROW*2-1
        
updateSegmentDownFast_done anop
        sta segmentScreenOffsets,y
        rts
      
      
updateSegmentDownSlow entry
        lda segmentVerticalDir,y
        beq updateSegmentDownSlow_down
        lda segmentScreenOffsets,y
        sec
        sbc #SCREEN_BYTES_PER_ROW
        bra updateSegmentDownSlow_cont
        
updateSegmentDownSlow_down anop
        lda segmentScreenOffsets,y
        clc
        adc #SCREEN_BYTES_PER_ROW
        
updateSegmentDownSlow_cont anop
        sta segmentScreenOffsets,y

        lda segmentPixelOffset
        cmp #2
        bne updateSegmentDownSlow_done
        
        lda segmentHorizontalDir,y
        beq updateSegmentDownSlow_left
        
        lda #SEGMENT_FACING_DOWN_RIGHT
        sta segmentFacing,y
        tyx
        inc segmentScreenOffsets,x
        rts
        
updateSegmentDownSlow_left anop
        lda #SEGMENT_FACING_DOWN_LEFT
        sta segmentFacing,y
        tyx
        dec segmentScreenOffsets,x

updateSegmentDownSlow_done anop
        rts
 
 
updateSegmentDownRightFast entry
		lda segmentScreenOffsets,y
		cmp #$2000-(7*SCREEN_BYTES_PER_ROW)
		bge updateSegmentDownRightFast_notOffscreen
		lda segmentPixelOffset
		cmp #7
		beq updateSegmentDownRightFast_moveOnScreen
		rts

updateSegmentDownRightFast_moveOnScreen anop
		lda segmentScreenOffsets,y
		clc
		adc #2*SCREEN_BYTES_PER_ROW
		sta segmentScreenOffsets,y
		rts

updateSegmentDownRightFast_notOffscreen anop
        lda segmentVerticalDir,y
        beq updateSegmentDownRightFast_down
        lda segmentScreenOffsets,y
        sec
        sbc #SCREEN_BYTES_PER_ROW*2-1
        bra updateSegmentDownRightFast_cont
        
updateSegmentDownRightFast_down anop
        lda segmentScreenOffsets,y
        clc
        adc #SCREEN_BYTES_PER_ROW*2+1
        
updateSegmentDownRightFast_cont anop
        sta segmentScreenOffsets,y
        
        lda segmentPixelOffset
        and #3
        beq updateSegmentDownRightFast_nextOffset
        
        lda #SEGMENT_FACING_RIGHT
        sta segmentFacing,y
        lda segmentVerticalDir,y
        beq updateSegmentDownRightFast_tilesDown
        
        lda segmentTileOffsetsUL,y
        sta segmentTileOffsetsLL,y
        lda segmentTileOffsetsUR,y
        sta segmentTileOffsetsLR,y
        rts
        
updateSegmentDownRightFast_tilesDown anop
        lda segmentTileOffsetsLL,y
        sta segmentTileOffsetsUL,y
        lda segmentTileOffsetsLR,y
        sta segmentTileOffsetsUR,y
        rts
        
updateSegmentDownRightFast_nextOffset anop
        lda #SEGMENT_FACING_DOWN
        sta segmentFacing,y
        rts
        
        
updateSegmentDownRightSlow entry
        lda segmentScreenOffsets,y
        tax
        lda #1
        and segmentPixelOffset
        beq updateSegmentDownRightSlow_skipInc
        inx

updateSegmentDownRightSlow_skipInc anop
        lda segmentVerticalDir,y
        beq updateSegmentDownRightSlow_down
        txa
        sec
        sbc #SCREEN_BYTES_PER_ROW
        bra updateSegmentDownRightSlow_cont
        
updateSegmentDownRightSlow_down anop
        txa
        clc
        adc #SCREEN_BYTES_PER_ROW
        
updateSegmentDownRightSlow_cont anop
        sta segmentScreenOffsets,y
        
        lda segmentPixelOffset
        cmp #4
        bne updateSegmentDownRightSlow_nextOffset
        
        lda #SEGMENT_FACING_RIGHT
        sta segmentFacing,y
        lda segmentVerticalDir,y
        beq updateSegmentDownRightSlow_tilesDown
        
        lda segmentTileOffsetsUL,y
        sta segmentTileOffsetsLL,y
        lda segmentTileOffsetsUR,y
        sta segmentTileOffsetsLR,y
        rts
        
updateSegmentDownRightSlow_tilesDown anop
        lda segmentTileOffsetsLL,y
        sta segmentTileOffsetsUL,y
        lda segmentTileOffsetsLR,y
        sta segmentTileOffsetsUR,y
        rts
        
updateSegmentDownRightSlow_nextOffset anop
        cmp #7
        bne updateSegmentDownRightSlow_done
        lda #SEGMENT_FACING_DOWN
        sta segmentFacing,y
        
updateSegmentDownRightSlow_done anop
        rts

                
updateSegmentRightFast entry
        tyx
        inc segmentScreenOffsets,x
        
        lda segmentPixelOffset
        and #3
        bne updateSegmentRightFast_nextOffset
        lda segmentTileOffsetsUR,y
        sta segmentTileOffsetsUL,y
        sta segmentTileOffsetsLL,y
        rts
        
updateSegmentRightFast_nextOffset anop
        cmp #1
        bne updateSegmentRightFast_nextOffset2
        
        lda segmentTileOffsetsUR,y
        tax
        lda tileRight,x
        sta segmentTileOffsetsUR,y
        sta segmentTileOffsetsLR,y
		sta segmentCurrentTile,y
        
updateSegmentRightFast_nextOffset2 anop
        cmp #3
        beq updateSegmentRightFast_checkDir
        rts
        
updateSegmentRightFast_checkDir anop
        ldx segmentBeingUpdated
        lda segmentStates,x
        cmp #SEGMENT_STATE_POISONED_HEAD
        beq updateSegmentRightFast_changeDir
; This is a important special case.  When we add segments on the side, we don't want a mushroom
; right on the edge or near the edge to cause a turn and make the segment head off-screen.  This
; special case is there only for fast head segments because we only add fast head segments on the
; right and left.
		ldx segmentTileOffsetsUL,y
		cpx #LHS_FIRST_TILE_OFFSET
		bge updateSegmentRightFast_noChangeDir
        ldx segmentTileOffsetsUR,y
		cpx #RHS_FIRST_TILE_OFFSET
		bge updateSegmentRightFast_changeDir
		lda tileType,x
		bne updateSegmentRightFast_checkPoison
		phy
		txy
		ldx tileBitOffset,y
		lda tileBitMask,y
		ply
		and segmentTileMask,x
		bne updateSegmentRightFast_changeDir
		ldx segmentTileOffsetsUR,y

        lda tileRight,x
        cmp #RHS_FIRST_TILE_OFFSET
        bge updateSegmentRightFast_changeDir
        tax
        lda tileType,x
        bne updateSegmentRightFast_checkPoison
		phy
		txy
		ldx tileBitOffset,y
		lda tileBitMask,y
		ply
		and segmentTileMask,x
		bne updateSegmentRightFast_changeDir
updateSegmentRightFast_noChangeDir anop
        rts
updateSegmentRightFast_checkPoison anop
        cmp #TILE_POISON_MUSHROOM1
        blt updateSegmentRightFast_changeDir
        ldx segmentBeingUpdated
        lda #SEGMENT_STATE_POISONED_HEAD
        sta segmentStates,x

updateSegmentRightFast_changeDir anop
        lda #SEGMENT_FACING_DOWN_RIGHT
        sta segmentFacing,y
        lda #SEGMENT_DIR_LEFT
        sta segmentHorizontalDir,y
        lda segmentVerticalDir,y
        beq updateSegmentRightFast_dirDown

        lda segmentTileOffsetsUR,y
        cmp #(NUM_GAME_TILES-5*GAME_NUM_TILES_WIDE)*SIZEOF_TILE_INFO
        bge updateSegmentRightFast_doUp
        lda #SEGMENT_DIR_DOWN
        sta segmentVerticalDir,y
        bra updateSegmentRightFast_doDown

updateSegmentRightFast_doUp anop
        lda segmentScreenOffsets,y
        sec
        sbc #SCREEN_BYTES_PER_ROW*2
        sta segmentScreenOffsets,y
        
        lda segmentTileOffsetsLR,y
        tax
        lda tileAbove,x
        sta segmentTileOffsetsUR,y
		sta segmentCurrentTile,y
        
        lda segmentTileOffsetsLL,y
        tax
        lda tileAbove,x
        sta segmentTileOffsetsUL,y
        rts
        
updateSegmentRightFast_dirDown anop
        lda segmentTileOffsetsUR,y
        cmp #(NUM_GAME_TILES-GAME_NUM_TILES_WIDE)*SIZEOF_TILE_INFO
        blt updateSegmentRightFast_doDown
; If the head segment was poisoned, it is no longer poisoned once the head is going up again.
		lda #SEGMENT_DIR_UP
		sta segmentVerticalDir,y
        ldx segmentBeingUpdated
		lda segmentStates,x
		cmp #SEGMENT_STATE_HEAD
		beq updateSegmentRightFast_notPoisoned
        lda #SEGMENT_STATE_HEAD
        sta segmentStates,x
        bra updateSegmentRightFast_doUp
updateSegmentRightFast_notPoisoned anop
		stz segmentsAddEnabled
		bra updateSegmentRightFast_doUp
        
updateSegmentRightFast_doDown anop
        lda segmentScreenOffsets,y
        clc
        adc #SCREEN_BYTES_PER_ROW*2
        sta segmentScreenOffsets,y
        
        lda segmentTileOffsetsUR,y
        tax
        lda tileBelow,x
        sta segmentTileOffsetsLR,y
		sta segmentCurrentTile,y
        
        lda segmentTileOffsetsUL,y
        tax
        lda tileBelow,x
        sta segmentTileOffsetsLL,y
        rts
        

updateSegmentRightSlow entry
        lda #1
        and segmentPixelOffset
        beq updateSegmentRightSlow_skipInc
        tyx
        inc segmentScreenOffsets,x

updateSegmentRightSlow_skipInc anop
        lda segmentPixelOffset
        bne updateSegmentRightSlow_nextOffset
        lda segmentTileOffsetsUR,y
        sta segmentTileOffsetsUL,y
        sta segmentTileOffsetsLL,y
        rts
        
updateSegmentRightSlow_nextOffset anop
        cmp #1
        bne updateSegmentRightSlow_nextOffset2
        
        lda segmentTileOffsetsUR,y
        tax
        lda tileRight,x
        sta segmentTileOffsetsUR,y
        sta segmentTileOffsetsLR,y
		sta segmentCurrentTile,y
		lda numSegments
		cmp #1
		beq updateSegmentsRightSlow_lastSegment
		rts
updateSegmentsRightSlow_lastSegment anop
		ldx segmentBeingUpdated
		lda #SEGMENT_SPEED_FAST
		sta segmentSpeed,x
		rts

updateSegmentRightSlow_nextOffset2 anop
        cmp #5
        beq updateSegmentRightSlow_checkDir
        rts

updateSegmentRightSlow_checkDir anop
        ldx segmentBeingUpdated
        lda segmentStates,x
        cmp #SEGMENT_STATE_POISONED_HEAD
        beq updateSegmentRightSlow_changeDir
        ldx segmentTileOffsetsUR,y
		cpx #RHS_FIRST_TILE_OFFSET
		bge updateSegmentRightSlow_changeDir
		lda tileType,x
		bne updateSegmentRightSlow_checkPoison
		phy
		txy
		ldx tileBitOffset,y
		lda tileBitMask,y
		ply
		and segmentTileMask,x
		bne updateSegmentRightSlow_changeDir
		ldx segmentTileOffsetsUR,y
		
        lda tileRight,x
        cmp #RHS_FIRST_TILE_OFFSET
        bge updateSegmentRightSlow_changeDir
        tax
        lda tileType,x
        bne updateSegmentRightSlow_checkPoison
		phy
		txy
		ldx tileBitOffset,y
		lda tileBitMask,y
		ply
		and segmentTileMask,x
		bne updateSegmentRightSlow_changeDir
        rts
updateSegmentRightSlow_checkPoison anop
        cmp #TILE_POISON_MUSHROOM1
        blt updateSegmentRightSlow_changeDir
        ldx segmentBeingUpdated
        lda #SEGMENT_STATE_POISONED_HEAD
        sta segmentStates,x

updateSegmentRightSlow_changeDir anop
        lda #SEGMENT_FACING_DOWN_RIGHT
        sta segmentFacing,y
        lda #SEGMENT_DIR_LEFT
        sta segmentHorizontalDir,y
        lda segmentVerticalDir,y
        beq updateSegmentRightSlow_dirDown

        lda segmentTileOffsetsUR,y
        cmp #(NUM_GAME_TILES-5*GAME_NUM_TILES_WIDE)*SIZEOF_TILE_INFO
        bge updateSegmentRightSlow_doUp
        lda #SEGMENT_DIR_DOWN
        sta segmentVerticalDir,y
        bra updateSegmentRightSlow_doDown

updateSegmentRightSlow_doUp anop
        lda segmentScreenOffsets,y
        sec
        sbc #SCREEN_BYTES_PER_ROW
        sta segmentScreenOffsets,y
        
        lda segmentTileOffsetsLR,y
        tax
        lda tileAbove,x
        sta segmentTileOffsetsUR,y
		sta segmentCurrentTile,y
        
        lda segmentTileOffsetsLL,y
        tax
        lda tileAbove,x
        sta segmentTileOffsetsUL,y
        rts
        
updateSegmentRightSlow_dirDown anop
        lda segmentTileOffsetsUR,y
        cmp #(NUM_GAME_TILES-GAME_NUM_TILES_WIDE)*SIZEOF_TILE_INFO
        blt updateSegmentRightSlow_doDown
; If the head segment was poisoned, it is no longer poisoned once the head is going up again.
		lda #SEGMENT_DIR_UP
		sta segmentVerticalDir,y
		ldx segmentBeingUpdated
		lda segmentStates,x
		cmp #SEGMENT_STATE_HEAD
		beq updateSegmentRightSlow_notPoisoned
        lda #SEGMENT_STATE_HEAD
        sta segmentStates,x
		bra updateSegmentRightSlow_doUp
updateSegmentRightSlow_notPoisoned anop
		stz segmentsAddEnabled
        bra updateSegmentRightSlow_doUp
        
updateSegmentRightSlow_doDown anop
        lda segmentScreenOffsets,y
        clc
        adc #SCREEN_BYTES_PER_ROW
        sta segmentScreenOffsets,y
        
        lda segmentTileOffsetsUR,y
        tax
        lda tileBelow,x
        sta segmentTileOffsetsLR,y
		sta segmentCurrentTile,y
        
        lda segmentTileOffsetsUL,y
        tax
        lda tileBelow,x
        sta segmentTileOffsetsLL,y
        rts
        

addBodySegment entry
        lda numSegments
        asl a
        tax
        
        inc numSegments
		
        lda #SEGMENT_STATE_BODY
        sta segmentStates,x
        
        lda segmentSpeed-2,x
        sta segmentSpeed,x
        
        beq addBodySegment_fast
        jmp addBodySegment_slow

addBodySegment_fast anop
        lda segmentPosOffset-2,x
        clc
        adc #8
        sta segmentPosOffset,x
        tay

        lda segmentHorizontalDir-8,y
        sta segmentHorizontalDir-6,y
        sta segmentHorizontalDir-4,y
        sta segmentHorizontalDir-2,y
        sta segmentHorizontalDir,y
        
        lda segmentVerticalDir-8,y
        sta segmentVerticalDir-6,y
        sta segmentVerticalDir-4,y
        sta segmentVerticalDir-2,y
        sta segmentVerticalDir,y
        
        lda segmentFacing-8,y
        sta segmentFacing-6,y
        sta segmentFacing-4,y
        sta segmentFacing-2,y
        sta segmentFacing,y

        lda tileScreenOffset
        sec
        sbc #SCREEN_BYTES_PER_ROW*8
        sta segmentScreenOffsets-6,y
        sta segmentScreenOffsets-4,y
        sta segmentScreenOffsets-2,y
        sta segmentScreenOffsets,y

        lda segmentTileOffsetsUL-8,y
        sta segmentTileOffsetsUL-6,y
        sta segmentTileOffsetsUL-4,y
        sta segmentTileOffsetsUL-2,y
        sta segmentTileOffsetsUL,y
        
        lda segmentTileOffsetsUR-8,y
        sta segmentTileOffsetsUR-6,y
        sta segmentTileOffsetsUR-4,y
        sta segmentTileOffsetsUR-2,y
        sta segmentTileOffsetsUR,y
        sta segmentTileOffsetsUR,y
        
        lda segmentTileOffsetsLL-8,y
        sta segmentTileOffsetsLL-6,y
        sta segmentTileOffsetsLL-4,y
        sta segmentTileOffsetsLL-2,y
        sta segmentTileOffsetsLL,y
        
        lda segmentTileOffsetsLR-8,y
        sta segmentTileOffsetsLR-6,y
        sta segmentTileOffsetsLR-4,y
        sta segmentTileOffsetsLR-2,y
        sta segmentTileOffsetsLR,y

; This is a bit weird but we pretend the body segment is in the bottom RHS tile while it is offscreen.
; We have to put in a valid game tile here and it doesn't matter if body segments "collide".  But we
; don't want head segments to collide with a off-screen body segment.  Since the head segments always
; appear at the top, it is safe to just tuck the body segments away at the top RHS.
		lda #RHS_FIRST_TILE_OFFSET
		sta segmentCurrentTile-6,y
		sta segmentCurrentTile-4,y
		sta segmentCurrentTile-2,y
		sta segmentCurrentTile,y

        rtl
addBodySegment_slow anop
        lda segmentPosOffset-2,x
        clc
        adc #16
        sta segmentPosOffset,x
        tay

        lda segmentHorizontalDir-16,y
        sta segmentHorizontalDir-14,y
        sta segmentHorizontalDir-12,y
        sta segmentHorizontalDir-10,y
        sta segmentHorizontalDir-8,y
        sta segmentHorizontalDir-6,y
        sta segmentHorizontalDir-4,y
        sta segmentHorizontalDir-2,y
        sta segmentHorizontalDir,y
        
        lda segmentVerticalDir-16,y
        sta segmentVerticalDir-14,y
        sta segmentVerticalDir-12,y
        sta segmentVerticalDir-10,y
        sta segmentVerticalDir-8,y
        sta segmentVerticalDir-6,y
        sta segmentVerticalDir-4,y
        sta segmentVerticalDir-2,y
        sta segmentVerticalDir,y
        
        lda segmentFacing-16,y
        sta segmentFacing-14,y
        sta segmentFacing-12,y
        sta segmentFacing-10,y
        sta segmentFacing-8,y
        sta segmentFacing-6,y
        sta segmentFacing-4,y
        sta segmentFacing-2,y
        sta segmentFacing,y

        lda tileScreenOffset
        sec
        sbc #SCREEN_BYTES_PER_ROW*8
        sta segmentScreenOffsets-14,y
        sta segmentScreenOffsets-12,y
        sta segmentScreenOffsets-10,y
        sta segmentScreenOffsets-8,y
        sta segmentScreenOffsets-6,y
        sta segmentScreenOffsets-4,y
        sta segmentScreenOffsets-2,y
        sta segmentScreenOffsets,y

        lda segmentTileOffsetsUL-16,y
        sta segmentTileOffsetsUL-14,y
        sta segmentTileOffsetsUL-12,y
        sta segmentTileOffsetsUL-10,y
        sta segmentTileOffsetsUL-8,y
        sta segmentTileOffsetsUL-6,y
        sta segmentTileOffsetsUL-4,y
        sta segmentTileOffsetsUL-2,y
        sta segmentTileOffsetsUL,y
        
        lda segmentTileOffsetsUR-16,y
        sta segmentTileOffsetsUR-14,y
        sta segmentTileOffsetsUR-12,y
        sta segmentTileOffsetsUR-10,y
        sta segmentTileOffsetsUR-8,y
        sta segmentTileOffsetsUR-6,y
        sta segmentTileOffsetsUR-4,y
        sta segmentTileOffsetsUR-2,y
        sta segmentTileOffsetsUR,y
        sta segmentTileOffsetsUR,y
        
        lda segmentTileOffsetsLL-16,y
        sta segmentTileOffsetsLL-14,y
        sta segmentTileOffsetsLL-12,y
        sta segmentTileOffsetsLL-10,y
        sta segmentTileOffsetsLL-8,y
        sta segmentTileOffsetsLL-6,y
        sta segmentTileOffsetsLL-4,y
        sta segmentTileOffsetsLL-2,y
        sta segmentTileOffsetsLL,y
        
        lda segmentTileOffsetsLR-16,y
        sta segmentTileOffsetsLR-14,y
        sta segmentTileOffsetsLR-12,y
        sta segmentTileOffsetsLR-10,y
        sta segmentTileOffsetsLR-8,y
        sta segmentTileOffsetsLR-6,y
        sta segmentTileOffsetsLR-4,y
        sta segmentTileOffsetsLR-2,y
        sta segmentTileOffsetsLR,y
        sta segmentTileOffsetsLR,y

; See the comment above why we pretend the body segment is at the top RHS tile.
		lda #RHS_FIRST_TILE_OFFSET
		sta segmentCurrentTile-14,y
		sta segmentCurrentTile-12,y
		sta segmentCurrentTile-10,y
		sta segmentCurrentTile-8,y
		sta segmentCurrentTile-6,y
		sta segmentCurrentTile-4,y
		sta segmentCurrentTile-2,y
		sta segmentCurrentTile,y

        rtl


; This method is called with X register pointing to a structure which has the following info in it:
;		- segment speed (2 bytes)
;		- segment direction (2 bytes)
; 		- tile offset (2 bytes)
; 		- number of body segments (2 bytes)
;
; It reads this info and sets up the appropriate centipede segments.  Also, it is expected to
; preserve the X register.
addCentipede entry
		lda numSegments
		asl a
		tay
	  
		lda #SEGMENT_STATE_HEAD
		sta segmentStates,y

		lda |$0,x
	 	sta segmentSpeed,y

		tya
		asl a
		asl a
		asl a
		sta segmentPosOffset,y
		tay

		lda #SEGMENT_DIR_DOWN
		sta segmentVerticalDir,y

		lda |$2,x
		sta segmentHorizontalDir,y

		phx
		lda |$4,x
		tax
		lda tileScreenOffset,x
; We will correct this later to adjust for speed and direction.
		sta segmentScreenOffsets,y

		lda segmentHorizontalDir,y
		beq addCentipede_left anop

		txa
		sta segmentTileOffsetsUL,y
		sta segmentTileOffsetsLL,y
		sta segmentCurrentTile,y
		lda tileRight,x
		sta segmentTileOffsetsUR,y
		sta segmentTileOffsetsLR,y
		
		plx
		lda |$0,x
		beq addCentipede_rightFast anop

		lda segmentScreenOffsets,y
		sec
		sbc #SCREEN_BYTES_PER_ROW*7+2
		bra addCentipede_rightFacing
		
addCentipede_rightFast anop
		lda segmentScreenOffsets,y
		sec
		sbc #SCREEN_BYTES_PER_ROW*8+2

addCentipede_rightFacing anop
		sta segmentScreenOffsets,y
		lda #SEGMENT_FACING_DOWN_LEFT
		bra addCentipede_facing

addCentipede_left anop
		txa
		sta segmentTileOffsetsUR,y
		sta segmentTileOffsetsLR,y
		sta segmentCurrentTile,y
		lda tileLeft,x
		sta segmentTileOffsetsUL,y
		sta segmentTileOffsetsLL,y
		
		plx

		lda |$0,x
		beq addCentipede_leftFast anop

		lda segmentScreenOffsets,y
		sec
		sbc #SCREEN_BYTES_PER_ROW*7+4
		bra addCentipede_leftFacing

addCentipede_leftFast anop
		lda segmentScreenOffsets,y
		sec
		sbc #SCREEN_BYTES_PER_ROW*8+4

addCentipede_leftFacing anop
		sta segmentScreenOffsets,y
		lda #SEGMENT_FACING_DOWN_RIGHT

addCentipede_facing anop
		sta segmentFacing,y

		inc numSegments
		lda #5
		sta segmentPixelOffset
		lda #1
		sta segmentsAddEnabled

		ldy |$6,x
		beq addCentipede_done
		phx
addCentipede_body anop
		phy
		jsl addBodySegment
		ply
		dey
		bne addCentipede_body
		plx
addCentipede_done anop
		rtl
    

; The accummulator has the tile offset where the collision happened
; The y register has the address where the collision happened
;
; The result is a clear carry if there is no collision found or
; a set carry and the segment num * 2 in the X register
isSegmentCollision entry
		ldy numSegments
		beq isSegmentCollision_returnFalse
		
		ldx #SEGMENT_MAX_OFFSET
isSegmentCollision_loop anop
		ldy segmentStates,x
		beq isSegmentCollision_nextSegment
		cpy #SEGMENT_STATE_EXPLODING
		beq isSegmentCollision_nextSegment
		ldy segmentPosOffset,x
		cmp segmentTileOffsetsLL,y
		beq isSegmentCollision_returnTrue
		cmp segmentTileOffsetsLR,y
		beq isSegmentCollision_returnTrue
		cmp segmentTileOffsetsUL,y
		beq isSegmentCollision_returnTrue
		cmp segmentTileOffsetsUR,y
		beq isSegmentCollision_returnTrue
isSegmentCollision_nextSegment anop
		dex
		dex
		bpl isSegmentCollision_loop
	
isSegmentCollision_returnFalse anop
		clc
		rtl
		
isSegmentCollision_returnTrue anop
		sec
		rtl

		
; Call this with the segment num * 2 in the X register
shootSegment entry
		phx
		ldy segmentPosOffset,x
		ldx segmentCurrentTile,y
		jsl playKillSound
		plx
		phx
		lda segmentStates,x
		cmp #SEGMENT_STATE_BODY
		beq shootSegment_body
		jsl scoreAddOneHundred
		bra shootSegment_doneScore
shootSegment_body anop
		jsl scoreAddTen
shootSegment_doneScore anop
		plx
		jsl explodeSegment
		ldy segmentPosOffset,x
		
		lda segmentCurrentTile,y
		cmp #(NUM_GAME_TILES-GAME_NUM_TILES_WIDE)*SIZEOF_TILE_INFO
		bge shootSegment_done
		tay
		lda tileType,y
		beq shootSegment_noMushroom
		cmp #TILE_POISON_MUSHROOM1
		blt shootSegment_normalMushroom
		lda #TILE_POISON_MUSHROOM4
		bra shootSegment_dirtyTile
shootSegment_noMushroom anop
		cpy #SPIDER_STARTING_TOP_ROW_OFFSET
		blt shootSegment_normalMushroom
		ldx playerNum
		inc numInfieldMushrooms,x
shootSegment_normalMushroom anop
		lda #TILE_MUSHROOM4
shootSegment_dirtyTile anop
		sta tileType,y
		lda #TILE_STATE_DIRTY
		sta tileDirty,y
shootSegment_done anop
        rtl
	
	
; Call this with the segment num * 2 in the X register
explodeSegment entry
		dec numSegments
		lda #SEGMENT_STATE_EXPLODING
		sta segmentStates,x
		ldy segmentPosOffset,x
		
; We take over the segmentFacing value when exploding to be an explosion sprite offset
		lda #EXPLOSION_LAST_OFFSET
		sta segmentFacing,y
		
; If this is the last segment, then do not look for a following body segment
		cpx #22
		bge shootSegment_done
; If the segment after this is a body segment, then it is now a head segment
		lda segmentStates+2,x
		cmp #SEGMENT_STATE_BODY
		bne explodeSegment_done
		lda #SEGMENT_STATE_HEAD
		sta segmentStates+2,x
explodeSegment_done anop
		rtl


segmentsAddEnabled	dc i2'1'

; The method used to track a segments position and other details on the screen are a bit
; funky.  In order to have body segments follow a head segment, we keep information from
; the position of the head segment.  The segmentPosOffset gives an offset into the other
; larger arrays (96 words) which describes the position of the segment.  When a head is
; updated, the segmentPosOffset is decremented.  That way, the previous positions are
; still there and body segments after that can reference it.
;
; We need at least 96 of them because a slow moving head segment goes through 8 positions
; before it can be re-used by the next body segment.  If there are 12 segments max, we
; need (8*12) positions to ensure all segments can know where there position was and will
; be next.
segmentStates           dc 12i2'SEGMENT_STATE_NONE'
segmentSpeed            dc 12i2'SEGMENT_SPEED_SLOW'
segmentPosOffset        dc 12i2'0'
segmentHorizontalDir    dc 96i2'SEGMENT_DIR_RIGHT'
segmentVerticalDir      dc 96i2'SEGMENT_DIR_DOWN'
segmentFacing           dc 96i2'SEGMENT_FACING_DOWN'
segmentScreenOffsets    dc 96i2'0'
segmentTileOffsetsUL    dc 96i2'0'
segmentTileOffsetsUR    dc 96i2'0'
segmentTileOffsetsLL    dc 96i2'0'
segmentTileOffsetsLR    dc 96i2'0'
segmentCurrentTile      dc 96i2'0'

segmentTileMask         dc 40i2'0'

SEGMENT_SPRITE_LAST_OFFSET  gequ 7*4
segmentSpriteOffset dc i2'0'
segmentPixelOffset  dc i2'0'
segmentBeingUpdated dc i2'0'
segmentEmptyOffset 	dc i2'0'


headJumpTable anop
; leftHeadJumpTable
                    dc i4'leftHead5'
                    dc i4'leftHead4'
                    dc i4'leftHead1'
                    dc i4'leftHead2'
                    dc i4'leftHead3'
                    dc i4'leftHead2'
                    dc i4'leftHead1'
                    dc i4'leftHead4'

; leftDownHeadJumpTable
                    dc i4'leftDownHead1'
                    dc i4'leftDownHead2'
                    dc i4'leftDownHead1'
                    dc i4'leftDownHead2'
                    dc i4'leftDownHead1'
                    dc i4'leftDownHead2'
                    dc i4'leftDownHead1'
                    dc i4'leftDownHead2'


; downHeadJumpTable
                    dc i4'downHead3'
                    dc i4'downHead3'
                    dc i4'downHead1'
                    dc i4'downHead1'
                    dc i4'downHead2'
                    dc i4'downHead2'
                    dc i4'downHead1'
                    dc i4'downHead1'


; rightDownHeadJumpTable
                    dc i4'rightDownHead1s'
                    dc i4'rightDownHead2s'
                    dc i4'rightDownHead1s'
                    dc i4'rightDownHead2s'
                    dc i4'rightDownHead1s'
                    dc i4'rightDownHead2s'
                    dc i4'rightDownHead1s'
                    dc i4'rightDownHead2s'


; rightHeadJumpTable
                    dc i4'rightHead5'
                    dc i4'rightHead4'
                    dc i4'rightHead1'
                    dc i4'rightHead2'
                    dc i4'rightHead3'
                    dc i4'rightHead2'
                    dc i4'rightHead1'
                    dc i4'rightHead4'
                    
                    
headShiftJumpTable  anop
; leftHeadShiftJumpTable
                    dc i4'leftHead5s'
                    dc i4'leftHead4s'
                    dc i4'leftHead1s'
                    dc i4'leftHead2s'
                    dc i4'leftHead3s'
                    dc i4'leftHead2s'
                    dc i4'leftHead1s'
                    dc i4'leftHead4s'


; leftDownShiftHeadJumpTable
                    dc i4'leftDownHead1s'
                    dc i4'leftDownHead2s'       ; Problem, spills into next tile...
                    dc i4'leftDownHead1s'
                    dc i4'leftDownHead2s'       ; Problem, spills into next tile...
                    dc i4'leftDownHead1s'
                    dc i4'leftDownHead2s'       ; Problem, spills into next tile...
                    dc i4'leftDownHead1s'
                    dc i4'leftDownHead2s'       ; Problem, spills into next tile...


; downHeadJumpTable
                    dc i4'downHead3'
                    dc i4'downHead3'
                    dc i4'downHead1'
                    dc i4'downHead1'
                    dc i4'downHead2'
                    dc i4'downHead2'
                    dc i4'downHead1'
                    dc i4'downHead1'


; rightDownShiftHeadJumpTable
                    dc i4'rightDownHead1'
                    dc i4'rightDownHead2'
                    dc i4'rightDownHead1'
                    dc i4'rightDownHead2'
                    dc i4'rightDownHead1'
                    dc i4'rightDownHead2'
                    dc i4'rightDownHead1'
                    dc i4'rightDownHead2'


; rightHeadShiftJumpTable
                    dc i4'rightHead5s'
                    dc i4'rightHead4s'
                    dc i4'rightHead1s'
                    dc i4'rightHead2s'
                    dc i4'rightHead3s'
                    dc i4'rightHead2s'
                    dc i4'rightHead1s'
                    dc i4'rightHead4s'


bodyJumpTable   anop
; leftBodyJumpTable
                    dc i4'leftBody5'
                    dc i4'leftBody4'
                    dc i4'leftBody1'
                    dc i4'leftBody2'
                    dc i4'leftBody3'
                    dc i4'leftBody2'
                    dc i4'leftBody1'
                    dc i4'leftBody4'


; leftDownBodyJumpTable
                    dc i4'leftDownBody1'
                    dc i4'leftDownBody2'
                    dc i4'leftDownBody1'
                    dc i4'leftDownBody2'
                    dc i4'leftDownBody1'
                    dc i4'leftDownBody2'
                    dc i4'leftDownBody1'
                    dc i4'leftDownBody2'


; downBodyJumpTable
                    dc i4'downBody3'
                    dc i4'downBody3'
                    dc i4'downBody1'
                    dc i4'downBody1'
                    dc i4'downBody2'
                    dc i4'downBody2'
                    dc i4'downBody1'
                    dc i4'downBody1'


; rightDownBodyJumpTable
                    dc i4'rightDownBody1s'
                    dc i4'rightDownBody2s'
                    dc i4'rightDownBody1s'
                    dc i4'rightDownBody2s'
                    dc i4'rightDownBody1s'
                    dc i4'rightDownBody2s'
                    dc i4'rightDownBody1s'
                    dc i4'rightDownBody2s'


; rightBodyJumpTable
                    dc i4'rightBody5'
                    dc i4'rightBody4'
                    dc i4'rightBody1'
                    dc i4'rightBody2'
                    dc i4'rightBody3'
                    dc i4'rightBody2'
                    dc i4'rightBody1'
                    dc i4'rightBody4'
                    
                    
bodyShiftJumpTable anop
; leftBodyShiftJumpTable
                    dc i4'leftBody5s'
                    dc i4'leftBody4s'
                    dc i4'leftBody1s'
                    dc i4'leftBody2s'
                    dc i4'leftBody3s'
                    dc i4'leftBody2s'
                    dc i4'leftBody1s'
                    dc i4'leftBody4s'


; leftDownShiftBodyJumpTable
                    dc i4'leftDownBody1s'
                    dc i4'leftDownBody2s'       ; Problem, spills into next tile...
                    dc i4'leftDownBody1s'
                    dc i4'leftDownBody2s'       ; Problem, spills into next tile...
                    dc i4'leftDownBody1s'
                    dc i4'leftDownBody2s'       ; Problem, spills into next tile...
                    dc i4'leftDownBody1s'
                    dc i4'leftDownBody2s'       ; Problem, spills into next tile...
        
        
; downBodyJumpTable
                    dc i4'downBody3'
                    dc i4'downBody3'
                    dc i4'downBody1'
                    dc i4'downBody1'
                    dc i4'downBody2'
                    dc i4'downBody2'
                    dc i4'downBody1'
                    dc i4'downBody1'


; rightDownShiftBodyJumpTable
                    dc i4'rightDownBody1'
                    dc i4'rightDownBody2'
                    dc i4'rightDownBody1'
                    dc i4'rightDownBody2'
                    dc i4'rightDownBody1'
                    dc i4'rightDownBody2'
                    dc i4'rightDownBody1'
                    dc i4'rightDownBody2'


; rightBodyShiftJumpTable
                    dc i4'rightBody5s'
                    dc i4'rightBody4s'
                    dc i4'rightBody1s'
                    dc i4'rightBody2s'
                    dc i4'rightBody3s'
                    dc i4'rightBody2s'
                    dc i4'rightBody1s'
                    dc i4'rightBody4s'

segmentUpdateJumpTable  dc i2'updateSegmentLeftFast'
                        dc i2'updateSegmentLeftSlow'
                        dc i2'updateSegmentDownLeftFast'
                        dc i2'updateSegmentDownLeftSlow'
                        dc i2'updateSegmentDownFast'
                        dc i2'updateSegmentDownSlow'
                        dc i2'updateSegmentDownRightFast'
                        dc i2'updateSegmentDownRightSlow'
                        dc i2'updateSegmentRightFast'
                        dc i2'updateSegmentRightSlow'
                        
        end
