147 lines
6.7 KiB
C
147 lines
6.7 KiB
C
// Scenery interpreter VM. Drives a stream of opcoded records the
|
|
// same way the original FS2 chunk5 dispatcher does, but writes into
|
|
// the modern framebuffer via the renderer instead of poking hires
|
|
// bytes directly.
|
|
|
|
#ifndef SCENERY_VM_H
|
|
#define SCENERY_VM_H
|
|
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include "renderer.h"
|
|
#include "sceneryProjection.h"
|
|
#include "types.h"
|
|
|
|
#define SCENERY_VERTEX_CAP 64
|
|
|
|
// FS2 cached-vertex pool lives at $0140 in zero-page-extended memory,
|
|
// 8 bytes per vertex slot. Opcodes $31/$32/$33/$35/$42 reference these
|
|
// by 1-byte index from the stream. The pool holds up to 80 vertices
|
|
// (= $140..$540 = 0x400 / 8).
|
|
#define SCENERY_CACHED_POOL_BASE 0x0140
|
|
#define SCENERY_CACHED_POOL_CAP 80
|
|
|
|
// Forward decl so we don't pull in camera.h here.
|
|
struct CameraT;
|
|
struct SceneryStateT;
|
|
|
|
|
|
typedef enum SceneryStationTypeE {
|
|
SCENERY_STATION_ADF = 'A',
|
|
SCENERY_STATION_NAV = 'N',
|
|
SCENERY_STATION_COM = 'C'
|
|
} SceneryStationTypeE;
|
|
|
|
|
|
// Decoded station record passed to the optional callback. `freq` is
|
|
// the raw little-endian word from the record (BCD-packed); `x`/`y` are
|
|
// 24-bit signed scenery coordinates; `z` is 0 for ADF/COM and a 24-bit
|
|
// signed altitude/north for NAV. `name` is the COM record's airport
|
|
// name or NULL.
|
|
typedef struct SceneryStationT {
|
|
SceneryStationTypeE type;
|
|
uint16_t freq;
|
|
int32_t x;
|
|
int32_t y;
|
|
int32_t z;
|
|
const char *name;
|
|
} SceneryStationT;
|
|
|
|
|
|
typedef void (*SceneryStationCbF)(struct SceneryStateT *state, const SceneryStationT *station);
|
|
|
|
|
|
// Working state for a single scenery interpretation pass.
|
|
typedef struct SceneryStateT {
|
|
const uint8_t *stream;
|
|
const uint8_t *cursor;
|
|
const uint8_t *streamEnd;
|
|
// When non-NULL, $1A (WriteWord) and $25 (StoreImmWord) opcodes
|
|
// patch this buffer at the bytecode-supplied target addresses.
|
|
// The full 64K RAM image is treated as one flat address space;
|
|
// chunk5 SceneryOpStoreImmWord stores into $0846/$0848 zero-page
|
|
// slots and similar, all of which live inside the same 64K
|
|
// buffer when we're driving from a RAM dump.
|
|
uint8_t *writableRam;
|
|
// Raw .SD scenery file used by HEADER's demand-load. The file
|
|
// is indexed in 256-byte sectors; chunk5 HEADER stores
|
|
// (sectionId, count) at $08E5/$08E6 and triggers a copy of
|
|
// count*256 bytes from sceneryFile[sectionId*256] to the
|
|
// relocated dest at $08E7/$08E8 -- mirrors chunk5 LA63A.
|
|
const uint8_t *sceneryFile;
|
|
uint32_t sceneryFileSize;
|
|
RenderStateT *renderer;
|
|
const struct CameraT *camera; // NULL for 2D streams
|
|
uint8_t subDepth;
|
|
SceneryStationCbF stationCb; // NULL = ignore station records
|
|
void *userData; // forwarded to stationCb
|
|
SceneryPipelineT pipeline; // 3D vertex / projection state
|
|
// Drawing mode flags toggled by the $1B/$1C opcodes. dayOnlySkip
|
|
// is set by SceneryOpDayOnly when night, suppressing line draws
|
|
// for ground-only objects until SceneryOpModeWhite restores.
|
|
bool dayOnlySkip;
|
|
// Set by main.c (or the time-of-day step) so $1C can decide
|
|
// whether to suppress draws.
|
|
bool isNight;
|
|
// Offline extraction mode: every conditional opcode walks BOTH
|
|
// branches (recursively) instead of evaluating the predicate.
|
|
// The visited[] array bounds the total work. Used by tools that
|
|
// want to extract every reachable polygon / station regardless
|
|
// of the aircraft's runtime position.
|
|
bool walkAllPaths;
|
|
uint8_t visited[200000]; // cycle guard for offline walks
|
|
|
|
// Polygon vertex accumulator. Each $40/$41 (xform-B) emit appends
|
|
// its projected screen coords. The $29 (CopyToD2) opcode triggers
|
|
// rendererFillPolygon over these coords, then resets the buffer.
|
|
// Mirrors chunk5's PrimVert*/SecVert* polygon arrays at $0AF9+
|
|
// that the L7826 scan-line rasterizer fills from.
|
|
int16_t polyXs[64];
|
|
int16_t polyYs[64];
|
|
int polyCount;
|
|
// 3D-vertex accumulator that mirrors chunk5's PrimVerts array
|
|
// BEFORE the 4-pass Sutherland-Hodgman clipping at L6F98.
|
|
// Vertices live in camera-space (post-TransformVertex, pre-
|
|
// PerspectiveDivide). The clipper introduces new vertices at
|
|
// frustum-edge intersections, then projection expands the
|
|
// resulting screen-Y range -- without this 3D-then-clip path
|
|
// a polygon whose vertices all map to a narrow screen-Y range
|
|
// (because their Z's are all similar) collapses to a thin
|
|
// sliver instead of the real wedge shape chunk5 produces.
|
|
// The clipper output lives in a second array (`polyV3DOut`)
|
|
// and we ping-pong between the two per pass.
|
|
SceneryVertexT polyV3D[64];
|
|
SceneryVertexT polyV3DOut[64];
|
|
int polyV3DCount;
|
|
// MAME-patched chunk5 EmitClippedLine ends with RTS, so each
|
|
// $41/$02 op (= line-emit) terminates its parent's
|
|
// SceneryInterpreterStep iteration. SubInvoke ($18) calls JSR
|
|
// $6751 which then RTSes when a line is emitted, returning
|
|
// control to the SubInvoke handler that restores the parent
|
|
// cursor and continues. Mirror this with an exitDispatch flag:
|
|
// setting it tells sceneryRun to stop iterating.
|
|
bool exitDispatch;
|
|
} SceneryStateT;
|
|
|
|
// Initialise an interpreter pointing at the given byte stream. Pass
|
|
// `camera = NULL` for legacy 2D fixture streams.
|
|
void sceneryInit(SceneryStateT *state, const uint8_t *stream, uint32_t length, RenderStateT *renderer);
|
|
|
|
void sceneryAttachCamera(SceneryStateT *state, const struct CameraT *cam);
|
|
|
|
// Install a station-record callback. Pass NULL to clear. Used by the
|
|
// offline scenery dump tool to collect ADF/NAV/COM records without
|
|
// rendering anything.
|
|
void sceneryAttachStationCb(SceneryStateT *state, SceneryStationCbF cb, void *userData);
|
|
|
|
// Run the interpreter until it hits a stream-terminator record.
|
|
void sceneryRun(SceneryStateT *state);
|
|
|
|
// Walk every reachable record from the given entry offset, collecting
|
|
// stations into the provided callback. Used by extractstations as a
|
|
// drop-in replacement for the old hand-rolled walker. The visited
|
|
// array tracks already-walked positions to bound work and let the
|
|
// caller invoke from many entry points cheaply.
|
|
void sceneryWalkFrom(SceneryStateT *state, uint32_t entryOffset);
|
|
|
|
#endif
|