Game Duenix for Amiga computers/Duenix strategy.s
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
;--------------------------------------------------------------------