Aviator on the BBC Micro

# Flight model: ApplyFlightControl

```       Name: ApplyFlightControl                                      [Show more]
Type: Subroutine
Category: Flight model
Summary: Calculate the effects of the primary flight controls (elevator,
rudder and ailerons), and implement the "instant centre" feature
Deep dive: The flight model
On-ground calculations
Context: See this subroutine in context in the source code
References: This subroutine is called as follows:
* ApplyFlightModel (Part 2 of 7) calls ApplyFlightControl

This routine sets the following, depending on the current position of each
primary flight control:

xControls = zLiftDrag * elevatorPosition  (pitch)

yControls = zLiftDrag * rudderPosition    (yaw)

zControls = zLiftDrag * aileronPosition   (roll)

It also implements the "instant centre" feature for the aileron (roll) and
the ground steering controls (the latter is controlled by using the rudder
keys when on the ground). This feature is for keyboard users only, and
instantly centres the control when you press the key for the opposite
direction, so if we press "S" to roll left, then pressing "D" will instantly
centre the joystick before rolling to the right. This feature does not apply
to the elevator or rudder, though it does apply to the rudder controls when on
the ground, as they double-up as brake controls for ground steering, which
does have this feature.

Arguments:

L                    The current value of onGround:

* 0 = we are not on the ground

* 1 = we are on the ground

.ApplyFlightControl

LDX #2                 \ Set a counter in X to work through the three axes, so
\ we can work our way through the three primary flight
\ controls as follows:
\
\   * 2 = aileron, for roll around the z-axis
\   * 1 = rudder, for yaw around the y-axis
\   * 0 = elevator, for pitch around the x-axis
\
\ We also work through the aileron, rudder and elevator
\ key pairs, whose values are stored in these key logger
\ offsets in (keyLoggerHi keyLoggerLo), to check
\ whether any of the relevant control keys are being
\ pressed
\
\ We set a counter in X to count down through these
\ index values, from 2 to 1 to 0

.fcon1

LDA elevatorPosition,X \ Fetch the position of the flight control that affects
BEQ fcon7              \ the axis we are currently processing

LDY keyLoggerLo,X      \ Fetch the low byte for this key pair, which will be 1
\ if a key is being pressed, or 0 if no key is pressed

BNE fcon3              \ If Y is non-zero then a key in this key pair is being
\ pressed, so jump down to fcon3

TAY                    \ No key is being pressed for this axis, so copy the
\ current position of the flight control into Y

BMI fcon2              \ If the control's current position is negative then

CPY #4                 \ If the control's current position >= 4, jump to fcon6
BCS fcon6              \ to set the control's position to A, which will make no
\ difference as A already contains the current value

BCC fcon5              \ The control's current position < 4, so jump to fcon5
\ to zero the control (this BCC is effectively a JMP as
\ we just passed through a BCS)

.fcon2

\ If we get here then the control's current position in
\ Y is negative

CPY #&FD               \ If the control's current position >= -3, jump to fcon5
BCS fcon5              \ to zero the control

BCC fcon6              \ The control's current position < -3, so jump to fcon6
\ to set the control's position to A, which will make no
\ difference as A already contains the current value
\ (this BCC is effectively a JMP as we just passed
\ through a BCS)

.fcon3

\ If we get here then a key is being pressed for this
\ control and A contains the control's current position

CPX #1                 \ If the axis in X < 1, then an elevator key is being
BCC fcon7              \ pressed, so jump to fcon7 to leave the control's
\ position alone

BNE fcon4              \ If the axis in X <> 0, then an aileron key is being
\ the aileron's "instant centre" feature

\ If we get here then a rudder key is being pressed

LDY L                  \ Fetch the current value of onGround

BEQ fcon7              \ If onGround is zero then we are not on the ground, so
\ otherwise keep going to check whether to apply the
\ ground steering's "instant centre" feature

.fcon4

\ If we get here then either an aileron key is being
\ pressed, or we are on the ground and a rudder key is
\ being pressed
\
\ Pressing the rudder key while on the ground controls
\ ground steering, by applying the brakes to the
\ individual wheels

EOR keyLoggerHi,X      \ The relevant entry in keyLoggerHi will be -1 or +1
\ depending on the direction of the aileron or rudder
\ key being applied, so this EOR compares the key's
\ direction with the current position, setting bit 7
\ if the two have different signs, and clearing bit 7
\ if they have the same sign

BPL fcon7              \ If bit 7 is clear, then the key's direction and the
\ current position of the control have the same sign, so
\
\ Otherwise we are pressing a key in the opposite
\ direction to the current aileron or ground steering
\ position, so we instantly move the control back to the
\ centre point

.fcon5

\ We jump here if control's current position is:
\
\   * Positive and < 4
\   * Negative and >= -3
\
\ i.e. -3 <= position <= 3
\
\ In either case, we set the position to 0, so this
\ implements a dead zone around the control's centre
\ point, and the "instant centre" feature for the
\ aileron and ground steering

LDA #0                 \ Set A = 0 to set as the control's new position, i.e.
\ centre the control

.fcon6

STA elevatorPosition,X \ Set the control's new position to the value in A

.fcon7

LDA elevatorPosition,X \ Fetch the updated position of the flight control for
\ the current axis

BPL fcon8              \ If the control's new position is positive, jump to
\ fcon8 to skip the following

EOR #&FF               \ Negate A using two's complement, so:
CLC                    \
ADC #1                 \   A = |A|
\     = |controlPosition|

.fcon8

STA R                  \ Set R = |A|
\       = |controlPosition|

LDY zLiftDragLo        \ Set (A Y) = zLiftDrag
LDA zLiftDragHi

JSR Multiply8x16-6     \ Store X in VV and set:
\
\   (G W V) = (A Y) * R
\           = zLiftDrag * |controlPosition|
\
\ Also set A to the high byte of the result, so (A W V)
\ also contains the result

LDX VV                 \ Retrieve X from VV so it once again contains the axis
\ index

LDY elevatorPosition,X \ If the position of the flight control is positive,

SEC                    \ Negate (G W) and return the high byte in A, so (A W)
JSR Negate16Bit        \ contains the negated result, so we now have the
\ following:
\
\   (A W V) = zLiftDrag * controlPosition

.fcon9

STA xControlsHi,X      \ Set xControls = (A W)
LDA W                  \             = zLiftDrag * controlPosition
STA xControlsLo,X

DEX                    \ Decrement the loop counter to move to the next axis

BEQ fcon7              \ If this is the elevator axis (X = 0), loop back to
\ fcon7 to skip the "instant centre" check, as this
\ feature doesn't apply to pitching

BPL fcon1              \ If this is the rudder y-axis (X = 1), loop back to
\ fcon1 to apply the "instant centre" check, as this
\ feature applies to the ground steering

RTS                    \ Return from the subroutine
```