1361 lines
52 KiB
C
1361 lines
52 KiB
C
// See chunk3.h.
|
|
|
|
#include <stdio.h>
|
|
#include "chunk3.h"
|
|
#include "chunk4.h"
|
|
#include "chunk5Main.h"
|
|
#include "chunk5Panel.h"
|
|
#include "chunk5Zp.h"
|
|
#include "fs2Symbols.h"
|
|
#include "hires.h"
|
|
#include "machine.h"
|
|
#include "sceneryVm.h"
|
|
|
|
#define ZP_A0 0xA0
|
|
#define ZP_B6 0xB6
|
|
#define ZP_BA 0xBA // L00BA
|
|
#define ZP_INSTR_FB 0xFB
|
|
#define ZP_INSTR_FC 0xFC
|
|
#define ZP_INSTR_FD 0xFD
|
|
#define ZP_ROT_ANGLE 0xF2
|
|
#define ZP_ROT_XDISP 0x3E
|
|
#define ZP_ROT_YDISP 0x9A
|
|
#define ZP_ROT_XCOS 0x18
|
|
#define ZP_ROT_XSIN 0x1B
|
|
#define ZP_ROT_YCOS 0x1E
|
|
#define ZP_ROT_SIN 0xA9
|
|
|
|
#define RAM_ADF_LOOKUP_GATE 0x08A8
|
|
#define RAM_ADF_LOOKUP_LATCH 0x08A9
|
|
#define RAM_ADF_ENGAGED 0x08F1
|
|
#define RAM_STATION_SLOT 0x08F9
|
|
#define RAM_PROBLEM_TIMER 0x08BC
|
|
#define RAM_DAMAGE 0x099E
|
|
#define RAM_DAMAGE_TARGET 0x09A0
|
|
#define RAM_WEAR 0x08AB
|
|
#define RAM_LIGHT_A 0x0917
|
|
#define RAM_LIGHT_SRC 0x089B
|
|
#define RAM_WATER_PROBE 0x2A36
|
|
#define RAM_ROTATED_FLAG 0x0877
|
|
#define RAM_TRAPEZOID_X 0xDFB0
|
|
#define RAM_TRAPEZOID_DX 0xDFB2
|
|
#define RAM_TRAPEZOID_W 0xDFB4
|
|
#define RAM_TRAPEZOID_DW 0xDFB6
|
|
#define RAM_TRAPEZOID_Y 0xDFB8
|
|
#define RAM_TRAPEZOID_H 0xDFB9
|
|
#define RAM_MIXTURE 0x0A49
|
|
#define RAM_MAG1_OK 0x0999
|
|
#define RAM_MAG2_OK 0x099A
|
|
#define RAM_TIME_ZONE 0x0854
|
|
#define RAM_CEILING 0x0856
|
|
#define RAM_INSTR_MASK_A 0x0977
|
|
#define RAM_INSTR_MASK_B 0x0978
|
|
#define RAM_INSTR_MASK_C 0x0979
|
|
#define RAM_INSTR_FLAG_B 0x0915
|
|
#define RAM_INSTR_FLAG_C 0x0916
|
|
#define RAM_XSTART 0xE676
|
|
#define RAM_YSTART 0xE678
|
|
#define RAM_YSIZE 0xE679
|
|
#define RAM_XSIZE 0xE67A
|
|
#define RAM_XCOORD 0xE67B
|
|
#define RAM_XCOUNT 0xE67D
|
|
#define RAM_ROT_TEMPLATE 0xF240
|
|
#define RAM_ROT_242 0xF242
|
|
#define RAM_ROT_244 0xF244
|
|
#define RAM_ROT_247 0xF247
|
|
#define RAM_ROT_249 0xF249
|
|
#define RAM_ROT_24C 0xF24C
|
|
#define RAM_ROT_24E 0xF24E
|
|
#define RAM_ROT_251 0xF251
|
|
#define RAM_ROT_253 0xF253
|
|
#define RAM_ROT_258 0xF258
|
|
#define RAM_ROT_25A 0xF25A
|
|
#define RAM_ROT_25C 0xF25C
|
|
#define RAM_ROT_25E 0xF25E
|
|
#define RAM_NIBBLE_DEST 0x3B60
|
|
#define RAM_NIBBLE_END_PAGE 0x3E
|
|
#define RAM_NIBBLE_CHECKSUM 0x3B5E
|
|
|
|
#define ADF_MODE_FIRST 0x0D
|
|
#define ADF_MODE_END 0x10
|
|
#define ADF_NEEDLE 4
|
|
#define ADF_INIT_ANGLE 0x0A
|
|
#define MIXTURE_MAX 8
|
|
#define PROBLEM_PERIOD 0x0A
|
|
#define PROBLEM_CHANCE_MAX 100
|
|
#define DAMAGE_STEP 2
|
|
#define WEAR_STEP 2
|
|
#define SPLASH_FRAMES 5
|
|
#define WATER_BYTE 0x55
|
|
#define CRASH_DELAY 0xFF
|
|
#define LIGHT_DIM_A 0x10
|
|
#define LIGHT_DIM_B 0x80
|
|
#define LIGHT_DIM_C 0x01
|
|
#define OVERLAY_ALT_PAGE 0x20
|
|
#define WING_COLOR_CODE 9
|
|
#define RPM_CURVE_MAX 0x1F
|
|
#define RPM_START_THRESHOLD 0x25
|
|
#define MAGNETO_START 4
|
|
#define MAGNETO_BOTH 3
|
|
#define SPINUP_DELAY 0x64
|
|
#define CARB_HEAT_BIAS 0xF8
|
|
#define SPINUP_BIAS 0xE0
|
|
#define MIXTURE_CLAMP 0x10
|
|
#define MAGNETO_DEGRADE 4
|
|
#define RPM_DRAG_MIN 0x0E
|
|
#define PROP_DRAG_SCALE 0x07D0
|
|
#define CHT_ON 6
|
|
#define CHT_FAULT 0x0A
|
|
#define CHT_STEP 0x0A
|
|
#define OIL_ON 6
|
|
#define OIL_FAULT 2
|
|
#define OIL_STEP 0x64
|
|
#define ATIS_ROW_STEP 7
|
|
#define ATIS_ROW_MAX 90
|
|
#define ATIS_COL_MAX 100
|
|
#define ATIS_COL_STEP 4
|
|
#define ATIS_START 3
|
|
#define ATIS_DELAY_INNER 0x78
|
|
#define SAVE_RECORD_LEN 6
|
|
#define SAVE_RECORDS 8
|
|
#define ROT_ANGLE_BIAS 0x41
|
|
#define ROT_RECORD_LEN 6
|
|
#define LOADER_STREAM_END 0x46
|
|
#define LOADER_BLOCK_BIAS 0x02
|
|
#define LOADER_INIT_OFFSET 0x20
|
|
#define LOADER_ROM_PAGE 0xC0
|
|
#define RAM_ENEMIES_SHOT_DOWN 0x0898
|
|
#define RAM_DAMAGE_BY_ENEMY 0x08A4
|
|
#define OVL_WW1_BOMB_HITS 0xA81B // in the WW1 overlay
|
|
#define OVL_WW1_ENEMY_STATE 0xA972
|
|
#define OVL_WW1_ENEMY_STRIDE 0x1C
|
|
#define WAR_REPORT_ENEMIES 6
|
|
#define DIGIT_ZERO 0x30
|
|
#define NIBBLE_MASK 0xAA
|
|
#define ADF_SCALE 0x58
|
|
|
|
typedef enum LoaderEntryE {
|
|
LOADER_ENTRY_READ_UNTIL_C0 = 1,
|
|
LOADER_ENTRY_READ_FIXED = 2,
|
|
LOADER_ENTRY_NO_OP = 3,
|
|
LOADER_ENTRY_READ_DECODED = 4,
|
|
LOADER_ENTRY_BUFFER1 = 5,
|
|
LOADER_ENTRY_INIT = 6,
|
|
LOADER_ENTRY_BUFFER2 = 7
|
|
} LoaderEntryE;
|
|
|
|
|
|
static void atisAdvance(void);
|
|
static void atisAdvanceCursor(void);
|
|
static void atisDrawChar(uint8_t c);
|
|
static void atisRun(void);
|
|
static uint8_t aTo2Digits(uint8_t value, uint8_t *outOnes);
|
|
static uint8_t axTo3Digits(uint16_t value, uint8_t *outTens, uint8_t *outOnes);
|
|
static uint16_t calcPixelAddrAndMask(uint8_t xLo, uint8_t y, uint8_t *outMask);
|
|
static void drawADFHeadingDigits(void);
|
|
static void drawWingsOrTail(uint8_t index);
|
|
static uint8_t erasePixelReturnIfSet(uint8_t xLo, uint8_t y);
|
|
static void hideOrShow8Instruments(uint16_t records);
|
|
static void hideOrShowInstrument(void);
|
|
static void initADFPanel(void);
|
|
static void loaderExtension(void);
|
|
static uint8_t loadPixelState(void);
|
|
static void maybeXORPixel(uint8_t toggle, uint8_t xLo, uint8_t y);
|
|
static void negate16To(uint16_t dst, uint16_t value);
|
|
static void populateA5ThruA8(void);
|
|
static void realityModeOff(void);
|
|
static void savePixelState(uint8_t wasSet);
|
|
static bool sceneryComputeBlockOffset(void);
|
|
static bool sceneryEnsureOpen(void);
|
|
static void sceneryLoaderInit(void);
|
|
static void sceneryNibbleDecode(void);
|
|
static bool sceneryReadDecoded(void);
|
|
static bool sceneryReadFixed(void);
|
|
static bool sceneryReadUntilC0(void);
|
|
static void sceneryReopen(void);
|
|
static void setMagnetoState(uint8_t state);
|
|
static void stopEngine(void);
|
|
static void storeADFDigitAndRedraw(uint8_t digit, uint8_t slot);
|
|
static void triggerAircraftProblem(void);
|
|
|
|
|
|
static void atisAdvance(void) {
|
|
fs2Ram[ZP_B8 + 1] = (uint8_t)(fs2Ram[ZP_B8 + 1] + ATIS_COL_STEP);
|
|
}
|
|
|
|
|
|
static void atisAdvanceCursor(void) {
|
|
if (fs2Ram[ZP_B8 + 1] < ATIS_COL_MAX) {
|
|
atisAdvance();
|
|
return;
|
|
}
|
|
uint8_t row = (uint8_t)(fs2Ram[ZP_B8] + ATIS_ROW_STEP + 1);
|
|
if (row >= ATIS_ROW_MAX) {
|
|
chunk4ClearViewportsToBlack();
|
|
row = ATIS_START;
|
|
}
|
|
fs2Ram[ZP_B8] = row;
|
|
fs2Ram[ZP_B8 + 1] = ATIS_START;
|
|
}
|
|
|
|
|
|
// DoChar: draw one character with the inter-character delay.
|
|
static void atisDrawChar(uint8_t c) {
|
|
fs2Ram[ZP_B8 + 2] = c;
|
|
chunk4DrawMessageWhite(ZP_B8);
|
|
chunk3Delay((uint8_t)(0u - fs2Ram[SYM_ATISPacing]));
|
|
atisAdvance();
|
|
}
|
|
|
|
|
|
// IncPtr / IncPtr_TestChar: type the message at ($B6), expanding
|
|
// chunk references (bytes >= $80) with a space either side.
|
|
static void atisRun(void) {
|
|
for (;;) {
|
|
uint16_t ptr = ramRead16(ZP_B6);
|
|
uint8_t c = fs2Ram[ptr];
|
|
ramWrite16(ZP_B6, (uint16_t)(ptr + 1));
|
|
if (c == 0) {
|
|
return;
|
|
}
|
|
if (c & 0x80) {
|
|
uint16_t saved = ramRead16(ZP_B6);
|
|
uint16_t chunk = (uint16_t)(SYM_MessageChunks + fs2Ram[(uint16_t)(SYM_ChunkOffsetTable + (c - 0x80))]);
|
|
ramWrite16(ZP_B6, chunk);
|
|
atisAdvanceCursor();
|
|
atisRun();
|
|
atisAdvanceCursor();
|
|
ramWrite16(ZP_B6, saved);
|
|
continue;
|
|
}
|
|
if (c == ' ') {
|
|
atisAdvanceCursor();
|
|
continue;
|
|
}
|
|
atisDrawChar(c);
|
|
}
|
|
}
|
|
|
|
|
|
// ATo2Digits: value as tens/ones characters (Y is discarded).
|
|
static uint8_t aTo2Digits(uint8_t value, uint8_t *outOnes) {
|
|
uint8_t tens;
|
|
uint8_t ones;
|
|
axTo3Digits(value, &tens, &ones);
|
|
*outOnes = ones;
|
|
return tens;
|
|
}
|
|
|
|
|
|
// AXTo3Digits: Y = hundreds, A = tens, X = ones through TmpStr.
|
|
static uint8_t axTo3Digits(uint16_t value, uint8_t *outTens, uint8_t *outOnes) {
|
|
ramWrite16(SYM_ValueForString, value);
|
|
chunk5Set3DigitString(SYM_TmpStr);
|
|
*outTens = fs2Ram[SYM_TmpStr + 1];
|
|
*outOnes = fs2Ram[SYM_TmpStr + 2];
|
|
return fs2Ram[SYM_TmpStr];
|
|
}
|
|
|
|
|
|
// CalcPixelAddrAndMask: byte pointer ($B8) and bit mask for a pixel.
|
|
static uint16_t calcPixelAddrAndMask(uint8_t xLo, uint8_t y, uint8_t *outMask) {
|
|
uint16_t row = hiresRowAddress(y);
|
|
uint8_t hi = fs2Ram[ZP_A5];
|
|
uint8_t off = fs2Ram[(uint16_t)(SYM_HiresPixelToByteTable + (hi ? 256 : 0) + xLo)];
|
|
uint16_t addr = (uint16_t)(row + off);
|
|
ramWrite16(ZP_B8, addr);
|
|
*outMask = fs2Ram[(uint16_t)(SYM_HiresPixelToBitMaskTable + (hi ? 242 : 0) + xLo)];
|
|
return addr;
|
|
}
|
|
|
|
|
|
void chunk3ADFKeyboardHook(void) {
|
|
uint8_t mode = fs2Ram[SYM_InputMode];
|
|
if (fs2Ram[RAM_ADF_ENGAGED] != 0 && mode >= ADF_MODE_FIRST && mode < ADF_MODE_END) {
|
|
chunk5SetInputModeAndCounter((uint8_t)(mode + 1));
|
|
return;
|
|
}
|
|
chunk5SetInputModeAndCounter(ADF_MODE_FIRST);
|
|
}
|
|
|
|
|
|
// ComputeDayPhase: the minutes compare against row 0, the hours
|
|
// against the season's row (the original indexes only the hour bytes).
|
|
void chunk3ComputeDayPhase(void) {
|
|
uint8_t x = (uint8_t)((fs2Ram[SYM_Season] & 0x03) << 3);
|
|
uint8_t minutes = fs2Ram[SYM_Minutes];
|
|
uint8_t hours = fs2Ram[SYM_Hours];
|
|
uint8_t phase;
|
|
uint16_t t;
|
|
t = (uint16_t)((hours << 8) | minutes) - (uint16_t)((fs2Ram[(uint16_t)(SYM_DayPhaseTable + 1 + x)] << 8) | fs2Ram[SYM_DayPhaseTable]);
|
|
if (t & 0x8000) {
|
|
phase = 4;
|
|
} else {
|
|
t = (uint16_t)((hours << 8) | minutes) - (uint16_t)((fs2Ram[(uint16_t)(SYM_DayPhaseTable + 3 + x)] << 8) | fs2Ram[SYM_DayPhaseTable + 2]);
|
|
if (t & 0x8000) {
|
|
phase = 2;
|
|
} else {
|
|
t = (uint16_t)((hours << 8) | minutes) - (uint16_t)((fs2Ram[(uint16_t)(SYM_DayPhaseTable + 5 + x)] << 8) | fs2Ram[SYM_DayPhaseTable + 4]);
|
|
if (t & 0x8000) {
|
|
phase = 1;
|
|
} else {
|
|
t = (uint16_t)((hours << 8) | minutes) - (uint16_t)((fs2Ram[(uint16_t)(SYM_DayPhaseTable + 7 + x)] << 8) | fs2Ram[SYM_DayPhaseTable + 6]);
|
|
phase = (t & 0x8000) ? 2 : 4;
|
|
}
|
|
}
|
|
}
|
|
fs2Ram[RAM_DAY_PHASE] = phase;
|
|
uint8_t scale = 1;
|
|
uint8_t n = fs2Ram[SYM_Season];
|
|
do {
|
|
fs2Ram[RAM_SEASON_SCALE] = scale;
|
|
scale = (uint8_t)(scale << 1);
|
|
n--;
|
|
} while (n != 0);
|
|
}
|
|
|
|
|
|
void chunk3Delay(uint8_t count) {
|
|
// 255 inner iterations of about 16 cycles per outer step.
|
|
uint32_t steps = (count == 0) ? 256u : count;
|
|
machineDelayMicroseconds(steps * 255u * 16u);
|
|
}
|
|
|
|
|
|
void chunk3DrawATISMessage(void) {
|
|
if (fs2Ram[RAM_ATIS_BUSY] != 0) {
|
|
return;
|
|
}
|
|
fs2Ram[RAM_ATIS_BUSY]++;
|
|
fs2Ram[ZP_B8 + 3] = 0;
|
|
fs2Ram[ZP_B8] = ATIS_START;
|
|
fs2Ram[ZP_B8 + 1] = ATIS_START;
|
|
chunk4ClearViewportsToBlack();
|
|
ramWrite16(ZP_B6, ramRead16(RAM_ATIS_NAME_PTR));
|
|
atisRun();
|
|
}
|
|
|
|
|
|
void chunk3DrawMagnetoStateHook(uint16_t lightsMsg) {
|
|
chunk4DrawMessageWhite(lightsMsg);
|
|
uint8_t x = (uint8_t)(fs2Ram[SYM_MagnetoState] << 1);
|
|
chunk4DrawMessageWhite(ramRead16((uint16_t)(SYM_MagnetoStateMessageTable + x)));
|
|
}
|
|
|
|
|
|
void chunk3DrawSlewOverlays(void) {
|
|
if ((fs2Ram[SYM_ShowSlewDigits] & 0x01) == 0) {
|
|
return;
|
|
}
|
|
ramWrite16(ZP_B6, ramRead16(SYM_NorthPosition));
|
|
chunk4DrawMessage(SYM_msg_north);
|
|
ramWrite16(ZP_B6, ramRead16(SYM_EastPosition));
|
|
chunk4DrawMessage(SYM_msg_east);
|
|
}
|
|
|
|
|
|
void chunk3DrawViewOverlays(void) {
|
|
uint8_t x;
|
|
if (fs2Ram[SYM_RadarView] != 0) {
|
|
x = (uint8_t)(SYM_AirplaneOverlayPixels - SYM_ImageOverlays);
|
|
} else {
|
|
uint8_t dir = fs2Ram[SYM_ViewDirection];
|
|
if ((dir & 0x80) == 0) {
|
|
drawWingsOrTail((uint8_t)(dir << 2));
|
|
return;
|
|
}
|
|
if (fs2Ram[SYM_WW1AceMode] == 0) {
|
|
drawWingsOrTail((uint8_t)(0x10 << 2));
|
|
return;
|
|
}
|
|
x = (uint8_t)(SYM_BombSightOverlayPixels - SYM_ImageOverlays);
|
|
}
|
|
for (;;) {
|
|
uint8_t lo = fs2Ram[(uint16_t)(SYM_ImageOverlays + x)];
|
|
x++;
|
|
uint8_t hi = fs2Ram[(uint16_t)(SYM_ImageOverlays + x)];
|
|
if (hi == 0) {
|
|
return;
|
|
}
|
|
x++;
|
|
uint8_t mask = fs2Ram[(uint16_t)(SYM_ImageOverlays + x)];
|
|
x++;
|
|
uint16_t addr = (uint16_t)(lo | ((uint16_t)hi << 8));
|
|
fs2Ram[addr] |= mask;
|
|
addr = (uint16_t)(lo | ((uint16_t)(uint8_t)(hi + OVERLAY_ALT_PAGE) << 8));
|
|
fs2Ram[addr] |= mask;
|
|
}
|
|
}
|
|
|
|
|
|
uint8_t chunk3HandleCrashOrSplash(uint8_t x) {
|
|
uint8_t code = fs2Ram[RAM_CRASH_CODE];
|
|
uint16_t msg;
|
|
if (code != 0) {
|
|
msg = ramRead16((uint16_t)(SYM_crash_msg_table + (code & 0x06)));
|
|
fs2Ram[RAM_CRASH_CODE] = 0;
|
|
} else {
|
|
if (fs2Ram[SYM_OnGroundFlag] == 0) {
|
|
return x;
|
|
}
|
|
// CheckSplash: the screen byte under the aircraft is water.
|
|
bool splash = (fs2Ram[RAM_WATER_PROBE] == WATER_BYTE) && (fs2Ram[RAM_DAY_PHASE] & 0x01) != 0 && (fs2Ram[SYM_SlewMode] | fs2Ram[SYM_RadarView] | fs2Ram[SYM_ViewDirection]) == 0;
|
|
if (!splash) {
|
|
fs2Ram[SYM_SplashCounter] = 0;
|
|
return x;
|
|
}
|
|
fs2Ram[SYM_SplashCounter]++;
|
|
if (fs2Ram[SYM_SplashCounter] != SPLASH_FRAMES) {
|
|
return x;
|
|
}
|
|
msg = SYM_msg_splash;
|
|
}
|
|
// DrawCrashAndAbort.
|
|
chunk4DrawMessageWhite(msg);
|
|
chunk3Delay(CRASH_DELAY);
|
|
chunk5SetModeLibraryAction(1);
|
|
return 1;
|
|
}
|
|
|
|
|
|
void chunk3HideOrShowInstruments(void) {
|
|
uint8_t a = (uint8_t)(fs2Ram[RAM_INSTR_MASK_A] & fs2Ram[SYM_InstrumentOperationalFlags] & fs2Ram[RAM_LIGHT_A]);
|
|
if (a != fs2Ram[ZP_INSTR_FB]) {
|
|
fs2Ram[0x9E] = a;
|
|
fs2Ram[ZP_A0] = fs2Ram[ZP_INSTR_FB];
|
|
fs2Ram[ZP_INSTR_FB] = a;
|
|
hideOrShow8Instruments(SYM_InstrumentSaveRecords_Group1);
|
|
}
|
|
a = (uint8_t)(fs2Ram[RAM_INSTR_MASK_B] & fs2Ram[RAM_INSTR_FLAG_B] & fs2Ram[RAM_LIGHT_A + 1]);
|
|
if (a != fs2Ram[ZP_INSTR_FC]) {
|
|
fs2Ram[0x9E] = a;
|
|
fs2Ram[ZP_A0] = fs2Ram[ZP_INSTR_FC];
|
|
fs2Ram[ZP_INSTR_FC] = a;
|
|
hideOrShow8Instruments(SYM_InstrumentSaveRecords_Group2);
|
|
}
|
|
a = (uint8_t)(fs2Ram[RAM_INSTR_MASK_C] & fs2Ram[RAM_INSTR_FLAG_C] & fs2Ram[RAM_LIGHT_A + 2]);
|
|
if (a != fs2Ram[ZP_INSTR_FD]) {
|
|
fs2Ram[0x9E] = a;
|
|
fs2Ram[ZP_A0] = fs2Ram[ZP_INSTR_FD];
|
|
fs2Ram[ZP_INSTR_FD] = a;
|
|
hideOrShow8Instruments(SYM_InstrumentSaveRecords_Group3);
|
|
}
|
|
}
|
|
|
|
|
|
uint8_t chunk3KeyDecreasePatch(void) {
|
|
if (fs2Ram[SYM_ADFMode] != 0) {
|
|
uint8_t mode = fs2Ram[SYM_InputMode];
|
|
if (mode >= ADF_MODE_FIRST && mode < ADF_MODE_END) {
|
|
uint8_t slot = (uint8_t)(mode - ADF_MODE_FIRST);
|
|
uint8_t digit = fs2Ram[(uint16_t)(SYM_str_adf_frequency + 1 + slot)];
|
|
if (digit == '0') {
|
|
digit = '9' + 1;
|
|
}
|
|
storeADFDigitAndRedraw((uint8_t)(digit - 1), slot);
|
|
}
|
|
}
|
|
if (fs2Ram[SYM_InputMode] == INPUT_MODE_MAGNETO) {
|
|
uint8_t x = (uint8_t)(fs2Ram[RAM_MIXTURE] + 1);
|
|
if (x != MIXTURE_MAX) {
|
|
chunk4UpdateMixtureControlIndicator(x);
|
|
}
|
|
}
|
|
return fs2Ram[SYM_InputMode];
|
|
}
|
|
|
|
|
|
uint8_t chunk3KeyIncreasePatch(void) {
|
|
if (fs2Ram[SYM_ADFMode] != 0) {
|
|
uint8_t mode = fs2Ram[SYM_InputMode];
|
|
if (mode >= ADF_MODE_FIRST && mode < ADF_MODE_END) {
|
|
uint8_t slot = (uint8_t)(mode - ADF_MODE_FIRST);
|
|
uint8_t digit = fs2Ram[(uint16_t)(SYM_str_adf_frequency + 1 + slot)];
|
|
if (digit == '9') {
|
|
digit = '0' - 1;
|
|
}
|
|
storeADFDigitAndRedraw((uint8_t)(digit + 1), slot);
|
|
}
|
|
}
|
|
if (fs2Ram[SYM_InputMode] == INPUT_MODE_MAGNETO) {
|
|
uint8_t x = (uint8_t)(fs2Ram[RAM_MIXTURE] - 1);
|
|
if ((x & 0x80) == 0) {
|
|
chunk4UpdateMixtureControlIndicator(x);
|
|
}
|
|
}
|
|
return fs2Ram[SYM_InputMode];
|
|
}
|
|
|
|
|
|
void chunk3LookupADFStation(void) {
|
|
if (fs2Ram[RAM_ADF_LOOKUP_LATCH] == 0) {
|
|
return;
|
|
}
|
|
fs2Ram[RAM_ADF_LOOKUP_GATE] = 0;
|
|
uint16_t cur = ramRead16(ZP_STREAM_PTR);
|
|
if (fs2Ram[SYM_ADFFreqLowPacked] != fs2Ram[(uint16_t)(cur + 1)]) {
|
|
return;
|
|
}
|
|
if (fs2Ram[SYM_ADFFreqHighDigit] != fs2Ram[(uint16_t)(cur + 2)]) {
|
|
return;
|
|
}
|
|
fs2Ram[RAM_STATION_SLOT] = 0;
|
|
fs2Ram[RAM_STATION_SLOT + 4] = 0;
|
|
fs2Ram[RAM_STATION_SLOT + 1] = fs2Ram[(uint16_t)(cur + 3)];
|
|
fs2Ram[RAM_STATION_SLOT + 2] = fs2Ram[(uint16_t)(cur + 4)];
|
|
fs2Ram[RAM_STATION_SLOT + 3] = fs2Ram[(uint16_t)(cur + 5)];
|
|
fs2Ram[RAM_STATION_SLOT + 5] = fs2Ram[(uint16_t)(cur + 6)];
|
|
fs2Ram[RAM_STATION_SLOT + 6] = fs2Ram[(uint16_t)(cur + 7)];
|
|
fs2Ram[RAM_STATION_SLOT + 7] = fs2Ram[(uint16_t)(cur + 8)];
|
|
fs2Ram[SYM_ADFStationActive] = 1;
|
|
}
|
|
|
|
|
|
void chunk3RealityModeHook(void) {
|
|
if (fs2Ram[SYM_RealityMode] == 0) {
|
|
realityModeOff();
|
|
return;
|
|
}
|
|
bool carry = false;
|
|
if (fs2Ram[SYM_SlewMode] == 0) {
|
|
// Trim drift toward the auto-trim target.
|
|
uint8_t yoke = fs2Ram[SYM_YokeVertPos];
|
|
int diff = (int)(int8_t)yoke - (int)(int8_t)fs2Ram[SYM_ElevatorTrim];
|
|
if (diff != 0) {
|
|
yoke = (uint8_t)(yoke + ((diff < 0) ? 2 : -2));
|
|
fs2Ram[SYM_YokeVertPos] = yoke;
|
|
}
|
|
carry = chunk5RefreshElevatorIndicator();
|
|
}
|
|
// Aircraft-problem trigger every 10 ticks.
|
|
fs2Ram[RAM_PROBLEM_TIMER]--;
|
|
if (fs2Ram[RAM_PROBLEM_TIMER] & 0x80) {
|
|
fs2Ram[RAM_PROBLEM_TIMER] = PROBLEM_PERIOD;
|
|
uint8_t chance = (uint8_t)(fs2Ram[0x63] + fs2Ram[0x5B] + (carry ? 1 : 0));
|
|
if (chance < PROBLEM_CHANCE_MAX && chance >= fs2Ram[SYM_ReliabilityFactor]) {
|
|
triggerAircraftProblem();
|
|
}
|
|
}
|
|
// Damage accumulator slew toward the target.
|
|
uint16_t damage = ramRead16(RAM_DAMAGE);
|
|
uint16_t diff16 = (uint16_t)(damage - fs2Ram[RAM_DAMAGE_TARGET]);
|
|
if (diff16 & 0x8000) {
|
|
damage = (uint16_t)(damage + DAMAGE_STEP);
|
|
} else if (diff16 != 0) {
|
|
damage = (uint16_t)(damage - DAMAGE_STEP);
|
|
}
|
|
ramWrite16(RAM_DAMAGE, damage);
|
|
// Wear counter += 2.
|
|
ramWrite16(RAM_WEAR, (uint16_t)(ramRead16(RAM_WEAR) + WEAR_STEP));
|
|
}
|
|
|
|
|
|
void chunk3RequestADFStationLookup(void) {
|
|
fs2Ram[SYM_ADFStationActive] = 0;
|
|
fs2Ram[RAM_ADF_LOOKUP_GATE] = 1;
|
|
}
|
|
|
|
|
|
bool chunk3SceneryLoaderEntry(int index) {
|
|
switch (index) {
|
|
case LOADER_ENTRY_READ_UNTIL_C0:
|
|
return sceneryReadUntilC0();
|
|
case LOADER_ENTRY_READ_FIXED:
|
|
return sceneryReadFixed();
|
|
case LOADER_ENTRY_NO_OP:
|
|
return false;
|
|
case LOADER_ENTRY_READ_DECODED:
|
|
return sceneryReadDecoded();
|
|
case LOADER_ENTRY_INIT:
|
|
sceneryLoaderInit();
|
|
return false;
|
|
default:
|
|
// SceneryBuffer1 / SceneryBuffer2 hold no code unless a
|
|
// protected-disk stage loads some; nothing does.
|
|
fprintf(stderr, "scenery loader entry %d has no code\n", index);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
// SceneryRotatedTransform ($03): rotate the record's displacement by
|
|
// the angle and run the chunk3 template with the results filled in.
|
|
void chunk3SceneryRotatedTransform(void) {
|
|
uint16_t cur = ramRead16(ZP_STREAM_PTR);
|
|
uint8_t angle = (uint8_t)(fs2Ram[(uint16_t)(cur + 1)] + fs2Ram[RAM_COURSE_STEP_B]);
|
|
angle = (uint8_t)(angle + fs2Ram[RAM_COURSE_STEP_A]);
|
|
angle = (uint8_t)((uint8_t)(~angle) + ROT_ANGLE_BIAS);
|
|
fs2Ram[ZP_ROT_ANGLE] = angle;
|
|
uint16_t sinV = (uint16_t)chunk4SinByteAngle(angle);
|
|
ramWrite16(ZP_ROT_SIN, sinV);
|
|
uint16_t cosV = (uint16_t)chunk4SinShiftedByteAngle(angle);
|
|
ramWrite16(ZP_BE, cosV);
|
|
ramWrite16(ZP_C4, cosV);
|
|
uint16_t xDisp = ramRead16((uint16_t)(cur + 2));
|
|
ramWrite16(ZP_ROT_XDISP, xDisp);
|
|
ramWrite16(ZP_C2, xDisp);
|
|
uint16_t xCos = (uint16_t)chunk4ScaleC2ByC4();
|
|
ramWrite16(ZP_ROT_XCOS, xCos);
|
|
ramWrite16(RAM_ROT_25A, xCos);
|
|
negate16To(RAM_ROT_25E, xCos);
|
|
ramWrite16(ZP_C4, sinV);
|
|
ramWrite16(ZP_C2, xDisp);
|
|
uint16_t xSin = (uint16_t)chunk4ScaleC2ByC4();
|
|
ramWrite16(ZP_ROT_XSIN, xSin);
|
|
ramWrite16(RAM_ROT_258, xSin);
|
|
negate16To(RAM_ROT_25C, xSin);
|
|
uint16_t yDisp = ramRead16((uint16_t)(cur + 4));
|
|
ramWrite16(ZP_ROT_YDISP, yDisp);
|
|
ramWrite16(ZP_C2, yDisp);
|
|
ramWrite16(ZP_C4, cosV);
|
|
uint16_t yCos = (uint16_t)chunk4ScaleC2ByC4();
|
|
ramWrite16(ZP_ROT_YCOS, yCos);
|
|
ramWrite16(ZP_C2, yDisp);
|
|
ramWrite16(ZP_C4, sinV);
|
|
uint16_t ySin = (uint16_t)chunk4ScaleC2ByC4();
|
|
ramWrite16(ZP_ROT_SIN, ySin);
|
|
uint16_t r244 = (uint16_t)(ySin + xCos);
|
|
ramWrite16(RAM_ROT_244, r244);
|
|
negate16To(RAM_ROT_24E, r244);
|
|
uint16_t r247 = (uint16_t)(xSin + yCos);
|
|
ramWrite16(RAM_ROT_247, r247);
|
|
negate16To(RAM_ROT_251, r247);
|
|
uint16_t r242 = (uint16_t)(xSin - yCos);
|
|
ramWrite16(RAM_ROT_242, r242);
|
|
negate16To(RAM_ROT_24C, r242);
|
|
uint16_t r249 = (uint16_t)(xCos - ySin);
|
|
ramWrite16(RAM_ROT_249, r249);
|
|
negate16To(RAM_ROT_253, r249);
|
|
// Run the template with the cursor saved.
|
|
uint16_t saved = ramRead16(ZP_STREAM_PTR);
|
|
ramWrite16(ZP_STREAM_PTR, RAM_ROT_TEMPLATE);
|
|
fs2Ram[RAM_FILL_COLOR] = 0;
|
|
fs2Ram[RAM_ROTATED_FLAG] = 0;
|
|
sceneryProcessScenery();
|
|
ramWrite16(ZP_STREAM_PTR, saved);
|
|
sceneryAdvance(ROT_RECORD_LEN);
|
|
}
|
|
|
|
|
|
void chunk3Select3DViewPatch(void) {
|
|
if (fs2Ram[SYM_InputMode] == INPUT_MODE_MAGNETO) {
|
|
chunk5ApplyMagnetoState(3, MAGNETO_START);
|
|
return;
|
|
}
|
|
if (fs2Ram[SYM_RadarView] != 0) {
|
|
chunk5StoreRadarViewFromX(1);
|
|
return;
|
|
}
|
|
fs2Ram[SYM_InputMode] = 1;
|
|
}
|
|
|
|
|
|
void chunk3SelectRadarViewPatch(void) {
|
|
if (fs2Ram[SYM_InputMode] == INPUT_MODE_MAGNETO) {
|
|
chunk5ApplyMagnetoState(3, MAGNETO_BOTH);
|
|
return;
|
|
}
|
|
fs2Ram[SYM_InputMode] = 2;
|
|
chunk5StoreRadarViewFromX(2);
|
|
}
|
|
|
|
|
|
void chunk3SetMagnetoFromA(uint8_t state, uint8_t bits) {
|
|
fs2Ram[SYM_InputMode] = 0;
|
|
fs2Ram[SYM_RightMagnetoOn] = (uint8_t)(state >> 1);
|
|
fs2Ram[SYM_LeftMagnetoOn] = (uint8_t)(state & 1);
|
|
setMagnetoState(bits);
|
|
}
|
|
|
|
|
|
// ShowWarReport ($F13E): after a war the tallies and the six enemy
|
|
// states, then wait for a key.
|
|
void chunk3ShowWarReport(void) {
|
|
if (fs2Ram[RAM_WAR_REPORT_DUE] == 0) {
|
|
return;
|
|
}
|
|
fs2Ram[RAM_WAR_REPORT_DUE] = 0;
|
|
fs2Ram[SYM_ValueForString] = fs2Ram[RAM_ENEMIES_SHOT_DOWN];
|
|
fs2Ram[SYM_ValueForString + 1] = 0;
|
|
chunk5Set3DigitString(SYM_str_enemy_shot_down);
|
|
fs2Ram[SYM_ValueForString] = fs2Ram[OVL_WW1_BOMB_HITS];
|
|
fs2Ram[SYM_ValueForString + 1] = 0;
|
|
chunk5Set3DigitString(SYM_str_bomb_hits);
|
|
fs2Ram[SYM_ValueForString] = fs2Ram[RAM_DAMAGE_BY_ENEMY];
|
|
fs2Ram[SYM_ValueForString + 1] = 0;
|
|
chunk5Set3DigitString(SYM_str_damage_by_enemy);
|
|
static const uint16_t kStatus[WAR_REPORT_ENEMIES] = { SYM_str_enemy1_status, SYM_str_enemy2_status, SYM_str_enemy3_status, SYM_str_enemy4_status, SYM_str_enemy5_status, SYM_str_enemy6_status };
|
|
for (int i = 0; i < WAR_REPORT_ENEMIES; i++) {
|
|
fs2Ram[kStatus[i]] = (uint8_t)(fs2Ram[(uint16_t)(OVL_WW1_ENEMY_STATE + i * OVL_WW1_ENEMY_STRIDE)] | DIGIT_ZERO);
|
|
}
|
|
chunk4ClearViewportsToBlack();
|
|
chunk4DrawMultiMessage(SYM_msg_war_report);
|
|
chunk4DrawMessageOrange(SYM_msg_wr2);
|
|
chunk4DrawMessageOrange(SYM_msg_wr3);
|
|
chunk4DrawMessageOrange(SYM_msg_wr4);
|
|
chunk4DrawMessageOrange(SYM_msg_wr5);
|
|
chunk4DrawMessageOrange(SYM_msg_wr6);
|
|
chunk4DrawMessageOrange(SYM_msg_wr7);
|
|
chunk4DrawMessageOrange(SYM_msg_wr8);
|
|
chunk4DrawMessageOrange(SYM_msg_wr9);
|
|
chunk4DrawMessageOrange(SYM_msg_wr10);
|
|
chunk5TogglePause();
|
|
}
|
|
|
|
|
|
void chunk3UpdateADFIndicator(void) {
|
|
if (fs2Ram[SYM_ADFMode] == 0) {
|
|
return;
|
|
}
|
|
bool first = (fs2Ram[SYM_ADFNeedsInit] & 1) != 0;
|
|
fs2Ram[SYM_ADFNeedsInit] >>= 1;
|
|
if (first) {
|
|
initADFPanel();
|
|
}
|
|
if (fs2Ram[SYM_ADFStationActive] == 0) {
|
|
return;
|
|
}
|
|
bool carry = chunk5ComputeStationDelta(0);
|
|
// lda $B6; sbc $70 (carry from ComputeStationDelta); ... the
|
|
// 16-bit deviation minus the heading, plus the course offsets.
|
|
uint16_t d = (uint16_t)(ramRead16(ZP_B6) - ramRead16(ZP_ATT_HEADING) - (carry ? 0 : 1));
|
|
uint8_t lo = (uint8_t)d;
|
|
uint8_t hi = (uint8_t)((d >> 8) + 0x40);
|
|
hi = (uint8_t)(hi + fs2Ram[RAM_COURSE_STEP_B]);
|
|
hi = (uint8_t)(hi + fs2Ram[RAM_COURSE_STEP_A]);
|
|
fs2Ram[ZP_C2] = lo;
|
|
fs2Ram[ZP_BE] = lo;
|
|
uint8_t hiHalf = (uint8_t)(hi >> 1);
|
|
fs2Ram[ZP_C2 + 1] = hiHalf;
|
|
fs2Ram[ZP_BE + 1] = hiHalf;
|
|
fs2Ram[ZP_C2] = (uint8_t)((lo >> 1) | ((hi & 1) << 7));
|
|
fs2Ram[ZP_BE] = (uint8_t)((lo >> 1) | ((lo & 1) << 7));
|
|
uint16_t s = (uint16_t)chunk4ScaleC2ByAX(ADF_SCALE);
|
|
uint8_t angle = (uint8_t)((uint8_t)(~(uint8_t)s) + ADF_SCALE);
|
|
uint8_t last = fs2Ram[SYM_ADFLastDrawnAngle];
|
|
if (last & 0x80) {
|
|
return;
|
|
}
|
|
fs2Ram[SYM_ADFLastDrawnAngle] = angle;
|
|
if (angle == last) {
|
|
return;
|
|
}
|
|
chunk4DrawIndicatorDialNeedle(last, ADF_NEEDLE);
|
|
chunk4DrawIndicatorDialNeedle(fs2Ram[SYM_ADFLastDrawnAngle], ADF_NEEDLE);
|
|
drawADFHeadingDigits();
|
|
}
|
|
|
|
|
|
void chunk3UpdateCOMMessageChunks(void) {
|
|
uint8_t ones;
|
|
uint8_t temp = (uint8_t)(fs2Ram[(uint16_t)(SYM_SeasonTempTable + fs2Ram[SYM_Season])] + fs2Ram[SYM_BaseTemp]);
|
|
fs2Ram[SYM_str_temp_digits] = aTo2Digits(temp, &ones);
|
|
fs2Ram[SYM_str_temp_digits + 1] = ones;
|
|
// Wind direction: (dir * 360) >> 16 as three digits.
|
|
ramWrite16(ZP_C2, DEGREES_360);
|
|
uint16_t p = chunk4MultiplyAXByC2(ramRead16(SYM_WindDirection));
|
|
uint8_t tens;
|
|
uint8_t hundreds = axTo3Digits((uint16_t)((p >> 8) | ((uint16_t)fs2Ram[0xC9] << 8)), &tens, &ones);
|
|
fs2Ram[SYM_str_wind_dir_digits + 1] = tens;
|
|
fs2Ram[SYM_str_wind_dir_digits + 2] = ones;
|
|
if (hundreds == '0') {
|
|
hundreds = ' ';
|
|
}
|
|
fs2Ram[SYM_str_wind_dir_digits] = hundreds;
|
|
uint8_t speedTens = aTo2Digits(fs2Ram[SYM_WindSpeed], &ones);
|
|
if (speedTens == '0') {
|
|
speedTens = ' ';
|
|
}
|
|
fs2Ram[SYM_str_wind_speed_digits] = speedTens;
|
|
fs2Ram[SYM_str_wind_speed_digits + 1] = ones;
|
|
uint8_t hours = (uint8_t)(fs2Ram[SYM_Hours] + fs2Ram[RAM_TIME_ZONE]);
|
|
if (hours >= 24) {
|
|
hours = (uint8_t)(hours - 24);
|
|
}
|
|
fs2Ram[SYM_str_time_digits] = aTo2Digits(hours, &ones);
|
|
fs2Ram[SYM_str_time_digits + 1] = ones;
|
|
// Ceiling: ($0856 * $0866) >> 16, or omitted when zero.
|
|
ramWrite16(ZP_C2, 0x0866);
|
|
p = chunk4MultiplyAXByC2(ramRead16(RAM_CEILING));
|
|
fs2Ram[SYM_str_ceiling_include] = 0;
|
|
uint16_t ceiling = (uint16_t)((p >> 8) | ((uint16_t)fs2Ram[0xC9] << 8));
|
|
if (ceiling != 0) {
|
|
fs2Ram[SYM_str_ceiling_include] = ' ';
|
|
hundreds = axTo3Digits(ceiling, &tens, &ones);
|
|
if (hundreds == '0') {
|
|
hundreds = ' ';
|
|
if (tens == '0') {
|
|
tens = ' ';
|
|
}
|
|
}
|
|
fs2Ram[SYM_str_ceiling_digits] = hundreds;
|
|
fs2Ram[SYM_str_ceiling_digits + 1] = tens;
|
|
fs2Ram[SYM_str_ceiling_digits + 2] = ones;
|
|
}
|
|
uint8_t runway = fs2Ram[(uint16_t)(SYM_RunwaysTable + (fs2Ram[SYM_WindDirection + 1] >> 6))];
|
|
fs2Ram[SYM_str_runway_digits] = aTo2Digits(runway, &ones);
|
|
fs2Ram[SYM_str_runway_digits + 1] = ones;
|
|
}
|
|
|
|
|
|
void chunk3UpdateEngineWithMagneto(void) {
|
|
bool engineOff = false;
|
|
if (fs2Ram[SYM_WW1AceMode] == 0 && fs2Ram[SYM_RealityMode] != 0) {
|
|
if (fs2Ram[RAM_MIXTURE] == 7) {
|
|
engineOff = true;
|
|
} else {
|
|
uint8_t mags = (uint8_t)((fs2Ram[SYM_LeftMagnetoOn] & fs2Ram[RAM_MAG1_OK]) | (fs2Ram[SYM_RightMagnetoOn] & fs2Ram[RAM_MAG2_OK]));
|
|
if (mags == 0) {
|
|
engineOff = true;
|
|
}
|
|
}
|
|
}
|
|
if (!engineOff) {
|
|
// Fuel check on the selected tank.
|
|
if (fs2Ram[RAM_TANK_SELECT] == 0) {
|
|
if (fs2Ram[RAM_FUEL_LEFT + 2] == 0) {
|
|
engineOff = true;
|
|
}
|
|
} else if (fs2Ram[RAM_FUEL_RIGHT + 2] == 0) {
|
|
engineOff = true;
|
|
}
|
|
}
|
|
if (engineOff) {
|
|
stopEngine();
|
|
} else if ((fs2Ram[SYM_RealityMode] | fs2Ram[SYM_EngineOnFlag]) == 0) {
|
|
fs2Ram[SYM_EngineOnFlag] = 1;
|
|
} else if (fs2Ram[SYM_EngineOnFlag] == 0) {
|
|
if (fs2Ram[RAM_AIRSPEED + 1] >= RPM_START_THRESHOLD) {
|
|
fs2Ram[SYM_EngineOnFlag] = 1;
|
|
} else if (fs2Ram[SYM_MagnetoState] == MAGNETO_START) {
|
|
chunk3Delay(SPINUP_DELAY);
|
|
setMagnetoState(MAGNETO_BOTH);
|
|
if (fs2Ram[SYM_Season] != 1 || (fs2Ram[SYM_UpdateCounter] & 0x04) != 0) {
|
|
fs2Ram[SYM_EngineOnFlag] = 1;
|
|
}
|
|
}
|
|
}
|
|
// Phase 1: RPM curve selection.
|
|
uint8_t x = (uint8_t)(fs2Ram[RAM_AIRSPEED + 1] >> 2);
|
|
if (x >= RPM_CURVE_MAX) {
|
|
x = RPM_CURVE_MAX;
|
|
}
|
|
fs2Ram[ZP_BA] = fs2Ram[(uint16_t)(SYM_RPMCurveRun + x)];
|
|
uint8_t y = fs2Ram[RAM_THROTTLE];
|
|
if (fs2Ram[SYM_EngineOnFlag] == 0) {
|
|
y = 0;
|
|
} else if (fs2Ram[RAM_CARB_HEAT] != 0) {
|
|
uint8_t bias = CARB_HEAT_BIAS;
|
|
if (fs2Ram[SYM_EngineSpinupCounter] != 0) {
|
|
bias = SPINUP_BIAS;
|
|
fs2Ram[SYM_EngineSpinupCounter]--;
|
|
}
|
|
y = (uint8_t)(bias + fs2Ram[RAM_THROTTLE]);
|
|
if (y & 0x80) {
|
|
y = 0;
|
|
}
|
|
}
|
|
if ((fs2Ram[RAM_ENGINE_FAULTS] & 0x03) != 0 && y >= MIXTURE_CLAMP) {
|
|
y = MIXTURE_CLAMP;
|
|
}
|
|
if ((fs2Ram[SYM_LeftMagnetoOn] & fs2Ram[RAM_MAG1_OK]) == 0 || (fs2Ram[SYM_RightMagnetoOn] & fs2Ram[RAM_MAG2_OK]) == 0) {
|
|
y = (uint8_t)(y - MAGNETO_DEGRADE);
|
|
if (y & 0x80) {
|
|
y = 0;
|
|
}
|
|
}
|
|
uint8_t rpm = fs2Ram[(uint16_t)(SYM_RPMCurveIdle + (y >> 2))];
|
|
if (fs2Ram[SYM_EngineOnFlag] == 0) {
|
|
rpm = 0;
|
|
}
|
|
fs2Ram[RAM_TARGET_RPM] = rpm;
|
|
uint8_t drag = (rpm >= RPM_DRAG_MIN) ? rpm : 0;
|
|
fs2Ram[ZP_C2 + 1] = drag;
|
|
fs2Ram[ZP_C2] = 0;
|
|
chunk4ScaleC2ByAXIntoC2(PROP_DRAG_SCALE);
|
|
ramWrite16(RAM_PROP_DRAG, (uint16_t)chunk4ScaleC2ByAX(ramRead16(0x09A9)));
|
|
// Cap at the run curve.
|
|
uint8_t cap = fs2Ram[ZP_BA];
|
|
if (((uint8_t)(cap - fs2Ram[RAM_TARGET_RPM]) & 0x80) == 0) {
|
|
fs2Ram[RAM_TARGET_RPM] = cap;
|
|
}
|
|
// Phase 2: idle floor outside reality mode.
|
|
if (fs2Ram[SYM_RealityMode] == 0 && fs2Ram[RAM_TARGET_RPM] < RPM_IDLE_FLOOR) {
|
|
fs2Ram[RAM_TARGET_RPM] = RPM_IDLE_FLOOR;
|
|
}
|
|
// Phase 3 / 4: cylinder head and oil temperature slews.
|
|
if (fs2Ram[ZP_INSTR_FC] & 0x40) {
|
|
uint8_t target = 0;
|
|
if (fs2Ram[SYM_EngineOnFlag] != 0) {
|
|
target = ((fs2Ram[RAM_ENGINE_FAULTS] & 0x01) != 0) ? CHT_FAULT : CHT_ON;
|
|
}
|
|
uint8_t cur = fs2Ram[RAM_CHT];
|
|
if (target != cur) {
|
|
uint16_t frac = (uint16_t)(fs2Ram[SYM_CHTSlewFraction] | ((uint16_t)cur << 8));
|
|
// The sbc has no sec: borrow from the cpx (target < cur).
|
|
frac = (target < cur) ? (uint16_t)(frac - CHT_STEP - 1) : (uint16_t)(frac + CHT_STEP + 1);
|
|
fs2Ram[SYM_CHTSlewFraction] = (uint8_t)frac;
|
|
fs2Ram[RAM_CHT] = (uint8_t)(frac >> 8);
|
|
}
|
|
target = 0;
|
|
if (fs2Ram[SYM_EngineOnFlag] != 0) {
|
|
target = ((fs2Ram[RAM_ENGINE_FAULTS] & 0x01) != 0) ? OIL_FAULT : OIL_ON;
|
|
}
|
|
cur = fs2Ram[RAM_OIL_TEMP];
|
|
if (target != cur) {
|
|
uint16_t frac = (uint16_t)(fs2Ram[SYM_OilTempSlewFraction] | ((uint16_t)cur << 8));
|
|
frac = (target < cur) ? (uint16_t)(frac - OIL_STEP - 1) : (uint16_t)(frac + OIL_STEP + 1);
|
|
fs2Ram[SYM_OilTempSlewFraction] = (uint8_t)frac;
|
|
fs2Ram[RAM_OIL_TEMP] = (uint8_t)(frac >> 8);
|
|
}
|
|
}
|
|
// Phase 5: fuel consumption every 32 ticks.
|
|
if ((fs2Ram[SYM_InputTickCounter] & 0x1F) != 0 || fs2Ram[SYM_EngineOnFlag] == 0) {
|
|
return;
|
|
}
|
|
uint8_t burnIndex = FUEL_BURN_INDEX;
|
|
if (fs2Ram[SYM_WW1AceMode] != 0) {
|
|
burnIndex = (uint8_t)(burnIndex << 2);
|
|
}
|
|
uint16_t burn = chunk4MultiplyXYAndHalve(fs2Ram[RAM_THROTTLE], burnIndex);
|
|
fs2Ram[ZP_A5] = (uint8_t)burn;
|
|
fs2Ram[ZP_A5 + 1] = (uint8_t)(burn >> 8);
|
|
if (fs2Ram[RAM_ENGINE_FAULTS] & 0x04) {
|
|
fs2Ram[RAM_FUEL_LEFT + 2]--;
|
|
if (fs2Ram[RAM_FUEL_LEFT + 2] & 0x80) {
|
|
fs2Ram[RAM_FUEL_LEFT + 2]++;
|
|
}
|
|
}
|
|
if (fs2Ram[RAM_TANK_SELECT] == 0) {
|
|
uint16_t s = (uint16_t)fs2Ram[ZP_A5] + fs2Ram[RAM_FUEL_LEFT];
|
|
fs2Ram[RAM_FUEL_LEFT] = (uint8_t)s;
|
|
if ((burn >> 8) & 0x80) {
|
|
uint16_t s2 = (uint16_t)(burn >> 8) + fs2Ram[RAM_FUEL_LEFT + 1] + (s >> 8);
|
|
fs2Ram[RAM_FUEL_LEFT + 1] = (uint8_t)s2;
|
|
uint16_t s3 = (uint16_t)0xFF + fs2Ram[RAM_FUEL_LEFT + 2] + (s2 >> 8);
|
|
if ((uint8_t)s3 & 0x80) {
|
|
return;
|
|
}
|
|
fs2Ram[RAM_FUEL_LEFT + 2] = (uint8_t)s3;
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
if (fs2Ram[RAM_ENGINE_FAULTS] & 0x08) {
|
|
fs2Ram[RAM_FUEL_RIGHT + 2]--;
|
|
if (fs2Ram[RAM_FUEL_RIGHT + 2] & 0x80) {
|
|
fs2Ram[RAM_FUEL_RIGHT + 2]++;
|
|
}
|
|
}
|
|
if (fs2Ram[RAM_TANK_SELECT] == 0) {
|
|
return;
|
|
}
|
|
uint16_t s = (uint16_t)fs2Ram[ZP_A5] + fs2Ram[RAM_FUEL_RIGHT];
|
|
fs2Ram[RAM_FUEL_RIGHT] = (uint8_t)s;
|
|
if (((burn >> 8) & 0x80) == 0) {
|
|
return;
|
|
}
|
|
uint16_t s2 = (uint16_t)(burn >> 8) + fs2Ram[RAM_FUEL_RIGHT + 1] + (s >> 8);
|
|
fs2Ram[RAM_FUEL_RIGHT + 1] = (uint8_t)s2;
|
|
uint16_t s3 = (uint16_t)0xFF + fs2Ram[RAM_FUEL_RIGHT + 2] + (s2 >> 8);
|
|
if (((uint8_t)s3 & 0x80) == 0) {
|
|
fs2Ram[RAM_FUEL_RIGHT + 2] = (uint8_t)s3;
|
|
}
|
|
}
|
|
|
|
|
|
void chunk3UpdateInstrumentLights(void) {
|
|
if ((fs2Ram[RAM_DAY_PHASE] & 0x01) == 0) {
|
|
fs2Ram[RAM_LIGHT_A] = LIGHT_DIM_A;
|
|
fs2Ram[RAM_LIGHT_A + 1] = LIGHT_DIM_B;
|
|
fs2Ram[RAM_LIGHT_A + 2] = LIGHT_DIM_C;
|
|
if (fs2Ram[SYM_PanelLights] == 0) {
|
|
return;
|
|
}
|
|
}
|
|
fs2Ram[RAM_LIGHT_A] = fs2Ram[RAM_LIGHT_SRC];
|
|
fs2Ram[RAM_LIGHT_A + 1] = fs2Ram[RAM_LIGHT_SRC + 1];
|
|
fs2Ram[RAM_LIGHT_A + 2] = fs2Ram[RAM_LIGHT_SRC + 2];
|
|
}
|
|
|
|
|
|
static void drawADFHeadingDigits(void) {
|
|
fs2Ram[ZP_C2] = fs2Ram[ZP_BE];
|
|
fs2Ram[ZP_C2 + 1] = (uint8_t)((fs2Ram[ZP_BE + 1] + 0x20) & 0x7F);
|
|
ramWrite16(SYM_ValueForString, (uint16_t)chunk4ScaleC2ByAX(DEGREES_360));
|
|
chunk5Set3DigitString(SYM_str_adf_heading);
|
|
chunk4DrawMessageOrange(SYM_msg_adf_heading);
|
|
}
|
|
|
|
|
|
// DrawWingsOrTail: one trapezoid record, painted as spans.
|
|
static void drawWingsOrTail(uint8_t index) {
|
|
fs2Ram[RAM_FILL_COLOR] = WING_COLOR_CODE;
|
|
hiresMapColorAndPrepRowRoutine();
|
|
uint16_t rec = (uint16_t)(SYM_WingOrTailTrapezoids + index);
|
|
fs2Ram[RAM_TRAPEZOID_W] = 0;
|
|
fs2Ram[RAM_TRAPEZOID_X] = 0;
|
|
fs2Ram[RAM_TRAPEZOID_W + 1] = fs2Ram[rec];
|
|
if (fs2Ram[rec] == 0) {
|
|
return;
|
|
}
|
|
ramWrite16(RAM_TRAPEZOID_DW, ramRead16((uint16_t)(rec + 1)));
|
|
fs2Ram[RAM_TRAPEZOID_Y] = fs2Ram[(uint16_t)(rec + 3)];
|
|
fs2Ram[RAM_TRAPEZOID_X + 1] = fs2Ram[(uint16_t)(rec + 4)];
|
|
ramWrite16(RAM_TRAPEZOID_DX, ramRead16((uint16_t)(rec + 5)));
|
|
fs2Ram[RAM_TRAPEZOID_H] = fs2Ram[(uint16_t)(rec + 7)];
|
|
do {
|
|
fs2Ram[0x27] = fs2Ram[RAM_TRAPEZOID_X + 1];
|
|
hiresSetRowPtr(fs2Ram[RAM_TRAPEZOID_Y]);
|
|
hiresDrawColorSpan(fs2Ram[RAM_TRAPEZOID_W + 1]);
|
|
ramWrite16(RAM_TRAPEZOID_X, (uint16_t)(ramRead16(RAM_TRAPEZOID_X) + ramRead16(RAM_TRAPEZOID_DX)));
|
|
ramWrite16(RAM_TRAPEZOID_W, (uint16_t)(ramRead16(RAM_TRAPEZOID_W) + ramRead16(RAM_TRAPEZOID_DW)));
|
|
fs2Ram[RAM_TRAPEZOID_Y]++;
|
|
fs2Ram[RAM_TRAPEZOID_H]--;
|
|
} while (fs2Ram[RAM_TRAPEZOID_H] != 0);
|
|
}
|
|
|
|
|
|
// ErasePixelReturnIfSet: clear the pixel on both pages, return 1 if it
|
|
// was set.
|
|
static uint8_t erasePixelReturnIfSet(uint8_t xLo, uint8_t y) {
|
|
uint8_t mask;
|
|
uint16_t addr = calcPixelAddrAndMask(xLo, y, &mask);
|
|
uint8_t was = ((mask & fs2Ram[addr]) != 0) ? 1 : 0;
|
|
fs2Ram[ZP_A7] = was;
|
|
uint8_t v = (uint8_t)(fs2Ram[addr] & (uint8_t)~mask);
|
|
fs2Ram[addr] = v;
|
|
uint8_t hi = (uint8_t)((addr >> 8) + 0x20);
|
|
if (hi >= 0x60) {
|
|
hi = (uint8_t)(hi - 0x40);
|
|
}
|
|
fs2Ram[ZP_B8 + 1] = hi;
|
|
fs2Ram[(uint16_t)((addr & 0xFF) | ((uint16_t)hi << 8))] = v;
|
|
return was;
|
|
}
|
|
|
|
|
|
static void hideOrShow8Instruments(uint16_t records) {
|
|
ramWrite16(ZP_BC, records);
|
|
fs2Ram[0xA2] = SAVE_RECORDS;
|
|
do {
|
|
fs2Ram[ZP_B6] = (uint8_t)(fs2Ram[ZP_A0] & 0x01);
|
|
fs2Ram[ZP_B7] = (uint8_t)(fs2Ram[0x9E] & 0x01);
|
|
ramWrite16(ZP_B8, ramRead16(ZP_BC));
|
|
hideOrShowInstrument();
|
|
fs2Ram[0x9E] >>= 1;
|
|
fs2Ram[ZP_A0] >>= 1;
|
|
ramWrite16(ZP_BC, (uint16_t)(ramRead16(ZP_BC) + SAVE_RECORD_LEN));
|
|
fs2Ram[0xA2]--;
|
|
} while (fs2Ram[0xA2] != 0);
|
|
}
|
|
|
|
|
|
// HideOrShowInstrument: save (hide) or restore (show) the pixels of one
|
|
// panel rectangle, one bit per pixel in the record's buffer.
|
|
static void hideOrShowInstrument(void) {
|
|
if (fs2Ram[ZP_B7] == fs2Ram[ZP_B6]) {
|
|
return;
|
|
}
|
|
fs2Ram[SYM_HideOrShow] = fs2Ram[ZP_B7];
|
|
uint16_t rec = ramRead16(ZP_B8);
|
|
fs2Ram[RAM_XSTART] = fs2Ram[rec];
|
|
uint8_t packed = fs2Ram[(uint16_t)(rec + 1)];
|
|
fs2Ram[RAM_YSIZE] = (uint8_t)(packed >> 1);
|
|
fs2Ram[RAM_XSTART + 1] = (uint8_t)(packed & 0x01);
|
|
fs2Ram[RAM_YSTART] = fs2Ram[(uint16_t)(rec + 2)];
|
|
fs2Ram[RAM_XSIZE] = fs2Ram[(uint16_t)(rec + 3)];
|
|
ramWrite16(ZP_BA, ramRead16((uint16_t)(rec + 4)));
|
|
fs2Ram[SYM_BitNumber] = 0;
|
|
do {
|
|
fs2Ram[RAM_XCOUNT] = fs2Ram[RAM_XSIZE];
|
|
ramWrite16(RAM_XCOORD, ramRead16(RAM_XSTART));
|
|
do {
|
|
uint8_t y = fs2Ram[RAM_YSTART];
|
|
if (fs2Ram[SYM_HideOrShow] != 0) {
|
|
uint8_t toggle = loadPixelState();
|
|
fs2Ram[ZP_A5] = fs2Ram[RAM_XCOORD + 1];
|
|
maybeXORPixel(toggle, fs2Ram[RAM_XCOORD], y);
|
|
} else {
|
|
fs2Ram[ZP_A5] = fs2Ram[RAM_XCOORD + 1];
|
|
savePixelState(erasePixelReturnIfSet(fs2Ram[RAM_XCOORD], y));
|
|
}
|
|
ramWrite16(RAM_XCOORD, (uint16_t)(ramRead16(RAM_XCOORD) + 1));
|
|
fs2Ram[RAM_XCOUNT]--;
|
|
} while (fs2Ram[RAM_XCOUNT] != 0);
|
|
fs2Ram[RAM_YSTART]++;
|
|
fs2Ram[RAM_YSIZE]--;
|
|
} while (fs2Ram[RAM_YSIZE] != 0);
|
|
if (fs2Ram[SYM_HideOrShow] != 0) {
|
|
return;
|
|
}
|
|
// Pad the last buffer byte to 8 bits.
|
|
uint16_t buf = ramRead16(ZP_BA);
|
|
uint8_t a = fs2Ram[buf];
|
|
for (uint8_t x = (uint8_t)(fs2Ram[SYM_BitNumber] + 1); x != 9; x++) {
|
|
a = (uint8_t)(a << 1);
|
|
}
|
|
fs2Ram[buf] = a;
|
|
}
|
|
|
|
|
|
static void initADFPanel(void) {
|
|
chunk4DrawMultiMessage(SYM_DrawADFPanel);
|
|
fs2Ram[SYM_ADFLastDrawnAngle] = ADF_INIT_ANGLE;
|
|
chunk4DrawIndicatorDialNeedle(ADF_INIT_ANGLE, ADF_NEEDLE);
|
|
chunk4DrawMessageOrange(SYM_msg_adf_heading);
|
|
chunk4DrawMessageOrange(SYM_msg_adf_frequency);
|
|
}
|
|
|
|
|
|
// LD5C8: the loader's self-extension stub (the protected-disk boot
|
|
// decodes real code here; the port's disk never needs it).
|
|
static void loaderExtension(void) {
|
|
}
|
|
|
|
|
|
// LoadPixelState: next saved bit (bit 7 of the buffer byte, which is
|
|
// rotated up with the carry the bit-count compare left).
|
|
static uint8_t loadPixelState(void) {
|
|
fs2Ram[SYM_BitNumber]++;
|
|
bool wrapped = (fs2Ram[SYM_BitNumber] == 9);
|
|
if (wrapped) {
|
|
fs2Ram[SYM_BitNumber] = 1;
|
|
ramWrite16(ZP_BA, (uint16_t)(ramRead16(ZP_BA) + 1));
|
|
}
|
|
uint16_t buf = ramRead16(ZP_BA);
|
|
uint8_t v = fs2Ram[buf];
|
|
uint8_t bit = (uint8_t)(v >> 7);
|
|
fs2Ram[buf] = (uint8_t)((v << 1) | (wrapped ? 1 : 0));
|
|
return bit;
|
|
}
|
|
|
|
|
|
// MaybeXORPixel: toggle the pixel on both pages when `toggle` is set.
|
|
static void maybeXORPixel(uint8_t toggle, uint8_t xLo, uint8_t y) {
|
|
uint8_t mask;
|
|
uint16_t addr = calcPixelAddrAndMask(xLo, y, &mask);
|
|
uint8_t v = fs2Ram[addr];
|
|
if (toggle != 0) {
|
|
v = (uint8_t)(v ^ mask);
|
|
}
|
|
fs2Ram[addr] = v;
|
|
uint8_t hi = (uint8_t)((addr >> 8) + 0x20);
|
|
if (hi >= 0x60) {
|
|
hi = (uint8_t)(hi - 0x40);
|
|
}
|
|
fs2Ram[ZP_B8 + 1] = hi;
|
|
fs2Ram[(uint16_t)((addr & 0xFF) | ((uint16_t)hi << 8))] = v;
|
|
}
|
|
|
|
|
|
static void negate16To(uint16_t dst, uint16_t value) {
|
|
ramWrite16(dst, (uint16_t)(0u - value));
|
|
}
|
|
|
|
|
|
static void populateA5ThruA8(void) {
|
|
chunk4PopulateA5ThruA8From1E03();
|
|
}
|
|
|
|
|
|
static void realityModeOff(void) {
|
|
ramWrite16(RAM_DAMAGE, 0);
|
|
ramWrite16(RAM_WEAR, 0);
|
|
}
|
|
|
|
|
|
// SavePixelState: shift the pixel's bit into the buffer.
|
|
static void savePixelState(uint8_t wasSet) {
|
|
fs2Ram[ZP_A5] = wasSet;
|
|
fs2Ram[SYM_BitNumber]++;
|
|
if (fs2Ram[SYM_BitNumber] == 9) {
|
|
fs2Ram[SYM_BitNumber] = 1;
|
|
ramWrite16(ZP_BA, (uint16_t)(ramRead16(ZP_BA) + 1));
|
|
}
|
|
uint16_t buf = ramRead16(ZP_BA);
|
|
fs2Ram[buf] = (uint8_t)((fs2Ram[buf] << 1) | (wasSet & 1));
|
|
}
|
|
|
|
|
|
// SceneryComputeBlockOffset: false = carry clear (in range, re-opened).
|
|
static bool sceneryComputeBlockOffset(void) {
|
|
uint8_t v = (uint8_t)((fs2Ram[SYM_SceneryLoaderSector] << 1) + LOADER_BLOCK_BIAS);
|
|
if (v >= LOADER_STREAM_END) {
|
|
return true;
|
|
}
|
|
fs2Ram[SYM_SceneryLoaderBlockOffset] = v;
|
|
sceneryReopen();
|
|
return false;
|
|
}
|
|
|
|
|
|
static bool sceneryEnsureOpen(void) {
|
|
if (chunk4ComputeBlockFromSector()) {
|
|
fs2Ram[SYM_SceneryLoaderOpen] = 0;
|
|
return true;
|
|
}
|
|
sceneryReopen();
|
|
return false;
|
|
}
|
|
|
|
|
|
static void sceneryLoaderInit(void) {
|
|
fs2Ram[SYM_SceneryLoaderOpen] = 0;
|
|
fs2Ram[SYM_SceneryLoaderBlockOffset] = LOADER_INIT_OFFSET;
|
|
}
|
|
|
|
|
|
// SceneryNibbleDecode: 6+2 split of the sector into $3B60.., with the
|
|
// EOR checksum at $3B5E/$3B5F.
|
|
static void sceneryNibbleDecode(void) {
|
|
populateA5ThruA8();
|
|
ramWrite16(ZP_A7, RAM_NIBBLE_DEST);
|
|
fs2Ram[ZP_B6] = 0;
|
|
do {
|
|
uint16_t src = ramRead16(ZP_A5);
|
|
uint8_t b = fs2Ram[src];
|
|
fs2Ram[ZP_B6] ^= b;
|
|
uint16_t dst = ramRead16(ZP_A7);
|
|
fs2Ram[dst] = (uint8_t)((b >> 1) | NIBBLE_MASK);
|
|
dst++;
|
|
fs2Ram[dst] = (uint8_t)(b | NIBBLE_MASK);
|
|
dst++;
|
|
ramWrite16(ZP_A7, dst);
|
|
ramWrite16(ZP_A5, (uint16_t)(src + 1));
|
|
} while (fs2Ram[ZP_A7 + 1] < RAM_NIBBLE_END_PAGE);
|
|
fs2Ram[RAM_NIBBLE_CHECKSUM + 1] = (uint8_t)(fs2Ram[ZP_B6] | NIBBLE_MASK);
|
|
fs2Ram[RAM_NIBBLE_CHECKSUM] = (uint8_t)((fs2Ram[ZP_B6] >> 1) | NIBBLE_MASK);
|
|
}
|
|
|
|
|
|
static bool sceneryReadDecoded(void) {
|
|
if (sceneryComputeBlockOffset()) {
|
|
return true;
|
|
}
|
|
sceneryNibbleDecode();
|
|
fs2Ram[SYM_SceneryLoaderOpen] = 0;
|
|
loaderExtension();
|
|
ramWrite16(SYM_SceneryLoaderSector, (uint16_t)(ramRead16(SYM_SceneryLoaderSector) + 1));
|
|
ramWrite16(SYM_SceneryLoaderPtr, ramRead16(ZP_A5));
|
|
return false;
|
|
}
|
|
|
|
|
|
// SceneryReadFixed: four pages from the loader pointer to the staging
|
|
// buffer.
|
|
static bool sceneryReadFixed(void) {
|
|
if (sceneryEnsureOpen()) {
|
|
return true;
|
|
}
|
|
populateA5ThruA8();
|
|
for (int x = 0; x < 4; x++) {
|
|
uint16_t src = ramRead16(ZP_A5);
|
|
uint16_t dst = ramRead16(ZP_A7);
|
|
for (int y = 0; y < 256; y++) {
|
|
fs2Ram[(uint16_t)(dst + y)] = fs2Ram[(uint16_t)(src + y)];
|
|
}
|
|
fs2Ram[ZP_A5 + 1]++;
|
|
fs2Ram[ZP_A7 + 1]++;
|
|
fs2Ram[SYM_SceneryLoaderPtr + 1]++;
|
|
}
|
|
ramWrite16(SYM_SceneryLoaderSector, (uint16_t)(ramRead16(SYM_SceneryLoaderSector) + 1));
|
|
loaderExtension();
|
|
fs2Ram[SYM_SceneryLoaderOpen] = 0;
|
|
return false;
|
|
}
|
|
|
|
|
|
// SceneryReadUntilC0: four pages from the staging buffer to the loader
|
|
// pointer, stopping at $C000.
|
|
static bool sceneryReadUntilC0(void) {
|
|
if (sceneryEnsureOpen()) {
|
|
return true;
|
|
}
|
|
populateA5ThruA8();
|
|
fs2Ram[ZP_A0] = 4;
|
|
uint8_t y = 0;
|
|
for (;;) {
|
|
if ((fs2Ram[ZP_A5 + 1] & 0xF0) == LOADER_ROM_PAGE) {
|
|
break;
|
|
}
|
|
fs2Ram[ramRead16(ZP_A5)] = fs2Ram[ramRead16(ZP_A7)];
|
|
ramWrite16(ZP_A7, (uint16_t)(ramRead16(ZP_A7) + 1));
|
|
ramWrite16(ZP_A5, (uint16_t)(ramRead16(ZP_A5) + 1));
|
|
y++;
|
|
if (y != 0) {
|
|
continue;
|
|
}
|
|
fs2Ram[ZP_A0]--;
|
|
if (fs2Ram[ZP_A0] == 0) {
|
|
break;
|
|
}
|
|
}
|
|
ramWrite16(SYM_SceneryLoaderSector, (uint16_t)(ramRead16(SYM_SceneryLoaderSector) + 1));
|
|
ramWrite16(SYM_SceneryLoaderPtr, ramRead16(ZP_A5));
|
|
return false;
|
|
}
|
|
|
|
|
|
static void sceneryReopen(void) {
|
|
chunk4FetchSectorFromDisk();
|
|
fs2Ram[SYM_SceneryLoaderOpen] = 0xFF;
|
|
}
|
|
|
|
|
|
static void setMagnetoState(uint8_t state) {
|
|
fs2Ram[SYM_MagnetoState] = state;
|
|
chunk5DrawCarbHeatAndLights();
|
|
}
|
|
|
|
|
|
static void stopEngine(void) {
|
|
fs2Ram[SYM_EngineOnFlag] = 0;
|
|
}
|
|
|
|
|
|
static void storeADFDigitAndRedraw(uint8_t digit, uint8_t slot) {
|
|
fs2Ram[(uint16_t)(SYM_str_adf_frequency + 1 + slot)] = digit;
|
|
chunk4DrawMessageOrange(SYM_msg_adf_frequency);
|
|
uint8_t mid = (uint8_t)(fs2Ram[SYM_str_adf_frequency + 2] << 4);
|
|
fs2Ram[ZP_B7] = mid;
|
|
fs2Ram[SYM_ADFFreqLowPacked] = (uint8_t)((fs2Ram[SYM_str_adf_frequency + 3] & 0x0F) | mid);
|
|
fs2Ram[SYM_ADFFreqHighDigit] = (uint8_t)(fs2Ram[SYM_str_adf_frequency + 1] & 0x0F);
|
|
chunk3RequestADFStationLookup();
|
|
}
|
|
|
|
|
|
// TriggerAircraftProblem: announce and pick one of eight failures.
|
|
static void triggerAircraftProblem(void) {
|
|
chunk4ClearViewportsToBlack();
|
|
chunk4DrawMessageOrange(SYM_msg_problem);
|
|
uint8_t x = (uint8_t)((fs2Ram[SYM_UpdateCounter] + fs2Ram[0x5F]) & 0x0E);
|
|
uint16_t proc = ramRead16((uint16_t)(SYM_FailureProcTable + x));
|
|
ramWrite16(ZP_BA, proc);
|
|
switch (proc) {
|
|
case SYM_FailInstrumentBit0:
|
|
fs2Ram[SYM_InstrumentOperationalFlags] &= 0xFE;
|
|
break;
|
|
case SYM_SetEngineFault01:
|
|
fs2Ram[RAM_ENGINE_FAULTS] |= 0x03;
|
|
break;
|
|
case SYM_FailInstrumentBit2:
|
|
fs2Ram[SYM_InstrumentOperationalFlags] &= 0xFB;
|
|
break;
|
|
case SYM_FailInstrumentBit3:
|
|
fs2Ram[SYM_InstrumentOperationalFlags] &= 0xF7;
|
|
break;
|
|
case SYM_SetEngineFault23:
|
|
fs2Ram[RAM_ENGINE_FAULTS] |= 0x0C;
|
|
break;
|
|
case SYM_FailInstrumentBit5:
|
|
fs2Ram[SYM_InstrumentOperationalFlags] &= 0xDF;
|
|
break;
|
|
case SYM_FailInstrumentBit6:
|
|
fs2Ram[SYM_InstrumentOperationalFlags] &= 0xBF;
|
|
break;
|
|
default:
|
|
fs2Ram[SYM_InstrumentOperationalFlags] &= 0x7F;
|
|
break;
|
|
}
|
|
}
|