Loading listing…
1988 · Electronic Arts
Every routine, table and variable the game uses, with the names and descriptions given to them. Click an operand to follow it; the counter beside a label lists what refers to it. The reference below the fold is rendered from the game's facts file.
This game is in 64 parts, and the same addresses hold something else in each. Each part has a listing of its own, chosen from the list beside it.
The game is loaded over Resident engine and loader. The listing shows them together, as the machine holds them; the rows of this part are marked.
The program the party plays in: the main loop and the menu commands, movement and the map window's scroll, the clock, the handlers of the square classes, encounters and the fight, damage and healing, parties, hiring, loot and the game's own text. The start-up, the radio and the Ranger Center load it from side 1, track 31, logical sector 9, to $7E00; its header asks for 72 pages, $7E00-$C5FF. The item table at $3100-$33FF belongs to this part too. What spans the parts (the memory layout, the saves, the map record's header, the square classes in brief, the menu bar and its keys) is in the game's facts.md and is not repeated here. Addresses are this part's own unless a part is named. The code was read in parts/game/work/play-map.vsf; live marks what was observed in the emulator, and runs on the kit's 6502 simulator are named as such.
$7E00 is JMP $7E36, then the load address $7E00 and the page count $48, 72 ($7E03-$7E05).$8296-$829E), the radio (radio $8190-$81A1) and the Ranger Center's Start (ranger $7EEE-$7F00).$7E06-$7E35 is the hit list of a member's ranged attack, cleared by each one ($9FB2-$9FC7): three target lines at $7E06 ($FF ends them), kill counts at $7E09, damage words at $7E0C, ten hit counters an entry at $7E12, overkill flags at $7E30 and the weapon class of each entry's last kill at $7E33 (or 1 when that monster's flag byte was already 0, damage_monster $A704-$A70A).| Range | What |
|---|---|
$7E36-$7F63 | start, main loop, menu tables, message printers |
$7F64-$82B4 | melee |
$82B5-$82DF | the self-destruct countdown |
$82E0-$8346 | radiation (class 9) and class 4 squares |
$8347-$83D6 | line of sight |
$83D7-$8801 | the monster groups' moves |
$8802-$8838 | Radio and Save |
$8839-$8A76 | classes 6, 12, 8 and 10, the exit question |
$8A77-$8DEB | Use and healing |
$8DEC-$915D | check squares (class 2) |
$915E-$9193 | walking into walls and objects |
$9194-$936D | loot (class 5) |
$936E-$98F4 | placing and finding encounters |
$98F5-$9C64 | parties: split, merge, Disband, View |
$9C65-$9EFC | the monsters' fire, monster records |
$9EFD-$A263 | orders of a round, members' ranged attacks |
$A264-$A3F0 | order handlers, runs and party moves |
$A3F1-$A583 | Hire and its helpers |
$A584-$A98B | hit roll, range, damage, damage to members |
$A98C-$AC68 | automatic fire, kills, combat phrases, class tests |
$AC69-$B014 | square dispatch, moves, the view, the clock |
$B015-$B104 | random encounters |
$B105-$B3FF | square helpers, the clock line, the fight |
$B400-$B794 | combat orders and the attack list |
$B795-$B866 | health |
$B867-$BCBF | the battle round, group records |
$BCC0-$BE25 | order tables, map messages, arithmetic, class 1 |
$BE26-$BF32 | the map window's scroll |
$BF33-$BF5E | Order |
$BF5F-$C5B2 | the text block |
$C5B3-$C5FF | leftover bytes (below, "Code nothing reaches") |
$26BD, save_item_table $26D6). Its first eight bytes, 60 60 60 00 37 08 F8 39, are read by nothing; the 95 records of eight bytes follow, item n at $3108 + 8*n* (engine item_record $0BCD).$7E36-$7E62) points the engine's menu texts $3E/$3F at $7F3C, one word for the bar's text at $5BAB, and its handler tables $40/$41 at $7F3E, one word for $7F18; sets $91 to $FF, so that enter_map loads the map; sets the bar's state ($39 and $3B 0, $3A 6); draws the bar (engine $02E7), enters the map (engine $03B6) and draws the view (redraw_view $AC8C). When $92 is set (the start-up sets it to 1, and the utilities' Start loads the game with that 1 still set, utils $8213-$8226; the Ranger Center's Start and the radio set it to 0, radio $8194) it runs cmd_stay $AE21 once, which moves the clock a step and runs the party's square ($7E5C-$7E60).$7E63-$7F14), every pass: the Geiger counter ($7E63-$7E84), the idle count ($7E87-$7E9E), a key ($7EA1-$7ECF), the countdown (countdown_check $82B5), the end test ($7ED5-$7EFC), and View when nobody in the current party is conscious ($7F0C-$7F11). It makes 20.02 passes a frame with no radiation in view (live, the game's facts, "Live tests").$B4 counts passes and $B5 its carries. When $B5 AND $3F comes to 0 the party takes a turn standing still: try_move_party with direction 4 ($ADD1), random_encounter ($B015), draw_view and encounter_if_engaged ($7E93-$7E9E). A key clears $B5 (engine read_key, through $022A), so the first idle turn comes 16,129 to 16,384 passes after the last key and the next ones every 16,384. Live: three idle turns in 3,000 frames, four minutes each on the world map (the game's facts, "Live tests").$AE0A) but does not run the square's action, as no after_move follows. Direction 4 still puts the party's own square through square_blocked ($AD5B), so on an exit square whose record has bit 6 an idle turn asks "Enter new location (Y/N)?" (exit_confirm $8A36).1 to 7 ($B1-$B7), up to the party size $07, show that member's character screen (view_member_cmd $AC6C: engine view_character, redraw_view) and then fall into after_move ($AC72), which runs the action of the party's square again ($7EA4-$7EB7).$8AA2): the engine's bytes at $5BA3 + member hold 0, $F1, $F3, $F5, $F7, 0, 0, 0, and the search runs only for a key other than 0 ($7EBA-$7ECF). Any other key goes to the engine's menu_key (engine $02DE).$0A = 0) the game is over at once; otherwise records 1 to $0A, of every party, are tested, and one that is conscious, or UNC (condition_index 0) with +$28 = 0, keeps the game going. With none, the program asks for side 1 and loads T4/L2, the death part ($7EFE-$7F09). The test matches the healing: a UNC character with +$28 = 0 gains a point on every health tick, and one with +$28 set never does (con_heal_one $B854).$7F11). View looks for the next party it can show (view_party $9BBA wants a conscious member); each time the search comes round to the party it started from it advances the clock a step and prints it ($9BAE-$9BB1), so time runs until someone wakes ($9B8C-$9BB7).$9F is the distance, in tenths of a square, from the party to the nearest radiation square drawn. redraw_special_squares sets it to $FF and redraws the view's squares of classes 4, 5 and 9 ($AF7E); the engine's draw_square lowers it for a class 9 square before it chooses that square's tile by the hour (engine $0B74-$0B8A), so the counter clicks by day as well, when those squares show as plain ground.$9F to $FF when no member 1 to $07 carries item 48, the Geiger counter ($B176-$B192, engine find_item): carried is enough, it need not be equipped.$9F is not $FF, each pass takes the divider $7F17 down one; at 0 it is set back to 5 and the countdown, the operand at $7E6A, goes down one, and at 0 the click sounds (engine play_sound_16, through $04D2). The next pass reloads the countdown with a random 1 to $9F + 1 ($7E69-$7E72). So clicks come 5*c* passes apart, c drawn from 1 to $9F + 1: 2.5 × ($9F + 2) passes on average for evenly drawn numbers.$9F held, with CIA 1's timer A running as the snapshot has it (latch $4025, continuous): a click every 10.0, 30.2, 40.2, 54.9, 127.6 and 241.9 passes for $9F = 2, 10, 14, 20, 50 and 98 (the rule: 10, 30, 40, 55, 130, 250), about 76, 33, 25, 19, 8 and 4 a second at the simulator's pace. With the timer left reading 0 the clicks at 50 and 98 came every 114 and 215 passes, as the engine's random_byte adds in the timer's low byte (engine $24E3). The simulator gives the video chip no cycles, and makes 21.19 passes a frame with $9F = $FF and no idle turn where the machine makes 20.02 (live, above); the clicks themselves slow the loop, to 15.2 passes a frame at $9F = 2.$7F18: Use $8A77, Enc $B1A3, Order $BF33, Disband $99EE, View $9B8C, Save $8820, Radio $8802, then the hidden keys I $AE19, K $AE84, J $AE46, L $AE65, back-arrow $AE21 (cmd_stay) and space $7F32 (the roster shown or hidden).$8833: engine save_game, through $02D8, then save_map, through $03B9), asks for side 1 and loads T26/L17, the radio program ($880A-$881A). N redraws.$8820-$8830).$0505 to $FF, reads one page from T34/L15 to $CA00 and jumps there, with no side check ($BF33-$BF5C; the order part's facts). N redraws.$7F50: word-wrapped output, then the game's text block), from 88 JSR and JMP instructions here and from order $CA26; game_msg_direct_at $7F40 and game_msg_direct $7F44 print with direct output ($B8E6, $92B9; $931A). select_game_text ($7F5B) points $43/$44 at $BF5F.$ADBF): 0 north, 1 south, 2 west, 3 east, 4 none, 5 north-west, 6 north-east, 7 south-west, 8 south-east.$AD5B) refuses a step off the map, onto class 15 or 3 (an encounter), or when the next three tests refuse it: blocking_check_square ($8DEC, check squares), exit_confirm ($8A36) and bump_blocking_square ($915E).$915E-$9193).$ADD1) tests the step and calls move_party ($ADDF), which refuses (carry set) when nobody is conscious. Outside a fight ($D8 = 0) it redraws the party's square without the marker and scrolls the window; in a fight only the view's corner $02/$03 moves. Then party_square and advance_clock ($AE07-$AE0A).$AE26): the step's sound (engine play_sound_16, through $04D2), random_encounter, after_move. cmd_stay (back-arrow) runs step_done whether or not the move was allowed ($AE21-$AE2C). Live: each step that moved called step_done once, and a refused one none (the game's facts, "Live tests").$AC72): party_square, redraw_special_squares, the markers ($ACB5: the other parties $AD13, the party $AF1D, the clock and the Geiger check $B143, engine draw_geiger_gauge), merge_parties_here ($9959); then, when someone is conscious, run_square_action ($ACC1), restore_view_if_covered ($B10C) and encounter_if_engaged ($B197).$54, $59, $5A). The scroll ($BE26-$BF32) moves the bitmap and the colours through the engine's row copier at engine $DD80, which it patches ($BECC) with all RAM switched in (engine $04FC, $04FF): left for a step east ($BE26), right for a step west ($BE44), up for a step south ($BE74, rows 3-15 to 1-13), down for a step north ($BE90, rows 2-14 to 4-16). The new squares are drawn after it: rows 1 and 0 going north, rows 7 and 8 going south, columns 1 and 0 going west, columns 17 and 18 going east ($AE2F-$AEA2).$8347), for the encounter scan ($95A9, $97AD) and the attack list ($B6D1), walks from the party towards a square, patching INC/DEC instructions for the direction, and fails at the first class 11 square on the way, the party's own square included; the end square itself is not tested.$AF32-$AF7B) runs for every step and adds the map header's step cost (the game's facts, "The maps"): +$34 to the fraction $0C, +$35 and the carry to the minutes $0D (60 or more: less 60, and the hour $0E goes up one, 24 wrapping to 0), and +$36 to $D2 and to the 24-bit count $04-$06, whose carry goes on into $F4FD. Its callers: move_party ($AE0A), View ($9BAE) and the end of each battle round ($B393).$D2 runs at 4 units a minute everywhere but maps 38 and 39 (2) and map 40 (8).$B795) runs when a step leaves $D2's low nibble at 0 ($AF75-$AF7B). enter_map clears $D2 (engine $25E1-$25E3), so the tick comes 16 units after entering a map and every 16 units after that: every 4 minutes on 39 maps, which on the world map is every step, every 8 minutes on maps 38 and 39 and every 2 minutes on map 40.$B393, $B396), so a round brings one tick, or two when the round's step brings $D2's low nibble to 0, which on map 0 is every round.$82B5-$82DF): once $F4F9 is set (map-20 $406B sets it and keeps the count $04-$06 in $F4FA-$F4FC), every pass of the main loop subtracts the kept count from the present one, 24 bits; at 240 or more it saves the map (engine save_map, through $03B9) and runs action $84, module 4 (run_action $8845). No map is tested: 240 units are an hour on most maps, two hours on maps 38 and 39 and half an hour on map 40.$ACF3: classes 0, 3, 7, 11 and 13-15 $AE0D (nothing), 1 $BDE9, 2 $8E14, 4 $833D, 5 $9194, 6 $8839, 8 $88E3, 9 $82E0, 10 $89A3, 12 $8878. Encounters (3, 15) and walls (11) are dealt with elsewhere: by the scan ($94CC) and by bump_blocking_square.$ACC1) runs the handler of the party's square, again for as long as it returns carry set (most end in alter_party_square $B105, carry set when the square changed), then sets $02AA to 0 and keeps the square's class and number in $C6/$C7 ($B138). A class 2 square is skipped unless its flags have bit 7, but that test comes once, before the loop ($ACC4-$ACCC): when a handler turns the party's square into a class 2 square without bit 7, the loop runs its checks too.$BDE9): the map messages listed in the record, the last with bit 7, unless the party's square has the class and number that $B138 kept in $C6/$C7 when the last square action ended, so after a step a neighbouring square with the same pair shows nothing ($8CC3 sets $C6 to 0 when a Use moves the party); then the party's square changes by the pair after the list.$833D): the map message at +0 (when not 0), then the change at +2/+3.$8839): run_action ($8845) on record byte 0 — $80 + n unpacks module n to $CA00 (engine $03BF) and calls it, a smaller number calls word n of the map's list 16 through $AC69 with $5F/$60 still on the record, and carry set on return redraws the view — then the change at +1/+2. The modules are 0 the doctor, 1 the shop, 2 the library, 3 the way into the Ranger Center and 4 the destruction of Base Cochise (the module parts' facts).$8878): the map message at +0, then five-byte entries from +1 (flags, column, row, class and number; flags bit 0: column and row relative to the party, bit 7: the last entry) applied with $AD/$AE kept around them; the view is redrawn unless the last entry's flags have bit 6; then the party's own square changes by the pair after the entries.$88E3), a question: the map message at +0 AND $7F. With bit 7 the answer is one key (RETURN or back-arrow: no answer); without it, a line of up to 16 characters typed after ">" into $5A00 (empty: no answer). Each answer message from +3 (the last with bit 7) is decoded to $5A80 and compared with the typed text (engine strings_equal, through $024F). The square then changes by pair n of the list after the answers for answer n, by the pair after those for a wrong answer, and by +1/+2 for none.$82E0-$8332), radiation: the map message at +0; then every member of the party, conscious or not, who is not wearing item 41, the Rad suit, as armour takes record byte +1 in d6 off CON (change_member_field $9082 with X = $1D, Y = $FF) and gets bit 0 of the disease byte +$28 set, Radiation poisoning in the doctor's list (bit_masks $8335, of which only the first byte is ever read; module-0's facts); then the change at +2/+3. Whether armour counts is decided by the record's byte 0 (the game's facts, "The maps", live).$89A3-$8A35), an exit. Nothing while $02AA says N was answered. Otherwise its message (bits 0-5 of byte 0) unless exit_confirm has shown it ($022E), the change at +4/+5, and the party table's +$0B/+$0C/+$0D take the party's column, row and map as the return point, the old values kept in the operands $8A12, $8A19 and $89FC. Byte +3 = $FF goes back to the old return point and returns carry clear ($89EE-$8A01); otherwise bytes +1/+2 are the column and row, added to the party's own when byte 0 has bit 7, and +3 the map ($89E1-$8A24). exit_enter_map ($8A26) enters the map, draws the view, merges parties and returns carry set, so the arrival square's handler runs next.$8A36), on a move into a class 10 square whose byte 0 has bit 6: the exit's message and "Enter new location (Y/N)?" (message 103). Y sets $022E; N redraws, sets $02AA and refuses the move ($8A60).$402A) holds E7 00 00 0A 06 01: message 39 with bit 6 to ask, relative 0, 0, map 10, and the change to class 6 number 1, whose record (map-10 $3F6F, 81 FD FD) runs module 1, the shop (map-10's and module-1's facts).$8DEC-$915D): +0 flags, +1 a message shown whenever the checks run, +2 the success message, +3 the failure message, +4/+5 the change on success, +6/+7 the change on failure, +8 the effect's character-record offset (bits 0-6; a whole byte of 0: none, $9046, so $80 would act on field 0, which no map's record has; bit 7: the amount at +9 as it stands, else that many d6), +9 the amount (bits 0-6; bit 7: subtract), and pairs from +$0A to an $FF; with flag bit 3, one change for each pair follows (check_change_offset $9108).$8DCB, and the Use below); bit 0, a CON effect ignores armour ($90B2).$8E47-$8EBE). Kind 0 a skill, where difficulty 0 means knowing it is enough (engine skill_level and skill_check, through $0359 and $0207); kind 2 an attribute, the value being its record offset (engine attribute_check, through $0212); kind 3 passes only when the party has exactly value members (CMP $07, $8E85-$8E8A); kind 4 passes when the member has less cash than the value and, when the member has that much, fails at once without trying the other pairs, taking no money (engine $03D4, $8E73-$8E83); every other kind is an item the member carries, a charge of which is used (engine find_item and use_item_charge, through $0344 and $0380). An item Used on the square matches kind 1 pairs only (find_item_check $8F84).$3BE0) asks for a party of 8, where a party has room for seven (ranger's facts, "Code nothing reaches").$8DEC) runs the checks for a square the party tries to enter, again while they change the square, and refuses the step unless they pass.$9082): subtracting from CON goes through member_takes_damage ($A85B) with $C8 = 1 and $A0 = flag bit 0; from cash through engine $03CE; from any other field the low byte only, never below 0. Adding to experience goes through engine add_experience ($03DD); to any other field the low byte, unless that would pass 255. apply_square_effect ($9039) makes the change with slow scrolling on ($914D) at the speed in its operand $906A, which is 2 in the snapshot and afterwards whatever the speed $0F was when the last effect ended (the cursor keys change it while text scrolls slowly, engine $253A); the speed and $A8 from before are put back after ($9144).+$1D, 72 records), but one: map-41's check 22 (map-41 $3CBF) reads as an effect on +$7F (+8/+9 = $FF $FF) and a pair for skill 129, bytes that look like a CON −1d6 effect two bytes later than the reader expects.$8A77) asks "Which player?" (message 2) unless the party has one member. use_by_member ($8AA2) refuses a member whose CON is 0 or below ("is incapacitated.", message 3).$8AD5) asks "Use: I)tem S)kill A)ttribute?" (message 4, keys S, I, A at $8BEB). A skill comes from the skills page; Medic (25) or Doctor (32) then asks "Who do you want to heal?" (message 5) and refuses the healer (6), one at CON 0 (7) and one who is conscious (8). An item comes from the item page (record offset $BB + 2 × its line), an attribute from "Which: 1) Strength ... 7) Charisma" (message 11, offsets $0E-$14). Except for healing it then asks the direction ("Which way?", ask_direction $B8D0, message 68).$8BEF): a non-player character may refuse ("disobeys you.", message 69): its byte +$2C for a skill, +$2B for an item, +$2D for an attribute, tested against +$31 ($8D83, $8D86, $8D89; engine npc_obey_check, through $021F). The square in the chosen direction must be class 2, or the answer is "You see nothing special." (38) for Perception and "Nothing happens." (12) otherwise ($8D9D). A matching pair is tried; success shows +2 and changes the square ($8C77), failure or no pair takes $8CC7, with the square's +3 when its flags have bit 1 or a pair was tried, else "That doesn't seem to work." (61) ($8DC8).$8D05): the healer rolls the skill (engine skill_roll_total, through $020A: 2d6 rolling again on doubles, under 5 failing, plus the attribute and 4 × the level) against 15 + 5 × (|CON's low byte| >> 2) for Medic, >> 3 for Doctor ($8D40-$8D57). Failure: "You didn't help <name>." (9). Success: the patient's CON is shifted right with the sign kept ($8D5C-$8D68: −20 becomes −10, −3 becomes −2, −1 stays −1), "<name> is improving." (10), and the healer may learn (engine skill_improve_roll, through $020F, with that difficulty).$B015), after each step: when the map header's +$2F is not 0, a chance of 1 in +$2F; it then needs a free entry, bytes 3, 5 and 7 zero, among the first +$32 of the map's list 15. The monster type is 1 to +$31 at random. By that monster's weapon class 0-12 the entry's byte 0 comes from $B0EB (15, 35, 40, 65, 90, 40, 65, 90, 65, 90, 40, 65, 90), byte 1 from $B0F8 (15, 15, 40, 65, 90, 40, 65, 90, 65, 90, 40, 65, 90), and the distance in squares from the engine's $5B93 (1, 1, 1, 3, 4, 1, 3, 4, 3, 4, 1, 3, 4). A random direction (not 4) is walked that far from the party, each square on the map, not in column or row 0, of class 0 and without a party; if one fails, the next direction, and when all have failed, one square less. The entry gets line 0 = the type with 1 to (monster byte 4 >> 4) monsters, and the square becomes class 15 with the entry's number ($B045-$B0C8).$94CC; with the view drawn, $94CF) clears the 16 position records at $5B4F and sets each party's reach $F4E1 + party to the longest of its members' reaches, the operand of the CMP # at $9510 keeping the longest so far from 0 ($94EC-$951E): member_reach ($BC39) gives 0 for a member who is not conscious, $FE for a ranged weapon with rounds in it, and 15 otherwise. Each party on the battle map $8D then scans its view for class 3 and 15 squares. A square counts when the party can see it, has someone conscious, and its distance (engine distance_to_party, through $04CF) is within the record's byte 1, or within byte 0 and the reach ($95A9-$9630). It goes to the nearest party that can engage it, four records a party, a full party's records giving way to a nearer square.$95DE-$95EA; group_write_back $BA59 zeroes the type byte of a line wiped out in battle) (engine $5BF5 + the monster record's byte 6; kind 3 while the line's monster is still to be rolled), and one beyond it or out of sight is drawn as the map has it ($95D7-$960B, $9828).$936E) gives each recorded square a battle group, $EE00 + 376 × party + 94 × group (group_ptr $BBC7): lines whose number or count has bit 7 are rolled, 1 to the low seven bits, and written back into the map's record, so they stay fixed; the record's message is printed and the group announced ("... appear at d feet."); and every monster gets hit points: a quarter of the type's 16-bit seed (monster bytes 0/1) + 1 to its low byte + 256 × (1 to its high byte) ($9483-$94B9). So a Laser Hypermortar (map 16, seed 350) has 344 to 437 and a Finster Leviathan (map 23, seed 1,200) 557 to 1,500.$9EA4): +0/+1 the hit-point seed, which is also the experience base; +2 to-hit; +3 damage dice; +4 maximum count (high nibble) and armour dice (low); +5 damage bonus (high) and weapon class (low); +6 kind 1-5; +7 the picture, and the index of its pronoun (engine $5B00). Until the encounter is identified (record byte 9 bit 0) a line is named by its kind: game messages 83-87, Animal, Mutant, Humanoid, Cyborg, Robot ($9E5D). Of the 365 encounter records that the 42 maps' squares name, 357 start identified, 98 peaceful, 52 stationary and 6 with an NPC to hire.$B1A3), from Enc, from encounter_if_engaged and so after every move: $D8 = 1, and the experience of records 1 to 7 is kept at $F487 ($F486 = $0A). Each round ($B1E7) takes every party in turn: one whose map is in the fight list $5B8F or in memory fights; another with someone conscious is asked "This party isn't on this map and isn't in battle. Do you want them to execute a battle round (Y/N)?" (messages 54, 76). For a party that fights, its map is entered, the encounters scanned and placed, the groups measured and their moves planned ($B237-$B243), and every party on that map gives its orders; "Use these options (Y/N)?" (70) then runs the round ($B290-$B2A0).$B3B2): a party none of whose groups is in use is asked "This party is not being attacked. Do you want them to execute a battle round (Y/N)?" (20, 76). Each conscious member is shown "<name>, choose: R)un U)se H)ire E)vade A)ttack W)eapon L)oad/Unjam" (55) and takes a key of $B4BD: space H A W R E L U S, the order being the key's index; S, not on the prompt, stores its index 8 and becomes order 2, A)ttack, with the group list always shown for a player character ($B449, $B4A0; an NPC still chooses its own line, $B50B). A party Run gives every member order 8 with the direction.$B867): "Encounter begins..." (30), then four passes over the parties on the battle map ($9F40): the members' ranged attacks ($9F70), the monsters' fire ($9C65), melee ($7F64), and the other orders ($9EFD: Hire, Weapon, Evade, Load, Use); the last three only when a party took part. Then runs and party moves ($A276) and merge_parties_here for every party ($B8AC-$B8C0).$7F64) fills slots at $5A00 with initiative: a monster of a group within 15 feet (distance below 16), hostile, of a line whose weapon class is not ranged, alive and with its act flag set (which is cleared) gets 2d6 + 8 × its byte +2, the product taken in 8 bits; a conscious member with the Attack order still standing (members with a ranged reach fired already and their order was cleared, unless their target line had nobody alive left, $9FB0: such a member keeps order 2, joins here and strikes nobody, $80CD-$80D0) gets 2d6 + SP + 4 × Brawling. The highest slot acts first, and on a tie the later slot, so members, whose slots come last, beat monsters ($8057-$8080).$80BF-$80C7), hitting when 1d100 is under melee_chance ($8256): base 60 when the target's group has a move planned ($F3EC + 4 × party + group not $FF), else 50 ($80E7-$80F1), + 4 × Brawling + the AGL bonus, − 4 × the monster's byte +2 when its class is 1, else − (byte +2 − 5), within 0 and 100. A miss prints "<name> misses." (35). A hit does the weapon's roll + 4 × the weapon skill + the DEX, ST and LK bonuses, added with a floor at 0 ($811A-$813B), to a random living monster of the line, less that monster's armour roll (monster_takes_damage $AA90).$8163, $8166). Live: with the party put on map 1's graveyard with the game's own enter_map, weapons cleared, A each round, three kills of Shambler Ghouls worth 40 each took each killer from 0 to 80, with no experience printed (the game's facts, "Live tests").$8177): as many attacks as its byte +2, each at a random conscious member, who avoids it when 1d100 is under melee_chance with base 60 for the Evade order, 50 for Attack and 40 otherwise ($81CF-$81DF). A miss prints "The <monster> misses." (31); a hit, one of the map's melee verbs (header +$56/+$57) and byte +3 d6 + the high nibble of byte +5 to the member ($8217-$821D).$9C65): for each hostile group of the party and each line from 2 down to 0, a line whose monster has a ranged class fires, each monster with its act flag once (a dead one only when the flag has bit 7, left by the shot that killed it when its flag was still set, $A704-$A711; a monster killed after its flag was cleared keeps 0 and does not fire). A line whose monster is not ranged ends the group, so the lines after it do not fire ($9CA9-$9CAE). A shot hits when 2d6 rolling again on doubles, under 4 always missing, + the to-hit byte + 8 × the fire mode reaches the range target ($9CF0-$9D2E). Mode 2 (classes 11 and 12) fires 3d4 shots, mode 1 (classes 5-7) 1 to 3, mode 0 one ($9D0B-$9D67); each strikes a random member (monster_shot_hits_member $9DEC), who escapes with the Evade order when the roll beat the target by less than 6 ("but <name> nimbly evades.", message 93).$83D7, move_groups $872D): a hostile group that is not stationary plans from its first line with monsters. A melee line closes in unless it is within 15 feet; but one whose monsters are not animals flees instead once their average hit points are under half the type's seed ($8448-$8461). A ranged line flees when weaker than the party (robots never), stays when its to-hit byte plus 15 (robots), 11 (cyborgs) or 7 reaches its range target, and otherwise moves to a better position ($847F-$84D7). A hostile group left without a plan gets its act flags set. The plans are kept at $F3EC, and the moves print " moves to a better position.", " runs away." or " runs at you." (72, 74, 75).$B320): the party chosen at the start ($B6) again, "<name> gains n experience." (39, 40) for each record whose experience grew, $D8 = 0, the map entered and the view drawn.$9F70): every conscious member with the Attack order and a reach of $FE attacks its target, a line index 0-47 in the order's argument (12 × party + 3 × group + line, $A535) with the fire mode in its bits 6-7.$A584): 2d6 rolling again on doubles; a roll under 4 followed by a 1 on a d6 spoils the attack: classes 8, 9 and 13 "fail to detonate" (41), other weapons jam (42: the count byte becomes $80, the rounds lost). Otherwise the roll + DEX + 4 × the weapon skill, + 10 for class 13, + 4 for a burst, + the rounds left for automatic fire.$9FA9) adds 8 to the roll when the target is in another party's groups ($9FEF-$9FFA), and hits when the total reaches the range target (member_range_target $A741). range_target ($BD48) gives, for distance d in tenths of a square and k = d/5 rounded up: 5*k* for classes 2, 5, 10 and 13, 10 + 2*k* for classes 3, 6, 8 and 11, 20 + k for the rest, and 7 more when the monster looked up is not of a ranged class.$A02B-$A040). A hit with class 13 rolls once and strikes every living monster of the group's three lines; otherwise a single shot hits once, a burst 1 to 3 times, automatic fire max(1, rounds/4) d4 times spread over the group's lines ($A98C-$A9AC).$A7DE): the weapon's dice (item byte 6) in d6, plus the LK bonus. Classes 8 and 9 roll 2 × dice − the target's armour dice instead, when the dice are at least the armour, and armour is not taken off them again ($A818-$A835, $A9AD); for the rest the monster's armour dice are rolled and taken off, stopping at 0. An empty or jammed gun is swung: 1d6 for classes 2 and 10, 2d6 for the others, plus the class number ($A807-$A816).$A7C3) for an attribute v: (v − 12)/2 rounded down from 13, 0 for 9-12, (v − 9)/2 rounded down below 9, so −3 for 3 and 4.$A9E8). After an attack the member may learn the weapon's skill at difficulty armour/2 + 1 ($AA35). A one-shot weapon, record byte 4 = 1 and class 1 or ranged (items 6-12, 14 and 15), is used up; a class 13 one is replaced from another slot holding the same item (attack_done $A53A).+$3E on, the points of damage, and " killing him/her/it/n of them" or, when the kill went 15 or more below 0 (set_overkill $ABF1), one of the map's pair at +$5A/+$5B; " just as he peels off a final shot" (34) follows when the killed monster's class was ranged and its flag byte was still set; otherwise $7E33 holds 1 ($A708).$B4C7) refuses a jammed weapon (56); with no line in reach it says "Reload your weapon." (80) for an empty ranged weapon, else "No one is within range." (57). The group list ($B61C) runs items 1 to 64 ($4D = $41, $B626-$B628), each a line index item − 1, of which 0-47 exist; group_list_line ($B63D) shows an item when group_in_reach ($B692) passes it and its count is not 0. With a gun of classes 5-7 or 10-12 holding 3 rounds or more the player chooses the fire mode, S)ingle or B)urst, and A)uto too from 4 rounds (messages 58-60); any other attack is single ($B5BE-$B60E, class_fires_bursts $AC04).$B50B-$B589): at melee reach the first line in reach in single mode; otherwise a random line in reach, single shots with a weapon that cannot burst or with fewer than 3 rounds, a burst with exactly 3, and with more a burst, or automatic fire when a d10 reaches the byte of $B4F8 for the number of characters (records 1 to $0A of every party) who are not conscious: $FF, 9, 7, 5, 3, 0, 0, 0 ($B528-$B556). So an NPC never fires automatic while everyone is up, 20 % of the time with one down, 40 %, 60 %, 80 %, and always from five down.$A85B), called by a monster's melee ($821D), a square's effect ($90BC) and the monsters' fire ($9E23), does nothing (carry set) to a member at CON 0. A member already down (CON negative) takes damage that is not from a monster ($C8 set); from a monster of a ranged class, never (" but misses.", 91); from any other, only when 2d6 rolling again on doubles is under the monster's byte +3 ($A889-$A8AF). That last test is never reached, as a monster's melee picks conscious members only ($81B7-$81C7). So no monster hurts a member who is down, and the monsters' fire never touches one.+$1A) in d6 off the damage, unless $A0 is set ($A8B2-$A8C5). When nothing is left, damage that is not from a monster prints "'s armor absorbs the damage." (100, 101) after the name, and a monster's " but his/her armor absorbs the damage." (94, 101), or " but misses." (91) when the monster's class is 1 and the member's AC is 0 ($A8E8-$A900). Otherwise the line goes on with " gets hurt for " (99) or the map's verb (header +$51) and the points of damage. Then CON goes down (engine con_subtract, through $0407): reaching exactly 0 makes it −1; −50 or below makes it 0, dead; a member going down keeps the CON from before the blow in +$26/+$27 and gets " and " and the condition ($A986: messages 49-53, 25).$B795), for records 1 to $0A, skips the dead (CON exactly 0). A disease of bits 4-7 of +$28 (Desert dust, Rabies and two named "D6" and "D7" in the doctor's list, module-0's facts) costs a conscious member a point, 1 going to −1 rather than 0, and one who is not conscious two, every fourth tick ($D2 AND $3F = 0, $B816-$B83F). Otherwise a UNC member (−1 to −10) gains a point each tick and wakes at 0 with half of +$26/+$27 (1 when that is 0); one at −11 or below loses a point each tick, dying below −50 ($B842); one with CON above 0 and below MAXCON gains a point when $D2 AND $3F is 0, or $D2 AND $7F in the current party, which so heals at half the rate of the others ($B7FD-$B810).+$28 is not 0 (con_heal_one $B854), and radiation sets its bit 0 ($8313-$831C): a member it has touched recovers neither CON nor consciousness until the bit is cleared, as the doctor does for a fee (module-0's facts).$B7E9-$B7FB): a CON whose high byte is above MAXCON's goes on healing while its low byte is below MAXCON's. On the 6502 simulator: CON $0105 with MAXCON $00FA healed to $0106; $01FA stayed; $00F0 healed and $00FA stayed.CMP #$CE, $A94C-$A950, equal counting), the tick only below −50 ($B848-$B84A, equal surviving), so −50 reached by bleeding is still alive.$9C17, 27 call sites) keeps the current party's column, row and map in its table ($9C45), then takes party A's: $08, $07, $57/$58, $09, and the view's corner. clear_party_table ($9C58) zeroes a 14-byte table.+$0B-+$0D hold the return point of the last exit ($89BA-$89D7); copy_member_to_party ($9B27) carries them from the old party to the new one through $9B69.$9959), after every move, exit, run and battle round: one other party on the party's square and map joins it, the later tables move down and $0B drops. $08 drops when it came after the merged party; $B6, the party chosen when a fight began, drops only when it is at or above both the merged party and $08 ($99A2-$99AD), so with $B6 between the merged party and the current one it ends up naming the next party.$98F5): a member who runs leaves in a party of his own, $0B + 1, unless he is alone or there are four parties, when the whole party moves (with four parties and others in his party, "couldn't run.", message 88, $A276).$99EE): nothing in a party of one; with four parties "No more can disband." (21). "Who wants to disband?" (22) takes member keys until RETURN, back-arrow or all but one are chosen: " has been chosen." (23) for one already chosen, " is unconscious." (24) for one below CON 0, who stays; a dead one is offered "Do you want to bury <name>?" (79, 78) and an NPC, while two or more are conscious, "Do you want to dismiss <name> forever?" (79, 77); Y removes the character for good. Then "Which way?" is asked, and the chosen members leave, become the current party and take the step ($ADD1), then after_move.$9B8C, $9BBA): the next party with a conscious member; on another map "They aren't on this map. Are you sure you want to view them (Y/N)?" (27); "You can't view this party." (14) when it has nobody conscious but other records are.$A3F1), in the round's last pass: nothing when the target line is dead; "No room in roster." (95) when seven records are in use; a failure when the group's record is not peaceful (byte 9 bit 1) or names no NPC (byte 9 high nibble).$0A + 1 ($A424-$A433). When its byte +$31 is not 0 the hirer needs (CHR + IQ)/2 + CHR + rank + 2d6 rolling again on doubles to reach the NPC's (CHR + IQ)/2 + +$31 + rank, and dice under 5 fail at once, raising the copy's +$2B-+$2D by one ($A44C-$A478, $A4E0-$A4EA); that copy is not kept.$0A and $07 + 1), its message (+$30, map text) shows, its +$31 becomes the hirer's CHR, its +$2B-+$2D drop once for every 4 points of margin, its line's hit points are cleared so the line leaves the fight, and every item slot's count is filled from the item's byte 4 ($A47A-$A4DD). With +$31 = 0 no roll is made and the margin is taken from whatever the accumulators held. Failure: "<name> tries to hire the <monster> but fails." (44, 81).$9194): +0/+1 the change once the bag is empty, then entries of an item byte and a count to an $FF. On opening, each entry is fixed in the map's record: an item byte 1-$5D without bit 7 names an item class and becomes a random item of that class with bit 7 set; $5E (cash) becomes $DE and the two bytes after it are each rolled from 1 to themselves; a count with bit 7 becomes 1 to its low seven bits ($919C-$921A).LDY that follows, whose operand is the entry's offset, leaves Z clear, so the BNE after the store is taken ($91C8-$91D1). On the 6502 simulator, entries naming classes 5 and 20 were emptied.$921D-$92E0).$3108 + 8*n*): bytes 0-1 the price, 2 the stock (0 not sold; a purchase lowers it by one, $FF included, and a sale raises it by one up to $FF, module-1 $CC12-$CC19, $CB2C-$CB37), 3 class × 8 + effect, 4 the clip size or uses, 5 the governing skill, 6 damage dice or AC, 7 the ammunition item ($AC39). Item n's name is engine message 36 + n. The table is the item-table file that $75 names (directory at T35/L15; engine $26BD loads it, $26D6 writes it back): the start-up and the utilities load it before the game, and a shop loads the file its record names and writes it back after a trade.$91B4) and its record's bytes in the item-table files 0 and 1 (07 BF 11 FB B9 C4 7E 08) are not an item's. Files 2 and 4 have other bytes there, and in file 4 its class is 1, so with that file loaded (map 21's Market and Blackmarket) count_items_of_class ($92EA) counts item 94 in class 1 and the loot pick ($91D3-$91FA) may choose it (not traced further).$BF5F: the 60-byte alphabet, then the group table at $BF9B, 28 words of which 0-26 point at groups $BFD3-$C596 and word 27 ($D9C4, at $BFD1) outside the block; so the messages are 0 to 106, message 0 empty.$BCE1 (one of two at random) and show_map_msg_at $BCE5.$7F4C) has no caller: no JSR, JMP or address word for it in any part's or map's snapshot.$8A9E) has no caller either, and $E6, which it sets, is otherwise only cleared ($8B3B), so use_choose's $E6 branches are never taken.BIT $C010 at $88FF and $8A31 read a byte of the text; their flags are not used. On the Apple II $C010 clears the keyboard strobe.$C5B3-$C5FF, after the text block, are never read, jumped to or pointed at; from $C5B6 they decode as code that loads and stores $C08C-$C08E,X with X = $60, the Disk II controller's switches for slot 6 on the Apple II.$7EA2-$7EFF, with calls into this program that never run (order $CAA2-$CAFF, the order part's facts).$8335) has eight bytes and only the first is read, through an LDY #0 whose operand nothing changes ($8313-$8315).BIT instructions: $80F1, $81DC, $81DF, $8D86, $8D89, $8DB9, $8DBC, $9029, $94CF, $9E52, $A03B, $A03E, $A7BB, $A7BE, $A7C1, $B4F1, $B8CB, $B9E6 and $BF12, among others.$7FDB-$7FDD), which would wrap from 32; the melee types in the 42 maps have 7 at most (map 15's Titanium Clawer and map 35's Brother Goliath).$FF as its high byte, so 0 becomes −256 ($828B-$8292). The only such type in the maps is map 16's Chopter, a ranged one, which a member then cannot hit in melee.$A748-$A768), so the 7 points for a non-ranged target follow another line's monster, and for group 3 a byte past the line table ($BAEC).$A7E7-$A7F2): a sum below 0 comes back as 65,535 or near it, a kill. A throwing knife (2 dice) thrown with LK 3 or 4 does it when both dice show 1.$A80B: the LDA #$00 at $A809 is overwritten), so a swung Meson cannon does 2d6 + 12.$ABF1-$AC01).$8550-$855C).$9C6A, $9D8F), and monster_shot_hits_member picks members until one is not dead, with no limit ($9DFD-$9E26). So when a mode 0 shot kills the party's last living member, the next hit in the same call never returns, unless its roll beat the target by less than 6 and the member picked still holds the Evade order ($9E08-$9E18). Since the monsters' fire cannot touch a member who is down, that last member has to go from conscious to −50 or below in one blow.LDA # at $9649 replaces the distance that nearest_engaging_party has just returned with its own operand, the party chosen in an earlier pass (stored at $9652 and $96C4). So the test of whether the nearest party already holds the square compares that earlier party ($964B), and a record moved to the nearest party keeps that party's number, 0 to 3, as its distance ($964F, store_position_record $97F2).$B91E) picks the nearest line by the group's stored distance (byte 3), not the one group_in_reach has just measured (nearest_line $B9A0).$424A-$4263) reads as code, CMP #$03, BEQ, CMP #$02, BNE, LDA $7E47, JSR $08FD, LDA $7E47, JSR $9D6A, CMP #$10, BCC, where $9D6A in this build is a store inside monsters_fire. A class 0 square's number only finds a record, and class 0's handler does nothing ($AE0D).Current truth for this game. The workflow lives in kit/skills/; how this understanding developed lives in agent-history.md. Every fact names the routine or table it comes from. Unless marked live, a fact comes from reading the code in the snapshot named in orientation.md.
The game is in 64 parts (orientation.md, "The parts"): a resident engine, the programs loaded over it, nine tile sets and 42 maps. What is true of one part is in that part's own parts/<id>/facts.md, with that part's addresses. This file holds what spans the parts, and names the part beside every address it gives: "engine $25BD" is the engine's routine at $25BD.
PRODOS and 2.0 on sides 1 and 3 and the game's data in raw sectors on all four (orientation.md, "The image").$81AD), and its packed text "Electronic Arts and Interplay Productions proudly present" and "Copyright 1986-88 Interplay Productions." (startup $84BE, $8505).$514F) reads "A computer software poster reads, "WASTELAND!"" and lists programming, design, maps, graphics, general help and playtest, 26 credits to 20 people, ending "Rated PG-13. Coming soon to a theatre near you." It is one of four messages on one wall square (column 20, row 25, class 11), each bump showing the next: 91 "Coming soon, "The Radheads!"", 92 the credits, 93 and 94 two more films, then 91 again (live). Map 1's message 26 is a gravestone: "Ken St. Andre, 4/28/47--4/28/87." (map-01 $4300), shown on the graveyard's square at column 27, row 24 (live). The string sweep of every program part and the decoded text of every text block (the 52 blocks' 4,920 messages, 478 the programs' and 4,442 the maps') find no other credit: the start-up's lines above are the only others that name a maker.$7E06, against side 1's $7F2A; 2,766 of its 4,096 bytes differ), and its own boot sector, named "A JERKVISION PRODUCTION". Its credits read "Copyright 1986,87 Interplay Productions." where side 1's read "1986-88", and where side 1's start-up calls its title sounds, side 3's calls three RTS stubs ($7E03-$7E05) and writes no SID register. Booted, side 3 cannot run: its 2.0 loads side 3's track 34, logical sector 10 to $0200-$30FF, where side 1 has the engine and side 3 has other bytes, and the first raster interrupt after the hand-over goes through engine $04F9 into them and halts the processor on a JAM at $2931 (live). Run over side 1's engine instead, with side 3 in the drive, it shows a title picture it unpacks from side 3's track 3 and stops at "Computer defense initiative activated." (live, reference/side-3-start-up.png).$8600-$8FFF is the radio program's $7E00-$87FF, all 2,560 bytes.$874B-$87FF, and so also the Ranger Center's $8F4B-$8FFF, is the game's $874B-$87FF except two operand bytes ($87B4, $87B9), each 4 lower than the game's: a copy from a slightly different build of the game.$860B-$86FF is the engine's $0A0B-$0AFF.In play (parts/game/work/play-map.vsf), with $01 = $35: all RAM but the I/O area, both ROMs out.
| Range | What | Part |
|---|---|---|
$0000-$00FF | the engine's zero page; $02-$0F are saved with the game | engine |
$0200-$30FF | the engine: two jump tables ($0204-$044E, $04CF-$0504), its code and its text block ($29E4-$30FE) | engine |
$3100-$33FF | the item table: eight bytes an item | game |
$3400-$59FF | the map the party is in: class layer, number layer, record, lists, text and code | map-nn |
$5A00-$5AFF | the sector buffer | engine |
$5B00-$5BFF | tables and work space the boot file copies from its $C17B, among them the menu bar's text at $5BAB | engine |
$5C00-$5FFF | the screen matrix: the bitmap's colours | engine (output) |
$6000-$7DFF | the bitmap, 24 character rows | engine (output) |
$7E00-$C5FF | the program in play: the game, or the start-up, the utilities, the Ranger Center, the radio or the death screen | each its own part |
$C600-$C9FF | the font, the track table $C930, the key table $C953, the sound player $C993 and its three sounds | engine |
$CA00-$CFFF | a module, the party-order program, or a picture unpacked for the picture window | module-n, order |
$D000-$DD7F | the tile set, in RAM under the I/O area | tiles-n |
$DD80-$DDFF | the map window's scroll copy, in RAM under the I/O area | engine |
$DE00-$EDFF | the tile layer of the map, one byte a square, under the I/O area and above it | map-nn |
$EE00-$F3FF | cleared by the start-up (startup $7F3D-$7F55); the game keeps four 376-byte records from $EE00 (game $BBC7) | engine |
$F400-$FBFF | the game state: party tables, saved zero page, line buffer, character records from $F500, written to the disk as the save | engine |
$FC00-$FFFF | the fast loader's computer side, and the hardware vectors in RAM | engine |
The bitmap's 25th character row would be $7E00-$7F3F; with $D011 set to $37 (24 rows, startup $7F55-$7F57) it is hidden, except that screen_shake's random vertical scroll (engine $077A) brings its top lines into view for a moment, and the programs load from $7E00.
$FF00), installed by the boot file. It moves whole 256-byte sectors with no link bytes: a file is a track, a logical sector and a page count, and logical sectors run down from there.$FFD3, $FDEA, $FDD8, $FFE8).$C930 gives for that track (engine $FD0B). The table is laid out as if indexed by the track number less one, and is indexed by the track number: track 17 is entered at logical sector 0, track 24 at 17 and track 30 at 16, below their top sectors of 20, 18 and 17. Files are laid out on the disks by the same rule: maps 0, 2, 5, 11 and 19 cross one of those tracks, and each loads the sectors the rule names, not the ones a full track would give (the 42 maps loaded by the game's own enter_map compared with the image read both ways). The game writes through the same routine.$2790).$2759).$27C3).$C800 (glyphs $60-$7F, the lower case), from T35/L16 for each unpack and replaces from T35/L11 afterwards (engine $0524). T35/L11 holds the same 256 bytes as the third page of the font's own file, four pages from T34/L14 (engine $C800).$280C-$283E).$D7 $CC side on the masters, $00 $CC side on copies (orientation.md, "The play disks").$1897) reads T35/L8 and wants its byte $FD to equal $ED, its byte $FE to be $CC and byte $FF the side asked for; until they are, it prints "INSERT SIDE n. (RETURN)". $ED is 0 in play, so the masters are refused (live). The start-up sets $ED to $D7 only around the check of side 1 before it loads the utilities (startup $81E4-$81ED), which is how Copy reads the masters.$2856, from $25BD); a map entered by a number from $80 up (the generic maps 5 and 11) never is (engine $2858).save_game (engine $18EA) copies zero page $02-$0F to $F478-$F485, sums $F400-$FBFF into one byte (engine $1918: for each page and Y = 0-254, the sum plus the byte, then plus Y and the carry out of that first addition; the carry out of the second is dropped, and byte 255 of each page is left out), keeps the sum at $F4FF, scrambles bytes 0-254 of every page by EOR with (2*Y* mod 256) EOR the sum (engine $1944), writes page n to T35, logical sector 7-n, and unscrambles. The key is the byte at $F4FF, saved in clear. The start-up reads the eight sectors back, unscrambles them and compares the sum (startup $830E-$832D), but a sum that does not match is accepted: every byte is EORed with twice its offset again, which leaves it as stored EORed with the key alone, and the start-up goes on with no message (startup $832F-$834C). So a save written in clear with a key of 0 loads as written, unless the sum the start-up forms from it comes out 0. Live: member 1's cash set to 12,345 in play and saved with S and Y; the eight sectors read off the disk image afterwards are this rule applied to memory as the save began, all 2,048 bytes, with the key $6B the sum as the code forms it.save_game has no side check, so a save lands on the side in the drive. It is called by Save (game $8820, after asking for side 1), by Radio (game $8802, before asking for side 1), at the end of every map change (engine $2600), by modules 1 and 3, by the Ranger Center's Start and by the utilities.$3400: the class layer, a nibble a square, the high nibble for an even column (engine $0953); the number layer, a byte a square (engine $098E); then the map record, at $3A00 on a 32 x 32 map and at $4C00 on the four 64 x 64 maps, 0, 12, 22 and 26. From $DE00: the tile layer, a byte a square (engine $0B06), unpacked from the map's stream.$5C-byte header: address words for the packed text (engine $1E4F), the monster names and records (game $9E76, $9EA4), a list of records for each square class 0-15 (engine $09B8), the map's own routines (game $885C) and its non-player characters (game $A424); then the size (+$2C, engine $25D3), the chance of a random encounter after a step (+$2F, game $B017), the tile set (+$30, engine $25EE), the tile drawn off the map (+$33, engine $0B5E), what a step costs (+$34 and +$35, a fraction of a minute and minutes, and +$36, ticks of the elapsed count, game $AF32-$AF72), and 37 message numbers for the phrases of combat (+$37-$5B, game $BCE1, $BCE5).$ACF3, a table of 16 words) runs on the record that word number of the class's list names (engine $09B8): class 1 shows a message, 2 tests the party (skills, attributes, items, cash, party size), 3 and 15 are encounters, 4 shows another tile and a message, 5 is loot, 6 an action, 8 a question, 9 radiation, 10 an exit, 11 a wall that shows its message when walked into (game $915E), and 12 changes other squares. Classes 0, 7, 13 and 14 do nothing, and no map uses 7, 13 or 14.$0B80-$0B8A; live). Tiles 0-8, the party, the creatures, the loot bag and the sign, are the same bytes in all nine tile sets as they load (the nine sets compared byte for byte); maps 0 and 26 exchange tile 7's bytes with the jeep's or the train's for their rides (map-00 $4FCD, map-26 $4EF9).$A0, "ignore armour", to 1 (game $82E7), but the routine that applies the damage replaces it, for each member, with bit 0 of the square record's byte 0 (game $90AF-$90B4), which is the flag byte of the check squares it was written for and the message number of a radiation square. So armour counts on radiation squares whose message is even and not on those whose message is odd. The radiation records name messages 23 on map 0, 28, 29 and 30 on map 19, 69, 70 and 71 on map 20, 0 on map 26, 12 on map 31, and 43 and 0 on map 38 (live).$0A00). An action square's byte $80 + n loads module n to $CA00 and runs it; a smaller n calls the map's own routine n (game $8845).$09B8). On map 11 the square at column 24, row 13, a wall on the screen, is class 2 number 2, and word 2 of class 2's list is $0000, so its record is read from address 0 on: the processor's port registers, then zero page. Its flags are the port's direction register, $2F, which has no checks tested and the move refused; the message it shows is the byte at $0001, the port, $35. So walking into that wall prints map 11's message 53, "Ah! Fresh air and open spaces once again.", every time (live). The wall is next to squares the party can walk to from the map's exit at column 28, row 0 (a search of the map's classes).$43/$44 points at (engine $1DFB). The engine's is $29E4-$30FE, messages 0-169, and $30FF is a spare byte.$291D): code $1E capitalises the next letter, $1F adds $1E to the next code, and the code whose alphabet byte is 0 ends the message (engine $28F4).$1F42, $1F64): $0A singular or plural on the count $38, $0B the current character's name, $0C his or her, $0E him, her or it, $0F the count, $0D a new line, $05 and $06 wait for RETURN, space or back-arrow (wait_return, engine $2501).$6000 with its colours at $5C00, in VIC bank $4000 (live: $DD00 = $86, $D018 = $79, $D011 = $B7, $D016 = $D8). The start-up writes $D011 as $37 (startup $7F55-$7F57, $7F91-$7F93); bit 7 reads back as the high bit of the raster line, which was past 255 when it was read. Text is drawn into the bitmap from the 6-pixel font at $C600 (engine $1F03).$2947), which scans the keyboard; the NMI vector ends in an RTI, so RESTORE does nothing (engine $2966).$C953: Apple II-style codes with bit 7 set, no shifted keys, no repeat, one new key per interrupt (engine $29BD-$29D4).$1B36, $1BBF): the game's bar is "Use Enc Order Disband View Save Radio", and its hidden keys I, J, K and L move the party, back-arrow and space (game $7F18).$7E00-$8DFF replaced at the hand-over, side 3 attached): above.orientation.md, "The play disks").$6A from 0 to 1 and the roster drawn over the bottom of the map window.$57/$58 before and after each key).$DC00) and port B ($DC01) and, as the control, on the interrupt handler (engine $2947). The party stayed at column 55, row 62 ($57/$58), and in each hold port A was read 0 times against 203 or 204 reads of port B and as many interrupts; I then moved the party north, to row$AE26), play_sound_16 (engine $0741) and play_sound (engine $C993). The three steps the party took called each once; K, which the map refused, called none.kit/c64/frame.py and drawn by site/lib/c64.js: 0 of 104,448 pixels differ from the emulator's picture, with the roster hidden and shown.enter_map, its pages, stream and tile set measured with two fills (orientation.md, "The maps").$6B.enter_map and K (south, into the wall) pressed five times, with RETURN through the credits' six screens; the messages came 91, 92, 93, 94, 91, and the clock did not move.enter_map and L (east) pressed three times; each press printed message 53 and left the party, the square (class 2, number 2) and the clock as they were. The processor read $2F $35 at $0000-$0001 throughout.redraw_view (game $AC8C) at 5:00, 6:00, 12:00, 17:00 and 18:00. The 36 radiation squares around the building at columns 57-58, rows 36-37 showed the sign at 5:00 and 18:00 and plain ground at the other three.$7E63), its idle turn (game $7E93) and, as the control, the interrupt handler (engine $2947): 2,999 interrupts, 58,486 passes of the loop (19.5 a frame), three idle turns, and the clock from 1:24 to 1:36, four minutes a turn on the world map. So with no key pressed a turn passes about every 840 frames, 17 seconds on a PAL machine.$9F = $FF (no radiation in view), and 471 in the one chunk that held an idle turn. The port of the loop on the page makes 21.19 passes a frame on a processor that has every cycle; giving the video chip 1,075 cycles a frame brings it to 20.02, and the Geiger counter's player on the How it works page runs it so.$8382-$83C0; parts/ranger/facts.md).$A8C5) was skipped for all four members and they lost 22, 23, 16 and 14 CON; with it poked to 22 the roll was taken four times and nobody lost any.$8390 read the five dice of each of 1,000 attribute rolls: 1,043 ones, 869 twos, 786 threes, 727 fours, 749 fives and 826 sixes (even dice: 833 each), and two neighbours equal 699 times in 4,000. From play-map.vsf, a stub threw 4,096 pairs of dice with roll_2d6_open's instructions between them (engine $084A-$0850), each after the seed was moved 1 to 256 times as read_key moves it: 869 doubles, 21.2% (fair dice: 16.7%).enter_map, every member's weapon cleared so that all four fought with fists, and K (south) pressed: "3 Shambler Ghouls appear at 14 feet." With every member ordered to attack each round, ten melee rounds (game $7F64) brought three kills. Each kill was worth 40 experience (engine $1C-$1E at game $8163), and each killer's experience went from 0 to 80 across the two adds at game $8163 and $8166. The screen named the kill ("KIT hammers the Shambler Ghoul for 6 points of damage killing it.") and printed no experience.enter_map), every weapon cleared, then Hell Razor (record $F500) given LK 3 (+$10) and a Throwing knife (item 15) in slot 1, equipped (+$BD, +$1F = 1); K brought "2 Shambler Ghouls appear at 14 feet." With every member ordered to attack, the first throw stopped at game $A7E7, where weapon_damage has written the roll into its two ADC operands ($A7EC, $A7F0): the roll was 10, and 2 and 0 were written there, the roll of a 1 and a 1. weapon_damage returned 65,535 ($A7F3: X:A = $FFFF), the kill path at game $A6E5 ran, and the screen read "Hell Razor attacks and hits 1 Shambler Ghoul for 65532 points of damage spinning it into a dance of death." (reference/unlucky-throw.png).$A7C3); weapon_damage adds it to the roll with plain ADCs ($A7EA-$A7F0), while the melee path adds it through num2_add_clamped (engine $07C6), which stops at 0. Of the item table's thrown and fired weapons the Throwing knife has the fewest dice, 2 (item 15, byte 6); every other gun and rocket has 3 or more (the Mangler's and Sabot rocket's 2 × dice − armour is never fewer than their dice), and explosives take no luck ($A054). So only a 1 and a 1 on the knife with an LK of 3 or 4 goes below 0. No loot record of the 42 maps names item 15 or a random class-2 item (43 records, scanned with the levels page's port); Christina, NPC 1 of map 32 ($40AE), carries one in her slot 3. The ranger roll gives an LK of 3 or 4 for 261 of the 7,776 ways five dice fall (3.4%), the best three of five for 6. The highest hit-point seed of any named monster in the 42 maps is the Night Terror's, 32,000 (map 23, monster 1), which gives at most about 40,000 hit points (game $9483-$94B9).$05C5), the calls that pass its flags (engine $05DA) and the channels' step (engine $05E6), read every 50 frames. While the title's picture moved, the engine called the tick 3,508 to 3,553 times in 50 frames, every call passing the flags, and the channels stepped 7 times, about 7 steps a second; in a later stretch of the same sequence 2,624 to 3,176 calls and 5 or 6 steps. Between the two the flag at engine $AC was 0 and nothing moved.work/lead/sample.py): one stratum for each annotator's range, and one for the maps' descriptions that a decoder wrote, 7,973 comments (69%). Three agents that wrote none of the 63 checked them against the bytes: 48 were right, 14 had a wrong detail and 1 had the code wrong, so 15 of 63, 23.8% (Wilson 95% interval 15.0-35.6%). Weighted by the strata's sizes the rate was about 52%, because two of the three generated descriptions drawn carried an error of their template. The details were callers and paths left out, counts, the keys a prompt takes, and a field read alike for every value of a flag; the one wrong comment had taken an operand's value in the snapshot for a constant (map 49's poisoned needle, said to miss its target: the member comes from an operand the game writes when a Use begins). All 15 were rewritten to agree with the code.$7E0C); the skill list's header said to print on row 2, where the new line it opens with puts it on row 3 (module 2 $CC63); and play_sound's last write said to start the note, which sound 11's does not, its control byte $A4 having the gate bit clear (engine $C993). All three were rewritten, and so were five neighbouring comments of the death screen that left out the game's instructions in the same way.Pokes that change the game within its own parameters. Each one names the variable it changes and whether it has been tested live. Untested pokes are labelled as candidates.
| Effect | Poke | Status |
|---|