M/L THE MICROLEAGUE PROJECT
← Research overview

SOURCE DOCUMENT · 24 OCTOBER 2026

Crowd animation and randomness

# Crowd animation and outcome randomness

**Conclusion: Timing relationship plausible but unverified.**

The idle display animation reads the same POKEY RANDOM register as the initial outcome selector. Input timing therefore provides a plausible connection. Static analysis does **not** establish that a human-visible crowd pattern predicts the next outcome, or that timing a keypress can improve a player's odds.

This was a small static investigation of the existing saved reference and relevant original bytes. No emulator, gameplay experiment, exploit, patch, new probability model or graphics reconstruction was performed. All existing files and the original ATR are preserved.

## 1. Idle crowd update

The strongly identified crowd/stands animation routine is **$2015–$2054**. It is called directly by the human input-wait loop at **$34F7**, and also during presentation at $2076 and $2A9D. Identification rests on its input-wait call and symmetric writes to the outer edges of the stadium's top display rows; it has not been visually confirmed in Altirra.

$2015 saves A/X/Y, starts X=$25 (row37), and iterates down through row0. $2957 obtains the row pointer from $0680,X/$0740,X; the saved pointer tables give address `$A000 + 40*row`. The material operations are:

| Address | Operation |
|---|---|
| $201F | Read $D20A, then mask to a byte-column offset0..7 |
| $2024–$202B | For rows20..37, skip this row if offset is outside its narrowing edge limit |
| $2031, $2034 | Two separate RANDOM reads, AND their values |
| $2037–$2039 | XOR that mask into screen byte `(row_pointer),Y` |
| $203B–$2041 | Set mirrored byte-column offset to39 minus original offset |
| $2042, $2045 | Two more separate RANDOM reads, AND their values |
| $2048–$204A | XOR that mask into the opposite screen edge |

The row20..37 limits at $2057–$2068 are `[7,7,6,5,4,4,3,3,3,3,2,2,2,2,1,1,1,1]`. Rows0..19 always pass the position test. A complete call therefore performs **118–190 RANDOM reads**, depending on which upper-numbered rows are skipped:38 position reads plus four reads per accepted row. Zero masks can leave a chosen screen byte unchanged.

There is no fixed crowd-frame sequence or frame-number lookup in this routine. It combines hardware samples with the **previous image through XOR**. Its result is deterministic given complete machine/RNG state and timing, but it is not a simple pre-recorded animation. X, $2056 and $94/$95 are local row/offset/pointer workspaces, not a recovered software RNG seed or shared outcome counter.

## 2. It really runs while waiting for a key

Human offensive/defensive menus call $41D8, which calls **$34E5** at $41E9. The extra pitch-choice prompt also calls $34E5 at $40FB.

$34E5 clears the OS key latch **$02FC** to$FF. Its waiting loop:

1. $34F0–$34F4 calls $3549, a small prompt-display inversion routine.
2. **$34F7 calls $2015**, updating the crowd.
3. $34FA–$350B polls $02FC with nested X/Y delay counters: up to90*256 checks before looping back to another crowd update.
4. Once a key is observed, $350D–$3541 translates its scan code through $1D17, records it and returns the character. For menu calls with input-length argument0, an accepted single character returns without another crowd pass.

Thus the crowd is updated synchronously between polling batches. It need not be a separate crowd-specific interrupt or a vertical-blank animation. The examined refresh decision uses software polling counters, not a frame counter or RTCLOK timer. Actual refresh duration depends on instruction timing, data-dependent row skips, DMA and interrupts; no exact wall-clock cadence is claimed. A displayed picture can also span partial screen updates rather than identify one atomic animation frame.

## 3. From the last human choice to the outcome byte

For an ordinary valid defensive choice, the relevant chain is:

```
$34F7 -> $2015                     crowd reads RANDOM
$34FE -> $350D                    observe a key in $02FC
$41E9 return / $41D8 validation    accept menu character
$41B1–$41CD                       store defense B2 and pitch index DA
$41CF -> $4031; $41D2 -> $2B2C    display/cleanup work
$41D5 -> $46DA                    derive combined action B3
$4726 -> $4735 -> $0C00           build thresholds
$4738 -> $0CDA                    sample initial outcome
```

At **$0CDA** the code executes `LDA $D20A`, then compares that one byte against $1D0E..$1D16 and stores class0..9 in $DB at $0CE9. The crowd does not pass its sampled value to this selector. No crowd-image bytes, $2056 offset, or input-delay X/Y count are arithmetic inputs to the threshold/class comparison.

The sample is later than the crowd update and includes intervening input recognition, validation, display work and threshold computation. Invalid keys, extra pitch prompts for positioning, computer decisions and a second human's choices can change the path/delay. In a two-human game, the offensive player's earlier key is not the final input before selection: the defensive player may still wait arbitrarily long.

The direct idle animation stops running once that input routine returns; this is not evidence that the hardware RNG stops. Additional presentation sites can call the same animation elsewhere. This investigation does not claim a cycle-complete trace of all OS, interrupt or display code.

## 4. Shared hardware does not establish a usable cue

POKEY's RANDOM exposes bits of a continuously clocked polynomial generator. Its mode can be9 or17 bits depending on AUDCTL; initialization state also matters. Reading RANDOM samples this stream rather than advancing a software queue by one entry. The generator advances with machine time, including between reads. This hardware behavior is documented in Avery Lee's [Altirra Hardware Reference Manual, chapter5 and RANDOM reference](http[local research path]

Consequently:

* **Yes, the crowd samples random bytes.** It uses the same hardware generator as $0CDA, not a separate identified software PRNG.
* **Yes, timing can change the sampled outcome byte.** Crowd execution itself occupies time, and row skips make its duration sample-dependent. This is a timing mechanism, not proof of favorable statistical bias.
* **No reproducible visible-state-to-outcome mapping has been established.** The picture accumulates XOR changes from multiple masked samples; one visible patch does not directly display the last byte or the full hidden generator state. Full image/history plus precise timing might carry more information, but recovering that was not attempted.
* Human key timing, OS key recognition, display scan phase and intervening work introduce uncertainty about the later sample. Identical-looking crowd patches may occur with different RNG phases. That does not prove zero correlation, nor that all visual history is insufficient in principle.

Under the previous studies' **IID-byte assumption**, conditioning on earlier crowd samples would not change the future byte distribution. Real POKEY output is deterministic and temporally structured; this side quest identifies a reason to test that assumption for timed input, not a reason to replace it with invented probabilities. A deterministic replay at the same CPU cycle is not evidence that a human can use a crowd cue.

## 5. One focused Altirra experiment, if pursued

No experiment was performed. The plausible shared-hardware connection justifies this limited test:

1. Use a disposable working image and a fixed mid-game state with no imminent walk-off. Fix batter, pitcher, runners, outs, thresholds and offense0/one ordinary defensive choice. Test the **last human choice** before $0CDA. For each trial, press a fresh key after the wait routine clears $02FC; do not accidentally test a discarded pre-existing key.
2. Trace $34F7/$2015, $34FE/$350D and $0CDA/$0CE9. Record crowd-region bytes or a screenshot immediately before input, key recognition time, machine-cycle/scan position, the actual byte loaded into A by $0CDA, and resulting $DB. A breakpoint before LDA has not yet captured the value; step/trace the actual read. Keep tracing overhead from adding emulated cycles, or account for it.
3. Sweep input timing while keeping baseball inputs fixed. Repeat across multiple starting hardware phases, not just one restored RNG state. A cycle-identical save-state replay is a determinism check only. Observe effective AUDCTL/SKCTL configuration through emulator chip state or writes; reads at their write-register addresses are not reliable configuration readback. Do not reset/reseed the generator to manufacture repeatability.
4. Predefine one simple visible cue, such as a particular crowd patch changing light/dark, and compare it with randomized-timing controls. Start with several thousand trials per cue group, report effect sizes/confidence intervals and the full256-byte/class distributions, and increase sample size for smaller effects. Split discovery and validation trials to avoid finding a lucky pattern after the fact.
5. Add realistic input/reaction jitter and repeat with new starting phases. An effect confined to exact-cycle scripted input establishes timing sensitivity, not human exploitability. A practical cue needs a reproducible held-out improvement that survives that jitter and varying starting phases.

Do not change game rules, patch the disk or build an exploit for this investigation. If no stable held-out cue survives, stop at “no useful cue demonstrated.”

## Evidence and confidence

The examined byte-backed instruction regions are $2015..$2054, $2957..$2961, $34E5..$3541, $3549..$355D, $40DF..$41D7, $41D8..$4230, $46DA..$473A and $0CDA..$0CEB. The routine's crowd identification is high-confidence static interpretation, not visual confirmation. Shared RANDOM reads and input-loop reachability are established locally; exploitable prediction remains unknown.

Resident address A maps to ATR file offset A+$7C10; arithmetic A maps to A+$2110. Useful checkpoints: $2015 -> file$009C25, $34F7 -> $00B107, $4738 -> $00C348, $0CDA -> $002DEA. Image SHA-256 remains `2f94eb9a5fa06408c46ad24cf5b8d7398de195d7354f5734dea48c03045cca75`. Existing saved indexes were used; the entire disassembly was not restarted. Only this new report was written.