CORNCOB Research archive

Gameplay and engine investigation

The flight engine is substantially mapped: its update loop, all 61 object-handler entries, thirteen eight-quality tuning tables, aircraft force calculation, weapons, damage, enemy state machines and objective checks are identified. The campaign front end also supplies an exact scoring formula and different promotion rules for the normal game and Other Worlds.

Evidence and scope

Unless stated otherwise, addresses are file offsets in installs/deluxe_dnk/3.EXE, the normal-game 3.42 flight engine. DS:xxxx means a data-segment offset, not an executable file offset. The data segment is 1129h relative to the loaded module; its file base is 11490h. Normal near-code pointers become file offsets by adding the 512-byte MZ header. Campaign helpers have different headers and segments, recorded separately.

The explanations below distinguish traced code, execution of isolated original routines, and claims from the supplied manuals. Descriptive variable names are research labels, not recovered assembler symbols. MBUILDER.DOC actually does preserve original symbols and tables; those are identified as such.

520 controlled native cases pass, with eight additional recorded flight-force scenarios. These cover collision boundaries, damage, all quality selectors, aircraft position scaling, weapon restrictions, saucer states, all eight orb objectives, scoring, medal boundaries in both editions, promotion differences, and experimental-aircraft throttle permission. The force scenarios retain original vector transforms, trigonometry and arithmetic; only HUD and sound calls are stubbed. These checks do not amount to a full-game emulator or an exhaustive campaign playthrough.

The newer flight engine's addresses must not be applied to the older engines. The pristine-world catalogue uses the 3.42 rules to describe their object fields; it does not establish that every older executable behaves identically.

Runtime organisation

The top-level frame loop begins at 280c. Much of the player's physics is inline in this loop rather than behind one convenient flight-update function.

Phase Principal code Work
Frame timing and pause/help 280c–2a1c Read timer, clamp elapsed step, apply held T, handle pause/help
View and scenery setup 2a1c–2cc7 Camera transforms, player geometry, ground/sky, stars and sun
Distant-object pass 7ef2, called at 2cc7 Inspect distant records and promote objects becoming near enough
Prepare local census ba36, called at 2e26 Preserve/reset defence and objective counters, update effect state
Particle and moving-object integration 0b17, a187 Lifetimes, velocity, gravity, drag and position
Active world pass 7c8c, called at 2e42 Object handlers, collisions, drawing, walking surfaces and hazards
Portal/autopilot relocation and streaming 217b, 0e5c, 7dc8, 8362 Relocate player; load/offload scenery
Aircraft rotation and forces 31a7–3c0c Control response, damage biases, stall limiter, thrust, drag and lift
Ground contact and movement 3c0c–3fbc Landing, aircraft position, walking, jumping, parachute and remote aircraft
Effects, instruments and input remainder through 4c5a Weapon recharge/fire, booster, views, ejection, status and exit
Finish sortie 4c5a–4de6 Serialize state/results, update world and present outcome

Rendering and simulation are interleaved. Distance sorting, collision eligibility and scenery streaming affect which handlers run. A faithful reconstruction cannot simply draw all objects and update every distant enemy indefinitely.

Object storage and dispatch

A live object occupies 74 bytes. Important recovered fields:

Byte offset Representation Meaning
00,04,08 signed 32-bit World position X/Y/Z
0c,0e,10 16-bit turns Orientation
12 byte Distance tier; ff when detached from lists
13 byte Collision group/instance tag; also overloaded by effects
14 32-bit Squared distance key; scale depends on tier
18 word Live flags, quality and type-specific state
1a byte Rotation/detail-related selector, used by generic integration
1b byte Hit points for damageable scenery/enemies
1c,20,24 three 32-bit values Squared distance thresholds used by drawing/object logic
28,2a,2c three near data pointers Near/middle/far model records
2e,30,32 signed words Velocity components
34 word Lifetime/countdown, age or other type-specific timer
36,38,3a signed words Generic angular increments
3c,3e words Random motion/drag inputs for generic integration
40,41 bytes Gravity and effect/type-specific flags
42,44,46 words Collision half-extents
48 word Object type

The compact world record's four raw parameters expand into the three velocities and timer (7f85–8056). Model pointers are data pointers, not executable callbacks.

The separate code-pointer table is at DS:2c50, indexed by type * 2. A handler entered with carry clear performs ordinary updates; carry set notifies a collision. For player/aircraft contact, DI=ffff is a sentinel rather than another object pointer. Several handlers give this sentinel special treatment. The extended type-properties table at DS:2d44 governs walking, runway, forcefield, capture and other contact rules. It is distinct from live flags.

Live flag meanings are context-dependent, but the common uses include:

Mask Common use
0001 Object allocated/present
0006 State bits, including saucer movement states
0008 Receive object collision callbacks
0010 Run per-frame handler
0020 Participate in player contact checks
0040 Six-hit attacking object
0080 Twenty-five-hit attacking object
0100 Select box-collision path; also damaged-aircraft state
0200 Objective complete, for applicable types
0800 Type-specific special option
7000 Quality index 0–7; builder displays 1–8
8000 Newly allocated record awaiting list insertion

A flag cannot be renamed globally from one example: crew use 1000 for their shot pose, aircraft use the quality bits for variant selection, and saucers and vehicles use low state bits differently.

The main moving pool has 320 slots, starting at DS:8fa6, allocated at 9fac. Guns have a separate 32-slot pool at DS:7732 (08a5). Objects need both zero flags and a detached tier before their slot is reusable. World loading also uses a separate distant record store; the working tile neighbourhood contains four tiles. The original builder documentation's warnings about far-object editing and crowded tile boundaries follow directly from these finite stores.

Clocks and reproducibility

The PIT interrupt at 1732 increments the fast clock and frame counter. Normal programming uses a divisor of 1024; every 64 fast interrupts chains the normal BIOS timer. The game therefore has a fast timer, a BIOS-rate timer and a rendered frame count, rather than one universal tick.

Frame step is fast_frame_ticks >> 3, clamped to 2..35. The common time-scaling denominator at DS:1b7f is 5. Holding T replaces the effective step with 50, while preserving the normal step separately. It is a large simulation step per rendered frame, not a request for one fixed multiplier independent of frame rate.

Many operations multiply by step / 5; others decrement once per frame or use the hardware timers. The five-frame explosion is one example of a frame-based lifetime. Damage and AI also consume the shared random stream. Frame cadence, active objects and execution order consequently matter to reproduction; recording only player input would not guarantee identical outcomes under a different loop.

The assembly PRNG updates a 32-bit state by multiplication with 278dde6dh, wrapping at 32 bits (ea60). The bounded wrapper at ea51 uses the high part of a product. This is separate from the Borland PRNG used to mask pilot saves. It affects gameplay as well as cosmetic effects.

Undocumented controls confirmed in the shipped 3.42 engines

These refer to the DOS originals. The native reconstruction notes describe how those behaviours are carried into the unified executable; deliberate Redux changes are separate from the findings here.

Input Behaviour and scope
Alt-I Enables invulnerability and clears current aircraft damage. Normal 3.42 forces the flag on; Other Worlds toggles it on/off.
Main-row 9 Latches the tester cheat flag. The sortie-exit code then skips marking the result as invulnerable (2000h). This is a result-marker bypass, not proof that every other career restriction disappears.
Hold H with R, G or B Changes the corresponding hallucination/palette counter. The source calls the routine takedrugs. Use from the cockpit: H also has an on-foot handler.
H+1 / H+2 Copies the currently altered sky/ground colours into the base palette and packs them into the world header; normal world saving can persist them in 3UNIV.DAT.
U Other Worlds: starts/stops recording the current flight to 3DEMO.DAT. Normal 3.42 omits this recorder.
L Toggles cockpit/full-view rendering; omitted from the main manual's key lists.
F10 Queues the mission-objective congratulations message. It does not complete an objective or increment the objective counter.

The main-row 9 branch is at normal-engine file 0x45e9..0x45f5 and expansion 0x4af4..0x4b00. Its flags are DS:0ae7 and DS:0bb0. The corresponding result exclusion tests start at 0x4c6a and 0x51da. Isolated execution of both unchanged binaries verifies that an invulnerable sortie sets 2000h without the latch and leaves that bit clear with it. No whole-career scoring claim is implied.

The H key calls the normal palette routine at file 0x227b from 0x4264; Other Worlds calls its equivalent at 0x22c1 from 0x472d. Original keyboard-bit reads and each R/G/B counter update were executed independently in both binaries. With a five-unit step and rate 310, each key changes only its own counter by 310. A private DOSBox run also confirms the visible H+R palette change in Other Worlds.

H+1/H+2 do more than change display registers. In normal 3.42 their setters at file 0x22e1 and 0x22fa both call packbits at 0x643a. This packs the sky and ground RGB components into the five colorbits bytes of the 16-byte world header. writeuniv writes that header to the working world file, 3UNIV.DAT; normal exit writes it when readfileflag is set and no critical error occurred. Resuming that saved world can therefore retain the colours. The shortcuts themselves make no immediate DOS file-write call. They do not rewrite the distributed theatre templates or install a global preference. This persistence path is established by source and native setter disassembly; a separate DOSBox save/reload experiment has not been performed.

A second private DOSBox check presses U twice in Other Worlds. It writes a 530-byte full/delta recording with the original 37-byte terminator. The recording is made in a disposable install, not in the retained source installation. The command-line -b playback path reads 3DEMO.DAT in the current directory; it must accompany the appropriate world state.

The normal engine also retains a developer world-copy operation gated by -e. At file 0x2e4e, it checks DS:0ae5, then the Right Shift release scan b6h, flushes pending tile changes and calls copyuniv (0x84a6). The preserved F3DVEC.ASM copies the current four-tile neighbourhood to eight preset locations: (2,2), (2,4), (2,6), (4,2), (4,6), (6,2), (6,4), (6,6). This changes world object records; it is a construction tool, not an ordinary flight difficulty setting. The dispatch and call are confirmed in the shipped normal executable; the resulting world rewrite has not been exercised in DOSBox.

TOWER's keyboard tables add J/K, U/D, G/Shift-G, several control-key navigation aliases, and F1's boss-screen round trip. Those are reader functions, not a second hidden tower menu. The early sun-face trigger is confirmed in 1.20/2.00, while the later engines retain its geometry without a confirmed selection trigger.

Compile-time variants in the preserved source

These are assembly-time branches, not additional command-line cheats. The preserved KA.INC actually selects slalom=1, guns=0, saucey=0 and normaltet=0. That is not the configuration of the ordinary retained 3.42 executables. The source collection contains historical and experimental paths; this inspection does not establish that every switch combination still builds or that a slalom edition was released.

Slalom and the alternate scenery

The training path in 3.ASM:9850 follows six ordered crossings, numbered 0..5. It takes the high word of X divided by two as the checkpoint index, requires movement toward increasing X, and alternates the required side of the line Y=2*65536 cm. An out-of-sequence crossing, wrong side or ground contact away from home resets the six cockpit progress indicators. The final crossing calls the normal incondest mission-objective routine. No separate race timer is established by this path; it should not be described as a proven timed race.

DEFOBJS.ASM:4861 constructs two rows of six Tet markers at different heights. normaltet=0 adds a long stem to their geometry and slalom enlarges their collision bounds. The same alternate layout places fifty trees and a farm building: type 49's shell geometry becomes a tree (DEFOBJS.ASM:3166), while type 50's saucer geometry is replaced by FARM.INC (DEFOBJS.ASM:3420). The alternate type-51 template is another propeller aircraft. Several handler paths change too: mortar/saucer updates feed radar and nearest-object detection, forcefields omit their ordinary shutdown paths, and the training checkpoint code is replaced by a proximity-warning display when a world file is loaded.

guns=0 independently omits the player's gun, missile and bomb control/update blocks. It is broader than merely hiding the gun sight. slalom also disables the map-builder variant even if mbflag is selected. These observations are from source inspection; no alternate executable has been assembled or play-tested.

The blue ground polygon reuses crtrobj, but slalom sets its span to 16,000 cm and its colour to blue. A pond is the likely visual intent; the retained symbol name identifies the reused geometry, not a documented crater in the course.

For standalone generation, 3.ASM initializes naas, nvest and ndbst to zero. INITPERMS can still place AAA, ground-vehicle and KLA sites when those counts are explicitly supplied, but the ordinary menu's combat-training defaults must not be assumed for this source-only standalone run. The three forcefield placements precede the saucey layout branch and remain part of the course. Finishing the six crossings calls incondest, which queues the ordinary cmpltrmsg congratulations popup and its airstrip/F2 advice; no separate slalom completion text was found in this path.

The training generator also places two coincident objsurv markers at (0,0,0) and three more completing the corners of a 20.97152-kilometre square. These are survey markers, not established world boundaries. survobj is a 20-metre yellow horizontal square with a black cross: the origin marker becomes conspicuous when the usual generator is omitted. It has no generator/objective role.

The slalom farm's bmrtim delegates collision handling to gmdest, which replaces it with the ordinary fuel-dump wreck (gmobj2/gmobj3, type 33) and emits twelve fragments. It does not use the saucer's roll-on-hit or tumbling death behaviour. Slalom's tree type 49 selects donothing instead of shelltim, so hitting a tree must not unpack a concealed enemy. Type 51's slalom bmrstim handles hits and moves the three placed aircraft straight along their fixed attitude with followangles; it does not run the normal enemy-launch countdown. Their placement height is 131,072 cm, and their type-51 property entry is zero, unlike boardable player aircraft. Their precise intended gameplay role is not documented; traffic or a proximity-warning experiment is an inference from these routines.

Other significant switches

Switch Source-level effect
shareflag Shareware/registered feature gates, including helper access, invulnerability toggle behaviour, demo machinery and world-type encoding choices. The makefile has explicit 3s/3r targets.
mbflag Includes the in-game mission-builder machinery; registered target selects it, shareware target omits it. Slalom overrides its map-builder selection.
demover, nodemo, demorec Distinct demo-only, playback-availability and recording branches. A demo-only build omits ordinary configuration/tower/report paths.
qrocket Includes the rechargeable rear rocket booster and its thrust/HUD/input code.
protection Old registration/trial checks and bouncing-ball interruption machinery; disabled in this snapshot. The 2.00 readme explicitly says all protection was removed.
testerflag Includes the main-row 9 tester latch and its invulnerable-result-marker bypass. Enabled in the snapshot and confirmed in both retained 3.42 engines.
MVPCD CD packaging path: separate writable-directory prefix for generated files, and changed argument parsing.
getunivbin Includes UNIVBIN.DAT object importing/merging into the saved world.
vgmode Legacy 640x350 and 320x200 colour and Hercules-related rendering branches. Their presence does not establish a working alternative build on modern or original hardware.
normaltet, saucey Alternative Tet geometry and alternative procedural training/scenery layouts.
soundflag, fadeflag Sound-code and fade branches; sound is enabled when soundflag=0.
simpflag Includes the beginner-aerodynamics pitch-assistance branch.
avgthrot Smooths the engine sound's frequency value, rather than smoothing physical throttle input.
stestflag Replaces normal startup flow with an interactive sound test.
larryflag Startup test-point messages addressed to Larry; the separate ADL module's setting suppresses hardware port writes.
printflag, diagflag, jdiag, polydiag, copydiag, chopchop, finalscreen Assorted state dumps, joystick/renderer/copy diagnostics, test paths and end-of-run reporting.

The protection code is unusually personal. DEFOBJS.ASM:5443 has a hotshot branch described as detecting players too good for a trial. It checks roll and attack-evasion progress, then branches to hosehim, with the comment "hose this guy, he's too good!". That changes a survey object's collision-radius field to -2. The later collision path explicitly treats negative radii as protection signals: -1 sets critical error 97, -2 sets 98, and a negative radius on a type other than survey marker 31 sets error 59. It also forces the ejected flag. The main loop exits on a nonzero critical error and skips the normal world save.

Eight isolated native trap checks confirm these outcomes in both 1.20 and normal 3.42. The 1.20 code contains both negative-radius producers (0xc8d9, 0xc910); normal 3.42 contains neither producer even though the trap consumer remains. These checks execute the unchanged trap prefix only, not a complete trial-expiry session or all front-end handling.

Recovered resources and runnable experiments

The model browser now includes the source-only farm, tree, unused foliage-only tree helper, tall Tet marker and large blue crater. They appear beside normal models and under the source variants edition. tools/extract_source_models.py reads the original DW/EQU/DUP data, preserving all eight painter lists; it does not build or execute source instructions. Its normalized data addresses start at 0xf800 and are not historical executable offsets. The six stored detail models occupy 935 bytes. Source-file hashes are recorded in their metadata.

The C++ reconstruction bundles these data and provides --slalom. It launches the recovered training-layout/checkpoint experiment with player weapons disabled, six progress cells and local objective completion; it does not save this run into pilot careers. The selected slalom aircraft reuse the ordinary aircraft's stored middle/far models, rather than adding another hidden aircraft mesh. Their placements use Z=131072 cm and zero angles: STRTPERM copies the nine KBUF words over the template, including the high word of Z and all three angles. The tree object uses its full model at near/middle detail and its line-trunk helper at far detail; the foliage-only helper is stored but unreferenced by that template.

--graphics-mode=ega-low and --graphics-mode=hercules make the source's 320x200 colour and 720x348 monochrome scene profiles available alongside default 640x350 EGA. World projection/rasterization runs at the selected resolution. Hercules uses the recovered BKCLRS fill masks and HGCLINE solid-line mapping. The port adapts the preserved cockpit/instruments and retains the original helper display modes: there are no verified alternative cockpit bitmaps, some source helpers assume 640 pixels, and the alternate branches omit the usual full-view definitions. These are source-based reconstructions with documented adaptations, not pixel comparisons against a retained released alternate build.

Was a Hercules edition released?

Publication remains unconfirmed after the 17 October 2026 web/archive search. No identified Hercules distribution, contemporary announcement or authenticated Hercules screenshot was found. This is a negative search result, not proof that no such build ever circulated.

The source evidence is stronger than an unused resolution constant: 3M.LNK and 3DEMO.LNK explicitly link hgcpixad and hgcline. Together with the CRTC setup, raster routines and 720x348 projection branch, these establish intended Hercules rendering support. The linker inputs do not prove that a complete Hercules executable was built or distributed; the snapshot also contains incomplete alternative paths and lacks verified alternate cockpit art.

Evidence from released versions points to VGA as the supported public target. The retained 1.20 CORNCOB.DOC says “Corncob absolutely requires a VGA adapter” and separately mentions that a mono-VGA system may or may not work. Its FAQ explains the dependency on VGA write mode 3 and palette effects. A monochrome VGA monitor is not a Hercules adapter. Later retained manuals require colour VGA. The June 1993 Brisbug catalogue lists version 2.0 as VGA: contemporary newsletter.

The working hypothesis is an experimental or older development configuration, possibly predating the documented releases. Its age and publication history cannot currently be established. The C++ Hercules option is a reconstruction from those source paths, not evidence of a recovered historical executable.

MVP CD packaging switch

MVPCD is set to zero in the snapshot. Its branches in 3.ASM and CMDLINE.ASM redirect generated files through a writable-directory prefix, instead of trying to write beside an executable on read-only media. A CD-enabled build accepts -p<path> (also the -z dispatch alias) and prefixes 3.CMD, 3.RES, 3UNIV.DAT, 3D.CFG and 3DEMO.DAT; helper invocations also receive the path. It suppresses the ordinary registered-copy banner. The branch does not introduce CD audio, new models or a separate campaign-content loader.

The name and publisher identify this as MVP Software CD packaging, but no retained disc executable has been compared. CD distribution itself is documented: the Centre for Computing History's physical 1993 MVP Software's Game Jamboree contents transcription lists Corncob Other Worlds Campaign. That establishes a CD compilation appearance; it does not establish an enhanced CD edition or which source configuration that particular disc used.

Early protection behaviour

The 1.20 registration help offers a $15 machine-bound economy reply code or a $25 supplied unprotected edition with the builder. Its menu executable contains trial-expiry, incorrect-reply-code and incorrect-system-date messages. These establish the trial/registration system independently of the later source's protection condition.

The protected source's welcomewagon interrupts play with IMG help/welcome pages: the first immediately, others after a delay, an airspeed or altitude threshold, and later fifteen-second intervals. bounceimg silences effects and makes the player wait for a bouncing ball before Space resumes. A hidden Enter + keypad 5

The hotshot branch can end a trial flight based on roll/attack-evasion progress, not merely elapsed calendar time. It deliberately reaches the error-98/ejection trap described above. The normal critical-error path bypasses world saving; this is not a hidden reward or a more difficult enemy. The 2.00 readme explicitly announces removal of all protection. None of this enforcement is enabled in the C++ reconstruction.

Cockpit frame and airflow indicator

In the unchanged deluxe 3.42 3.EXE, EPAGE (file offsets 0x561f..0x570e) subtracts one byte from the main window's first-byte offset, then adds bpl-1 to select the right strip. At 80 bytes per row this is column 79, pixels 632–639; using byte 78 incorrectly lets the scenery overwrite the frame.

DRAWMDS (0xcd51..0xcdd2, through its DRAWDisc call) computes a six-pixel airflow indicator using body velocity and passes win0, the radar clipping rectangle (496,259)–(615,327). At zero velocity the computed centre is (638,243), outside this rectangle: the original writes no pixels. Drawing it against the whole screen instead causes a stray red dot beside the cockpit. Forward velocity (1000,0,0) gives a visible ball at (559,298); lateral and vertical samples independently confirm clipping at the right and top.

tools/verify_cockpit.py reproduces these findings in isolated original routines without stubs, and retains the source hash, addresses and results in analysis/gameplay/cockpit-checks.json. VGA memory writes establish pixel coverage; this probe does not emulate four-plane colour composition or a complete frame.

Reproduce the investigation

From the repository root, using the same Python environment as resource extraction:

python3 tools/analyse_gameplay.py
python3 tools/verify_gameplay.py
python3 tools/catalogue_mission_rules.py
python3 tools/extract_source_models.py
python3 tools/verify_source_switches.py
python3 tools/verify_cockpit.py

The first tool needs Capstone and the existing extraction dependencies. Native checks additionally require Unicorn. An arbitrary extra range can be inspected without rewriting reports:

python3 tools/analyse_gameplay.py --show 0x37d0:0x3c0c

Original installations are read-only inputs. The generated reports contain source hashes and the native checks state their stubs and limits.

Scope and subsequent reconstruction

This report records the initial disassembly and native probes. The later C++ reconstruction adds independent native comparisons for fast-projectile contacts, landing/suspension, autopilot feedback, actor updates and campaign transitions. Those are no longer unimplemented items merely because this earlier report describes a smaller set of checks.

The limits of the evidence still matter. We have not compared every gameplay branch across all seven flight binaries, nor established whole-session equivalence against long DOS recordings with identical random-number consumption. A routine matching its native fixtures does not by itself prove every possible interaction in a complete campaign. The reconstruction notes list the coverage and remaining qualifications for that work.