Game Duenix for Amiga computers/Duenix strategy.s

From Wikiversity
Jump to navigation Jump to search
;__________________________________________________________________________________________________
;  Duenix Computer Strategy
;__________________________________________________________________________________________________
;
; This is Motorola 68000 assembly.
;
; The comments are in part original from years 1994-1998 and in part updated to make them more legible
; and comprehensive. Comments were added to make the code and logic easier to follow.
;
; The strategy is split between two turns/passes (probably for performance reasons); the choice to
; turn left, turn right or move straight on is made only at the end of the 2nd turn/pass. That is to say,
; the choice is made on every 2nd call from the AMOS code to the strategy.
;
; The core of the strategy is this: identify a set of directions to investigate: straight on, left 1, right 1
; left 2, right 2, etc. For each direction, determine how far one can go without hitting an obstacle,
; where an obstacle is the rectangle bounding the playground or a line drawn by another player.
; Then, turn left or right depending on whether the maximum length direction found is at the left
; or the right; or go straight on. The strategy starts at label DoStrategy.
;
; Additional logic covers the case where there are obstacles ahead rather close
; to the player, in which case letting large free space e.g. in left 4 or right 4 direction determine
; the decision would be a mistake.  The case is given by the condition
; Len0 * 2 <= 6 and Len0 * 2 < LenL1 and Len0 * 2 < LenR1.
; To achieve its objective, it sets blocks of LenL* and LenR* to zero in dependence on certain
; conditions, thereby disabling the impact of these LenL* values on the final decision.
; This logic starts at label DS_ArrowCollisionLogic.
;
; Additional logic reckons with where other players are and it is to be reverse
; engineered and documented. It seems to be replacing the direction lengths.
; This logic starts at label OtherPlayersCare. It is called from turn/pass 1.
;
; A small number of comments seems questionable or incorrect. Finding out what the proper correction
; would be was decided (in 2024) to be not worth the effort. One can learn something from
; the code and comments as they are. A search for "issue" should find places marked as potentially
; problematic.
;
; At the end of this file, there is a quick 68000 asm guide.
;__________________________________________________________________________________________________
; Constants
LengthLimit = 100
ASizeLimit  = 200            ;For handling of other players, the limit of the vector length ("a") above which
                             ;the logic is not applied.

BitPlan4    dc.l 0           ;The address of bitplan 4, where bitplans are numbered from 0; obstacles have a bit there
SinTable    dc.l 0           ;The table of precalculated sine values of directions
AtanTable   dc.l 0           ;The table of precalculated atan 

Directions  dc.l 80          ;The direction count (into how many direction angles the 360 degrees are to be divided)
DirStep0    dc.l 10          ;The step size of a small bend, expressed as an int in the units given by the above direction count
DirStep1    dc.l 20
DirStep2    dc.l 30
DirStep3    dc.l 40     
DirStep4    dc.l 50          ;The skips for different directions

Rotation    dc.l 15          ;The rotation radius
Direction   dc.l 60          ;The direction of motion, expressed as an integer in the units given by direction count
XAMOSReal   dc.l $80000045   ;X coordinate in AMOS real
YAMOSReal   dc.l $80000045   ;Y coordinate in AMOS real
Player      dc.l 0
XHash       dc.l $80000048   ;The address of an array of player x coordinate in AMOS real
YHash       dc.l $80000048   ;The address of an array of player y coordinate in AMOS real
PlayersDir  dc.l 46          ;The address of an array of directions of the players
PlayerOn    dc.l 0           ;The address of the indicator whether the player is playing, of an array
PlayersChoice dc.b 0,0,0,0   ;The choice made by the players in the previous turn/pass
            dc.b 0,0
Turn        dc.b 0           ;Which turn/pass is occurring; for a player this is global
IsBigScreen dc.b 0           ;1=We have big screen
        

        Bra DoStrategyDebug
;       Bra DoStrategy
        Bra CalculateSinAndCos
        Bra CalcTestAndSpeedPoints_1Pixel
        Bra CalcTestAndSpeedPoints_2Pixel
        Bra CalculateSinAndCosFixed

;_____________________________
;
;  Program Variables
;_____________________________

XFixed  dc.l 0      ;Fixed X coord
YFixed  dc.l 0      ;Fixed Y coord
XInt    dc.l $80
YInt    dc.l $80

;    ________________________________________________________________________________________________
;   |
;   | Convert AMOSReal to Fixed 
;   | 
;   | in:  d0 - AMOSReal input, assuming it is positive; negative numbers are malprocessed.
;   | out: d0 - Fixed-point output: 16.16 signed, but during the conversion, we do not reckon with
;   |           numbers not fitting into it.
;   | changed regs: d0, d1
;   |
;   | The logic:
;   | sub.l #$40,d1 .. AMOS convention .. we get the position of the decimal point.
;   | Now we want to shift the number by ($10-dec point position) to the right.
;   | Hence  Sub.l #$10,d1
;   |        Neg.l d1
;   |________________________________________________________________________________________________

ConvertAMOSRealToFixed
    Move.l  d0,d1
    And.l   #$FF,d1
    Sub.l   #$50,d1
    Neg.l   d1               ; d1 := $50 - (d0 & $FF)
    And.l   #$FFFFFF00,d0    ; in d0, erase the information about the decimal point
    Cmp.b   #$20,d1
    Ble     CARTF_1          ; if d1 > 32: return 0
    Moveq   #$0,d0
    Rts
CARTF_1
    Lsr.l   d1,d0            ; else: shift to the right to handle the decimal point
    Rts

;    ______________________________________________________________________________________________
;   |
;   | Test Length of Direction
;   |
;   | Determines the length to which the worm can go into the direction before it hits an obstacle,
;   | e.g. a worm line. The obstacle is determined as a filled bit in bitplan 4. (In a bitplan, there
;   | is a bit per pixel, so a byte covers 8 pixels. This means the worm line colors are in the range
;   | of 16-31 of the color palette.) Depends on the sin values calculated from the AMOS program and
;   | stored in SinTable; these values are in the AMOS real format and need to be converted
;   | to fixed-point representation (the int part in the upper word, the after dec.point part
;   | in the lower word).
;   |
;   | in:        d0     = Direction (integer in units given by the number of directions)
;   | out:       d7     = Length
;   | changed registers: d-all a0, a6
;   |______________________________________________________________________________________________

TestLengthOfDirection
    
    ;____________________________
    ; Calculate Sin and Cos
    ;____________________________

    Move.l Directions(pc),d6
    Lsr.w  #2,d6             ;d6 := direction count / 4 (DirCntQuarter)
    Move.l d0,d7             ;d7 := direction
    Divu.w d6,d7             ;d7 := direction / (direction count / 4)
                             ;remainder is saved in the upper word or d7; d7.w has quadrant
    Move.l d7,d1
    Swap   d1
    Ext.l  d1                ;d1 := remainder of direction / (direction count / 4)

    Btst   #0,d7
    Beq TL_FinalDir
    Sub.l  d6,d1
    Neg.l  d1                ;dir = -(dir - direction count / 4)
TL_FinalDir

    ;Now d1 == DifDirection
    Move.l d1,d5             ;Store DifDirection In (d5 is free yet)
    Asl.l  #2,d1             ;d1 := d1 * 4
    Move.l SinTable(pc),a0
    Move.l (a0,d1),d0
    Bsr    ConvertAMOSRealToFixed

    ;If required, multiply by -1
    Btst   #1,d7
    Beq TL_SinCalculated     ;If the quadrant is 2 or 3 => minus
    Neg.l  d0
TL_SinCalculated
    Move.l d0,d3             ;d3 := WorkSin just calculated

    Move.l d6,d2             ;d2 := DirCntQuarter
    Sub.w  d5,d2             ;d2 := DirCntQuarter-DifDirection
    Asl.l  #2,d2
    Move.l SinTable(pc),a0
    Move.l (a0,d2),d0
    Bsr    ConvertAMOSRealToFixed

    Move.b d7,d2             ;d2 := quadrant
    Add.b  #1,d2
    Btst   #1,d2
    Beq TL_CosCalculated
    Neg.l  d0
TL_CosCalculated
    Move.l d0,d4             ;d4 := WorkCos just calculated
    
    ;  Set start position
    Move.l XFixed(pc),d5
    Move.l YFixed(pc),d6

    ;____________________________
    ; Go straight
    ;____________________________

    ;a6: Bitplan4
    ;d3: worksin
    ;d4: workcos
    ;d5: XFixed
    ;d6: YFixed
    ;d7: length
        
    ;____________________________
    ;  First two times forth
    ;____________________________

    Move.l BitPlan4(pc),a6
    Cmp.w  #0,d7             ;Is quadrant 0?
    Beq  TL_QUAD_ZERO
    Moveq  #0,d7
    Bra    TL_TwoTimes
TL_QUAD_ZERO
    Moveq  #1,d7
TL_TwoTimes
    Add.l  d3,d5             ;XFixed := XFixed + worksin
    Add.l  d4,d6             ;YFixed := YFixed + workcos
    DBra   d7,TL_TwoTimes    ;Decrement and branch unless the result is -1

    Moveq  #0,d7             ;Clear length

    Move.b IsBigScreen(pc),d0
    Bne TL_GoStraight_BS

TL_GoStraight
    
    Addq   #1,d7             ;line length := line length + 1

    Add.l  d3,d5             ;XFixed := XFixed + worksin
    Move.l d5,d0             ;d0 := XFixed
    Swap   d0;               ;d0 := X (the int part of XFixed, with the upper 16 bit dirty)
    ; No need to extend since in the following, we work with word.

    Add.l  d4,d6             ;YFixed := YFixed + workcos
    Move.l d6,d1             ;d1 := YFixed
    Swap   d1
    And.l  #$FFFF,d1         ;d1 := Y (the int part of YFixed)

    ;d1 := Y * 40 (40 = screen x size / 8)
    Asl.w   #3,d1
    Move.w  d1,d2
    Asl.w   #2,d2
    Add.w   d2,d1

    ;d1 := d1 + X / 8
    Move.w  d0,d2
    Lsr.w   #3,d2
    Add.w   d2,d1

    ;d1 == the offset of the address from the beginning of BitPlan4

    ;d0 := (X bitand 7) xor 7 == the bit number in the byte
    And.b   #7,d0
    Eor.b   #7,d0

    Btst.b  d0,(a6,d1)       ;test d0 bit from BitPlan4
    Bne  TL_Finished         ;as long as zero, go on
    Cmp.b  #LengthLimit,d7
    Bls  TL_GoStraight 
TL_Finished
    Rts

    ;  Go straight (for big screen)
TL_GoStraight_BS

    Addq   #1,d7             ;line length := line length + 1

    Add.l  d3,d5             ;XFixed := Xfixed + worksin
    Move.l d5,d0             ;d0:=XFixed 
    Swap   d0
    ; No need to extend since in the following, we work with word.

    Add.l  d4,d6             ;YFixed := Yfixed + workcons
    Move.l d6,d1             ;d1:=YFixed
    Swap   d1
    And.l  #$FFFF,d1

    ;d1 := d1 * 80
    Asl.w   #4,d1
    Move.w  d1,d2
    Asl.w   #2,d2
    Add.w   d2,d1

    ;d1 := d1 + X / 8
    Move.w  d0,d2
    Lsr.w   #3,d2
    Add.w   d2,d1

    ;d1 == the offset of the address from the beginning of bitplan 4
    
    ;d0 := (X bitand 7) xor 7 == the bit number in the byte
    And.b   #7,d0            ;V D0 je pocet bitu
    Eor.b   #7,d0

    Btst.b  d0,(a6,d1.l)    ;test d0 bit from bitplan 4
    Bne  TL_Finished_BS     ;as long as zero, go on
    Cmp.b  #LengthLimit,d7   
    Bls  TL_GoStraight_BS 
TL_Finished_BS       
    Rts


; _____________________________________________________________________________
;| DoStrategy
;|
;| d5: Tested Direction.Variable
;|
;| Inputs: XAMOSReal(pc), YAMOSReal(pc), Directions(pc), Direction(pc),
;|         Rotation(pc)
;|
;| output d7: 0: no action, 1: turn left, 2: turn right
;|_____________________________________________________________________________

DS_Len0 dc.l 18      ;length in the straight direction
DS_LenL1 dc.l 17     ;length in the left 1 direction
DS_LenR1 dc.l 23     ;length in the right 1 direction
DS_LenL2 dc.l 101    ;length in the left 2 direction
DS_LenR2 dc.l 200    ;length in the right 2 direction
DS_LenL3 dc.l 14     ;length in the left 3 direction
DS_LenR3 dc.l 13     ;length in the right 3 direction
DS_LenL4 dc.l 101    ;length in the left 4 direction
DS_LenR4 dc.l 200    ;length in the right 4 direction
DS_LenL5 dc.l 3      ;length in the left 5 direction
DS_LenR5 dc.l 8      ;length in the right 5 direction

DS_PrecalculatedLens
    ds.l 6*8         ;For all players, obstacle-free lengths calculated in turn/pass 0, to be used in turn/pass 1.
                     ;There is a room for 8 lengths per player, but only 5 lengths are stored.

DS_ArrowDecisionStore
        dc.l 0,0     ;6 bytes of one of 0, 1, and 2, one per player

; a2: DirStore
; a3: Directions
; a4: Pointer to DS_Len Table 
; a5: Dirs 1/6

DoStrategy

    ;-------------------------------------------------
    ; Convert XAMOSReal to XFixed and XInt
    ;-------------------------------------------------
    Move.l XAMOSReal(pc),d0
    Bsr    ConvertAMOSRealToFixed
    Lea.l  XFixed(pc),a0
    Move.l d0,(a0)
    Swap   d0
    Ext.l  d0
    Lea.l  XInt(pc),a0
    Move.l d0,(a0)

    ;--------------------------------------------------
    ; Convert YAMOSReal to YFixed and YInt
    ;--------------------------------------------------
    Move.l YAMOSReal(pc),d0
    Bsr    ConvertAMOSRealToFixed
    Lea.l  YFixed(pc),a0
    Move.l d0,(a0)
    Swap   d0
    Ext.l  d0
    Lea.l  YInt(pc),a0
    Move.l d0,(a0)

    ;------------------------
    ; Pick turn
    ;------------------------    
    Btst.b #0,Turn(pc)
    Bne    DS_Turn1

    ;--------------------
    ; TURN ZERO
    ;--------------------

DS_Turn0

    ;--------------------
    ; Fill variables
    ;--------------------
    Lea.l  DS_Len0(pc),a4
    Move.l Directions(pc),a3

    ; ----------------------------------------------
    ; Calculate the length of the straight direction
    ; ----------------------------------------------
    Move.l Direction(pc),d0
    Bsr    TestLengthOfDirection
    Move.l d7,(a4)+

    ; ----------------------------------------------
    ; Calculate the length of the left 1 direction
    ; ----------------------------------------------
    Move.l DirStep0(pc),a5
    Move.l Direction(pc),d0 
    Add.l  a5,d0
    Cmp    d0,a3
    Bhi    DS_OK0           ;d0 < a3
    Sub.l  a3,d0            
DS_OK0
    Bsr    TestLengthOfDirection
    Move.l d7,(a4)+     

    ; ----------------------------------------------
    ; Calculate the length of the right 1 direction
    ; ----------------------------------------------
    Move.l Direction(pc),d0
    Cmp    d0,a5
    Bls    DS_OK1       ;d0 >= a5
    Add.l  a3,d0    
DS_OK1  Sub.l  a5,d0    
    Bsr    TestLengthOfDirection
    Move.l d7,(a4)+

    ; ----------------------------------------------
    ; Calculate the length of the left 2 direction
    ; ----------------------------------------------
    Move.l DirStep1(pc),a5
    Move.l Direction(pc),d0 
    Add.l  a5,d0
    Cmp    d0,a3
    Bhi    DS_OK2           ;d0 < a3
    Sub.l  a3,d0            
DS_OK2
    Bsr    TestLengthOfDirection
    Move.l d7,(a4)+     

    ; ----------------------------------------------
    ; Calculate the length of the right 2 direction
    ; ----------------------------------------------
    Move.l Direction(pc),d0
    Cmp    d0,a5
    Bls    DS_OK3       ;d0 >= a5
    Add.l  a3,d0    
DS_OK3  Sub.l  a5,d0
    Move.l d0,a2            ;Store
    Bsr    TestLengthOfDirection
    Move.l d7,(a4)+

    ; ---------------------
    ; OtherPlayersCare
    ; ---------------------
    Bsr OtherPlayersCare

    ;-----------------------------------------------
    ; Return the choice made in previous turn via d7
    ;-----------------------------------------------
    Move.l Player(pc),d0
    Lea.l  PlayersChoice(pc),a0
    Moveq  #0,d7
    Move.b (a0,d0),d7        ;d7 := PlayersChoice[Player]

    ;----------------------
    ; Store lengths
    ;----------------------
    ;DS_PrecalculatedLens[0, 1, ...] := DS_Len0, DS_LenL1, ...
    Asl.w  #5,d0             ;d0 := player no * 4 * 8
    Moveq  #5,d1             ;This could also be only 4.
    Lea.l  DS_PrecalculatedLens(pc),a0
    Add.l  d0,a0
    Lea.l  DS_Len0(pc),a1
DS_StoreLensLoop
    Move.l (a1)+,(a0)+  
    Dbra   d1,DS_StoreLensLoop

    ;----------------------
    ; Debugging
    ;----------------------
    Move.l Player(pc),d0
    Lea.l  DS_ArrowDecisionStore(pc),a0
    Move.b (a0,d0),d0       ;d0 := DS_ArrowDecisionStore[Player]
    Moveq  #1,d6
    Tst.b  d0
    Bne    DS_DebugSkip
    Moveq  #0,d6
DS_DebugSkip
    Rts

    ;----------------------
    ; TURN ONE
    ;----------------------

DS_Turn1
    ;----------------------
    ; Load lengths
    ;----------------------

    Move.l Player(pc),d0
    Asl.w  #5,d0        ;d0 := player*4*8

    Moveq  #5,d1        ; This could also be only 4.
    Lea.l  DS_PrecalculatedLens(pc),a0
    Add.l  d0,a0
    Lea.l  DS_Len0(pc),a1
DS_LoadLensLoop
    Move.l (a0)+,(a1)+  
    Dbra   d1,DS_LoadLensLoop

    ;--------------------
    ; Fill variables
    ;--------------------

    Lea.l  DS_LenL3(pc),a4
    Move.l Directions(pc),a3

    ; ----------------------------------------------
    ; Calculate the length of the left 3 direction
    ; ----------------------------------------------
    Move.l DirStep2(pc),a5
    Move.l Direction(pc),d0 
    Add.l  a5,d0
    Cmp    d0,a3
    Bhi    DS_1OK0          ;d0 < a3
    Sub.l  a3,d0            
DS_1OK0
    Bsr    TestLengthOfDirection
    Move.l d7,(a4)+

    ; ----------------------------------------------
    ; Calculate the length of right 3 direction
    ; ----------------------------------------------
    Move.l Direction(pc),d0
    Cmp    d0,a5
    Bls    DS_1OK1      ;d0 >= a5
    Add.l  a3,d0    
DS_1OK1 Sub.l  a5,d0
    
    Bsr    TestLengthOfDirection
    Move.l d7,(a4)+

    ; ----------------------------------------------
    ; Calculate the length of left 4 direction
    ; ----------------------------------------------
    Move.l DirStep3(pc),a5
    Move.l Direction(pc),d0 
    Add.l  a5,d0
    Cmp    d0,a3
    Bhi    DS_1OK2          ;d0 < a3
    Sub.l  a3,d0            
DS_1OK2
    Bsr    TestLengthOfDirection
    Move.l d7,(a4)+

    ; ----------------------------------------------
    ; Calculate the length of right 4 direction
    ; ----------------------------------------------
    Move.l Direction(pc),d0
    Cmp    d0,a5
    Bls    DS_1OK3      ;d0 >= a5
    Add.l  a3,d0    
DS_1OK3 Sub.l  a5,d0
    
    Bsr    TestLengthOfDirection
    Move.l d7,(a4)+

    ; ----------------------------------------------
    ; Calculate the length of left 5 direction
    ; ----------------------------------------------
    Move.l DirStep4(pc),a5
    Move.l Direction(pc),d0 
    Add.l  a5,d0
    Cmp    d0,a3
    Bhi    DS_1OK4          ;d0 < a3
    Sub.l  a3,d0            
DS_1OK4
    Bsr    TestLengthOfDirection
    Move.l d7,(a4)+

    ; ----------------------------------------------
    ; Calculate the length of right 5 direction
    ; ----------------------------------------------
    
    Move.l Direction(pc),d0
    Cmp    d0,a5
    Bls    DS_1OK5      ;d0 >= a5
    Add.l  a3,d0    
DS_1OK5 Sub.l  a5,d0
    Bsr    TestLengthOfDirection
    Move.l d7,(a4)+

    ;------------------------------------------------------
    ; Rotation radius and arrow collision special logic
    ;------------------------------------------------------
    ; An "arrow" collision is the following condition:
    ; Len0 * 2 <= 6 and Len0 * 2 < LenL1 and Len0 * 2 < LenR1
    ; In words, the straight ahead len is small, and
    ; LenL1 and LenR1 are greater than twice the straight ahead len.
    ; On a new arrow collision, make the decision only
    ; based on LenL1 and LenR1 and not the other
    ; LenL* and LenR*.
    ; There is a certain stickiness to the decision from a previous turn/pass,
    ; as per the above jump to DS_WasArrowCollision.
    ;------------------------------------------------------
    
DS_ArrowCollisionLogic
    Move.l  #0,d6           ;DEBUG

    Move.l Player(pc),d0
    Lea.l  DS_ArrowDecisionStore(pc),a0
    Move.b (a0,d0),d0        ;d0 := DS_ArrowDecisionStore[Player]
    Tst.b  d0
    Bne    DS_WasArrowCollision

DS_WasntArrowCollision

    ;Set fractions of the radius of the rotation into d1, d2, d3, d4 and d5
    Move.l Rotation(pc),d1   ;d1 := the radius of the rotation
    ;   Move.l d1,d2
    ;   Lsr.w  #3,d2            
    ;   Sub.w  d1,d2
    ;   Neg.w  d2            ;d2 := 7/8 * the radius
    Move.l d1,d2
    Lsr.w  #2,d2            
    Move.l d2,d5             ;d5 := 1/4 * the radius
    Sub.w  d1,d2
    Neg.w  d2                ;d2 := 3/4 * the radius
    Addq   #2,d2    
    Move.l d1,d3
    Lsr.w  #1,d3             ;d3 := 1/2 * the radius
    Addq   #4,d3
    Move.l d2,d4
    Lsr.w  #1,d4             ;d4 := 3/8 * the radius

    ; Check for arrow collision
    Move.l DS_Len0(pc),d0
    Lsr.w  #1,d0
    Cmp.l  #6,d0
    Bgt    DS_NotNewArrowCollision       ;if DS_Len0 * 2 > 6: branch to DS_NotNewArrowCollision
    ;   Cmp.l  d0,d4
    ;   Bls    DS_NotNewArrowCollision
    Cmp.l  DS_LenL1(pc),d0
    Bge    DS_NotNewArrowCollision       ;if DS_Len0 * 2 >= DS_LenL1: branch to DS_NotNewArrowCollision
    Cmp.l  DS_LenR1(pc),d0
    Blt    DS_NewArrowCollision          ;if DS_Len0 * 2 < DS_LenR1: branch to DS_NewArrowCollision

DS_NotNewArrowCollision
    ; If LenL* or LenR* are so small that turning left or right becomes too tight given the rotation radius,
    ; zero all directions at the same side with a higher index;
    ; then branch to DS_FindMax.
    ; Rationale: the FindMax logic takes plain maximum from L* and R*, and thus, free space in
    ; e.g. L4 overrides lack of free space in L1. The above zeroing makes sure that the tight
    ; conditions near to the ahead direction prevail in the decision.

DS_Left_Care
    Cmp.l  DS_LenL1(pc),d3
    Bgt  DS_L1_Collision            ;if 1/2 * the radius > DS_LenL1: branch to DS_L1_Collision
    Cmp.l  DS_LenL2(pc),d2
    Bgt  DS_L2_Collision            ;if 3/4 * the radius > DS_LenL2: branch to DS_L2_Collision
    Cmp.l  DS_LenL3(pc),d3           
    Bgt  DS_L3_Collision;           ;if 1/2 * the radius > DS_LenL3 (changed from d1, which is the radius)
DS_Right_Care
    Cmp.l  DS_LenR1(pc),d3
    Bgt  DS_R1_Collision            ;if 1/2 * the radius > DS_LenR1: branch to DS_R1_Collision
    Cmp.l  DS_LenR2(pc),d2
    Bgt  DS_R2_Collision            ;if 3/4 * the radius > DS_LenR2: branch to DS_R2_Collision
    Cmp.l  DS_LenR3(pc),d3
    Bgt  DS_R3_Collision            ;if 1/2 * the radius > DS_LenR3 (changed from d1, which is the radius)

    Bra  DS_FindMax

DS_L1_Collision
    Moveq  #0,d0
    Lea.l  DS_LenL2(pc),a0
    Move.l d0,(a0)+                 ;DS_LenL2 := 0
    Addq   #4,a0
    Move.l d0,(a0)+                 ;DS_LenL3 := 0
    Addq   #4,a0
    Move.l d0,(a0)+                 ;DS_LenL4 := 0
    Addq   #4,a0
    Move.l d0,(a0)                  ;DS_LenL5 := 0
    Bra DS_Right_Care

DS_L2_Collision
    Moveq  #0,d0
    Lea.l  DS_LenL3(pc),a0
    Move.l d0,(a0)+                 ;DS_LenL3 := 0
    Addq   #4,a0
    Move.l d0,(a0)+                 ;DS_LenL4 := 0
    Addq   #4,a0
    Move.l d0,(a0)                  ;DS_LenL5 := 0
    Bra DS_Right_Care

DS_L3_Collision
    Moveq  #0,d0
    Lea.l  DS_LenL4(pc),a0
    Move.l d0,(a0)+                 ;DS_LenL4 := 0
    Addq   #4,a0    
    Move.l d0,(a0)                  ;DS_LenL5 := 0
    Bra DS_Right_Care

DS_R1_Collision
    Moveq  #0,d0
    Lea.l  DS_LenR2(pc),a0
    Move.l d0,(a0)+;                ;DS_LenR2 := 0
    Addq   #4,a0
    Move.l d0,(a0)+                 ;DS_LenR3 := 0
    Addq   #4,a0
    Move.l d0,(a0)+                 ;DS_LenR4 := 0
    Addq   #4,a0
    Move.l d0,(a0)                  ;DS_LenR5 := 0
    Bra  DS_FindMax
    
DS_R2_Collision
    Moveq  #0,d0
    Lea.l  DS_LenR3(pc),a0
    Move.l d0,(a0)+                 ;DS_LenR3 := 0
    Addq   #4,a0
    Move.l d0,(a0)+                 ;DS_LenR4 := 0
    Addq   #4,a0
    Move.l d0,(a0)                  ;DS_LenR5 := 0
    Bra  DS_FindMax
    
DS_R3_Collision
    Moveq  #0,d0
    Lea.l  DS_LenR4(pc),a0
    Move.l d0,(a0)+                 ;DS_LenR4 := 0
    Addq   #4,a0
    Move.l d0,(a0)                  ;DS_LenR5 := 0
    Bra  DS_FindMax

DS_WasArrowCollision              ;There was an arrow collision in the previous frame.
    ;Preserve the previously made turn left or turn right decision driven by arrow collision
    ;unless quitting condition obtains
    Move.l DS_Len0(pc),d1
    Cmp.l  DS_LenL1(pc),d1
    Bge  DS_QuitArrowCollision    ;if DS_Len0 >= DS_LenL1: branch to DS_QuitArrowCollision
    Cmp.l  DS_LenR1(pc),d1
    Bge  DS_QuitArrowCollision    ;if DS_Len0 >= DS_LenR1: branch to DS_QuitArrowCollision

    Move.l d0,d7                  ;d7 := DS_ArrowDecisionStore[Player]

    Move.l  #$1,d6                ;DEBUG

    Bra DS_Store_Decision

DS_QuitArrowCollision
    Move.l Player(pc),d0
    Move.b #0,(a0,d0)             ;DS_ArrowDecisionStore[Player] := 0
    Bra DS_WasntArrowCollision

DS_NewArrowCollision
    Moveq  #1,d7                  ;#1: turn left
    Move.l DS_LenL1(pc),d0
    Cmp.l  DS_LenR1(pc),d0
    Bge    DS_NAC_DirInD7         ;if DS_LenL1 >= DS_LenR1: branch to DS_NAC_DirInD7
    Moveq #2,d7                   ;#2: turn right
DS_NAC_DirInD7
    Move.l Player(pc),d0
    Move.b d7,(a0,d0)             ;DS_ArrowDecisionStore[Player] := d7

    Move.l #$1,d6                 ;DEBUG

    Bra DS_Store_Decision
    
    ;------------------------------------------------------
    ; Find max direction length and max index of the length
    ;------------------------------------------------------
DS_FindMax
    Lea.l  DS_Len0(pc),a4
    Moveq  #$A,d0
    Moveq  #0,d1        ;lenMax: max value of length
    Moveq  #0,d2        ;lenMaxIdx: index of max length
DS_DecLoop
    Move.l (a4)+,d3
    Cmp.w  d1,d3
    Bls    DS_NotHigher ;if DS_LenX <= lenMax: goto DS_NotHigher
    Move.w d3,d1        ;lenMax := value
    Move.b d0,d2        ;lenMaxIdx := index of the max value found
DS_NotHigher
    DBra   d0,DS_DecLoop

    ;------------------------------------------------------------------------
    ; Calculate decision number (0, 1, 2) and store it to PlayersChoice array
    ;------------------------------------------------------------------------
    Cmp.b  #$A,d2
    Beq    DS_STRAIGHT_ON_INDEX   
    
    ; d7 := ((lenMaxIdx bitand 1) xor 1) + 1
    And.b  #1,d2
    Eor.b  #1,d2
    Add.b  #1,d2
    Move.l d2,d7        ;Save to decision register, d7

DS_Store_Decision
    Move.l Player(pc),d0
    Lea.l  PlayersChoice(pc),a0
    Move.b d7,(a0,d0)   ;PlayersChoice[Player] := decision (0: no action, 1: turn left, 2: turn right)
    Rts

DS_STRAIGHT_ON_INDEX
    Moveq  #0,d7        ;Save to decision register
    Bra DS_Store_Decision


; ___________________________________________________________________
;| Handle other players
;|___________________________________________________________________
;| Take into account a possible collision with a future trajectory
;| of other players and potentially change/reduce the calculated
;| lengths for how far one can go in different directions, thereby
;| affecting the decision to turn left or right or stay moving straight on.
;| The projected trajectory is a linear extension of the
;| current direction.
;|
;| The detail of the logic seems difficult to understand in retrospect (2024).
;| It involves the calculation of a certain vector, its size and a direction,
;| certain angles, certain other values based on these angles,
;| sin values of these values, a ratio of these sin values,
;| and a certain length times the ratio.
;|
;| Input: PlayerOn, PlayersDir, XHash, YHash, XInt 
;|        YInt, Direction
;| Affected: DS_Len0, DS_LenL1, DS_LenL2, DS_LenR1, DS_LenR2.
;|
;| Loop registers:
;| a1: plrNoTimesFour: other player no * 4 (an outer loop variable)
;| a2: ownPlrExamDirection: the examined own player direction,
;|     initially the own player direction itself and then its modifications
;| a3: directionLoopVar: driving variable of the direction loop
;| Other register with invariant meaning:
;| a4: othPlrDirection: a player direction
;|
;| This procedure does not depend on any registers for input or output.
;|___________________________________________________________________

OP_PlayerX  dc.l 0
OP_PlayerY  dc.l 0
OP_A        dc.l 0

OtherPlayersCare
    Move.l #20,a1           ;for plrNoTimesFour from 5*4 to 0*4, step -4
    
OP_PlayersLoop_Start
    Move.l PlayerOn(pc),a0
    Tst.l  (a0,a1)
    Beq OP_PlayerFinished   ;if PlayerOn[plrNoTimesFour] == 0, skip the player.

    Move.l PlayersDir(pc),a0
    Move.l (a0,a1),a4       ;othPlrDirection := PlayersDir[plrNoTimesFour]

    ;----------------------------------------------------------------
    ; Calculate dx and dy; and the vector quadrant
    ;----------------------------------------------------------------
    ; dx := abs(other player x int - the own player x int)
    ; dy := abs(other player y int - the own player y int)
    ; d2 := quadrant of the dx, dy vector before applying the abs
    ;----------------------------------------------------------------

    Move.l YHash(pc),a0
    Move.l (a0,a1),d0
    Bsr   ConvertAMOSRealToFixed
    Swap   d0
    Move.w d0,d3            ;d3.w := Y#[plrNoTimesFour] converted to int

    Move.l XHash(pc),a0
    Move.l (a0,a1),d0
    Bsr   ConvertAMOSRealToFixed 
    Swap   d0
    Ext.l  d0               ;d0 := int(X#[plrNoTimesFour])
    Move.w d3,d1
    Ext.l  d1               ;d1 := int(Y#[plrNoTimesFour])
    Sub.l  XInt(pc),d0      ;d0 := int(X#[plrNoTimesFour]) - XInt
    Sub.l  YInt(pc),d1      ;d1 := int(Y#[plrNoTimesFour]) - YInt
    Tst.l  d0
    Bmi    OP_XMinus
    Tst.l  d1
    Bmi    OP_XPlus_YMinus
OP_XPlus_YPlus    
    Moveq  #0,d2            ;quadrant is 0
    Bra    OP_Quadrant_Calculated
OP_XPlus_YMinus
    Moveq  #1,d2            ;quadrant is 1
    Neg.l  d1
    Bra    OP_Quadrant_Calculated
OP_XMinus
    Tst.l  d1
    Bmi    OP_XMinus_YMinus
OP_XMinus_YPlus
    Moveq  #3,d2            ;quadrant is 3
    Neg.l  d0
    Bra    OP_Quadrant_Calculated
OP_XMinus_YMinus
    Neg.l  d0
    Neg.l  d1
    Moveq  #2,d2            ;quadrant is 2
OP_Quadrant_Calculated 

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

    ; If dx == 0 and dy == 0, finish the business.
    Tst.w  d1
    Bne OP_DxDyNotZero
    Tst.w  d0
    Beq OP_PlayerFinished

    ;----------------------------------------------------------------
    ; Calculate the direction of the dx, dy vector given the quadrant
    ;----------------------------------------------------------------
    
OP_DxDyNotZero
    Move.l d1,d7            ;d7 := dy
    Move.l d0,d6            ;d6 := dx

    Cmp.w  d1,d0
    Bls    OP_ReverseOK     ;if dx <= dy: goto OP_ReverseOK
OP_ReverseDo
    Asl.w  #8,d1
    Divu.w d0,d1            ;d1 == tan(alpha)*$100, which is d1 * $100/d0, which is dy * $100/dx
    Ext.l  d1
    Move.l AtanTable(pc),a0
    Moveq  #0,d3
    Move.b (a0,d1),d3       ;d3 := ATan(d1), in the directions units
    Move.l Directions(pc),d4
    Lsr.w  #2,d4            ;d4 := DirectionsQuarter
    Sub.w  d3,d4            ;d4 := DirectionsQuarter - ATan(d1), in the directions units
    Bra OP_Correct_Angle_Using_Quadrant
OP_ReverseOK                ;d0 <=d1         
    Asl.w  #8,d0
    Divu.w d1,d0            ;d0 == tan(alpha)*$100, which is d0 * $100/d1, which is dx * $100/dy
    Ext.l  d0
    Move.l AtanTable(pc),a0
    Moveq  #0,d4
    Move.b (a0,d0),d4       ;d4 := ATan(d0) in the directions units
OP_Correct_Angle_Using_Quadrant
    Move.l Directions(pc),d3
    Lsr.w  #1,d3            ;d3 := DirectionsHalf
    Btst   #0,d2            ;Is it 1 or 3 quadrant?
    Beq OP_TestBit2         ;It isn't.
    Sub.w  d3,d4
    Neg.w  d4               ;d4 := DirectionsHalf - Direction
OP_TestBit2
    Btst   #1,d2            ;Is it 2 or 3 quadrant?
    Beq OP_VectorDirectionCalculated
    Add.w  d3,d4            ;d4 := d4 + DirectionsHalf
                            ;This pertains only to quadrant 3
    Cmp.l  Directions(pc),d4    
    Bne    OP_VectorDirectionCalculated
    Moveq  #0,d4;           ;if d4 == 2 * pi: d4 := 0
OP_VectorDirectionCalculated
    
    ;-----------------------------
    ; d4: direction of vector
    ; d6: dx                    
    ; d7: dy                    
    ;-----------------------------
    ; Calculate the size of vector
    ;-----------------------------
    Bsr CalculateVectorSize
    Move.l d6,d0               ;d0 := vector size of dy and dy

    ;---------------------------
    ; We need to preserve:
    ; d0: a (the vector size of dy and dy)
    ; d4: ownToOthrVectorDir: direction of vector
    ; Issue: it needs to be verified that the above is really a direction from own player
    ; to the other player rather than the other way around.
    ;---------------------------

    Cmp.w  #ASizeLimit,d0
    Bhi OP_PlayerFinished      ;if vector size > ASizeLimit: goto OP_PlayerFinished
    Move.l #100,a6             ;DEBUG

    ;-----------------------------------
    ; A loop for own player directions
    ;-----------------------------------
    ;Direction will be in a2
    ;a3: loop variable 4->0 (init to 5, but the first taking into account is a decrement)

    Move.l Direction(pc),a2    ;a2 := the own player direction (loop initialization)
    Move.l #5,a3               ;directionLoopVar := straight ahead
OP_DirectionLoop_Start

    ;Let's check possibilities
    Cmp.w  a2,d4
    Beq  OP_LengthIsA          ;if the own player direction == direction of vector: goto OP_LengthIsA
    Move.l #$FFFF,d1           ;length := $FFFF
    
    ;------------------------------------------------------------------------------------
    ; Calculate alpha and beta
    ;------------------------------------------------------------------------------------
    ; alpha := direction of the vector - other player direction, normalized to positive value
    ; beta := direction of the vector - own player direction, normalized to positive value
    ; The normalization to positive value is adding 2 * pi if required.
    ; The alpha and beta angles are expressed in direction count units as integers.
    ; d7 := alpha; d6 := beta    
    ;------------------------------------------------------------------------------------

    Move.l a4,d3               ;d3 := othPlrDirection
    Move.l d4,d7               ;d7 := direction of vector
    Sub.l  d3,d7               ;d7 := direction of vector - othPlrDirection
    Bpl    OP_AlphaCalculated
    Add.l  Directions(pc),d7
OP_AlphaCalculated             ;d7 := alpha (direction of vector - othPlrDirection, norm. to positive value)

    Move.l a2,d3               ;d3 := own player direction
    Move.l d4,d6               ;d6 := direction of vector
    Sub.l  d3,d6
    Bpl    OP_BetaCalculated
    Add.l  Directions(pc),d6
OP_BetaCalculated              ;d6 := beta (direction of vector - own player direction, norm. to positive value)

    Move.l Directions(pc),d3
    Lsr.w  #1,d3               ;d3:= pi (=directions half)

    ;----------------------------------------------------------------
    ; Compare alpha and beta
    ; Issue: a description or a specification could be added here.
    ;----------------------------------------------------------------
    ; at time of entry: d7 == alpha; d6 == beta; d3 == pi
    ;----------------------------------------------------------------

    Cmp.w  d3,d7
    Bls OP_AlphaNotGtThanPi ;if alpha <= pi: goto OP_AlphaNoGtThanPi
    Cmp.w  d3,d6
    Bls OP_NoCollision      ;if alpha > pi and beta <= pi: goto OP_NoCollision

    ; Holds true: alpha > pi and beta > pi

    Asl.w  #1,d3            ;d3 := 2 * pi
    Sub.l  d3,d7
    Neg.l  d7               ;d7 := -(alpha - 2 * pi) == 2 * pi - alpha
    Sub.l  d3,d6
    Neg.l  d6               ;d6 := -(beta - 2 * pi) == 2 * pi - beta
    Lsr.w  #1,d3            ;d3 := pi
    Bra OP_MainAlphaBetaCase

OP_AlphaNotGtThanPi
    Cmp.w  d3,d6
    Bhi OP_NoCollision      ;if alpha <= pi and beta > pi: goto OP_NoCollision
OP_MainAlphaBetaCase
    ;----------------------------------------------------------------
    ; Handle main case
    ;----------------------------------------------------------------
    ; d6 and d7 are a) beta and alpha or b) 2 * pi - beta and 2 * pi - alpha.
    ; d7: alpha2; d6: beta2 (let us choose this notation).
    ; Issue: a description or specification could be added.
    ;----------------------------------------------------------------
    Cmp.l  d6,d7
    Bls OP_NoCollision          ;if alpha2 <= beta2: goto OP_NoCollision
    Move.l d3,d5
    Sub.l  d7,d5                ;d5 := pi - alpha2
    Cmp.l  d5,d6
    Bhi OP_NoCollision          ;if pi + beta2 > alpha2: goto OP_NoCollision
    ; Issue: The above comment "if pi + beta2 > alpha2: goto OP_NoCollision" needs to be verified.
    
    ; Calculate to handle potential collision
    ; Issue: The above description needs to be verified.
    Move.l #1000,a6             ;DEBUG

    Sub.l  d7,d6
    Neg.l  d6                   ;d6 := alpha2 - beta2
    Sub.l  d3,d7
    Neg.l  d7                   ;d7 := pi - alpha2
    Move.l d3,d5                ;d5 := pi 
    Lsr.w  #1,d3                ;d3 := pi/2

    Cmp.w  d3,d7
    Bls OP_PiMinusAlphaCorrected
    Sub.l  d5,d7
    Neg.l  d7                   ;if pi - alpha2 > pi/2: d7 := pi - d7
OP_PiMinusAlphaCorrected
    Cmp.w  d3,d6
    Bls OP_AlphaMinusBetaCorrected  
    Sub.l  d5,d6
    Neg.l  d6                   ;if alpha2 - beta2 > pi/2: d6 := pi - d6
OP_AlphaMinusBetaCorrected
    Move.l d0,d5                ;d5 := d0, which is a
    ;
    Move.l SinTable(pc),a0
    Asl.w  #2,d6
    Move.l (a0,d6),d0
    Bsr ConvertAMOSRealToFixed
    Move.l d0,d3                ;d3 := sin(d6)

    Asl.w  #2,d7
    Move.l (a0,d7),d0
    Bsr ConvertAMOSRealToFixed  ;d0 := sin(d7)

    ;----------------------------------------------------------------
    ; Calculate d0/d3
    ; d0 and d3 are 16.16 fixed-point numbers
    ;
    ; Issue: The above comment seems incorrect since the code seems to do more than calculate d0/d3.
    ; This idea is supported by the label "OP_DividePlusTimes" containing "PlusTimes".
    ; The purpose of the initial swaps and rors is unclear.
    ;
    ; Issue: The meaning of the comment "This operation shifts d1,d2 as high as possible" is unclear.
    ;
    ; d1: position in out counter
    ; d6: result
    ;
    ; What are the conditions under which there is and is not the ror applied to d0 and d3:
    ; Ror is skipped iff d3.w == 0 and d0.w == 0.
    ;----------------------------------------------------------------
OP_DividePlusTimes
    Swap   d3
    Swap   d0
    Cmp.w  #0,d3
    Beq OP_d1OK              ;if d3.w == 0: goto OP_d1OK
OP_DoRor
    Ror.l  #1,d3
    Ror.l  #1,d0
    Bra OP_RorFin
OP_d1OK
    Cmp.w  #0,d0
    Bne OP_DoRor             ;if d0.w != 0: goto OP_DoRor
OP_RorFin                    ;This operation shifts d1,d2 as high as possible
    
    Swap   d0
    And.l  #$FFFF,d1         ;This seems unnecessary given there is d1 := 31 later anyway.

    Moveq  #0,d6             ;Clear the result
    Moveq  #31,d1            ;Position := 31
OP_Divide_Loop_Start
    ;if d3 <= d0: d0 := d0 - d3; set bit d1 in d6(result)
    Cmp.l  d0,d3
    Bhi OP_Divide_NotSub     ;d3>d0
    Sub.l  d3,d0
    Bset.l d1,d6
OP_Divide_NotSub    
    Lsr.l  #1,d3             ;d3 := d3 / 2
    Subq   #1,d1             ;position counter -= 1
    Tst.l  d3
    Bne OP_Divide_Loop_Start ;if d3 != 0: goto OP_Divide_Loop_Start
    Asl.l  #1,d6             ;d6 := d6 * 2

    ;----------------------------------------------------------------
    ; d6 is a 16.16 fixed-point number.
    ;----------------------------------------------------------------
    ; d1 := d6 * a, where a is in d5
    ; a is the dx, dy vector length, an integer
    ;----------------------------------------------------------------
    Move.w d6,d0
    Mulu.w d5,d0             ;d0 := ratio.w * a
    Swap   d6
    Mulu.w d5,d6             ;d6 := ratio.uw * a
    Moveq  #0,d1
    Move.w d6,d1
    Swap   d1                ;d1.uw := d6.w
    Add.l  d0,d1             ;d1 := d1 + d0; and thus, d1 := (ratio.uw * a) << 16 + ratio.w * a
    Swap   d1
    And.l  #$FFFF,d1         ;d1 := int(d1 as 16.16 fixed-point number)
    ;-------------------------------
    ; d1 == collDist
    ;-------------------------------
    Move.l d5,d0             ;d0 := a
    Bra OP_NoCollision

OP_LengthIsA
    Move.l d0,d1             ;collDist := d0

    ;--------------------------------------------------------------------------
    ; Use the calculated distance to collision (in d1) to limit DS_LenL* or DS_LenR*
    ; as applicable for the currently evaluated direction.
    ; Then, finish the direction loop by changing a2, which is ownPlrExamDirection.
    ; Issue: The part "calculated distance to collision" needs to be verified.
    ;--------------------------------------------------------------------------
    ; d1 == collDist: the distance to a potential collision with the other player,
    ; possibly an approximation or heuristic
    ;--------------------------------------------------------------------------
    ; Issue: The label "OP_NoCollision" below seems misleading;
    ; it seems once can arrive at the label even when there was a collision.
    
OP_NoCollision
    Subq   #1,a3             ;directionLoopVar -= 1
    Cmp.w  #4,a3
    Beq    OP_Straight_Lim   ;if directionLoopVar == 4: goto straight
    Cmp.w  #3,a3
    Beq    OP_L0_Lim         ;if directionLoopVar == 3: goto left 0
    Cmp.w  #2,a3
    Beq    OP_R0_Lim         ;if directionLoopVar == 2: goto right 0
    Cmp.w  #1,a3
    Beq    OP_L1_Lim         ;if directionLoopVar == 1: goto left 1
    Bra    OP_R1_Lim         ;goto right 1

    ;------------------------------------------------------
    ; Limit straight and advance to next direction
    ;------------------------------------------------------

OP_Straight_Lim
                    ;   Cmp.w  #$FFFF,d1
                    ;   Beq    OP_PlayerFinished
    Lea.l  DS_Len0(pc),a0
    Cmp.l  (a0),d1
    Bhi OP_Straight_NoCol
    Move.l d1,(a0)           ;if collDist <= DS_Len0: DS_Len0 := collDist
OP_Straight_NoCol
    Add.l  DirStep0(pc),a2   ;ownPlrExamDirection += DirStep0
    Move.l Directions(pc),d1
    Cmp    d1,a2
    Bls    OP_OK0
    Sub.l  d1,a2             ;if ownPlrExamDirection > Directions: ownPlrExamDirection -= Directions
OP_OK0
    Bra OP_DirectionLoop_Start

    ;--------------------------------------------
    ; Limit left 1 and advance to next direction
    ;--------------------------------------------

OP_L0_Lim
    Lea.l  DS_LenL1(pc),a0
    Cmp.l  (a0),d1
    Bhi OP_L0_NoCol
    Move.l d1,(a0)              ;if collDist <= DS_LenL1: DS_LenL1 := collDist
OP_L0_NoCol
    Move.l Direction(pc),a2 
    Move.l DirStep0(pc),d1      ;ownPlrExamDirection := Direction
    Cmp    a2,d1
    Bls    OP_OK1
    Add.l  Directions(pc),a2    ;if ownPlrExamDirection > DirStep0: ownPlrExamDirection += Directions
OP_OK1
    Sub.l  d1,a2                ;ownPlrExamDirection -= DirStep0
    Bra OP_DirectionLoop_Start

    ;--------------------------------------------
    ; Limit right 1 and advance to next direction
    ;--------------------------------------------
OP_R0_Lim
    Lea.l  DS_LenR1(pc),a0
    Cmp.l  (a0),d1
    Bhi OP_R0_NoCol
    Move.l d1,(a0)              ;if collDist <= DS_LenR1: DS_LenR1 := collDist
OP_R0_NoCol
    Move.l Direction(pc),a2
    Add.l  DirStep1(pc),a2      ;ownPlrExamDirection := Direction + DirStep1
    Move.l Directions(pc),d1
    Cmp    d1,a2
    Bls    OP_OK2
    Sub.l  d1,a2                ;if ownPlrExamDirection > Directions: ownPlrExamDirection -= Directions
OP_OK2
    Bra OP_DirectionLoop_Start

    ;--------------------------------------------
    ; Limit left 2 and advance to next direction
    ;--------------------------------------------

OP_L1_Lim
    Lea.l  DS_LenL2(pc),a0
    Cmp.l  (a0),d1
    Bhi OP_L1_NoCol
    Move.l d1,(a0)              ;if collDist <= DS_LenL2: DS_LenL2 := collDist
OP_L1_NoCol
    Move.l Direction(pc),a2
    Move.l DirStep1(pc),d1
    Cmp    a2,d1
    Bls    OP_OK3
    Add.l  Directions(pc),a2    ;if ownPlrExamDirection < DirStepSmall: ownPlrExamDirection += Directions
OP_OK3
    Sub.l  d1,a2                ;ownPlrExamDirection -= DirStepSmall
    Bra OP_DirectionLoop_Start

    ;--------------------------------------------
    ; Limit right 2 and exit the direction loop
    ;--------------------------------------------
OP_R1_Lim
    Lea.l  DS_LenR2(pc),a0
    Cmp.l  (a0),d1
    Bhi OP_R1_NoCol
    Move.l d1,(a0)              ;if collDist <= DS_LenR2: DS_LenR2 := collDist
OP_R1_NoCol

OP_PlayerFinished
    ;--------------------
    ; End of player loop
    ;--------------------
    Subq   #4,a1                ;plrNoTimesFour -= 4
    Cmp.w  #0,a1
    Bge    OP_PlayersLoop_Start ;loop on plrNoTimesFour >= 0

    Rts

;    _________________________________________________________
;   | Calculate vector size
;   |_________________________________________________________
;   | in d6: dx
;   | in d7: dy
;   | out d6: the result
;   | preserve d4
;   | used up: d2, d3
;   | This is sqrt(dx^2+dy^2) as a four-case approximation,
;   | where each case is a linear combination of dx and dy.
;   |_________________________________________________________

CalculateVectorSize
    Cmp.l  d6,d7
    Bls CV_RegOK        ;d7<=d6
    Exg.l  d6,d7        ;Swap d6 and d7
CV_RegOK                ;Holds true: d7 <= d6
    Move.l d6,d2
    Lsr.w  #2,d2        ;d2 := d6/4
    Move.l d6,d3        ;d3 := d6
    Sub.w  d2,d3        ;d3 := 3/4 * d6
    Cmp.w  d7,d3
    Bls CV_CASE1        ;d7 >= 3/4 * d6
    Sub.w  d2,d3
    Cmp.w  d7,d3
    Bls CV_CASE2        ;d7 >= 1/2 * d6
    Sub.w  d2,d3
    Cmp.w  d7,d3
    Bls CV_CASE3        ;d7 >= 1/4 * d6
CV_CASE4    
    Rts     ; The result is in d6.
CV_CASE1
    Sub.w  d2,d6        ;d6 := 3/4*d6
    Move.l d7,d3
    Lsr.w  #1,d3        ;d3 := d7/2
    Add.w  d3,d6        ;d6 := 3/4*d6+d7/2
    Lsr.w  #3,d7
    Add.w  d7,d6        ;d6 := 3/4*d6+d7/2+d7/8
    Rts
CV_CASE2
    Lsr.w  #1,d2
    Sub.w  d2,d6        ;d6 := d6*7/8
    Move.l d7,d3
    Lsr.w  #4,d3
    Sub.w  d3,d6        ;d6 := d6*7/8-d7/16
    Lsr.w  #1,d7
    Add.w  d7,d6        ;d6 := d6*7/8+d7/2-d7/16
    Rts             
CV_CASE3
    Lsr.w  #3,d7        ;d7 := d7/2
    Add.w  d7,d6        ;d6 := d6+d7/8
    Lsr.w  #1,d7    
    Add.w  d7,d6        ;d6 := d6+d7/8+d7/16
    Rts

;=========================== DEBUGGING =========================

DoStrategyDebug
    Bsr DoStrategy

;   Move.l a6,d0
;   Move.l a5,d1
;   In a1 there is the direction of the vector
;
    Move.l DS_Len0(pc),d0
    Move.l DS_LenL1(pc),d1
    Move.l DS_LenR1(pc),d2
;   Move.l DS_LenL2(pc),d3
;   Move.l DS_LenR2(pc),d4
    Rts

;  _______________________________
; |                           
; | CalcTestAndSpeedPoints 
; |_______________________________
; |                           
; | d0-direction     IN       
; | a12-oxy#                  
; |                           
; | a12-speedxy      OUT      
; | d01-testxy                
; |                           
; | changed regs: d0-d7
; |               a1, a2, a4, a5 
; |_______________________________

; d4 -xint
; d5 -yint
; d6 -xint+2
; d7 -yint+2

CalcTestAndSpeedPoints_2Pixel
    Bsr CalculateSinAndCosFixed
    Move.l d4,a4
    Move.l d5,a5        ;Store fixed dx,dy for later

    Move.l d4,d2
    Move.l d5,d3        ;d23 = dxy

    Move.l a2,d0
    Bsr ConvertAMOSRealToFixed
    Move.l d0,a2
    Move.l a1,d0
    Bsr ConvertAMOSRealToFixed
    Move.l d0,a1

    Move.l a2,d1        ;so that d0, d1 == xfixed, yfixed

    Move.l d0,d4
    And.l  #$FFFF0000,d4
    Move.l d4,d6
    Move.l d1,d5
    And.l  #$FFFF0000,d5
    Move.l d5,d7        ;d46:=xint, d57:=yint
    Sub.l  #$10000,d4
    Sub.l  #$10000,d5
    Add.l  #$30000,d6
    Add.l  #$30000,d7   ;INTS filled

    Bsr    CTP_StraightLoop

    Exg.l  d0,a1   
    Exg.l  d1,a2        ;a12:=xyspeed, d01:=xyfixed

    Move.l a4,d2
    Move.l a5,d3        ;d23 = dxy

    Move.l d0,d4
    And.l  #$FFFF0000,d4
    Move.l d4,d6
    Move.l d1,d5
    And.l  #$FFFF0000,d5
    Move.l d5,d7        ;d4,d6:=xint d5,d7:=yint

    Add.l  #$20000,d6
    Add.l  #$20000,d7       ;INTS filled

    Bsr    CTP_StraightLoop

    Rts         ;d0,d1..test,a1,a2..speed

;------------------------------------- d01 ----
    
CTP_StraightLoop
    Add.l  d2,d0
    Add.l  d3,d1

    Cmp.l  d0,d4        ;x<xint
    Bgt CTP_Finished
    Cmp.l  d1,d5        ;y<yint
    Bgt CTP_Finished
    Cmp.l  d0,d6        ;x>=xint+2
    Bls CTP_Finished
    Cmp.l  d1,d7        ;not y>=yint+2
    Bgt CTP_StraightLoop

CTP_Finished
    And.l  #$FFFF0000,d0
    And.l  #$FFFF0000,d1
    Swap   d0
    Swap   d1
    Rts

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

CalcTestAndSpeedPoints_1Pixel
    Bsr CalculateSinAndCosFixed
    Move.l d4,a4        
    Move.l d5,a5             ;Store fixed dx,dy

    Move.l d4,d2
    Move.l d5,d3             ;d2 is dx, d3 is dy

    Move.l a2,d0
    Bsr ConvertAMOSRealToFixed
    Move.l d0,a2
    Move.l a1,d0
    Bsr ConvertAMOSRealToFixed
    Move.l d0,a1

    Move.l a2,d1             ;so that d0,d1=x,y fixed

    Move.l d0,d4
    And.l  #$FFFF0000,d4
    Move.l d4,d6
    Move.l d1,d5
    And.l  #$FFFF0000,d5
    Move.l d5,d7             ;d4,d6:=xint d5,d7:=yint
    Sub.l  #$10000,d4
    Sub.l  #$10000,d5
    Add.l  #$20000,d6
    Add.l  #$20000,d7        ;INTS filled

    Bsr    CTP_StraightLoop

    Exg.l  d0,a1   
    Exg.l  d1,a2             ;a1,a2:=xyspeed,d0,d1:=beginxyfixed

    Move.l a4,d2
    Move.l a5,d3             ;d2 is dx, d3 is dy

    Move.l d0,d4
    And.l  #$FFFF0000,d4
    Move.l d4,d6
    Move.l d1,d5
    And.l  #$FFFF0000,d5
    Move.l d5,d7             ;d4,d6:=xint d5,d7:=yint
    Add.l  #$10000,d6
    Add.l  #$10000,d7        ;INTS filled
    Bsr    CTP_StraightLoop 

    Rts         ;d0,d1..test,a1,a2..speed

;    _____________________________________________
;   |                           
;   | CalculateSinAndCosFixed   
;   |_____________________________________________
;   |
;   | d0: direction in
;   | d4: sin fixed out
;   | d5: cos fixed out
;   |
;   | change regs: d0,d1,d3,d4,d5,d6,d7
;   |        a0
;   |_____________________________________________

CalculateSinAndCosFixed
    Move.l Directions(pc),d6
    Lsr.w  #2,d6            ;d6 := DirectQuarter
    Move.l d0,d7            ;d7 := Direction    
    Divu.w d6,d7            ;d7 := direction / (direction count / 4)
                            ; remainder is saved in the upper word or d7; d7.w has quadrant
    Move.l d7,d1        
    Swap   d1
    Ext.l  d1               ;d1 := remainder

    Btst   #0,d7
    Beq CSF_FinalDir
    Sub.l  d6,d1
    Neg.l  d1               ;Dir=-(Dir-DirsQuarter)
CSF_FinalDir
    ;Now d1 == DifDirection
    Move.l d1,d3            ;StoreDifDirection

    Asl.l  #2,d1            ; d1 := d1 * 4
    Move.l SinTable(pc),a0
    Move.l (a0,d1),d0
    Bsr ConvertAMOSRealToFixed
    Move.l d0,d4

    Btst   #1,d7
    Beq CSF_SinCalculated
    Neg.l  d4
CSF_SinCalculated
    Sub.l  d6,d3
    Neg.l  d3               ;Dir=-(Dir-DirsQuarter)
    Asl.l  #2,d3
    Move.l (a0,d3),d0       ;a0 == SinTable
    Bsr ConvertAMOSRealToFixed
    Move.l d0,d5
    
    Add.b  #1,d7
    Btst   #1,d7
    Beq CSF_CosCalculated
    Neg.l  d5
CSF_CosCalculated
    Rts

;    _____________________________________________
;   |                           
;   | CalculateSinAndCos
;   |_____________________________________________
;   |                           
;   | d0: direction in           
;   | d0: sin       out
;   | d2: cos       out          
;   |                           
;   | changed regs: d0, d1, d2, d3, d6, d7
;   |               a0
;   |_____________________________________________

CalculateSinAndCos
    Move.l Directions(pc),d6
    Lsr.w  #2,d6            ;d6 := direction count / 4 (DirCntQuarter)
    Move.l d0,d7            ;d7 := direction
    Divu.w d6,d7            ;d7 := direction / (direction count / 4)
                            ;remainder is saved in the upper word or d7; d7.w has quadrant
    Move.l d7,d1
    Swap   d1
    Ext.l  d1               ;d1 := remainder of direction / (direction count / 4)

    Btst   #0,d7
    Beq CS_FinalDir
    Sub.l  d6,d1
    Neg.l  d1               ;dir = -(dir - direction count / 4)
CS_FinalDir
    ;Now d1 == DifDirection
    Move.l d1,d3            ;StoreDifDirection

    Asl.l  #2,d3
    Move.l SinTable(pc),a0
    Move.l (a0,d3),d0       ;Picked from the table

    Btst   #1,d7
    Beq CS_SinCalculated    
    Add.b  #$80,d0
CS_SinCalculated            ;The sine is in d0

    Sub.l  d6,d1
    Neg.l  d1               ;Dir=-(Dir-DirsQuarter)
    Asl.l  #2,d1
    Move.l (a0,d1),d2       ;Picked from the table
    
    Add.b  #1,d7
    Btst   #1,d7
    Beq CS_CosCalculated
    Add.b  #$80,d2
CS_CosCalculated
    Rts
    
;--------------------------------------------------------------------
; Quick 68000 asm guide, to support the above as a quick reference
;--------------------------------------------------------------------
; Registers: 32-bit registers d0 through d7, and then a0 through a7;
; then there are other registers such as stack pointer.
; .l, .w and .b selects on which part of the register or location to operate:
; .l: 32 bits; .w: 16 bits; .b: 8 bits
; Negative integers are using two's complement representation.
;
; Move.l: copy the 32 bits from 1st to 2nd operand (left to right)
; Move.l (a0,d1),d2: add d1 to the address a0 to get address to read from, and copy the result to d2
; Moveq: move quick a small immediate value to the 32 bits of the register
; Lea: load effective address
; Swap: swap the upper and lower 16-bit words of the register
; Ext.l: extend the sign bit of the lower 16-bit word to all the bits of the upper 16-bit word
; Exg: exchange/swap the values of the two locations, e.g. registers
; Bsr: branch to subroutine
; Rts: return from subroutine
; Bra: branch
; DBra: decrement and branch unless the result is -1 (good for loop control)
; Asl: arithmetic shift left, a bit operation
; Lsr: logical shift right, a bit operation
; Neg.l: multiply the 32 bits of the register -1
; And.l: bitwise and
; Add.l: integer addition
; Sub.l: integer subtraction
; Mulu.w: multiplies (unsigned) the lower 16-bit words, storing the result into the 32 bits of the 2nd operand
; Mulu.l: not possible
; Divu: divide unsigned; the upper 16-bit word has the remainder, whereas the lower 16-bit word has the quotient
; Cmp: compares two values, affecting subsequent Beq, etc.
; Btst #0,d0: test the least significant bit (bits counted from 0, from least significant); affects subsequent Beq
; Beq: branch on equal
; Bne: branch on not equal  
; Bhi: branch on higher than (the 2nd operand of Cmp > the 1st operand; somewhat counterintuitive)
; Bls: branch on lower than or same
; Bgt: branch on greater than
; Bge: branch on greater than or equal
; Bmi: branch on minus; used e.g. after Tst
; Bpl: branch on plus; used e.g. after Tst
; Bset x, y: set the bit number x (counting from least significant ones, from zero) in y
; Tst: compare to zero and affect subsequent Beq and Bne
; Ror: bitwise rotate right
; Rol: bitwise rotate left
;--------------------------------------------------------------------