Revs on the BBC Micro

# 3D objects: CompareSegments

```       Name: CompareSegments                                         [Show more]
Type: Subroutine
Category: 3D objects
Summary: Calculate the distance between two objects, in terms of the
difference in their segment numbers
Deep dive: Placing cars on the track
Context: See this subroutine in context in the source code
References: This subroutine is called as follows:
* BuildVisibleCar calls CompareSegments
* PlaceCarsOnTrack calls CompareSegments

Arguments:

X                    The number of object X

Y                    The number of object Y

C flag               Determines the accuracy of the arithmetic:

* Clear for a 16-bit calculation using objectSegment

* For a 24-bit calculation, contains the carry from
CompareCarSegments above

Returns:

A                    The distance between the two objects, negative if object

T                    The same as A

C flag               How far apart the objects are:

* Set if objects are far apart (distance >= 128)

* Clear if they are close (distance < 128)

N flag               The object order (if the objects are close):

* Set if object X is ahead by more than 256

* Clear otherwise

H                    Relationship to the starting line:

* Bit 7 is clear if the object are quite close (when
distance < 256) but are on opposite sides of the
starting line

* Bit 7 set otherwise

.CompareSegments

LDA objectSegmentLo,Y  \ Set (A T) =   objectSegment for object Y
SBC objectSegmentLo,X  \             - objectSegment for object X
STA T                  \
\ starting with the low bytes

LDA objectSegmentHi,Y  \ And then the high bytes
SBC objectSegmentHi,X  \
\ So (A T) now contains the difference between the two
\ objects in terms of their segment numbers, i.e. the
\ distance between the two in terms of segment numbers
\
\ Let's call this dSegments

PHP                    \ Store the status register on the stack, so the N flag
\ on the stack is the sign of the above subtraction, so
\ it's set if objectSegmentHi for object Y <
\ objectSegmentHi for object X, i.e. if object X is
\ ahead by 256 segments or more

BPL dist1              \ If the result of the subtraction was positive, jump
\ to dist1 to skip the following instruction

JSR Absolute16Bit      \ The result of the subtraction was negative, so set
\ (A T) = |A T|

.dist1

STA U                  \ Set (U T) = (A T)
\           = |dSegments|

SEC                    \ Set the C flag to shift into bit 7 of H below

BEQ dist2              \ If the high byte of the segment difference is zero,

\ If we get here then the high byte of the difference
\ is non-zero, so now we need to check whether this is
\ down to the objects being close but either side of the
\ starting line
\
\ This is because the segment number resets to zero at
\ the starting line, so objects that are on either side
\ of the line will have a big difference in their
\ segment numbers even though they are actually quite
\ close together

PLA                    \ Flip the N flag in the status register on the stack
EOR #%10000000         \ (as the N flag is bit 7 of the status register), so
PHP                    \ the N flag on the stack is the opposite sign to the
\ objectSegment subtraction we did above, in other words
\ it's now clear if object X is ahead by 256 or more and
\ set otherwise
\
\ This caters for the situation where the cars are close
\ but on either side of the starting line, in which case
\ the subtraction we did above will give the wrong
\ result, so this flips the order of the two objects to
\ be correct

\ In the following, the 16-bit number at trackLength
\ contains the length of the full track in terms of
\ segments

LDA trackLength        \ Set (A T) = trackLength - (U T)
SEC                    \           = trackLength - |dSegments|
SBC T                  \
STA T                  \ starting with the low bytes

LDA trackLength+1      \ And then the high bytes
SBC U

BNE dist3              \ If the high byte is non-zero, then that means the
\ objects are not just either side of the starting line,
\ so they must be far away from each other, so jump to
\ dist3 to return from the subroutine with the C flag
\ set

\ If we get here then the high byte is zero, which means
\ the objects are quite close but are either side of the
\ starting line

CLC                    \ Clear the C flag

.dist2

\ If we get here then the objects are close together
\ (the high byte of the difference in segment numbers is
\ zero)

ROR H                  \ Set bit 7 of H to the C flag, so it's clear if the
\ objects are quite close but on opposite sides of the
\ starting line, otherwise it is set

LDA T                  \ If T >= 128, then the difference between the objects
CMP #128               \ is >= 128 segments, so jump to dist3 to return from
BCS dist3              \ the subroutine with the C flag set

\ If we get here then the difference between the objects
\ is 127 or less, so the objects are determined to be
\ close

PLP                    \ Retrieve the status flags from the stack

JSR Absolute8Bit       \ Multiply the sign of A by the N flag

STA T                  \ Set T = A

LDA T                  \ Set the N flag according to the value of A

CLC                    \ Clear the C flag

RTS                    \ Return from the subroutine

.dist3

\ We get here if the objects are far away from each
\ other, i.e. the segment difference between them is
\ >= 128

PLP                    \ Retrieve the status flags from the stack, to return
\ from the subroutine

SEC                    \ Set the C flag

RTS                    \ Return from the subroutine
```