The Sentinel disassembly ======================== The Sentinel was written by Geoff Crammond and published by Firebird in 1986. It is a puzzle game in which the player must climb to the top of one of ten thousand landscapes while avoiding the attention of the game's eponymous antagonist. It is notable for its solid three-dimensional graphics. The following disassembly was created by reverse engineering binary images, without access to any source code. It is nevertheless reasonably complete, allowing the technical approaches used to be understood. The author of this disassembly imposes no additional copyright restrictions beyond those already present on the game itself. It is provided for educational purposes only, and it is hoped that the original authors will accept it in the good faith it was intended - as a tribute to their skills. Technical notes =============== A custom tape loader loads the main game binary from an encrypted stream. The game uses an equirectangular projection to avoid replotting, resulting in the graphics having a fisheye appearance. Angles are stored such that &100 = 360 degrees. The screen is 20 * 360 / 256 (28.125) degrees wide, and 12 * 360 / 256 (16.875) degrees high. Polygons are only plotted if their lines run anticlockwise. The order of variables in the code suggests that the author used x and z for the two horizontal axes, and y for the vertical axis. In this disassembly, x and y are used for the horizontal axes, and z for the vertical. The carry flag is often used in association with shift operations to process boolean variables. There is a large bank of variables at &0c00 - &0cff. The steepness of a landscape and the number of trees is determined at random. The number of enemies in a landscape is chosen according to a distribution curve that increases by one every 1000 landscapes, but is limited for the first 100 landscapes. The secret code needed to access a landscape is taken from the same random number generator that generates the landscape, after the landscape has been generated. The landscape is therefore always generated prior to the code being checked. Both the generation and the checking of the secret code are obfuscated. Anti-tampering code is embedded in unrelated functions. Interesting pokes ================= &1051 = &00, &14d4 = &00, &2553 = &e4 allow any landscape to be chosen without secret code &1710 = &ad player can't be teleported by meanies &1a06 = 18, &1a07 = &60 player can't be drained by enemies &2127 = &18, &2128 = &60 player can create objects without losing energy &5e38 = &1d see viewpoint drawing Tape protection disassembly =========================== ; Sentnel ; 007200 007200 000200 ; entry_point &7200 a9 0c LDA #&0c ; CLS &7202 20 ee ff JSR &ffee ; OSWRCH &7205 20 6d 72 JSR &726d ; start_tape &7208 a0 00 LDY #&00 &720a 84 62 STY &62 ; expected_block &720c a9 0f LDA #&0f &720e 85 5a STA &5a ; padding_key &7210 20 04 73 JSR &7304 ; write_searching ; read_data_loop &7213 a4 61 LDY &61 ; checksum # Unnecessary code &7215 b1 5f LDA (&5f),Y ; code_address &7217 85 63 STA &63 ; unused &7219 c8 INY &721a b1 5f LDA (&5f),Y ; code_address &721c 85 64 STA &64 ; unused &721e 20 9a 72 JSR &729a ; read_leading_padding &7221 20 b7 72 JSR &72b7 ; read_header &7224 a5 5c LDA &5c ; block &7226 c5 62 CMP &62 ; expected_block &7228 f0 06 BEQ &7230 ; is_expected_block &722a 20 18 73 JSR &7318 ; write_block_and_rewind &722d 4c 13 72 JMP &7213 ; read_data_loop ; is_expected_block &7230 20 0e 73 JSR &730e ; write_loading &7233 20 d1 72 JSR &72d1 ; read_block &7236 f0 06 BEQ &723e ; next_block &7238 20 18 73 JSR &7318 ; write_block_and_rewind &723b 4c 13 72 JMP &7213 ; read_data_loop ; next_block &723e a5 5b LDA &5b ; next_padding_key &7240 85 5a STA &5a ; padding_key &7242 e6 62 INC &62 ; expected_block &7244 20 86 72 JSR &7286 ; get_byte_from_tape &7247 85 63 STA &63 ; jump_address_low &7249 20 86 72 JSR &7286 ; get_byte_from_tape &724c 85 64 STA &64 ; jump_address_high &724e 20 86 72 JSR &7286 ; get_byte_from_tape &7251 8d 08 fe STA &fe08 ; Cassette ACIA control register &7254 6c 63 00 JMP (&0063) ; jump_address # &7213 every block until last block, then &0400 ; write_two_digit_hex_number &7257 48 PHA &7258 4a LSR A &7259 4a LSR A &725a 4a LSR A &725b 4a LSR A &725c 20 62 72 JSR &7262 ; write_hex_digit &725f 68 PLA &7260 29 0f AND #&0f ; write_hex_digit &7262 09 30 ORA #&30 ; "0" &7264 c9 3a CMP #&3a ; "9" + 1 &7266 90 02 BCC &726a ; write_character &7268 69 06 ADC #&06 ; write_character &726a 4c ee ff JMP &ffee ; OSWRCH ; start_tape &726d a9 e8 LDA #&e8 ; Read/Write 6850 IRQ mask &726f a2 fd LDX #&fd # Ignore RS423 receiver and transmitter interrupts &7271 a0 00 LDY #&00 &7273 20 f4 ff JSR &fff4 ; OSBYTE &7276 a9 03 LDA #&03 # Master reset &7278 8d 08 fe STA &fe08 ; Cassette ACIA control register &727b a9 19 LDA #&19 # 8 bit word, 1 stop bit, 1200 baud &727d 8d 08 fe STA &fe08 ; Cassette ACIA control register &7280 a9 85 LDA #&85 # Set to 2400 baud, switch on cassette motor &7282 8d 10 fe STA &fe10 ; Serial ULA control register &7285 60 RTS ; get_byte_from_tape &7286 ad 08 fe LDA &fe08 ; Cassette ACIA status register &7289 29 01 AND #&01 ; get_byte_from_tape &728b f0 f9 BEQ &7286 ; get_byte_from_tape # Has there been a receiver interrupt? &728d ad 08 fe LDA &fe08 ; Cassette ACIA status register &7290 29 50 AND #&50 # If so, has there been a parity or framing error? &7292 f0 02 BEQ &7296 ; not_error &7294 85 61 STA &61 ; checksum ; not_error &7296 ad 09 fe LDA &fe09 ; Cassette ACIA data register # Return a byte of data &7299 60 RTS ; read_leading_padding &729a a9 16 LDA #&16 &729c 20 f1 72 JSR &72f1 ; delay ; read_leading_padding_loop &729f 20 86 72 JSR &7286 ; get_byte_from_tape &72a2 c5 5a CMP &5a ; padding_key # Leading padding ends with three byte sequence &72a4 d0 f9 BNE &729f ; read_leading_padding_loop &72a6 20 86 72 JSR &7286 ; get_byte_from_tape &72a9 49 ff EOR #&ff &72ab c5 5a CMP &5a ; padding_key &72ad d0 f0 BNE &729f ; read_leading_padding_loop &72af 20 86 72 JSR &7286 ; get_byte_from_tape &72b2 c5 5a CMP &5a ; padding_key &72b4 d0 e9 BNE &729f ; read_leading_padding_loop &72b6 60 RTS ; read_header &72b7 a0 00 LDY #&00 &72b9 84 5f STY &5f ; code_address_low ; read_header_loop &72bb 20 86 72 JSR &7286 ; get_byte_from_tape # Read four bytes of block header: &72be 99 5b 00 STA &005b,Y ; header # &5b next_padding_key &72c1 c8 INY # &5c block &72c2 c0 04 CPY #&04 # &5d data_address_low &72c4 d0 f5 BNE &72bb ; read_header_loop # &5e data_address_high &72c6 68 PLA &72c7 85 63 STA &63 ; tmp # Low byte of return address &72c9 68 PLA &72ca 85 60 STA &60 ; code_address_high # High byte of return address (&72) &72cc 48 PHA &72cd a5 63 LDA &63 ; tmp &72cf 48 PHA # Push return address back on stack &72d0 60 RTS ; read_block &72d1 a0 00 LDY #&00 &72d3 84 61 STY &61 ; checksum ; read_block_loop &72d5 20 86 72 JSR &7286 ; get_byte_from_tape &72d8 91 5d STA (&5d),Y ; data_address &72da 45 61 EOR &61 ; checksum &72dc 85 61 STA &61 ; checksum &72de c8 INY &72df d0 f4 BNE &72d5 ; read_block &72e1 20 86 72 JSR &7286 ; get_byte_from_tape &72e4 a8 TAY ; read_trailing_padding_loop &72e5 20 86 72 JSR &7286 ; get_byte_from_tape &72e8 88 DEY &72e9 d0 fa BNE &72e5 ; read_trailing_padding_loop &72eb 20 86 72 JSR &7286 ; get_byte_from_tape &72ee c5 61 CMP &61 ; checksum &72f0 60 RTS ; delay &72f1 85 63 STA &63 ; count_high ; delay_high_loop &72f3 85 64 STA &64 ; count_middle ; delay_middle_loop &72f5 85 65 STA &65 ; count_low ; delay_low_loop &72f7 c6 65 DEC &65 ; count_low &72f9 d0 fc BNE &72f7 ; delay_low_loop &72fb c6 64 DEC &64 ; count_middle &72fd d0 f6 BNE &72f5 ; delay_middle_loop &72ff c6 63 DEC &63 ; count_high &7301 d0 f0 BNE &72f3 ; delay_high_loop &7303 60 RTS ; write_searching &7304 a2 00 LDX #&00 ; searching_string - strings &7306 20 32 73 JSR &7332 ; write_string &7309 a5 62 LDA &62 ; expected_block &730b 4c 57 72 JMP &7257 ; write_two_digit_hex_number ; write_loading &730e a2 0e LDX #&0e ; loading_string - strings &7310 20 32 73 JSR &7332 ; write_string &7313 a5 5c LDA &5c ; block &7315 4c 57 72 JMP &7257 ; write_two_digit_hex_number ; write_block_and_rewind &7318 a2 1a LDX #&1a ; block_string - strings &731a 20 32 73 JSR &7332 ; write_string &731d a5 5c LDA &5c ; block &731f 20 57 72 JSR &7257 ; write_two_digit_hex_number &7322 a5 5c LDA &5c ; block &7324 c5 62 CMP &62 ; expected_block &7326 90 dc BCC &7304 ; write_searching &7328 a2 26 LDX #&26 ; rewind_string - strings &732a 20 32 73 JSR &7332 ; write_string &732d a5 62 LDA &62 ; expected_block &732f 4c 57 72 JMP &7257 ; write_two_digit_hex_number ; write_string &7332 bd 40 73 LDA &7340,X ; strings &7335 c9 23 CMP #&23 ; "#" &7337 f0 06 BEQ &733f ; leave &7339 20 ee ff JSR &ffee ; OSWRCH &733c e8 INX &733d d0 f3 BNE &7332 ; write_string &733f 60 RTS ; strings ; searching_string &7340 1f 00 01 53 65 61 72 63 68 69 6e 67 20 23 ; TAB(&00, &01); "Searching " ; loading_string &734e 0c 20 20 4c 6f 61 64 69 6e 67 20 23 ; CLS; " Loading " ; block_string &735a 1e 20 42 6c 6f 63 6b 20 3f 20 20 23 ; HOME; " Block ? " ; rewind_string &7366 1f 00 01 52 65 77 69 6e 64 20 74 6f 20 23 ; TAB(&00, &01); "Rewind to " ; unused &7374 6f 58 76 13 c6 62 86 a4 21 29 e5 9e 03 15 95 e4 # Random bytes used to decrypt main binary &7384 df af c0 af 18 8b 4f 7b ba 61 cf 6a 1b f3 f8 66 &7394 97 7b c3 91 cd 6a a2 2b b3 ab 66 9f 28 63 db fd &73a4 fe 2e 58 a6 38 6c 48 df b7 d0 c2 e5 da b1 95 68 &73b4 42 f5 a0 3f 6f e1 3f 08 13 df e3 b9 29 f7 a0 3f &73c4 28 50 b9 30 e7 cd cc 77 b0 57 ee c6 f7 06 53 2e &73d4 50 b6 55 9f 19 21 5a 0f e0 df 14 95 0a 80 1a 65 &73e4 ca 23 1e 94 7f 52 98 53 70 90 0f e0 26 28 16 c7 &73f4 25 31 ac 31 57 3c 3e cd 71 b2 48 42 The loader loads the pages of the main binary in the following order: ; 0 1 2 3 4 5 6 7 8 9 a b c d e f ; &0700 &1900 &1a00 &1b00 &1c00 &1d00 &1e00 &1f00 &2000 &2100 &2200 &2300 &2400 &2500 &2600 &2700 ; &00 ; &2800 &2900 &2a00 &2b00 &2c00 &2d00 &2e00 &2f00 &3000 &3100 &7200 &3200 &7200 &3300 &3400 &3500 ; &10 ; &3600 &3700 &3800 &3900 &7200 &3a00 &3b00 &3c00 &3d00 &7200 &3e00 &3f00 &4000 &4100 &4200 &4300 ; &20 ; &4400 &4500 &4600 &4700 &4800 &4900 &4a00 &4b00 &4c00 &4d00 &4e00 &4f00 &5000 &5100 &5200 &5300 ; &30 ; &7200 &5400 &5500 &5600 &5700 &5800 &5900 &5a00 &5b00 &5c00 &5d00 &5e00 &5f00 &6000 &6100 &6200 ; &40 ; &6300 &6400 &0400 ; &50 &7200 - &72ff is reloaded at blocks &1a, &1c. &24, &29 and &40. Game variables and memory usage =============================== &0100 - &013f ; objects_flags &0140 - &017f ; objects_v_angle &0180 - &019f ; plottables_data &0180 - &01bf ; tile_raytrace_visibility_table &0400 - &07ff ; tiles_table &0900 - &093f ; objects_x &0940 - &097f ; objects_z &0980 - &09bf ; objects_y &09c0 - &09ff ; objects_h_angle &0a00 - &0a3f ; objects_z_fraction &0a40 - &0a7f ; objects_type &0a80 - &0abf ; plottables_screen_y_low &0ae0 - &0b3f ; plottables_screen_y_high &0b40 - &0b9f ; plottables_screen_x_high &0ba0 - &0bff ; plottables_relative_h_angle_low &3f00 - &4999 ; buffer &54a0 - &54ff ; plottables_screen_x_low &5500 - &5560 ; plottables_relative_h_angle_high &5a00 - &5aff ; polygon_left_edge_table &5b00 - &5bff ; polygon_right_edge_table &6000 - &7fff ; screen memory (&140 bytes per group of eight rows, 40 * 4 = 160 pixels wide, 25 * 8 = 200 pixels high) &0c00 - &0c01 ; sine_or_cosine_low &0c02 - &0c03 ; sine_or_cosine_high &0c04 ; bar_state &0c05 ; sprite_x &0c06 ; lowest_enemy_z &0c07 ; maximum_number_of_enemies &0c08 ; landscape_vertical_scale &0c09 ; previous_bar_state &0c0a ; player_energy &0c0c ; angle_high &0c0d ; stack_on_entry_to_update_enemies &0c0e ; meanie_rotation &0c0f ; force_single_colour_characters &0c10 - &0c18 ; character_buffer &0c19 ; platform_x &0c1a ; platform_y &0c1b ; force_pan_when_player_moves &0c1c ; title_screen_object &0c1d ; initial_panning_direction &0c1e ; not_ready_to_dither &0c1f ; suppress_update_of_visible_objects &0c20 - &0c27 ; enemies_draining_cooldown &0c28 - &0c2f ; enemies_rotation_cooldown &0c30 - &0c37 ; enemies_update_cooldown &0c40 - &0c44 : temporary_vertex_data &0c47 ; polygon_skip_in_pass_mask &0c48 ; previous_first_tile_to_consider_in_row &0c49 ; big_text_x_position &0c4a ; big_text_y_position &0c4b ; in_title_screen &0c4c ; background_type &0c4d ; what_to_plot_behind_object_to_update &0c4e ; player_has_died &0c4f ; player_exposure &0c50 ; all_enemies_cooldown &0c51 ; uturn_previously_pressed &0c52 ; zero_if_landscape_0000 &0c53 ; angle_times_pi_fraction &0c54 ; angle_times_pi &0c55 ; prnd_byte_after_generation &0c56 ; targeted_object_is_in_line_of_sight &0c57 ; object_relative_h_angle_high &0c58 ; targeted_object &0c59 ; object_relative_h_angle_low &0c5b ; object_relative_z_fraction &0c5c ; object_relative_z &0c5d ; object_relative_distance_low &0c5e ; object_relative_distance_high &0c5f ; sights_are_active &0c60 ; big_or_small_characters &0c61 ; player_action &0c62 ; visible_object_offset_in_groups &0c63 ; viewpoint_has_changed &0c64 ; player_quit_game &0c65 ; first_secret_code_check_bits &0c67 ; enemy_is_considering_boulder &0c68 ; enemy_angular_range_of_vision &0c69 ; buffer_width_in_groups &0c6a ; visible_object_width_in_groups &0c6b ; height_of_stack &0c6c ; top_object &0c6d ; how_to_plot_object_to_update &0c6e ; enemy_is_considering_robot &0c6f ; number_of_enemies &0c70 ; note_length &0c71 ; play_game_after_generation &0c72 ; game_is_paused &0c73 ; sound_type &0c74 ; dying_cooldown &0c75 ; landscape_plus_one_hundred_high &0c76 ; tree_was_in_line_of_sight &0c78 ; global_objects_x_offset &0c7a ; suppress_lines &0c7b - &0c7f ; prnd_state &0c80 - &0c87 ; enemies_meanie_search_object &0c88 - &0c8f ; enemies_energy_to_discharge &0c90 - &0c97 ; enemies_failed_meanie_memory &0c98 - &0c9f ; enemies_meanie_attempt_scans &0ca0 - &0ca7 ; enemies_meanie_object &0ca8 - &0caf ; enemies_targeted_object &0cb0 - &0cb7 ; enemies_targeted_object_exposure &0cb8 - &0cbf ; enemies_considering_meanie &0cc1 ; scrolling_steps_remaining &0cc2 ; viewport_screen_address_low &0cc3 ; viewport_screen_address_high &0cc4 ; sights_screen_address_low &0cc5 ; sights_screen_address_high &0cc6 ; sights_x &0cc7 ; sights_y &0cc8 ; sights_inertia &0cc9 ; size_of_sights &0cca ; status_bar_screen_address_low &0ccb ; status_bar_screen_address_high &0ccc ; sights_x_pixel &0ccd ; random_byte_offset_within_buffer &0cce ; suppress_second_secret_code_check &0ccf ; mask_for_dithering_randomness &0cd0 ; random_value_for_dithering &0cd1 ; scrolling_steps_total &0cd2 ; dither_count &0cd3 ; buffer_width_in_bytes &0cd4 ; previous_object_height_fraction &0cd7 ; suppress_plotting_of_sights &0cdc ; previous_busy_plotting &0cdd ; tree_is_in_line_of_sight &0cde ; player_has_hyperspaced &0cdf ; sound_cooldown &0ce4 ; busy_plotting &0ce5 ; start_of_game_grace &0ce6 ; player_is_on_platform &0ce7 ; tune_position &0ce8 ; player_wants_to_pan_left_or_right &0ce9 ; player_wants_to_create_absorb_uturn_or_hyperspace &0cea ; player_wants_to_pan_up_or_down &0ceb ; player_wants_to_change_volume_pause_or_unpause &0cf0 - &0cf7 ; input_buffer &0cfc ; not_playing_game &0cfd ; landscape_number_low &0cfe ; landscape_number_high Game disassembly ================ ; entry_point &0400 a1 00 LDA (&00,X) # Unnecessary ; read_addresses_loop &0402 20 86 72 JSR &7286 ; get_byte_from_tape # Read eight bytes of addresses: &0405 99 00 00 STA &0000,Y # &00, &01 table_address &0408 c8 INY # &02, &03 data_address &0409 c0 08 CPY #&08 # &04, &05 end_address &040b d0 f5 BNE &0402 ; read_addresses_loop # &06, &07 run_address &040d a0 20 LDY #&20 ; decrypt_0420_to_04ff_loop # Decrypt &0420 - &04ff &040f b9 00 04 LDA &0400,Y ; payload - &20 &0412 51 5f EOR (&5f),Y ; code_address # using &7220 - &72ff &0414 99 00 04 STA &0400,Y ; payload - &20 &0417 c8 INY &0418 d0 f5 BNE &040f ; decrypt_0400_to_04ff_loop &041a a9 00 LDA #&00 ; Identify Host/Operating System &041c a2 01 LDX #&01 ; Return host/OS in X &041e a0 00 LDY #&00 ; payload &0420 20 f4 ff JSR &fff4 ; OSBYTE &0423 e0 01 CPX #&01 ; BBC &0425 d0 19 BNE &0440 ; skip_resetting_vectors &0427 78 SEI &0428 ad b7 ff LDA &ffb7 ; os_rom_default_vector_table_address_low &042b 85 0a STA &0a ; default_vector_table_address_low &042d ad b8 ff LDA &ffb8 ; os_rom_default_vector_table_address_high &0430 85 0b STA &0b ; default_vector_table_address_high &0432 a0 00 LDY #&00 ; reset_vector_table_loop &0434 b1 0a LDA (&0a),Y ; default_vector_table_address &0436 99 00 02 STA &0200,Y ; os_vector_table &0439 c8 INY &043a cc b6 ff CPY &ffb6 ; os_rom_vector_table_length &043d d0 f5 BNE &0434 ; reset_vector_table_loop &043f 58 CLI ; skip_resetting_vectors &0440 a9 c8 LDA #&c8 ; Read/Write BREAK/ESCAPE effect &0442 a2 03 LDX #&03 ; Clear memory on next RESET &0444 a0 00 LDY #&00 &0446 20 f4 ff JSR &fff4 ; OSBYTE &0449 a9 7c LDA #&7c ; Clear ESCAPE condition &044b 20 f4 ff JSR &fff4 ; OSBYTE &044e a9 f9 LDA #&f9 &0450 8d 02 02 STA &0202 ; brk_vector_low &0453 a9 04 LDA #&04 ; &04f9 = loader_break_handler &0455 8d 03 02 STA &0203 ; brk_vector_high &0458 78 SEI ; decrypt_main_binary_loop # Decrypt &1900 - &64ff &0459 b1 02 LDA (&02),Y ; data_address &045b 51 5f EOR (&5f),Y ; key_address # using &7200 - &72ff &045d e6 60 INC &60 ; code_address_high &045f 51 5f EOR (&5f),Y ; key_address # and &7300 - &73ff &0461 c6 60 DEC &60 ; code_address_high &0463 51 00 EOR (&00),Y ; table_address # and &0700 - &07ff &0465 91 02 STA (&02),Y ; data_address &0467 c8 INY &0468 d0 ef BNE &0459 ; decrypt_main_binary_loop &046a e6 03 INC &03 ; target_address_high &046c a5 03 LDA &03 ; target_address_high &046e c5 05 CMP &05 ; end_address_high &0470 d0 e7 BNE &0459 ; decrypt_main_binary_loop &0472 a9 40 LDA #&40 # Give RS423 control of serial system, stop tape &0474 8d 10 fe STA &fe10 ; Serial ULA control register &0477 a0 00 LDY #&00 ; BRK &0479 98 TYA ; wipe_0400_to_047a_loop &047a 99 00 04 STA &0400,Y # Wipe &0400 - &047a with &00 (BRK) &047d c8 INY &047e d0 fa BNE &047a ; wipe_0400_to_047a_loop # When Y = &7a, call loader_break_handler ; unused &0480 cf 1e 9c c1 e9 0b f8 0b 7e 46 0d b6 b1 40 ce 4f &0490 33 a3 59 be 9e 51 63 59 4e c9 c5 a8 e6 c9 85 d0 &04a0 38 f0 d3 d3 f3 4b 53 45 98 2d 26 56 fb 7a fa 67 &04b0 a9 fb 28 88 8b 6d 84 b0 ab 08 9c 80 40 cb ec d1 &04c0 2c 4f 0b b6 7a 09 6d da cf fa af 95 ff fc e2 7a &04d0 59 62 18 f6 a3 c4 68 36 ca a2 6a 41 50 c5 66 79 &04e0 2e d8 ac 71 ec 19 20 9b 7a 0c 9d 99 60 5d 97 a3 &04f0 36 9c 08 6f d8 75 69 18 da ; loader_break_handler &04f9 68 PLA &04fa 68 PLA &04fb 68 PLA &04fc 58 CLI &04fd 6c 06 00 JMP (&0006) ; run_address # Jump to decrypted_entry_point ; random_table # Used to decrypt main binary at &0463 &0700 5b fd 47 6f 5b cc c3 b1 23 4c 9c 89 e7 38 88 4f &0710 6f b4 ac 88 5c a0 b0 21 29 ed fb c0 9d ce 6b 3f &0720 25 6d 75 09 02 e8 61 4c eb bd fb bc a2 0c cc 49 &0730 d3 a0 d8 93 ac 37 eb b8 22 c0 8c b0 2b 21 35 d7 &0740 9f 2f a1 3e db a1 31 f3 e7 dc 58 44 9d 62 87 7c &0750 64 f3 95 10 0e 84 98 d7 63 81 f0 80 57 f7 78 77 &0760 4a ff a8 dc 5e 29 35 40 fe 65 4c 80 21 1e 92 f8 &0770 2c 94 84 e4 46 f2 2c 4b 8a 4c b4 d0 81 34 bd f7 &0780 56 58 6f 51 f7 ca ac d7 7a 0d 4f 01 0a 6c a9 5e &0790 7a 19 29 74 b6 13 9c 7b bc 44 e7 63 b5 8d 7d bc &07a0 fe 3e fe 31 2a ef e3 df 63 68 90 34 bc 4b 8c ca &07b0 eb f4 64 95 8e 89 ec ad 52 54 a1 38 02 dd 1e 49 &07c0 01 b4 b1 35 db dd 8b 76 53 ea 84 3d 66 f0 eb b4 &07d0 cb 36 e9 2d 95 3a 3a d9 78 8f d8 d7 6b eb 5a 22 &07e0 2f ef 5f 06 ff ed 35 a8 e2 6c d3 5d 4b e9 38 3d &07f0 df 21 70 c8 03 7b 67 3d 27 9b bc 7f 36 02 39 06 # &1900 - &64ff is decrypted at &0459 # &1900 - &63ff is moved to &0d00 - &57ff at &6410 ; nmi_handler &0d00 40 RTI ; previous_irq1_vector_low &0d01 00 ; previous_irq1_vector_high &0d02 00 ; multiply_A_by_byte # Multiply two 8 bit numbers into 16 bit result &0d03 85 74 STA &74 ; multiplier ; multiply_byte_by_byte &0d05 a9 00 LDA #&00 &0d07 46 74 LSR &74 ; multiplier &0d09 90 03 BCC &0d0e ; skip_bit_0 &0d0b 18 CLC &0d0c 65 75 ADC &75 ; multiplicand ; skip_bit_0 &0d0e 6a ROR A &0d0f 66 74 ROR &74 ; multiplier &0d11 90 03 BCC &0d16 ; skip_bit_1 &0d13 18 CLC &0d14 65 75 ADC &75 ; multiplicand ; skip_bit_1 &0d16 6a ROR A &0d17 66 74 ROR &74 ; multiplier &0d19 90 03 BCC &0d1e ; skip_bit_2 &0d1b 18 CLC &0d1c 65 75 ADC &75 ; multiplicand ; skip_bit_2 &0d1e 6a ROR A &0d1f 66 74 ROR &74 ; multiplier &0d21 90 03 BCC &0d26 ; skip_bit_3 &0d23 18 CLC &0d24 65 75 ADC &75 ; multiplicand ; skip_bit_3 &0d26 6a ROR A &0d27 66 74 ROR &74 ; multiplier &0d29 90 03 BCC &0d2e ; skip_bit_4 &0d2b 18 CLC &0d2c 65 75 ADC &75 ; multiplicand ; skip_bit_4 &0d2e 6a ROR A &0d2f 66 74 ROR &74 ; multiplier &0d31 90 03 BCC &0d36 ; skip_bit_5 &0d33 18 CLC &0d34 65 75 ADC &75 ; multiplicand ; skip_bit_5 &0d36 6a ROR A &0d37 66 74 ROR &74 ; multiplier &0d39 90 03 BCC &0d3e ; skip_bit_6 &0d3b 18 CLC &0d3c 65 75 ADC &75 ; multiplicand ; skip_bit_6 &0d3e 6a ROR A &0d3f 66 74 ROR &74 ; multiplier &0d41 90 03 BCC &0d46 ; skip_bit_7 &0d43 18 CLC &0d44 65 75 ADC &75 ; multiplicand ; skip_bit_7 &0d46 6a ROR A &0d47 66 74 ROR &74 ; a_fraction &0d49 60 RTS # Leave with A = result_high, a_fraction = result_low ; calculate_partial_angle # Calculate arctan(a / b), where a < b ; division_round_1 # Start by calculating a / b &0d4a 06 74 ASL &74 ; a_fraction # Multiply a by 2 &0d4c 2a ROL A &0d4d b0 0c BCS &0d5b ; division_round_1_over # If overflow &0d4f c5 76 CMP &76 ; b &0d51 90 15 BCC &0d68 ; division_round_2 &0d53 d0 06 BNE &0d5b ; division_round_1_over # or a >= b &0d55 a4 74 LDY &74 ; a_fraction &0d57 c4 77 CPY &77 ; b_fraction &0d59 90 0d BCC &0d68 ; division_round_2 ; division_round_1_over &0d5b 85 75 STA &75 ; a &0d5d a5 74 LDA &74 ; a_fraction # then a = a - b &0d5f e5 77 SBC &77 ; b_fraction &0d61 85 74 STA &74 ; a_fraction &0d63 a5 75 LDA &75 ; a &0d65 e5 76 SBC &76 ; b &0d67 38 SEC # and set lowest bit of a_fraction ; division_round_2 &0d68 26 74 ROL &74 ; a_fraction # Multiply a by 2 &0d6a 2a ROL A # (bit 7 of a_fraction contains partial result) &0d6b b0 0c BCS &0d79 ; division_round_2_over # If overflow &0d6d c5 76 CMP &76 ; b &0d6f 90 15 BCC &0d86 ; division_round_3 &0d71 d0 06 BNE &0d79 ; division_round_2_over # or a >= b &0d73 a4 74 LDY &74 ; a_fraction &0d75 c4 77 CPY &77 ; b_fraction &0d77 90 0d BCC &0d86 ; division_round_3 ; division_round_2_over &0d79 85 75 STA &75 ; a &0d7b a5 74 LDA &74 ; a_fraction # then a = a - b &0d7d e5 77 SBC &77 ; b_fraction &0d7f 85 74 STA &74 ; a_fraction &0d81 a5 75 LDA &75 ; a &0d83 e5 76 SBC &76 ; b &0d85 38 SEC # and set lowest bit of a_fraction ; division_round_3 &0d86 26 74 ROL &74 ; a_fraction # Multiply a by 2 &0d88 2a ROL A # (bits 6-7 of a_fraction contain partial result) &0d89 b0 0c BCS &0d97 ; division_round_3_over # If overflow &0d8b c5 76 CMP &76 ; b &0d8d 90 15 BCC &0da4 ; consider_further_rounds &0d8f d0 06 BNE &0d97 ; division_round_3_over # or a >= b &0d91 a4 74 LDY &74 ; a_fraction &0d93 c4 77 CPY &77 ; b_fraction &0d95 90 0d BCC &0da4 ; consider_further_rounds ; division_round_3_over &0d97 85 75 STA &75 ; a &0d99 a5 74 LDA &74 ; a_fraction # then a = a - b &0d9b e5 77 SBC &77 ; b_fraction &0d9d 85 74 STA &74 ; a_fraction &0d9f a5 75 LDA &75 ; a &0da1 e5 76 SBC &76 ; b &0da3 38 SEC # and set carry ; consider_further_rounds &0da4 08 PHP # Push result of round 3 &0da5 c5 76 CMP &76 ; b # If a = b, &0da7 f0 67 BEQ &0e10 ; skip_further_division # skip rounds 4 - 10 ; division_round_4 &0da9 06 74 ASL &74 ; a_fraction # Multiply a by 2 &0dab 2a ROL A # (bits 5-6 of a_fraction contain partial result) &0dac b0 04 BCS &0db2 ; division_round_4_over # If overflow &0dae c5 76 CMP &76 ; b &0db0 90 03 BCC &0db5 ; division_round_5 # or a >= b ; division_round_4_over &0db2 e5 76 SBC &76 ; b # then a = a - b &0db4 38 SEC # and set lowest bit of a_fraction ; division_round_5 &0db5 26 74 ROL &74 ; a_fraction # Multiply a by 2 &0db7 2a ROL A # (bits 4-5 and 7 of a_fraction contain partial result) &0db8 b0 04 BCS &0dbe ; division_round_5_over # If overflow &0dba c5 76 CMP &76 ; b &0dbc 90 03 BCC &0dc1 ; division_round_6 # or a >= b ; division_round_5_over &0dbe e5 76 SBC &76 ; b # then a = a - b &0dc0 38 SEC # and set lowest bit of a_fraction ; division_round_6 &0dc1 26 74 ROL &74 ; a_fraction # Multiply a by 2 &0dc3 2a ROL A # (bits 3-4, 6-7 of a_fraction contain partial result) &0dc4 b0 04 BCS &0dca ; division_round_6_over # If overflow &0dc6 c5 76 CMP &76 ; b &0dc8 90 03 BCC &0dcd ; division_round_7 # or a >= b ; division_round_6_over &0dca e5 76 SBC &76 ; b # then a = a - b &0dcc 38 SEC # and set lowest bit of a_fraction ; division_round_7 &0dcd 26 74 ROL &74 ; a_fraction # Multiply a by 2 &0dcf 2a ROL A # (bits 2-3, 5-7 of a_fraction contain partial result) &0dd0 b0 04 BCS &0dd6 ; division_round_7_over # If overflow &0dd2 c5 76 CMP &76 ; b &0dd4 90 03 BCC &0dd9 ; division_round_8 # or a >= b ; division_round_7_over &0dd6 e5 76 SBC &76 ; b # then a = a - b &0dd8 38 SEC # and set lowest bit of a_fraction ; division_round_8 &0dd9 26 74 ROL &74 ; a_fraction # Multiply a by 2 &0ddb 2a ROL A # (bits 1-2, 4-7 of a_fraction contain partial result) &0ddc b0 04 BCS &0de2 ; division_round_8_over # If overflow &0dde c5 76 CMP &76 ; b &0de0 90 03 BCC &0de5 ; division_round_9 # or a >= b ; division_round_8_over &0de2 e5 76 SBC &76 ; b # then a = a - b &0de4 38 SEC # and set lowest bit of a_fraction ; division_round_9 &0de5 26 74 ROL &74 ; a_fraction # Multiply a by 2 &0de7 2a ROL A # (bits 0-1, 3-7 of a_fraction contain partial result) &0de8 b0 04 BCS &0dee ; division_round_9_over # If overflow &0dea c5 76 CMP &76 ; b &0dec 90 03 BCC &0df1 ; division_round_10 # or a >= b ; division_round_9_over &0dee e5 76 SBC &76 ; b # then a = a - b &0df0 38 SEC # and set highest bit of result_low (bit 0) ; division_round_10 &0df1 66 78 ROR &78 ; result_low &0df3 2a ROL A # Multiply a by 2 &0df4 b0 02 BCS &0df8 ; division_round_10_over # If overflow &0df6 c5 76 CMP &76 ; b # or a > b ; division_round_10_over &0df8 66 78 ROR &78 ; result_low # set highest bit of result_high (bits 0-1) &0dfa a5 74 LDA &74 ; a_fraction ; consider_overflow &0dfc 28 PLP # Pull result of round 3 &0dfd 90 02 BCC &0e01 ; division_round_3_under &0dff 69 1f ADC #&1f # If round 3 was over, set bit 2 (&1f + carry) ; division_round_3_under &0e01 90 1c BCC &0e1f ; no_overflow ; leave_with_forty_five_degrees # If (a / b) * 256 = 256, i.e. a = b, &0e03 a9 ff LDA #&ff &0e05 85 7e STA &7e ; partial_angle_tangent &0e07 a9 00 LDA #&00 &0e09 85 8a STA &8a ; angle_low &0e0b a9 20 LDA #&20 # &2000 = 45 degrees &0e0d 85 8b STA &8b ; angle_high &0e0f 60 RTS ; skip_further_division &0e10 a9 00 LDA #&00 &0e12 85 78 STA &78 ; result_low &0e14 66 74 ROR &74 ; a_fraction &0e16 6a ROR A &0e17 66 74 ROR &74 ; a_fraction &0e19 6a ROR A &0e1a 09 20 ORA #&20 # Set bit 2, corresponding to round 3 &0e1c 4c fc 0d JMP &0dfc ; consider_overflow ; no_overflow &0e1f a8 TAY # A = (a / b) * 256 &0e20 85 7e STA &7e ; partial_angle_tangent &0e22 b9 00 3b LDA &3b00,Y ; arctan_low_table # Look up arctan(a / b) in table &0e25 85 8a STA &8a ; angle_low &0e27 b9 01 3c LDA &3c01,Y ; arctan_high_table &0e2a 85 8b STA &8b ; angle_high &0e2c 24 78 BIT &78 ; result_low # Use lowest bits of division to make more accurate &0e2e 30 05 BMI &0e35 ; calculate_delta &0e30 70 1e BVS &0e50 ; skip_calculating_delta &0e32 4c 74 0e JMP &0e74 ; leave # Do nothing if round 9 and round 10 were both under ; calculate_delta # If round 10 was over, &0e35 a5 8a LDA &8a ; angle_low # Calculate delta = angle - arctan((a / b) + (1 / 256)) &0e37 38 SEC &0e38 f9 01 3b SBC &3b01,Y ; arctan_low_table + 1 &0e3b 85 74 STA &74 ; a_fraction &0e3d a5 8b LDA &8b ; angle_high &0e3f f9 02 3c SBC &3c02,Y ; arctan_high_table + 1 &0e42 24 78 BIT &78 ; result_low &0e44 50 03 BVC &0e49 ; skip_inversion # If round 9 was under, delta is negative &0e46 20 09 10 JSR &1009 ; invert_A_and_a_fraction # If round 9 was over, make delta positive ; skip_inversion &0e49 85 75 STA &75 ; a &0e4b 2a ROL A # Keeping the sign, &0e4c 66 75 ROR &75 ; a # divide delta by 2 &0e4e 66 74 ROR &74 ; a_fraction ; skip_calculating_delta # If round 9 or round 10 was over, &0e50 a5 8a LDA &8a ; angle_low # Calculate angle + arctan((a / b) + (1 / 256)) &0e52 18 CLC &0e53 79 01 3b ADC &3b01,Y ; arctan_low_table + 1 &0e56 85 8a STA &8a ; angle_low &0e58 a5 8b LDA &8b ; angle_high &0e5a 79 02 3c ADC &3c02,Y ; arctan_high_table + 1 &0e5d 85 8b STA &8b ; angle_high &0e5f 24 78 BIT &78 ; result_low &0e61 10 0d BPL &0e70 ; skip_adding_delta &0e63 a5 8a LDA &8a ; angle_low # If round 10 was over, &0e65 18 CLC &0e66 65 74 ADC &74 ; a_fraction # add delta &0e68 85 8a STA &8a ; angle_low &0e6a a5 8b LDA &8b ; angle_high &0e6c 65 75 ADC &75 ; a &0e6e 85 8b STA &8b ; angle_high ; skip_adding_delta &0e70 46 8b LSR &8b ; angle_high # Return average of angle + arctan((a / b) + (1 / 256)) &0e72 66 8a ROR &8a ; angle_low ; leave &0e74 60 RTS ; calculate_double_sine_and_cosine # Called with A = angle_high, a_fraction = angle_low &0e75 8d 0c 0c STA &0c0c ; angle_high &0e78 8e 3b 0f STX &0f3b ; tmp_x # Preserve X on exit &0e7b 20 3e 0f JSR &0f3e ; multiply_double_A_by_pi # Calculate angle * PI &0e7e 8d 53 0c STA &0c53 ; angle_times_pi_fraction &0e81 a5 75 LDA &75 ; a &0e83 8d 54 0c STA &0c54 ; angle_times_pi &0e86 a2 01 LDX #&01 ; cosine # Calculate sine first, then cosine &0e88 86 60 STX &60 ; second_value_to_calculate &0e8a a2 00 LDX #&00 ; sine &0e8c 2c 0c 0c BIT &0c0c ; angle_high &0e8f 50 03 BVC &0e94 ; calculate_double_sine_and_cosine_loop # unless sine and cosine have opposite signs, &0e91 e8 INX &0e92 c6 60 DEC &60 ; second_value_to_calculate # If so, calculate cosine first, then sine ; calculate_double_sine_and_cosine_loop &0e94 c9 7a CMP #&7a &0e96 90 09 BCC &0ea1 ; calculate_sine_or_cosine_method_one &0e98 b0 31 BCS &0ecb ; calculate_sine_or_cosine_method_two # Always branches ; unreachable_code &0e9a ad 53 0c LDA &0c53 ; angle_times_pi_fraction # Unreachable code &0e9d c9 f0 CMP #&f0 &0e9f b0 2a BCS &0ecb ; calculate_sine_or_cosine_method_two ; calculate_sine_or_cosine_method_one # Use cubic approximation (sin a =~ aPI - aPI ^ 3 / 6) &0ea1 a9 ab LDA #&ab ; 171 # 171 is approximately 4 * 256 / 6 or 256 * 2 / 3 &0ea3 20 03 0d JSR &0d03 ; multiply_A_by_byte # Returns A = angle_times_pi * 171 &0ea6 20 03 0d JSR &0d03 ; multiply_A_by_byte # Returns A = angle_times_pi ^ 2 * 171 &0ea9 85 76 STA &76 ; multiplier_high &0eab 20 4a 0f JSR &0f4a ; multiply_double_by_byte # Calculate angle_times_pi ^ 3 * 171 &0eae ad 53 0c LDA &0c53 ; angle_times_pi_fraction &0eb1 38 SEC &0eb2 e5 74 SBC &74 ; a_fraction # Subtract this from angle_times_pi &0eb4 85 74 STA &74 ; a_fraction &0eb6 ad 54 0c LDA &0c54 ; angle_times_pi &0eb9 e5 75 SBC &75 ; a &0ebb 06 74 ASL &74 ; a_fraction # Double &0ebd 2a ROL A &0ebe 9d 02 0c STA &0c02,X ; sine_or_cosine_high # Bug: 2 * (aPI - (aPI ^ 3 * 2 / 3)), so not quite right &0ec1 a5 74 LDA &74 ; a_fraction &0ec3 29 fe AND #&fe # Clear lowest bit, which will contain sign &0ec5 9d 00 0c STA &0c00,X ; sine_or_cosine_low &0ec8 4c fd 0e JMP &0efd ; calculate_next_value ; calculate_sine_or_cosine_method_two # Use quadratic approximation &0ecb a9 00 LDA #&00 # sin a =~ 1 - 2 * (PI / 4 - aPI) ^ 2 &0ecd 38 SEC &0ece ed 53 0c SBC &0c53 ; angle_times_pi_fraction &0ed1 85 74 STA &74 ; a_fraction &0ed3 a9 c9 LDA #&c9 # &0.c9 is approximately PI / 4 (&0.c90f...) &0ed5 ed 54 0c SBC &0c54 ; angle_times_pi &0ed8 85 75 STA &75 ; a # PI / 4 - aPI &0eda 85 76 STA &76 ; b &0edc 20 4a 0f JSR &0f4a ; multiply_double_by_byte # Calculate (PI / 4 - aPI) ^ 2 &0edf 06 74 ASL &74 ; a_fraction &0ee1 26 75 ROL &75 ; a # Double &0ee3 a9 00 LDA #&00 &0ee5 38 SEC &0ee6 e5 74 SBC &74 ; a_fraction &0ee8 29 fe AND #&fe # Clear lowest bit, which will contain sign &0eea 9d 00 0c STA &0c00,X ; sine_or_cosine_low &0eed a9 00 LDA #&00 # Subtract from 1 &0eef e5 75 SBC &75 ; a &0ef1 90 07 BCC &0efa ; skip_overflow &0ef3 a9 fe LDA #&fe # Use &fffe as limit (lowest bit will contain sign) &0ef5 9d 00 0c STA &0c00,X ; sine_or_cosine_low &0ef8 a9 ff LDA #&ff ; skip_overflow &0efa 9d 02 0c STA &0c02,X ; sine_or_cosine_high ; calculate_next_value &0efd e4 60 CPX &60 ; second_value_to_calculate &0eff f0 18 BEQ &0f19 ; set_signs # Have both sine and cosine been calculated? &0f01 a6 60 LDX &60 ; second_value_to_calculate # If not, calculate the other one &0f03 a9 00 LDA #&00 &0f05 38 SEC &0f06 ed 53 0c SBC &0c53 ; angle_times_pi_fraction &0f09 8d 53 0c STA &0c53 ; angle_times_pi_fraction &0f0c a9 c9 LDA #&c9 # &0.c9 is approximately PI / 4 (&0.c90f...) &0f0e ed 54 0c SBC &0c54 ; angle_times_pi &0f11 8d 54 0c STA &0c54 ; angle_times_pi # Use (PI / 4) - angle for other calculation &0f14 85 75 STA &75 ; a &0f16 4c 94 0e JMP &0e94 ; calculate_double_sine_and_cosine_loop ; set_signs &0f19 ad 0c 0c LDA &0c0c ; angle_high &0f1c 10 08 BPL &0f26 ; sine_is_positive # Top bit set if sin(angle) is negative &0f1e a9 01 LDA #&01 &0f20 0d 00 0c ORA &0c00 ; sine_or_cosine_low # Set lowest bit of low byte to invert value &0f23 8d 00 0c STA &0c00 ; sine_or_cosine_low # &0c00 + 0 for sine ; sine_is_positive &0f26 ad 0c 0c LDA &0c0c ; angle_high # One of top two bits set if cos(angle) is negative &0f29 0a ASL A &0f2a 4d 0c 0c EOR &0c0c ; angle_high &0f2d 10 08 BPL &0f37 ; cosine_is_positive &0f2f a9 01 LDA #&01 &0f31 0d 01 0c ORA &0c01 ; sine_or_cosine_low + 1 # Set lowest bit of low byte to invert value &0f34 8d 01 0c STA &0c01 ; sine_or_cosine_low + 1 # &0c00 + 1 for cosine ; cosine_is_positive &0f37 ae 3b 0f LDX &0f3b ; tmp_x # Leave with &0c00 = sin_low, &0c01 = cosine_low &0f3a 60 RTS # &0c02 = sin_high, &0c03 = cosine_high ; tmp_x &0f3b 00 ; multiply_byte_by_pi # Unused entry point &0f3c a9 00 LDA #&00 # actually LDA copy_of_landscape_high_from_prnd # Not used as code, but obfuscated variable ; multiply_double_A_by_pi &0f3e 06 74 ASL &74 : multiplier_low # Multiply A.a_fraction by 4 &0f40 2a ROL A &0f41 06 74 ASL &74 : multiplier_low &0f43 2a ROL A &0f44 85 76 STA &76 ; multiplier_high &0f46 a9 c9 LDA #&c9 ; 201 # Multiply by 804 (&324), approximately PI (&3.243f...) &0f48 85 75 STA &75 ; multiplicand ; multiply_double_by_byte # Multiply 16 bit and 8 bit number into 16 bit result &0f4a 20 05 0d JSR &0d05 ; multiply_byte_by_byte # Multiply multiplier_low * multiplicand &0f4d 85 77 STA &77 ; tmp # (a_fraction is discarded) &0f4f a5 76 LDA &76 ; multiplier_high &0f51 20 03 0d JSR &0d03 ; multiply_A_by_byte # Multiply multiplier_high * multiplicand &0f54 85 75 STA &75 ; result_high &0f56 a5 77 LDA &77 ; tmp &0f58 18 CLC &0f59 65 74 ADC &74 ; result_low &0f5b 85 74 STA &74 ; result_low &0f5d 90 02 BCC &0f61 ; skip_overflow &0f5f e6 75 INC &75 ; result_high ; skip_overflow &0f61 60 RTS # Leave with A = result_low, a = result_high ; check_for_keypress &0f62 98 TYA &0f63 48 PHA &0f64 a9 81 LDA #&81 ; Scan for a particular key &0f66 a0 ff LDY #&ff &0f68 20 f4 ff JSR &fff4 ; OSBYTE &0f6b 68 PLA &0f6c a8 TAY &0f6d e0 ff CPX #&ff # Leave with equal set if key pressed &0f6f 60 RTS ; calculate_sine_and_cosine # Returns &100 * ABS(SIN(RAD(a * 360/256)) * 256) &0f70 10 02 BPL &0f74 ; positive_sine # and &100 * ABS(COS(RAD(a * 360/256)) * 256) &0f72 49 40 EOR #&40 ; positive_sine # Top bit set if sine is negative &0f74 85 67 STA &67 ; sine_and_cosine_signs # Second highest bit set if cosine is negative &0f76 06 74 ASL &74 ; a_fraction &0f78 2a ROL A &0f79 29 7f AND #&7f &0f7b aa TAX &0f7c 49 7f EOR #&7f &0f7e 18 CLC &0f7f 69 01 ADC #&01 &0f81 10 02 BPL &0f85 ; skip_ceiling &0f83 a9 7f LDA #&7f ; skip_ceiling &0f85 a8 TAY &0f86 bd 80 59 LDA &5980,X ; sine_table &0f89 be 80 59 LDX &5980,Y ; sine_table &0f8c 24 67 BIT &67 ; sine_and_cosine_signs &0f8e 30 07 BMI &0f97 ; negative_sine &0f90 70 07 BVS &0f99 ; sine_and_cosine_are_opposite_signs ; sine_and_cosine_are_same_signs &0f92 85 8e STA &8e ; unsigned_sine &0f94 86 8f STX &8f ; unsigned_cosine &0f96 60 RTS ; negative_sine &0f97 70 f9 BVS &0f92 ; sine_and_cosine_are_same_signs ; sine_and_cosine_are_opposite_signs &0f99 85 8f STA &8f ; unsigned_cosine &0f9b 86 8e STX &8e ; unsigned_sine &0f9d 60 RTS ; multiply_double_by_double # Multiply two 16 bit numbers into 16 bit result &0f9e a5 6b LDA &6b ; double_y_high &0fa0 10 13 BPL &0fb5 ; skip_y_inversion # Is y negative? &0fa2 a9 00 LDA #&00 # If so, make it positive &0fa4 38 SEC &0fa5 e5 6a SBC &6a ; double_y_low &0fa7 85 6a STA &6a ; double_y_low &0fa9 a9 00 LDA #&00 &0fab e5 6b SBC &6b ; double_y_high &0fad 85 6b STA &6b ; double_y_high &0faf a5 67 LDA &67 ; invert_result # Zero prior to call &0fb1 49 80 EOR #&80 # Set top bit to invert result at end &0fb3 85 67 STA &67 ; invert_result ; skip_y_inversion &0fb5 a5 68 LDA &68 ; double_x_low &0fb7 29 01 AND #&01 # If lowest bit of double_x_low set, &0fb9 f0 06 BEQ &0fc1 ; skip_x_inversion &0fbb a5 67 LDA &67 ; invert_result &0fbd 49 80 EOR #&80 # Flip sign of result &0fbf 85 67 STA &67 ; invert_result ; skip_x_inversion &0fc1 a5 6b LDA &6b ; double_y_high &0fc3 85 75 STA &75 ; a &0fc5 a5 68 LDA &68 ; double_x_low &0fc7 20 03 0d JSR &0d03 ; multiply_A_by_byte # Multiply x_low * y_high &0fca 85 77 STA &77 ; result_low # Store in result_low and result_low_fraction &0fcc a5 74 LDA &74 ; a_fraction &0fce 18 CLC &0fcf 69 80 ADC #&80 # Round &0fd1 85 76 STA &76 ; result_low_fraction &0fd3 90 02 BCC &0fd7 ; skip_overflow_low &0fd5 e6 77 INC &77 ; result_low ; skip_overflow_low &0fd7 a5 69 LDA &69 ; double_x_high &0fd9 20 03 0d JSR &0d03 ; multiply_A_by_byte # Multiply x_high * y_high &0fdc 85 78 STA &78 ; result_high # Add to result_high and result_low &0fde a5 74 LDA &74 ; a_fraction &0fe0 18 CLC &0fe1 65 77 ADC &77 ; result_low &0fe3 85 77 STA &77 ; result_low &0fe5 90 02 BCC &0fe9 ; skip_overflow_high &0fe7 e6 78 INC &78 ; result_high ; skip_overflow_high &0fe9 a5 6a LDA &6a ; double_y_low &0feb 85 75 STA &75 ; a &0fed a5 69 LDA &69 ; double_x_high &0fef 20 03 0d JSR &0d03 ; multiply_A_by_byte # Multiply x_high * y_low &0ff2 85 75 STA &75 ; a # Add to result_low and result_low_fraction &0ff4 a5 74 LDA &74 ; a_fraction &0ff6 18 CLC &0ff7 65 76 ADC &76 ; result_low_fraction &0ff9 a5 75 LDA &75 ; a &0ffb 65 77 ADC &77 ; result_low &0ffd 85 74 STA &74 ; a_fraction &0fff 90 02 BCC &1003 ; skip_overflow_high &1001 e6 78 INC &78 ; result_high ; skip_overflow_high &1003 a5 78 LDA &78 ; result_high # Leave with A = result_high, a_fraction = result_low &1005 24 67 BIT &67 ; invert_result # Top bit set if final result should be inverted ; invert_A_and_a_fraction_if_negative &1007 10 0d BPL &1016 ; leave ; invert_A_and_a_fraction &1009 85 75 STA &75 ; a &100b a9 00 LDA #&00 &100d 38 SEC &100e e5 74 SBC &74 ; a_fraction &1010 85 74 STA &74 ; a_fraction &1012 a9 00 LDA #&00 &1014 e5 75 SBC &75 ; a ; leave &1016 60 RTS ; title_screen &1017 a2 ff LDX #&ff &1019 9a TXS &101a a9 04 LDA #&04 ; B # Set all four colours to blue &101c 20 2c 5e JSR &5e2c ; set_palette &101f 20 49 11 JSR &1149 ; reset_game_state &1022 a9 00 LDA #&00 # Clear top bit to display title screen &1024 20 4c 32 JSR &324c ; plot_title_or_completion_screen &1027 a2 00 LDX #&00 # "PRESS ANY KEY" &1029 20 ad 36 JSR &36ad ; plot_text &102c a9 87 LDA #&87 # Use title colour scheme, B K R Y &102e 20 2c 5e JSR &5e2c ; set_palette &1031 20 07 5e JSR &5e07 ; get_character_after_flushing_keyboard ; ask_for_landscape &1034 20 49 11 JSR &1149 ; reset_game_state &1037 a2 01 LDX #&01 # "LANDSCAPE NUMBER ?" &1039 20 ad 36 JSR &36ad ; plot_text &103c a9 04 LDA #&04 # Get four digit number for landscape &103e 20 9f 32 JSR &329f ; get_number_into_input_buffer &1041 20 21 33 JSR &3321 ; convert_input_buffer_to_number &1044 ac f1 0c LDY &0cf1 ; input_buffer + 1 &1047 ae f0 0c LDX &0cf0 ; input_buffer &104a 20 b7 33 JSR &33b7 ; seed_prnd_from_landscape_number &104d ad 52 0c LDA &0c52 ; zero_if_landscape_0000 &1050 d0 0d BNE &105f ; ask_for_code &1052 a2 03 LDX #&03 # Use stored code for landscape 0000 ; copy_code_loop &1054 bd 8c 10 LDA &108c,X ; landscape_0000_code &1057 9d f0 0c STA &0cf0,X ; input_buffer &105a ca DEX &105b 10 f7 BPL &1054 ; copy_code_loop &105d 30 0d BMI &106c ; clear_screen_and_generate_landscape # Always branches ; ask_for_code &105f a2 02 LDX #&02 # "SECRET ENTRY CODE?" &1061 20 ad 36 JSR &36ad ; plot_text &1064 a9 08 LDA #&08 # Get eight digit number for code &1066 20 9f 32 JSR &329f ; get_number_into_input_buffer &1069 20 21 33 JSR &3321 ; convert_input_buffer_to_number ; clear_screen_and_generate_landscape &106c a9 04 LDA #&04 ; B # Set all four colours to blue &106e 20 2c 5e JSR &5e2c ; set_palette &1071 20 9c 2a JSR &2a9c ; generate_landscape_and_play_game # Generate landscape and play game if code is correct ; wrong_code_screen &1074 20 49 11 JSR &1149 ; reset_game_state &1077 a9 00 LDA #&00 # Clear top bit to display title screen &1079 20 4c 32 JSR &324c ; plot_title_or_completion_screen &107c a9 87 LDA #&87 # Use title colour scheme, B K R Y &107e 20 2c 5e JSR &5e2c ; set_palette &1081 a2 03 LDX #&03 # "WRONG SECRET CODE" &1083 20 ad 36 JSR &36ad ; plot_text &1086 20 07 5e JSR &5e07 ; get_character_after_flushing_keyboard &1089 4c 34 10 JMP &1034 ; ask_for_landscape ; landscape_0000_code &108c 87 53 04 06 # 06045387 is the secret code for landscape 0000 ; fill_screen_with_background &1090 20 0c 13 JSR &130c ; reset_screen_start_address &1093 20 99 36 JSR &3699 ; wipe_status_bar &1096 a9 00 LDA #&00 ; BUFFER_WIDE &1098 20 63 29 JSR &2963 ; initialise_buffer_variables &109b a9 00 LDA #&00 # Start at top of screen &109d a0 18 LDY #&18 # 192 pixels high &109f a2 28 LDX #&28 # 160 pixels wide &10a1 20 02 22 JSR &2202 ; plot_background &10a4 ad 4c 0c LDA &0c4c ; background_type &10a7 c9 03 CMP #&03 &10a9 d0 0b BNE &10b6 ; leave &10ab a9 03 LDA #&03 &10ad 85 15 STA &15 ; count ; plot_stars_loop &10af 20 d5 56 JSR &56d5 ; fade_to_white # Change 80 random pixels to white &10b2 c6 15 DEC &15 ; count &10b4 d0 f9 BNE &10af ; plot_stars_loop ; leave &10b6 60 RTS ; pan_viewpoint &10b7 a4 08 LDY &08 ; previous_panning_direction &10b9 a6 6e LDX &6e ; object_to_consider # Player &10bb c0 02 CPY #&02 &10bd b0 3e BCS &10fd ; is_panning_vertically ; is_panning_horizontally &10bf bd c0 09 LDA &09c0,X ; objects_h_angle # Rotate player left or right &10c2 18 CLC &10c3 79 f4 38 ADC &38f4,Y ; panning_angles &10c6 9d c0 09 STA &09c0,X ; objects_h_angle &10c9 a9 19 LDA #&19 # Start at top of buffer &10cb a0 18 LDY #&18 # 192 pixels high &10cd a2 10 LDX #&10 # 80 pixels wide &10cf 8e 69 0c STX &0c69 ; buffer_width_in_groups &10d2 20 02 22 JSR &2202 ; plot_background # Fill buffer with sky &10d5 20 1e 39 JSR &391e ; initialise_tall_buffer &10d8 20 24 26 JSR &2624 ; plot_world # Returns carry clear if world completely plotted &10db a6 6e LDX &6e ; object_to_consider # Player &10dd a4 08 LDY &08 ; previous_panning_direction &10df b0 11 BCS &10f2 ; undo_horizontal_pan &10e1 d0 09 BNE &10ec ; is_panning_left ; is_panning_right &10e3 bd c0 09 LDA &09c0,X ; objects_h_angle # The player panned more than was necessary at &10bf &10e6 38 SEC # for the buffer to be drawn correctly; fix this once &10e7 e9 0c SBC #&0c # plotting done so right pans +8 to match left pan -8 &10e9 9d c0 09 STA &09c0,X ; objects_h_angle ; is_panning_left &10ec a9 10 LDA #&10 # Horizontal panning takes 16 steps &10ee 20 f8 38 JSR &38f8 ; initialise_buffer_unbuffering_address_and_panning_steps &10f1 60 RTS ; undo_horizontal_pan &10f2 bd c0 09 LDA &09c0,X ; objects_h_angle &10f5 38 SEC &10f6 f9 f4 38 SBC &38f4,Y ; panning_angles &10f9 9d c0 09 STA &09c0,X ; objects_h_angle &10fc 60 RTS ; is_panning_vertically &10fd bd 40 01 LDA &0140,X ; objects_v_angle # Stop the player panning too far up or down &1100 d9 45 11 CMP &1145,Y ; vertical_angle_limits - 2 &1103 f0 34 BEQ &1139 ; leave &1105 18 CLC # Rotate player up or down &1106 79 f4 38 ADC &38f4,Y ; panning_angles &1109 9d 40 01 STA &0140,X ; objects_v_angle &110c a9 19 LDA #&19 # Start at top of buffer &110e a0 08 LDY #&08 # 64 pixels high &1110 a2 28 LDX #&28 # 160 pixels wide &1112 8e 69 0c STX &0c69 ; buffer_width_in_groups &1115 20 02 22 JSR &2202 ; plot_background # Fill buffer with screen &1118 20 08 39 JSR &3908 ; initialise_wide_buffer &111b 20 24 26 JSR &2624 ; plot_world # Returns carry clear if world completely plotted &111e a6 0b LDX &0b ; player_object &1120 a4 08 LDY &08 ; previous_panning_direction &1122 b0 16 BCS &113a ; undo_vertical_pan &1124 c0 03 CPY #&03 &1126 d0 09 BNE &1131 ; is_panning_up ; is_panning_down &1128 bd 40 01 LDA &0140,X ; objects_v_angle # The player panned more than was necessary at &10bf &112b 18 CLC # for the buffer to be drawn correctly; fix this once &112c 69 08 ADC #&08 # plotting done so down pans -4 to match up pan +4 &112e 9d 40 01 STA &0140,X ; objects_v_angle ; is_panning_up &1131 a9 08 LDA #&08 # Vertical panning takes 8 steps &1133 20 f8 38 JSR &38f8 ; initialise_buffer_unbuffering_address_and_panning_steps ; to_set_tall_set_buffer_screen_y_values &1136 20 23 39 JSR &3923 ; set_tall_set_buffer_screen_y_values ; leave &1139 60 RTS ; undo_vertical_pan &113a bd 40 01 LDA &0140,X ; objects_v_angle &113d 38 SEC &113e f9 f4 38 SBC &38f4,Y ; panning_angles &1141 9d 40 01 STA &0140,X ; objects_v_angle &1144 4c 36 11 JMP &1136 ; to_set_tall_set_buffer_screen_y_values ; vertical_angle_limits ; u d &1147 35 cd ; reset_game_state &1149 38 SEC &114a 6e fc 0c ROR &0cfc ; not_playing_game # Set top bit to indicate game is inactive &114d a2 00 LDX #&00 ; reset_game_state_loop &114f a9 00 LDA #&00 &1151 9d 00 09 STA &0900,X ; objects_x # Wipe &0900 - &0aff with &00 &1154 9d 00 0a STA &0a00,X ; objects_z_fraction &1157 e0 90 CPX #&90 &1159 b0 02 BCS &115d ; skip_zero_page &115b 95 00 STA &00,X # Wipe &0000 - &008f with &00 ; skip_zero_page &115d e0 c0 CPX #&c0 &115f b0 03 BCS &1164 ; skip_first_page &1161 9d 00 01 STA &0100,X ; objects_flags # Wipe &0100 - &01bf with &00 ; skip_first_page &1164 e0 e4 CPX #&e4 &1166 90 02 BCC &116a ; skip_variables_page &1168 a9 80 LDA #&80 &116a 9d 00 0c STA &0c00,X # Wipe &0ce4 - &0cff with &80 ; skip_variables_page &116d e8 INX &116e e0 f0 CPX #&f0 &1170 90 dd BCC &114f ; reset_state_loop ; reset_tile_visibility_bit_table_and_object_flags &1172 a2 3f LDX #&3f ; reset_game_state_second_loop &1174 a9 ff LDA #&ff &1176 9d 80 3e STA &3e80,X ; tile_visibility_bit_table # Wipe &3e80 - &3eff with &ff &1179 9d c0 3e STA &3ec0,X ; tile_visibility_bit_table + &40 &117c a9 80 LDA #&80 &117e 9d 00 01 STA &0100,X ; objects_flags # Wipe &0100 - &013f with &80 &1181 ca DEX &1182 10 f0 BPL &1174 ; reset_game_state_second_loop &1184 ee 7d 0c INC &0c7d ; prnd_state + 2 &1187 20 23 39 JSR &3923 ; set_tall_set_buffer_screen_y_values &118a 60 RTS ; check_for_full_player_input &118b a9 80 LDA #&80 # Set top bit to indicate no panning &118d 85 09 STA &09 ; panning_direction &118f a2 8e LDX #&8e ; f1 &1191 20 62 0f JSR &0f62 ; check_for_keypress &1194 d0 04 BNE &119a ; f1_not_presed # If the player pressed f1, &1196 38 SEC &1197 6e 64 0c ROR &0c64 ; player_quit_game # Set top bit to indicate player quit game ; f1_not_presed &119a a2 9d LDX #&9d ; SPACE &119c 20 62 0f JSR &0f62 ; check_for_keypress &119f d0 1f BNE &11c0 ; space_not_pressed # If the player pressed SPACE, &11a1 ad 22 12 LDA &1222 ; space_previously_pressed &11a4 d0 1f BNE &11c5 ; skip_sights_toggle &11a6 ad 5f 0c LDA &0c5f ; sights_are_active &11a9 49 80 EOR #&80 # Toggle top bit to toggle sights &11ab 8d 5f 0c STA &0c5f ; sights_are_active &11ae 10 09 BPL &11b9 ; sights_not_active # Top bit set if sights are active &11b0 20 31 13 JSR &1331 ; initialise_sights &11b3 20 d9 39 JSR &39d9 ; plot_sights # Plot sights &11b6 4c bc 11 JMP &11bc ; prevent_auto_repeat ; sights_not_active &11b9 20 a7 3a JSR &3aa7 ; unplot_sights # or unplot sights ; prevent_auto_repeat &11bc a9 80 LDA #&80 # Set top bit to prevent auto-repeat &11be d0 02 BNE &11c2 ; set_space_previously_pressed ; space_not_pressed &11c0 a9 00 LDA #&00 # Clear top bit to allow SPACE to be pressed again ; set_space_previously_pressed &11c2 8d 22 12 STA &1222 ; space_previously_pressed ; skip_sights_toggle &11c5 a0 0e LDY #&0e # Check for all other keys &11c7 20 53 13 JSR &1353 ; check_for_player_input &11ca 10 11 BPL &11dd ; player_wants_to_move # Top bit clear if player moving in any direction &11cc a9 6b LDA #&6b ; 01101011 # Zero bit for movement, one for not, topmost first &11ce 8d c8 0c STA &0cc8 ; sights_inertia &11d1 ad e9 0c LDA &0ce9 ; player_wants_to_create_absorb_uturn_or_hyperspace &11d4 10 2a BPL &1200 ; set_busy_plotting # Top bit set if player wants to do something &11d6 a9 40 LDA #&40 &11d8 8d 51 0c STA &0c51 ; uturn_previously_pressed # Set &40 to prevent auto-repeat of u-turn &11db d0 2b BNE &1208 ; not_panning # Always branches ; player_wants_to_move &11dd ae 5f 0c LDX &0c5f ; sights_are_active &11e0 10 0b BPL &11ed ; sights_not_visible &11e2 0e c8 0c ASL &0cc8 ; sights_inertia &11e5 b0 21 BCS &1208 ; not_panning &11e7 20 27 39 JSR &3927 ; move_sights # Move sights, which may set panning_direction &11ea 4c f9 11 JMP &11f9 ; check_panning ; sights_not_visible &11ed ad e8 0c LDA &0ce8 ; player_wants_to_pan_left_or_right # Does the player want to pan? &11f0 10 05 BPL &11f7 ; set_panning_direction &11f2 ad ea 0c LDA &0cea ; player_wants_to_pan_up_or_down &11f5 30 11 BMI &1208 ; not_panning ; set_panning_direction &11f7 85 09 STA &09 ; panning_direction # If so, set panning direction ; check_panning &11f9 a5 09 LDA &09 ; panning_direction &11fb 30 0b BMI &1208 ; not_panning &11fd 8d 1d 0c STA &0c1d ; initial_panning_direction ; set_busy_plotting &1200 a9 80 LDA #&80 &1202 8d e4 0c STA &0ce4 ; busy_plotting # Set top bit to indicate world is being plotted &1205 8d 1e 0c STA &0c1e ; not_ready_to_dither # Set top bit to indicate dithering can't happen yet ; not_panning &1208 ad e4 0c LDA &0ce4 ; busy_plotting &120b 8d dc 0c STA &0cdc ; previous_busy_plotting &120e 60 RTS ; check_if_player_still_wants_to_pan &120f 78 SEI &1210 a0 03 LDY #&03 # Check for left, right, up and down only &1212 20 53 13 JSR &1353 ; check_for_player_input &1215 ad 1d 0c LDA &0c1d ; initial_panning_direction &1218 cd e8 0c CMP &0ce8 ; player_wants_to_pan_left_or_right &121b f0 03 BEQ &1220 ; leave # Leave with equal set if player still wants to pan &121d cd ea 0c CMP &0cea ; player_wants_to_pan_up_or_down ; leave &1220 58 CLI &1221 60 RTS ; space_previously_pressed &1222 00 ; unused &1223 00 ; put_object_in_random_tile_below_z &1224 85 06 STA &06 ; z &1226 a9 00 LDA #&00 &1228 85 15 STA &15 ; attempts ; put_object_in_random_tile_below_z_loop &122a c6 15 DEC &15 ; attempts &122c d0 08 BNE &1236 ; skip_increase &122e e6 06 INC &06 ; z &1230 a5 06 LDA &06 ; z &1232 c9 0c CMP #&0c &1234 b0 22 BCS &1258 ; leave_with_carry_set ; skip_increase &1236 20 5a 12 JSR &125a ; get_random_tile_coordinate &1239 85 24 STA &24 ; tile_x &123b 20 5a 12 JSR &125a ; get_random_tile_coordinate &123e 85 26 STA &26 ; tile_y &1240 20 78 2b JSR &2b78 ; calculate_tile_address &1243 b0 e5 BCS &122a ; put_object_in_random_tile_below_z_loop # Carry set if tile contains object &1245 29 0f AND #&0f &1247 d0 e1 BNE &122a ; put_object_in_random_tile_below_z_loop # Can't use tile if not flat &1249 b1 5e LDA (&5e),Y ; tile_address &124b 4a LSR A &124c 4a LSR A &124d 4a LSR A &124e 4a LSR A &124f c5 06 CMP &06 ; z &1251 b0 d7 BCS &122a ; put_object_in_random_tile_below_z_loop # Can't use tile if too high &1253 20 ff 1e JSR &1eff ; put_object_in_tile &1256 18 CLC # Leave with carry clear to indicate success &1257 60 RTS ; leave_with_carry_set &1258 38 SEC # Leave with carry set to indicate failure &1259 60 RTS ; get_random_tile_coordinate &125a 20 94 31 JSR &3194 ; prnd &125d 29 1f AND #&1f &125f c9 1f CMP #&1f &1261 b0 f7 BCS &125a ; get_random_tile_coordinate &1263 60 RTS ; update_game &1264 a9 00 LDA #&00 &1266 8d e4 0c STA &0ce4 ; busy_plotting # Clear top bit to indicate world is not being plotted &1269 8d 51 0c STA &0c51 ; uturn_previously_pressed # Clear top bit to allow u-turn ; update_game_loop &126c ad e4 0c LDA &0ce4 ; busy_plotting # Top bit set if world is being plotted &126f 30 3c BMI &12ad ; is_plotting ; is_not_plotting &1271 4e 1f 0c LSR &0c1f ; suppress_update_of_visible_objects # Clear top bit to allow visible objects to be replotted &1274 ad dc 0c LDA &0cdc ; previous_busy_plotting # Top bit set if world was being plotted &1277 10 09 BPL &1282 ; skip_suppression &1279 20 0f 12 JSR &120f ; check_if_player_still_wants_to_pan # Returns equal set if player still wants to pan &127c d0 04 BNE &1282 ; skip_suppression &127e 38 SEC &127f 6e 1f 0c ROR &0c1f ; suppress_update_of_visible_objects # Set top bit to prevent visible objects being replotted ; skip_suppression &1282 20 a8 16 JSR &16a8 ; update_enemies &1285 ad 4e 0c LDA &0c4e ; player_has_died # Top bit set if player died by draining or hyperspacing &1288 f0 0a BEQ &1294 ; player_hasn't_died &128a a9 1e LDA #&1e # Display death screen for 30 when killed by enemy &128c 20 24 5f JSR &5f24 ; death_screen ; leave_update_game &128f 20 00 12 JSR &1200 ; set_busy_plotting &1292 38 SEC # Leave with carry set to indicate viewpoint has changed &1293 60 RTS ; player_hasn't_died &1294 0e 63 0c ASL &0c63 ; viewpoint_has_changed # Rotate top bit into carry &1297 b0 f6 BCS &128f ; leave_update_game &1299 2c 64 0c BIT &0c64 ; player_quit_game # Top bit set if player quit game &129c 30 f1 BMI &128f ; leave_update_game &129e 20 1a 19 JSR &191a ; calculate_player_exposure &12a1 20 e1 34 JSR &34e1 ; consider_pausing_game &12a4 20 5a 35 JSR &355a ; update_sound &12a7 20 80 34 JSR &3480 ; consider_changing_volume &12aa 4c 6c 12 JMP &126c ; update_game_loop ; is_plotting &12ad a5 09 LDA &09 ; panning_direction &12af 30 02 BMI &12b3 ; consider_player_action # Is the player wanting to pan? If so, &12b1 18 CLC # Leave with carry clear to indicate panning wanted &12b2 60 RTS ; consider_player_action &12b3 ad e9 0c LDA &0ce9 ; player_wants_to_create_absorb_uturn_or_hyperspace &12b6 30 33 BMI &12eb ; to_update_game &12b8 c9 22 CMP #&22 ; ACTION_HYPERSPACE &12ba b0 05 BCS &12c1 ; skip_sights_check # Player can't create, absorb or transfer &12bc 2c 5f 0c BIT &0c5f ; sights_are_active # if sights are not active, but can hyperspace or u-turn &12bf 10 2a BPL &12eb ; to_update_game ; skip_sights_check &12c1 8d 61 0c STA &0c61 ; player_action &12c4 4e e5 0c LSR &0ce5 ; start_of_game_grace # Clear top bit when player takes action &12c7 20 0b 1b JSR &1b0b ; handle_player_actions # Returns carry clear if an object was created &12ca b0 19 BCS &12e5 ; no_object_was_affected # or removed as a result of the player's action &12cc 20 53 35 JSR &3553 ; flush_sound_channel_0 &12cf a9 02 LDA #&02 ; sound_2 &12d1 20 40 34 JSR &3440 ; play_sound_with_envelope # Play sound for creating or absorbing object &12d4 a9 c0 LDA #&c0 # Set &80 to unplot sights, set &40 to dither object &12d6 8d 6d 0c STA &0c6d ; how_to_plot_object_to_update &12d9 4e 1e 0c LSR &0c1e ; not_ready_to_dither # Clear top bit to permit dithering to occur &12dc 20 84 1f JSR &1f84 ; update_object_on_screen &12df 20 53 35 JSR &3553 ; flush_sound_channel_0 &12e2 20 c7 36 JSR &36c7 ; plot_status_bar ; no_object_was_affected &12e5 0e 63 0c ASL &0c63 ; viewpoint_has_changed # Rotate top bit into carry, set if viewpoint changed &12e8 90 01 BCC &12eb ; to_update_game &12ea 60 RTS # Leave with carry set to indicate viewpoint has changed ; to_update_game &12eb 4c 64 12 JMP &1264 ; update_game ; update_enemy_cooldowns &12ee ad 50 0c LDA &0c50 ; all_enemies_cooldown &12f1 d0 15 BNE &1308 ; skip_enemy_cooldowns &12f3 a2 17 LDX #&17 ; update_enemy_cooldowns_loop &12f5 bd 20 0c LDA &0c20,X ; enemies_draining_cooldown # Also affects enemies_rotation_cooldown, &12f8 c9 02 CMP #&02 # and enemies_update_cooldown &12fa 90 03 BCC &12ff ; skip_this_cooldown &12fc de 20 0c DEC &0c20,X ; enemies_draining_cooldown # Cooldowns stick at 1 until changed elsewhere ; skip_this_cooldown &12ff ca DEX &1300 10 f3 BPL &12f5 ; update_enemy_cooldowns_loop &1302 a9 02 LDA #&02 &1304 8d 50 0c STA &0c50 ; all_enemies_cooldown &1307 60 RTS ; skip_enemy_cooldowns &1308 ce 50 0c DEC &0c50 ; all_enemies_cooldown &130b 60 RTS ; reset_screen_start_address &130c 78 SEI &130d a9 c0 LDA #&c0 &130f 8d c2 0c STA &0cc2 ; viewport_screen_address_low &1312 a9 60 LDA #&60 ; &60c0 &1314 8d c3 0c STA &0cc3 ; viewport_screen_address_high &1317 20 d3 3a JSR &3ad3 ; calculate_status_bar_screen_address &131a a9 0f LDA #&0f ; &0ff0 = &7f80 / 8 &131c 48 PHA &131d a9 f0 LDA #&f0 &131f a2 0d LDX #&0d # R13: Displayed screen start address register (low) &1321 8e 00 fe STX &fe00 ; video register number &1324 8d 01 fe STA &fe01 ; video register value &1327 ca DEX # R12: Displayed screen start address register (high) &1328 8e 00 fe STX &fe00 ; video register number &132b 68 PLA &132c 8d 01 fe STA &fe01 ; video register value &132f 58 CLI &1330 60 RTS ; initialise_sights &1331 ad c2 0c LDA &0cc2 ; viewport_screen_address_low &1334 18 CLC &1335 69 a0 ADC #&a0 &1337 8d c4 0c STA &0cc4 ; sights_screen_address_low &133a ad c3 0c LDA &0cc3 ; viewport_screen_address_high &133d 69 0f ADC #&0f &133f c9 80 CMP #&80 &1341 90 02 BCC &1345 ; skip_wraparound &1343 e9 20 SBC #&20 ; skip_wraparound &1345 8d c5 0c STA &0cc5 ; sights_screen_address_high &1348 a9 50 LDA #&50 &134a 8d c6 0c STA &0cc6 ; sights_x &134d a9 5f LDA #&5f &134f 8d c7 0c STA &0cc7 ; sights_y &1352 60 RTS ; check_for_player_input &1353 a2 03 LDX #&03 &1355 a9 80 LDA #&80 # Set top bit to indicate no action wanted ; unset_player_wants_loop # Affects player_wants_to_pan_left_or_right, &1357 9d e8 0c STA &0ce8,X ; player_wants # player_wants_to_create_absorb_uturn_or_hyperspace, &135a ca DEX # player_wants_to_pan_up_or_down, &135b 10 fa BPL &1357 ; unset_player_wants_loop # and player_wants_to_change_volume_pause_or_unpause ; check_for_player_input_loop &135d be 7d 13 LDX &137d,Y ; keycode_table &1360 20 62 0f JSR &0f62 ; check_for_keypress &1363 d0 0e BNE &1373 ; key_not_pressed &1365 b9 8c 13 LDA &138c,Y ; actions_table &1368 29 03 AND #&03 # Low two bits are variable &136a aa TAX &136b b9 8c 13 LDA &138c,Y ; actions_table &136e 4a LSR A # Top six bits are value &136f 4a LSR A &1370 9d e8 0c STA &0ce8,X ; player_wants ; key_not_pressed &1373 88 DEY &1374 10 e7 BPL &135d ; check_for_player_input_loop &1376 ad e8 0c LDA &0ce8 ; player_wants_to_pan_left_or_right &1379 2d ea 0c AND &0cea ; player_wants_to_pan_up_or_down # Leave with top bit clear if direction pressed &137c 60 RTS ; keycode_table &137d ae ; S # Pan left &137e cd ; D # Pan right &137f a9 ; L # Pan up &1380 99 ; , # Pan down &1381 be ; A # Absorb &1382 ef ; Q # Transfer &1383 cc ; R # Create robot &1384 dc ; T # Create tree &1385 9b ; B # Create boulder &1386 ab ; H # Hyperspace &1387 db ; 7 # Decrease volume &1388 ea ; 8 # Increase volume &1389 96 ; COPY # Pause &138a a6 ; DELETE # Unpause &138b ca ; U # U-turn ; actions_table &138c 04 ; 0, &01 MOVE_LEFT &138d 00 ; 0, &00 MOVE_RIGHT &138e 0a ; 2, &02 MOVE_UP &138f 0e ; 2, &03 MOVE_DOWN &1390 81 ; 1, &20 ACTION_ABSORB &1391 85 ; 1, &21 ACTION_TRANSFER &1392 01 ; 1, &00 ACTION_CREATE_ROBOT &1393 09 ; 1, &02 ACTION_CREATE_TREE &1394 0d ; 1, &03 ACTION_CREATE_BOULDER &1395 89 ; 1, &22 ACTION_HYPERSPACE &1396 03 ; 3, &00 INTERFACE_VOLUME_DOWN &1397 07 ; 3, &01 INTERFACE_VOLUME_UP &1398 0b ; 3, &02 INTERFACE_PAUSE &1399 0f ; 3, &03 INTERFACE_UNPAUSE &139a 8d ; 1, &23 ACTION_UTURN ; plot_title_and_preview_screen_polygons # Y = perspective type &139b 8d 1c 0c STA &0c1c ; title_screen_object # A = object type, or &80 for none &139e 8e 4c 0c STX &0c4c ; background_type # X = background type &13a1 8a TXA &13a2 a2 02 LDX #&02 &13a4 49 03 EOR #&03 &13a6 8d 97 57 STA &5797 ; double_colour_character_string + 1 # Set colour of text &13a9 f0 01 BEQ &13ac ; is_black &13ab e8 INX ; is_black &13ac 8e a2 57 STX &57a2 ; double_colour_character_string + 12 # Set colour of shadow &13af 8c 0f 14 STY &140f ; viewpoint_perspective &13b2 b9 03 14 LDA &1403,Y ; viewpoints_observer_z &13b5 8d 50 09 STA &0950 ; objects_z + &10 # Use object &10 as observer &13b8 b9 05 14 LDA &1405,Y ; viewpoints_v_angle &13bb 8d 50 01 STA &0150 ; objects_v_angle + &10 &13be b9 07 14 LDA &1407,Y ; viewpoints_observer_x &13c1 8d 10 09 STA &0910 ; objects_x + &10 &13c4 b9 0b 14 LDA &140b,Y ; viewpoints_observer_y &13c7 8d 90 09 STA &0990 ; objects_y + &10 &13ca b9 0d 14 LDA &140d,Y ; viewpoints_h_angle &13cd 8d d0 09 STA &09d0 ; objects_h_angle + &10 &13d0 b9 09 14 LDA &1409,Y ; viewpoints_global_objects_x_offset &13d3 48 PHA &13d4 20 90 10 JSR &1090 ; fill_screen_with_background &13d7 ad 1c 0c LDA &0c1c ; title_screen_object &13da 30 03 BMI &13df ; skip_object &13dc 20 e5 5f JSR &5fe5 ; plot_title_screen_object_on_platform ; skip_object &13df 68 PLA &13e0 8d 78 0c STA &0c78 ; global_objects_x_offset &13e3 a2 10 LDX #&10 &13e5 86 6e STX &6e ; object_to_consider # Observer &13e7 20 24 26 JSR &2624 ; plot_world &13ea ad 0f 14 LDA &140f ; viewpoint_perspective # Zero if preview &13ed d0 0b BNE &13fa ; not_preview &13ef a2 7f LDX #&7f # Set viewpoint_perspective to non-zero for game &13f1 8e 0f 14 STX &140f ; viewpoint_perspective ; wipe_tile_visibility_bit_table_loop # Wipe &3e80 - &3eff with &00 &13f4 9d 80 3e STA &3e80,X ; tile_visibility_bit_table &13f7 ca DEX &13f8 10 fa BPL &13f4 ; wipe_tile_visibility_bit_table_loop ; not_preview &13fa a9 00 LDA #&00 &13fc 8d 78 0c STA &0c78 ; global_objects_x_offset &13ff 8d 4c 0c STA &0c4c ; background_type &1402 60 RTS ; 0 1 # 0 = preview, 1 = title screen ; viewpoints_observer_z &1403 21 4b ; viewpoints_v_angle &1405 ea d9 ; viewpoints_observer_x &1407 0f 00 ; viewpoints_global_objects_x_offset &1409 00 ef ; viewpoints_observer_y &140b c2 bf ; viewpoints_h_angle &140d 00 12 ; viewpoint_perspective &140f 01 ; set_palette_and_initialise_enemies &1410 ad 52 0c LDA &0c52 ; zero_if_landscape_0000 &1413 d0 04 BNE &1419 ; use_enemies_based_on_landscape_number &1415 a9 01 LDA #&01 # Only the sentinel on landscape 0000 &1417 d0 0b BNE &1424 ; set_number_of_enemies ; use_enemies_based_on_landscape_number &1419 20 f0 33 JSR &33f0 ; get_maximum_number_of_enemies # Use topmost digit of landscape number to get a second &141c cd 07 0c CMP &0c07 ; maximum_number_of_enemies # limit on the number of enemies (see &33da for first) &141f 90 03 BCC &1424 ; set_number_of_enemies &1421 ad 07 0c LDA &0c07 ; maximum_number_of_enemies ; set_number_of_enemies &1424 8d 6f 0c STA &0c6f ; number_of_enemies # Use the minimum of the two as number of enemies &1427 20 eb 14 JSR &14eb ; initialise_enemies &142a ad 6f 0c LDA &0c6f ; number_of_enemies &142d 38 SEC &142e e9 01 SBC #&01 &1430 29 07 AND #&07 &1432 aa TAX &1433 bd c4 14 LDA &14c4,X ; landscape_colours_three # Colour scheme depends on number of enemies &1436 8d 5a 5e STA &5e5a ; colour_schemes + 3 &1439 bd e3 14 LDA &14e3,X ; landscape_colours_two &143c 8d 59 5e STA &5e59 ; colour_schemes + 2 &143f 60 RTS ; initialise_player_and_trees &1440 a9 00 LDA #&00 ; TYPE_ROBOT &1442 20 0e 21 JSR &210e ; create_object &1445 86 0b STX &0b ; player_object &1447 a9 0a LDA #&0a # Player starts with 10 energy &1449 8d 0a 0c STA &0c0a ; player_energy &144c ad 52 0c LDA &0c52 ; zero_if_landscape_0000 &144f d0 0e BNE &145f ; not_landscape_0000 &1451 a9 08 LDA #&08 # Player starts in fixed position on landscape 0000 &1453 85 24 STA &24 ; tile_x &1455 a9 11 LDA #&11 &1457 85 26 STA &26 ; tile_y &1459 20 ff 1e JSR &1eff ; put_object_in_tile &145c 4c 6d 14 JMP &146d ; initialise_trees ; not_landscape_0000 &145f ad 06 0c LDA &0c06 ; lowest_enemy_z # Otherwise, player starts on random tile below enemies &1462 c9 06 CMP #&06 &1464 90 02 BCC &1468 ; skip_floor &1466 a9 06 LDA #&06 ; skip_floor &1468 20 24 12 JSR &1224 ; put_object_in_random_tile_below_z # Returns carry set if object couldn't be put in tile &146b b0 f2 BCS &145f ; not_landscape_0000 # Repeat until player is successfully placed ; initialise_trees &146d a9 30 LDA #&30 &146f 38 SEC &1470 ed 6f 0c SBC &0c6f ; number_of_enemies &1473 ed 6f 0c SBC &0c6f ; number_of_enemies &1476 ed 6f 0c SBC &0c6f ; number_of_enemies &1479 85 75 STA &75 ; energy_cap # Maximum landscape energy is 48 - number_of_enemies * 3 &147b 20 1b 34 JSR &341b ; get_random_number_between_0_and_22 &147e 18 CLC &147f 69 0a ADC #&0a # Landscape energy is randomly chosen between 10 - 32 &1481 c5 75 CMP &75 ; energy_cap &1483 90 02 BCC &1487 ; skip_ceiling &1485 a5 75 LDA &75 ; energy_cap # but no greater than maximum ; skip_ceiling &1487 85 1e STA &1e ; number_of_trees ; add_trees_loop &1489 a9 02 LDA #&02 ; TYPE_TREE &148b 20 0e 21 JSR &210e ; create_object &148e ad 06 0c LDA &0c06 ; lowest_enemy_z # Trees are placed below enemies &1491 20 24 12 JSR &1224 ; put_object_in_random_tile_below_z # Returns carry set if object couldn't be put in tile &1494 b0 04 BCS &149a ; skip_remaining_trees # Stop adding trees if no suitable tile could be found &1496 c6 1e DEC &1e ; number_of_trees &1498 d0 ef BNE &1489 ; add_trees_loop ; skip_remaining_trees ; generate_secret_code_validation_table &149a a2 aa LDX #&aa &149c bc ab 0b LDY &0bab,X ; prnd_byte_after_generation - &aa # Set to part of prnd state while plotting landscape &149f 2c 71 0c BIT &0c71 ; play_game_after_generation # Top bit is set if about to play landscape &14a2 10 02 BPL &14a6 ; generate_validation_table_loop # Top bit is clear if only previewing landscape &14a4 a2 a5 LDX #&a5 ; generate_validation_table_loop # Runs over &0d19 to &0cef or &0d14 to &0cef &14a6 20 64 33 JSR &3364 ; get_random_two_digit_bcd_number # &0cf0 - &0cf3 is code entered by player &14a9 38 SEC # &0cfd - &0cfe is landscape number &14aa fd 6f 0c SBC &0c6f,X ; input_buffer - &81 # all other bytes have fixed values &14ad f0 01 BEQ &14b0 ; not_equal &14af 18 CLC # Bit is set only if two values are equal ; not_equal &14b0 2e 65 0c ROL &0c65 ; first_secret_code_check_bits # Push carry into lowest bit of check &14b3 18 CLC &14b4 6d 00 01 ADC &0100 ; objects_flags # Always &7f if sentinel is on platform &14b7 99 00 3f STA &3f00,Y ; secret_code_validation_table # Set &2a or &25 bytes in table, starting from prnd byte &14ba c8 INY &14bb ca DEX &14bc 30 e8 BMI &14a6 ; generate_validation_table_loop &14be 0e 71 0c ASL &0c71 ; play_game_after_generation # Top bit set if about to play landscape, clear top bit &14c1 90 09 BCC &14cc ; first_secret_code_check ; leave &14c3 60 RTS ; landscape_colours_three ; G R Y C R C R Y &14c4 02 01 03 06 01 06 01 03 ; first_secret_code_check &14cc ad 65 0c LDA &0c65 ; first_secret_code_check_bits # &2, &4, &8 and &10 set only if entered code is correct &14cf 29 1e AND #&1e # i.e. &0cf0 - &0cf3 are the same as the values from &14d1 c9 1e CMP #&1e # get_random_two_digit_bcd_number &14d3 d0 ee BNE &14c3 ; leave &14d5 68 PLA # Pop return address off stack &14d6 68 PLA &14d7 18 CLC &14d8 ad 00 01 LDA &0100 ; objects_flags # Always &7f if sentinel is on platform &14db 69 b6 ADC #&b6 &14dd 48 PHA # Push return address on stack &14de 18 CLC &14df 69 6e ADC #&6e &14e1 48 PHA &14e2 60 RTS # Leaves to &35a4, play_landscape ; landscape_colours_two ; W Y C R W Y C W &14e3 07 03 06 01 07 03 06 01 ; initialise_enemies &14eb 20 bc 15 JSR &15bc ; find_highest_tiles_in_grid # Sets &06 to be height of highest tile &14ee a2 00 LDX #&00 ; initialise_enemies_loop &14f0 86 6e STX &6e ; object_to_consider # Enemy &14f2 a9 01 LDA #&01 ; TYPE_SENTRY &14f4 9d 40 0a STA &0a40,X ; objects_type ; find_grid_sections_for_enemies_loop &14f7 20 8d 15 JSR &158d ; find_grid_sections_at_given_z # Returns carry clear if grid sections found &14fa 90 0f BCC &150b ; choose_a_random_grid_section &14fc a5 06 LDA &06 ; maximum_or_minimum_z # If no grid sections were found, &14fe 38 SEC &14ff e9 10 SBC #&10 # Drop the height and try again &1501 85 06 STA &06 ; maximum_or_minimum_z &1503 d0 f2 BNE &14f7 ; find_grid_sections_for_enemies_loop &1505 8e 6f 0c STX &0c6f ; number_of_enemies # Limit the number of enemies if necessary &1508 4c 82 15 JMP &1582 ; finished_adding_enemies ; choose_a_random_grid_section # Choose a random grid section at given height &150b 20 94 31 JSR &3194 ; prnd &150e 25 27 AND &27 ; mask_for_grid_sections_randomness &1510 c5 74 CMP &74 ; number_of_valid_grid_sections &1512 b0 f7 BCS &150b ; choose_a_random_grid_section &1514 a8 TAY &1515 be 40 5a LDX &5a40,Y ; valid_grid_section_offsets &1518 a9 00 LDA #&00 # Mark this and surrounding grid sections as used &151a 9d 07 5b STA &5b07,X ; grid_sections_highest_tile_z - 8 - 1 &151d 9d 08 5b STA &5b08,X ; grid_sections_highest_tile_z - 8 &1520 9d 09 5b STA &5b09,X ; grid_sections_highest_tile_z - 8 + 1 &1523 9d 0f 5b STA &5b0f,X ; grid_sections_highest_tile_z - 1 &1526 9d 10 5b STA &5b10,X ; grid_sections_highest_tile_z &1529 9d 11 5b STA &5b11,X ; grid_sections_highest_tile_z + 1 &152c 9d 17 5b STA &5b17,X ; grid_sections_highest_tile_z + 8 - 1 &152f 9d 18 5b STA &5b18,X ; grid_sections_highest_tile_z + 8 &1532 9d 19 5b STA &5b19,X ; grid_sections_highest_tile_z + 8 + 1 &1535 bd 60 5b LDA &5b60,X ; grid_sections_highest_tile_x # Use highest tile in grid section for enemy &1538 85 24 STA &24 ; tile_x &153a bd a0 5b LDA &5ba0,X ; grid_sections_highest_tile_y &153d 85 26 STA &26 ; tile_y &153f a6 6e LDX &6e ; object_to_consider # Enemy &1541 d0 1c BNE &155f ; not_sentinel ; is_sentinel &1543 8d 1a 0c STA &0c1a ; platform_y &1546 a5 24 LDA &24 ; tile_x &1548 8d 19 0c STA &0c19 ; platform_x &154b a9 05 LDA #&05 ; TYPE_SENTINEL &154d 8d 40 0a STA &0a40 ; objects_type # Object &00 is the sentinel &1550 a9 06 LDA #&06 ; TYPE_PLATFORM &1552 20 0e 21 JSR &210e ; create_object &1555 20 ff 1e JSR &1eff ; put_object_in_tile &1558 a9 00 LDA #&00 # Platform is always aligned with landscape &155a 9d c0 09 STA &09c0,X ; objects_h_angle &155d a6 6e LDX &6e ; object_to_consider # Sentinel ; not_sentinel &155f 20 ff 1e JSR &1eff ; put_object_in_tile &1562 20 6a 19 JSR &196a ; initialise_enemy_meanie_variables &1565 20 94 31 JSR &3194 ; prnd &1568 4a LSR A # Use lowest bit of random number for rotation direction &1569 29 3f AND #&3f &156b 09 05 ORA #&05 &156d 9d 30 0c STA &0c30,X ; enemies_update_cooldown &1570 a9 14 LDA #&14 # Set enemy to rotate clockwise by one screen width &1572 90 02 BCC &1576 ; set_enemies_rotation_speed # (20 * 256/360 degrees) &1574 a9 ec LDA #&ec # or anticlockwise ; set_enemies_rotation_speed &1576 9d 37 4a STA &4a37,X ; enemies_rotation_speed &1579 e8 INX &157a ec 6f 0c CPX &0c6f ; number_of_enemies &157d b0 03 BCS &1582 ; finished_adding_enemies &157f 4c f0 14 JMP &14f0 ; initialise_enemies_loop ; finished_adding_enemies &1582 a5 06 LDA &06 ; maximum_or_minimum_z &1584 4a LSR A &1585 4a LSR A &1586 4a LSR A &1587 4a LSR A &1588 8d 06 0c STA &0c06 ; lowest_enemy_z &158b 18 CLC &158c 60 RTS ; find_grid_sections_at_given_z &158d a2 3f LDX #&3f &158f a0 00 LDY #&00 ; find_grid_sections_at_given_z_loop &1591 bd 10 5b LDA &5b10,X ; grid_sections_highest_tile_z &1594 c5 06 CMP &06 ; maximum_or_minimum_z &1596 d0 05 BNE &159d ; consider_next_section &1598 8a TXA &1599 99 40 5a STA &5a40,Y ; valid_grid_section_offsets &159c c8 INY ; consider_next_section &159d ca DEX &159e 10 f1 BPL &1591 ; find_grid_sections_at_given_z_loop &15a0 98 TYA &15a1 f0 0f BEQ &15b2 ; leave_with_carry_set &15a3 85 74 STA &74 ; number_of_valid_grid_sections ; calculate_mask_for_grid_sections_randomness &15a5 a0 ff LDY #&ff ; calculate_mask_for_grid_sections_randomness_loop &15a7 0a ASL A &15a8 c8 INY &15a9 90 fc BCC &15a7 ; calculate_mask_for_grid_sections_randomness_loop &15ab b9 b4 15 LDA &15b4,Y ; mask_table &15ae 85 27 STA &27 ; mask_for_grid_sections_randomness &15b0 18 CLC # Leave with carry clear if grid sections found &15b1 60 RTS ; leave_with_carry_set &15b2 38 SEC # Leave with carry set if no grid sections found &15b3 60 RTS ; mask_table &15b4 ff 7f 3f 1f 0f 07 03 01 ; find_highest_tiles_in_grid &15bc a2 00 LDX #&00 &15be 86 06 STX &06 ; maximum_or_minimum_z ; find_highest_tiles_in_grid_loop # Divide the landscape up into 64 4*4 sections &15c0 8a TXA &15c1 29 07 AND #&07 &15c3 0a ASL A &15c4 0a ASL A &15c5 85 18 STA &18 ; centre_tile_x &15c7 8a TXA &15c8 29 38 AND #&38 &15ca 4a LSR A &15cb 85 1a STA &1a ; centre_tile_y &15cd a9 00 LDA #&00 &15cf 9d 10 5b STA &5b10,X ; grid_sections_highest_tile_z &15d2 a9 04 LDA #&04 # For each tile in a section, &15d4 85 0c STA &0c ; y_offset &15d6 a5 1a LDA &1a ; centre_tile_y &15d8 85 26 STA &26 ; tile_y &15da c9 1c CMP #&1c &15dc 90 02 BCC &15e0 ; find_highest_tiles_in_grid_y_loop &15de c6 0c DEC &0c ; y_offset # Don't go beyond edge of landscape ; find_highest_tiles_in_grid_y_loop &15e0 a9 04 LDA #&04 &15e2 85 0d STA &0d ; x_offset &15e4 a5 18 LDA &18 ; centre_tile_x &15e6 85 24 STA &24 ; tile_x &15e8 c9 1c CMP #&1c &15ea 90 02 BCC &15ee ; find_highest_tiles_in_grid_x_loop &15ec c6 0d DEC &0d ; x_offset # Don't go beyond edge of landscape ; find_highest_tiles_in_grid_x_loop &15ee 20 78 2b JSR &2b78 ; calculate_tile_address &15f1 29 0f AND #&0f &15f3 d0 1c BNE &1611 ; consider_next_tile # Only consider flat tiles &15f5 b1 5e LDA (&5e),Y ; tile_address &15f7 29 f0 AND #&f0 # Get the height of the tile &15f9 dd 10 5b CMP &5b10,X ; grid_sections_highest_tile_z &15fc 90 13 BCC &1611 ; consider_next_tile &15fe 9d 10 5b STA &5b10,X ; grid_sections_highest_tile_z # Store its details if best found so far &1601 c5 06 CMP &06 ; maximum_or_minimum_z &1603 90 02 BCC &1607 ; not_new_maximum &1605 85 06 STA &06 ; maximum_or_minimum_z # Note the height of the highest tile in the landscape ; not_new_maximum &1607 a5 24 LDA &24 ; tile_x &1609 9d 60 5b STA &5b60,X ; grid_sections_highest_tile_x &160c a5 26 LDA &26 ; tile_y &160e 9d a0 5b STA &5ba0,X ; grid_sections_highest_tile_y ; consider_next_tile &1611 e6 24 INC &24 ; tile_x &1613 c6 0d DEC &0d ; x_offset &1615 d0 d7 BNE &15ee ; find_highest_tiles_in_grid_x_loop &1617 e6 26 INC &26 ; tile_y &1619 c6 0c DEC &0c ; y_offset &161b d0 c3 BNE &15e0 ; find_highest_tiles_in_grid_y_loop &161d e8 INX &161e e0 40 CPX #&40 &1620 90 9e BCC &15c0 ; find_highest_tiles_in_grid_loop &1622 60 RTS ; update_bar &1623 ad 04 0c LDA &0c04 ; bar_state &1626 d0 05 BNE &162d ; skip_check # Always replot the bar if player is being targeted &1628 cd 09 0c CMP &0c09 ; previous_bar_state &162b f0 6b BEQ &1698 ; leave # Otherwise, only plot it when targeting changes ; skip_check &162d 8d 09 0c STA &0c09 ; previous_bar_state &1630 8d 9b 16 STA &169b ; bar_type &1633 ad c2 0c LDA &0cc2 ; viewport_screen_address_low # Calculate bar screen address &1636 38 SEC &1637 e9 4f SBC #&4f &1639 85 22 STA &22 ; bar_screen_address_low &163b ad c3 0c LDA &0cc3 ; viewport_screen_address_high &163e e9 00 SBC #&00 &1640 c9 60 CMP #&60 &1642 b0 02 BCS &1646 ; skip_wraparound &1644 69 20 ADC #&20 ; skip_wraparound &1646 85 23 STA &23 ; bar_screen_address_high &1648 a9 08 LDA #&08 &164a 8d 9a 16 STA &169a ; bar_width ; reshuffle_rnd &164d a0 03 LDY #&03 &164f 20 8e 56 JSR &568e ; shuffle_rnd &1652 4c 5a 16 JMP &165a ; plot_bar ; plot_bar_group_loop &1655 ad 99 16 LDA &1699 ; bar_rnd &1658 4a LSR A &1659 4a LSR A ; plot_bar &165a 8d 99 16 STA &1699 ; bar_rnd &165d 29 03 AND #&03 &165f 0d 9b 16 ORA &169b ; bar_type &1662 aa TAX &1663 bd 9c 16 LDA &169c,X ; bar_pixel_values &1666 91 22 STA (&22),Y ; bar_screen_address &1668 88 DEY &1669 10 ea BPL &1655 ; plot_bar_group_loop &166b a5 22 LDA &22 ; bar_screen_address_low &166d 18 CLC &166e 69 08 ADC #&08 # Move right two pixels &1670 85 22 STA &22 ; bar_screen_address_low &1672 a5 23 LDA &23 ; bar_screen_address_high &1674 69 00 ADC #&00 &1676 c9 80 CMP #&80 &1678 90 02 BCC &167c ; skip_wraparound &167a e9 20 SBC #&20 ; skip_wraparound &167c 85 23 STA &23 ; bar_screen_address_high &167e ce 9a 16 DEC &169a ; bar_width &1681 f0 15 BEQ &1698 ; leave &1683 ad 9a 16 LDA &169a ; bar_width &1686 c9 04 CMP #&04 # At the half point of the bar, &1688 d0 c3 BNE &164d ; get_new_bar_rnd &168a ad 4f 0c LDA &0c4f ; player_exposure # If the player is partly visible, &168d c9 40 CMP #&40 &168f d0 bc BNE &164d ; get_new_bar_rnd &1691 a9 00 LDA #&00 # use black for remainder of bar &1693 8d 9b 16 STA &169b ; bar_type &1696 f0 b5 BEQ &164d ; get_new_bar_rnd # Always branches ; leave &1698 60 RTS ; bar_rnd &1699 00 ; bar_width &169a 00 ; bar_type &169b 00 ; bar_pixel_values &169c 0f 0f 0f 0f ; &00 unpaused &16a0 8f 4f 2f 1f ; &04 player is being scanned &16a4 ff ff ff ff ; &08 paused ; update_enemies &16a8 ba TSX # Store stack position so update_enemies can be &16a9 8e 0d 0c STX &0c0d ; stack_on_entry_to_update_enemies # popped out of if an object needs to be replotted &16ac a6 00 LDX &00 ; enemy_to_consider &16ae bd 40 0a LDA &0a40,X ; objects_type &16b1 c9 01 CMP #&01 ; TYPE_SENTRY &16b3 f0 04 BEQ &16b9 ; is_sentinel_or_sentry &16b5 c9 05 CMP #&05 ; TYPE_SENTINEL &16b7 d0 10 BNE &16c9 ; finished_updating_enemy ; is_sentinel_or_sentry &16b9 8d 1c 0c STA &0c1c ; title_screen_object &16bc bd 00 01 LDA &0100,X ; objects_flags # Has the enemy been absorbed? (SLOT_EMPTY set) &16bf 10 18 BPL &16d9 ; consider_enemy_state &16c1 20 54 1a JSR &1a54 ; consider_discharging_enemy_energy # If so, return any residual energy to the landscape &16c4 b0 03 BCS &16c9 ; finished_updating_enemy # Carry set if no energy discharged &16c6 4c 71 18 JMP &1871 ; enemy_caused_object_to_dither_change ; finished_updating_enemy &16c9 20 94 31 JSR &3194 ; prnd # Unnecessary &16cc c6 00 DEC &00 ; enemy_to_consider &16ce 10 04 BPL &16d4 ; skip_wraparound &16d0 a9 07 LDA #&07 &16d2 85 00 STA &00 ; enemy_to_consider ; skip_wraparound &16d4 a5 0b LDA &0b ; player_object &16d6 85 6e STA &6e ; object_to_consider # Player &16d8 60 RTS ; consider_enemy_state &16d9 bd 30 0c LDA &0c30,X ; enemies_update_cooldown &16dc c9 02 CMP #&02 &16de b0 e9 BCS &16c9 ; finished_updating_enemy &16e0 a9 04 LDA #&04 &16e2 9d 30 0c STA &0c30,X ; enemies_update_cooldown &16e5 a9 14 LDA #&14 # One screen width (20 * 256/360 degrees) &16e7 8d 68 0c STA &0c68 ; enemy_angular_range_of_vision &16ea bd a0 0c LDA &0ca0,X ; enemies_meanie_object # Top bit set if no meanie associated with enemy &16ed 10 03 BPL &16f2 ; update_meanie &16ef 4c 6a 17 JMP &176a ; no_meanie ; update_meanie # Enemy has an associated meanie &16f2 85 6e STA &6e ; object_to_consider # Meanie &16f4 bc a8 0c LDY &0ca8,X ; enemies_targeted_object &16f7 b9 00 01 LDA &0100,Y ; objects_flags # Does the player object at the time of creating the &16fa 30 53 BMI &174f ; remove_meanie_and_reset_enemy # meanie still exist? If not, remove meanie &16fc a9 00 LDA #&00 ; TYPE_ROBOT &16fe 20 82 18 JSR &1882 ; check_if_enemy_can_see_object &1701 ad 57 0c LDA &0c57 ; object_relative_h_angle_high # This is relative angle + 10 * 256/360 degrees &1704 c9 14 CMP #&14 # Is it less than screen width (20 * 256/360 degrees)? &1706 b0 13 BCS &171b ; meanie_not_looking_at_player # i.e. is the meanie looking at the object? &1708 c4 0b CPY &0b ; player_object # If so, has the player transferred out of the object? &170a d0 43 BNE &174f ; remove_meanie_and_reset_enemy &170c a5 14 LDA &14 ; object_exposure # Zero if object can't be seen by enemy &170e f0 44 BEQ &1754 ; remove_meanie &1710 20 47 21 JSR &2147 ; do_hyperspace # Hyperspace player by meanie &1713 a9 04 LDA #&04 ; TYPE_MEANIE &1715 8d 1c 0c STA &0c1c ; title_screen_object # Use the meanie for the death screen if player killed &1718 4c c9 16 JMP &16c9 ; finished_updating_enemy ; meanie_not_looking_at_player &171b a9 08 LDA #&08 &171d 2c 57 0c BIT &0c57 ; object_relative_h_angle_high # Meanie rotates towards player &1720 10 02 BPL &1724 ; skip_inversion &1722 a9 f8 LDA #&f8 ; skip_inversion &1724 8d 0e 0c STA &0c0e ; meanie_rotation &1727 a4 00 LDY &00 ; enemy_to_consider &1729 be a0 0c LDX &0ca0,Y ; enemies_meanie_object &172c 8a TXA &172d 20 e7 1a JSR &1ae7 ; finish_update_if_object_being_plotted # Don't update meanie if meanie is on updating screen &1730 bd c0 09 LDA &09c0,X ; objects_h_angle &1733 18 CLC &1734 6d 0e 0c ADC &0c0e ; meanie_rotation # Rotate meanie &1737 9d c0 09 STA &09c0,X ; objects_h_angle &173a a9 0a LDA #&0a # Enemy updates 10 rounds after rotating meanie &173c 99 30 0c STA &0c30,Y ; enemies_update_cooldown &173f 8a TXA &1740 48 PHA &1741 a2 03 LDX #&03 &1743 a0 46 LDY #&46 &1745 a9 01 LDA #&01 ; sound_1 &1747 20 3a 34 JSR &343a ; play_sound_with_envelope_and_pitches # Play sound for rotating meanie &174a 68 PLA &174b aa TAX # Meanie &174c 4c 76 18 JMP &1876 ; enemy_caused_object_to_change # Replot meanie ; remove_meanie_and_reset_enemy &174f a9 00 LDA #&00 # Zero to indicate enemy isn't waiting to drain &1751 9d 20 0c STA &0c20,X ; enemies_draining_cooldown ; remove_meanie &1754 a4 00 LDY &00 ; enemy_to_consider &1756 be a0 0c LDX &0ca0,Y ; enemies_meanie_object &1759 8a TXA &175a 20 e7 1a JSR &1ae7 ; finish_update_if_object_being_plotted # Don't remove meanie if meanie is on updating screen &175d a9 80 LDA #&80 # Set top bit to indicate enemy has no meanie &175f 99 a0 0c STA &0ca0,Y ; enemies_meanie_object &1762 a9 02 LDA #&02 ; TYPE_TREE &1764 9d 40 0a STA &0a40,X ; objects_type # Turn meanie back into tree &1767 4c 71 18 JMP &1871 ; enemy_caused_object_to_dither_change ; no_meanie &176a 86 6e STX &6e ; object_to_consider # Enemy &176c 20 54 1a JSR &1a54 ; consider_discharging_enemy_energy # Returns carry set if no energy discharged &176f b0 03 BCS &1774 ; no_discharge &1771 4c 71 18 JMP &1871 ; enemy_caused_object_to_dither_change ; no_discharge &1774 a6 00 LDX &00 ; enemy_to_consider &1776 bd b8 0c LDA &0cb8,X ; enemies_considering_meanie # Top bit clear if already considering creating meanie &1779 10 11 BPL &178c ; skip_search_for_targeted_object &177b 20 a7 1a JSR &1aa7 ; find_drainable_boulder_or_tree_on_stack # Returns carry clear if drainable object found &177e a6 00 LDX &00 ; enemy_to_consider &1780 b0 07 BCS &1789 ; skip_search_for_targeted_object &1782 a9 40 LDA #&40 # Set the enemy searching for trees to turn into meanies &1784 9d 80 0c STA &0c80,X ; enemies_meanie_search_object &1787 d0 58 BNE &17e1 ; drain_object # Always branches ; skip_search_for_targeted_object &1789 5e b8 0c LSR &0cb8,X ; enemies_considering_meanie # Clear top bit to indicate considering creating meanie &178c bd 20 0c LDA &0c20,X ; enemies_draining_cooldown &178f f0 12 BEQ &17a3 ; not_draining_or_waiting_to_drain # Zero if enemy hasn't targeted an object &1791 bc a8 0c LDY &0ca8,X ; enemies_targeted_object &1794 a9 00 LDA #&00 ; TYPE_ROBOT &1796 20 82 18 JSR &1882 ; check_if_enemy_can_see_object &1799 a5 14 LDA &14 ; object_exposure # Zero if object can't be seen by enemy &179b f0 03 BEQ &17a0 ; set_enemies_draining_cooldown &179d 4c 20 18 JMP &1820 ; target_object ; set_enemies_draining_cooldown &17a0 9d 20 0c STA &0c20,X ; enemies_draining_cooldown # Set to zero to indicate enemy not targeting an object ; not_draining_or_waiting_to_drain &17a3 a9 80 LDA #&80 # Set top bit to indicate no drainable robot found &17a5 85 0f STA &0f ; drainable_robot &17a7 a0 3f LDY #&3f # Can the enemy drain the player? ; find_drainable_robot_loop &17a9 a9 00 LDA #&00 ; TYPE_ROBOT &17ab 20 82 18 JSR &1882 ; check_if_enemy_can_see_object &17ae ad 76 0c LDA &0c76 ; tree_was_in_line_of_sight # &40 set if enemy saw a tree prior to the robot &17b1 29 40 AND #&40 &17b3 d0 0c BNE &17c1 ; consider_next_object &17b5 a5 14 LDA &14 ; object_exposure # Zero if object can't be seen by enemy &17b7 f0 08 BEQ &17c1 ; consider_next_object &17b9 30 65 BMI &1820 ; target_object # Top bit set if object is fully visible &17bb c4 0b CPY &0b ; player_object &17bd d0 02 BNE &17c1 ; consider_next_object &17bf 84 0f STY &0f ; drainable_robot ; consider_next_object &17c1 88 DEY &17c2 10 e5 BPL &17a9 ; find_drainable_robot_loop &17c4 a4 0f LDY &0f ; drainable_robot # If player can't be drained, look for something else &17c6 30 0f BMI &17d7 ; reset_draining_cooldown_and_look_for_tree_or_boulder &17c8 98 TYA &17c9 dd 90 0c CMP &0c90,X ; enemies_failed_meanie_memory &17cc f0 09 BEQ &17d7 ; reset_draining_cooldown_and_look_for_tree_or_boulder &17ce 20 6a 19 JSR &196a ; initialise_enemy_meanie_variables &17d1 a9 40 LDA #&40 # Set &40 to indicate player partially visible &17d3 85 14 STA &14 ; object_exposure &17d5 d0 49 BNE &1820 ; target_object # Always branches ; reset_draining_cooldown_and_look_for_tree_or_boulder &17d7 a9 00 LDA #&00 # Zero to indicate enemy isn't waiting to drain &17d9 9d 20 0c STA &0c20,X ; enemies_draining_cooldown &17dc 20 a7 1a JSR &1aa7 ; find_drainable_boulder_or_tree_on_stack # Returns carry clear if drainable object found &17df b0 0f BCS &17f0 ; no_drain ; drain_object &17e1 20 ff 19 JSR &19ff ; reduce_object_energy # Returns carry set if player was drained &17e4 b0 13 BCS &17f9 ; to_finished_updating_enemy &17e6 a4 00 LDY &00 ; enemy_to_consider &17e8 a9 1e LDA #&1e # Enemy updates 30 rounds after draining object &17ea 99 30 0c STA &0c30,Y ; enemies_update_cooldown &17ed 4c 71 18 JMP &1871 ; enemy_caused_object_to_dither_change ; no_drain &17f0 a6 00 LDX &00 ; enemy_to_consider &17f2 bd 28 0c LDA &0c28,X ; enemies_rotation_cooldown &17f5 c9 02 CMP #&02 &17f7 90 03 BCC &17fc ; to_finished_updating_enemy &17f9 4c c9 16 JMP &16c9 ; finished_updating_enemy ; rotate_enemy &17fc 8a TXA &17fd 20 e7 1a JSR &1ae7 ; finish_update_if_object_being_plotted # Don't rotate enemy if enemy is on updating screen &1800 bd c0 09 LDA &09c0,X ; objects_h_angle &1803 18 CLC &1804 7d 37 4a ADC &4a37,X ; enemies_rotation_speed &1807 9d c0 09 STA &09c0,X ; objects_h_angle &180a a9 c8 LDA #&c8 # Enemies rotate every 168 rounds &180c 9d 28 0c STA &0c28,X ; enemies_rotation_cooldown &180f 20 6a 19 JSR &196a ; initialise_enemy_meanie_variables &1812 a2 07 LDX #&07 &1814 a0 78 LDY #&78 &1816 a9 00 LDA #&00 ; sound_0 &1818 20 3a 34 JSR &343a ; play_sound_with_envelope_and_pitches # Play sound for rotating enemy &181b a6 00 LDX &00 ; enemy_to_consider &181d 4c 76 18 JMP &1876 ; enemy_caused_object_to_change # Replot enemy ; target_object &1820 98 TYA &1821 9d a8 0c STA &0ca8,X ; enemies_targeted_object &1824 a5 14 LDA &14 ; object_exposure &1826 9d b0 0c STA &0cb0,X ; enemies_targeted_object_exposure &1829 bd 20 0c LDA &0c20,X ; enemies_draining_cooldown &182c c9 01 CMP #&01 &182e b0 08 BCS &1838 ; consider_reducing_object &1830 a9 78 LDA #&78 # Reduce objects every 120 rounds &1832 9d 20 0c STA &0c20,X ; enemies_draining_cooldown ; to_finished_updating_enemy &1835 4c c9 16 JMP &16c9 ; finished_updating_enemy ; consider_reducing_object &1838 d0 fb BNE &1835 ; to_finished_updating_enemy &183a a5 14 LDA &14 ; object_exposure # Top bit clear if object can't be drained by enemy &183c 10 0f BPL &184d ; enemy_can't_drain_object &183e 20 ff 19 JSR &19ff ; reduce_object_energy # Returns carry set if player was drained &1841 a4 00 LDY &00 ; enemy_to_consider &1843 a9 1e LDA #&1e # Enemy updates 30 rounds after draining object &1845 99 30 0c STA &0c30,Y ; enemies_update_cooldown &1848 b0 35 BCS &187f ; to_finished_updating_enemy # Finish if the player was drained &184a 4c 71 18 JMP &1871 ; enemy_caused_object_to_dither_change # Otherwise, replot drained object ; enemy_can't_drain_object &184d 20 7d 19 JSR &197d ; consider_creating_meanie # Returns carry clear if meanie was created &1850 a4 00 LDY &00 ; enemy_to_consider &1852 90 15 BCC &1869 ; meanie_was_created &1854 b9 98 0c LDA &0c98,Y ; enemies_meanie_attempt_scans &1857 c9 02 CMP #&02 &1859 b0 07 BCS &1862 ; stop_trying_to_create_meanies &185b a9 80 LDA #&80 # Set top bit to continue trying to create meanies &185d 99 b8 0c STA &0cb8,Y ; enemies_considering_meanie &1860 d0 1d BNE &187f ; to_finished_updating_enemy # Always branches ; stop_trying_to_create_meanies &1862 a9 00 LDA #&00 # Zero to indicate enemy isn't waiting to drain &1864 99 20 0c STA &0c20,Y ; enemies_draining_cooldown &1867 f0 16 BEQ &187f ; to_finished_updating_enemy ; meanie_was_created &1869 a9 32 LDA #&32 # Enemy updates 50 rounds after creating meanie &186b 99 30 0c STA &0c30,Y ; enemies_update_cooldown &186e be a0 0c LDX &0ca0,Y ; enemies_meanie_object # Plot the newly created meanie ; enemy_caused_object_to_dither_change &1871 a9 40 LDA #&40 # Clear &80 to keep sights, set &40 to dither object &1873 8d 6d 0c STA &0c6d ; how_to_plot_object_to_update ; enemy_caused_object_to_change &1876 86 01 STX &01 ; object_to_update &1878 a5 0b LDA &0b ; player_object &187a 85 6e STA &6e ; object_to_consider # Player &187c 20 84 1f JSR &1f84 ; update_object_on_screen ; to_finished_updating_enemy &187f 4c c9 16 JMP &16c9 ; finished_updating_enemy ; check_if_enemy_can_see_object # Called with X = enemy, Y = target, A = target type &1882 85 74 STA &74 ; expected_object_type &1884 8e 19 19 STX &1919 ; tmp_x # Preserve X on exit &1887 8c 58 0c STY &0c58 ; targeted_object &188a a9 00 LDA #&00 # Clear top bit to indicate enemy can't see object &188c 85 14 STA &14 ; object_exposure &188e b9 00 01 LDA &0100,Y ; objects_flags # Top bit set if no object in slot (SLOT_EMPTY) &1891 30 7e BMI &1911 ; leave_with_carry_clear # Can't drain if no object in slot &1893 b9 40 0a LDA &0a40,Y ; objects_type &1896 c5 74 CMP &74 ; expected_object_type &1898 d0 77 BNE &1911 ; leave_with_carry_clear # Can't drain if object isn't expected type &189a 20 01 5c JSR &5c01 ; calculate_object_relative_angles_and_distance ; obfuscate_secret_code &189d a2 07 LDX #&07 # Move to next thousand if at nine hundred &189f bd 36 0f LDA &0f36,X ; copy_of_landscape_high_from_prnd - 7 # This is the high byte of the landscape number &18a2 85 74 STA &74 ; tmp &18a4 29 0f AND #&0f &18a6 c9 09 CMP #&09 &18a8 f0 02 BEQ &18ac ; not_nine &18aa a2 01 LDX #&01 # Add 100 to landscape number and store as maximum cap ; not_nine &18ac 8a TXA &18ad 18 CLC &18ae 65 74 ADC &74 ; tmp # landscape_plus_one_hundred_high is greater than &18b0 8d 75 0c STA &0c75 ; landscape_plus_one_hundred_high # copy_of_landscape_high_from_prnd once game active ; check_if_object_within_enemy_range_of_vision &18b3 ad 68 0c LDA &0c68 ; enemy_angular_range_of_vision &18b6 4a LSR A &18b7 85 74 STA &74 ; half_angle &18b9 ad 57 0c LDA &0c57 ; object_relative_h_angle_high # This is relative angle + 10 * 256/360 degrees &18bc 38 SEC # (half a screen width) &18bd e9 0a SBC #&0a # so subtract 10 * 256 / 360 degrees &18bf 18 CLC &18c0 65 74 ADC &74 ; half_angle &18c2 cd 68 0c CMP &0c68 ; enemy_angular_range_of_vision # Is object within the enemy's horizontal visual range? &18c5 b0 4b BCS &1912 ; leave # If not, leave &18c7 a5 8a LDA &8a ; angle_low &18c9 85 3d STA &3d ; vector_h_angle_low &18cb a5 8b LDA &8b ; angle_high &18cd 85 3e STA &3e ; vector_h_angle_high &18cf a9 02 LDA #&02 &18d1 85 1e STA &1e ; count &18d3 a5 4c LDA &4c ; object_type # Set in calculate_object_relative_angles_and_distance &18d5 d0 28 BNE &18ff ; not_robot # to be type of object Y (target object) ; is_robot &18d7 38 SEC &18d8 6e 6e 0c ROR &0c6e ; enemy_is_considering_robot # Set top bit to indicate enemy considering a robot &18db a5 81 LDA &81 ; z_fraction &18dd 85 80 STA &80 ; x_fraction &18df a5 84 LDA &84 ; z ; check_if_enemy_has_line_of_sight_to_object &18e1 20 1d 56 JSR &561d ; calculate_object_relative_vertical_angle &18e4 a5 8a LDA &8a ; angle_low &18e6 85 3f STA &3f ; vector_v_angle_low &18e8 85 74 STA &74 ; a_fraction &18ea a5 8b LDA &8b ; angle_high &18ec 85 40 STA &40 ; vector_v_angle_high &18ee 20 43 1c JSR &1c43 ; prepare_vector_from_angle &18f1 20 cc 1c JSR &1ccc ; check_for_line_of_sight_to_tile # Returns carry set if no line of sight &18f4 2e 56 0c ROL &0c56 ; targeted_object_is_in_line_of_sight # If tile is in line of sight, object is fully visible &18f7 66 14 ROR &14 ; object_exposure # Set top bit from targeted_object_is_in_line_of_sight &18f9 2e dd 0c ROL &0cdd ; tree_is_in_line_of_sight &18fc 6e 76 0c ROR &0c76 ; tree_was_in_line_of_sight # Set top bit from tree_is_in_line_of_sight ; not_robot &18ff 4e 6e 0c LSR &0c6e ; enemy_is_considering_robot # Clear top bit to indicate not considering a robot &1902 a5 81 LDA &81 ; z_fraction &1904 38 SEC &1905 e9 e0 SBC #&e0 # Check again from nearer the base of the enemy &1907 85 80 STA &80 ; x_fraction &1909 a5 84 LDA &84 ; z &190b e9 00 SBC #&00 &190d c6 1e DEC &1e ; count &190f d0 d0 BNE &18e1 ; check_if_enemy_has_line_of_sight_to_object ; leave_with_carry_clear &1911 18 CLC # Leave with carry clear if object not expected type ; leave &1912 ae 19 19 LDX &1919 ; tmp_x &1915 ac 58 0c LDY &0c58 ; targeted_object &1918 60 RTS ; tmp_x &1919 00 ; calculate_player_exposure &191a a0 00 LDY #&00 ; SOUND_NONE &191c 84 74 STY &74 ; exposure &191e a2 07 LDX #&07 # For each enemy, ; calculate_player_exposure_loop &1920 bd 00 01 LDA &0100,X ; objects_flags # Top bit set if no object in slot (SLOT_EMPTY) &1923 30 20 BMI &1945 ; consider_next_enemy &1925 bd 40 0a LDA &0a40,X ; objects_type &1928 c9 01 CMP #&01 ; TYPE_SENTRY &192a f0 04 BEQ &1930 ; is_sentry_or_sentinel &192c c9 05 CMP #&05 ; TYPE_SENTINEL &192e d0 15 BNE &1945 ; consider_next_enemy ; is_sentry_or_sentinel &1930 bd a8 0c LDA &0ca8,X ; enemies_targeted_object &1933 c5 0b CMP &0b ; player_object # Is the enemy targeting the player? &1935 d0 0e BNE &1945 ; consider_next_enemy &1937 bd 20 0c LDA &0c20,X ; enemies_draining_cooldown &193a f0 09 BEQ &1945 ; consider_next_enemy &193c a0 04 LDY #&04 ; SOUND_TARGETED &193e bd b0 0c LDA &0cb0,X ; enemies_targeted_object_exposure # Top bit set if enemy can fully see object &1941 85 74 STA &74 ; exposure &1943 30 03 BMI &1948 ; set_bar_state # If so, end loop ; consider_next_enemy &1945 ca DEX # Otherwise, continue to check for partial exposure &1946 10 d8 BPL &1920 ; calculate_player_exposure_loop ; set_bar_state &1948 8c 04 0c STY &0c04 ; bar_state # &00 for empty bar, &04 for random bar &194b a5 74 LDA &74 ; exposure &194d 8d 4f 0c STA &0c4f ; player_exposure &1950 ad 73 0c LDA &0c73 ; sound_type &1953 cc 73 0c CPY &0c73 ; sound_type &1956 8c 73 0c STY &0c73 ; sound_type &1959 f0 0e BEQ &1969 ; leave # Leave if the targeting sound hasn't changed &195b a0 12 LDY #&12 # Channel 2, flush &195d 8c 10 59 STY &5910 ; sound_block_2 + 0 &1960 c9 03 CMP #&03 ; SOUND_TUNE &1962 f0 05 BEQ &1969 ; leave &1964 a2 06 LDX #&06 ; sound channel 2 # Flush channel 2 if a tune was interrupted &1966 20 55 35 JSR &3555 ; flush_buffer ; leave &1969 60 RTS ; initialise_enemy_meanie_variables &196a a9 80 LDA #&80 &196c 9d a0 0c STA &0ca0,X ; enemies_meanie_object # Set top bit to indicate enemy has no associated meanie &196f 9d 90 0c STA &0c90,X ; enemies_failed_meanie_memory &1972 a9 00 LDA #&00 &1974 9d 98 0c STA &0c98,X ; enemies_meanie_attempt_scans &1977 a9 40 LDA #&40 # Set the enemy searching for trees to turn into meanies &1979 9d 80 0c STA &0c80,X ; enemies_meanie_search_object &197c 60 RTS ; consider_creating_meanie &197d a9 28 LDA #&28 # Enemy can see two screen widths (40 * 256/360 degrees) &197f 8d 68 0c STA &0c68 ; enemy_angular_range_of_vision &1982 a6 00 LDX &00 ; enemy_to_consider # For the current enemy, &1984 86 6e STX &6e ; object_to_consider # Enemy ; consider_creating_meanie_loop &1986 a6 00 LDX &00 ; enemy_to_consider &1988 bc 80 0c LDY &0c80,X ; enemies_meanie_search_object # Is the enemy searching for trees to turn into meanies? &198b d0 0b BNE &1998 ; attempt_to_create_meanie &198d fe 98 0c INC &0c98,X ; enemies_meanie_attempt_scans # Note that the enemy has considered all the objects &1990 bd a8 0c LDA &0ca8,X ; enemies_targeted_object &1993 9d 90 0c STA &0c90,X ; enemies_failed_meanie_memory # Note that the player is currently not vulnerable &1996 38 SEC # Leave with carry set to indicate no meanie created &1997 60 RTS ; attempt_to_create_meanie &1998 de 80 0c DEC &0c80,X ; enemies_meanie_search_object # For each object, &199b 88 DEY &199c b9 00 01 LDA &0100,Y ; objects_flags # Top bit set if no object in slot (SLOT_EMPTY) &199f 30 e5 BMI &1986 ; consider_creating_meanie_loop &19a1 b9 40 0a LDA &0a40,Y ; objects_type &19a4 c9 02 CMP #&02 ; TYPE_TREE &19a6 d0 de BNE &1986 ; consider_creating_meanie_loop # Is it a tree? If not, it's no good; look for another &19a8 bd a8 0c LDA &0ca8,X ; enemies_targeted_object &19ab aa TAX &19ac bd 00 09 LDA &0900,X ; objects_x &19af 38 SEC &19b0 f9 00 09 SBC &0900,Y ; objects_x &19b3 10 05 BPL &19ba ; skip_x_inversion &19b5 49 ff EOR #&ff &19b7 18 CLC &19b8 69 01 ADC #&01 ; skip_x_inversion &19ba c9 0a CMP #&0a # Is it more than 10 tiles away from the player in x? &19bc b0 c8 BCS &1986 ; consider_creating_meanie_loop # If so, it's no good; look for another &19be bd 80 09 LDA &0980,X ; objects_y &19c1 38 SEC &19c2 f9 80 09 SBC &0980,Y ; objects_y &19c5 10 05 BPL &19cc ; skip_y_inversion &19c7 49 ff EOR #&ff &19c9 18 CLC &19ca 69 01 ADC #&01 ; skip_y_inversion &19cc c9 0a CMP #&0a # Is it more than 10 tiles away from the player in y? &19ce b0 b6 BCS &1986 ; consider_creating_meanie_loop # If so, it's no good; look for another &19d0 a9 02 LDA #&02 ; TYPE_TREE &19d2 20 82 18 JSR &1882 ; check_if_enemy_can_see_object # Does the enemy have a clear line of sight to the tree? &19d5 a5 14 LDA &14 ; object_exposure # Top bit set if so by check_if_enemy_can_see_object &19d7 10 ad BPL &1986 ; consider_creating_meanie_loop # If not, the object is no good; look for another &19d9 a6 00 LDX &00 ; enemy_to_consider &19db 98 TYA &19dc 20 f3 1a JSR &1af3 ; check_if_object_can_be_updated # Returns carry clear if tree is on an updating screen &19df 90 10 BCC &19f1 ; pause_meanie_creation # If so, don't create the meanie just yet &19e1 98 TYA &19e2 9d a0 0c STA &0ca0,X ; enemies_meanie_object # Note that the enemy has created a meanie &19e5 a9 04 LDA #&04 ; TYPE_MEANIE &19e7 99 40 0a STA &0a40,Y ; objects_type # Turn the tree into a meanie &19ea a9 68 LDA #&68 &19ec 8d d4 0c STA &0cd4 ; previous_object_height_fraction # Ensure tree is unplotted even if meanie isn't visible &19ef 18 CLC # Leave with carry clear to indicate meanie created &19f0 60 RTS ; pause_meanie_creation &19f1 fe 80 0c INC &0c80,X ; enemies_meanie_search_object # Come back to this tree next time &19f4 4c ec 1a JMP &1aec ; finish_updating_enemy ; kill_player &19f7 a9 80 LDA #&80 # Set top bit to indicate player died (by draining) &19f9 8d 4e 0c STA &0c4e ; player_has_died &19fc 4c ec 1a JMP &1aec ; finish_updating_enemy ; reduce_object_energy &19ff ae 58 0c LDX &0c58 ; targeted_object &1a02 e4 0b CPX &0b ; player_object # Is it the player? &1a04 d0 17 BNE &1a1d ; not_player ; is_player &1a06 ad 0a 0c LDA &0c0a ; player_energy &1a09 f0 ec BEQ &19f7 ; kill_player # Is the player out of energy? If so, kill them &1a0b 38 SEC &1a0c e9 01 SBC #&01 # Reduce player energy by one &1a0e 8d 0a 0c STA &0c0a ; player_energy &1a11 20 c7 36 JSR &36c7 ; plot_status_bar # Replot status bar &1a14 a9 05 LDA #&05 ; sound_5 &1a16 20 40 34 JSR &3440 ; play_sound_with_envelope # Play sound for energy loss &1a19 38 SEC # Set carry to indicate player was drained &1a1a 4c 46 1a JMP &1a46 ; increase_enemy_energy_to_discharge ; not_player &1a1d 8a TXA &1a1e 20 e7 1a JSR &1ae7 ; finish_update_if_object_being_plotted # Don't reduce object if object is on updating screen &1a21 bd 40 0a LDA &0a40,X ; objects_type &1a24 d0 0b BNE &1a31 ; not_robot # Is the object a robot? &1a26 a4 00 LDY &00 ; enemy_to_consider &1a28 a9 00 LDA #&00 # Zero to indicate enemy isn't waiting to drain &1a2a 99 20 0c STA &0c20,Y ; enemies_draining_cooldown &1a2d a9 03 LDA #&03 ; TYPE_BOULDER # Robots get reduced to boulders &1a2f d0 11 BNE &1a42 ; set_type # Always branches ; not_robot &1a31 c9 02 CMP #&02 ; TYPE_TREE &1a33 d0 06 BNE &1a3b ; not_tree # Is the object a tree? &1a35 20 d8 1e JSR &1ed8 ; remove_object # If so, remove it &1a38 4c 45 1a JMP &1a45 ; increase_enemy_energy_to_discharge_from_object ; not_tree &1a3b a9 74 LDA #&74 &1a3d 8d d4 0c STA &0cd4 ; previous_object_height_fraction # Ensure boulder is unplotted even if tree isn't visible &1a40 a9 02 LDA #&02 ; TYPE_TREE # Boulders get reduced to trees ; set_type &1a42 9d 40 0a STA &0a40,X ; objects_type ; increase_enemy_energy_to_discharge_from_object &1a45 18 CLC # Leave with carry clear if other object was drained ; increase_enemy_energy_to_discharge &1a46 08 PHP &1a47 a4 00 LDY &00 ; enemy_to_consider &1a49 b9 88 0c LDA &0c88,Y ; enemies_energy_to_discharge &1a4c 18 CLC &1a4d 69 01 ADC #&01 # Store energy in enemy for discharge later &1a4f 99 88 0c STA &0c88,Y ; enemies_energy_to_discharge &1a52 28 PLP &1a53 60 RTS # Leave with carry set if player was drained ; consider_discharging_enemy_energy &1a54 a6 00 LDX &00 ; enemy_to_consider &1a56 38 SEC &1a57 bd 88 0c LDA &0c88,X ; enemies_energy_to_discharge &1a5a f0 1b BEQ &1a77 ; leave # Leave with carry set if no energy to discharge &1a5c a9 02 LDA #&02 ; TYPE_TREE &1a5e 20 0e 21 JSR &210e ; create_object &1a61 ad 06 0c LDA &0c06 ; lowest_enemy_z &1a64 20 24 12 JSR &1224 ; put_object_in_random_tile_below_z # Returns carry set if object couldn't be put in tile &1a67 b0 0e BCS &1a77 ; leave # Leave with carry set if tree couldn't be created &1a69 8a TXA &1a6a 20 f3 1a JSR &1af3 ; check_if_object_can_be_updated # Returns carry clear if tree is on an updating screen &1a6d 90 09 BCC &1a78 ; remove_newly_created_tree # If so, don't create the tree, but finish update &1a6f a6 00 LDX &00 ; enemy_to_consider &1a71 de 88 0c DEC &0c88,X ; enemies_energy_to_discharge &1a74 a6 01 LDX &01 ; object_to_update &1a76 18 CLC ; leave &1a77 60 RTS ; remove_newly_created_tree &1a78 20 d8 1e JSR &1ed8 ; remove_object &1a7b 4c ec 1a JMP &1aec ; finish_updating_enemy ; next_landscape_screen &1a7e f8 SED &1a7f 20 2c 34 JSR &342c ; convert_player_energy_to_decimal &1a82 18 CLC &1a83 6d fd 0c ADC &0cfd ; landscape_number_low # Skip one landscape per unit of energy remaining &1a86 aa TAX &1a87 ad fe 0c LDA &0cfe ; landscape_number_high &1a8a 69 00 ADC #&00 &1a8c a8 TAY &1a8d d8 CLD &1a8e 20 b7 33 JSR &33b7 ; seed_prnd_from_landscape_number &1a91 20 9c 2a JSR &2a9c ; generate_landscape_and_play_game # Generate landscape, but don't play game &1a94 20 10 14 JSR &1410 ; set_palette_and_initialise_enemies &1a97 20 40 14 JSR &1440 ; initialise_player_and_trees &1a9a a9 80 LDA #&80 # Set top bit to display completion screen &1a9c 20 4c 32 JSR &324c ; plot_title_or_completion_screen &1a9f a2 05 LDX #&05 # "SECRET ENTRY CODE", "LANDSCAPE" &1aa1 20 ad 36 JSR &36ad ; plot_text &1aa4 4c ab 33 JMP &33ab ; plot_landscape_number ; find_drainable_boulder_or_tree_on_stack &1aa7 a2 3f LDX #&3f ; find_drainable_boulder_or_tree_on_stack_loop &1aa9 bd 00 01 LDA &0100,X ; objects_flags &1aac 30 34 BMI &1ae2 ; consider_next_object # Skip if no object in slot (SLOT_EMPTY set) &1aae c9 40 CMP #&40 ; OBJECT_ON_OBJECT &1ab0 b0 07 BCS &1ab9 ; is_on_stack # Object can be drained if on a boulder &1ab2 bd 40 0a LDA &0a40,X ; objects_type &1ab5 c9 03 CMP #&03 ; TYPE_BOULDER &1ab7 d0 29 BNE &1ae2 ; consider_next_object # or is a boulder ; is_on_stack &1ab9 bd 00 09 LDA &0900,X ; objects_x &1abc 85 24 STA &24 ; tile_x &1abe bd 80 09 LDA &0980,X ; objects_y &1ac1 85 26 STA &26 ; tile_y &1ac3 20 78 2b JSR &2b78 ; calculate_tile_address # Returns carry set if tile contains an object &1ac6 90 1a BCC &1ae2 ; consider_next_object # Skip if no object in tile (shouldn't happen) &1ac8 29 3f AND #&3f ; OBJECT_UNDER_OBJECT_MASK # Otherwise, consider topmost object &1aca a8 TAY &1acb b9 40 0a LDA &0a40,Y ; objects_type &1ace c9 02 CMP #&02 ; TYPE_TREE &1ad0 f0 04 BEQ &1ad6 ; is_tree_or_boulder # Skip if object is not tree or boulder &1ad2 c9 03 CMP #&03 ; TYPE_BOULDER &1ad4 d0 0c BNE &1ae2 ; consider_next_object ; is_tree_or_boulder &1ad6 20 82 18 JSR &1882 ; check_if_enemy_can_see_object &1ad9 a5 14 LDA &14 ; object_exposure # Top bit set if enemy can fully see object &1adb 10 05 BPL &1ae2 ; consider_next_object &1add 8c 58 0c STY &0c58 ; targeted_object &1ae0 18 CLC # Leave with carry clear to indicate object found &1ae1 60 RTS ; consider_next_object &1ae2 ca DEX &1ae3 10 c4 BPL &1aa9 ; find_drainable_boulder_or_tree_on_stack &1ae5 38 SEC # Leave with carry set to indicate no object found &1ae6 60 RTS ; finish_update_if_object_being_plotted &1ae7 20 f3 1a JSR &1af3 ; check_if_object_can_be_updated # Returns carry clear if object is on an updating screen &1aea b0 1e BCS &1b0a ; leave ; finish_updating_enemy &1aec ae 0d 0c LDX &0c0d ; stack_on_entry_to_update_enemies # Pop out of updating_enemy on next return &1aef 9a TXS &1af0 4c c9 16 JMP &16c9 ; finished_updating_enemy ; check_if_object_can_be_updated # Leaves with carry set if object can be updated &1af3 38 SEC &1af4 2c 1f 0c BIT &0c1f ; suppress_update_of_visible_objects # Top bit set if world is being plotted &1af7 10 11 BPL &1b0a ; leave # Leave with carry set if plotting has finished &1af9 85 01 STA &01 ; object_to_update &1afb a5 0b LDA &0b ; player_object # Otherwise, check if the object is on screen &1afd 85 6e STA &6e ; object_to_consider # Player &1aff 8a TXA &1b00 48 PHA &1b01 98 TYA &1b02 48 PHA &1b03 20 96 20 JSR &2096 ; check_if_object_to_update_is_visible # Returns carry clear if object visible &1b06 68 PLA &1b07 a8 TAY &1b08 68 PLA &1b09 aa TAX ; leave &1b0a 60 RTS # Leave with carry clear if object can't be replotted ; handle_player_actions &1b0b ad 61 0c LDA &0c61 ; player_action &1b0e c9 22 CMP #&22 ; ACTION_HYPERSPACE &1b10 d0 0a BNE &1b1c ; not_hyperspace ; handle_hyperspace &1b12 20 47 21 JSR &2147 ; do_hyperspace &1b15 a9 00 LDA #&00 ; TYPE_ROBOT &1b17 8d 1c 0c STA &0c1c ; title_screen_object # Use the player for the death screen if player killed ; leave_with_carry_set &1b1a 38 SEC # Leave with carry set to indicate no object changed &1b1b 60 RTS ; not_hyperspace &1b1c a6 6e LDX &6e ; object_to_consider # Player &1b1e c9 23 CMP #&23 ; ACTION_UTURN &1b20 d0 11 BNE &1b33 ; not_uturn ; handle_uturn &1b22 0e 51 0c ASL &0c51 ; uturn_previously_pressed # &40 set at &11d8 if u-turn previously pressed &1b25 10 f3 BPL &1b1a ; leave_with_carry_set # Prevent repeating u-turn via auto-repeat &1b27 bd c0 09 LDA &09c0,X ; objects_h_angle &1b2a 49 80 EOR #&80 # Turn 180 degrees &1b2c 9d c0 09 STA &09c0,X ; objects_h_angle &1b2f a9 28 LDA #&28 ; tune_2 # Play tune for u-turn &1b31 d0 40 BNE &1b73 ; start_tune_and_set_viewpoint_has_changed # Always branches ; not_uturn &1b33 4e 6e 0c LSR &0c6e ; enemy_is_considering_robot # Clear top bit to indicate not considering a robot &1b36 20 ff 1b JSR &1bff ; prepare_vector_from_player_sights &1b39 20 cc 1c JSR &1ccc ; check_for_line_of_sight_to_tile # Returns carry set if no line of sight &1b3c b0 5a BCS &1b98 ; play_bad_action_sound # Player must be able to see the tile &1b3e ad 61 0c LDA &0c61 ; player_action &1b41 29 20 AND #&20 ; ACTION_ABSORB # Does the player want to absorb or transfer? &1b43 f0 64 BEQ &1ba9 ; try_to_create_object &1b45 20 78 2b JSR &2b78 ; calculate_tile_address # If so, check that there's an object in the tile &1b48 90 4e BCC &1b98 ; play_bad_action_sound # Carry clear if no object in tile &1b4a 29 3f AND #&3f ; OBJECT_UNDER_OBJECT_MASK &1b4c aa TAX &1b4d ad 61 0c LDA &0c61 ; player_action &1b50 4a LSR A &1b51 90 2a BCC &1b7d ; try_to_absorb_object ; try_to_transfer_into_object &1b53 bc 40 0a LDY &0a40,X ; objects_type &1b56 d0 40 BNE &1b98 ; play_bad_action_sound # Can't transfer if object isn't robot &1b58 20 00 12 JSR &1200 ; set_busy_plotting &1b5b 86 0b STX &0b ; player_object ; find_platform_below_player_loop &1b5d bd 00 01 LDA &0100,X ; objects_flags # Is the object on another object? &1b60 c9 40 CMP #&40 ; OBJECT_ON_OBJECT &1b62 90 0d BCC &1b71 ; player_not_on_platform &1b64 29 3f AND #&3f ; OBJECT_UNDER_OBJECT_MASK &1b66 aa TAX &1b67 49 3f EOR #&3f ; OBJECT_UNDER_OBJECT_MASK # The platform is always object &3f &1b69 d0 f2 BNE &1b5d ; find_platform_below_player_loop # Is this the platform? &1b6b bd 40 0a LDA &0a40,X ; objects_type &1b6e 8d e6 0c STA &0ce6 ; player_is_on_platform # Set to &06, TYPE_PLATFORM if player is on platform ; player_not_on_platform &1b71 a9 19 LDA #&19 ; tune_1 # Play tune for transferring consciousness ; start_tune_and_set_viewpoint_has_changed &1b73 20 f6 5f JSR &5ff6 ; start_tune &1b76 a9 80 LDA #&80 # Set top bit &1b78 8d 63 0c STA &0c63 ; viewpoint_has_changed &1b7b 38 SEC # Leave with carry set to indicate no object changed &1b7c 60 RTS ; try_to_absorb_object &1b7d ad 00 01 LDA &0100 ; objects_flags &1b80 30 16 BMI &1b98 ; play_bad_action_sound # Can't absorb if no object in slot (SLOT_EMPTY) &1b82 bd 40 0a LDA &0a40,X ; objects_type &1b85 c9 04 CMP #&04 ; TYPE_MEANIE &1b87 f0 52 BEQ &1bdb ; try_to_absorb_meanie &1b89 c9 06 CMP #&06 ; TYPE_PLATFORM &1b8b f0 0b BEQ &1b98 ; play_bad_action_sound # Can't absorb platform ; absorb_object &1b8d 20 d8 1e JSR &1ed8 ; remove_object # Remove object from stack or tile &1b90 86 01 STX &01 ; object_to_update &1b92 18 CLC # Clear carry to gain energy from object &1b93 20 27 21 JSR &2127 ; gain_or_lose_energy_from_object &1b96 18 CLC # Leave with carry clear to indicate object changed &1b97 60 RTS ; play_bad_action_sound &1b98 a9 aa LDA #&aa &1b9a 8d 1c 59 STA &591c ; sound_block_3 + 4 ; pitch &1b9d a9 05 LDA #&05 ; sound_5 &1b9f 20 40 34 JSR &3440 ; play_sound_with_envelope # Play sound for bad action (uses sound_block_3) &1ba2 a9 90 LDA #&90 &1ba4 8d 1c 59 STA &591c ; sound_block_3 + 4 ; pitch &1ba7 38 SEC # Leave with carry set to indicate no object changed &1ba8 60 RTS ; try_to_create_object &1ba9 20 11 21 JSR &2111 ; create_object_from_action &1bac b0 ea BCS &1b98 ; play_bad_action_sound &1bae 38 SEC # Set carry to lose energy from object &1baf 20 27 21 JSR &2127 ; gain_or_lose_energy_from_object &1bb2 b0 e4 BCS &1b98 ; play_bad_action_sound &1bb4 a6 01 LDX &01 ; object_to_update &1bb6 a5 3a LDA &3a ; object_x &1bb8 85 24 STA &24 ; tile_x &1bba a5 3c LDA &3c ; object_y &1bbc 85 26 STA &26 ; tile_y &1bbe 20 ff 1e JSR &1eff ; put_object_in_tile # Returns carry clear if object put in tile &1bc1 90 07 BCC &1bca ; object_was_created &1bc3 18 CLC # Clear carry to gain energy from object &1bc4 20 27 21 JSR &2127 ; gain_or_lose_energy_from_object &1bc7 4c 98 1b JMP &1b98 ; play_bad_action_sound ; object_was_created &1bca bd 40 0a LDA &0a40,X ; objects_type &1bcd d0 0a BNE &1bd9 ; leave_with_carry_clear ; is_robot &1bcf a4 0b LDY &0b ; player_object &1bd1 b9 c0 09 LDA &09c0,Y ; objects_h_angle &1bd4 49 80 EOR #&80 &1bd6 9d c0 09 STA &09c0,X ; objects_h_angle # Make new robot face player ; leave_with_carry_clear &1bd9 18 CLC # Leave with carry clear to indicate object changed &1bda 60 RTS ; try_to_absorb_meanie &1bdb a0 07 LDY #&07 # For each enemy, ; check_enemies_for_meanie_association_loop &1bdd b9 00 01 LDA &0100,Y ; objects_flags &1be0 30 18 BMI &1bfa ; consider_next_enemy # Skip if no object in slot (SLOT_EMPTY set) &1be2 b9 40 0a LDA &0a40,Y ; objects_type &1be5 c9 01 CMP #&01 ; TYPE_SENTRY # Skip if not sentinel or sentry &1be7 f0 04 BEQ &1bed ; is_sentry_or_sentinel &1be9 c9 05 CMP #&05 ; TYPE_SENTINEL &1beb d0 0d BNE &1bfa ; consider_next_enemy ; is_sentry_or_sentinel &1bed 8a TXA # X is the meanie &1bee d9 a0 0c CMP &0ca0,Y ; enemies_meanie_object &1bf1 d0 07 BNE &1bfa ; consider_next_enemy # Did this enemy create the meanie? &1bf3 a9 80 LDA #&80 &1bf5 99 a0 0c STA &0ca0,Y ; enemies_meanie_object # If so, remove the association &1bf8 d0 93 BNE &1b8d ; absorb_object # Always branches ; consider_next_enemy &1bfa 88 DEY &1bfb 10 e0 BPL &1bdd ; check_enemies_for_meanie_association_loop &1bfd 30 8e BMI &1b8d ; absorb_object # Always branches ; prepare_vector_from_player_sights &1bff ad c6 0c LDA &0cc6 ; sights_x &1c02 85 75 STA &75 ; a &1c04 a9 00 LDA #&00 &1c06 46 75 LSR &75 ; a &1c08 6a ROR A &1c09 46 75 LSR &75 ; a &1c0b 6a ROR A &1c0c 46 75 LSR &75 ; a &1c0e 6a ROR A &1c0f 18 CLC &1c10 85 3d STA &3d ; vector_h_angle_low &1c12 a5 75 LDA &75 ; a &1c14 7d c0 09 ADC &09c0,X ; objects_h_angle # vector h_angle = (sights_x / 8) + player h_angle &1c17 38 SEC &1c18 e9 0a SBC #&0a &1c1a 85 3e STA &3e ; vector_h_angle_high &1c1c ad c7 0c LDA &0cc7 ; sights_y &1c1f 38 SEC &1c20 e9 05 SBC #&05 &1c22 85 75 STA &75 ; a &1c24 a9 00 LDA #&00 &1c26 46 75 LSR &75 ; a &1c28 6a ROR A &1c29 46 75 LSR &75 ; a &1c2b 6a ROR A &1c2c 46 75 LSR &75 ; a &1c2e 6a ROR A &1c2f 46 75 LSR &75 ; a &1c31 6a ROR A &1c32 18 CLC &1c33 69 20 ADC #&20 &1c35 85 3f STA &3f ; vector_v_angle_low # vector v_angle = (sights_y / 16) + player v_angle &1c37 85 74 STA &74 ; a_fraction # + &320 &1c39 a5 75 LDA &75 ; a &1c3b 7d 40 01 ADC &0140,X ; objects_v_angle &1c3e 18 CLC &1c3f 69 03 ADC #&03 &1c41 85 40 STA &40 ; vector_v_angle_high ; prepare_vector_from_angle &1c43 20 75 0e JSR &0e75 ; calculate_double_sine_and_cosine # Calculate sin and cosine of vertical angle &1c46 a0 01 LDY #&01 # Process cosine &1c48 20 8c 1c JSR &1c8c ; process_sine_or_cosine &1c4b 85 33 STA &33 ; vector_vertical_cosine_high &1c4d 86 32 STX &32 ; vector_vertical_cosine_low &1c4f a0 00 LDY #&00 # Process sine &1c51 20 8c 1c JSR &1c8c ; process_sine_or_cosine &1c54 85 30 STA &30 ; vector_vertical_sine_high &1c56 86 2d STX &2d ; vector_vertical_sine_low &1c58 a5 3d LDA &3d ; vector_h_angle_low &1c5a 85 74 STA &74 ; a_fraction &1c5c a5 3e LDA &3e ; vector_h_angle_high &1c5e 20 75 0e JSR &0e75 ; calculate_double_sine_and_cosine # Calculate sin and cosine of horizontal angle &1c61 a0 01 LDY #&01 # Multiply cos(v_angle) * cos(h_angle) &1c63 a2 02 LDX #&02 # to set vector_y &1c65 20 6c 1c JSR &1c6c ; set_vector &1c68 a0 00 LDY #&00 # Multiply cos(v_angle) * sin(h_angle) &1c6a a2 00 LDX #&00 # to set vector_x ; set_vector &1c6c a9 00 LDA #&00 &1c6e 85 67 STA &67 ; invert_result &1c70 a5 32 LDA &32 ; vector_vertical_cosine_low # cos(v_angle) &1c72 85 6a STA &6a ; double_y_low &1c74 a5 33 LDA &33 ; vector_vertical_cosine_high &1c76 85 6b STA &6b ; double_y_high &1c78 b9 00 0c LDA &0c00,Y ; sine_or_cosine_low # sin(h_angle) for Y = 0, cos(h_angle) for Y = 1 &1c7b 85 68 STA &68 ; double_x_low &1c7d b9 02 0c LDA &0c02,Y ; sine_or_cosine_high &1c80 85 69 STA &69 ; double_x_high &1c82 20 9e 0f JSR &0f9e ; multiply_double_by_double # Multiply &1c85 95 2f STA &2f,X ; vector_x_fraction &1c87 a5 74 LDA &74 ; a_fraction &1c89 95 2c STA &2c,X ; vector_x_fraction_fraction # and store in vector_x for X = 0, vector_y for X = 2 &1c8b 60 RTS ; process_sine_or_cosine &1c8c b9 00 0c LDA &0c00,Y ; sine_or_cosine_low # Sign is stored in lowest bit of lowest byte &1c8f 85 74 STA &74 ; tmp &1c91 b9 02 0c LDA &0c02,Y ; sine_or_cosine_high &1c94 4a LSR A &1c95 66 74 ROR &74 ; bottom bit into carry &1c97 08 PHP &1c98 4a LSR A &1c99 66 74 ROR &74 ; tmp &1c9b 4a LSR A &1c9c 66 74 ROR &74 ; tmp &1c9e 4a LSR A &1c9f 66 74 ROR &74 ; tmp # Divide by 16 &1ca1 28 PLP &1ca2 90 03 BCC &1ca7 ; skip_inversion &1ca4 20 09 10 JSR &1009 ; invert_A_and_a_fraction # Invert if lowest bit of lowest byte is set ; skip_inversion &1ca7 a6 74 LDX &74 ; tmp &1ca9 60 RTS # Leave with A = result_high, X = result_low ; add_vector_to_object_position &1caa a2 02 LDX #&02 ; add_vector_to_object_position_loop # Loop over x, z, y (X = 0, 1, 2 respectively) &1cac a9 00 LDA #&00 &1cae 85 74 STA &74 ; sign &1cb0 b5 34 LDA &34,X ; object_x_fraction_fraction &1cb2 18 CLC &1cb3 75 2c ADC &2c,X ; vector_x_fraction_fraction &1cb5 95 34 STA &34,X ; object_x_fraction_fraction &1cb7 b5 2f LDA &2f,X ; vector_x_fraction &1cb9 10 02 BPL &1cbd ; is_positive &1cbb c6 74 DEC &74 ; sign ; is_positive &1cbd 75 37 ADC &37,X ; object_x_fraction &1cbf 95 37 STA &37,X ; object_x_fraction &1cc1 b5 3a LDA &3a,X ; objects_x &1cc3 65 74 ADC &74 ; sign &1cc5 95 3a STA &3a,X ; objects_x &1cc7 ca DEX &1cc8 10 e2 BPL &1cac ; add_vector_to_object_position_loop &1cca 60 RTS ; unused &1ccb 00 ; check_for_line_of_sight_to_tile &1ccc a6 6e LDX &6e ; object_to_consider # Observer &1cce 4e 56 0c LSR &0c56 ; targeted_object_is_in_line_of_sight # Clear top bit &1cd1 4e dd 0c LSR &0cdd ; tree_is_in_line_of_sight # Clear top bit &1cd4 20 b5 1e JSR &1eb5 ; get_object_details # Put observer's position in object variables ; check_for_line_of_sight_to_tile_loop &1cd7 20 aa 1c JSR &1caa ; add_vector_to_object_position # Add vector to position &1cda a5 3a LDA &3a ; object_x &1cdc 85 24 STA &24 ; tile_x &1cde c9 1f CMP #&1f &1ce0 b0 51 BCS &1d33 ; leave_with_carry_set # Leave with fail if beyond edge of landscape &1ce2 a5 3c LDA &3c ; object_y &1ce4 85 26 STA &26 ; tile_y &1ce6 c9 1f CMP #&1f &1ce8 b0 49 BCS &1d33 ; leave_with_carry_set # Leave with fail if beyond edge of landscape &1cea a9 80 LDA #&80 # Set top bit to check in get_tile_z_from_object &1cec 85 60 STA &60 ; do_line_of_sight_checks &1cee 85 0c STA &0c ; maximum_z_fraction_for_line_of_sight &1cf0 a9 00 LDA #&00 &1cf2 85 79 STA &79 ; z_fraction_relative_to_tile_or_object &1cf4 8d 67 0c STA &0c67 ; enemy_is_considering_boulder # Clear top bit to indicate not considering boulder &1cf7 20 e6 1d JSR &1de6 ; calculate_tile_address_z_and_slope # Returns carry set if tile not flat &1cfa b0 39 BCS &1d35 ; check_sloping_tile ; check_flat_tile &1cfc aa TAX ; tile z &1cfd a5 79 LDA &79 ; z_fraction_relative_to_tile_or_object &1cff 38 SEC &1d00 e5 38 SBC &38 ; object_z_fraction &1d02 85 79 STA &79 ; z_fraction_relative_to_tile_or_object &1d04 8a TXA &1d05 e5 3b SBC &3b ; object_z &1d07 30 ce BMI &1cd7 ; check_for_line_of_sight_to_tile_loop # Keep checking if tile is below position &1d09 d0 28 BNE &1d33 ; leave_with_carry_set # Stop if tile is above position &1d0b a5 79 LDA &79 ; z_fraction_relative_to_tile_or_object &1d0d c5 0c CMP &0c ; maximum_z_fraction_for_line_of_sight &1d0f b0 22 BCS &1d33 ; leave_with_carry_set # Stop if position is too far above tile &1d11 24 60 BIT &60 ; do_line_of_sight_checks # &40 is set if a slope or object has been checked &1d13 70 1e BVS &1d33 ; leave_with_carry_set # Don't allow flat tiles partially obscured by slopes &1d15 ad 6e 0c LDA &0c6e ; enemy_is_considering_robot &1d18 0d 67 0c ORA &0c67 ; enemy_is_considering_boulder &1d1b 30 04 BMI &1d21 ; skip_angle_check # Is the enemy considering a tree? &1d1d a5 30 LDA &30 ; vector_vertical_sine_high # Is the vector looking upwards? &1d1f 10 12 BPL &1d33 ; leave_with_carry_set # If, the enemy can't see the tree ; skip_angle_check &1d21 a6 6e LDX &6e ; object_to_consider # Observer &1d23 a5 24 LDA &24 ; tile_x # Is the same tile as the observer? &1d25 dd 00 09 CMP &0900,X ; objects_x &1d28 d0 07 BNE &1d31 ; leave_with_carry_clear &1d2a a5 26 LDA &26 ; tile_y &1d2c dd 80 09 CMP &0980,X ; objects_y &1d2f f0 a6 BEQ &1cd7 ; check_for_line_of_sight_to_tile_loop # If so, keep checking. If not, ; leave_with_carry_clear &1d31 18 CLC # Leave with carry clear to indicate clear line of sight &1d32 60 RTS ; leave_with_carry_set &1d33 38 SEC # Leave with carry set to indicate no line of sight &1d34 60 RTS ; check_sloping_tile &1d35 85 73 STA &73 ; tile_0_0_z # &73 - &77 form a square, first and last points same &1d37 85 77 STA &77 ; tile_0_0_z + 4 &1d39 46 60 LSR &60 ; do_line_of_sight_checks # Set &40 to indicate a sloping tile has been checked &1d3b e6 24 INC &24 ; tile_x &1d3d 20 e6 1d JSR &1de6 ; calculate_tile_address_z_and_slope # Find the heights of the other points of the tile &1d40 85 76 STA &76 ; tile_1_0_z &1d42 e6 26 INC &26 ; tile_y &1d44 20 e6 1d JSR &1de6 ; calculate_tile_address_z_and_slope &1d47 85 75 STA &75 ; tile_1_1_z &1d49 c6 24 DEC &24 ; tile_x &1d4b 20 e6 1d JSR &1de6 ; calculate_tile_address_z_and_slope &1d4e 85 74 STA &74 ; tile_0_1_z &1d50 c6 26 DEC &26 ; tile_y &1d52 20 78 2b JSR &2b78 ; calculate_tile_address &1d55 29 0f AND #&0f # What sort of slope is it? &1d57 c9 04 CMP #&04 ; SLOPE_4 &1d59 f0 04 BEQ &1d5f ; tile_has_one_flat_and_one_sloping_edge &1d5b c9 0c CMP #&0c ; SLOPE_C &1d5d d0 18 BNE &1d77 ; tile_is_corner_or_quadrilateral ; tile_has_one_flat_and_one_sloping_edge # If the tile has one flat and one sloping edge, &1d5f a5 3b LDA &3b ; object_z # i.e. 4: a= or =a or c: ab or == &1d61 c5 73 CMP &73 ; tile_0_0_z # b= =b == ab, &1d63 b0 0f BCS &1d74 ; to_check_for_line_of_sight_to_tile_loop &1d65 c5 74 CMP &74 ; tile_0_1_z &1d67 b0 0b BCS &1d74 ; to_check_for_line_of_sight_to_tile_loop &1d69 c5 75 CMP &75 ; tile_1_1_z &1d6b b0 07 BCS &1d74 ; to_check_for_line_of_sight_to_tile_loop &1d6d c5 76 CMP &76 ; tile_1_0_z &1d6f b0 03 BCS &1d74 ; to_check_for_line_of_sight_to_tile_loop # then the check fails only if the vector is below &1d71 4c 33 1d JMP &1d33 ; leave_with_carry_set # all four points in the tile ; to_check_for_line_of_sight_to_tile_loop &1d74 4c d7 1c JMP &1cd7 ; check_for_line_of_sight_to_tile_loop ; tile_is_corner_or_quadrilateral &1d77 4a LSR A # Choose an edge to use depending on the tile type &1d78 90 0f BCC &1d89 ; tile_is_corner_type_two &1d7a 4a LSR A &1d7b b0 05 BCS &1d82 ; tile_is_corner_type_one ; tile_is_quadrilateral # If &1 set and &2 clear, tile is quadrilateral &1d7d 29 01 AND #&01 # &4 determines which pair of edges to use for slope &1d7f 4c 9c 1d JMP &1d9c ; use_edge_for_slope # SLOPE_1 0, SLOPE_5 1, SLOPE_9 0, SLOPE_d 1 ; tile_is_corner_type_two &1d82 69 01 ADC #&01 # If &1 set and &2 set, tile is corner &1d84 29 03 AND #&03 # &4 and &8 determines which edge to use for slope &1d86 4c 8a 1d JMP &1d8a ; use_corner_for_slope # SLOPE_3 1, SLOPE_7 2, SLOPE_B 3, SLOPE_F 0 ; tile_is_corner_type_one # If &1 clear, tile is corner, &4 and &8 determine edge &1d89 4a LSR A # SLOPE 2 0, SLOPE_6 1, SLOPE_A 2. SLOPE_E 3 ; use_corner_for_slope &1d8a 85 78 STA &78 ; slope_edge_pair &1d8c 4a LSR A # If using pairs 1 or 3, &1d8d a5 37 LDA &37 ; object_x_fraction &1d8f 90 02 BCC &1d93 ; skip_inversion &1d91 49 ff EOR #&ff # Invert x component of vector for comparison ; skip_inversion &1d93 c5 39 CMP &39 ; object_y_fraction &1d95 a5 78 LDA &78 ; slope_edge_pair &1d97 2a ROL A # Pick an edge from the pair depending on whether &1d98 a8 TAY # x or y component of vector is larger &1d99 b9 de 1d LDA &1dde,Y ; edges_for_slope_check ; use_edge_for_slope &1d9c aa TAX &1d9d 4a LSR A &1d9e a4 37 LDY &37 ; object_x_fraction # Consider the vector relative to the tile &1da0 b0 02 BCS &1da4 ; use_x_for_slope ; use_y_for_slope &1da2 a4 39 LDY &39 ; object_y_fraction # using x or y co-ordinate depending on direction ; use_x_for_slope &1da4 4a LSR A &1da5 98 TYA &1da6 90 02 BCC &1daa ; skip_inversion &1da8 49 ff EOR #&ff # inverting if necessary ; skip_inversion &1daa 85 02 STA &02 ; vector_coordinate &1dac b5 73 LDA &73,X ; tile_0_0_z &1dae 85 78 STA &78 ; slope_start_z &1db0 b5 74 LDA &74,X ; tile_0_1_z # Calculate slope along edge: X = 0, 01 - 00 (x- edge) &1db2 38 SEC # X = 1, 11 - 01 (y+ edge) &1db3 f5 73 SBC &73,X ; tile_0_0_z # X = 2, 10 - 11 (x+ edge) &1db5 08 PHP # X = 3, 00 - 10 (y- edge) &1db6 10 05 BPL &1dbd ; skip_inversion &1db8 49 ff EOR #&ff # Use absolute value of slope for multiplication &1dba 18 CLC &1dbb 69 01 ADC #&01 ; skip_inversion &1dbd 85 75 STA &75 ; multiplicand # Calculate where the vector crosses the slope &1dbf a5 02 LDA &02 ; vector_coordinate &1dc1 20 03 0d JSR &0d03 ; multiply_A_by_byte # Multiply vector coordinate within tile * slope &1dc4 28 PLP ; sign of slope &1dc5 20 07 10 JSR &1007 ; invert_A_and_a_fraction_if_negative # Restore sign of slope to result &1dc8 18 CLC &1dc9 65 78 ADC &78 ; slope_start_z # Turn into a position &1dcb 85 75 STA &75 ; a &1dcd a5 38 LDA &38 ; object_z_fraction # Compare the points &1dcf 38 SEC &1dd0 e5 74 SBC &74 ; a_fraction &1dd2 a5 3b LDA &3b ; object_z &1dd4 e5 75 SBC &75 ; a &1dd6 10 03 BPL &1ddb ; to_check_for_line_of_sight_to_tile_loop &1dd8 4c 33 1d JMP &1d33 ; leave_with_carry_set # If the vector is below the slope, the tile was hit ; to_check_for_line_of_sight_to_tile_loop &1ddb 4c d7 1c JMP &1cd7 ; check_for_line_of_sight_to_tile_loop # If the vector is above the slope, continue checking ; edges_for_slope_check ; y x &1dde 00 03 ; pair 0: x- y- &1de0 01 00 ; pair 1: y+ x- &1de2 01 02 ; pair 2: y+ x+ &1de4 02 03 ; pair 3: x+ y- ; calculate_tile_address_z_and_slope &1de6 20 78 2b JSR &2b78 ; calculate_tile_address # Carry set if the tile contains one or more objects &1de9 b0 3d BCS &1e28 ; get_tile_z_from_object &1deb 48 PHA &1dec 29 0f AND #&0f &1dee a8 TAY &1def 68 PLA &1df0 4a LSR A &1df1 4a LSR A &1df2 4a LSR A &1df3 4a LSR A # Leave with tile z in A &1df4 c0 01 CPY #&01 # Leave with tile slope in Y, carry set if not flat &1df6 60 RTS # Leave with X unchanged ; get_tile_z_for_line_of_sight &1df7 cc 58 0c CPY &0c58 ; targeted_object &1dfa d0 03 BNE &1dff ; not_targeted_object &1dfc 6e 56 0c ROR &0c56 ; targeted_object_is_in_line_of_sight # Set top bit if this is the targeted object ; not_targeted_object &1dff b9 40 0a LDA &0a40,Y ; objects_type &1e02 c9 03 CMP #&03 ; TYPE_BOULDER &1e04 f0 2b BEQ &1e31 ; get_boulder_or_tree_z_for_line_of_sight &1e06 c9 02 CMP #&02 ; TYPE_TREE &1e08 f0 27 BEQ &1e31 ; get_boulder_or_tree_z_for_line_of_sight &1e0a c9 06 CMP #&06 ; TYPE_PLATFORM &1e0c d0 7f BNE &1e8d ; get_height_of_lowest_object # Leave with height of object if robot or enemy ; get_platform_z_for_line_of_sight # If the object is the platform, &1e0e 20 98 1e JSR &1e98 ; get_minimum_x_or_y_fraction_from_tile_centre &1e11 c9 64 CMP #&64 # Is the observer looking sufficiently near its centre? &1e13 b0 6d BCS &1e82 ; skip_targeting_object &1e15 a9 10 LDA #&10 # Require the observer to look close vertically &1e17 85 0c STA &0c ; maximum_z_fraction_for_line_of_sight &1e19 b9 00 0a LDA &0a00,Y ; objects_z_fraction &1e1c 18 CLC &1e1d 69 20 ADC #&20 # Use the top of the platform for the check &1e1f 85 79 STA &79 ; z_fraction_relative_to_tile_or_object &1e21 b9 40 09 LDA &0940,Y ; objects_z &1e24 69 00 ADC #&00 &1e26 18 CLC &1e27 60 RTS ; get_tile_z_from_object &1e28 29 3f AND #&3f ; OBJECT_UNDER_OBJECT_MASK &1e2a a8 TAY &1e2b 24 60 BIT &60 ; do_line_of_sight_checks &1e2d 10 5e BPL &1e8d ; get_height_of_lowest_object &1e2f 30 c6 BMI &1df7 ; get_tile_z_for_line_of_sight # Always branches ; get_boulder_or_tree_z_for_line_of_sight # If the object is a tree or boulder, &1e31 20 98 1e JSR &1e98 ; get_minimum_x_or_y_fraction_from_tile_centre &1e34 c9 40 CMP #&40 # Is the observer looking sufficiently near its centre? &1e36 b0 4a BCS &1e82 ; skip_targeting_object &1e38 b9 40 0a LDA &0a40,Y ; objects_type # If so, &1e3b c9 02 CMP #&02 ; TYPE_TREE &1e3d f0 13 BEQ &1e52 ; is_tree ; is_boulder # if the object is a boulder &1e3f 38 SEC &1e40 6e 67 0c ROR &0c67 ; enemy_is_considering_boulder # Set top bit to indicate boulder is targetable &1e43 b9 00 0a LDA &0a00,Y ; objects_z_fraction &1e46 38 SEC &1e47 e9 60 SBC #&60 # Use the top of the boulder for the check &1e49 85 79 STA &79 ; z_fraction_relative_to_tile_or_object &1e4b b9 40 09 LDA &0940,Y ; objects_z &1e4e e9 00 SBC #&00 &1e50 18 CLC &1e51 60 RTS ; is_tree # If the object is a tree, &1e52 b9 00 0a LDA &0a00,Y ; objects_z_fraction &1e55 38 SEC &1e56 e5 38 SBC &38 ; object_z_fraction # Use tree z - point z &1e58 85 75 STA &75 ; z_fraction_relative_to_vector &1e5a b9 40 09 LDA &0940,Y ; objects_z &1e5d e5 3b SBC &3b ; object_z &1e5f 48 PHA &1e60 a5 75 LDA &75 ; z_fraction_relative_to_vector &1e62 18 CLC &1e63 69 e0 ADC #&e0 # Use top of tree &1e65 85 75 STA &75 ; z_fraction_relative_to_vector &1e67 68 PLA &1e68 69 00 ADC #&00 &1e6a 30 16 BMI &1e82 ; skip_targeting_object &1e6c 4a LSR A &1e6d 66 75 ROR &75 ; z_fraction_relative_to_vector # Halve difference &1e6f 4a LSR A &1e70 d0 10 BNE &1e82 ; skip_targeting_object &1e72 a5 75 LDA &75 ; z_fraction_relative_to_vector &1e74 6a ROR A &1e75 c5 74 CMP &74 ; a_fraction # From get_minimum_x_or_y_fraction_from_tile_centre &1e77 90 09 BCC &1e82 ; skip_targeting_object &1e79 2c 56 0c BIT &0c56 ; targeted_object_is_in_line_of_sight # Top bit set if this is the targeted object &1e7c 30 04 BMI &1e82 ; skip_targeting_object &1e7e 38 SEC &1e7f 6e dd 0c ROR &0cdd ; tree_is_in_line_of_sight # Otherwise set top bit to indicate enemy can see a tree ; skip_targeting_object &1e82 b9 40 0a LDA &0a40,Y ; objects_type &1e85 c9 02 CMP #&02 ; TYPE_TREE &1e87 f0 04 BEQ &1e8d ; get_height_of_lowest_object &1e89 a9 c0 LDA #&c0 # Set &40 to indicate an object has been checked &1e8b 85 60 STA &60 ; do_line_of_sight_checks # and &80 to do get_tile_z_for_line_of_sight next time ; get_height_of_lowest_object &1e8d b9 00 01 LDA &0100,Y ; objects_flags # Find the lowest object in the stack &1e90 c9 40 CMP #&40 ; OBJECT_ON_OBJECT &1e92 b0 94 BCS &1e28 ; get_tile_z_from_object &1e94 b9 40 09 LDA &0940,Y ; objects_z # Leave with object z &1e97 60 RTS ; get_minimum_x_or_y_fraction_from_tile_centre &1e98 a5 37 LDA &37 ; object_x_fraction &1e9a 38 SEC &1e9b e9 80 SBC #&80 &1e9d 10 02 BPL &1ea1 ; skip_x_inversion &1e9f 49 ff EOR #&ff ; skip_x_inversion &1ea1 85 74 STA &74 ; a_fraction &1ea3 a5 39 LDA &39 ; object_y_fraction &1ea5 38 SEC &1ea6 e9 80 SBC #&80 &1ea8 10 02 BPL &1eac ; skip_y_inversion &1eaa 49 ff EOR #&ff ; skip_y_inversion &1eac c5 74 CMP &74 ; a_fraction &1eae b0 02 BCS &1eb2 ; skip_minimum &1eb0 a5 74 LDA &74 ; a_fraction ; skip_minimum &1eb2 85 74 STA &74 ; a_fraction &1eb4 60 RTS ; get_object_details &1eb5 a9 00 LDA #&00 &1eb7 85 34 STA &34 ; object_x_fraction_fraction &1eb9 85 35 STA &35 ; object_z_fraction_fraction &1ebb 85 36 STA &36 ; object_y_fraction_fraction &1ebd a9 80 LDA #&80 &1ebf 85 37 STA &37 ; object_x_fraction &1ec1 85 39 STA &39 ; object_y_fraction &1ec3 bd 00 0a LDA &0a00,X ; objects_z_fraction &1ec6 85 38 STA &38 ; object_z_fraction &1ec8 bd 00 09 LDA &0900,X ; objects_x &1ecb 85 3a STA &3a ; object_x &1ecd bd 40 09 LDA &0940,X ; objects_z &1ed0 85 3b STA &3b ; object_z &1ed2 bd 80 09 LDA &0980,X ; objects_y &1ed5 85 3c STA &3c ; object_y &1ed7 60 RTS ; remove_object &1ed8 bd 00 09 LDA &0900,X ; objects_x &1edb 85 24 STA &24 ; tile_x &1edd bd 80 09 LDA &0980,X ; objects_y &1ee0 85 26 STA &26 ; tile_y &1ee2 20 78 2b JSR &2b78 ; calculate_tile_address &1ee5 bd 00 01 LDA &0100,X ; objects_flags &1ee8 c9 40 CMP #&40 ; OBJECT_ON_OBJECT # Was the object on another object? &1eea 90 04 BCC &1ef0 ; use_object_z &1eec 09 c0 ORA #&c0 ; TILE_HAS_OBJECT # If so, set that object to be topmost in the tile &1eee d0 07 BNE &1ef7 ; set_tile_z # Always branches ; use_object_z &1ef0 bd 40 09 LDA &0940,X ; objects_z # Otherwise, use object z for tile z &1ef3 0a ASL A &1ef4 0a ASL A &1ef5 0a ASL A &1ef6 0a ASL A ; set_tile_z &1ef7 91 5e STA (&5e),Y ; tile_address &1ef9 a9 80 LDA #&80 ; SLOT_EMPTY # Set top bit to indicate object slot is empty &1efb 9d 00 01 STA &0100,X ; objects_flags &1efe 60 RTS ; put_object_in_tile &1eff a5 24 LDA &24 ; tile_x &1f01 9d 00 09 STA &0900,X ; objects_x &1f04 a5 26 LDA &26 ; tile_y &1f06 9d 80 09 STA &0980,X ; objects_y &1f09 20 78 2b JSR &2b78 ; calculate_tile_address &1f0c 90 3d BCC &1f4b ; no_object_in_tile # Is there already an object in the tile? &1f0e 8c 77 1f STY &1f77 ; tmp_y # Preserve Y on exit &1f11 29 3f AND #&3f ; OBJECT_UNDER_OBJECT_MASK # Get the id of the object &1f13 a8 TAY &1f14 b9 40 0a LDA &0a40,Y ; objects_type &1f17 c9 03 CMP #&03 ; TYPE_BOULDER &1f19 f0 04 BEQ &1f1f ; is_boulder_or_platform # Is it a boulder or the sentinel's platform? &1f1b c9 06 CMP #&06 ; TYPE_PLATFORM &1f1d d0 56 BNE &1f75 ; leave_with_carry_set # If not, an object can't go here ; is_boulder_or_platform &1f1f 98 TYA &1f20 09 40 ORA #&40 ; OBJECT_ON_OBJECT # Set &40 to indicate object is on another object &1f22 9d 00 01 STA &0100,X ; objects_flags &1f25 b9 40 0a LDA &0a40,Y ; objects_type &1f28 c9 06 CMP #&06 ; TYPE_PLATFORM &1f2a d0 0b BNE &1f37 ; is_boulder &1f2c b9 00 0a LDA &0a00,Y ; objects_z_fraction # The sentinel's platform is one unit high &1f2f 9d 00 0a STA &0a00,X ; objects_z_fraction &1f32 18 CLC &1f33 a9 01 LDA #&01 &1f35 d0 0b BNE &1f42 ; to_set_objects_z # Always branches ; is_boulder &1f37 b9 00 0a LDA &0a00,Y ; objects_z_fraction # Boulders are half a unit high &1f3a 18 CLC &1f3b 69 80 ADC #&80 &1f3d 9d 00 0a STA &0a00,X ; objects_z_fraction &1f40 a9 00 LDA #&00 ; to_set_objects_z &1f42 79 40 09 ADC &0940,Y ; objects_z &1f45 ac 77 1f LDY &1f77 ; tmp_y &1f48 4c 5b 1f JMP &1f5b ; set_object_z ; no_object_in_tile &1f4b 48 PHA &1f4c a9 00 LDA #&00 # Zero to indicate object is on tile &1f4e 9d 00 01 STA &0100,X ; objects_flags &1f51 a9 e0 LDA #&e0 &1f53 9d 00 0a STA &0a00,X ; objects_z_fraction &1f56 68 PLA &1f57 4a LSR A # Use tile z &1f58 4a LSR A &1f59 4a LSR A &1f5a 4a LSR A ; set_object_z &1f5b 9d 40 09 STA &0940,X ; objects_z &1f5e 8a TXA # Store id of topmost object in tile array &1f5f 09 c0 ORA #&c0 ; TILE_HAS_OBJECT # Set &c0 to indicate tile contains object &1f61 91 5e STA (&5e),Y ; tile_address &1f63 a9 f5 LDA #&f5 # Only the player's vertical angle is meaningful &1f65 9d 40 01 STA &0140,X ; objects_v_angle &1f68 20 94 31 JSR &3194 ; prnd # Give object random rotation &1f6b 29 f8 AND #&f8 &1f6d 18 CLC &1f6e 69 60 ADC #&60 &1f70 9d c0 09 STA &09c0,X ; objects_h_angle &1f73 18 CLC # Leave with carry clear to indicate object placed &1f74 60 RTS ; leave_with_carry_set &1f75 38 SEC # Leave with carry set to indicate object can't go here &1f76 60 RTS ; tmp_y &1f77 00 ; reset_variables_after_updating_object &1f78 a9 00 LDA #&00 # Clear &80 to keep sights, clear &40 for no dithering &1f7a 8d 6d 0c STA &0c6d ; how_to_plot_object_to_update &1f7d 8d 4d 0c STA &0c4d ; what_to_plot_behind_object_to_update # Clear top bit to plot world behind object &1f80 8d 1e 0c STA &0c1e ; not_ready_to_dither # Clear top bit to permit dithering to occur &1f83 60 RTS ; update_object_on_screen &1f84 20 96 20 JSR &2096 ; check_if_object_to_update_is_visible # Returns carry clear if object visible &1f87 b0 ef BCS &1f78 ; reset_variables_after_updating_object # Is the object on screen? If not, no plotting needed &1f89 a9 19 LDA #&19 # If so, dither it over 25 frames &1f8b 8d 94 20 STA &2094 ; dithering_frames &1f8e ad 6d 0c LDA &0c6d ; how_to_plot_object_to_update # If top bit set, unplot sights &1f91 10 05 BPL &1f98 ; update_part_of_object_to_update &1f93 78 SEI &1f94 20 a7 3a JSR &3aa7 ; unplot_sights &1f97 58 CLI ; update_part_of_object &1f98 a6 6e LDX &6e ; object_to_consider # Updating object &1f9a ad 62 0c LDA &0c62 ; visible_object_offset_in_groups &1f9d 8d 95 20 STA &2095 ; half_visible_object_offset_in_groups &1fa0 a9 00 LDA #&00 &1fa2 4e 95 20 LSR &2095 ; half_visible_object_offset_in_groups &1fa5 6a ROR A &1fa6 85 1f STA &1f ; angle_low_adjustment &1fa8 ad 95 20 LDA &2095 ; half_visible_object_offset_in_groups # Temporarily change angle to centre buffer &1fab 7d c0 09 ADC &09c0,X ; objects_h_angle # (undone at &1fe1) &1fae 9d c0 09 STA &09c0,X ; objects_h_angle &1fb1 a0 00 LDY #&00 ; MOVE_RIGHT &1fb3 84 08 STY &08 ; previous_panning_direction &1fb5 ad 69 0c LDA &0c69 ; buffer_width_in_groups &1fb8 20 97 29 JSR &2997 ; initialise_buffer_variables_for_updating_object &1fbb a9 19 LDA #&19 # Start at top of buffer &1fbd a0 18 LDY #&18 # 192 pixels high &1fbf ae 69 0c LDX &0c69 ; buffer_width_in_groups # width &1fc2 20 02 22 JSR &2202 ; plot_background &1fc5 2c 4d 0c BIT &0c4d ; what_to_plot_behind_object_to_update # If top bit set, plot only object, &1fc8 10 08 BPL &1fd2 ; plot_world_behind_dithering_object # if top bit clear, plot world as well ; plot_only_dithering_object &1fca a4 01 LDY &01 ; object_to_update &1fcc 20 33 5d JSR &5d33 ; plot_object # Plot object to empty buffer &1fcf 4c d5 1f JMP &1fd5 ; skip_plotting_world ; plot_world_behind_dithering_object &1fd2 20 24 26 JSR &2624 ; plot_world # Plot object and world to buffer ; skip_plotting_world &1fd5 a0 00 LDY #&00 # Unbuffer as though panning right &1fd7 20 fb 38 JSR &38fb ; initialise_buffer_unbuffering_address &1fda a6 6e LDX &6e ; object_to_consider # Updating object &1fdc a9 00 LDA #&00 &1fde 85 1f STA &1f ; angle_low_adjustment &1fe0 38 SEC &1fe1 bd c0 09 LDA &09c0,X ; objects_h_angle # Undo temporary change from &1fa8 &1fe4 ed 95 20 SBC &2095 ; half_visible_object_offset_in_groups &1fe7 9d c0 09 STA &09c0,X ; objects_h_angle &1fea a9 00 LDA #&00 &1fec 85 75 STA &75 ; a &1fee ad 62 0c LDA &0c62 ; visible_object_offset_in_groups &1ff1 0a ASL A &1ff2 0a ASL A &1ff3 0a ASL A &1ff4 26 75 ROL &75 ; a &1ff6 18 CLC &1ff7 6d c2 0c ADC &0cc2 ; viewport_screen_address_low &1ffa 8d 92 20 STA &2092 ; base_screen_address_low &1ffd ad c3 0c LDA &0cc3 ; viewport_screen_address_high &2000 65 75 ADC &75 ; a &2002 c9 80 CMP #&80 &2004 90 02 BCC &2008 ; skip_wraparound &2006 e9 20 SBC #&20 ; skip_wraparound &2008 8d 93 20 STA &2093 ; base_screen_address_high &200b 2c 6d 0c BIT &0c6d ; how_to_plot_object_to_update # If &40 set, dither object onto screen &200e 50 12 BVC &2022 ; plot_changed_object_without_dithering &2010 2c 4e 0c BIT &0c4e ; player_has_died # Top bit set if player died by draining or hyperspacing &2013 10 05 BPL &201a ; player_is_alive &2015 a9 28 LDA #&28 # Dither in cause of death over 40 frames &2017 8d 94 20 STA &2094 ; dithering_frames ; player_is_alive &201a 20 5f 5e JSR &5e5f ; dither_buffer_to_screen &201d ad 4e 0c LDA &0c4e ; player_has_died # Top bit set if player died by draining or hyperspacing &2020 30 3f BMI &2061 ; skip_undithered_plot ; plot_changed_object_without_dithering # After dithering, replot the updated object completely &2022 78 SEI &2023 20 a7 3a JSR &3aa7 ; unplot_sights &2026 38 SEC &2027 6e d7 0c ROR &0cd7 ; suppress_plotting_of_sights # Set top bit to stop sights being plotted in unbuffer &202a 58 CLI &202b ad 92 20 LDA &2092 ; base_screen_address_low &202e 85 64 STA &64 ; target_address_low &2030 ad 93 20 LDA &2093 ; base_screen_address_high &2033 85 65 STA &65 ; target_address_high &2035 ad 69 0c LDA &0c69 ; buffer_width_in_groups &2038 85 15 STA &15 ; columns_to_unbuffer &203a 4c 58 20 JMP &2058 ; unbuffer_buffer ; unbuffer_buffer_loop &203d ad 92 20 LDA &2092 ; base_screen_address_low &2040 18 CLC &2041 69 08 ADC #&08 # Move right two pixels on screen &2043 8d 92 20 STA &2092 ; base_screen_address_low &2046 85 64 STA &64 ; target_address_low &2048 ad 93 20 LDA &2093 ; base_screen_address_high &204b 69 00 ADC #&00 &204d c9 80 CMP #&80 &204f 90 02 BCC &2053 ; skip_wraparound &2051 e9 20 SBC #&20 ; skip_wraparound &2053 8d 93 20 STA &2093 ; base_screen_address_high &2056 85 65 STA &65 ; target_address_high ; unbuffer_buffer &2058 a4 08 LDY &08 ; previous_panning_direction # Zero to unbuffer in columns &205a 20 32 38 JSR &3832 ; unbuffer_column_or_row &205d c6 15 DEC &15 ; columns_to_unbuffer &205f d0 dc BNE &203d ; unbuffer_buffer_loop ; skip_undithered_plot &2061 ad 62 0c LDA &0c62 ; visible_object_offset_in_groups &2064 18 CLC &2065 6d 69 0c ADC &0c69 ; buffer_width_in_groups &2068 8d 62 0c STA &0c62 ; visible_object_offset_in_groups &206b ad 6a 0c LDA &0c6a ; visible_object_width_in_groups &206e 38 SEC &206f ed 69 0c SBC &0c69 ; buffer_width_in_groups &2072 f0 0c BEQ &2080 ; leave_after_replotting_sights # Does the object need to be plotted in two parts? &2074 8d 6a 0c STA &0c6a ; visible_object_width_in_groups &2077 8d 69 0c STA &0c69 ; buffer_width_in_groups &207a 8d 94 20 STA &2094 ; dithering_frames &207d 4c 98 1f JMP &1f98 ; update_part_of_object # If so, plot the other part ; leave_after_replotting_sights &2080 4e d7 0c LSR &0cd7 ; suppress_plotting_of_sights # Clear top bit to permit sights plotting &2083 ad 5f 0c LDA &0c5f ; sights_are_active # Top bit set if sights are active &2086 10 05 BPL &208d ; to_reset_variables_after_updating_object &2088 78 SEI &2089 20 d9 39 JSR &39d9 ; plot_sights &208c 58 CLI ; to_reset_variables_after_updating_object &208d 4c 78 1f JMP &1f78 ; reset_variables_after_updating_object ; buffer_unbuffering_address_low &2090 00 ; buffer_unbuffering_address_high &2091 00 ; base_screen_address_low &2092 00 ; base_screen_address_high &2093 00 ; dithering_frames &2094 00 ; half_visible_object_offset_in_groups &2095 00 ; check_if_object_to_update_is_visible &2096 a4 01 LDY &01 ; object_to_update &2098 c4 0b CPY &0b ; player_object &209a f0 69 BEQ &2105 ; leave_with_carry_set # The player is never visible &209c 20 01 5c JSR &5c01 ; calculate_object_relative_angles_and_distance &209f a4 01 LDY &01 ; object_to_update &20a1 be 40 0a LDX &0a40,Y ; objects_type &20a4 bd 07 21 LDA &2107,X ; objects_height_fraction &20a7 cd d4 0c CMP &0cd4 ; previous_object_height_fraction &20aa b0 03 BCS &20af ; use_current_height &20ac ad d4 0c LDA &0cd4 ; previous_object_height_fraction # Use the height of a disappearing object if greater ; use_current_height &20af 85 80 STA &80 ; x_fraction &20b1 a9 00 LDA #&00 &20b3 8d d4 0c STA &0cd4 ; previous_object_height_fraction &20b6 20 1d 56 JSR &561d ; calculate_object_relative_vertical_angle # This actually calculates the angle between h and y &20b9 ad 59 0c LDA &0c59 ; object_relative_h_angle_low # where h is the hypotenuse in the horizontal plane &20bc 38 SEC &20bd e5 8a SBC &8a ; angle_low &20bf 85 74 STA &74 ; a_fraction &20c1 ad 57 0c LDA &0c57 ; object_relative_h_angle_high # This is relative angle + 10 * 256/360 degrees &20c4 e5 8b SBC &8b ; angle_high # Calculate h_angle - angle &20c6 10 04 BPL &20cc ; skip_floor &20c8 a9 00 LDA #&00 # Passes if negative &20ca f0 07 BEQ &20d3 ; skip_check # Always branches ; skip_floor &20cc 06 74 ASL &74 ; a_fraction &20ce 2a ROL A &20cf c9 28 CMP #&28 # If more than two screen widths (40 * 256/360 degrees) &20d1 b0 32 BCS &2105 ; leave_with_carry_set # object isn't visible ; skip_check &20d3 8d 62 0c STA &0c62 ; visible_object_offset_in_groups &20d6 ad 59 0c LDA &0c59 ; object_relative_h_angle_low # Calculate h_angle + angle &20d9 18 CLC &20da 65 8a ADC &8a ; angle_low &20dc 85 74 STA &74 ; a_fraction &20de ad 57 0c LDA &0c57 ; object_relative_h_angle_high # This is relative angle + 10 * 256/360 degrees &20e1 65 8b ADC &8b ; angle_high &20e3 30 20 BMI &2105 ; leave_with_carry_set # Fails if negative &20e5 06 74 ASL &74 ; a_fraction &20e7 2a ROL A &20e8 c9 28 CMP #&28 &20ea 90 02 BCC &20ee ; skip_ceiling &20ec a9 27 LDA #&27 ; skip_ceiling &20ee 18 CLC &20ef 69 01 ADC #&01 &20f1 38 SEC &20f2 ed 62 0c SBC &0c62 ; visible_object_offset_in_groups &20f5 8d 6a 0c STA &0c6a ; visible_object_width_in_groups &20f8 f0 0b BEQ &2105 ; leave_with_carry_set &20fa c9 15 CMP #&15 &20fc 90 02 BCC &2100 ; skip_floor &20fe a9 14 LDA #&14 # One screen width (20 * 256/360 degrees) ; skip_floor &2100 8d 69 0c STA &0c69 ; buffer_width_in_groups # Set width of buffer necessary to bound object &2103 18 CLC &2104 60 RTS ; leave_with_carry_set &2105 38 SEC # Leave with carry set to indicate object isn't visible &2106 60 RTS ; objects_height_fraction &2107 3e ; TYPE_ROBOT &2108 46 ; TYPE_SENTRY &2109 72 ; TYPE_TREE &210a 7a ; TYPE_BOULDER &210b 4a ; TYPE_MEANIE &210c 4e ; TYPE_SENTINEL &210d c1 ; TYPE_PLATFORM ; create_object &210e 8d 61 0c STA &0c61 ; player_action ; create_object_from_action &2111 a2 3f LDX #&3f ; find_empty_slot_loop &2113 bd 00 01 LDA &0100,X ; objects_flags &2116 30 05 BMI &211d ; found_empty_slot &2118 ca DEX &2119 10 f8 BPL &2113 ; find_empty_slot_loop &211b 38 SEC # Leave with carry set to indicate no free slots &211c 60 RTS ; found_empty_slot &211d 86 01 STX &01 ; object_to_update &211f ad 61 0c LDA &0c61 ; player_action &2122 9d 40 0a STA &0a40,X ; objects_type &2125 18 CLC # Leave with carry clear to indicate slot found &2126 60 RTS ; gain_or_lose_energy_from_object &2127 bc 40 0a LDY &0a40,X ; objects_type &212a ad 0a 0c LDA &0c0a ; player_energy &212d 90 07 BCC &2136 ; add_energy_from_object &212f f9 40 21 SBC &2140,Y ; energy_in_objects # If carry set, subtract object energy from player &2132 b0 05 BCS &2139 ; set_player_energy &2134 38 SEC # Leave with carry set to indicate player out of energy &2135 60 RTS ; add_energy_from_object &2136 79 40 21 ADC &2140,Y ; energy_in_objects # If carry clear, add object energy to player ; set_player_energy &2139 29 3f AND #&3f &213b 8d 0a 0c STA &0c0a ; player_energy &213e 18 CLC # Leave with carry clear to indicate player has energy &213f 60 RTS ; energy_in_objects &2140 03 ; TYPE_ROBOT &2141 03 ; TYPE_SENTRY &2142 01 ; TYPE_TREE &2143 02 ; TYPE_BOULDER &2144 01 ; TYPE_MEANIE &2145 04 ; TYPE_SENTINEL &2146 00 ; TYPE_PLATFORM ; do_hyperspace &2147 a9 00 LDA #&00 ; TYPE_ROBOT &2149 20 0e 21 JSR &210e ; create_object # Returns new object in object_to_update &214c a6 0b LDX &0b ; player_object &214e bd 40 09 LDA &0940,X ; objects_z &2151 18 CLC &2152 69 01 ADC #&01 &2154 a6 01 LDX &01 ; object_to_update &2156 20 24 12 JSR &1224 ; put_object_in_random_tile_below_z # Returns carry set if object couldn't be put in tile &2159 b0 43 BCS &219e ; leave &215b 38 SEC # Carry set to lose energy from object &215c 20 27 21 JSR &2127 ; gain_or_lose_energy_from_object &215f 90 0f BCC &2170 ; player_survived_hyperspace # Returns carry clear if player still has energy &2161 20 d8 1e JSR &1ed8 ; remove_object &2164 a9 03 LDA #&03 # Use black background with stars if player died &2166 8d 4c 0c STA &0c4c ; background_type &2169 a9 80 LDA #&80 # Set top bit to indicate player has hyperspaced &216b 8d de 0c STA &0cde ; player_has_hyperspaced &216e d0 28 BNE &2198 ; set_viewpoint_has_changed # Always branches ; player_survived_hyperspace &2170 a9 00 LDA #&00 ; tune_0 # Play tune for hyperspacing &2172 20 f6 5f JSR &5ff6 ; start_tune &2175 a6 0b LDX &0b ; player_object &2177 bd 00 09 LDA &0900,X ; objects_x # Is the player hyperspacing from the platform? &217a cd 19 0c CMP &0c19 ; platform_x &217d d0 12 BNE &2191 ; not_on_platform &217f bd 80 09 LDA &0980,X ; objects_y &2182 cd 1a 0c CMP &0c1a ; platform_y &2185 d0 0a BNE &2191 ; not_on_platform &2187 a9 c0 LDA #&c0 # Set &80 to indicate player has hyperspaced &2189 8d de 0c STA &0cde ; player_has_hyperspaced # and &40 to indicate landscape has been completed &218c a9 80 LDA #&80 &218e 8d 71 0c STA &0c71 ; play_game_after_generation # Set top bit to play game after generating landscape ; not_on_platform &2191 20 00 12 JSR &1200 ; set_busy_plotting &2194 a6 01 LDX &01 ; object_to_update &2196 86 0b STX &0b ; player_object # Transfer player to new object ; set_viewpoint_has_changed &2198 a9 80 LDA #&80 &219a 8d 63 0c STA &0c63 ; viewpoint_has_changed # Set top bit to redraw the viewpoint &219d 18 CLC ; leave &219e 60 RTS ; plot_stack_of_objects &219f 29 3f AND #&3f ; OBJECT_UNDER_OBJECT_MASK &21a1 8d 6c 0c STA &0c6c ; top_object &21a4 a2 00 LDX #&00 ; calculate_height_of_stack_loop &21a6 e8 INX &21a7 29 3f AND #&3f ; OBJECT_UNDER_OBJECT_MASK &21a9 a8 TAY &21aa b9 00 01 LDA &0100,Y ; objects_flags &21ad c9 40 CMP #&40 ; OBJECT_ON_OBJECT &21af b0 f5 BCS &21a6 ; calculate_height_of_stack_loop &21b1 ca DEX &21b2 8e 6b 0c STX &0c6b ; height_of_stack &21b5 f0 3c BEQ &21f3 ; plot_objects_loop # Plot object if only one object in stack ; plot_objects_below_player_loop &21b7 a6 0b LDX &0b ; player_object &21b9 bd 00 0a LDA &0a00,X ; objects_z_fraction # Calculate the relative height of player to object &21bc 38 SEC &21bd f9 00 0a SBC &0a00,Y ; objects_z_fraction &21c0 85 74 STA &74 ; relative_z_fraction &21c2 bd 40 09 LDA &0940,X ; objects_z &21c5 f9 40 09 SBC &0940,Y ; objects_z &21c8 30 26 BMI &21f0 ; plot_objects_above_player # Plot rest of the stack if it is above the player &21ca 05 74 ORA &74 ; relative_z_fraction &21cc d0 07 BNE &21d5 ; object_is_above_player ; object_is_level_with_player &21ce b9 40 0a LDA &0a40,Y ; objects_type &21d1 c9 06 CMP #&06 ; TYPE_PLATFORM &21d3 f0 1b BEQ &21f0 ; plot_objects_above_player ; object_is_below_player &21d5 20 33 5d JSR &5d33 ; plot_object # Plot objects below the player individually first &21d8 ac 6c 0c LDY &0c6c ; top_object &21db ce 6b 0c DEC &0c6b ; height_of_stack # Don't plot these objects again later &21de 30 21 BMI &2201 ; leave &21e0 ae 6b 0c LDX &0c6b ; height_of_stack &21e3 10 07 BPL &21ec ; into_find_bottommost_unplotted_object_loop ; find_bottommost_unplotted_object_loop &21e5 b9 00 01 LDA &0100,Y ; objects_flags &21e8 29 3f AND #&3f ; OBJECT_UNDER_OBJECT_MASK &21ea a8 TAY &21eb ca DEX ; into_find_bottommost_unplotted_object_loop &21ec d0 f7 BNE &21e5 ; find_bottommost_unplotted_object_loop &21ee f0 c7 BEQ &21b7 ; plot_objects_below_player_loop # Always branches ; plot_objects_above_player &21f0 ac 6c 0c LDY &0c6c ; top_object # Plot objects from top down ; plot_objects_loop &21f3 20 33 5d JSR &5d33 ; plot_object &21f6 b9 00 01 LDA &0100,Y ; objects_flags &21f9 29 3f AND #&3f ; OBJECT_UNDER_OBJECT_MASK &21fb a8 TAY &21fc ce 6b 0c DEC &0c6b ; height_of_stack &21ff 10 f2 BPL &21f3 ; plot_objects_loop ; leave &2201 60 RTS ; plot_background &2202 85 26 STA &26 ; group # Called with A = first group &2204 84 57 STY &57 ; groups_to_plot # Y = height of area, in groups &2206 8a TXA # X = width of area, in eight byte columns &2207 c9 20 CMP #&20 &2209 90 06 BCC &2211 ; not_width_of_screen # If the area is more than 256 bytes (one page) wide, &220b e9 20 SBC #&20 # calculate how much is in the first page &220d a2 20 LDX #&20 &220f d0 02 BNE &2213 ; set_area_spans_page # Top bit clear to indicate area spans page ; not_width_of_screen &2211 a9 80 LDA #&80 # Top bit set to indicate area doesn't span page ; set_area_spans_page &2213 85 1e STA &1e ; area_spans_page &2215 86 25 STX &25 ; size_low # Number of eight byte columns to plot per group &2217 ae 4c 0c LDX &0c4c ; background_type &221a bd 77 22 LDA &2277,X ; odd_row_pixel_values &221d 85 74 STA &74 ; odd_row_pixel_value &221f bd 7b 22 LDA &227b,X ; even_row_pixel_values &2222 85 75 STA &75 ; even_row_pixel_value ; plot_background_group_loop &2224 a6 26 LDX &26 ; group &2226 bd 83 3d LDA &3d83,X ; buffer_group_addresses_low &2229 85 70 STA &70 ; screen_address_low &222b bd b5 3d LDA &3db5,X ; buffer_group_addresses_high &222e 85 71 STA &71 ; screen_address_high &2230 a6 25 LDX &25 ; size_low &2232 a9 01 LDA #&01 &2234 85 15 STA &15 ; size_high ; plot_background_page_loop &2236 a0 ff LDY #&ff ; plot_background_loop &2238 c8 INY # Set vertical group of eight bytes to be background &2239 a5 74 LDA &74 ; odd_row_pixel_value &223b 91 70 STA (&70),Y ; screen_address &223d c8 INY &223e a5 75 LDA &75 ; even_row_pixel_values &2240 91 70 STA (&70),Y ; screen_address &2242 c8 INY &2243 a5 74 LDA &74 ; odd_row_pixel_value &2245 91 70 STA (&70),Y ; screen_address &2247 c8 INY &2248 a5 75 LDA &75 ; even_row_pixel_values &224a 91 70 STA (&70),Y ; screen_address &224c c8 INY &224d a5 74 LDA &74 ; odd_row_pixel_value &224f 91 70 STA (&70),Y ; screen_address &2251 c8 INY &2252 a5 75 LDA &75 ; even_row_pixel_values &2254 91 70 STA (&70),Y ; screen_address &2256 c8 INY &2257 a5 74 LDA &74 ; odd_row_pixel_value &2259 91 70 STA (&70),Y ; screen_address &225b c8 INY &225c a5 75 LDA &75 ; even_row_pixel_values &225e 91 70 STA (&70),Y ; screen_address &2260 ca DEX &2261 d0 d5 BNE &2238 ; plot_background_loop &2263 c6 15 DEC &15 ; size_high &2265 30 09 BMI &2270 ; next_group &2267 a6 1e LDX &1e ; area_spans_page &2269 30 05 BMI &2270 ; next_group &226b e6 71 INC &71 ; screen_address_high # Continue in next page of memory &226d 4c 36 22 JMP &2236 ; plot_background_page_loop ; next_group &2270 e6 26 INC &26 ; group &2272 c6 57 DEC &57 ; groups_to_plot &2274 d0 ae BNE &2224 ; plot_background_group_loop &2276 60 RTS ; odd_row_pixel_values # Backgrounds: ; 0 1 2 3 # 0: alternating rows of blue and black &2277 00 00 aa 0f ; 0000 0000 3030 1111 # 1: solid blue ; even_row_pixel_values # 2: checkerboard colour 3 (unused) &227b 0f 00 55 0f ; 1111 0000 0303 1111 # 3: solid black ; colour_3_pixel_values ; 0 1 2 3 &227f 88 44 22 11 ; colour_pixel_values ; 0 1 2 3 colour &2283 00 0f f0 ff ; left_of_row_masks ; 0 1 2 3 leftmost pixels in colour 3 &2287 00 88 cc ee ; right_of_row_masks ; 3 2 1 0 rightmost pixels in colour 3 &228b 77 33 11 00 ; left_pixel_values ; 1 2 3 4 leftmost pixels in colour 3 &228f 88 cc ee ff ; buffer_address_adjustments_low &2293 00 60 00 ; buffer_address_adjustments_high &2296 00 00 00 ; fill_polygon &2299 a9 01 LDA #&01 # Non-zero to indicate polygon hasn't been clipped &229b 85 2c STA &2c ; polygon_extends_to_right &229d 85 2d STA &2d ; polygon_extends_to_left &229f a5 06 LDA &06 ; top_row_of_polygon # Calculate middle row of polygon &22a1 18 CLC &22a2 65 04 ADC &04 ; bottom_row_of_polygon &22a4 6a ROR A &22a5 aa TAX &22a6 bd 00 5b LDA &5b00,X ; polygon_right_edge_table # Leave if polygon's vertices run clockwise &22a9 dd 00 5a CMP &5a00,X ; polygon_left_edge_table # i.e. it faces away from the observer &22ac 90 5e BCC &230c ; leave ; calculate_initial_buffer_address &22ae a9 f0 LDA #&f0 # Calculate buffer address for first row of polygon &22b0 18 CLC &22b1 e5 06 SBC &06 ; top_row_of_polygon # Polygon rows are counted from bottom of screen &22b3 85 74 STA &74 ; tmp &22b5 4a LSR A &22b6 4a LSR A &22b7 4a LSR A &22b8 18 CLC &22b9 65 55 ADC &55 ; use_screen_or_buffer_for_plotting # &00 for screen, &19 for buffer &22bb aa TAX &22bc a5 74 LDA &74 ; tmp &22be 29 07 AND #&07 &22c0 18 CLC &22c1 7d 83 3d ADC &3d83,X ; buffer_group_addresses_low &22c4 85 72 STA &72 ; buffer_address_low &22c6 bd b5 3d LDA &3db5,X ; buffer_group_addresses_high &22c9 85 73 STA &73 ; buffer_address_high ; adjust_buffer_address &22cb a4 10 LDY &10 ; buffer_number &22cd a5 72 LDA &72 ; buffer_address_low &22cf 18 CLC &22d0 79 93 22 ADC &2293,Y ; buffer_address_adjustments_low # Offset BUFFER_WIDE_TWO by &60 bytes &22d3 85 72 STA &72 ; buffer_address_low &22d5 a5 73 LDA &73 ; buffer_address_high &22d7 79 96 22 ADC &2296,Y ; buffer_address_adjustments_high &22da 85 73 STA &73 ; buffer_address_high ; initialise_colours &22dc a5 19 LDA &19 ; polygon_colours &22de 2c 7a 0c BIT &0c7a ; suppress_lines # If top bit clear, plot edges in a different colour &22e1 10 0c BPL &22ef ; skip_line_suppression &22e3 29 cf AND #&cf &22e5 85 74 STA &74 ; tmp &22e7 a5 19 LDA &19 ; polygon_colours # Otherwise, use same colour for edge &22e9 0a ASL A &22ea 0a ASL A &22eb 29 30 AND #&30 &22ed 05 74 ORA &74 ; tmp ; skip_line_suppression &22ef 8d c7 23 STA &23c7 ; left_edge_pixel_value_offset_two &22f2 8d 67 23 STA &2367 ; left_edge_pixel_value_offset_one &22f5 09 40 ORA #&40 # &40 = right_edge_pixel_values - left_edge_pixel_values &22f7 8d a2 23 STA &23a2 ; right_edge_pixel_value_offset &22fa 4a LSR A # Fill colour is in bits &04 and &08 &22fb 4a LSR A &22fc 29 03 AND #&03 &22fe a8 TAY &22ff b9 83 22 LDA &2283,Y ; colour_pixel_values &2302 85 58 STA &58 ; fill_pixel_value # Pixel value to fill middle of polygon ; start_filling_polygon &2304 a4 06 LDY &06 ; top_row_of_polygon &2306 84 1a STY &1a ; row &2308 c4 04 CPY &04 ; bottom_row_of_polygon &230a b0 73 BCS &237f ; consider_filling_row ; leave &230c 60 RTS ; move_to_next_row &230d a4 1a LDY &1a ; row &230f c4 04 CPY &04 ; bottom_row_of_polygon &2311 f0 f9 BEQ &230c ; leave &2313 98 TYA &2314 29 07 AND #&07 # Is this row in the same group of eight rows? &2316 d0 62 BNE &237a ; consider_filling_next_row_in_group &2318 a5 72 LDA &72 ; buffer_address_low # If not, move the buffer address down a group &231a 18 CLC &231b 69 39 ADC #&39 &231d 85 72 STA &72 ; buffer_address_low &231f a5 73 LDA &73 ; buffer_address_high &2321 69 01 ADC #&01 &2323 c9 53 CMP #&53 &2325 d0 08 BNE &232f ; set_screen_address_high &2327 ad ac 3d LDA &3dac ; buffer_group_addresses_low + &29 # Wrap buffer around to start at &3fa0 &232a 85 72 STA &72 ; buffer_address_low &232c ad de 3d LDA &3dde ; buffer_group_addresses_high + &29 # (i.e. &3f00 + &a0) ; set_screen_address_high &232f 85 73 STA &73 ; buffer_address_high &2331 d0 49 BNE &237c ; consider_filling_next_row # Always branches ; set_polygon_extends_to_left &2333 a9 00 LDA #&00 # Set to zero to indicate right edge of polygon &2335 85 2d STA &2d ; polygon_extends_to_left # went beyond left edge of buffer &2337 f0 d4 BEQ &230d ; move_to_next_row # Always branches ; set_polygon_extends_to_right &2339 a9 00 LDA #&00 # Set to zero to indicate left edge of polygon &233b 85 2c STA &2c ; polygon_extends_to_right # went beyond right edge of buffer &233d f0 ce BEQ &230d ; move_to_next_row # Always branches ; plot_row_clipped_to_right &233f a5 61 LDA &61 ; right_edge_minus_left_edge_of_buffer # If the row is clipped to the right, &2341 0a ASL A &2342 85 56 STA &56 ; right_edge_byte_offset # set its length to end at the right edge of the buffer &2344 85 2c STA &2c ; polygon_extends_to_right # Bug: non-zero means polygon wasn't clipped (not used) &2346 d0 5e BNE &23a6 ; skip_right_edge # Always branches ; plot_row_clipped_to_left &2348 a5 72 LDA &72 ; buffer_address_low # If the row is clipped to the left, set the row &234a 38 SEC # to start two pixels left of buffer left edge &234b e9 08 SBC #&08 &234d 85 70 STA &70 ; row_screen_address_low &234f a5 73 LDA &73 ; buffer_address_high &2351 e9 00 SBC #&00 &2353 85 71 STA &71 ; row_screen_address_high &2355 a9 00 LDA #&00 # Set to zero to indicate polygon was clipped &2357 85 2d STA &2d ; polygon_extends_to_left &2359 a9 f8 LDA #&f8 # Left edge is two pixels left of buffer left edge &235b d0 7b BNE &23d8 ; plot_middle_of_row # Always branches ; plot_row_edges_in_same_byte &235d 8a TXA # Calculate which pixel of the byte the left edge is &235e 29 03 AND #&03 &2359 aa TAX &2361 a5 54 LDA &54 ; left_background_pixel_value # Value stored before right edge was plotted &2363 3d 87 22 AND &2287,X ; left_of_row_masks &2366 1d 00 3e ORA &3e00,X ; left_edge_pixel_values # Add the left edge, filling to the right of the byte # actually ORA &3e00 + left_edge_pixel_value_offset_one,X &2369 3d 8f 22 AND &228f,X ; left_pixel_values &236c 85 74 STA &74 ; left_fill_pixel_value &236e b1 72 LDA (&72),Y ; screen_address # Combine with previously plotted right edge &2370 3d 8b 22 AND &228b,X ; right_of_row_masks &2373 05 74 ORA &74 ; left_fill_pixel_value &2375 91 72 STA (&72),Y ; screen_address &2377 4c 0d 23 JMP &230d ; move_to_next_row ; consider_filling_next_row_in_group &237a e6 72 INC &72 ; buffer_address_low # Move down a row ; consider_filling_next_row &237c 88 DEY &237d 84 1a STY &1a ; row ; consider_filling_row &237f b9 00 5b LDA &5b00,Y ; polygon_right_edge_table # Is there anything to fill in this row? &2382 d9 00 5a CMP &5a00,Y ; polygon_left_edge_table &2385 90 85 BCC &230c ; leave # If not leave &2387 aa TAX &2388 e5 35 SBC &35 ; left_edge_of_buffer # Is the right edge left of the left of the buffer? &238a 90 a7 BCC &2333 ; set_polygon_extends_to_left # If so, note this, and consider next row &238c c5 61 CMP &61 ; right_edge_minus_left_edge_of_buffer # Is the right edge right of the right of the buffer? &238e b0 af BCS &233f ; plot_row_clipped_to_right # If so, plot a clipped row &2390 0a ASL A # Otherwise, calculate position of right edge &2391 29 f8 AND #&f8 &2393 a8 TAY ; plot_right_edge_of_row &2394 8a TXA # Calculate which pixel of the byte the right edge is &2395 29 03 AND #&03 &2397 aa TAX &2398 84 56 STY &56 ; right_edge_byte_offset &239a b1 72 LDA (&72),Y ; buffer_address # Store existing buffer value &239c 85 54 STA &54 ; left_background_pixel_value # in case the left edge is in the same byte &239e 3d 8b 22 AND &228b,X ; right_of_row_masks &23a1 1d 40 3e ORA &3e40,X ; right_edge_pixel_values # Plot the right edge, filling to the left of the byte # actually ORA &3e00 + right_edge_pixel_value_offset,X &23a4 91 72 STA (&72),Y ; buffer_address ; skip_right_edge &23a6 a4 1a LDY &1a ; row &23a8 b9 00 5a LDA &5a00,Y ; polygon_left_edge_table &23ab aa TAX &23ac c5 36 CMP &36 ; right_edge_of_buffer # Is the left edge right of the right of the buffer? &23ae b0 89 BCS &2339 ; set_polygon_extends_to_right # If so, note this, and consider next row &23b0 38 SEC &23b1 e5 35 SBC &35 ; left_edge_of_buffer # Is the left edge left of the left of the buffer? &23b3 90 93 BCC &2348 ; plot_row_clipped_to_left # If so, plot a clipped row &23b5 0a ASL A # Otherwise, calculate position of left edge &23b6 29 f8 AND #&f8 &23b8 a8 TAY &23b9 c4 56 CPY &56 ; right_edge_byte_offset # Is the left edge in the same byte as the right edge? &23bb b0 a0 BCS &235d ; plot_row_edges_in_same_byte # If so, combine them ; plot_left_edge_of_row &23bd 8a TXA # Calculate which pixel of the byte the left edge is &23be 29 03 AND #&03 &23c0 aa TAX &23c1 b1 72 LDA (&72),Y ; buffer_address &23c3 3d 87 22 AND &2287,X ; left_of_row_masks &23c6 1d 00 3e ORA &3e00,X ; left_edge_pixel_values # Plot the left edge, filling to the right of the byte # actually ORA &3e00 + left_edge_pixel_value_offset_two,X &23c9 91 72 STA (&72),Y ; buffer_address &23cb 98 TYA # Move right two pixels &23cc 18 CLC &23cd 65 72 ADC &72 ; buffer_address_low &23cf 85 70 STA &70 ; fill_address_low &23d1 a5 73 LDA &73 ; buffer_address_high &23d3 69 00 ADC #&00 &23d5 85 71 STA &71 ; fill_address_high &23d7 98 TYA ; plot_middle_of_row &23d8 38 SEC # Use length of middle &23d9 e5 56 SBC &56 ; right_edge_byte_offset &23db 4a LSR A &23dc 8d e3 23 STA &23e3 ; branch_offset # to set length of unrolled loop &23df a5 58 LDA &58 ; fill_pixel_value &23e1 18 CLC &23e2 90 0c BCC &23f0 # actually BCC &23e4 + branch_offset &23e4 a0 f8 LDY #&f8 # Unrolled loop fills middle of row &23e6 91 70 STA (&70),Y ; fill_address &23e8 a0 f0 LDY #&f0 &23ea 91 70 STA (&70),Y ; fill_address &23ec a0 e8 LDY #&e8 &23ee 91 70 STA (&70),Y ; fill_address &23f0 a0 e0 LDY #&e0 &23f2 91 70 STA (&70),Y ; fill_address &23f4 a0 d8 LDY #&d8 &23f6 91 70 STA (&70),Y ; fill_address &23f8 a0 d0 LDY #&d0 &23fa 91 70 STA (&70),Y ; fill_address &23fc a0 c8 LDY #&c8 &23fe 91 70 STA (&70),Y ; fill_address &2400 a0 c0 LDY #&c0 &2402 91 70 STA (&70),Y ; fill_address &2404 a0 b8 LDY #&b8 &2406 91 70 STA (&70),Y ; fill_address &2408 a0 b0 LDY #&b0 &240a 91 70 STA (&70),Y ; fill_address &240c a0 a8 LDY #&a8 &240e 91 70 STA (&70),Y ; fill_address &2410 a0 a0 LDY #&a0 &2412 91 70 STA (&70),Y ; fill_address &2414 a0 98 LDY #&98 &2416 91 70 STA (&70),Y ; fill_address &2418 a0 90 LDY #&90 &241a 91 70 STA (&70),Y ; fill_address &241c a0 88 LDY #&88 &241e 91 70 STA (&70),Y ; fill_address &2420 a0 80 LDY #&80 &2422 91 70 STA (&70),Y ; fill_address &2424 a0 78 LDY #&78 &2426 91 70 STA (&70),Y ; fill_address &2428 a0 70 LDY #&70 &242a 91 70 STA (&70),Y ; fill_address &242c a0 68 LDY #&68 &242e 91 70 STA (&70),Y ; fill_address &2430 a0 60 LDY #&60 &2432 91 70 STA (&70),Y ; fill_address &2434 a0 58 LDY #&58 &2436 91 70 STA (&70),Y ; fill_address &2438 a0 50 LDY #&50 &243a 91 70 STA (&70),Y ; fill_address &243c a0 48 LDY #&48 &243e 91 70 STA (&70),Y ; fill_address &2440 a0 40 LDY #&40 &2442 91 70 STA (&70),Y ; fill_address &2444 a0 38 LDY #&38 &2446 91 70 STA (&70),Y ; fill_address &2448 a0 30 LDY #&30 &244a 91 70 STA (&70),Y ; fill_address &244c a0 28 LDY #&28 &244e 91 70 STA (&70),Y ; fill_address &2450 a0 20 LDY #&20 &2452 91 70 STA (&70),Y ; fill_address &2454 a0 18 LDY #&18 &2456 91 70 STA (&70),Y ; fill_address &2458 a0 10 LDY #&10 &245a 91 70 STA (&70),Y ; fill_address &245c a0 08 LDY #&08 &245e 91 70 STA (&70),Y ; fill_address &2460 4c 0d 23 JMP &230d ; move_to_next_row ; populate_tile_visibility_bit_table &2463 ad de 0c LDA &0cde ; player_has_hyperspaced # Top bit set if player has just hyperspaced &2466 30 79 BMI &24e1 ; leave # Unnecessary check (already checked at &35e7) &2468 a9 00 LDA #&00 &246a 85 05 STA &05 ; table_offset &246c a2 7f LDX #&7f ; wipe_tile_visibility_bit_table_loop &246e 9d 80 3e STA &3e80,X ; tile_visibility_bit_table # Clear all bits to indicate tiles hidden by default &2471 ca DEX &2472 10 fa BPL &246e ; wipe_tile_visibility_bit_table_loop &2474 20 c3 25 JSR &25c3 ; populate_temporary_tile_z_table &2477 a9 1f LDA #&1f &2479 85 1a STA &1a ; tile_to_consider_y &247b 20 ea 24 JSR &24ea ; trace_rays_from_observer_to_row_of_tiles # Populate the table for the furthest row &247e c6 1a DEC &1a ; tile_to_consider_y ; populate_tile_visibility_y_loop # For each of the remaining rows, &2480 a5 05 LDA &05 ; table_offset &2482 49 20 EOR #&20 # Alternate between two rows of &2484 85 05 STA &05 ; table_offset # tile_raytrace_visibility_table &2486 20 ea 24 JSR &24ea ; trace_rays_from_observer_to_row_of_tiles &2489 a6 0b LDX &0b ; observer_object &248b bd 40 09 LDA &0940,X ; objects_z &248e 85 76 STA &76 ; observer_z &2490 a2 1e LDX #&1e # For each full tile in the row, ; populate_tile_visibility_x_loop &2492 8a TXA &2493 a8 TAY &2494 b1 70 LDA (&70),Y ; temporary_tile_z_table_address &2496 a0 ff LDY #&ff # Set all bits to indicate tile is visible &2498 4a LSR A &2499 b0 07 BCS &24a2 ; set_tile_is_not_hidden_because_of_height # Lowest bit of temporary table set if tile is not flat &249b c5 76 CMP &76 ; observer_z &249d 90 03 BCC &24a2 ; set_tile_is_not_hidden_because_of_height # Tile is visible if lower or equal to observer z &249f f0 01 BEQ &24a2 ; set_tile_is_not_hidden_because_of_height # If the tile is flat and higher than the observer, &24a1 c8 INY # clear all bits to indicate tile is hidden ; set_tile_is_not_hidden_because_of_height &24a2 84 77 STY &77 ; tile_is_not_hidden_because_of_height &24a4 8a TXA # Calculate offset into tile_raytrace_visibility_table &24a5 0a ASL A &24a6 0a ASL A &24a7 0a ASL A &24a8 29 e0 AND #&e0 &24aa 05 1a ORA &1a ; tile_to_consider_y &24ac 4a LSR A &24ad 85 74 STA &74 ; tile_visibility_bit_table_offset &24af 8a TXA &24b0 29 03 AND #&03 &24b2 2a ROL A &24b3 a8 TAY # and calculate bit within byte for tile &24b4 b9 e2 24 LDA &24e2,Y ; descending_bits_table &24b7 49 ff EOR #&ff &24b9 85 27 STA &27 ; bit_mask &24bb bd 80 01 LDA &0180,X ; tile_raytrace_visibility_table # Are any of this tile &24be 1d 81 01 ORA &0181,X ; tile_raytrace_visibility_table + 1 # or its three neighbours visible? &24c1 1d a0 01 ORA &01a0,X ; tile_raytrace_visibility_table + &20 # All bits set if any of them are &24c4 1d a1 01 ORA &01a1,X ; tile_raytrace_visibility_table + &21 &24c7 25 77 AND &77 ; tile_is_not_hidden_because_of_height # All bits set if height and slope don't hide tile &24c9 39 e2 24 AND &24e2,Y ; bits_to_set &24cc 85 75 STA &75 ; previous_table_value &24ce a4 74 LDY &74 ; tile_visibility_bit_table_offset &24d0 b9 80 3e LDA &3e80,Y ; tile_visibility_bit_table &24d3 25 27 AND &27 ; bit_mask &24d5 05 75 ORA &75 ; previous_table_value # Set or clear bit corresponding to this tile &24d7 99 80 3e STA &3e80,Y ; tile_visibility_bit_table &24da ca DEX &24db 10 b5 BPL &2492 ; populate_tile_visibility_x_loop &24dd c6 1a DEC &1a ; tile_to_consider_y &24df 10 9f BPL &2480 ; populate_tile_visibility_y_loop ; leave &24e1 60 RTS ; bits_to_set &24e2 80 40 20 10 08 04 02 01 ; trace_rays_from_observer_to_row_of_tiles &24ea a5 1a LDA &1a ; tile_to_consider_y &24ec 18 CLC &24ed 69 60 ADC #&60 # Use &6000 - &7f00 for temporary storage &24ef 85 71 STA &71 ; temporary_tile_z_table_address_high &24f1 a9 1f LDA #&1f &24f3 85 18 STA &18 ; tile_to_consider_x ; trace_rays_from_observer_to_row_of_tiles_loop # For each tile in the row, &24f5 20 5a 35 JSR &355a ; update_sound &24f8 a0 00 LDY #&00 &24fa 84 74 STY &74 ; maximum_distance &24fc 88 DEY &24fd 84 17 STY &17 ; steps_to_trace # Starts as &ff &24ff a4 18 LDY &18 ; tile_to_consider_x &2501 b1 70 LDA (&70),Y ; temporary_tile_z_table_address # Get minimum height of tile to consider &2503 4a LSR A # Discard lowest bit (set if tile isn't flat) &2504 85 19 STA &19 ; tile_to_consider_z &2506 a6 0b LDX &0b ; observer_object &2508 20 b5 1e JSR &1eb5 ; get_object_details # Put the observer's position in object variables &250b a2 02 LDX #&02 ; calculate_tile_relative_position_loop # For x, z and y (X = 0, 1 and 2 respectively): &250d a9 00 LDA #&00 &250f 95 86 STA &86,X ; signed_x # Calculate the tile's relative position to the observer &2511 38 SEC &2512 f5 37 SBC &37,X ; object_x_fraction &2514 95 2c STA &2c,X ; vector_x_fraction_fraction &2516 b5 18 LDA &18,X ; tile_to_consider_x &2518 f5 3a SBC &3a,X ; object_x &251a 95 2f STA &2f,X ; vector_x_fraction &251c 10 0b BPL &2529 ; skip_inversion &251e d6 86 DEC &86,X ; signed_x &2520 a9 00 LDA #&00 &2522 38 SEC &2523 f5 2c SBC &2c,X ; vector_x_fraction_fraction &2525 a9 00 LDA #&00 &2527 f5 2f SBC &2f,X ; vector_x_fraction ; skip_inversion &2529 c5 74 CMP &74 ; maximum_distance &252b 90 02 BCC &252f ; not_maximum &252d 85 74 STA &74 ; maximum_distance # Note the maximum distance along any direction ; not_maximum &252f ca DEX &2530 10 db BPL &250d ; calculate_tile_relative_position_loop &2532 a5 74 LDA &74 ; maximum_distance &2534 0a ASL A &2535 0a ASL A &2536 c9 06 CMP #&06 # If the tile is zero or one tiles away in any &2538 90 75 BCC &25af ; set_tile_as_visible_if_carry_clear # direction, then the tile is regarded as visible ; scale_vector_loop &253a 06 2c ASL &2c ; vector_x_fraction_fraction # Scale the vector between tile and observer &253c 26 2f ROL &2f ; vector_x_fraction # until there are approximately two to four &253e 06 2d ASL &2d ; vector_z_fraction_fraction # steps to trace for every tile in between &2540 26 30 ROL &30 ; vector_z_fraction &2542 06 2e ASL &2e ; vector_y_fraction_fraction &2544 26 31 ROL &31 ; vector_y_fraction &2546 46 17 LSR &17 ; steps_to_trace # Halve &2548 0a ASL A # Double &2549 90 ef BCC &253a ; scale_vector_loop &254b a5 3c LDA &3c ; object_y # Convert observer_tile_y into table address &254d 18 CLC &254e 69 60 ADC #&60 &2550 85 3c STA &3c ; object_y &2552 ad ce 0c LDA &0cce ; suppress_second_secret_code_check # Zero until after landscape preview &2555 30 27 BMI &257e ; skip_second_secret_code_check ; second_secret_code_check &2557 bd 56 0b LDA &0b56,X ; prnd_byte_after_generation - &ff # Calculate address where values stored in first stage: &255a 18 CLC # &255b 69 29 ADC #&29 # secret_code_validation_table &255d 85 0c STA &0c ; table_address_low # + ( prnd_byte_after_generation &255f a2 03 LDX #&03 # + &2a - 1 ) MOD 256 &2561 8a TXA # &2562 18 CLC # (values are stored in reverse order) &2563 69 3c ADC #&3c ; (secret_code_validation_table - 3) / &100 &2565 85 0d STA &0d ; table_address_high &2567 a0 00 LDY #&00 ; second_secret_code_check_loop &2569 b1 0c LDA (&0c),Y ; table_address # Check four table values corresponding to input_buffer &256b c9 7f CMP #&7f # Table value is (expected value - entered value) + &7f &256d d0 68 BNE &25d7 ; to_to_title_screen # Return to title screen if code is incorrect &256f c6 0c DEC &0c ; address_low &2571 ca DEX &2572 10 f5 BPL &2569 ; second_secret_code_check_loop &2574 b1 0c LDA (&0c),Y ; table_address # Check table value corresponding to &0cef. This is a &2576 c9 7f CMP #&7f # random BCD number - &80 + &7f, so can never be &7f. &2578 f0 5d BEQ &25d7 ; to_to_title_screen # Return to title screen if tampering attempted &257a 38 SEC &257b 6e ce 0c ROR &0cce ; suppress_second_secret_code_check # Set top bit to suppress checking again ; skip_second_secret_code_check ; trace_path_to_tile_loop &257e a2 02 LDX #&02 # Move the position to test a step along the vector &2580 18 CLC &2581 90 06 BCC &2589 ; add_vector_loop ; add_vector_including_fraction_fraction &2583 b5 34 LDA &34,X ; object_x_fraction_fraction # Only add fraction for y &2585 75 2c ADC &2c,X ; vector_x_fraction_fraction &2587 95 34 STA &34,X ; object_x_fraction_fraction ; add_vector_loop &2589 b5 37 LDA &37,X ; object_x_fraction &258b 75 2f ADC &2f,X ; vector_x_fraction &258d 95 37 STA &37,X ; object_x_fraction &258f b5 3a LDA &3a,X ; object_x &2591 75 86 ADC &86,X ; signed_x &2593 95 3a STA &3a,X ; object_x &2595 18 CLC &2596 ca DEX &2597 f0 f0 BEQ &2589 ; add_vector_including_fraction_fraction &2599 10 e8 BPL &2583 ; add_vector_loop &259b a5 3c LDA &3c ; object_y # Get the height of the tile at that position &259d 85 73 STA &73 ; temporary_tile_z_table_address_high # Use &6000 + &100 * object_y + object_x &259f a4 3a LDY &3a ; object_x &25a1 a5 3b LDA &3b ; object_z # Compare the height of the position to test &25a3 d1 72 CMP (&72),Y ; temporary_tile_z_table_address &25a5 90 07 BCC &25ae ; set_tile_as_hidden # If the intermediate tile is higher, target is hidden &25a7 c6 17 DEC &17 ; steps_to_trace &25a9 d0 d3 BNE &257e ; trace_path_to_tile_loop &25ab 18 CLC # If nothing obscured the target tile, mark it visible &25ac 90 01 BCC &25af ; set_tile_as_visible_if_carry_clear # Always branches ; set_tile_as_hidden &25ae 38 SEC ; set_tile_as_visible_if_carry_clear &25af a5 18 LDA &18 ; tile_to_consider_x &25b1 05 05 ORA &05 ; table_offset &25b3 a8 TAY &25b4 a9 00 LDA #&00 &25b6 e9 00 SBC #&00 # Set to &00 if carry set, target tile is obscured &25b8 99 80 01 STA &0180,Y ; tile_raytrace_visibility_table # Set to &ff if carry clear, target tile is visible &25bb c6 18 DEC &18 ; tile_to_consider_x &25bd 30 03 BMI &25c2 ; leave &25bf 4c f5 24 JMP &24f5 ; trace_rays_from_observer_to_row_of_tiles_loop ; leave &25c2 60 RTS ; populate_temporary_tile_z_table # Populate a table of tile heights, using screen memory &25c3 a9 00 LDA #&00 &25c5 85 70 STA &70 ; temporary_tile_z_table_address_low &25c7 85 60 STA &60 ; do_line_of_sight_checks # Clear top bit to skip checks in get_tile_z_from_object &25c9 a9 7f LDA #&7f ; &7f00 # Store row &1f tile heights in &7f00 - &7f1f &25cb 85 71 STA &71 ; temporary_tile_z_table_address_high &25cd a9 1f LDA #&1f &25cf 85 26 STA &26 ; tile_y ; populate_temporary_tile_z_table_y_loop &25d1 a9 1f LDA #&1f &25d3 85 24 STA &24 ; tile_x &25d5 d0 03 BNE &25da ; populate_temporary_tile_z_table_x_loop # Always branches ; to_to_title_screen &25d7 4c 63 36 JMP &3663 ; to_title_screen ; populate_temporary_tile_z_table_x_loop &25da 20 e6 1d JSR &1de6 ; calculate_tile_address_z_and_slope # Get height of tile, carry set if tile is not flat &25dd a4 24 LDY &24 ; tile_x &25df 2a ROL A # Store height and flatness &25e0 91 70 STA (&70),Y ; temporary_tile_z_table_address &25e2 c6 24 DEC &24 ; tile_x &25e4 10 f4 BPL &25da ; populate_temporary_tile_z_table_x_loop &25e6 c6 71 DEC &71 ; temporary_tile_z_table_address_low &25e8 c6 26 DEC &26 ; tile_y &25ea 10 e5 BPL &25d1 ; populate_temporary_tile_z_table_y_loop ; populate_temporary_tile_maximum_z_table # Populate a second, unused table, of maximum heights &25ec a9 20 LDA #&20 &25ee 85 72 STA &72 ; temporary_tile_maximum_z_table_address_low &25f0 a2 1e LDX #&1e # Store row &1e maximum tile heights in &7e20 - &7e3f ; populate_temporary_tile_maximum_z_table_y_loop &25f2 8a TXA ; tile_y &25f3 18 CLC &25f4 69 60 ADC #&60 &25f6 85 71 STA &71 ; temporary_tile_z_table_address_high &25f8 85 73 STA &73 ; temporary_tile_maximum_z_table_address_high &25fa a0 1e LDY #&1e ; tile_x ; populate_temporary_tile_maximum_z_table_x_loop &25fc b1 70 LDA (&70),Y ; temporary_tile_z_table_address &25fe 4a LSR A # Carry clear if tile is flat &25ff 90 1a BCC &261b ; set_temporary_tile_maximum_z # If so, use height of tile as maximum height &2601 2a ROL A &2602 c8 INY # Otherwise, use maximum of four corner points &2603 d1 70 CMP (&70),Y ; temporary_tile_z_table_address &2605 90 02 BCC &2609 ; not_maximum_one &2607 b1 70 LDA (&70),Y ; temporary_tile_z_table_address ; not_maximum_one &2609 e6 71 INC &71 ; temporary_tile_z_table_address_high &260b d1 70 CMP (&70),Y ; temporary_tile_z_table_address &260d 90 02 BCC &2611 ; not_maximum_two &260f b1 70 LDA (&70),Y ; temporary_tile_z_table_address ; not_maximum_two &2611 88 DEY &2612 d1 70 CMP (&70),Y ; temporary_tile_z_table_address &2614 90 02 BCC &2618 ; not_maximum_three &2616 b1 70 LDA (&70),Y ; temporary_tile_z_table_address ; not_maximum_three &2618 c6 71 DEC &71 ; temporary_tile_z_table_address_high &261a 4a LSR A ; set_temporary_tile_maximum_z &261b 91 72 STA (&72),Y ; temporary_tile_maximum_z_table_address &261d 88 DEY &261e 10 dc BPL &25fc ; populate_temporary_tile_maximum_z_table_x_loop &2620 ca DEX &2621 10 cf BPL &25f2 ; populate_temporary_tile_maximum_z_table_y_loop &2623 60 RTS ; plot_world &2624 a6 6e LDX &6e ; object_to_consider # Observer &2626 a9 40 LDA #&40 &2628 85 3c STA &3c ; polygon_vertex_data_address_low &262a a9 0c LDA #&0c ; &0c40 = temporary_vertex_data &262c 85 3d STA &3d ; polygon_vertex_data_address_high &262e bd c0 09 LDA &09c0,X ; objects_h_angle &2631 18 CLC &2632 69 20 ADC #&20 # Add 45 degrees &2634 85 1c STA &1c ; view_angle &2636 29 3f AND #&3f # Limit to quadrant &2638 38 SEC &2639 e9 20 SBC #&20 # Subtract 45 degrees &263b 85 74 STA &74 ; view_angle_from_quadrant &263d a5 1c LDA &1c ; view_angle &263f 0a ASL A # Move top two bits into bottom two bits &2640 2a ROL A # to get quadrant for plotting direction &2641 2a ROL A &2642 29 03 AND #&03 &2644 a8 TAY &2645 b9 ab 27 LDA &27ab,Y ; offset_to_tile_table &2648 85 1b STA &1b ; offset_to_tile &264a 98 TYA &264b 0a ASL A &264c 0a ASL A &264d 85 66 STA &66 ; view_quadrant_times_four &264f 98 TYA &2650 38 SEC &2651 e9 02 SBC #&02 &2653 85 4b STA &4b ; view_quadrant_minus_two &2655 a5 74 LDA &74 ; view_angle_from_quadrant &2657 38 SEC &2658 e9 0a SBC #&0a # Subtract half a screen width (10 * 256/360 degrees) &265a 85 20 STA &20 ; view_angle_from_quadrant_minus_half_screen &265c 24 1c BIT &1c ; view_angle # Consider where the observer is predominantly looking &265e 30 1f BMI &267f ; looking_south_or_west &2660 70 0d BVS &266f ; looking_east ; looking_north &2662 bd 00 09 LDA &0900,X ; objects_x # Plot rows from x- to x+, starting at y- &2665 85 03 STA &03 ; observer_tile_column &2667 bd 80 09 LDA &0980,X ; objects_y &266a 85 1d STA &1d ; observer_tile_row &266c 4c a1 26 JMP &26a1 ; plot_rows ; looking_east &266f 18 CLC # Plot rows from y+ to y-, starting at x- &2670 a9 1f LDA #&1f &2672 fd 80 09 SBC &0980,X ; objects_y &2675 85 03 STA &03 ; observer_tile_column &2677 bd 00 09 LDA &0900,X ; objects_x &267a 85 1d STA &1d ; observer_tile_row &267c 4c a1 26 JMP &26a1 ; plot_rows ; looking_south_or_west &267f 70 13 BVS &2694 ; looking_west ; looking_south &2681 18 CLC # Plot rows from x+ to x-, starting at y+ &2682 a9 1f LDA #&1f &2684 fd 00 09 SBC &0900,X ; objects_x &2687 85 03 STA &03 ; observer_tile_column &2689 18 CLC &268a a9 1f LDA #&1f &268c fd 80 09 SBC &0980,X ; objects_y &268f 85 1d STA &1d ; observer_tile_row &2691 4c a1 26 JMP &26a1 ; plot_rows ; looking_west &2694 bd 80 09 LDA &0980,X ; objects_y # Plot rows from y- to y+, starting at x+ &2697 85 03 STA &03 ; observer_tile_column &2699 18 CLC &269a a9 1f LDA #&1f &269c fd 00 09 SBC &0900,X ; objects_x &269f 85 1d STA &1d ; observer_tile_row ; plot_rows # Plot tiles in rows from furthest to nearest &26a1 a9 1f LDA #&1f &26a3 85 26 STA &26 ; tile_row &26a5 ad 48 0c LDA &0c48 ; previous_first_tile_to_consider_in_row &26a8 85 32 STA &32 ; first_tile_to_consider_in_row &26aa a9 00 LDA #&00 ; PLOTTABLES_EVEN_TILES &26ac 85 05 STA &05 ; plottables_type &26ae 20 af 27 JSR &27af ; find_visible_extent_of_row_of_tiles # Find which tiles are on screen in furthest row &26b1 a5 32 LDA &32 ; first_tile_to_consider_in_row &26b3 8d 48 0c STA &0c48 ; previous_first_tile_to_consider_in_row ; plot_rows_in_front_of_observer_loop &26b6 a5 05 LDA &05 ; plottables_type &26b8 49 20 EOR #&20 ; PLOTTABLES_ODD_TILES &26ba 85 05 STA &05 ; plottables_type &26bc a5 32 LDA &32 ; first_tile_to_consider_in_row &26be 85 37 STA &37 ; first_tile_to_plot_in_row &26c0 a5 33 LDA &33 ; last_tile_to_consider_in_row &26c2 85 38 STA &38 ; last_tile_column_to_plot_plus_one &26c4 20 5a 35 JSR &355a ; update_sound &26c7 c6 26 DEC &26 ; tile_row &26c9 30 09 BMI &26d4 ; leave_with_carry_clear # Is this the last row? If so, world is fully plotted &26cb a4 26 LDY &26 ; tile_row &26cd c4 1d CPY &1d ; observer_tile_row &26cf d0 05 BNE &26d6 ; move_to_nearer_row &26d1 4c 47 27 JMP &2747 ; consider_plotting_observer_row ; leave_with_carry_clear &26d4 18 CLC # Leave with carry clear to indicate world fully plotted &26d5 60 RTS ; move_to_nearer_row # For each row in front of the observer, &26d6 20 af 27 JSR &27af ; find_visible_extent_of_row_of_tiles # Find which tiles are on screen &26d9 a4 32 LDY &32 ; first_tile_to_consider_in_row # Compare the start of this row &26db c4 37 CPY &37 ; first_tile_to_plot_in_row # with the start of the previous row &26dd f0 28 BEQ &2707 ; consider_end_of_row &26df 90 0a BCC &26eb ; this_row_starts_before &26e1 88 DEY # For this row, ; calculate_this_row_new_first_tiles_coordinates_loop # calculate co-ordinates for any extra tiles at start &26e2 20 15 28 JSR &2815 ; check_if_tile_is_on_screen_and_calculate_screen_coordinates &26e5 c4 37 CPY &37 ; first_tile_to_plot_in_row &26e7 d0 f8 BNE &26e1 ; calculate_this_row_new_first_tiles_coordinates_loop &26e9 f0 1c BEQ &2707 ; consider_end_of_row # Always branches ; this_row_starts_before &26eb a5 05 LDA &05 ; plottables_type &26ed 49 20 EOR #&20 ; PLOTTABLES_ODD_TILES &26ef 85 05 STA &05 ; plottables_type &26f1 e6 26 INC &26 ; tile_row &26f3 a4 37 LDY &37 ; first_tile_to_plot_in_row ; calculate_previous_row_new_first_tiles_coordinates_loop # For the previous row, &26f5 88 DEY # Calculate co-ordinates for any extra tiles at start &26f6 20 15 28 JSR &2815 ; check_if_tile_is_on_screen_and_calculate_screen_coordinates &26f9 c4 32 CPY &32 ; first_tile_to_consider_in_row &26fb d0 f8 BNE &26f5 ; calculate_previous_row_new_first_tiles_coordinates_loop &26fd 84 37 STY &37 ; first_tile_to_plot_in_row &26ff c6 26 DEC &26 ; tile_row &2701 a5 05 LDA &05 ; plottables_type &2703 49 20 EOR #&20 ; PLOTTABLES_ODD_TILES &2705 85 05 STA &05 ; plottables_type ; consider_end_of_row &2707 a4 33 LDY &33 ; last_tile_to_consider_in_row # Compare the end of this row &2709 c4 38 CPY &38 ; last_tile_column_to_plot_plus_one # with the end of the previous row &270b f0 28 BEQ &2735 ; plot_row &270d b0 0a BCS &2719 ; this_row_ends_after ; calculate_this_row_new_last_tiles_coordinates_loop # For this row, &270f c8 INY # calculate co-ordinates for any extra tiles at end &2710 20 15 28 JSR &2815 ; check_if_tile_is_on_screen_and_calculate_screen_coordinates &2713 c4 38 CPY &38 ; last_tile_to_plot_in_row_plus_one &2715 d0 f8 BNE &270f ; calculate_this_row_new_last_tiles_coordinates_loop &2717 f0 1c BEQ &2735 ; plot_row ; this_row_ends_after &2719 a5 05 LDA &05 ; plottables_type &271b 49 20 EOR #&20 ; PLOTTABLES_ODD_TILES &271d 85 05 STA &05 ; plottables_type &271f e6 26 INC &26 ; tile_row &2721 a4 38 LDY &38 ; last_tile_to_plot_in_row_plus_one ; calculate_previous_row_new_last_tiles_coordinates_loop # For previous row, &2723 c8 INY # calculate co-ordinates for any extra tiles at end &2724 20 15 28 JSR &2815 ; check_if_tile_is_on_screen_and_calculate_screen_coordinates &2727 c4 33 CPY &33 ; last_tile_to_consider_in_row &2729 d0 f8 BNE &2723 ; calculate_previous_row_new_last_tiles_coordinates_loop &272b 84 38 STY &38 ; last_tile_to_plot_in_row_plus_one &272d c6 26 DEC &26 ; tile_row &272f a5 05 LDA &05 ; plottables_type &2731 49 20 EOR #&20 ; PLOTTABLES_ODD_TILES &2733 85 05 STA &05 ; plottables_type ; plot_row &2735 20 2d 29 JSR &292d ; plot_row_of_tiles_or_block # Plot row &2738 2c 1b 0c BIT &0c1b ; force_pan_when_player_moves # Top bit set if sights not active &273b 10 05 BPL &2742 ; to_plot_rows_loop &273d 20 0f 12 JSR &120f ; check_if_observer_still_wants_to_pan # Returns equal set if observer still wants to pan &2740 d0 03 BNE &2745 ; leave_with_carry_set # abort plotting if observer has stopped wanting to pan ; to_plot_rows_in_front_of_observer_loop &2742 4c b6 26 JMP &26b6 ; plot_rows_in_front_of_observer_loop ; leave_with_carry_set &2745 38 SEC # Leave with carry set if world not fully plotted &2746 60 RTS ; consider_plotting_observer_row # tile_row is observer_tile_row here &2747 a4 37 LDY &37 ; first_tile_to_plot_in_row &2749 c8 INY &274a c4 03 CPY &03 ; observer_tile_column # Does the row start next to the observer? &274c d0 05 BNE &2753 ; check_if_observer_row_is_visible &274e 84 38 STY &38 ; last_tile_to_plot_in_row_plus_one &2750 4c 5e 27 JMP &275e ; plot_observer_row # If so, plot it ; check_if_observer_row_is_visible &2753 a4 38 LDY &38 ; last_tile_to_plot_in_row_plus_one # If not, &2755 88 DEY &2756 88 DEY &2757 c4 03 CPY &03 ; observer_tile_column # Does the row end next to the observer? &2759 d0 10 BNE &276b ; skip_plotting_observer_row # If not, don't plot it &275b c8 INY &275c 84 37 STY &37 ; first_tile_to_plot_in_row ; plot_observer_row &275e a4 37 LDY &37 ; first_tile_to_plot_in_row # Calculate co-ordinates for tiles to sides of observer &2760 20 15 28 JSR &2815 ; check_if_tile_is_on_screen_and_calculate_screen_coordinates &2763 a4 38 LDY &38 ; last_tile_to_plot_in_row_plus_one &2765 20 15 28 JSR &2815 ; check_if_tile_is_on_screen_and_calculate_screen_coordinates &2768 20 2d 29 JSR &292d ; plot_row_of_tiles_or_block # Plot tiles to sides of observer ; skip_plotting_observer_row &276b a9 00 LDA #&00 ; PLOTTABLES_EVEN_TILES &276d 85 05 STA &05 ; plottables_type &276f e6 26 INC &26 ; tile_row &2771 a4 03 LDY &03 ; observer_tile_column # Consider the tile the observer is on &2773 20 15 28 JSR &2815 ; check_if_tile_is_on_screen_and_calculate_screen_coordinates &2776 b9 e0 0a LDA &0ae0,Y ; plottables_screen_y_high &2779 c9 02 CMP #&02 &277b b0 2c BCS &27a9 ; leave_with_with_carry_clear # Leave if observer tile is not on screen &277d 99 e1 0a STA &0ae1,Y ; plottables_screen_y_high + 1 # Make top of tile horizontal &2780 b9 80 0a LDA &0a80,Y ; plottables_screen_y_low &2783 99 81 0a STA &0a81,Y ; plottables_screen_y_low + 1 &2786 a9 20 LDA #&20 ; PLOTTABLES_ODD_TILES &2788 85 05 STA &05 ; plottables_type &278a c6 26 DEC &26 ; tile_row &278c a9 ff LDA #&ff # Put bottom of tile below bottom of screen &278e 99 00 0b STA &0b00,Y ; plottables_screen_y_high + &20 &2791 99 01 0b STA &0b01,Y ; plottables_screen_y_high + &21 &2794 99 20 55 STA &5520,Y ; plottables_relative_h_angle_high + &20 # Put bottom left of tile left of left of screen &2797 99 00 55 STA &5500,Y ; plottables_relative_h_angle_high # Put top left of tile left of left of screen &279a a9 14 LDA #&14 # One screen width (20 * 256/360 degrees) &279c 99 21 55 STA &5521,Y ; plottables_relative_h_angle_high + &21 # Put bottom right of tile right of right of screen &279f 99 01 55 STA &5501,Y ; plottables_relative_h_angle_high + 1 # Put top right of tile right of right of screen &27a2 a5 03 LDA &03 ; observer_tile_column &27a4 85 25 STA &25 ; plottables_offset &27a6 20 1b 2a JSR &2a1b ; plot_checkerboard_tile # Plot the tile containing the observer ; leave_with_with_carry_clear &27a9 18 CLC # Leave with carry clear to indicate world fully plotted &27aa 60 RTS ; offset_to_tile_table &27ab 00 01 21 20 ; find_visible_extent_of_row_of_tiles &27af a4 32 LDY &32 ; first_tile_to_consider_in_row &27b1 20 15 28 JSR &2815 ; check_if_tile_is_on_screen_and_calculate_screen_coordinates &27b4 f0 33 BEQ &27e9 ; find_first_visible_tile_at_start_loop # &00 if tile is completely off screen &27b6 c9 80 CMP #&80 # &80 if only left edge of tile is on screen &27b8 f0 1d BEQ &27d7 ; tile_is_cropped_to_right ; find_end_of_row_loop &27ba a5 24 LDA &24 ; tile_column &27bc 85 32 STA &32 ; first_tile_to_consider_in_row &27be 20 0e 28 JSR &280e ; increase_tile_column_and_check_again &27c1 b0 0f BCS &27d2 ; reached_end_of_row &27c3 c9 81 CMP #&81 # &81 if tile is completely on screen &27c5 f0 f3 BEQ &27ba ; find_end_of_row_loop &27c7 c9 80 CMP #&80 # &80 if only left edge of tile is on screen &27c9 f0 07 BEQ &27d2 ; reached_end_of_row # If so, this is the last visible tile in the row ; find_first_visible_tile_at_end_loop &27cb 20 0e 28 JSR &280e ; increase_tile_column_and_check_again &27ce b0 02 BCS &27d2 ; reached_end_of_row # Carry set if no more tiles in row &27d0 f0 f9 BEQ &27cb ; find_first_visible_tile_at_end_loop # &00 if tile is completely off screen ; reached_end_of_row &27d2 a5 24 LDA &24 ; tile_column &27d4 85 33 STA &33 ; last_tile_to_consider_in_row &27d6 60 RTS ; tile_is_cropped_to_right &27d7 a5 24 LDA &24 ; tile_column &27d9 85 33 STA &33 ; last_tile_to_consider_in_row &27db 20 06 28 JSR &2806 ; decrease_tile_column_and_check_again &27de b0 1f BCS &27ff ; reached_start_of_row &27e0 c9 80 CMP #&80 # &80 if right edge of tile is off screen &27e2 f0 f3 BEQ &27d7 ; tile_is_cropped_to_right &27e4 c9 00 CMP #&00 # &00 if tile is completely off screen &27e6 4c fd 27 JMP &27fd ; into_find_first_visible_tile_at_start_of_row_loop ; find_first_visible_tile_at_start_loop &27e9 20 0e 28 JSR &280e ; increase_tile_column_and_check_again &27ec b0 02 BCS &27f0 ; reached_end_of_row # Carry set if no more tiles in row &27ee f0 f9 BEQ &27e9 ; find_first_visible_tile_at_start_loop # &00 if tile is completely off screen ; reached_end_of_row &27f0 a5 24 LDA &24 ; tile_column &27f2 85 33 STA &33 ; last_tile_to_consider_in_row &27f4 a5 32 LDA &32 ; first_tile_to_consider_in_row &27f6 85 24 STA &24 ; tile_column ; find_first_visible_tile_at_start_of_row_loop &27f8 20 06 28 JSR &2806 ; decrease_tile_column_and_check_again &27fb b0 02 BCS &27ff ; reached_start_of_row # Carry set if no more tiles in row ; into_find_first_visible_tile_at_start_of_row_loop &27fd f0 f9 BEQ &27f8 ; find_first_visible_tile_at_start_of_row_loop ; reached_start_of_row &27ff a5 24 LDA &24 ; tile_column &2801 85 32 STA &32 ; first_tile_to_consider_in_row &2803 60 RTS ; leave_with_carry_set &2804 38 SEC # Leave with carry set to indicate end of row reached &2805 60 RTS ; decrease_tile_column_and_check_again &2806 a4 24 LDY &24 ; tile_column &2808 f0 fa BEQ &2804 ; leave_with_carry_set # Stop at the start of the row &280a 88 DEY # Otherwise check if previous tile in row is visible &280b 4c 15 28 JMP &2815 ; check_if_tile_is_on_screen_and_calculate_screen_coordinates ; increase_tile_column_and_check_again &280e a4 24 LDY &24 ; tile_column &2810 c8 INY &2811 c0 20 CPY #&20 # Stop at the end of the row &2813 f0 ef BEQ &2804 ; leave_with_carry_set # Otherwise check if next tile in row is visible ; check_if_tile_is_on_screen_and_calculate_screen_coordinates &2815 84 24 STY &24 ; tile_column &2817 84 0f STY &0f ; tmp_y # Preserve Y on exit &2819 98 TYA &281a 05 05 ORA &05 ; plottables_type &281c 85 21 STA &21 ; tile_plottables_offset &281e a9 00 LDA #&00 &2820 85 7f STA &7f ; result &2822 a6 6e LDX &6e ; object_to_consider # Observer &2824 a9 80 LDA #&80 &2826 85 80 STA &80 ; x_fraction &2828 18 CLC &2829 a5 24 LDA &24 ; tile_column # Calculate tile column relative to observer, into x &282b e5 03 SBC &03 ; observer_tile_column &282d 38 SEC &282e ed 78 0c SBC &0c78 ; global_objects_x_offset # Non-zero only for title screen text &2831 85 86 STA &86 ; signed_x &2833 10 0b BPL &2840 ; skip_inversion &2835 a9 00 LDA #&00 &2837 38 SEC &2838 e5 80 SBC &80 ; x_fraction &283a 85 80 STA &80 ; x_fraction &283c a9 00 LDA #&00 &283e e5 86 SBC &86 ; signed_x ; skip_inversion &2840 85 83 STA &83 ; x &2842 a9 80 LDA #&80 &2844 85 82 STA &82 ; y_fraction &2846 18 CLC &2847 a5 26 LDA &26 ; tile_row # Calculate tile row relative to observer, into y &2849 e5 1d SBC &1d ; observer_tile_row &284b 85 88 STA &88 ; signed_y &284d 10 0b BPL &285a ; skip_inversion &284f a9 00 LDA #&00 &2851 38 SEC &2852 e5 82 SBC &82 ; y_fraction &2854 85 82 STA &82 ; y_fraction &2856 a9 00 LDA #&00 &2858 e5 88 SBC &88 ; signed_y ; skip_inversion &285a 85 85 STA &85 ; y &285c 20 67 55 JSR &5567 ; calculate_angle # Calculate angle between x and y, &285f a4 21 LDY &21 ; tile_plottables_offset # i.e. horizontal angle between tile and observer &2861 a5 8a LDA &8a ; angle_low &2863 38 SEC &2864 e5 1f SBC &1f ; angle_low_adjustment &2866 99 a0 0b STA &0ba0,Y ; plottables_relative_h_angle_low # Store relative horizontal angle &2869 a5 8b LDA &8b ; angle_high &286b e5 20 SBC &20 ; view_angle_from_quadrant_minus_half_screen # to quadrant &286d 99 00 55 STA &5500,Y ; plottables_relative_h_angle_high &2870 20 5f 56 JSR &565f ; calculate_hypotenuse # Calculate distance from observer to tile in plane &2873 24 1c BIT &1c ; view_angle # Convert (column, row) into (tile_x, tile_y) &2875 30 14 BMI &288b ; looking_south_or_west # depending on direction of view &2877 70 07 BVS &2880 ; looking_east ; looking_north &2879 a6 24 LDX &24 ; tile_column &287b a4 26 LDY &26 ; tile_row &287d 4c a4 28 JMP &28a4 ; calculate_tile_address ; looking_east &2880 a6 26 LDX &26 ; tile_row &2882 a9 1f LDA #&1f &2884 38 SEC &2885 e5 24 SBC &24 ; tile_column &2887 a8 TAY &2888 4c a4 28 JMP &28a4 ; calculate_tile_address ; looking_south_or_west &288b 70 0f BVS &289c ; looking_west ; looking_south &288d a9 1f LDA #&1f &288f 38 SEC &2890 e5 24 SBC &24 ; tile_column &2892 aa TAX &2893 a9 1f LDA #&1f &2895 38 SEC &2896 e5 26 SBC &26 ; tile_row &2898 a8 TAY &2899 4c a4 28 JMP &28a4 ; calculate_tile_address ; looking_west &289c a9 1f LDA #&1f &289e 38 SEC &289f e5 26 SBC &26 ; tile_row &28a1 aa TAX &28a2 a4 24 LDY &24 ; tile_column ; calculate_tile_address &28a4 84 74 STY &74 ; tmp # Calculate tile address from tile co-ordinates &28a6 8a TXA &28a7 0a ASL A &28a8 0a ASL A &28a9 0a ASL A &28aa 29 e0 AND #&e0 &28ac 05 74 ORA &74 ; tmp &28ae a8 TAY &28af 8a TXA &28b0 29 03 AND #&03 &28b2 85 74 STA &74 ; offset &28b4 18 CLC &28b5 69 04 ADC #&04 &28b7 85 5f STA &5f ; tile_address_high &28b9 b1 5e LDA (&5e),Y ; tile_address # Consider the contents of the tile &28bb a6 21 LDX &21 ; tile_plottables_offset &28bd 9d 80 01 STA &0180,X ; plottables_data # Store for use when plotting &28c0 c9 c0 CMP #&c0 ; TILE_HAS_OBJECT &28c2 90 12 BCC &28d6 ; no_object_in_tile # Is there an object in the tile? ; find_bottommost_object &28c4 29 3f AND #&3f ; OBJECT_UNDER_OBJECT_MASK &28c6 a8 TAY &28c7 b9 00 01 LDA &0100,Y ; objects_flags &28ca c9 40 CMP #&40 ; OBJECT_ON_OBJECT &28cc b0 f6 BCS &28c4 ; find_bottommost_object &28ce b9 40 09 LDA &0940,Y ; objects_z # If so, find the height of the tile itself &28d1 85 75 STA &75 ; tile_or_object_z &28d3 4c ee 28 JMP &28ee ; tile_is_not_hidden # and regard the tile as not hidden ; no_object_in_tile &28d6 4a LSR A &28d7 4a LSR A &28d8 4a LSR A &28d9 4a LSR A &28da 85 75 STA &75 ; tile_or_object_z # Otherwise, use the height of tile &28dc 98 TYA &28dd 4a LSR A &28de a8 TAY &28df 26 74 ROL &74 ; offset &28e1 b9 80 3e LDA &3e80,Y ; tile_visibility_bit_table &28e4 a4 74 LDY &74 ; offset &28e6 39 e2 24 AND &24e2,Y ; descending_bits_table # and check if the tile isn't hidden &28e9 d0 03 BNE &28ee ; tile_is_not_hidden &28eb 9d 80 01 STA &0180,X ; plottables_data # Set to zero to indicate tile is hidden ; tile_is_not_hidden &28ee a6 6e LDX &6e ; object_to_consider # Observer &28f0 a9 00 LDA #&00 # Calculate height of tile relative to observer &28f2 38 SEC &28f3 fd 00 0a SBC &0a00,X ; objects_z_fraction &28f6 85 80 STA &80 ; x_fraction &28f8 a5 75 LDA &75 ; tile_or_object_z &28fa fd 40 09 SBC &0940,X ; objects_z &28fd 20 1d 56 JSR &561d ; calculate_object_relative_vertical_angle # Calculate vertical angle between tile and observer &2900 a4 21 LDY &21 ; tile_plottables_offset &2902 a5 8d LDA &8d ; object_relative_v_angle_high &2904 99 e0 0a STA &0ae0,Y ; plottables_screen_y_high # Store relative vertical angle &2907 a5 50 LDA &50 ; object_relative_v_angle_low &2909 99 80 0a STA &0a80,Y ; plottables_screen_y_low &290c b9 00 55 LDA &5500,Y ; plottables_relative_h_angle_high &290f c5 07 CMP &07 ; buffer_minimum_angle_high &2911 90 14 BCC &2927 ; leave_with_result # Leave with zero if tile is beyond left of buffer &2913 d0 0a BNE &291f ; skip_fraction_check &2915 b9 a0 0b LDA &0ba0,Y ; plottables_relative_h_angle_low &2918 c5 28 CMP &28 ; buffer_minimum_angle_low &291a 90 0b BCC &2927 ; leave_with_result &291c b9 00 55 LDA &5500,Y ; plottables_relative_h_angle_high ; skip_fraction_check &291f 66 7f ROR &7f ; result # Set &80 if left edge of tile is on screen &2921 c5 12 CMP &12 ; buffer_maximum_angle_high &2923 90 02 BCC &2927 ; leave_with_stuff # Leave if tile is beyond right of buffer &2925 e6 7f INC &7f ; result # Set &01 if right edge of tile is on screen ; leave_with_result &2927 a4 0f LDY &0f ; tmp_y &2929 a5 7f LDA &7f ; result # Leave with on-screen result in A &292b 18 CLC # Leave with carry clear to indicate not at end of row &292c 60 RTS ; plot_row_of_tiles_or_block &292d a5 37 LDA &37 ; first_tile_to_plot_in_row &292f 85 25 STA &25 ; plottables_offset ; plot_start_of_row_loop # Plot tiles in row from one end until observer &2931 c5 38 CMP &38 ; last_tile_to_plot_in_row_plus_one &2933 b0 27 BCS &295c ; leave &2935 c5 03 CMP &03 ; observer_tile_column &2937 b0 0a BCS &2943 ; plot_end_of_row &2939 20 e2 29 JSR &29e2 ; plot_tile_or_block &293c e6 25 INC &25 ; plottables_offset &293e a5 25 LDA &25 ; plottables_offset &2940 4c 31 29 JMP &2931 ; plot_start_of_row_loop ; plot_end_of_row # Plot tiles from other end until observer &2943 a5 38 LDA &38 ; last_tile_to_plot_in_row_plus_one ; plot_end_of_row_end &2945 38 SEC &2946 e9 01 SBC #&01 &2948 30 12 BMI &295c ; leave &294a 85 25 STA &25 ; plottables_offset &294c c5 37 CMP &37 ; first_tile_to_plot_in_row &294e 90 0c BCC &295c ; leave &2950 c5 03 CMP &03 ; observer_tile_column &2952 90 08 BCC &295c ; leave &2954 20 e2 29 JSR &29e2 ; plot_tile_or_block &2957 a5 25 LDA &25 ; plottables_offset &2959 4c 45 29 JMP &2945 ; plot_end_of_row_end ; leave &295c 60 RTS ; initialise_buffer_variables_for_other_vertical_buffer &295d a5 10 LDA &10 ; buffer_number &295f 29 01 AND #&01 # Ensure vertical buffer &2961 49 01 EOR #&01 ; BUFFER_WIDE_TWO # Use other vertical buffer ; initialise_buffer_variables &2963 85 10 STA &10 ; buffer_number &2965 a8 TAY &2966 b9 94 29 LDA &2994,Y ; buffers_minimum_angle_high &2969 85 07 STA &07 ; buffer_minimum_angle_high &296b 4a LSR A &296c 49 80 EOR #&80 # Halve and invert sign &296e 85 12 STA &12 ; buffer_maximum_angle_high &2970 b9 8b 29 LDA &298b,Y ; buffers_angle_offsets &2973 85 11 STA &11 ; buffer_h_angle_offset &2975 b9 91 29 LDA &2991,Y ; buffers_widths &2978 85 61 STA &61 ; right_edge_minus_left_edge_of_buffer &297a b9 8e 29 LDA &298e,Y ; buffers_left_edges &297d 85 35 STA &35 ; left_edge_of_buffer &297f 18 CLC &2980 65 61 ADC &61 ; right_edge_minus_left_edge_of_buffer &2982 85 36 STA &36 ; right_edge_of_buffer &2984 a9 00 LDA #&00 &2986 85 28 STA &28 ; buffer_minimum_angle_low &2988 85 29 STA &29 ; buffer_h_angle_offset_fraction &298a 60 RTS ; buffers_angle_offsets ; w1 w2 t &298b 0a 02 0c ; buffers_left_edges ; w1 w2 t &298e 50 40 60 ; buffers_widths ; w1 w2 t &2991 70 70 40 ; buffers_minimum_angle_high ; w1 w2 t &2994 14 14 08 ; initialise_buffer_variables_for_updating_object &2997 85 74 STA &74 ; buffer_width &2999 4a LSR A &299a 85 07 STA &07 ; half_buffer_width &299c a9 00 LDA #&00 &299e 6a ROR A &299f 85 28 STA &28 ; buffer_minimum_angle_low # half_buffer_width_fraction &29a1 a5 07 LDA &07 ; half_buffer_width &29a3 4a LSR A &29a4 49 80 EOR #&80 &29a6 85 12 STA &12 ; buffer_maximum_angle_high &29a8 a5 74 LDA &74 ; buffer_width &29aa 0a ASL A &29ab 0a ASL A &29ac 85 61 STA &61 ; right_edge_minus_left_edge_of_buffer &29ae 4a LSR A &29af 29 fc AND #&fc &29b1 09 80 ORA #&80 &29b3 85 36 STA &36 ; right_edge_of_buffer &29b5 38 SEC &29b6 e5 61 SBC &61 ; right_edge_minus_left_edge_of_buffer &29b8 85 35 STA &35 ; left_edge_of_buffer &29ba 4a LSR A &29bb 4a LSR A &29bc 4a LSR A &29bd 85 11 STA &11 ; buffer_h_angle_offset &29bf a9 00 LDA #&00 &29c1 6a ROR A &29c2 85 29 STA &29 ; buffer_h_angle_offset_fraction &29c4 a9 02 LDA #&02 ; BUFFER_TALL &29c6 85 10 STA &10 ; buffer_number ; leave &29c8 60 RTS ; plot_block &29c9 bd 80 01 LDA &0180,X ; plottables_data &29cc 29 0f AND #&0f # Low nibble of plottables_data is block type &29ce 8d 7f 0a STA &0a7f ; objects_type + &3f # Object &3f is used for the text blocks &29d1 f0 f5 BEQ &29c8 ; leave &29d3 a5 25 LDA &25 ; plottables_offset # Text blocks are plotted in rows &29d5 8d 3f 09 STA &093f ; objects_x + &3f &29d8 a5 26 LDA &26 ; tile_row &29da 8d bf 09 STA &09bf ; objects_y + &3f &29dd a0 3f LDY #&3f &29df 4c 33 5d JMP &5d33 ; plot_object ; plot_tile_or_block &29e2 20 5a 35 JSR &355a ; update_sound &29e5 a5 25 LDA &25 ; plottables_offset &29e7 05 05 ORA &05 ; plottables_type &29e9 18 CLC &29ea 65 1b ADC &1b ; offset_to_tile &29ec 29 3f AND #&3f &29ee aa TAX &29ef 2c 4b 0c BIT &0c4b ; in_title_screen # Top bit set if plotting title screen &29f2 30 d5 BMI &29c9 ; plot_block ; plot_tile &29f4 bd 80 01 LDA &0180,X ; plottables_data &29f7 f0 cf BEQ &29c8 ; leave # Zero if tile is hidden &29f9 c9 c0 CMP #&c0 ; TILE_HAS_OBJECT &29fb 90 08 BCC &2a05 ; plot_empty_tile # Does the tile contain objects? ; plot_tile_containing_objects &29fd 48 PHA &29fe 20 1b 2a JSR &2a1b ; plot_checkerboard_tile # If so, plot tile &2a01 68 PLA &2a02 4c 9f 21 JMP &219f ; plot_stack_of_objects # then objects ; plot_empty_tile &2a05 29 0f AND #&0f # How does the tile slope? &2a07 f0 12 BEQ &2a1b ; plot_checkerboard_tile &2a09 c9 0c CMP #&0c ; SLOPE_C &2a0b f0 04 BEQ &2a11 ; tile_has_flat_and_sloping_edge &2a0d c9 04 CMP #&04 ; SLOPE_4 &2a0f d0 28 BNE &2a39 ; plot_sloping_tile ; tile_has_flat_and_sloping_edge &2a11 48 PHA &2a12 a5 4b LDA &4b ; view_quadrant_minus_two &2a14 29 01 AND #&01 &2a16 85 45 STA &45 ; triangle_first_point_tile &2a18 68 PLA &2a19 d0 3f BNE &2a5a ; plot_two_triangles # Always branches ; plot_checkerboard_tile # Flat tiles are plotted in a checkerboard &2a1b a2 00 LDX #&00 &2a1d a5 25 LDA &25 ; plottables_offset &2a1f 45 26 EOR &26 ; tile_row &2a21 29 01 AND #&01 &2a23 f0 08 BEQ &2a2d ; plot_quadrilateral_tile # Use colour 3 for even tiles &2a25 a2 08 LDX #&08 # Use colour 0 for odd tiles ; initialise_second_secret_code_check &2a27 bd 75 0c LDA &0c75,X ; prnd_state + 2 - 8 # Copy part of prnd state &2a2a 9d 4d 0c STA &0c4d,X ; prnd_byte_after_generation - 8 # to use when checking secret code the second time ; plot_quadrilateral_tile &2a2d bd e3 2c LDA &2ce3,X ; tile_colours_table # Set colour of polygon depending on slope &2a30 85 19 STA &19 ; polygon_colours &2a32 a9 00 LDA #&00 ; POLYGON_QUADRILATERAL_FROM_TILE &2a34 85 3b STA &3b ; polygon_source &2a36 4c 79 2a JMP &2a79 ; plot_polygon # Plot quadrilateral ; plot_sloping_tile &2a39 aa TAX # X is slope &2a3a 38 SEC &2a3b e5 66 SBC &66 ; view_quadrant_times_four &2a3d 29 0f AND #&0f &2a3f a8 TAY &2a40 29 03 AND #&03 &2a42 c9 01 CMP #&01 &2a44 f0 e7 BEQ &2a2d ; plot_quadrilateral_tile # Plot quadrilateral tile ; tile_has_one_point_different_to_others &2a46 b9 03 2d LDA &2d03,Y ; triangle_first_point_tile_table &2a49 85 45 STA &45 ; triangle_first_point_tile &2a4b 8a TXA &2a4c 29 04 AND #&04 # &4 of slope &2a4e 4a LSR A &2a4f 4a LSR A &2a50 18 CLC &2a51 65 4b ADC &4b ; view_quadrant_minus_two &2a53 c9 02 CMP #&02 &2a55 8a TXA &2a56 b0 02 BCS &2a5a ; plot_two_triangles &2a58 09 10 ORA #&10 # Use second colour ; plot_two_triangles &2a5a 85 34 STA &34 ; first_triangle_in_slope_type &2a5c aa TAX &2a5d a9 80 LDA #&80 ; POLYGON_TRIANGLE_ONE_FROM_TILE &2a5f 85 3b STA &3b ; polygon_source &2a61 bd e3 2c LDA &2ce3,X ; tile_colours_table &2a64 85 19 STA &19 ; polygon_colours &2a66 20 79 2a JSR &2a79 ; plot_polygon # Plot first triangle (left for corners) &2a69 a5 34 LDA &34 ; first_triangle_in_slope_type &2a6b 49 10 EOR #&10 # Use other colour for second triangle &2a6d aa TAX &2a6e bd e3 2c LDA &2ce3,X ; tile_colours_table &2a71 85 19 STA &19 ; polygon_colours &2a73 a5 3b LDA &3b ; polygon_source &2a75 09 40 ORA #&40 ; POLYGON_TRIANGLE_TWO_FROM_TILE - POLYGON_TRIANGLE_ONE_FROM_TILE &2a77 85 3b STA &3b ; polygon_source # Plot second triangle (right for corners) ; plot_polygon &2a79 a4 10 LDY &10 ; buffer_number &2a7b c0 02 CPY #&02 ; BUFFER_TALL # Plot the polygon in a single pass if tall buffer &2a7d b0 14 BCS &2a93 ; not_wide_buffer &2a7f 20 36 2d JSR &2d36 ; prepare_polygon # Returns carry set if nothing to plot &2a82 b0 0c BCS &2a90 ; nothing_to_plot &2a84 20 99 22 JSR &2299 ; fill_polygon # Otherwise, for wide buffer, plot it in two parts &2a87 a4 10 LDY &10 ; buffer_number &2a89 b9 2c 00 LDA &002c,Y ; polygon_extends_to_right # Check if clipped to right (Y = 0) or left (Y = 1) &2a8c c9 01 CMP #&01 # Non-zero if polygon didn't extend beyond edge &2a8e f0 0b BEQ &2a9b ; leave ; nothing_to_plot &2a90 20 5d 29 JSR &295d ; initialise_buffer_variables_for_other_vertical_buffer ; not_wide_buffer &2a93 20 36 2d JSR &2d36 ; prepare_polygon &2a96 b0 03 BCS &2a9b ; leave &2a98 20 99 22 JSR &2299 ; fill_polygon # this is plotting left section of screen ; leave &2a9b 60 RTS ; generate_landscape_and_play_game &2a9c a2 50 LDX #&50 ; randomise_row_or_column_tile_z_table &2a9e 20 94 31 JSR &3194 ; prnd &2aa1 9d 00 5a STA &5a00,X ; row_or_column_tile_z_table # Sets value used in secret code obfuscation at &2c2e &2aa4 ca DEX &2aa5 10 f7 BPL &2a9e ; randomise_row_or_column_tile_z_table &2aa7 ad 52 0c LDA &0c52 ; zero_if_landscape_0000 &2aaa d0 04 BNE &2ab0 ; not_landscape_0000 &2aac a9 18 LDA #&18 # Use a fixed vertical scale for landscape 0000 &2aae d0 06 BNE &2ab6 ; set_landscape_vertical_scale ; not_landscape_0000 &2ab0 20 1b 34 JSR &341b ; get_random_number_between_0_and_22 # Otherwise choose a random vertical scale &2ab3 18 CLC &2ab4 69 0e ADC #&0e ; set_landscape_vertical_scale &2ab6 8d 08 0c STA &0c08 ; landscape_vertical_scale &2ab9 a9 80 LDA #&80 # &80 to give all tiles random heights &2abb 20 f2 2a JSR &2af2 ; process_landscape &2abe a9 00 LDA #&00 # &40 clear to average neighbouring tile heights &2ac0 20 53 2b JSR &2b53 ; smooth_landscape &2ac3 a9 01 LDA #&01 # &01 to scale landscape &2ac5 20 f2 2a JSR &2af2 ; process_landscape # (height is in the low nibble at this stage) &2ac8 a9 40 LDA #&40 # &40 set to level spikes &2aca 20 53 2b JSR &2b53 ; smooth_landscape ; set_tile_slopes &2acd a9 1e LDA #&1e # For each tile with neighbours, &2acf 85 26 STA &26 ; tile_y ; set_tile_slopes_loop_y &2ad1 a9 1e LDA #&1e &2ad3 85 24 STA &24 ; tile_x ; set_tile_slopes_loop_x &2ad5 20 4e 2c JSR &2c4e ; calculate_tile_slope # Use low nibble (height) to calculate slope of tile &2ad8 20 78 2b JSR &2b78 ; calculate_tile_address &2adb 8a TXA # Move the slope into the highest nibble &2adc 0a ASL A &2add 0a ASL A &2ade 0a ASL A &2adf 0a ASL A &2ae0 11 5e ORA (&5e),Y ; tile_address # Combine and height nibbles &2ae2 91 5e STA (&5e),Y ; tile_address &2ae4 c6 24 DEC &24 ; tile_x &2ae6 10 ed BPL &2ad5 ; set_tile_slopes_loop_x &2ae8 c6 26 DEC &26 ; tile_y &2aea 10 e5 BPL &2ad1 ; set_tile_slopes_loop_y &2aec a9 02 LDA #&02 # &02 to swap height and slope nibbles &2aee 20 f2 2a JSR &2af2 ; process_landscape # Returns height in top nibble, slope in bottom &2af1 60 RTS # Leaves to &5f7e to_preview_screen (see &2bf0) ; process_landscape &2af2 85 1c STA &1c ; how_to_process &2af4 a9 1f LDA #&1f # For each tile, &2af6 85 26 STA &26 ; tile_y ; process_landscape_y_loop &2af8 a9 1f LDA #&1f &2afa 85 24 STA &24 ; tile_x ; process_landscape_x_loop &2afc 20 78 2b JSR &2b78 ; calculate_tile_address &2aff a5 1c LDA &1c ; how_to_process &2b01 f0 45 BEQ &2b48 ; set_tile_z_and_move_to_next_tile # If &00, zero the tile height &2b03 30 40 BMI &2b45 ; randomise_tile_height # If &80, give the tile a random height &2b05 4a LSR A &2b06 b1 5e LDA (&5e),Y ; tile_address &2b08 b0 11 BCS &2b1b ; scale_tile_height ; swap_nibbles # If &02, swap height and slope nibbles &2b0a 4a LSR A &2b0b 4a LSR A &2b0c 4a LSR A &2b0d 4a LSR A &2b0e 85 74 STA &74 ; top_nibble &2b10 b1 5e LDA (&5e),Y ; tile_address &2b12 0a ASL A &2b13 0a ASL A &2b14 0a ASL A &2b15 0a ASL A &2b16 05 74 ORA &74 ; top_nibble &2b18 4c 48 2b JMP &2b48 ; set_tile_z_and_move_to_next_tile ; scale_tile_height # If &01, scale the tile height &2b1b 38 SEC &2b1c e9 80 SBC #&80 &2b1e 08 PHP &2b1f 10 05 BPL &2b26 ; skip_inversion &2b21 49 ff EOR #&ff &2b23 18 CLC &2b24 69 01 ADC #&01 ; skip_inversion &2b26 85 75 STA &75 ; a &2b28 ad 08 0c LDA &0c08 ; landscape_vertical_scale &2b2b 20 03 0d JSR &0d03 ; multiply_A_by_byte # Multiply (tile_z - &80) * landscape_vertical_scale &2b2e 28 PLP &2b2f 20 07 10 JSR &1007 ; invert_A_and_a_fraction_if_negative &2b32 18 CLC &2b33 69 06 ADC #&06 &2b35 10 02 BPL &2b39 ; skip_floor &2b37 a9 00 LDA #&00 # Ensure resulting height is between 1 and 11 ; skip_floor &2b39 18 CLC &2b3a 69 01 ADC #&01 &2b3c c9 0c CMP #&0c &2b3e 90 02 BCC &2b42 ; skip_ceiling &2b40 a9 0b LDA #&0b ; skip_ceiling &2b42 4c 48 2b JMP &2b48 ; set_tile_z_and_move_to_next_tile ; randomise_tile_height &2b45 20 94 31 JSR &3194 ; prnd ; set_tile_z_and_move_to_next_tile &2b48 91 5e STA (&5e),Y ; tile_address &2b4a c6 24 DEC &24 ; tile_x &2b4c 10 ae BPL &2afc ; process_landscape_x_loop &2b4e c6 26 DEC &26 ; tile_y &2b50 10 a6 BPL &2af8 ; process_landscape_y_loop &2b52 60 RTS ; smooth_landscape # Called with A = 0 to average, A = &40 to level spikes &2b53 85 66 STA &66 ; smoothing_type &2b55 a9 02 LDA #&02 &2b57 85 15 STA &15 ; count ; smooth_landscape_count_loop &2b59 a9 1f LDA #&1f # For each row of tiles, &2b5b 85 26 STA &26 ; tile_y ; smooth_landscape_y_loop &2b5d a9 00 LDA #&00 # Clear top bit to run smoothing over tiles in row &2b5f 20 90 2b JSR &2b90 ; smooth_row_or_column &2b62 c6 26 DEC &26 ; tile_y &2b64 10 f7 BPL &2b5d ; smooth_landscape_y_loop &2b66 a9 1f LDA #&1f # For each column of tiles, &2b68 85 24 STA &24 ; tile_x ; smooth_landscape_x_loop &2b6a a9 80 LDA #&80 # Set top bit to run smoothing over tiles in column &2b6c 20 90 2b JSR &2b90 ; smooth_row_or_column &2b6f c6 24 DEC &24 ; tile_x &2b71 10 f7 BPL &2b6a ; smooth_landscape_y_loop &2b73 c6 15 DEC &15 ; count &2b75 d0 e2 BNE &2b59 ; smooth_landscape_count_loop &2b77 60 RTS ; calculate_tile_address &2b78 a5 24 LDA &24 ; tile_x # Address for tile (x, y) is &2b7a 0a ASL A # &0400 (tiles_table) &2b7b 0a ASL A # + &100 * (x MOD 4) &2b7c 0a ASL A # + (x DIV 4) * 8 &2b7d 29 e0 AND #&e0 # + y &2b7f 05 26 ORA &26 ; tile_y &2b81 a8 TAY &2b82 a5 24 LDA &24 ; tile_x &2b84 29 03 AND #&03 &2b86 18 CLC &2b87 69 04 ADC #&04 ; &0400 = tiles_table &2b89 85 5f STA &5f ; tile_address_high &2b8b b1 5e LDA (&5e),Y ; tile_address # Leave with A = height of tile or topmost object &2b8d c9 c0 CMP #&c0 ; TILE_HAS_OBJECT # Leave with carry set if tile contains object &2b8f 60 RTS ; smooth_row_or_column &2b90 05 66 ORA &66 ; smoothing_type &2b92 85 1c STA &1c : how_to_smooth ; copy_heights_to_temporary_table_loop # For each tile in the row or column, &2b96 8a TXA &2b97 29 1f AND #&1f &2b99 24 1c BIT &1c ; global_plus_row_or_column_or # Top bit set if running over y, clear if over x &2b9b 10 05 BPL &2ba2 ; copy_height ; running_over_y &2b9d 85 26 STA &26 ; tile_y &2b9f 4c a4 2b JMP &2ba4 ; running_over_x &2ba2 85 24 STA &24 ; tile_x ; copy_height &2ba4 20 78 2b JSR &2b78 ; calculate_tile_address # Get height of tile &2ba7 9d 00 5a STA &5a00,X ; row_or_column_tile_z_table # and store in temporary table &2baa ca DEX &2bab 10 e9 BPL &2b96 ; copy_heights_to_temporary_table_loop ; smooth_temporary_table &2bad 24 1c BIT &1c ; how_to_smooth &2baf 50 4d BVC &2bfe ; average_tile_heights # If &40 clear, average tiles, if &40 set, level spikes ; level_spikes &2bb1 a2 1f LDX #&1f # If levelling, for each tile in the row or column, ; smooth_row_or_column_loop &2bb3 bd 01 5a LDA &5a01,X ; row_or_column_tile_z_table + 1 # Consider its height (z0) and the next two (z1, z2) &2bb6 dd 02 5a CMP &5a02,X ; row_or_column_tile_z_table + 2 &2bb9 f0 2d BEQ &2be8 ; consider_next_tile # Do nothing if z1 = z2 (keep plateau) &2bbb b0 10 BCS &2bcd ; middle_is_higher_than_last &2bbd dd 00 5a CMP &5a00,X ; row_or_column_tile_z_table # If z1 < z2, &2bc0 f0 26 BEQ &2be8 ; consider_next_tile # do nothing if z1 = z0 (keep plateau) &2bc2 b0 24 BCS &2be8 ; consider_next_tile # do nothing if z1 > z0 (keep upward slope) &2bc4 bd 02 5a LDA &5a02,X ; row_or_column_tile_z_table + 2 &2bc7 dd 00 5a CMP &5a00,X ; row_or_column_tile_z_table # set z1 = z0 if z1 < z0 and z2 < z0 &2bca 4c da 2b JMP &2bda ; use_first_if_carry_clear ; middle_is_higher_than_last # If z1 > z2 &2bcd dd 00 5a CMP &5a00,X ; row_or_column_tile_z_table &2bd0 f0 16 BEQ &2be8 ; consider_next_tile # do nothing if z1 = z0 (keep plateau) &2bd2 90 14 BCC &2be8 ; consider_next_tile # do nothing if z1 < z0 (keep downward slope) &2bd4 bd 00 5a LDA &5a00,X ; row_or_column_tile_z_table &2bd7 dd 02 5a CMP &5a02,X ; row_or_column_tile_z_table + 2 ; use_first_if_carry_clear &2bda 90 06 BCC &2be2 ; set_height_to_last # set z1 = z2 if z1 > z0 and z0 > z2 &2bdc bd 00 5a LDA &5a00,X ; row_or_column_tile_z_table # set z1 = z0 if z1 > z0 and z2 < z0 &2bdf 4c e5 2b JMP &2be5 ; set_height ; set_height_to_last &2be2 bd 02 5a LDA &5a02,X ; row_or_column_tile_z_table + 2 ; set_height &2be5 9d 01 5a STA &5a01,X ; row_or_column_tile_z_table + 1 # Set height of middle tile ; consider_next_tile &2be8 ca DEX &2be9 10 c8 BPL &2bb3 ; smooth_row_or_column_loop &2beb 2c 71 0c BIT &0c71 ; play_game_after_generation # Top bit clear if only previewing landscape &2bee 30 0b BMI &2bfb ; skip_setting_return_address ; set_return_address &2bf0 a9 5f LDA #&5f ; &5f7d = to_preview_screen - 1 &2bf2 9d 00 01 STA &0100,X ; stack # X is &ff here, so this sets the return address for &2bf5 ca DEX # generate_landscape_and_play_game &2bf6 a9 7d LDA #&7d &2bf8 9d 00 01 STA &0100,X ; stack ; skip_setting_return_address &2bfb 4c 34 2c JMP &2c34 ; copy_heights_from_temporary_table ; average_tile_heights &2bfe a2 00 LDX #&00 # If averaging, for each tile in the row or column, ; average_tile_heights_loop &2c00 a9 00 LDA #&00 &2c02 85 75 STA &75 ; a &2c04 bd 00 5a LDA &5a00,X ; row_or_column_tile_z_table # Add its height to the heights of the three next tiles &2c07 18 CLC &2c08 7d 01 5a ADC &5a01,X ; row_or_column_tile_z_table + 1 &2c0b 90 03 BCC &2c10 ; skip_overflow_one &2c0d 18 CLC &2c0e e6 75 INC &75 ; a ; skip_overflow_one &2c10 7d 02 5a ADC &5a02,X ; row_or_column_tile_z_table + 2 &2c13 90 03 BCC &2c18 ; skip_overflow_two &2c15 18 CLC &2c16 e6 75 INC &75 ; a ; skip_overflow_two &2c18 7d 03 5a ADC &5a03,X ; row_or_column_tile_z_table + 3 &2c1b 90 03 BCC &2c20 ; skip_overflow_three &2c1d 18 CLC &2c1e e6 75 INC &75 ; a ; skip_overflow_three &2c20 46 75 LSR &75 ; a # Divide by four to get an average height of the four &2c22 6a ROR A &2c23 46 75 LSR &75 ; a &2c25 6a ROR A &2c26 9d 00 5a STA &5a00,X ; row_or_column_tile_z_table # Set the height of the first tile to this average &2c29 e8 INX &2c2a e0 20 CPX #&20 &2c2c 90 d2 BCC &2c00 ; average_tile_heights_loop ; initialise_secret_code_obfuscation &2c2e bd 2e 5a LDA &5a2e,X ; row_or_column_tile_z_table + &4e - &20 # Set from prnd at &2aa1 &2c31 9d 1d 0f STA &0f1d,X ; copy_of_landscape_high_from_prnd - &20 # Store obfuscated copy of high byte of landscape number ; copy_heights_from_temporary_table &2c34 a2 1f LDX #&1f # For each tile in the row or column, ; copy_heights_from_temporary_table_loop &2c36 8a TXA &2c37 24 1c BIT &1c ; global_plus_row_or_column_or &2c39 10 05 BPL &2c40 ; running_over_x ; running_over_y &2c3b 85 26 STA &26 ; tile_y &2c3d 4c 42 2c JMP &2c42 ; set_height ; running_over_x &2c40 85 24 STA &24 ; tile_x ; set_height &2c42 20 78 2b JSR &2b78 ; calculate_tile_address &2c45 bd 00 5a LDA &5a00,X ; row_or_column_tile_z_table # Get height of tile from temporary table &2c48 91 5e STA (&5e),Y ; tile_address # and set height in landscape table accordinly &2c4a ca DEX &2c4b 10 e9 BPL &2c36 ; copy_heights_from_temporary_table_loop &2c4d 60 RTS # Slopes # ====== # 0: 1: 2: 3: 4: 5: 6: 7: 8: 9: a: b: c: d: e: f: # == -- ++ +- =a +- +- ++ ++ -+ -- ab -+ -- -+ # == ++ -+ ++ =b +- -- +- -- -- +- == -+ -+ ++ # # 8 is unused in calculate_tile_slope, but used for the other checkerboard colour for flat tiles ; calculate_tile_slope &2c4e 20 78 2b JSR &2b78 ; calculate_tile_address &2c51 29 0f AND #&0f &2c53 85 73 STA &73 ; tile_0_0_z &2c55 e6 24 INC &24 ; tile_x &2c57 20 78 2b JSR &2b78 ; calculate_tile_address &2c5a 29 0f AND #&0f &2c5c 85 76 STA &76 ; tile_1_0_z &2c5e e6 26 INC &26 ; tile_y &2c60 20 78 2b JSR &2b78 ; calculate_tile_address &2c63 29 0f AND #&0f &2c65 85 75 STA &75 ; tile_1_1_z &2c67 c6 24 DEC &24 ; tile_x &2c69 20 78 2b JSR &2b78 ; calculate_tile_address &2c6c 29 0f AND #&0f &2c6e 85 74 STA &74 ; tile_0_1_z &2c70 c6 26 DEC &26 ; tile_y &2c72 a5 73 LDA &73 ; tile_0_0_z &2c74 c5 76 CMP &76 ; tile_1_0_z &2c76 f0 39 BEQ &2cb1 ; is_1_3_6_9_a_c_or_f ; is_2_4_5_7_b_c_d_or_e &2c78 c5 74 CMP &74 ; tile_0_1_z &2c7a f0 16 BEQ &2c92 ; is_4_5_7_d_or_e ; is_2_4_b_or_c &2c7c a5 75 LDA &75 ; tile_1_1_z &2c7e c5 76 CMP &76 ; tile_1_0_z &2c80 f0 03 BEQ &2c85 ; is_2_4_or_b ; leave_with_c &2c82 a2 0c LDX #&0c ; SLOPE_C # one y edge flat, one sloped c: ab or == &2c84 60 RTS # == ab ; is_2_4_or_b &2c85 c5 74 CMP &74 ; tile_0_1_z &2c87 d0 13 BNE &2c9c ; leave_with_4 ; is_2_or_b &2c89 a2 02 LDX #&02 ; SLOPE_2 # origin point is different 2: ++ b: -- &2c8b c5 73 CMP &73 ; tile_0_0_z # -+ +- &2c8d b0 02 BCS &2c91 ; leave &2c8f a2 0b LDX #&0b ; SLOPE_B ; leave &2c91 60 RTS ; is_4_5_7_d_or_e &2c92 a5 75 LDA &75 ; tile_1_1_z &2c94 c5 76 CMP &76 ; tile_1_0_z &2c96 f0 10 BEQ &2ca8 ; is_5_or_d &2c98 c5 74 CMP &74 ; tile_0_1_z &2c9a f0 03 BEQ &2c9f ; is_7_or_e ; leave_with_4 &2c9c a2 04 LDX #&04 ; SLOPE_4 # one x edge flat, one sloped 4: =a a= &2c9e 60 RTS # =b or b= ; is_7_or_e &2c9f a2 0e LDX #&0e ; SLOPE_E # x+ point is different e: -- 7: ++ &2ca1 c5 76 CMP &76 ; tile_1_0_z # -+ +- &2ca3 90 02 BCC &2ca7 ; leave &2ca5 a2 07 LDX #&07 : SLOPE_7 ; leave &2ca7 60 RTS ; is_5_or_d &2ca8 a2 05 LDX #&05 ; SLOPE_5 # y edges different 5: +- d: -+ &2caa c5 74 CMP &74 ; tile_0_1_z # +- -+ &2cac 90 02 BCC &2cb0 ; leave &2cae a2 0d LDX #&0d ; SLOPE_D ; leave &2cb0 60 RTS ; is_1_3_6_9_a_c_or_f &2cb1 c5 74 CMP &74 ; tile_0_1_z &2cb3 f0 1c BEQ &2cd1 ; is_0_3_or_a ; is_1_6_9_c_or_f &2cb5 a5 75 LDA &75 ; tile_1_1_z &2cb7 c5 74 CMP &74 ; tile_0_1_z &2cb9 f0 0d BEQ &2cc8 ; is_1_or_9 ; is 6_c_or_f &2cbb c5 76 CMP &76 ; tile_1_0_z &2cbd d0 c3 BNE &2c82 ; leave_with_c ; is_6_or_f &2cbf a2 06 LDX #&06 ; SLOPE_6 # y+ point is different 6: +- f: -+ &2cc1 c5 74 CMP &74 ; tile_0_1_z # -- ++ &2cc3 90 02 BCC &2cc7 ; leave &2cc5 a2 0f LDX #&0f ; SLOPE_F ; leave &2cc7 60 RTS ; is_1_or_9 &2cc8 a2 01 LDX #&01 ; SLOPE_1 # x edges different 1: -- 9: ++ &2cca c5 76 CMP &76 ; tile_1_0_z # ++ -- &2ccc 90 02 BCC &2cd0 ; leave &2cce a2 09 LDX #&09 ; SLOPE_9 ; leave &2cd0 60 RTS ; is_0_3_or_a &2cd1 c5 75 CMP &75 ; tile_1_1_z ; is 3_or_a &2cd3 f0 07 BEQ &2cdc ; leave_with_zero &2cd5 a2 0a LDX #&0a ; SLOPE_A # x+ y+ point is different a: -+ 3: +- &2cd7 90 02 BCC &2cdb ; leave # -- ++ &2cd9 a2 03 LDX #&03 ; SLOPE_3 ; leave &2cdb 60 RTS ; leave_with_zero # tile is flat 0: 00 &2cdc a2 00 LDX #&00 ; SLOPE_0 # 00 &2cde 60 RTS ; triangle_corner_tile_delta_table &2cdf 01 ; +1x &2ce0 21 ; +1x +1y &2ce1 ff ; -1x &2ce2 1f ; -1x +1y ; tile_colours_table # &00 = blue (checkerboard) ; 0 1 2 3 4 5 6 7 8 9 a b c d e f # &04 = black &2ce3 3c 04 04 08 08 08 04 08 00 04 08 04 04 08 08 04 ; &00 # &08 = colour 2 (light) &2cf3 00 00 08 04 08 00 08 04 00 00 04 08 04 00 04 08 ; &10 # &3c = colour 3 (checkerboard) ; tile 0 1 2 3 4 5 6 7 8 9 a b c d e f ; 1: 3 K K 2 2 2 K 2 B K 2 K K 2 2 K ; &00 ; 2: - - 2 K 2 - 2 K - - K 2 K - K 2 ; &10 ; triangle_first_point_tile_table ; 0 1 2 3 4 5 6 7 8 9 a b c d e f &2d03 00 00 00 00 00 00 01 01 00 00 00 00 00 00 01 01 ; prepare_tile_triangle &2d13 a4 45 LDY &45 ; triangle_first_point_tile &2d15 50 07 BVC &2d1e ; is_triangle_one # Clear if triangle one (POLYGON_TRIANGLE_ONE_FROM_TILE) ; is_triangle_two # Set if triangle two (POLYGON_TRIANGLE_TWO_FROM_TILE) &2d17 18 CLC &2d18 69 21 ADC #&21 # First point is in other row and column &2d1a 29 3f AND #&3f &2d1c c8 INY # Third point changes accordingly &2d1d c8 INY ; is_triangle_one # POLYGON_TRIANGLE_ONE_FROM_TILE &2d1e 8d 40 0c STA &0c40 ; temporary_vertex_data # First &2d21 8d 43 0c STA &0c43 ; temporary_vertex_data + 3 # and last points of triangle are the same &2d24 49 20 EOR #&20 &2d26 8d 41 0c STA &0c41 ; temporary_vertex_data + 1 # Second point is in other row &2d29 18 CLC &2d2a 79 df 2c ADC &2cdf,Y ; triangle_corner_tile_delta_table # Third point is in other row or column &2d2d 29 3f AND #&3f &2d2f 8d 42 0c STA &0c42 ; temporary_vertex_data + 2 &2d32 a2 03 LDX #&03 # Plot a triangle &2d34 d0 22 BNE &2d58 ; set_last_vertex_offset # Always branches ; prepare_polygon &2d36 a5 25 LDA &25 ; plottables_offset &2d38 05 05 ORA &05 ; plottables_type &2d3a 24 3b BIT &3b ; polygon_source &2d3c 30 d5 BMI &2d13 ; prepare_tile_triangle # Top bit set if polygon is a triangle in a tile &2d3e 70 53 BVS &2d93 ; process_polygon_data # &40 (POLYGON_FROM_OBJECT_DATA) set if object polygon ; prepare_quadrilateral_tile_polygon # POLYGON_QUADRILATERAL_FROM_TILE set if quadrilateral &2d40 8d 40 0c STA &0c40 ; temporary_vertex_data # First &2d43 8d 44 0c STA &0c44 ; temporary_vertex_data + 4 # and last points of quadrilateral are the same &2d46 49 20 EOR #&20 &2d48 8d 41 0c STA &0c41 ; temporary_vertex_data + 1 # Second point is in other column &2d4b 18 CLC &2d4c 69 01 ADC #&01 &2d4e 8d 42 0c STA &0c42 ; temporary_vertex_data + 2 # Third point is in other column and other row &2d51 49 20 EOR #&20 &2d53 8d 43 0c STA &0c43 ; temporary_vertex_data + 3 # Fourth point is in other row &2d56 a2 04 LDX #&04 # Plot a quadrilateral ; set_last_vertex_offset &2d58 86 17 STX &17 ; last_vertex_offset &2d5a 4c 93 2d JMP &2d93 ; process_polygon_data ; convert_angles_into_double_coordinates &2d5d a9 c0 LDA #&c0 # Set &40 to indicate polygon goes outside inner area &2d5f 85 6c STA &6c ; polygon_goes_outside_inner_area_horizontally &2d61 a4 17 LDY &17 ; last_vertex_offset # For each vertex in the polygon, ; convert_angles_into_double_coordinates &2d63 b1 3c LDA (&3c),Y ; polygon_vertex_data_address &2d65 aa TAX &2d66 bd a0 0b LDA &0ba0,X ; plottables_relative_h_angle_low # screen_x = h_angle * 8 + buffer_h_angle_offset &2d69 18 CLC &2d6a 65 29 ADC &29 ; buffer_h_angle_offset_fraction &2d6c 85 74 STA &74 ; a_fraction &2d6e bd 00 55 LDA &5500,X ; plottables_relative_h_angle_high &2d71 65 11 ADC &11 ; buffer_h_angle_offset &2d73 06 74 ASL &74 ; a_fraction &2d75 2a ROL A &2d76 26 74 ROL &74 ; a_fraction &2d78 2a ROL A &2d79 26 74 ROL &74 ; a_fraction &2d7b 2a ROL A &2d7c 9d a0 54 STA &54a0,X ; plottables_screen_x_low &2d7f a5 74 LDA &74 ; a_fraction # Keep lowest two bits of high byte of screen_x &2d81 2a ROL A &2d82 29 07 AND #&07 &2d84 c9 04 CMP #&04 &2d86 90 02 BCC &2d8a ; not_negative &2d88 09 f8 ORA #&f8 # Set sign ; not_negative &2d8a 9d 40 0b STA &0b40,X ; plottables_screen_x_high &2d8d 88 DEY &2d8e 10 d3 BPL &2d63 ; convert_angles_into_double_coordinates &2d90 4c bc 2d JMP &2dbc ; process_lines ; process_polygon_data &2d93 a9 00 LDA #&00 # Clear &40 to indicate polygon within inner area &2d95 85 6c STA &6c ; polygon_goes_outside_inner_area_horizontally &2d97 a4 17 LDY &17 ; last_vertex_offset # For each vertex in the polygon, ; convert_angles_into_screen_coordinates_loop &2d99 b1 3c LDA (&3c),Y ; polygon_vertex_data_address &2d9b aa TAX &2d9c bd a0 0b LDA &0ba0,X ; plottables_relative_h_angle_low # screen_x = h_angle * 8 + buffer_h_angle_offset &2d9f 18 CLC &2da0 65 29 ADC &29 ; buffer_h_angle_offset_fraction &2da2 85 74 STA &74 ; a_fraction &2da4 bd 00 55 LDA &5500,X ; plottables_relative_h_angle_high &2da7 65 11 ADC &11 ; buffer_h_angle_offset &2da9 c9 20 CMP #&20 # Is any vertex horizontally outside the inner area? &2dab b0 b0 BCS &2d5d ; convert_angles_into_double_coordinates # If so, calculate high bytes for all vertices &2dad 06 74 ASL &74 ; a_fraction &2daf 2a ROL A &2db0 06 74 ASL &74 ; a_fraction &2db2 2a ROL A &2db3 06 74 ASL &74 ; a_fraction &2db5 2a ROL A &2db6 9d a0 54 STA &54a0,X ; plottables_screen_x_low &2db9 88 DEY &2dba 10 dd BPL &2d99 ; convert_angles_into_screen_coordinates_loop ; process_lines &2dbc a9 00 LDA #&00 &2dbe 85 06 STA &06 ; top_row_of_polygon # Floor for subsequent maxima &2dc0 85 31 STA &31 ; polygon_right_edge # Floor for subsequent maxima &2dc2 85 1e STA &1e ; lines_that_were_points &2dc4 a9 ff LDA #&ff &2dc6 85 04 STA &04 ; bottom_row_of_polygon # Ceiling for subsequent minima &2dc8 85 30 STA &30 ; polygon_left_edge # Ceiling for subsequent minima &2dca 85 7f STA &7f ; polygon_needs_filling # Set to non-zero to indicate nothing to fill &2dcc a0 00 LDY #&00 ; process_lines_loop &2dce 84 4a STY &4a ; vertex_offset &2dd0 b1 3c LDA (&3c),Y ; polygon_vertex_data_address # Get end of line vertex into X &2dd2 aa TAX &2dd3 c8 INY &2dd4 b1 3c LDA (&3c),Y ; polygon_vertex_data_address # Get start of line vertex into Y &2dd6 a8 TAY &2dd7 a9 5a LDA #&5a ; polygon_left_edge_table DIV 256 # Use polygon_left_edge_table for downward sloping lines &2dd9 85 02 STA &02 ; edge_table_address_high &2ddb b9 80 0a LDA &0a80,Y ; plottables_screen_y_low # Calculate difference in y co-ordinates between the two &2dde 38 SEC &2ddf fd 80 0a SBC &0a80,X ; plottables_screen_y_low &2de2 85 0c STA &0c ; line_y_delta_low &2de4 b9 e0 0a LDA &0ae0,Y ; plottables_screen_y_high # Is the end vertex greater (above) than the start? &2de7 fd e0 0a SBC &0ae0,X ; plottables_screen_y_high &2dea 10 17 BPL &2e03 ; skip_inversion &2dec 85 76 STA &76 ; line_y_delta_high &2dee e6 02 INC &02 ; edge_table_address_high # Use polygon_right_edge_table for upward sloping lines &2df0 86 74 STX &74 ; tmp_x # swap ends of line &2df2 84 75 STY &75 ; tmp_y &2df4 a6 75 LDX &75 ; tmp_y &2df6 a4 74 LDY &74 ; tmp_x &2df8 a9 00 LDA #&00 &2dfa 38 SEC &2dfb e5 0c SBC &0c ; line_y_delta_low # and invert difference &2dfd 85 0c STA &0c ; line_y_delta_low &2dff a9 00 LDA #&00 &2e01 e5 76 SBC &76 ; line_y_delta_high # so line always slopes downwards ; skip_inversion &2e03 85 76 STA &76 ; line_y_delta_high &2e05 24 6c BIT &6c ; polygon_goes_outside_inner_area_horizontally &2e07 50 13 BVC &2e1c ; line_is_entirely_within_inner_area # &40 clear if polygon entirely within inner area &2e09 b9 40 0b LDA &0b40,Y ; plottables_screen_x_high &2e0c 1d 40 0b ORA &0b40,X ; plottables_screen_x_high &2e0f f0 0b BEQ &2e1c ; line_is_entirely_within_inner_area # Is this line entirely within inner area horizontally? &2e11 a5 76 LDA &76 ; line_y_delta_high &2e13 d0 04 BNE &2e19 ; to_process_line # If not, does it slope? &2e15 a5 0c LDA &0c ; line_y_delta_low &2e17 f0 3d BEQ &2e56 ; consider_next_line # If neither, skip this line ; to_process_line &2e19 4c cc 2f JMP &2fcc ; process_line # Otherwise, process the line ; line_is_entirely_within_inner_area # If line is entirely within inner area horizontally, &2e1c a5 76 LDA &76 ; line_y_delta_high &2e1e f0 0b BEQ &2e2b ; process_horizontal_line # does it slope? &2e20 a9 00 LDA #&00 &2e22 99 40 0b STA &0b40,Y ; plottables_screen_x_high &2e25 9d 40 0b STA &0b40,X ; plottables_screen_x_high &2e28 4c cc 2f JMP &2fcc ; process_line # If so, process the line ; process_horizontal_line # If not, &2e2b a5 0c LDA &0c ; line_y_delta_low &2e2d f0 67 BEQ &2e96 ; line_is_a_point # and it isn't a point, &2e2f b9 e0 0a LDA &0ae0,Y ; plottables_screen_y_high &2e32 85 3e STA &3e ; start_vertex_screen_y_high &2e34 b9 80 0a LDA &0a80,Y ; plottables_screen_y_low &2e37 85 1a STA &1a ; start_vertex_screen_y_low &2e39 bd e0 0a LDA &0ae0,X ; plottables_screen_y_high &2e3c 85 3f STA &3f ; end_vertex_screen_y_high &2e3e bd 80 0a LDA &0a80,X ; plottables_screen_y_low &2e41 85 16 STA &16 ; end_vertex_screen_y_low &2e43 b9 a0 54 LDA &54a0,Y ; plottables_screen_x_low &2e46 85 18 STA &18 ; start_vertex_screen_x_low &2e48 bd a0 54 LDA &54a0,X ; plottables_screen_x_low &2e4b 85 39 STA &39 ; end_vertex_screen_x_low &2e4d a9 00 LDA #&00 &2e4f 85 41 STA &41 ; start_vertex_screen_x_high &2e51 85 42 STA &42 ; end_vertex_screen_x_high &2e53 20 ae 2e JSR &2eae ; process_line_section # process the line ; consider_next_line &2e56 a4 4a LDY &4a ; vertex_offset &2e58 c8 INY &2e59 c4 17 CPY &17 ; last_vertex_offset &2e5b f0 03 BEQ &2e60 ; check_if_polygon_is_point &2e5d 4c ce 2d JMP &2dce ; process_lines_loop ; check_if_polygon_is_point &2e60 a5 1e LDA &1e ; lines_that_were_points # If all the lines in the polygon were points, &2e62 c5 17 CMP &17 ; last_vertex_offset &2e64 d0 22 BNE &2e88 ; polygon_isn't_point &2e66 bd e0 0a LDA &0ae0,X ; plottables_screen_y_high # Is the polygon on screen? &2e69 d0 1d BNE &2e88 ; polygon_isn't_point &2e6b bc 80 0a LDY &0a80,X ; plottables_screen_y_low &2e6e c4 52 CPY &52 ; buffer_minimum_screen_y_low &2e70 90 16 BCC &2e88 ; polygon_isn't_point &2e72 c4 51 CPY &51 ; buffer_maximum_screen_y_low &2e74 b0 12 BCS &2e88 ; polygon_isn't_point &2e76 84 04 STY &04 ; bottom_row_of_polygon # If so, make it a single row &2e78 84 06 STY &06 ; top_row_of_polygon &2e7a a5 30 LDA &30 ; polygon_left_edge # a single pixel wide &2e7c 99 00 5a STA &5a00,Y ; polygon_left_edge_table &2e7f a5 31 LDA &31 ; polygon_right_edge &2e81 99 00 5b STA &5b00,Y ; polygon_right_edge_table &2e84 a9 00 LDA #&00 # Set to zero to indicate something to plot &2e86 85 7f STA &7f ; polygon_needs_filling ; polygon_isn't_point &2e88 a5 7f LDA &7f ; polygon_needs_filling &2e8a d0 08 BNE &2e94 ; leave_with_carry_set &2e8c a5 06 LDA &06 ; top_row_of_polygon &2e8e c5 04 CMP &04 ; bottom_row_of_polygon &2e90 90 02 BCC &2e94 ; leave_with_carry_set &2e92 18 CLC # Leave with carry clear to indicate something to plot &2e93 60 RTS ; leave_with_carry_set &2e94 38 SEC # Leave with carry set to indicate nothing to plot &2e95 60 RTS ; line_is_a_point # If the line is a point, &2e96 bd a0 54 LDA &54a0,X ; plottables_screen_x_low &2e99 c5 31 CMP &31 ; polygon_right_edge &2e9b 90 02 BCC &2e9f ; skip_floor_right &2e9d 85 31 STA &31 ; polygon_right_edge # don't plot anything, ; skip_floor_right &2e9f c5 30 CMP &30 ; polygon_left_edge &2ea1 b0 02 BCS &2ea5 ; skip_ceiling_left &2ea3 85 30 STA &30 ; polygon_left_edge # but note its position in case the polygon is a point ; skip_ceiling_left &2ea5 e6 1e INC &1e ; lines_that_were_points &2ea7 4c 56 2e JMP &2e56 ; consider_next_line ; to_process_wide_line &2eaa 4c 87 30 JMP &3087 ; process_wide_line ; leave &2ead 60 RTS ; process_line &2eae a5 3f LDA &3f ; end_vertex_screen_y_high # The line is always horizontal or sloping downwards &2eb0 30 10 BMI &2ec2 ; line_starts_ends_bottom_of_inner_area &2eb2 d0 f9 BNE &2ead ; leave # Leave if line ends above top of inner area &2eb4 a5 16 LDA &16 ; end_vertex_screen_y_low &2eb6 c5 51 CMP &51 ; buffer_maximum_screen_y_low &2eb8 b0 f3 BCS &2ead ; leave # Leave if line ends above top of buffer &2eba c5 04 CMP &04 ; bottom_row_of_polygon &2ebc b0 08 BCS &2ec6 ; not_new_bottom_row &2ebe c5 52 CMP &52 ; buffer_minimum_screen_y_low # Does the line end below the previous bottom row? &2ec0 b0 02 BCS &2ec4 ; set_bottom_row_of_polygon ; line_starts_ends_bottom_of_inner_area &2ec2 a5 52 LDA &52 ; buffer_minimum_screen_y_low # Limit to bottom of buffer ; set_bottom_row_of_polygon &2ec4 85 04 STA &04 ; bottom_row_of_polygon # Set bottom row of polygon ; not_new_bottom_row &2ec6 a5 3e LDA &3e ; start_vertex_screen_y_high &2ec8 30 e3 BMI &2ead ; leave # Leave if line starts below bottom of inner area &2eca d0 0e BNE &2eda ; line_starts_above_top_of_inner_area &2ecc a5 1a LDA &1a ; start_vertex_screen_y_low &2ece c5 52 CMP &52 ; buffer_minimum_screen_y_low &2ed0 90 db BCC &2ead ; leave # Leave if line starts below bottom of buffer &2ed2 c5 06 CMP &06 ; top_row_of_polygon &2ed4 90 0b BCC &2ee1 ; not_new_top_row &2ed6 c5 51 CMP &51 ; buffer_maximum_screen_y_low # Does the line start above the previous top row? &2ed8 90 05 BCC &2edf ; set_top_row_of_polygon ; line_starts_above_top_of_inner_area &2eda a5 51 LDA &51 ; buffer_maximum_screen_y_low # Limit to top of buffer &2edc 38 SEC &2edd e9 01 SBC #&01 ; set_top_row_of_polygon &2edf 85 06 STA &06 ; top_row_of_polygon # Set top row of polygon ; not_new_top_row &2ee1 a5 41 LDA &41 ; start_vertex_screen_x_high # Does the line extend beyond inner area horizontally? &2ee3 05 42 ORA &42 ; end_vertex_screen_x_high &2ee5 d0 c3 BNE &2eaa ; to_process_wide_line ; process_narrow_line # If not, &2ee7 a5 18 LDA &18 ; start_vertex_screen_x_low &2ee9 38 SEC &2eea e5 39 SBC &39 ; end_vertex_screen_x_low &2eec b0 0b BCS &2ef9 ; narrow_line_goes_from_right_to_left ; narrow_line_goes_from_left_to_right # If the line goes from left to right, &2eee 49 ff EOR #&ff # Ensure line_x_delta_low is positive &2ef0 18 CLC &2ef1 69 01 ADC #&01 &2ef3 85 0d STA &0d ; line_x_delta_low &2ef5 a2 e8 LDX #&e8 ; INX # Move right when moving down or along line &2ef7 d0 04 BNE &2efd ; consider_narrow_line_slope ; narrow_line_goes_from_right_to_left # If the line goes from right to left, &2ef9 85 0d STA &0d ; line_x_delta_low &2efb a2 ca LDX #&ca ; DEX # Move left when moving down or along line ; consider_narrow_line_slope &2efd a4 0d LDY &0d ; line_x_delta_low # Does the line slope more vertically or horizontally? &2eff c4 0c CPY &0c ; line_y_delta_low &2f01 a4 1a LDY &1a ; start_vertex_screen_y_low &2f03 a5 02 LDA &02 ; edge_table_address_high &2f05 b0 34 BCS &2f3b ; process_narrow_shallow_line ; process_narrow_steep_line # If y_delta > x_delta, &2f07 8d 2b 2f STA &2f2b ; narrow_steep_edge_address_high &2f0a 8c 2a 2f STY &2f2a ; narrow_steep_edge_address_low &2f0d 8e 28 2f STX &2f28 ; narrow_steep_edge_horizontal_movement_op &2f10 a4 0c LDY &0c ; line_y_delta_low &2f12 c8 INY &2f13 a5 0c LDA &0c ; line_y_delta_low &2f15 4a LSR A &2f16 49 ff EOR #&ff &2f18 18 CLC &2f19 a6 3e LDX &3e ; start_vertex_screen_y_high # Does the line start outside the inner area vertically? &2f1b d0 63 BNE &2f80 ; process_narrow_steep_line_outside_inner_area &2f1d a6 18 LDX &18 ; start_vertex_screen_x_low # If not, &2f1f 4c 29 2f JMP &2f29 ; set_row_of_narrow_steep_line # process first row of line ; process_narrow_steep_line_loop &2f22 65 0d ADC &0d ; line_x_delta_low &2f24 90 03 BCC &2f29 ; set_row_of_narrow_steep_line &2f26 e5 0c SBC &0c ; line_y_delta_low ; narrow_steep_edge_horizontal_movement_op &2f28 e8 INX # Move right a column if moving right alone line # or DEX # Move left a column if moving left alone line ; set_row_of_narrow_steep_line &2f29 8e 00 5a STX &5a00 ; polygon_left_edge_table # Set left or right edge of row # actually STX narrow_steep_edge_address &2f2c ce 2a 2f DEC &2f2a ; narrow_steep_edge_address_low # Move down a row &2f2f f0 06 BEQ &2f37 ; set_polygon_needs_filling_to_zero # Stop at the bottom of inner area ; continue_processing_narrow_steep_line &2f31 88 DEY # or end of line &2f32 d0 ee BNE &2f22 ; process_narrow_steep_line_loop # Loop finishes with Y = 0 ; set_polygon_needs_filling &2f34 84 7f STY &7f ; polygon_needs_filling # Set to zero to indicate something to plot ; leave &2f36 60 RTS ; set_polygon_needs_filling_to_zero &2f37 a0 00 LDY #&00 # Set to zero to indicate something to plot &2f39 f0 f9 BEQ &2f34 ; set_polygon_needs_filling # Always branches ; process_narrow_shallow_line # If x_delta > y_delta, &2f3b 8d 79 2f STA &2f79 ; narrow_shallow_edge_address_high &2f3e 8c 78 2f STY &2f78 ; narrow_shallow_edge_address_low &2f41 8e 6b 2f STX &2f6b ; narrow_shallow_edge_horizontal_movement_op &2f44 a0 07 LDY #&07 &2f46 c9 5a CMP #&5a ; polygon_left_edge_table DIV 256 &2f48 f0 06 BEQ &2f50 ; line_is_sloping_down ; line_is_sloping_up &2f4a e0 ca CPX #&ca ; DEX # If moving left along line, &2f4c f0 06 BEQ &2f54 ; use_alternative_branch &2f4e d0 06 BNE &2f56 ; set_narrow_shallow_edge_branch ; line_is_sloping_down &2f50 e0 ca CPX #&ca ; DEX # If moving left along line, &2f52 f0 02 BEQ &2f56 ; set_narrow_shallow_edge_branch ; use_alternative_branch &2f54 a0 0a LDY #&0a ; set_narrow_shallow_edge_branch &2f56 8c 6f 2f STY &2f6f ; narrow_shallow_edge_branch + 1 # Set offset for branch &2f59 a4 0d LDY &0d ; line_x_delta_low &2f5b c8 INY &2f5c a5 0d LDA &0d ; line_x_delta_low &2f5e 4a LSR A &2f5f 49 ff EOR #&ff &2f61 18 CLC &2f62 a6 3e LDX &3e ; start_vertex_screen_y_high # Does the line start outside the inner area vertically? &2f64 d0 40 BNE &2fa6 ; process_narrow_shallow_line_outside_inner_area &2f66 a6 18 LDX &18 ; start_vertex_screen_x_low # If not, &2f68 4c 77 2f JMP &2f77 ; set_row_of_narrow_shallow_line # process first row of line ; process_narrow_shallow_line_loop ; narrow_shallow_edge_horizontal_movement_op &2f6b e8 INX # Move right a column if moving right alone line # or DEX # Move left a column if moving left alone line &2f6c 65 0c ADC &0c ; line_y_delta_low ; narrow_shallow_edge_branch # If carry clear, then &2f6e 90 07 BCC &2f77 ; set_row_of_narrow_shallow_line # set row if line slowing down to left or up to right # or BCC &2f7a ; skip_setting_row_of_narrow_shallow_line # skip row if line sloping up to left or down to right &2f70 e5 0d SBC &0d ; line_x_delta_low &2f72 ce 78 2f DEC &2f78 ; narrow_shallow_edge_address_low # Move down a row &2f75 f0 c0 BEQ &2f37 ; set_polygon_needs_filling_to_zero # Stop at the bottom of inner area ; set_row_of_narrow_shallow_line &2f77 8e 00 5a STX &5a00 ; polygon_left_edge_table # Set left or right edge of row # actually STX narrow_shallow_edge_address ; skip_setting_row_of_narrow_shallow_line &2f7a 88 DEY # Stop at end of line &2f7b d0 ee BNE &2f6b ; process_narrow_shallow_line_loop # Loop finishes with Y = 0 &2f7d 4c 34 2f JMP &2f34 ; set_polygon_needs_filling # Zero to indicate something to plot ; process_narrow_steep_line_outside_inner_area # Steep line is vertically outside inner area &2f80 ee 2a 2f INC &2f2a ; narrow_steep_edge_address_low # Move up a row &2f83 ae 28 2f LDX &2f28 ; narrow_steep_edge_horizontal_movement_op &2f86 8e 94 2f STX &2f94 ; narrow_steep_edge_outside_horizontal_movement_op &2f89 a6 18 LDX &18 ; start_vertex_screen_x_low &2f8b 4c 95 2f JMP &2f95 ; move_to_next_row_of_narrow_steep_line ; process_steep_line_outside_loop &2f8e 65 0d ADC &0d ; line_x_delta_low &2f90 90 03 BCC &2f95 ; move_to_next_row_of_narrow_steep_line &2f92 e5 0c SBC &0c ; line_y_delta_low ; narrow_steep_edge_outside_horizontal_movement_op &2f94 e8 INX ; if moving right alone line # Move right or left a column # or DEX ; if moving left along line ; move_to_next_row_of_narrow_steep_line &2f95 ce 2a 2f DEC &2f2a ; narrow_steep_edge_address_low # Move down a row &2f98 f0 06 BEQ &2fa0 ; return_to_narrow_steep_line_inside # Continue until line goes back inside inner area &2f9a 88 DEY &2f9b d0 f1 BNE &2f8e ; process_steep_line_outside_loop # or the end of the line &2f9d 4c 36 2f JMP &2f36 ; leave ; return_to_narrow_steep_line_inside &2fa0 ce 2a 2f DEC &2f2a ; narrow_steep_edge_address_low # Move down a row &2fa3 4c 31 2f JMP &2f31 ; continue_processing_narrow_steep_line # Continue processing line inside inner area ; process_narrow_shallow_line_outside_inner_area # Shallow line is vertically outside inner area &2fa6 ee 78 2f INC &2f78 ; narrow_shallow_edge_address_low # Move up a row &2fa9 ae 6b 2f LDX &2f6b ; narrow_shallow_edge_horizontal_movement_op &2fac 8e b4 2f STX &2fb4 ; narrow_shallow_edge_outside_horizontal_movement_op &2faf a6 18 LDX &18 ; start_vertex_screen_x_low &2fb1 4c c0 2f JMP &2fc0 ; start_processing_shallow_line_outside ; process_shallow_line_outside_loop ; narrow_shallow_edge_outside_horizontal_movement_op &2fb4 e8 INX ; if moving right alone line # Move right or left a column # or DEX ; if moving left along line &2fb5 65 0c ADC &0c ; line_y_delta_low &2fb7 90 07 BCC &2fc0 ; start_processing_shallow_line_outside &2fb9 e5 0d SBC &0d ; line_x_delta_low &2fbb ce 78 2f DEC &2f78 ; narrow_shallow_edge_address_low # Move down a row &2fbe f0 06 BEQ &2fc6 ; continue_processing_shallow_line_inside # Continue until line goes back inside inner area ; start_processing_shallow_line_outside &2fc0 88 DEY &2fc1 d0 f1 BNE &2fb4 ; process_shallow_line_outside_loop # or the end of the line &2fc3 4c 36 2f JMP &2f36 ; leave ; continue_processing_shallow_line_inside &2fc6 ce 78 2f DEC &2f78 ; narrow_shallow_edge_address_low # Move down a row &2fc9 4c 7a 2f JMP &2f7a ; continue_processing_shallow_line # Continue processing line inside inner area ; process_line # Splits the line in manageable sections, then processes &2fcc 86 0e STX &0e ; end_vertex &2fce a9 00 LDA #&00 &2fd0 85 40 STA &40 ; sections_minus_one &2fd2 b9 a0 54 LDA &54a0,Y ; plottables_screen_x_low # Calculate difference in x co-ordinates &2fd5 38 SEC &2fd6 fd a0 54 SBC &54a0,X ; plottables_screen_x_low &2fd9 85 74 STA &74 ; a_fraction &2fdb b9 40 0b LDA &0b40,Y ; plottables_screen_x_high &2fde fd 40 0b SBC &0b40,X ; plottables_screen_x_high &2fe1 85 0a STA &0a ; line_x_delta_high &2fe3 20 07 10 JSR &1007 ; invert_A_and_a_fraction_if_negative &2fe6 85 75 STA &75 ; a # a.a_fraction contains unsigned line_x_delta &2fe8 05 76 ORA &76 ; line_y_delta_high # line_y_delta is also unsigned, so A is a maximum &2fea f0 0e BEQ &2ffa ; skip_calculating_sections # Does the line need to be split into sections? ; calculate_sections_loop # If so, calculate number of sections required &2fec 46 76 LSR &76 ; line_y_delta_high # Halve line_y_delta &2fee 66 0c ROR &0c ; line_y_delta_low &2ff0 46 75 LSR &75 ; a # Halve a &2ff2 66 74 ROR &74 ; a_fraction &2ff4 38 SEC &2ff5 26 40 ROL &40 ; sections_minus_one # Turn 0 into 1, then double subsequently &2ff7 4a LSR A # Halve maximum delta &2ff8 d0 f2 BNE &2fec ; calculate_sections_loop # Repeat until x and y differences fit within one byte ; skip_calculating_sections &2ffa a6 0c LDX &0c ; line_y_delta_low # Halve both again if y almost overflows byte &2ffc e0 ff CPX #&ff &2ffe f0 ec BEQ &2fec ; scaling_loop &3000 a6 74 LDX &74 ; a_fraction # Halve both again if x almost overflows byte &3002 e0 ff CPX #&ff &3004 f0 e6 BEQ &2fec ; scaling_loop &3006 a5 75 LDA &75 ; a &3008 24 0a BIT &0a ; line_x_delta_high &300a 20 07 10 JSR &1007 ; invert_A_and_a_fraction_if_negative # Restore the original sign to a.a_fraction &300d 85 43 STA &43 ; section_x_delta_high &300f a5 74 LDA &74 ; a_fraction &3011 85 3a STA &3a ; section_x_delta_low &3013 b9 a0 54 LDA &54a0,Y ; plottables_screen_x_low # Y is the start vertex &3016 85 39 STA &39 ; end_vertex_screen_x_low &3018 b9 40 0b LDA &0b40,Y ; plottables_screen_x_high &301b 85 42 STA &42 ; end_vertex_screen_x_high &301d b9 e0 0a LDA &0ae0,Y ; plottables_screen_y_high &3020 85 3f STA &3f ; end_vertex_screen_y_high &3022 b9 80 0a LDA &0a80,Y ; plottables_screen_y_low &3025 85 16 STA &16 ; end_vertex_screen_y_low &3027 a5 40 LDA &40 ; sections_minus_one &3029 f0 29 BEQ &3054 ; process_end_section ; process_sections_loop # If the line requires more than one section, &302b a5 16 LDA &16 ; end_vertex_screen_y_low &302d 85 1a STA &1a ; start_vertex_screen_y_low # Starting at the start vertex, &302f 38 SEC &3030 e5 0c SBC &0c ; line_y_delta_low # move along the line &3032 85 16 STA &16 ; end_vertex_screen_y_low # to set end vertex for this section &3034 a5 3f LDA &3f ; end_vertex_screen_y_high &3036 85 3e STA &3e ; start_vertex_screen_y_high &3038 e9 00 SBC #&00 &303a 85 3f STA &3f ; end_vertex_screen_y_high &303c a5 39 LDA &39 ; end_vertex_screen_x_low &303e 85 18 STA &18 ; start_vertex_screen_x_low &3040 38 SEC &3041 e5 3a SBC &3a ; section_x_delta_low &3043 85 39 STA &39 ; end_vertex_screen_x_low &3045 a5 42 LDA &42 ; end_vertex_screen_x_high &3047 85 41 STA &41 ; start_vertex_screen_x_high &3049 e5 43 SBC &43 ; section_x_delta_high &304b 85 42 STA &42 ; end_vertex_screen_x_high &304d 20 ae 2e JSR &2eae ; process_line_section # Process the section &3050 c6 40 DEC &40 ; sections_minus_one &3052 d0 d7 BNE &302b ; process_sections_loop ; process_end_section &3054 a5 16 LDA &16 ; end_vertex_screen_y_low # Use the end of the penultimate section &3056 85 1a STA &1a ; start_vertex_screen_y_low # as the start of the final section &3058 a5 3f LDA &3f ; end_vertex_screen_y_high &305a 85 3e STA &3e ; start_vertex_screen_y_high &305c a5 39 LDA &39 ; end_vertex_screen_x_low &305e 85 18 STA &18 ; start_vertex_screen_x_low &3060 a5 42 LDA &42 ; end_vertex_screen_x_high &3062 85 41 STA &41 ; start_vertex_screen_x_high &3064 a6 0e LDX &0e ; end_vertex &3066 bd 80 0a LDA &0a80,X ; plottables_screen_y_low # Use the end of the line &3069 85 16 STA &16 ; end_vertex_screen_y_low # as the end of the final section &306b bd e0 0a LDA &0ae0,X ; plottables_screen_y_high &306e 85 3f STA &3f ; end_vertex_screen_y_high &3070 bd a0 54 LDA &54a0,X ; plottables_screen_x_low &3073 85 39 STA &39 ; end_vertex_screen_x_low &3075 bd 40 0b LDA &0b40,X ; plottables_screen_x_high &3078 85 42 STA &42 ; end_vertex_screen_x_high &307a a5 1a LDA &1a ; start_vertex_screen_y_low # Recalculate slope of line for final section &307c 38 SEC &307d e5 16 SBC &16 ; end_vertex_screen_y_low &307f 85 0c STA &0c ; line_y_delta_low &3081 20 ae 2e JSR &2eae ; process_line_section &3084 4c 56 2e JMP &2e56 ; consider_next_line ; process_wide_line # If line goes outside inner area horizontally, &3087 a5 18 LDA &18 ; start_vertex_screen_x_low &3089 38 SEC &308a e5 39 SBC &39 ; end_vertex_screen_x_low &308c 85 0d STA &0d ; line_x_delta_low &308e a5 41 LDA &41 ; start_vertex_screen_x_high &3090 e5 42 SBC &42 ; end_vertex_screen_x_high &3092 10 10 BPL &30a4 ; line_goes_right_to_left ; line_goes_left_to_right # If the line goes from left to right, &3094 a9 00 LDA #&00 # Ensure line_x_delta_low is positive &3096 38 SEC &3097 e5 0d SBC &0d ; line_x_delta_low &3099 85 0d STA &0d ; line_x_delta_low &309b a2 e8 LDX #&e8 ; INX # Move right when moving down or along line &309d a9 00 LDA #&00 &309f a0 e6 LDY #&e6 ; INC &.. &30a1 4c aa 30 JMP &30aa ; consider_wide_line_slope ; line_goes_right_to_left # If the line goes from right to left, &30a4 a2 ca LDX #&ca ; DEX # Move left when moving down or along line &30a6 a9 ff LDA #&ff &30a8 a0 c6 LDY #&c6 ; DEC &.. ; consider_wide_line_slope &30aa 84 74 STY &74 ; next_x_high_op &30ac 85 76 STA &76 ; first_x_low_in_new_x_high &30ae a4 0d LDY &0d ; line_x_delta_low # Does the line slope more vertically or horizontally? &30b0 c4 0c CPY &0c ; line_y_delta_low &30b2 a4 1a LDY &1a ; start_vertex_screen_y_low &30b4 a5 02 LDA &02 ; edge_table_address_high &30b6 b0 5a BCS &3112 ; process_wide_shallow_line ; process_wide_steep_line # If y_delta > x_delta, &30b8 8d eb 30 STA &30eb ; wide_steep_edge_address_high &30bb 8e e3 30 STX &30e3 ; process_wide_steep_line_inx_low_op &30be a5 3e LDA &3e ; start_vertex_screen_y_high &30c0 f0 01 BEQ &30c3 ; skip_row &30c2 c8 INY # If the line isn't in the inner area, add a row ; skip_row &30c3 8c ea 30 STY &30ea ; wide_steep_edge_address_low &30c6 a5 74 LDA &74 ; next_x_high_op &30c8 8d 0a 31 STA &310a ; process_wide_steep_line_inx_high_op &30cb a4 0c LDY &0c ; line_y_delta_low &30cd 98 TYA &30ce 4a LSR A &30cf 49 ff EOR #&ff &30d1 18 CLC &30d2 c8 INY &30d3 84 75 STY &75 ; rows_to_process &30d5 a6 18 LDX &18 ; start_vertex_screen_x_low &30d7 20 6e 31 JSR &316e ; set_wide_line_store_ops_and_edge # Determine whether to plot depending on where line is &30da 4c e9 30 JMP &30e9 ; set_row_of_wide_steep_line ; process_wide_steep_line_loop &30dd 65 0d ADC &0d ; line_x_delta_low &30df 90 08 BCC &30e9 ; set_row_of_wide_steep_line &30e1 e5 0c SBC &0c ; line_y_delta_low ; process_wide_steep_line_inx_low_op &30e3 e8 INX # Move right a column if moving right alone line # or DEX # Move left a column if moving left alone line &30e4 e4 76 CPX &76 ; first_x_low_in_new_x_high &30e6 18 CLC &30e7 f0 21 BEQ &310a ; process_wide_steep_line_next_x_high # Has the line moved into or out of the inner area? ; set_row_of_wide_steep_line ; process_wide_steep_line_store_op # If not, set left or right edge of row &30e9 8e 00 5a STX &5a00 ; wide_steep_edge_address # use line if line is entirely inside inner area # or STY &5a00 ; wide_steep_edge_address # use edge if line is vertically inside inner area # or BIT &5a00 ; wide_steep_edge_address # do nothing if line is entirely outside inner area &30ec ce ea 30 DEC &30ea ; wide_steep_able_address_low # Move down a row &30ef f0 07 BEQ &30f8 ; move_to_next_area_of_wide_steep_line # Has the line moved into or out of the inner area? ; move_to_next_row_of_wide_steep_line &30f1 c6 75 DEC &75 ; rows_to_process # If not, &30f3 d0 e8 BNE &30dd ; process_wide_steep_line_loop # continue unless end of line &30f5 4c 36 2f JMP &2f36 ; leave ; move_to_next_area_of_wide_steep_line &30f8 c6 3e DEC &3e ; start_vertex_screen_y_high &30fa 10 03 BPL &30ff ; to_next_row_of_wide_steep_line # Stop at bottom of inner area &30fc 4c 36 2f JMP &2f36 ; leave ; to_next_row_of_wide_steep_line &30ff d0 f0 BNE &30f1 ; move_to_next_row_of_wide_steep_line # Has the line moved into or out of the inner area? &3101 ce ea 30 DEC &30ea ; wide_steep_edge_address_low # Move down a row &3104 20 6e 31 JSR &316e ; set_wide_line_store_ops_and_edge # If so, determine whether to start or continue plotting &3107 4c f1 30 JMP &30f1 ; move_to_next_row_of_wide_steep_line ; process_wide_steep_line_next_x_high ; process_wide_steep_line_inx_high_op &310a e6 41 INC &41 ; start_vertex_screen_x_high # Move right into new area if moving right along line # or DEC &41 ; start_vertex_screen_x_high # Move left into new area if moving left along line &310c 20 6e 31 JSR &316e ; set_wide_line_store_ops_and_edge # Determine whether to start or continue plotting &310f 4c e9 30 JMP &30e9 ; set_row_of_wide_steep_line ; process_wide_shallow_line # If x_delta > y_delta, &3112 8d 4a 31 STA &314a ; wide_shallow_edge_address_high &3115 8e 37 31 STX &3137 ; process_wide_shallow_line_inx_low_op &3118 a5 3e LDA &3e ; start_vertex_screen_y_high &311a f0 01 BEQ &311d ; skip_row &311c c8 INY # If the line isn't in the inner area, add a row ; skip_row &311d 8c 49 31 STY &3149 ; wide_shallow_edge_address_low &3120 a5 74 LDA &74 ; next_x_high_op &3122 8d 64 31 STA &3164 ; process_wide_shallow_line_next_x_high &3125 a4 0d LDY &0d ; line_x_delta_low &3127 98 TYA &3128 4a LSR A &3129 49 ff EOR #&ff &312b 18 CLC &312c c8 INY &312d 84 75 STY &75 ; rows_to_process &312f a6 18 LDX &18 ; start_vertex_screen_x_low &3131 20 6e 31 JSR &316e ; set_wide_line_store_ops_and_edge # Determine whether to plot depending on where line is &3134 4c 48 31 JMP &3148 ; set_row_of_wide_shallow_line ; process_wide_steep_line_loop ; process_wide_shallow_line_inx_low_op &3137 e8 INX # Move right a column if moving right alone line # or DEX # Move left a column if moving left alone line &3138 e4 76 CPX &76 ; first_x_low_in_new_x_high &313a 18 CLC &313b f0 27 BEQ &3164 ; process_wide_shallow_line_next_x_high # Has the line moved into or out of the inner area? ; to_set_row_of_wide_shallow_line &313d 65 0c ADC &0c ; line_y_delta_low &313f 90 07 BCC &3148 ; set_row_of_wide_shallow_line &3141 e5 0d SBC &0d ; line_x_delta_low &3143 ce 49 31 DEC &3149 ; wide_shallow_edge_address_low # Move down a row &3146 f0 0a BEQ &3152 ; move_to_next_area_of_wide_shallow_line # Has the line moved into or out of the inner area? ; set_row_of_wide_shallow_line ; process_wide_shallow_line_store_op &3148 8e 00 5a STX &5a00 ; wide_shallow_edge_address # use line if line is entirely inside inner area # or STY &5a00 ; wide_shallow_edge_address # use edge if line is vertically inside inner area # or BIT &5a00 ; wide_shallow_edge_address # do nothing if line is entirely outside inner area &314b c6 75 DEC &75 ; rows_to_process &314d d0 e8 BNE &3137 ; process_wide_steep_line_loop # Stop at end of line &314f 4c 36 2f JMP &2f36 ; leave ; move_to_next_area_of_wide_shallow_line &3152 c6 3e DEC &3e ; start_vertex_screen_y_high &3154 10 03 BPL &3159 ; move_to_next_row_of_wide_shallow_line # Stop at bottom of inner area &3156 4c 36 2f JMP &2f36 ; leave ; move_to_next_row_of_wide_shallow_line &3159 d0 ed BNE &3148 ; set_row_of_wide_shallow_line # Has the line moved into or out of the inner area? &315b ce 49 31 DEC &3149 ; wide_shallow_edge_address_low # Move down a row &315e 20 6e 31 JSR &316e ; set_wide_line_store_ops_and_edge # If so, determine whether to start or continue plotting &3161 4c 48 31 JMP &3148 ; set_row_of_wide_shallow_line ; process_wide_shallow_line_next_x_high ; process_wide_shallow_line_inx_high_op &3164 e6 41 INC &41 ; start_vertex_screen_x_high # Move right into new area if moving right along line # or DEC &41 ; start_vertex_screen_x_high # Move left into new area if moving left along line &3166 20 6e 31 JSR &316e ; set_wide_line_store_ops_and_edge # Determine whether to start or continue plotting &3169 4c 3d 31 JMP &313d ; to_set_row_of_wide_shallow_line ; to_prnd_if_carry_clear # Always called with carry clear &316c 90 26 BCC &3194 ; prnd # Always branches ; set_wide_line_store_ops_and_edge &316e 48 PHA &316f a5 3e LDA &3e ; start_vertex_screen_y_high &3171 f0 04 BEQ &3177 ; inside_inner_area_vertically &3173 a9 2c LDA #&2c ; BIT &.... # If the line is outside inner area, nothing to plot &3175 d0 15 BNE &318c ; set_wide_line_store_ops # Always branches ; inside_inner_area_vertically &3177 85 7f STA &7f ; polygon_needs_filling # Set to zero to indicate something to plot &3179 a5 41 LDA &41 ; start_vertex_screen_x_high &317b d0 04 BNE &3181 ; outside_inner_area_horizontally &317d a9 8e LDA #&8e ; STX &.... # Use x position of line for plotting &317f d0 0b BNE &318c ; set_wide_line_store_ops # Always branches ; outside_inner_area_horizontally &3181 10 05 BPL &3188 ; right_of_inner_area_horizontally ; left_of_inner_area_horizontally &3183 a0 00 LDY #&00 # Clip plotted lines to left edge of inner area &3185 4c 8a 31 JMP &318a ; use_edge_of_inner_area ; right_of_inner_area_horizontally &3188 a0 ff LDY #&ff # Clip plotted lines to right edge of inner area ; use_edge_of_inner_area &318a a9 8c LDA #&8c ; STY &.... # Use edge of inner area for plotting ; set_wide_line_store_ops &318c 8d e9 30 STA &30e9 ; process_wide_steep_line_store_op &318f 8d 48 31 STA &3148 ; process_wide_shallow_line_store_op &3192 68 PLA &3193 60 RTS ; prnd &3194 8c bc 31 STY &31bc ; tmp_y # Preserve Y on exit &3197 a0 08 LDY #&08 ; shuffle_prnd_state_loop &3199 ad 7d 0c LDA &0c7d ; prnd_state + 2 &319c 4a LSR A &319d 4a LSR A &319e 4a LSR A &319f 4d 7f 0c EOR &0c7f ; prnd_state + 4 &31a2 6a ROR A &31a3 2e 7b 0c ROL &0c7b ; prnd_state &31a6 2e 7c 0c ROL &0c7c ; prnd_state + 1 &31a9 2e 7d 0c ROL &0c7d ; prnd_state + 2 &31ac 2e 7e 0c ROL &0c7e ; prnd_state + 3 &31af 2e 7f 0c ROL &0c7f ; prnd_state + 4 &31b2 88 DEY &31b3 d0 e4 BNE &3199 ; shuffle_prnd_state_loop &31b5 ac bc 31 LDY &31bc ; tmp_y &31b8 ad 7f 0c LDA &0c7f ; prnd_state + 4 &31bb 60 RTS ; tmp_y &31bc 00 ; plot_number_big_or_small_characters &31bd 18 CLC &31be 69 30 ADC #&30 ; plot_character_big_or_small_characters &31c0 c9 30 CMP #&30 ; "0" &31c2 d0 02 BNE &31c6 ; not_zero &31c4 a9 4f LDA #&4f ; "O" # Change number zero to capital letter O ; not_zero &31c6 2c 60 0c BIT &0c60 ; big_or_small_characters &31c9 30 03 BMI &31ce ; plot_big_character # Plot big characters if top bit set &31cb 4c 44 57 JMP &5744 ; plot_small_character ; plot_big_character &31ce 48 PHA &31cf 8d 10 0c STA &0c10 ; character_buffer # Set character to read definition for &31d2 29 c0 AND #&c0 &31d4 10 0f BPL &31e5 ; is_character # If &80 set, set x or y position ; set_position &31d6 0a ASL A &31d7 0a ASL A &31d8 68 PLA &31d9 29 1f AND #&1f &31db b0 04 BCS &31e1 ; set_y_position # If &40 set (i.e. &c0), set y position ; set_x_position &31dd 8d 49 0c STA &0c49 ; big_text_x_position # If &40 clear (i.e. &80), set x position &31e0 60 RTS ; set_y_position &31e1 8d 4a 0c STA &0c4a ; big_text_y_position &31e4 60 RTS ; is_character &31e5 8a TXA &31e6 48 PHA &31e7 98 TYA &31e8 48 PHA &31e9 a0 0c LDY #&0c # Get OS font data &31eb a2 10 LDX #&10 ; &0c10 = character_buffer &31ed a9 0a LDA #&0a ; Read character definition &31ef 20 f1 ff JSR &fff1 ; OSWORD &31f2 ad 4a 0c LDA &0c4a ; big_text_y_position &31f5 85 26 STA &26 ; y_position &31f7 a2 07 LDX #&07 ; consider_corrupting_secret_code &31f9 bd 6e 0c LDA &0c6e,X ; landscape_plus_one_hundred_high - 7 &31fc dd 36 0f CMP &0f36,X ; copy_of_landscape_high_from_prnd - 7 # See &18b0. If the game has not been played, or &31ff b0 03 BCS &3204 ; skip_secret_code_corruption # tampering has occurred to make next landscape over &3201 20 6c 31 JSR &316c ; to_prnd_if_carry_clear # 100 higher than the last, corrupt the secret code ; skip_secret_code_corruption ; plot_big_character_column_loop &3204 1e 10 0c ASL &0c10,X ; character_buffer &3207 ad 49 0c LDA &0c49 ; big_text_x_position &320a 85 24 STA &24 ; tile_column &320c a9 04 LDA #&04 &320e 85 15 STA &15 ; blocks_to_plot ; plot_big_character_row_loop &3210 1e 10 0c ASL &0c10,X ; character_buffer # Take two bits of the character definition at a time &3213 2a ROL A &3214 1e 10 0c ASL &0c10,X ; character_buffer &3217 2a ROL A &3218 29 03 AND #&03 &321a a8 TAY &321b b9 48 32 LDA &3248,Y ; block_type_table # Convert into a block &321e 48 PHA &321f 20 78 2b JSR &2b78 ; calculate_tile_address &3222 68 PLA &3223 91 5e STA (&5e),Y ; tile_address &3225 e6 24 INC &24 ; tile_column &3227 c6 15 DEC &15 ; blocks_to_plot &3229 d0 e5 BNE &3210 ; plot_big_character_row_loop &322b e6 26 INC &26 ; tile_row &322d ca DEX &322e 30 09 BMI &3239 ; plotted_big_character &3230 d0 d2 BNE &3204 ; plot_big_character_column_loop &3232 a9 00 LDA #&00 &3234 8d 10 0c STA &0c10 ; character_buffer &3237 f0 cb BEQ &3204 ; plot_big_character_column_loop # Always branches ; plotted_big_character &3239 ad 49 0c LDA &0c49 ; big_text_x_position &323c 18 CLC &323d 69 04 ADC #&04 # Move right four blocks &323f 8d 49 0c STA &0c49 ; big_text_x_position &3242 68 PLA &3243 a8 TAY &3244 68 PLA &3245 aa TAX &3246 68 PLA &3247 60 RTS ; block_type_table &3248 20 &3249 27 ; TYPE_BLOCK_RIGHT | &20 &324a 28 ; TYPE_BLOCK_LEFT | &20 &324b 29 ; TYPE_BLOCK_BOTH | &20 ; plot_title_or_completion_screen &324c 8d 8f 32 STA &328f ; landscape_was_completed # Top bit set if landscape completed &324f a9 80 LDA #&80 &3251 8d ff 09 STA &09ff ; objects_h_angle + &3f # Object &3f is used for the text blocks &3254 a9 e0 LDA #&e0 &3256 8d 3f 0a STA &0a3f ; objects_z_fraction + &3f &3259 a9 02 LDA #&02 &325b 8d 7f 09 STA &097f ; objects_z + &3f &325e 38 SEC &325f 6e 4b 0c ROR &0c4b ; in_title_screen # Set top bit to plot text blocks, not tiles &3262 a9 00 LDA #&00 # Wipe tile table with &00 &3264 20 f2 2a JSR &2af2 ; process_landscape &3267 2c 8f 32 BIT &328f ; landscape_was_completed &326a 10 09 BPL &3275 ; not_completed &326c 20 81 33 JSR &3381 ; plot_completion_code &326f a2 03 LDX #&03 # Use black background with stars &3271 a9 00 LDA #&00 ; TYPE_ROBOT # Use robot as hero &3273 f0 11 BEQ &3286 ; plot_screen_polygons ; not_completed &3275 a2 00 LDX #&00 ; plot_the_sentinel_text_loop &3277 bd 90 32 LDA &3290,X ; the_sentinel_text &327a 20 ce 31 JSR &31ce ; plot_big_character # Plot "THE SENTINEL" &327d e8 INX &327e e0 0f CPX #&0f &3280 90 f5 BCC &3277 ; plot_the_sentinel_text_loop &3282 a2 01 LDX #&01 # Use solid blue background &3284 a9 05 LDA #&05 ; TYPE_SENTINEL # Use sentinel as hero ; plot_screen_polygons &3286 a0 01 LDY #&01 # Use title screen viewpoint &3288 20 9b 13 JSR &139b ; plot_title_and_preview_screen_polygons &328b 4e 4b 0c LSR &0c4b ; in_title_screen # Clear top bit to indicate not plotting title screen &328e 60 RTS ; landscape_was_completed &328f 00 ; the_sentinel_text &3290 84 d5 ; Move to (&04, &15) &3292 54 48 45 ; "THE" &3295 80 c7 ; Move to (&00, &07) &3297 53 45 4e 54 49 4e 45 4c ; "SENTINEL" ; get_number_into_input_buffer &329f 85 74 STA &74 ; input_length &32a1 20 20 5e JSR &5e20 ; flush_keyboard &32a4 a0 07 LDY #&07 &32a6 a9 20 LDA #&20 ; " " ; wipe_input_buffer_loop &32a8 99 f0 0c STA &0cf0,Y ; input_buffer &32ab 88 DEY &32ac 10 fa BPL &32a8 ; wipe_input_buffer_loop &32ae 20 03 33 JSR &3303 ; plot_input_buffer ; reset_input_buffer &32b1 a0 00 LDY #&00 ; get_number_into_input_buffer_loop &32b3 20 0a 5e JSR &5e0a ; get_character &32b6 c9 0d CMP #&0d # Leave if RETURN pressed &32b8 f0 48 BEQ &3302 ; leave &32ba c9 30 CMP #&30 ; "0" &32bc 90 f5 BCC &32b3 ; get_number_into_input_buffer_loop &32be c9 7f CMP #&7f &32c0 90 19 BCC &32db ; is_character &32c2 d0 ef BNE &32b3 ; get_number_into_input_buffer_loop ; delete_pressed &32c4 88 DEY &32c5 30 ea BMI &32b1 ; reset_input_buffer &32c7 a2 00 LDX #&00 ; shorten_input_buffer_loop &32c9 bd f1 0c LDA &0cf1,X ; input_buffer + 1 # Rotate input buffer left a character &32cc 9d f0 0c STA &0cf0,X ; input_buffer &32cf e8 INX &32d0 e0 07 CPX #&07 &32d2 d0 f5 BNE &32c9 ; shorten_input_buffer_loop &32d4 a9 20 LDA #&20 &32d6 8d f7 0c STA &0cf7 ; input_buffer + 7 &32d9 d0 21 BNE &32fc ; replot_input_buffer # Always branches ; is_character &32db c9 3a CMP #&3a ; "9" + 1 &32dd b0 d4 BCS &32b3 ; get_number_into_input_buffer_loop &32df c4 74 CPY &74 ; input_length &32e1 d0 08 BNE &32eb ; add_character_to_input_buffer &32e3 a9 07 LDA #&07 # Sound bell if input too long &32e5 20 ee ff JSR &ffee ; OSWRCH &32e8 4c b3 32 JMP &32b3 ; get_number_into_input_buffer_loop ; add_character_to_input_buffer &32eb c8 INY &32ec 48 PHA &32ed a2 06 LDX #&06 ; lengthen_input_buffer_loop &32ef bd f0 0c LDA &0cf0,X ; input_buffer # Rotate input buffer right a character &32f2 9d f1 0c STA &0cf1,X ; input_buffer + 1 &32f5 ca DEX &32f6 10 f7 BPL &32ef ; lengthen_input_buffer_loop &32f8 68 PLA &32f9 8d f0 0c STA &0cf0 ; input_buffer ; replot_input_buffer &32fc 20 03 33 JSR &3303 ; plot_input_buffer &32ff 4c b3 32 JMP &32b3 ; get_number_into_input_buffer_loop ; leave &3302 60 RTS ; plot_input_buffer &3303 38 SEC &3304 6e 0f 0c ROR &0c0f ; force_single_colour_characters # Set top bit to plot characters in one colour &3307 a6 74 LDX &74 ; input_length &3309 ca DEX ; plot_input_buffer_loop &330a bd f0 0c LDA &0cf0,X ; input_buffer &330d 20 c0 31 JSR &31c0 ; plot_character_big_or_small_characters # Plot the contents of the buffer &3310 ca DEX &3311 10 f7 BPL &330a ; plot_input_buffer_loop &3313 a6 74 LDX &74 ; input_length &3315 a9 08 LDA #&08 ; backspace ; move_cursor_to_start_of_input &3317 20 c0 31 JSR &31c0 ; plot_character_big_or_small_characters # Move cursor back to start of buffer &331a ca DEX &331b d0 fa BNE &3317 ; move_cursor_to_start_of_input &331d 4e 0f 0c LSR &0c0f ; force_single_colour_characters # Clear top bit to plot characters in two colours &3320 60 RTS ; convert_input_buffer_to_number &3321 a0 00 LDY #&00 &3323 a2 00 LDX #&00 ; convert_input_buffer_to_number_loop &3325 20 3e 33 JSR &333e ; convert_character_to_number &3328 85 74 STA &74 ; digit &332a c8 INY &332b 20 3e 33 JSR &333e ; convert_character_to_number &332e 0a ASL A &332f 0a ASL A &3330 0a ASL A &3331 0a ASL A &3332 05 74 ORA &74 ; digit &3334 9d f0 0c STA &0cf0,X ; input_buffer &3337 e8 INX &3338 c8 INY &3339 c0 08 CPY #&08 &333b d0 e8 BNE &3325 ; convert_input_buffer_to_number_loop &333d 60 RTS ; convert_character_to_number &333e b9 f0 0c LDA &0cf0,Y ; input_buffer &3341 c0 04 CPY #&04 &3343 90 07 BCC &334c ; skip_clear &3345 48 PHA &3346 a9 ff LDA #&ff &3348 99 f0 0c STA &0cf0,Y ; input_buffer # Wipe last four digits of input buffer &334b 68 PLA ; skip_clear &334c c9 20 CMP #&20 ; " " &334e d0 02 BNE &3352 ; not_space &3350 a9 30 LDA #&30 ; "0" # SPACE is treated as zero ; not_space &3352 38 SEC &3353 e9 30 SBC #&30 ; "0" &3355 60 RTS ; plot_two_digit_number &3356 48 PHA &3357 4a LSR A &3358 4a LSR A &3359 4a LSR A &335a 4a LSR A &335b 20 bd 31 JSR &31bd ; plot_number_big_or_small_characters &335e 68 PLA &335f 29 0f AND #&0f &3361 4c bd 31 JMP &31bd ; plot_number_big_or_small_characters ; get_random_two_digit_bcd_number &3364 20 94 31 JSR &3194 ; prnd &3367 48 PHA &3368 29 0f AND #&0f &336a c9 0a CMP #&0a &336c 90 02 BCC &3370 ; skip_wraparound_low &336e e9 06 SBC #&06 # &0a - &0f becomes &04 - &09 ; skip_wraparound_low &3370 8d 80 33 STA &3380 ; digit &3373 68 PLA &3374 29 f0 AND #&f0 &3376 c9 a0 CMP #&a0 &3378 90 02 BCC &337c ; skip_wraparound_high &337a e9 60 SBC #&60 # &a0 - &f0 becomes &40 - &90 ; skip_wraparound_high &337c 0d 80 33 ORA &3380 ; digit &337f 60 RTS ; digit &3380 00 ; plot_completion_code &3381 a9 80 LDA #&80 # Set top bit to use big characters &3383 8d 60 0c STA &0c60 ; big_or_small_characters &3386 20 ce 31 JSR &31ce ; plot_big_character # Set x position &3389 a9 c7 LDA #&c7 # Set y position &338b 20 ce 31 JSR &31ce ; plot_big_character &338e 4e e6 0c LSR &0ce6 ; player_is_on_platform # Set to &06 when player is on platform &3391 ae e6 0c LDX &0ce6 ; player_is_on_platform # so X should be 3 if no tampering attempted ; plot_completion_code_loop &3394 20 64 33 JSR &3364 ; get_random_two_digit_bcd_number &3397 e0 04 CPX #&04 &3399 b0 03 BCS &339e ; skip_plot # Use the wrong numbers if tampering has been attempted &339b 20 56 33 JSR &3356 ; plot_two_digit_number ; skip_plot &339e ca DEX &339f 10 f3 BPL &3394 ; plot_completion_code_loop &33a1 8e e6 0c STX &0ce6 ; player_is_on_platform # Set to &ff &33a4 20 94 31 JSR &3194 ; prnd &33a7 4e 60 0c LSR &0c60 ; big_or_small_characters # Clear top bit to use small characters &33aa 60 RTS ; plot_landscape_number &33ab ad fe 0c LDA &0cfe ; landscape_number_high &33ae 20 56 33 JSR &3356 ; plot_two_digit_number &33b1 ad fd 0c LDA &0cfd ; landscape_number_low &33b4 4c 56 33 JMP &3356 ; plot_two_digit_number ; seed_prnd_from_landscape_number &33b7 8c 7c 0c STY &0c7c ; prnd_state + 1 # Use landscape number to seed random number generator &33ba 8e 7b 0c STX &0c7b ; prnd_state &33bd 8c fe 0c STY &0cfe ; landscape_number_high &33c0 8e fd 0c STX &0cfd ; landscape_number_low &33c3 8c 52 0c STY &0c52 ; zero_if_landscape_0000 &33c6 98 TYA &33c7 d0 0f BNE &33d8 ; allow_maximum_number_of_enemies # Allow maximum number of enemies after landscape 0099 &33c9 8a TXA &33ca 8d 52 0c STA &0c52 ; zero_if_landscape_0000 # Set to zero if landscape 0000, non-zero otherwise &33cd 4a LSR A &33ce 4a LSR A &33cf 4a LSR A &33d0 4a LSR A &33d1 18 CLC # On landscapes 0000 to 0099, &33d2 69 01 ADC #&01 # maximum number of enemies is 1 + (landscape DIV 10) &33d4 c9 09 CMP #&09 &33d6 90 02 BCC &33da ; skip_ceiling ; allow_maximum_number_of_enemies &33d8 a9 08 LDA #&08 # limited to 8, further limited at &1419 ; skip_ceiling &33da 8d 07 0c STA &0c07 ; maximum_number_of_enemies &33dd 60 RTS ; plot_character &33de c9 c8 CMP #&c8 # Characters &c8 and above are tokens for strings &33e0 b0 03 BCS &33e5 ; plot_string_from_token &33e2 4c 6a 57 JMP &576a ; plot_actual_character ; plot_string_from_token &33e5 e9 c8 SBC #&c8 # Use string (token - &c8) &33e7 aa TAX &33e8 98 TYA &33e9 48 PHA &33ea 20 ad 36 JSR &36ad ; plot_text &33ed 68 PLA &33ee a8 TAY &33ef 60 RTS ; get_maximum_number_of_enemies &33f0 ad fe 0c LDA &0cfe ; landscape_number_high &33f3 4a LSR A &33f4 4a LSR A &33f5 4a LSR A &33f6 4a LSR A &33f7 18 CLC &33f8 69 02 ADC #&02 # Add 2 to the topmost digit of the landscape number &33fa 85 74 STA &74 ; base # to use as a base for the maximum number of enemies ; get_value_between_zero_and_seven_loop &33fc 20 94 31 JSR &3194 ; prnd # Get a random number &33ff a0 07 LDY #&07 &3401 0a ASL A # Use its top bit as a sign &3402 08 PHP &3403 f0 06 BEQ &340b ; skip_count &3405 a0 ff LDY #&ff ; count_zero_bits_loop # Then count number of leading zero bits &3407 c8 INY &3408 0a ASL A &3409 90 fc BCC &3407 ; count_zero_bits_loop ; &340b 98 TYA &340c 28 PLP &340d 90 02 BCC &3411 ; skip_inversion &340f 49 ff EOR #&ff # Invert number if top bit was set ; skip_inversion &3411 18 CLC &3412 65 74 ADC &74 ; base # Add this to the base &3414 c9 08 CMP #&08 &3416 b0 e4 BCS &33fc ; get_value_between_zero_and_seven_loop &3418 69 01 ADC #&01 # Leave with value between 1 and 8, distributed around &341a 60 RTS # base, each step half as likely to occur as previous ; get_random_number_between_0_and_22 &341b 20 94 31 JSR &3194 ; prnd &341e 48 PHA &341f 29 07 AND #&07 # Low three bits of random number (0 - 7) &3421 85 74 STA &74 ; tmp &3423 68 PLA &3424 4a LSR A &3425 4a LSR A &3426 29 1e AND #&1e # Next five bits of random number (0 - 15) &3428 4a LSR A # Bottom bit (always zero) into carry &3429 65 74 ADC &74 ; tmp &342b 60 RTS # Return random number between 0 and 22 ; convert_player_energy_to_decimal &342c a9 00 LDA #&00 &342e ae 0a 0c LDX &0c0a ; player_energy &3431 f0 06 BEQ &3439 ; leave ; convert_player_energy_to_decimal_loop &3433 18 CLC &3434 69 01 ADC #&01 &3436 ca DEX &3437 d0 fa BNE &3433 ; convert_player_energy_to_decimal_loop ; leave &3439 60 RTS ; play_sound_with_envelope_and_pitches &343a 8e 04 59 STX &5904 ; sound_block_0 + 4 ; pitch &343d 8c 0c 59 STY &590c ; sound_block_1 + 4 ; pitch ; play_sound_with_envelope &3440 48 PHA &3441 38 SEC &3442 e9 01 SBC #&01 # Use envelope (n - 1) for sound n &3444 b0 02 BCS &3448 ; skip_floor &3446 69 01 ADC #&01 # Use envelope_0 for sound_0 and sound_1 ; skip_floor &3448 20 63 34 JSR &3463 ; set_envelope &344b 68 PLA &344c aa TAX &344d bd 79 34 LDA &3479,X ; sound_to_block_number_table &3450 c9 01 CMP #&01 &3452 d0 05 BNE &3459 ; play_sound &3454 20 59 34 JSR &3459 ; play_sound &3457 a9 00 LDA #&00 # Play sound_0 to accompany sound_1 ; play_sound &3459 0a ASL A &345a 0a ASL A &345b 0a ASL A &345c 69 00 ADC #&00 ; &5900 + sound_block_number * 8 &345e aa TAX &345f a9 07 LDA #&07 ; Generate a sound (SOUND) &3461 d0 10 BNE &3473 ; to_osword # Always branches ; set_envelope &3463 8d 78 34 STA &3478 ; envelope_number &3466 0a ASL A # Multiply envelope number by 14 &3467 0a ASL A &3468 0a ASL A &3469 38 SEC &346a ed 78 34 SBC &3478 ; envelope_number &346d 0a ASL A &346e 69 28 ADC #&28 ; &5928 + envelope_number * 14 &3470 aa TAX &3471 a9 08 LDA #&08 ; Define a sound envelope (ENVELOPE) ; to_osword &3473 a0 59 LDY #&59 &3475 4c f1 ff JMP &fff1 ; OSWORD ; envelope_number &3478 00 ; sound_to_block_number_table &3479 01 ; sound_0 # Rotating enemy &347a 01 ; sound_1 # Rotating meanie &347b 04 ; sound_2 # Creating or absorbing object &347c 02 ; sound_3 # Tune &347d 02 ; sound_4 # Player targeted &347e 03 ; sound_5 # Energy loss, bad action, changing volume &347f 01 ; sound_6 # Player dying ; consider_changing_volume &3480 ad e4 0c LDA &0ce4 ; busy_plotting # Top bit set if world is being plotted &3483 30 4e BMI &34d3 ; leave &3485 ad d4 34 LDA &34d4 ; game_volume &3488 ae eb 0c LDX &0ceb ; player_wants_to_change_volume_pause_or_unpause &348b f0 0b BEQ &3498 ; decrease_volume &348d ca DEX &348e d0 43 BNE &34d3 ; leave ; increase_volume &3490 c9 78 CMP #&78 &3492 b0 0a BCS &349e ; set_volume &3494 69 08 ADC #&08 &3496 d0 06 BNE &349e ; set_volume ; decrease_volume &3498 c9 00 CMP #&00 &349a f0 02 BEQ &349e ; set_volume &349c e9 08 SBC #&08 ; set_volume &349e ae df 0c LDX &0cdf ; sound_cooldown &34a1 e0 02 CPX #&02 &34a3 b0 db BCS &3480 ; consider_changing_volume &34a5 8d d4 34 STA &34d4 ; game_volume &34a8 a8 TAY &34a9 f0 02 BEQ &34ad ; skip_floor &34ab a0 08 LDY #&08 ; skip_floor &34ad 8c 5f 59 STY &595f ; envelope_3 + 13 (ALD, decay amplitude target) &34b0 8c 51 59 STY &5951 ; envelope_2 + 13 (ALD, decay amplitude target) &34b3 a0 0b LDY #&0b ; poke_volume_into_envelopes_loop &34b5 be d5 34 LDX &34d5,Y ; envelope_offsets # Poke volume into envelope data &34b8 e0 4f CPX #&4f &34ba d0 04 BNE &34c0 ; skip_inversion &34bc 49 ff EOR #&ff # Invert volume for decay amplitude change &34be 69 00 ADC #&00 ; skip_inversion &34c0 9d 28 59 STA &5928,X ; envelope_data &34c3 88 DEY &34c4 10 ef BPL &34b5 ; poke_volume_into_envelopes_loop &34c6 a9 0c LDA #&0c &34c8 8d df 0c STA &0cdf ; sound_cooldown &34cb a9 05 LDA #&05 ; sound_5 &34cd 20 40 34 JSR &3440 ; play_sound_with_envelope # Play sound for changing volume &34d0 4c 80 34 JMP &3480 ; consider_changing_volume ; leave &34d3 60 RTS ; game_volume &34d4 58 ; envelope_offsets &34d5 4f ; envelope_5 + 9 (AD, decay amplitude change) &34d6 0c ; envelope_0 + 12 (ALA, attack amplitude target) &34d7 0d ; envelope_0 + 13 (ALD, decay amplitude target) &34d8 1a ; envelope_1 + 12 (ALA, attack amplitude target) &34d9 1b ; envelope_1 + 13 (ALD, decay amplitude target) &34da 24 ; envelope_2 + 8 (AA, attack amplitude change) &34db 28 ; envelope_2 + 12 (ALA, attack amplitude target) &34dc 36 ; envelope_3 + 12 (ALA, attack amplitude target) &34dd 40 ; envelope_4 + 8 (AA, attack amplitude change) &34de 44 ; envelope_4 + 12 (ALA, attack amplitude target) &34df 4e ; envelope_5 + 8 (AA, attack amplitude change) &34e0 52 ; envelope_5 + 12 (ALA, attack amplitude target) ; consider_pausing_game &34e1 ad eb 0c LDA &0ceb ; player_wants_to_change_volume_pause_or_unpause &34e4 30 1e BMI &3504 ; leave &34e6 c9 02 CMP #&02 ; INTERFACE_PAUSE &34e8 d0 1a BNE &3504 ; leave &34ea 6e 72 0c ROR &0c72 ; game_is_paused # Set top bit to indicate game paused &34ed a9 08 LDA #&08 ; paused &34ef 20 2d 16 JSR &162d ; plot_paused_bar &34f2 20 48 35 JSR &3548 ; flush_all_sound_channels ; wait_for_unpause &34f5 ad eb 0c LDA &0ceb ; player_wants_to_change_volume_pause_or_unpause &34f8 c9 03 CMP #&03 ; INTERFACE_UNPAUSE &34fa d0 f9 BNE &34f5 ; wait_for_unpause &34fc a9 00 LDA #&00 ; unpaused &34fe 20 2d 16 JSR &162d ; plot_paused_bar &3501 4e 72 0c LSR &0c72 ; game_is_paused # Clear top bit to indicate game not paused ; leave &3504 60 RTS ; play_tune &3505 ae e7 0c LDX &0ce7 ; tune_position &3508 30 3d BMI &3547 ; leave &350a ee e7 0c INC &0ce7 ; tune_position &350d bd 50 58 LDA &5850,X ; tune_data # Get a byte of tune data &3510 c9 ff CMP #&ff &3512 f0 30 BEQ &3544 ; set_tune_position # &ff marks end of tune &3514 c9 c8 CMP #&c8 &3516 90 0a BCC &3522 ; set_pitch # &c8 and above sets length of subsequent notes &3518 e9 c8 SBC #&c8 &351a 0a ASL A &351b 0a ASL A &351c 8d 70 0c STA &0c70 ; note_length &351f 4c 05 35 JMP &3505 ; play_tune ; set_pitch &3522 8d 14 59 STA &5914 ; sound_block_2 + 4 ; pitch &3525 ad 70 0c LDA &0c70 ; note_length &3528 8d df 0c STA &0cdf ; sound_cooldown &352b ad 10 59 LDA &5910 ; sound_block_2 + 0 ; channel &352e 18 CLC &352f 69 01 ADC #&01 &3531 c9 14 CMP #&14 &3533 90 02 BCC &3537 ; skip_wraparound &3535 a9 11 LDA #&11 # Rotate tune through channels 1 to 3 ; skip_wraparound &3537 8d 10 59 STA &5910 ; sound_block_2 + 0 ; channel &353a a9 04 LDA #&04 &353c 8d 12 59 STA &5912 ; sound_block_2 + 2 ; volume &353f a9 03 LDA #&03 ; sound_3 &3541 4c 40 34 JMP &3440 ; play_sound_with_envelope # Play sound for tune (uses sound_block_2) ; set_tune_position &3544 8d e7 0c STA &0ce7 ; tune_position ; leave &3547 60 RTS ; flush_all_sound_channels &3548 a2 07 LDX #&07 ; sound channel 3 ; flush_all_sound_channels_loop &354a 20 55 35 JSR &3555 ; flush_buffer &354d ca DEX &354e e0 04 CPX #&04 &3550 b0 f8 BCS &354a ; flush_all_sound_channels_loop &3552 60 RTS ; flush_sound_channel_0 &3553 a2 04 LDX #&04 ; flush_buffer &3555 a9 15 LDA #&15 ; Flush selected buffer &3557 4c f4 ff JMP &fff4 ; OSBYTE ; update_sound &355a ad df 0c LDA &0cdf ; sound_cooldown # Has the previous sound finished? &355d d0 2b BNE &358a ; leave &355f ad 73 0c LDA &0c73 ; sound_type &3562 c9 04 CMP #&04 ; SOUND_TARGETED &3564 f0 28 BEQ &358e ; play_targeted_sound &3566 c9 03 CMP #&03 ; SOUND_TUNE &3568 f0 21 BEQ &358b ; to_play_tune &356a c9 06 CMP #&06 ; SOUND_DYING &356c d0 1c BNE &358a ; leave ; play_dying_sound &356e a2 07 LDX #&07 &3570 ac 74 0c LDY &0c74 ; dying_cooldown &3573 c0 50 CPY #&50 &3575 90 13 BCC &358a ; leave &3577 a9 06 LDA #&06 ; sound_6 &3579 20 3a 34 JSR &343a ; play_sound_with_envelope_and_pitches # Play sound for player dying &357c 20 94 31 JSR &3194 ; prnd &357f 29 03 AND #&03 &3581 18 CLC &3582 69 01 ADC #&01 &3584 8d df 0c STA &0cdf ; sound_cooldown # Dying sound has random lengths &3587 ce 74 0c DEC &0c74 ; dying_cooldown ; leave &358a 60 RTS ; to_play_tune &358b 4c 05 35 JMP &3505 ; play_tune ; play_targeted_sound &358e a9 32 LDA #&32 &3590 8d df 0c STA &0cdf ; sound_cooldown &3593 a9 22 LDA #&22 &3595 8d 14 59 STA &5914 ; sound_block_2 + 4 ; pitch &3598 a9 03 LDA #&03 &359a 8d 12 59 STA &5912 ; sound_block_2 + 2 ; volume &359d a9 04 LDA #&04 ; sound_4 # Play sound for player targeted (uses sound_block_2) &359f 20 40 34 JSR &3440 ; play_sound_with_envelope &35a2 60 RTS ; unused &35a3 b9 ; play_landscape &35a4 a9 83 LDA #&83 # Use level colour scheme &35a6 20 2c 5e JSR &5e2c ; set_palette &35a9 20 07 5e JSR &5e07 ; get_character_after_flushing_keyboard &35ac 4e fc 0c LSR &0cfc ; not_playing_game # Clear top bit to indicate game is active ; play_landscape_loop # This loops every time player changes position &35af 20 48 35 JSR &3548 ; flush_all_sound_channels &35b2 ad 64 0c LDA &0c64 ; player_quit_game # Top bit set if player quit game &35b5 10 03 BPL &35ba ; player_didn't_quit &35b7 4c 17 10 JMP &1017 ; title_screen # Return immediately to to title screen if player quit ; player_didn't_quit &35ba ad 4e 0c LDA &0c4e ; player_has_died # Top bit set if player died by draining or hyperspacing &35bd 30 5e BMI &361d ; retry_landscape &35bf a9 04 LDA #&04 ; B # Set all four colours to blue &35c1 20 2c 5e JSR &5e2c ; set_palette &35c4 a9 00 LDA #&00 # Use screen for plotting &35c6 85 55 STA &55 ; use_screen_or_buffer_for_plotting &35c8 85 08 STA &08 ; previous_panning_direction &35ca 8d c9 0c STA &0cc9 ; size_of_sights &35cd 8d 5f 0c STA &0c5f ; sights_are_active # Clear top bit to not plot sights &35d0 20 34 57 JSR &5734 ; set_bar_state_and_delay # Set bar to unpaused, short delay &35d3 a5 0b LDA &0b ; player_object &35d5 85 6e STA &6e ; object_to_consider # Player &35d7 2c de 0c BIT &0cde ; player_has_hyperspaced # Top bit set if player has just hyperspaced &35da 10 08 BPL &35e4 ; not_hyperspaced &35dc 70 4e BVS &362c ; landscape_completed # Second highest bit set if landscape was completed &35de 20 90 10 JSR &1090 ; fill_screen_with_background &35e1 4c f5 35 JMP &35f5 ; skip_replotting_world ; not_hyperspaced &35e4 ad 51 0c LDA &0c51 ; uturn_previously_pressed # If the player hasn't u-turned, &35e7 30 03 BMI &35ec ; skip_populating_tile_visibility_bit_table &35e9 20 63 24 JSR &2463 ; populate_tile_visibility_bit_table # repopulate the tile visibility table ; skip_populating_tile_visibility_bit_table &35ec 20 90 10 JSR &1090 ; fill_screen_with_background &35ef 20 24 26 JSR &2624 ; plot_world &35f2 20 c7 36 JSR &36c7 ; plot_status_bar ; skip_replotting_world &35f5 a9 19 LDA #&19 # Use buffer for plotting &35f7 85 55 STA &55 ; use_screen_or_buffer_for_plotting &35f9 a9 02 LDA #&02 ; BUFFER_TALL &35fb 20 63 29 JSR &2963 ; initialise_buffer_variables ; wait_for_end_of_tune &35fe 20 5a 35 JSR &355a ; update_sound # Finish any tune before revealing landscape &3601 ad e7 0c LDA &0ce7 ; tune_position &3604 10 f8 BPL &35fe ; wait_for_end_of_tune &3606 a9 83 LDA #&83 # Use landscape colour scheme &3608 20 2c 5e JSR &5e2c ; set_palette &360b ad de 0c LDA &0cde ; player_has_hyperspaced # Top bit clear if player hasn't hyperspaced &360e 10 56 BPL &3666 ; update_game_and_continue &3610 8d 4e 0c STA &0c4e ; player_has_died # Set top bit to indicate player died (by hyperspacing) &3613 a9 06 LDA #&06 ; SOUND_DYING &3615 8d 73 0c STA &0c73 ; sound_type # Start dying sound &3618 a9 05 LDA #&05 # Display death screen for 5 when killed by player &361a 20 24 5f JSR &5f24 ; death_screen ; retry_landscape &361d 20 49 11 JSR &1149 ; reset_game_state &3620 ac fe 0c LDY &0cfe ; landscape_number_high &3623 ae fd 0c LDX &0cfd ; landscape_number_low &3626 20 b7 33 JSR &33b7 ; seed_prnd_from_landscape_number &3629 4c 6c 10 JMP &106c; clear_screen_and_generate_landscape ; landscape_completed &362c a9 04 LDA #&04 ; B # Set all four colours to blue &362e 20 2c 5e JSR &5e2c ; set_palette &3631 a2 03 LDX #&03 &3633 a9 00 LDA #&00 ; SOUND_NONE &3635 8d 73 0c STA &0c73 ; sound_type ; zero_prnd_state_loop &3638 9d 7c 0c STA &0c7c,X ; prnd_state + 1 &363b ca DEX &363c 10 fa BPL &3638 ; zero_prnd_state_loop &363e 20 72 11 JSR &1172 ; reset_tile_visibility_bit_table_and_object_flags &3641 20 7e 1a JSR &1a7e ; next_landscape_screen # Display preview of next landscape &3644 a9 87 LDA #&87 # Use title colour scheme, B K R Y &3646 20 2c 5e JSR &5e2c ; set_palette &3649 a9 0a LDA #&0a &364b 8d df 0c STA &0cdf ; sound_cooldown &364e a9 42 LDA #&42 ; tune_4 # Play tune for success &3650 20 f6 5f JSR &5ff6 ; start_tune ; wait_for_tune # Wait for tune to finish &3653 20 5a 35 JSR &355a ; update_sound &3656 ad e7 0c LDA &0ce7 ; tune_position &3659 10 f8 BPL &3653 ; wait_for_tune &365b a2 06 LDX #&06 # "PRESS ANY KEY" &365d 20 ad 36 JSR &36ad ; plot_text &3660 20 07 5e JSR &5e07 ; get_character_after_flushing_keyboard ; to_title_screen &3663 4c 17 10 JMP &1017 ; title_screen ; update_game_and_continue &3666 20 64 12 JSR &1264 ; update_game # Return carry set if viewpoint has changed &3669 90 03 BCC &366e ; no_change_of_viewpoint &366b 4c af 35 JMP &35af ; play_landscape_loop # If carry set, either game over or a full redraw needed ; no_change_of_viewpoint &366e a5 09 LDA &09 ; panning_direction &3670 85 08 STA &08 ; previous_panning_direction &3672 a9 00 LDA #&00 &3674 8d d1 0c STA &0cd1 ; scrolling_steps_total &3677 8d 1e 0c STA &0c1e ; not_ready_to_dither # Clear top bit to permit dithering to occur &367a 2c 5f 0c BIT &0c5f ; sights_are_active &367d 30 04 BMI &3683 ; sights_are_active &367f 38 SEC &3680 6e 1b 0c ROR &0c1b ; force_pan_when_player_moves # Set top bit to check for change during plotting ; sights_are_active &3683 20 b7 10 JSR &10b7 ; pan_viewpoint &3686 4e 1b 0c LSR &0c1b ; force_pan_when_player_moves # Clear top bit &3689 20 c7 36 JSR &36c7 ; plot_status_bar &368c ad d1 0c LDA &0cd1 ; scrolling_steps_total &368f 8d c1 0c STA &0cc1 ; scrolling_steps_remaining ; wait_for_end_of_scrolling &3692 ad c1 0c LDA &0cc1 ; scrolling_steps_remaining &3695 d0 fb BNE &3692 ; wait_for_end_of_scrolling &3697 f0 cd BEQ &3666 ; ongoing # Always branches ; wipe_status_bar &3699 a9 00 LDA #&00 &369b 8d 05 0c STA &0c05 ; sprite_x ; wipe_status_bar_loop &369e a9 00 LDA #&00 ; SPRITE_EMPTY &36a0 20 3a 37 JSR &373a ; plot_sprite &36a3 ad 05 0c LDA &0c05 ; sprite_x &36a6 c9 28 CMP #&28 &36a8 90 f4 BCC &369e ; wipe_status_bar_loop &36aa 4c eb 3a JMP &3aeb ; unbuffer_status_bar ; plot_text &36ad bc 84 57 LDY &5784,X ; text_offsets ; plot_text_loop &36b0 b9 96 57 LDA &5796,Y ; text_strings &36b3 c9 ff CMP #&ff &36b5 f0 07 BEQ &36be ; leave &36b7 20 de 33 JSR &33de ; plot_character &36ba c8 INY &36bb 4c b0 36 JMP &36b0 ; plot_text_loop &36be 60 RTS ; pixel_masks ; 0 1 2 3 &36bf 77 bb dd ee # All bits set except for pixel ; unused ; 0 1 2 3 &36c2 88 44 22 11 # Colour 3 pixel values ; plot_status_bar &36c7 a9 00 LDA #&00 ; SPRITE_EMPTY &36c9 8d 05 0c STA &0c05 ; sprite_x &36cc 20 3a 37 JSR &373a ; plot_sprite &36cf ad 0a 0c LDA &0c0a ; player_energy &36d2 85 15 STA &15 ; energy_to_plot ; plot_super_robots &36d4 a5 15 LDA &15 ; energy_to_plot &36d6 c9 0f CMP #&0f &36d8 90 11 BCC &36eb ; plot_robots &36da e9 0f SBC #&0f &36dc 85 15 STA &15 ; energy_to_plot &36de a9 06 LDA #&06 ; SPRITE_SUPER_ROBOT &36e0 20 3a 37 JSR &373a ; plot_sprite # Plot super robot for every fifteen energy &36e3 a9 00 LDA #&00 ; SPRITE_EMPTY &36e5 20 3a 37 JSR &373a ; plot_sprite &36e8 4c d4 36 JMP &36d4 ; plot_super_robots ; plot_robots &36eb a5 15 LDA &15 ; energy_to_plot &36ed c9 03 CMP #&03 &36ef 90 11 BCC &3702 ; plot_remainder &36f1 e9 03 SBC #&03 &36f3 85 15 STA &15 ; energy_to_plot &36f5 a9 01 LDA #&01 ; SPRITE_ROBOT &36f7 20 3a 37 JSR &373a ; plot_sprite # Plot robot for every three energy &36fa a9 00 LDA #&00 ; SPRITE_EMPTY &36fc 20 3a 37 JSR &373a ; plot_sprite &36ff 4c eb 36 JMP &36eb ; plot_robots ; plot_remainder &3702 c9 01 CMP #&01 &3704 90 0a BCC &3710 ; plot_spaces &3706 0a ASL A &3707 20 3a 37 JSR &373a ; plot_sprite # Plot any remaining energy as boulder or tree &370a 18 CLC &370b 69 01 ADC #&01 &370d 20 3a 37 JSR &373a ; plot_sprite ; plot_spaces &3710 a9 00 LDA #&00 ; SPRITE_EMPTY &3712 20 3a 37 JSR &373a ; plot_sprite # Plot empty space before bar &3715 ad 05 0c LDA &0c05 ; sprite_x &3718 c9 1d CMP #&1d &371a 90 f4 BCC &3710 ; plot_spaces &371c a9 07 LDA #&07 ; SPRITE_BAR_LEFT &371e 20 3a 37 JSR &373a ; plot_sprite # Plot left of bar ; plot_middle_of_bar &3721 a9 08 LDA #&08 ; SPRITE_BAR_MIDDLE &3723 20 3a 37 JSR &373a ; plot_sprite # Plot middle of bar &3726 ad 05 0c LDA &0c05 ; sprite_x &3729 c9 26 CMP #&26 &372b 90 f4 BCC &3721 ; plot_middle_of_bar &372d a9 09 LDA #&09 ; SPRITE_BAR_RIGHT &372f 20 3a 37 JSR &373a ; plot_sprite # Plot right of bar &3732 a9 00 LDA #&00 ; SPRITE_EMPTY &3734 20 3a 37 JSR &373a ; plot_sprite # Plot empty space after bar &3737 4c eb 3a JMP &3aeb ; unbuffer_status_bar ; plot_sprite &373a 48 PHA &373b 0a ASL A &373c 0a ASL A &373d 0a ASL A &373e 09 07 ORA #&07 &3740 aa TAX &3741 ad 05 0c LDA &0c05 ; sprite_x &3744 0a ASL A &3745 0a ASL A &3746 69 00 ADC #&00 &3748 85 70 STA &70 ; screen_address_low &374a a9 2d LDA #&2d ; &5a00 (status_bar_buffer) / &200 &374c 69 00 ADC #&00 &374e 06 70 ASL &70 ; screen_address_low &3750 2a ROL A &3751 85 71 STA &71 ; screen_address_high &3753 a0 07 LDY #&07 ; plot_sprite_loop &3755 bd b0 58 LDA &58b0,X ; sprite_data &3758 91 70 STA (&70),Y ; screen_address &375a ca DEX &375b 88 DEY &375c 10 f7 BPL &3755 ; plot_sprite_loop &375e ee 05 0c INC &0c05 ; sprite_x &3761 68 PLA &3762 60 RTS ; to_previous_irq1_vector &3763 6c 01 0d JMP (&0d01) ; previous_irq1_vector ; irq1_handler &3766 78 SEI &3767 ad 6d fe LDA &fe6d ; User VIA interrupt flag register &376a 29 40 AND #&40 # Is this an interrupt from User VIA timer 1? &376c f0 f5 BEQ &3763 ; to_previous_irq1_vector ; timer_1_interrupt &376e 8d 6d fe STA &fe6d ; User VIA interrupt flag register # Clear interrupt &3771 a5 fc LDA &fc ; irq_accumulator &3773 48 PHA &3774 8a TXA &3775 48 PHA &3776 98 TYA &3777 48 PHA &3778 d8 CLD &3779 ce df 0c DEC &0cdf ; sound_cooldown &377c 10 03 BPL &3781 ; skip_floor &377e ee df 0c INC &0cdf ; sound_cooldown ; skip_floor &3781 ad fc 0c LDA &0cfc ; not_playing_game # Top bit set if game is inactive &3784 30 45 BMI &37cb ; leave_interrupt # Leave if not in game &3786 ad 4e 0c LDA &0c4e ; player_has_died # Top bit set if player died by draining or hyperspacing &3789 30 38 BMI &37c3 ; update_after_death &378b ad 72 0c LDA &0c72 ; game_is_paused # Top bit set if game is paused &378e 30 21 BMI &37b1 ; check_for_paused_input &3790 ad c1 0c LDA &0cc1 ; scrolling_steps_remaining &3793 f0 06 BEQ &379b ; no_scrolling_needed # Does the screen need scrolling? &3795 20 d1 37 JSR &37d1 ; scroll_screen_and_unbuffer_column_or_row &3798 20 eb 3a JSR &3aeb ; unbuffer_status_bar # Replot status bar after scrolling screen ; no_scrolling_needed &379b ad e5 0c LDA &0ce5 ; start_of_game_grace # Top bit set if player has not yet taken any action &379e 30 06 BMI &37a6 ; skip_enemies &37a0 20 ee 12 JSR &12ee ; update_enemy_cooldowns # Otherwise, update enemies &37a3 20 23 16 JSR &1623 ; update_bar ; skip_draining &37a6 ad e4 0c LDA &0ce4 ; busy_plotting # Top bit set if world is being plotted &37a9 30 20 BMI &37cb ; leave_interrupt &37ab 20 8b 11 JSR &118b ; check_for_full_player_input # Otherwise, check for input &37ae 4c cb 37 JMP &37cb ; leave_interrupt ; check_for_paused_input &37b1 a0 0d LDY #&0d # Check for all keys apart from u-turn &37b3 20 53 13 JSR &1353 ; check_for_player_input &37b6 a2 02 LDX #&02 &37b8 a9 80 LDA #&80 ; clear_player_wants &37ba 9d e8 0c STA &0ce8,X ; player_wants # Prevent player interacting with game &37bd ca DEX # but still allow pause / volume &37be 10 fa BPL &37ba ; clear_player_wants &37c0 4c cb 37 JMP &37cb ; leave_interrupt ; update_after_death &37c3 ad 4d 0c LDA &0c4d ; what_to_plot_behind_object_to_update # Top bit set if plotting only object, clear if world &37c6 10 03 BPL &37cb ; leave_interrupt &37c8 20 d9 56 JSR &56d9 ; fade_to_black # Change 80 random pixels to black ; leave_interrupt &37cb 68 PLA &37cc a8 TAY &37cd 68 PLA &37ce aa TAX &37cf 68 PLA &37d0 40 RTI ; scroll_screen_and_unbuffer_column_or_row &37d1 a4 08 LDY &08 ; previous_panning_direction &37d3 ad c2 0c LDA &0cc2 ; viewport_screen_address_low &37d6 18 CLC &37d7 79 e4 38 ADC &38e4,Y ; viewpoint_scrolling_change_low # Move left or right two pixels &37da 8d c2 0c STA &0cc2 ; viewport_screen_address_low # or up or down eight pixels &37dd ad c3 0c LDA &0cc3 ; viewport_screen_address_high &37e0 79 e8 38 ADC &38e8,Y ; viewpoint_scrolling_change_high &37e3 c9 80 CMP #&80 # Keep it within screen memory &37e5 90 05 BCC &37ec ; skip_overflow &37e7 e9 20 SBC #&20 &37e9 4c f2 37 JMP &37f2 ; skip_underflow ; skip_overflow &37ec c9 60 CMP #&60 &37ee b0 02 BCS &37f2 ; skip_underflow &37f0 69 20 ADC #&20 ; skip_underflow &37f2 8d c3 0c STA &0cc3 ; viewport_screen_address_high &37f5 20 d3 3a JSR &3ad3 ; calculate_status_bar_screen_address # Returns status_bar_screen_address_high &37f8 85 6d STA &6d ; crtc_address_high &37fa ad ca 0c LDA &0cca ; status_bar_screen_address_low # Convert screen address to CRTC address &37fd 46 6d LSR &6d ; crtc_address_high &37ff 6a ROR A &3800 46 6d LSR &6d ; crtc_address_high &3802 6a ROR A &3803 46 6d LSR &6d ; crtc_address_high &3805 6a ROR A &3806 a2 0d LDX #&0d # R13: Displayed screen start address register (low) &3808 8e 00 fe STX &fe00 ; video register number &380b 8d 01 fe STA &fe01 ; video register value &380e a2 0c LDX #&0c # R12: Displayed screen start address register (high) &3810 8e 00 fe STX &fe00 ; video register number &3813 a5 6d LDA &6d ; crtc_address_high &3815 8d 01 fe STA &fe01 ; video register value &3818 ce c1 0c DEC &0cc1 ; scrolling_steps_remaining &381b ad c2 0c LDA &0cc2 ; viewport_screen_address_low &381e 18 CLC &381f 79 dc 38 ADC &38dc,Y ; offset_from_viewport_to_unbuffered_row_or_column_address_low &3822 85 64 STA &64 ; target_address_low &3824 ad c3 0c LDA &0cc3 ; viewport_screen_address_high &3827 79 e0 38 ADC &38e0,Y ; offset_from_viewport_to_unbuffered_row_or_column_address_high &382a c9 80 CMP #&80 &382c 90 02 BCC &3830 ; skip_overflow &382e e9 20 SBC #&20 ; skip_overflow &3830 85 65 STA &65 ; target_address_high ; unbuffer_column_or_row &3832 ad 90 20 LDA &2090 ; buffer_unbuffering_address_low # Move to next column or row of buffer &3835 18 CLC &3836 79 e4 38 ADC &38e4,Y ; viewpoint_scrolling_change_low &3839 8d 90 20 STA &2090 ; buffer_unbuffering_address_low &383c 85 62 STA &62 ; source_address_low &383e ad 91 20 LDA &2091 ; buffer_unbuffering_address_high &3841 79 e8 38 ADC &38e8,Y ; viewpoint_scrolling_change_high &3844 8d 91 20 STA &2091 ; buffer_unbuffering_address_high &3847 85 63 STA &63 ; source_address_high &3849 c0 02 CPY #&02 # Is the player panning vertically? &384b b0 3c BCS &3889 ; unbuffer_row ; unbuffer_column &384d a2 18 LDX #&18 ; unbuffer_column_loop &384f 20 b2 38 JSR &38b2 ; unbuffer_group # Copy eight bytes from buffer to screen &3852 a5 62 LDA &62 ; source_address_low &3854 18 CLC &3855 69 40 ADC #&40 # Move down eight pixels in buffer &3857 85 62 STA &62 ; source_address_low &3859 a5 63 LDA &63 ; source_address_high &385b 69 01 ADC #&01 &385d c9 53 CMP #&53 &385f d0 0d BNE &386e ; skip_buffer_wraparound &3861 ad 90 20 LDA &2090 ; buffer_unbuffering_address_low &3864 18 CLC &3865 69 a0 ADC #&a0 &3867 85 62 STA &62 ; source_address_low &3869 ad 91 20 LDA &2091 ; buffer_unbuffering_address_high &386c 69 00 ADC #&00 ; skip_buffer_wraparound &386e 85 63 STA &63 ; source_address_high &3870 a5 64 LDA &64 ; target_address_low &3872 18 CLC &3873 69 40 ADC #&40 # Move down eight pixels on screen &3875 85 64 STA &64 ; target_address_low &3877 a5 65 LDA &65 ; target_address_high &3879 69 01 ADC #&01 &387b c9 80 CMP #&80 &387d 90 02 BCC &3881 ; skip_screen_wraparound &387f e9 20 SBC #&20 ; skip_screen_wraparound &3881 85 65 STA &65 ; target_address_high &3883 ca DEX &3884 d0 c9 BNE &384f ; unbuffer_column_loop &3886 4c b1 38 JMP &38b1 ; leave ; unbuffer_row &3889 a2 28 LDX #&28 ; unbuffer_row_loop &388b 20 b2 38 JSR &38b2 ; unbuffer_group # Copy eight bytes from buffer to screen &388e a5 62 LDA &62 ; source_address_low &3890 18 CLC &3891 69 08 ADC #&08 # Move right two pixels in buffer &3893 85 62 STA &62 ; source_address_low &3895 a5 63 LDA &63 ; source_address_high &3897 69 00 ADC #&00 &3899 85 63 STA &63 ; source_address_high &389b a5 64 LDA &64 ; target_address_low &389d 18 CLC &389e 69 08 ADC #&08 # Move right two pixels on screen &38a0 85 64 STA &64 ; target_address_low &38a2 a5 65 LDA &65 ; target_address_high &38a4 69 00 ADC #&00 &38a6 c9 80 CMP #&80 &38a8 90 02 BCC &38ac ; skip_screen_wraparound &38aa e9 20 SBC #&20 ; skip_screen_wraparound &38ac 85 65 STA &65 ; target_address_high &38ae ca DEX &38af d0 da BNE &388b ; unbuffer_row_loop ; leave &38b1 60 RTS ; unbuffer_group &38b2 a0 00 LDY #&00 &38b4 b1 62 LDA (&62),Y ; source_address &38b6 91 64 STA (&64),Y ; target_address &38b8 c8 INY &38b9 b1 62 LDA (&62),Y ; source_address &38bb 91 64 STA (&64),Y ; target_address &38bd c8 INY &38be b1 62 LDA (&62),Y ; source_address &38c0 91 64 STA (&64),Y ; target_address &38c2 c8 INY &38c3 b1 62 LDA (&62),Y ; source_address &38c5 91 64 STA (&64),Y ; target_address &38c7 c8 INY &38c8 b1 62 LDA (&62),Y ; source_address &38ca 91 64 STA (&64),Y ; target_address &38cc c8 INY &38cd b1 62 LDA (&62),Y ; source_address &38cf 91 64 STA (&64),Y ; target_address &38d1 c8 INY &38d2 b1 62 LDA (&62),Y ; source_address &38d4 91 64 STA (&64),Y ; target_address &38d6 c8 INY &38d7 b1 62 LDA (&62),Y ; source_address &38d9 91 64 STA (&64),Y ; target_address &38db 60 RTS ; offset_from_viewport_to_unbuffered_row_or_column_address_low ; r l u d &38dc 38 00 00 c0 ; offset_from_viewport_to_unbuffered_row_or_column_address_high &38e0 01 00 00 1c ; viewpoint_scrolling_change_low ; r l u d &38e4 08 f8 c0 40 ; viewpoint_scrolling_change_high &38e8 00 ff fe 01 ; initial_buffer_unbuffering_address_high ; r l u d &38ec 3e 3f 49 3d ; initial_buffer_unbuffering_address_low &38f0 f8 80 00 c0 ; panning_angles ; r l u d &38f4 14 f8 04 f4 # Angle to pan in each direction (actually +8 -8 +4 -4) ; initialise_buffer_unbuffering_address_and_scrolling_steps &38f8 8d d1 0c STA &0cd1 ; scrolling_steps_total ; initialise_buffer_unbuffering_address &38fb b9 f0 38 LDA &38f0,Y ; initial_buffer_unbuffering_address_low &38fe 8d 90 20 STA &2090 ; buffer_unbuffering_address_low &3901 b9 ec 38 LDA &38ec,Y ; initial_buffer_unbuffering_address_high &3904 8d 91 20 STA &2091 ; buffer_unbuffering_address_high &3907 60 RTS ; initialise_wide_buffer &3908 a9 00 LDA #&00 ; BUFFER_WIDE &390a 20 63 29 JSR &2963 ; initialise_buffer_variables &390d a0 00 LDY #&00 ; WIDE ; set_buffer_screen_y_values &390f b9 1a 39 LDA &391a,Y ; buffer_maximum_screen_y_low_table &3912 85 51 STA &51 ; buffer_maximum_screen_y_low &3914 b9 1c 39 LDA &391c,Y ; buffer_minimum_screen_y_low_table &3917 85 52 STA &52 ; buffer_minimum_screen_y_low &3919 60 RTS ; buffer_maximum_screen_y_low_table ; w t &391a f0 f0 ; buffer_minimum_screen_y_low_table &391c b0 30 ; initialise_tall_buffer &391e a9 02 LDA #&02 ; BUFFER_TALL &3920 20 63 29 JSR &2963 ; initialise_buffer_variables ; set_tall_set_buffer_screen_y_values &3923 a0 01 LDY #&01 ; TALL &3925 d0 e8 BNE &390f ; set_buffer_screen_y_values # Always branches ; move_sights &3927 20 34 39 JSR &3934 ; move_sights_left_or_right &392a a5 09 LDA &09 ; panning_direction &392c 10 03 BPL &3931 ; to_plot_sights &392e 20 6b 39 JSR &396b ; move_sights_up_or_down ; to_plot_sights &3931 4c d9 39 JMP &39d9 ; plot_sights ; move_sights_left_or_right &3934 ae e8 0c LDX &0ce8 ; player_wants_to_pan_left_or_right &3937 30 31 BMI &396a ; leave &3939 d0 18 BNE &3953 ; move_sights_left ; move_sights_right &393b ad c6 0c LDA &0cc6 ; sights_x &393e 18 CLC &393f 69 01 ADC #&01 # Move sights right one pixel &3941 c9 90 CMP #&90 &3943 90 04 BCC &3949 ; not_at_right_edge &3945 e9 40 SBC #&40 &3947 86 09 STX &09 ; panning_direction # Pan screen right when sights reach right edge ; not_at_right_edge &3949 8d c6 0c STA &0cc6 ; sights_x &394c 29 03 AND #&03 &394e f0 66 BEQ &39b6 ; update_sights_screen_address # Update address if sights are in new screen byte &3950 4c 6a 39 JMP &396a ; leave ; move_sights_left &3953 ad c6 0c LDA &0cc6 ; sights_x &3956 38 SEC &3957 e9 01 SBC #&01 # Move sights left one pixel &3959 c9 10 CMP #&10 &395b b0 04 BCS &3961 ; not_at_left_edge &395d 69 40 ADC #&40 &395f 86 09 STX &09 ; panning_direction # Pan screen left when sights reach left edge ; not_at_left_edge &3961 8d c6 0c STA &0cc6 ; sights_x &3964 29 03 AND #&03 &3966 c9 03 CMP #&03 &3968 f0 4c BEQ &39b6 ; update_sights_screen_address # Update address if sights are in new screen byte ; leave &396a 60 RTS ; move_sights_up_or_down &396b a6 0b LDX &0b ; player_object &396d bc 40 01 LDY &0140,X ; objects_v_angle &3970 ae ea 0c LDX &0cea ; player_wants_to_pan_up_or_down &3973 30 63 BMI &39d8 ; leave &3975 e0 02 CPX #&02 &3977 d0 1e BNE &3997 ; move_sights_down ; move_sights_up &3979 ad c7 0c LDA &0cc7 ; sights_y &397c 18 CLC &397d 69 01 ADC #&01 &397f c9 a0 CMP #&a0 &3981 90 0a BCC &398d ; not_at_top_edge &3983 cc 47 11 CPY &1147 ; vertical_angle_limits &3986 f0 50 BEQ &39d8 ; leave # Don't move sights beyond top limit &3988 38 SEC &3989 e9 40 SBC #&40 &398b 86 09 STX &09 ; panning_direction # Pan screen up when sights reach top edge ; not_at_top_edge &398d 8d c7 0c STA &0cc7 ; sights_y &3990 29 07 AND #&07 &3992 d0 22 BNE &39b6 ; update_sights_screen_address # Update address &3994 4c b4 39 JMP &39b4 ; update_sights_screen_address_group # differently if sights have moved into new group ; move_sights_down &3997 ad c7 0c LDA &0cc7 ; sights_y &399a 38 SEC &399b e9 01 SBC #&01 &399d c9 20 CMP #&20 &399f b0 0a BCS &39ab ; not_at_bottom_edge &39a1 cc 48 11 CPY &1148 ; vertical_angle_limits + 1 &39a4 f0 32 BEQ &39d8 ; leave # Don't move sights beyond bottom limit &39a6 18 CLC &39a7 69 40 ADC #&40 &39a9 86 09 STX &09 ; panning_direction # Pan screen down when sights reach bottom edge ; not_at_bottom_edge &39ab 8d c7 0c STA &0cc7 ; sights_y &39ae 29 07 AND #&07 &39b0 c9 07 CMP #&07 &39b2 d0 02 BNE &39b6 ; update_sights_screen_address # Update address ; update_sights_screen_address_group # differently if sights have moved into new group &39b4 e8 INX &39b5 e8 INX ; update_sights_screen_address &39b6 ad c4 0c LDA &0cc4 ; sights_screen_address_low &39b9 18 CLC &39ba 7d c7 3a ADC &3ac7,X ; sights_address_delta_low_table &39bd 8d c4 0c STA &0cc4 ; sights_screen_address_low &39c0 ad c5 0c LDA &0cc5 ; sights_screen_address_high &39c3 7d cd 3a ADC &3acd,X ; sights_address_delta_high_table &39c6 c9 80 CMP #&80 &39c8 90 05 BCC &39cf ; skip_overflow_wraparound &39ca e9 20 SBC #&20 &39cc 4c d5 39 JMP &39d5 ; skip_underflow_wraparound ; skip_overflow_wraparound &39cf c9 60 CMP #&60 &39d1 b0 02 BCS &39d5 ; skip_underflow_wraparound &39d3 69 20 ADC #&20 ; skip_underflow_wraparound &39d5 8d c5 0c STA &0cc5 ; sights_screen_address_high ; leave &39d8 60 RTS ; plot_sights &39d9 ad d7 0c LDA &0cd7 ; suppress_plotting_of_sights # Top bit set if object being unbuffered &39dc 30 27 BMI &3a05 ; to_leave &39de 20 a7 3a JSR &3aa7 ; unplot_sights &39e1 ad c6 0c LDA &0cc6 ; sights_x # Calculate pixel in byte, &39e4 29 03 AND #&03 &39e6 8d cc 0c STA &0ccc ; sights_x_pixel &39e9 ad c4 0c LDA &0cc4 ; sights_screen_address_low &39ec 29 07 AND #&07 &39ee a8 TAY # byte in group &39ef ad c4 0c LDA &0cc4 ; sights_screen_address_low &39f2 29 f8 AND #&f8 &39f4 85 2a STA &2a ; screen_address_low # and group of rows corresponding to sight pixel &39f6 ad c5 0c LDA &0cc5 ; sights_screen_address_high &39f9 85 2b STA &2b ; screen_address_high ; plot_sights_loop &39fb ae c9 0c LDX &0cc9 ; size_of_sights # Move to next pixel in sights &39fe 98 TYA &39ff 18 CLC &3a00 7d 9a 3a ADC &3a9a,X ; sight_pixels_y_deltas &3a03 10 03 BPL &3a08 ; move_pixel_position # Top bit set indicates end of sight pixel data ; to_leave &3a05 4c 89 3a JMP &3a89 ; leave ; move_pixel_position &3a08 c9 08 CMP #&08 &3a0a a8 TAY &3a0b 90 16 BCC &3a23 ; not_new_group &3a0d e9 08 SBC #&08 # Keep offset within a group &3a0f a8 TAY &3a10 a5 2a LDA &2a ; screen_address_low # Move down eight pixels &3a12 18 CLC &3a13 69 40 ADC #&40 &3a15 85 2a STA &2a ; screen_address_low &3a17 a5 2b LDA &2b ; screen_address_high &3a19 69 01 ADC #&01 &3a1b c9 80 CMP #&80 &3a1d 90 02 BCC &3a21 ; skip_overflow &3a1f e9 20 SBC #&20 ; skip_overflow &3a21 85 2b STA &2b ; screen_address_high ; not_new_group &3a23 bd 8e 3a LDA &3a8e,X ; sight_pixels_x_deltas &3a26 f0 30 BEQ &3a58 ; skip_change_of_x &3a28 18 CLC &3a29 6d cc 0c ADC &0ccc ; sights_x_pixel &3a2c 8d cc 0c STA &0ccc ; sights_x_pixel &3a2f 29 fc AND #&fc &3a31 0a ASL A &3a32 10 02 BPL &3a36 ; skip_page &3a34 c6 2b DEC &2b ; screen_address_high ; skip_page &3a36 18 CLC &3a37 65 2a ADC &2a ; screen_address_low &3a39 85 2a STA &2a ; screen_address_low &3a3b a5 2b LDA &2b ; screen_address_high &3a3d 69 00 ADC #&00 &3a3f c9 60 CMP #&60 &3a41 b0 05 BCS &3a48 ; skip_underflow &3a43 69 20 ADC #&20 &3a45 4c 4e 3a JMP &3a4e ; skip_overflow ; skip_underflow &3a48 c9 80 CMP #&80 &3a4a 90 02 BCC &3a4e ; skip_overflow &3a4c e9 20 SBC #&20 ; skip_overflow &3a4e 85 2b STA &2b ; screen_address_high &3a50 ad cc 0c LDA &0ccc ; sights_x_pixel &3a53 29 03 AND #&03 &3a55 8d cc 0c STA &0ccc ; sights_x_pixel ; skip_change_of_x &3a58 98 TYA &3a59 05 2a ORA &2a ; screen_address_low &3a5b 9d c1 49 STA &49c1,X ; sights_screen_addresses_low # Store screen address of pixel for unplotting &3a5e a5 2b LDA &2b ; screen_address_high &3a60 9d f3 3d STA &3df3,X ; sights_screen_addresses_high &3a63 b1 2a LDA (&2a),Y ; screen_address &3a65 9d e7 3d STA &3de7,X ; sights_previous_screen_values &3a68 ae cc 0c LDX &0ccc ; sights_x_pixel &3a6b 3d 7f 22 AND &227f,X ; colour_3_pixel_values &3a6e c9 10 CMP #&10 &3a70 08 PHP &3a71 b1 2a LDA (&2a),Y ; screen_address &3a73 3d bf 36 AND &36bf,X ; pixel_masks &3a76 28 PLP &3a77 b0 05 BCS &3a7e ; use_black ; use_white &3a79 1d 8a 3a ORA &3a8a,X ; colour_2_pixel_values # Use white pixels on black and blue &3a7c 90 03 BCC &3a81 ; set_pixel ; use_black &3a7e 1d 30 57 ORA &5730,X ; colour_1_pixel_values # Use black pixels otherwise ; set_pixel &3a81 91 2a STA (&2a),Y ; screen_address # Plot pixel &3a83 ee c9 0c INC &0cc9 ; size_of_sights &3a86 4c fb 39 JMP &39fb ; plot_sights_loop ; leave &3a89 60 RTS ; colour_2_pixel_values ; 0 1 2 3 &3a8a 80 40 20 10 ; sight_pixels_x_deltas &3a8e 00 00 00 fb 02 02 02 02 02 fb 00 00 ; sight_pixels_y_deltas &3a9a 00 02 02 01 00 00 00 00 00 01 02 02 &3aa6 80 # Top bit set to indicate end of sight pixel data ; unplot_sights &3aa7 ae c9 0c LDX &0cc9 ; size_of_sights &3aaa f0 1a BEQ &3ac6 ; leave &3aac ca DEX &3aad a0 00 LDY #&00 ; unplot_sights_loop &3aaf bd c1 49 LDA &49c1,X ; sights_screen_addresses_low &3ab2 85 2a STA &2a ; screen_address_low &3ab4 bd f3 3d LDA &3df3,X ; sights_screen_addresses_high &3ab7 85 2b STA &2b ; screen_address_high &3ab9 bd e7 3d LDA &3de7,X ; sights_previous_screen_values &3abc 91 2a STA (&2a),Y ; screen_address &3abe ca DEX &3abf 10 ee BPL &3aaf ; unplot_sights_loop &3ac1 a2 00 LDX #&00 &3ac3 8e c9 0c STX &0cc9 ; size_of_sights ; leave &3ac6 60 RTS ; sights_address_delta_low_table &3ac7 08 f8 ff 01 c7 39 ; sights_address_delta_high_table &3acd 00 ff ff 00 fe 01 ; calculate_status_bar_screen_address &3ad3 ad c2 0c LDA &0cc2 ; viewport_screen_address_low &3ad6 38 SEC &3ad7 e9 40 SBC #&40 &3ad9 8d ca 0c STA &0cca ; status_bar_screen_address_low &3adc ad c3 0c LDA &0cc3 ; viewport_screen_address_high &3adf e9 01 SBC #&01 &3ae1 c9 60 CMP #&60 &3ae3 b0 02 BCS &3ae7 ; skip_wraparound &3ae5 69 20 ADC #&20 ; skip_wraparound &3ae7 8d cb 0c STA &0ccb ; status_bar_screen_address_high &3aea 60 RTS ; unbuffer_status_bar &3aeb a9 5a LDA #&5a ; &5a00 = status_bar_buffer &3aed 85 63 STA &63 ; source_address_high &3aef a9 00 LDA #&00 &3af1 85 62 STA &62 ; source_address_low &3af3 ad cb 0c LDA &0ccb ; status_bar_screen_address_high &3af6 85 65 STA &65 ; target_address_high &3af8 ad ca 0c LDA &0cca ; status_bar_screen_address_low &3afb 85 64 STA &64 ; target_address_low &3afd 4c 89 38 JMP &3889 ; unbuffer_row ; arctan_low_table # 257 byte arctan table &3b00 00 29 51 7a a3 cc f4 1d 46 6f 97 c0 e9 11 3a 62 # (&1000 * ATN(X / 256) / (2 * PI))) MOD 256 &3b10 8b b4 dc 05 2d 56 7e a7 cf f7 20 48 70 99 c1 e9 # i.e. low byte of arctan(x / 256) &3b20 11 39 61 89 b1 d9 01 29 51 78 a0 c8 ef 17 3e 66 &3b30 8d b5 dc 03 2a 51 78 9f c6 ed 14 3b 61 88 ae d5 &3b40 fb 22 48 6e 94 ba e0 06 2c 51 77 9d c2 e7 0d 32 &3b50 57 7c a1 c6 eb 10 34 59 7d a2 c6 ea 0f 33 56 7a &3b60 9e c2 e5 09 2c 50 73 96 b9 dc ff 21 44 67 89 ab &3b70 ce f0 12 34 56 77 99 bb dc fd 1f 40 61 82 a3 c3 &3b80 e4 05 25 45 66 86 a6 c6 e6 05 25 44 64 83 a2 c1 &3b90 e0 ff 1e 3d 5b 7a 98 b7 d5 f3 11 2f 4c 6a 88 a5 &3ba0 c2 e0 fd 1a 37 54 70 8d aa c6 e2 fe 1b 37 53 6e &3bb0 8a a6 c1 dd f8 13 2e 49 64 7f 9a b4 cf e9 04 1e &3bc0 38 52 6c 86 9f b9 d3 ec 05 1f 38 51 6a 83 9c b4 &3bd0 cd e5 fe 16 2e 46 5e 76 8e a6 be d5 ed 04 1b 33 &3be0 4a 61 78 8e a5 bc d3 e9 ff 16 2c 42 58 6e 84 9a &3bf0 b0 c5 db f0 06 1b 30 45 5a 6f 84 99 ae c3 d7 ec &3c00 00 ; arctan_high_table # 257 byte arctan table &3c01 00 00 00 00 00 00 00 01 01 01 01 01 01 02 02 02 # (&1000 * ATN(X / 256) / (2 * PI))) DIV 256 &3c11 02 02 02 03 03 03 03 03 03 03 04 04 04 04 04 04 # i.e. high byte of arctan(x / 256) &3c21 05 05 05 05 05 05 06 06 06 06 06 06 06 07 07 07 &3c31 07 07 07 08 08 08 08 08 08 08 09 09 09 09 09 09 &3c41 09 0a 0a 0a 0a 0a 0a 0b 0b 0b 0b 0b 0b 0b 0c 0c &3c51 0c 0c 0c 0c 0c 0d 0d 0d 0d 0d 0d 0d 0e 0e 0e 0e &3c61 0e 0e 0e 0f 0f 0f 0f 0f 0f 0f 0f 10 10 10 10 10 &3c71 10 10 11 11 11 11 11 11 11 11 12 12 12 12 12 12 &3c81 12 13 13 13 13 13 13 13 13 14 14 14 14 14 14 14 &3c91 14 14 15 15 15 15 15 15 15 15 16 16 16 16 16 16 &3ca1 16 16 16 17 17 17 17 17 17 17 17 17 18 18 18 18 &3cb1 18 18 18 18 18 19 19 19 19 19 19 19 19 19 1a 1a &3cc1 1a 1a 1a 1a 1a 1a 1a 1a 1b 1b 1b 1b 1b 1b 1b 1b &3cd1 1b 1b 1b 1c 1c 1c 1c 1c 1c 1c 1c 1c 1c 1d 1d 1d &3ce1 1d 1d 1d 1d 1d 1d 1d 1d 1d 1e 1e 1e 1e 1e 1e 1e &3cf1 1e 1e 1e 1e 1f 1f 1f 1f 1f 1f 1f 1f 1f 1f 1f 1f &3d01 20 ; hypotenuse_approximation_factor_table # 129 byte hypotenuse approximation factor table &3d02 00 02 04 06 08 0a 0c 0e 10 12 14 16 18 1a 1c 1e # &100 * ((2 / SIN(ATN(X / 128))) - (2 / (X / 128))) &3d12 20 22 24 26 28 2a 2c 2e 30 32 33 35 37 39 3b 3d # derived from hypotenuse = x / cos(angle) &3d22 3f 41 43 45 47 49 4a 4c 4e 50 52 54 56 57 59 5b # and hypotenuse =~ x + (y * f(angle) / 2) &3d32 5d 5f 60 62 64 66 68 69 6b 6d 6f 70 72 74 75 77 # where x > y &3d42 79 7b 7c 7e 80 81 83 84 86 88 89 8b 8d 8e 90 91 &3d52 93 94 96 97 99 9b 9c 9e 9f a1 a2 a3 a5 a6 a8 a9 &3d62 ab ac ad af b0 b2 b3 b4 b6 b7 b8 ba bb bc be bf &3d72 c0 c2 c3 c4 c5 c7 c8 c9 ca cc cd ce cf d1 d2 d3 &3d82 d4 ; buffer_group_addresses_low ; 0 1 2 3 4 5 6 7 8 9 a b c d e f &3d83 c0 00 40 80 c0 00 40 80 c0 00 40 80 c0 00 40 80 ; 00 # &00 - &18 : screen &3d93 c0 00 40 80 c0 00 40 80 c0 00 40 80 c0 00 40 80 ; 10 # &19 - &28 : buffer &3da3 c0 00 40 80 c0 00 40 80 c0 a0 e0 20 60 a0 e0 20 ; 20 # &29 - &32 : buffer + &a0 &3db3 60 a0 ; 30 ; buffer_group_addresses_high ; 0 1 2 3 4 5 6 7 8 9 a b c d e f &3db5 60 62 63 64 65 67 68 69 6a 6c 6d 6e 6f 71 72 73 ; 00 &3dc5 74 76 77 78 79 7b 7c 7d 7e 3f 40 41 42 44 45 46 ; 10 &3dd5 47 49 4a 4b 4c 4e 4f 50 51 3f 40 42 43 44 45 47 ; 20 &3de5 48 49 ; 30 ; sights_previous_screen_values &3de7 00 00 00 00 00 00 00 00 00 00 00 00 ; sights_screen_addresses_high &3df3 00 00 00 00 00 00 00 00 00 00 00 00 ; unused &3dff 08 ; left_edge_pixel_values &3e00 00 00 00 00 07 03 01 00 70 30 10 00 77 33 11 00 &3e10 08 04 02 01 0f 07 03 01 78 34 12 01 7f 37 13 01 &3e20 80 40 20 10 87 43 21 10 f0 70 30 10 f7 73 31 10 &3e30 88 44 22 11 8f 47 23 11 f8 74 32 11 ff 77 33 11 ; right_edge_pixel_values &3e40 00 00 00 00 00 08 0c 0e 00 80 c0 e0 00 88 cc ee &3e50 08 04 02 01 08 0c 0e 0f 08 84 c2 e1 08 8c ce ef &3e60 80 40 20 10 80 48 2c 1e 80 c0 e0 f0 80 c8 ec fe &3e70 88 44 22 11 88 4c 2e 1f 88 c4 e2 f1 88 cc ee ff ; tile_visibility_bit_table # End half of a 256 byte sine table &3e80 b5 b6 b7 b8 b9 ba bc bd be bf c0 c1 c2 c3 c4 c5 # &100 * SIN(RAD((X - 128) * 90 / 256)) &3e90 c6 c7 c8 c9 ca cb cc cd ce cf cf d0 d1 d2 d3 d4 # i.e. low byte of sin((x - 128) * 90 degrees / 256) &3ea0 d5 d6 d7 d7 d8 d9 da db dc dc dd de df e0 e0 e1 # &3eb0 e2 e3 e3 e4 e5 e5 e6 e7 e7 e8 e9 e9 ea eb eb ec # Contents are overwritten &3ec0 ed ed ee ee ef ef f0 f1 f1 f2 f2 f3 f3 f4 f4 f5 &3ed0 f5 f5 f6 f6 f7 f7 f8 f8 f8 f9 f9 f9 fa fa fa fb &3ee0 fb fb fc fc fc fc fd fd fd fd fe fe fe fe fe ff &3ef0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ; relocated_entry_point &3f00 38 SEC &3f01 6e fc 0c ROR &0cfc ; not_playing_game # Set top bit to indicate game is inactive &3f04 a9 04 LDA #&04 ; Define action of cursor editing keys &3f06 a0 00 LDY #&00 &3f08 a2 01 LDX #&01 ; Cursor keys return ASCII values 135-139 &3f0a 20 f4 ff JSR &fff4 ; OSBYTE &3f0d a9 90 LDA #&90 ; Set TV offset and interlacing &3f0f a2 00 LDX #&00 ; vertical screen shift &3f11 a0 00 LDY #&00 ; disable interlacing &3f13 20 f4 ff JSR &fff4 ; OSBYTE &3f16 a9 16 LDA #&16 # Change to MODE 5 &3f18 20 ee ff JSR &ffee ; OSWRCH &3f1b a9 05 LDA #&05 &3f1d 20 ee ff JSR &ffee ; OSWRCH &3f20 78 SEI &3f21 a9 06 LDA #&06 # R6: Vertical displayed register &3f23 8d 00 fe STA &fe00 ; video register number &3f26 a9 19 LDA #&19 # Screen is 200 pixels high &3f28 8d 01 fe STA &fe01 ; video register value &3f2b a9 07 LDA #&07 # R7: Vertical sync position &3f2d 8d 00 fe STA &fe00 ; video register number &3f30 a9 20 LDA #&20 &3f32 8d 01 fe STA &fe01 ; video register value &3f35 a9 0a LDA #&0a # R10: Cursor start register &3f37 8d 00 fe STA &fe00 ; video register number &3f3a a9 20 LDA #&20 # Disable cursor &3f3c 8d 01 fe STA &fe01 ; video register value &3f3f 58 CLI &3f40 a9 97 LDA #&97 ; Write SHEILA &3f42 a2 42 LDX #&42 # &fe42, System VIA data direction register B &3f44 a0 ff LDY #&ff # Set PB0-7 as outputs &3f46 20 f4 ff JSR &fff4 ; OSBYTE &3f49 a9 97 LDA #&97 ; Write SHEILA &3f4b a2 40 LDX #&40 # &fe40, System VIA port B input/output register &3f4d a0 05 LDY #&05 # Set B5 = 0 &3f4f 20 f4 ff JSR &fff4 ; OSBYTE &3f52 a9 97 LDA #&97 ; Write SHEILA &3f54 a2 40 LDX #&40 # &fe40, System VIA port B input/output register &3f56 a0 0c LDY #&0c # Set B4 = 1, i.e. size of screen is &2000 for scrolling &3f58 20 f4 ff JSR &fff4 ; OSBYTE &3f5b a9 00 LDA #&00 ; Identify Host/Operating System &3f5d a2 ff LDX #&ff ; Return host/OS in X &3f5f 20 f4 ff JSR &fff4 ; OSBYTE &3f62 e0 00 CPX #&00 &3f64 f0 0c BEQ &3f72 ; is_electron &3f6y a9 c8 LDA #&c8 ; Read/Write BREAK/ESCAPE effect &3f68 a2 02 LDX #&02 ; Memory cleared on next reset, Normal ESCAPE effect &3f6a a0 00 LDY #&00 &3f6c 20 f4 ff JSR &fff4 ; OSBYTE &3f6f 4c 8c 3f JMP &3f8c ; skip_relocating_break_handler ; is_electron # If the game is running on an Electron, &3f72 a2 1c LDX #&1c # Move &3fed - &4009 to &0380 - &039c ; relocate_break_handler_loop &3f74 bd ed 3f LDA &3fed,X ; unrelocated_electron_break_handler &3f77 9d 80 03 STA &0380,X ; electron_break_handler &3f7a ca DEX &3f7b 10 f7 BPL &3f74 ; relocate_break_handler_loop &3f7d a9 4c LDA #&4c &3f7f 8d 87 02 STA &0287 ; BREAK intercept code &3f82 a9 80 LDA #&80 &3f84 8d 88 02 STA &0288 ; BREAK intercept code + 1 &3f87 a9 03 LDA #&03 ; &0380 = electron_break_handler # and use the relocated code as a break handler &3f89 8d 89 02 STA &0289 ; BREAK intercept code + 2 ; skip_relocating_break_handler &3f8c a9 00 LDA #&00 # Move &4100 - &49ff to &5800 - &60ff &3f8e 85 70 STA &70 ; source_address_low &3f90 85 72 STA &72 ; target_address_low &3f92 a9 41 LDA #&41 &3f94 85 71 STA &71 ; source_address_high &3f96 a9 58 LDA #&58 &3f98 85 73 STA &73 ; target_address_high ; move_4100_to_49ff_outer_loop &3f9a a0 00 LDY #&00 ; move_4100_to_49ff_inner_loop &3f9c b1 70 LDA (&70),Y ; source_address &3f9e 91 72 STA (&72),Y ; target_address &3fa0 88 DEY &3fa1 d0 f9 BNE &3f9c ; move_4100_to_49ff_inner_loop &3fa3 e6 71 INC &71 ; source_address_high &3fa5 e6 73 INC &73 ; target_address_high &3fa7 a5 71 LDA &71 ; source_address_high &3fa9 c9 4a CMP #&4a &3fab 90 ed BCC &3f9a ; move_4100_to_49ff_outer_loop &3fad 78 SEI &3fae ad 04 02 LDA &0204 ; irq1_vector_low &3fb1 8d 01 0d STA &0d01 ; previous_irq1_vector_low &3fb4 ad 05 02 LDA &0205 ; irq1_vector_high &3fb7 8d 02 0d STA &0d02 ; previous_irq1_vector_high &3fba a9 02 LDA #&02 ; wait_for_vsync &3fbc 2c 4d fe BIT &fe4d ; System VIA interrupt flag register # &02 is set when System VIA CA1 (i.e. a v-sync) occurs &3fbf f0 fb BEQ &3fbc ; wait_for_vsync &3fc1 a9 40 LDA #&40 # Set User VIA timer 1 to generate repeated interrupts &3fc3 8d 6b fe STA &fe6b ; User VIA auxiliary control register &3fc6 a9 c0 LDA #&c0 # Enable interrupts from User VIA timer 1 &3fc8 8d 6e fe STA &fe6e ; User VIA interrupt enable register &3fcb a9 00 LDA #&00 &3fcd 8d 64 fe STA &fe64 ; User VIA timer 1 counter LSB &3fd0 a9 39 LDA #&39 &3fd2 8d 65 fe STA &fe65 ; User VIA timer 1 counter MSB &3fd5 a9 1e LDA #&1e &3fd7 8d 66 fe STA &fe66 ; User VIA timer 1 latch LSB &3fda a9 4e LDA #&4e &3fdc 8d 67 fe STA &fe67 ; User VIA timer 1 latch MSB &3fdf a9 37 LDA #&37 &3fe1 8d 05 02 STA &0205 ; irq1_vector_high &3fe4 a9 66 LDA #&66 ; &3766 = irq1_hanlder &3fe6 8d 04 02 STA &0204 ; irq1_vector_low &3fe9 58 CLI &3fea 4c 17 10 JMP &1017 ; title_screen # &3fed - &4009 is moved to &0380 - &039c at &3f74 on Electron ; electron_break_handler &0380 a9 04 LDA #&04 # Wipe &0400 - &7bff &0382 85 71 STA &71 ; wipe_address_high &0384 a9 00 LDA #&00 &0386 85 70 STA &70 ; wipe_address_low ; wipe_0400_to_7bff_outer_loop &0388 a0 ff LDY #&ff ; wipe_0400_to_7bff_inner_loop &038a 91 70 STA (&70),Y ; wipe_address &038c 88 DEY &038d d0 fb BNE &038a ; wipe_0400_to_7bff_inner_loop &038f e6 71 INC &71 ; wipe_address_high &0391 a6 71 LDX &71 ; wipe_address_high &0393 e0 7c CPX #&7c &0395 90 f1 BCC &0388 ; wipe_0400_to_7bff_outer_loop &0397 a9 00 LDA #&00 # Clear break handler &0399 8d 87 02 STA &0287 ; BREAK intercept code &039c 60 RTS # Source code fragment, corresponds to &1964 - &1985 &400a 20 65 74 73 36 0d ; ...ets6 &4010 12 ca 19 20 20 20 20 20 20 4c 44 58 23 36 3a 4a ; 4810 LDX#6:JSR CFLSH &4020 53 52 20 43 46 4c 53 48 0d &4029 12 d4 05 20 0d ; 4820 &402e 12 de 0d 2e 65 74 73 36 20 72 74 73 0d ; 4830.ets6 rts &403b 12 e8 05 20 0d ; 4840 &4040 12 f2 05 20 0d ; 4850 &4045 12 fc 05 20 0d ; 4860 &404a 13 06 05 20 0d ; 4870 &404f 13 10 05 20 0d ; 4880 &4054 13 1a 05 20 0d ; 4890 &4059 13 24 05 20 0d ; 4900 &405e 13 2e 2a 2e 4d 49 4e 49 20 4c 44 41 23 31 32 38 ; 4910.MINI LDA#128:STA MEANY,X:STA MEMORY,X &406e 3a 53 54 41 20 4d 45 41 4e 59 2c 58 3a 53 54 41 &407e 20 4d 45 4d 4f 52 59 2c 58 0d &4088 13 38 1f 20 20 20 20 20 20 4c 44 41 23 30 3a 53 ; 4920 LDA#0:STA MEANYSCAN,X &4098 54 41 20 4d 45 41 4e 59 53 43 41 4e 2c 58 0d &40a7 13 42 22 20 20 20 20 20 20 4c 44 41 23 36 34 3a ; 4930 LDA#64:STA MTRYCNT,X:rts &40b7 53 54 41 20 4d 54 52 59 43 4e 54 2c 58 3a 72 74 &40c7 73 0d &40c9 13 4c 05 20 0d ; 4940 &40ce 13 56 1a 2e 4d 45 41 4e 20 4c 44 41 23 34 30 3a ; 4950.MEAN LDA#40:STA COVER &40de 53 54 41 20 43 4f 56 45 52 0d &40e8 13 60 1b 20 20 20 20 20 20 4c 44 58 20 45 54 45 ; 4960 LDX ETEM:STX XT &40f8 4d 3a 53 54 58 20 58 54 # &4100 - &49ff is moved to &5800 - &60ff at &3f8c ; string &08 continued &5800 00 c0 02 &5803 ff ; string &09 &5804 19 04 c0 00 40 00 ; MOVE &00c0, &0040 &580a ff ; string &0a &580b 05 ; Write at graphics cursor &580c 12 00 80 ; GCOL 0,&80 (plot using foreground colour 0) &580f ff ; string &0b &5810 05 ; Write at graphics cursor &5811 12 00 81 ; GCOL 0,&81 (plot using foreground colour 1) &5814 ff ; string &0c &5815 19 04 40 00 a0 00 ; MOVE &0040, &00a0 &581b ff ; string &0d &581c 4c 41 4e 44 53 43 41 50 45 ; "LANDSCAPE" &5825 ff ; string &0e &5826 53 45 43 52 45 54 20 45 4e 54 52 59 20 43 4f 44 ; "SECRET ENTRY CODE" &5836 45 &5837 ff ; string &0f &5838 20 20 20 20 20 ; " " &583d ff ; string &10 &583e 20 20 20 ; " " &5841 ff ; string &11 &5842 50 52 45 53 53 20 41 4e 59 20 4b 45 59 ; "PRESS ANY KEY" &584f ff ; tune_data ; tune_0 # Hyperspace &5850 c8 08 24 ce 24 c8 14 30 ce 30 c8 1c 38 ce 38 c8 &5860 00 1c ce 1c c8 08 24 24 ff ; tune_1 # Transfer consciousness &5869 c8 08 24 ce 24 c8 14 30 ce 30 c8 10 2c ce 2c ; tune_2 # U-turn &5878 c8 00 1c ce 1c c8 08 24 24 ff ; tune_3 # Failure &5882 cc 08 24 40 44 d7 54 c9 68 54 44 40 24 d4 08 ff ; tune_4 # Success &5892 c9 08 24 d4 24 c9 00 1c cd 1c c9 1c 38 ce 38 c9 &58a2 14 30 cf 30 c9 10 1c d1 1c c9 08 24 24 ff ; sprite_data ; sprite_0 SPRITE_EMPTY &58b0 0f 0f 0f 0f 0f 0f 0f 0f ; sprite_1 SPRITE_ROBOT &58b8 6f 0f 00 06 09 09 0f 0f ; sprite_2 SPRITE_TREE &58c0 2f 2f 7a 7a fa 2d 0f 0f ; sprite_3 SPRITE_TREE_RIGHT &58c8 0f 0f 0f 0f 8f 0f 0f 0f ; sprite_4 SPRITE_BOULDER &58d0 0f 0f 78 d2 87 87 0f 0f ; sprite_5 SPRITE_BOULDER_RIGHT &58d8 0f 0f 0f 87 87 87 0f 0f ; sprite_6 SPRITE_SUPER_ROBOT &58e0 6f 0f ff 9f 6f 6f 0f 0f ; sprite_7 SPRITE_BAR_LEFT &58e8 1e 1e 1e 1e 1e 1e 0f 0f ; sprite_8 SPRITE_BAR_MIDDLE &58f0 f0 0f 0f 0f 0f f0 0f 0f ; sprite_9 SPRITE_BAR_RIGHT &58f8 87 87 87 87 87 87 0f 0f ; sound_data ; chan vol pitch dur ; sound_block_0 # Rotating enemy or meanie, player dying &5900 10 00 01 00 07 00 05 00 ; sound_block_1 # Rotating enemy or meanie, player dying &5908 11 00 00 00 78 00 0a 00 ; sound_block_2 # Tune, player targeted &5910 12 00 03 00 22 00 14 00 ; sound_block_3 # Energy loss, bad action, changing volume &5918 13 00 04 00 90 00 14 00 ; sound_block_4 # Creating or absorbing object &5920 10 00 02 00 04 00 28 00 ; envelope_data ; envelope_0 # Rotating enemy or meanie &5928 01 02 00 00 00 00 00 00 14 00 ec ec 78 78 ; envelope_1 # Creating or absorbing &5936 02 04 00 00 00 00 00 00 02 ff 00 00 78 78 ; envelope_2 # Tune &5944 04 02 01 ff 00 01 01 08 78 ff 00 ff 78 08 ; envelope_3 # Player targeted &5952 03 01 06 fa 00 01 01 00 01 ff 00 00 78 08 ; envelope_4 # Energy loss, bad action, changing volume &5960 04 82 01 ff 00 02 01 07 78 fa fe fe 78 00 ; envelope_5 # Player dying &596e 01 01 00 00 00 00 00 00 78 88 ff ff 78 00 ; unused &597c 00 00 00 00 ; sine_table # 128 byte sine table &5980 00 03 06 09 0d 10 13 16 19 1c 1f 22 26 29 2c 2f # &100 * SIN(RAD(X * 90 / 128)) &5990 32 35 38 3b 3e 41 44 47 4a 4d 50 53 56 59 5c 5f # i.e. low byte of sin(x * 90 degrees / 128) &59a0 62 65 68 6b 6d 70 73 76 79 7b 7e 81 84 86 89 8c &59b0 8e 91 93 96 98 9b 9d a0 a2 a5 a7 aa ac ae b1 b3 &59c0 b5 b7 b9 bc be c0 c2 c4 c6 c8 ca cc ce cf d1 d3 &59d0 d5 d7 d8 da dc dd df e0 e2 e3 e5 e6 e7 e9 ea eb &59e0 ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f8 f9 fa fa &59f0 fb fc fc fd fd fe fe fe ff ff ff ff ff ff ff ff # Source code fragment, corresponds to &19d9 - &1a25 &5a00 44 58 20 45 54 45 4d 0d ; ...DX ETEM &5a08 14 3c 05 20 0d ; 5180 &5a0d 14 46 23 20 20 20 20 20 20 54 59 41 3a 4a 53 52 ; 5190 TYA:JSR EMIRTEST:BCC mea2 &5a1d 20 45 4d 49 52 54 45 53 54 3a 42 43 43 20 6d 65 &5a2d 61 32 0d &5a30 14 50 05 20 0d ; 5200 &5a35 14 5a 1a 20 20 20 20 20 20 54 59 41 3a 53 54 41 ; 5210 TYA:STA MEANY,X &5a45 20 4d 45 41 4e 59 2c 58 20 0d &5a4f 14 64 05 20 0d ; 5220 &5a54 14 6e 1c 20 20 20 20 20 20 4c 44 41 23 34 3a 53 ; 5230 LDA#4:STA OBTYPE,Y &5a64 54 41 20 4f 42 54 59 50 45 2c 59 0d &5a70 14 78 23 20 20 20 20 20 20 4c 44 41 23 31 30 34 ; 5240 LDA#104:STA OBHALFSIZEMIN &5a80 3a 53 54 41 20 4f 42 48 41 4c 46 53 49 5a 45 4d &5a90 49 4e 0d &5a93 14 82 11 20 20 20 20 20 20 43 4c 43 3a 72 74 73 ; 5250 CLC:rts &5aa3 0d &5aa4 14 83 05 20 0d ; 5251 &5aa9 14 84 21 2e 6d 65 61 32 20 49 4e 43 20 4d 54 52 ; 5252.mea2 INC MTRYCNT,X:JMP EEXIT &5ab9 59 43 4e 54 2c 58 3a 4a 4d 50 20 45 45 58 49 54 &5ac9 0d &5aca 14 85 05 20 0d ; 5253 &5acf 14 8c 05 20 0d ; 5260 &5ad4 14 96 26 2e 74 61 6b 35 20 4c 44 41 23 31 32 38 ; 5270.tak5 LDA#128:STA THEEND:JMP EEXIT &5ae4 3a 53 54 41 20 54 48 45 45 4e 44 3a 4a 4d 50 20 &5af4 45 45 58 49 54 0d &5afa 14 a0 05 20 0d ; 5280 &5aff 14 aa 14 2e 54 41 4b 45 20 4c 44 58 20 50 45 52 ; 5290.TAKE LDX PERSON &5b0f 53 4f 4e 0d &5b13 14 b4 22 20 20 20 20 20 20 43 50 58 20 50 4c 41 ; 5300 CPX PLAYERINDEX:BNE tak1 &5b23 59 45 52 49 4e 44 45 58 3a 42 4e 45 20 74 61 6b &5b33 31 0d &5b35 14 be 1d 20 20 20 20 20 20 4c 44 41 20 45 4e 45 ; 5310 LDA ENERGY:BEQ tak5 &5b45 52 47 59 3a 42 45 51 20 74 61 6b 35 0d &5b52 14 c8 1e 20 20 20 20 20 20 53 45 43 3a 53 42 43 ; 5320 SEC:SBC#1:STA ENERGY &5b62 23 31 3a 53 54 41 20 45 4e 45 52 47 59 0d &5b70 14 d2 12 20 20 20 20 20 20 4a 53 52 20 45 44 49 ; 5330 JSR EDIS &5b80 53 0d &5b82 14 dc 18 20 20 20 20 20 20 4c 44 41 23 35 3a 4a ; 5340 LDA#5:JSR VIPO &5b92 53 52 20 56 49 50 4f 0d &5b9a 14 e6 16 20 20 20 20 20 20 53 45 43 3a 4a 4d 50 ; 5350 SEC:JMP tak3 &5baa 20 74 61 6b 33 0d &5bb0 14 f0 05 20 0d ; 5360 &5bb5 14 fa 05 20 0d ; 5370 &5bba 15 04 18 2e 74 61 6b 31 20 54 58 41 3a 4a 53 52 ; 5380.tak1 TXA:JSR EMIRPT &5bca 20 45 4d 49 52 50 54 0d &5bd2 15 0e 05 20 0d ; 5390 &5bd7 15 18 1f 20 20 20 20 20 20 4c 44 41 20 4f 42 54 ; 5400 LDA OBTYPE,X:BNE tak4 &5be7 59 50 45 2c 58 3a 42 4e 45 20 74 61 6b 34 0d &5bf6 15 22 05 20 0d ; 5410 &5bfb 15 2c 1e 20 5c ; 5420\ ; unused &5c00 60 RTS ; calculate_object_relative_angles_and_distance &5c01 84 6f STY &6f ; target_object &5c03 b9 40 0a LDA &0a40,Y ; objects_type &5c06 85 4c STA &4c ; object_type &5c08 a6 6e LDX &6e ; object_to_consider # Observer &5c0a 20 c4 5d JSR &5dc4 ; calculate_object_relative_x_and_y # Calculate relative position of target to observer &5c0d 20 f5 5d JSR &5df5 ; calculate_object_relative_z &5c10 20 67 55 JSR &5567 ; calculate_angle # Calculate horizontal angle between target and observer &5c13 a6 6e LDX &6e ; object_to_consider # Observer &5c15 a5 8a LDA &8a ; angle_low &5c17 38 SEC &5c18 e5 1f SBC &1f ; angle_low_adjustment &5c1a 8d 59 0c STA &0c59 ; object_relative_h_angle_low &5c1d a5 8b LDA &8b ; angle_high &5c1f fd c0 09 SBC &09c0,X ; objects_h_angle &5c22 18 CLC &5c23 69 0a ADC #&0a # Add half a screen width (10 * 256/360 degrees) &5c25 8d 57 0c STA &0c57 ; object_relative_h_angle_high &5c28 a4 6f LDY &6f ; target_object # Calculate apparent rotation of target to origin &5c2a a9 00 LDA #&00 &5c2c 38 SEC &5c2d e5 8a SBC &8a ; angle_low &5c2f 85 59 STA &59 ; object_relative_rotation_low &5c31 b9 c0 09 LDA &09c0,Y ; objects_h_angle &5c34 e5 8b SBC &8b ; angle_high &5c36 85 5a STA &5a ; object_relative_rotation_high &5c38 20 5f 56 JSR &565f ; calculate_hypotenuse # Calculate distance between target and origin into h &5c3b ad 0f 14 LDA &140f ; viewpoint_perspective # Zero if preview &5c3e d0 20 BNE &5c60 ; not_preview # If previewing a landscape, &5c40 a9 80 LDA #&80 # make all objects face the observer &5c42 85 5a STA &5a ; object_relative_rotation_high &5c44 a9 00 LDA #&00 &5c46 85 59 STA &59 ; object_relative_rotation_low &5c48 c0 3f CPY #&3f &5c4a f0 14 BEQ &5c60 ; is_platform &5c4c 46 7d LSR &7d ; h_high # Halve distance for everything but the platform &5c4e 66 7c ROR &7c ; h_low ; is_platform &5c50 38 SEC &5c51 66 84 ROR &84 ; z # Halve z, and make negative &5c53 66 81 ROR &81 ; z_fraction &5c55 a5 81 LDA &81 ; z_fraction # Raise enemies to accommodate this &5c57 18 CLC &5c58 69 70 ADC #&70 &5c5a 85 81 STA &81 ; z_fraction &5c5c 90 02 BCC &5c60 ; skip_overflow &5c5e e6 84 INC &84 ; z ; skip_overflow ; not_preview &5c60 a5 7c LDA &7c ; h_low &5c62 8d 5d 0c STA &0c5d ; object_relative_distance_low &5c65 a5 7d LDA &7d ; h_high &5c67 8d 5e 0c STA &0c5e ; object_relative_distance_high &5c6a a5 81 LDA &81 ; z_fraction &5c6c 8d 5b 0c STA &0c5b ; object_relative_z_fraction &5c6f a5 84 LDA &84 ; z &5c71 8d 5c 0c STA &0c5c ; object_relative_z &5c74 60 RTS ; calculate_vertex_positions &5c75 a6 4c LDX &4c ; object_type &5c77 a9 40 LDA #&40 ; PLOTTABLES_VERTICES &5c79 85 21 STA &21 ; vertex_plottables_offset &5c7b bd a1 49 LDA &49a1,X ; objects_first_vertex_table + 1 # For each vertex used in the object, &5c7e 85 4f STA &4f ; last_vertex_offset &5c80 bc a0 49 LDY &49a0,X ; objects_first_vertex_table &5c83 84 4e STY &4e ; vertex_offset ; calculate_vertex_positions_loop &5c85 a5 59 LDA &59 ; object_relative_rotation_low &5c87 85 74 STA &74 ; a_fraction &5c89 a5 5a LDA &5a ; object_relative_rotation_high &5c8b 18 CLC &5c8c 79 e0 4a ADC &4ae0,Y ; vertices_angular_coordinate_data &5c8f 20 70 0f JSR &0f70 ; calculate_sine_and_cosine # Calculate sine and cosine of its apparent angle &5c92 a4 4e LDY &4e ; vertex_offset &5c94 b9 60 4d LDA &4d60,Y ; vertices_radial_coordinate_data &5c97 85 75 STA &75 ; a &5c99 a5 8f LDA &8f ; cosine &5c9b 20 03 0d JSR &0d03 ; multiply_A_by_byte # Multiply v_radial * COS(v_angle) &5c9e 85 74 STA &74 ; a_fraction &5ca0 a9 00 LDA #&00 &5ca2 24 67 BIT &67 ; sine_and_cosine_signs &5ca4 50 03 BVC &5ca9 ; cosine_is_positive # Second highest bit set if cosine is negative &5ca6 20 09 10 JSR &1009 ; invert_A_and_a_fraction # Invert result if cosine is negative ; cosine_is_positive &5ca9 85 75 STA &75 ; a # a = v_radial * COS(v_angle) &5cab ad 5d 0c LDA &0c5d ; object_relative_distance_low &5cae 18 CLC &5caf 65 74 ADC &74 ; a_fraction &5cb1 85 82 STA &82 ; y_fraction &5cb3 ad 5e 0c LDA &0c5e ; object_relative_distance_high &5cb6 65 75 ADC &75 ; a &5cb8 85 88 STA &88 ; signed_y &5cba 10 0b BPL &5cc7 ; y_is_positive &5cbc a9 00 LDA #&00 &5cbe 38 SEC &5cbf e5 82 SBC &82 ; y_fraction &5cc1 85 82 STA &82 ; y_fraction &5cc3 a9 00 LDA #&00 &5cc5 e5 88 SBC &88 ; signed_y ; y_is_positive &5cc7 85 85 STA &85 ; y # y = radial component of vertex from observer &5cc9 b9 60 4d LDA &4d60,Y ; vertices_radial_coordinate_data &5ccc 85 75 STA &75 ; a &5cce a5 8e LDA &8e ; sine &5cd0 20 03 0d JSR &0d03 ; multiply_A_by_byte # Multiply v_radial * SIN(v_angle) &5cd3 85 80 STA &80 ; x_fraction &5cd5 a9 00 LDA #&00 &5cd7 85 83 STA &83 ; x # x = v_radial * SIN(v_angle), i.e. &5cd9 a5 67 LDA &67 ; sine_and_cosine_signs # x = tangential component of vertex from observer &5cdb 85 86 STA &86 ; signed_x # If sine is negative, x is negative too &5cdd 20 67 55 JSR &5567 ; calculate_angle # Calculate angle between x and y ; calculate_vertex_screen_x &5ce0 a4 21 LDY &21 ; vertex_plottables_offset &5ce2 a5 8a LDA &8a ; angle_low # Use x and y to calculate horizontal angle &5ce4 18 CLC &5ce5 6d 59 0c ADC &0c59 ; object_relative_h_angle_low &5ce8 99 a0 0b STA &0ba0,Y ; plottables_relative_h_angle_low # which will used for x screen co-ordinate &5ceb a5 8b LDA &8b ; angle_high &5ced 6d 57 0c ADC &0c57 ; object_relative_h_angle_high # This is relative angle + 10 * 256/360 degrees &5cf0 99 00 55 STA &5500,Y ; plottables_relative_h_angle_high &5cf3 20 5f 56 JSR &565f ; calculate_hypotenuse # Calculate h from relative x and y ; calculate_vertex_screen_y &5cf6 a4 4e LDY &4e ; vertex_offset &5cf8 b9 20 4c LDA &4c20,Y ; vertices_z_coordinate_data &5cfb 0a ASL A &5cfc 85 74 STA &74 ; a_fraction # Lowest seven bits are z coordinate &5cfe a9 00 LDA #&00 &5d00 90 03 BCC &5d05 ; z_is_positive &5d02 20 09 10 JSR &1009 ; invert_A_and_a_fraction ; z_is_positive &5d05 85 75 STA &75 ; a # a = v_z * 2 &5d07 a5 74 LDA &74 ; a_fraction &5d09 18 CLC &5d0a 6d 5b 0c ADC &0c5b ; object_relative_z_fraction &5d0d 85 80 STA &80 ; x_fraction &5d0f a5 75 LDA &75 ; a &5d11 6d 5c 0c ADC &0c5c ; object_relative_z # A = z coordinate of vertex relative to observer &5d14 a6 6e LDX &6e ; object_to_consider # Observer &5d16 20 1d 56 JSR &561d ; calculate_object_relative_vertical_angle # Calculate angle between h and z &5d19 a4 21 LDY &21 ; vertex_plottables_offset &5d1b a5 8d LDA &8d ; object_relative_v_angle_high # Vertical angle is used for y screen co-ordinate &5d1d 99 e0 0a STA &0ae0,Y ; plottables_screen_y_high &5d20 a5 50 LDA &50 ; object_relative_v_angle_low &5d22 99 80 0a STA &0a80,Y ; plottables_screen_y_low ; consider_next_vertex &5d25 e6 4e INC &4e ; vertex_offset &5d27 e6 21 INC &21 ; vertex_plottables_offset &5d29 a4 4e LDY &4e ; vertex_offset &5d2b c4 4f CPY &4f ; last_vertex_offset &5d2d f0 03 BEQ &5d32 ; leave &5d2f 4c 85 5c JMP &5c85 ; calculate_vertex_positions_loop ; leave &5d32 60 RTS ; plot_object &5d33 20 01 5c JSR &5c01 ; calculate_object_relative_angles_and_distance &5d36 a5 7d LDA &7d ; h_high # Distance from observer to object &5d38 c9 0f CMP #&0f # Is the object distant? &5d3a 6e 7a 0c ROR &0c7a ; suppress_lines # If so, set top bit to suppress line plotting &5d3d 20 75 5c JSR &5c75 ; calculate_vertex_positions # Apply object rotation to vertices used in object &5d40 a9 40 LDA #&40 ; POLYGON_FROM_OBJECT_DATA &5d42 85 3b STA &3b ; polygon_source &5d44 a9 00 LDA #&00 &5d46 85 53 STA &53 ; plot_pass &5d48 8d 47 0c STA &0c47 ; polygon_skip_in_pass_mask &5d4b a6 4c LDX &4c ; object_type &5d4d bd b6 49 LDA &49b6,X ; objects_plot_pass_method_table # Convex objects can be plotted in a single pass &5d50 f0 1d BEQ &5d6f ; plot_object_pass_loop &5d52 4a LSR A # Otherwise, plot concave parts in separate passes &5d53 b0 07 BCS &5d5c ; is_robot_sentry_or_sentinel # depending on object type and orientation ; is_meanie &5d55 a5 5a LDA &5a ; object_relative_rotation_high &5d57 69 c0 ADC #&c0 &5d59 4c 69 5d JMP &5d69 ; plot_object_in_one_pass_if_positive ; is_robot_sentry_or_sentinel &5d5c ad 5c 0c LDA &0c5c ; object_relative_z &5d5f d0 06 BNE &5d67 ; plot_object_in_one_pass_if_negative &5d61 ad 5b 0c LDA &0c5b ; object_relative_z_fraction &5d64 f0 09 BEQ &5d6f ; plot_object_pass_loop &5d66 4a LSR A ; plot_object_in_one_pass_if_negative &5d67 49 80 EOR #&80 ; plot_object_in_one_pass_if_positive &5d69 10 04 BPL &5d6f ; plot_object_pass_loop &5d6b a9 02 LDA #&02 &5d6d 85 53 STA &53 ; plot_pass ; plot_object_pass_loop &5d6f a6 4c LDX &4c ; object_type &5d71 bd ac 49 LDA &49ac,X ; objects_first_polygon_table + 1 # For each polygon in the object, &5d74 85 4f STA &4f ; last_polygon_offset &5d76 bc ab 49 LDY &49ab,X ; objects_first_polygon_table ; plot_object_polygons_loop &5d79 84 4e STY &4e ; polygon_offset &5d7b b9 a0 4e LDA &4ea0,Y ; polygons_type_and_colour_table &5d7e a6 53 LDX &53 ; plot_pass &5d80 f0 05 BEQ &5d87 ; always_plot_polygon # Always plot polygons on pass 0 &5d82 4d 47 0c EOR &0c47 ; polygon_skip_in_pass_mask # If top bit set of polygons_type_and_colour_table, &5d85 30 1d BMI &5da4 ; skip_plotting_polygon # skip on first pass (2), otherwise skip on second (1) ; always_plot_polygon &5d87 29 3c AND #&3c &5d89 85 19 STA &19 ; polygon_colours &5d8b b9 a0 4e LDA &4ea0,Y ; polygons_type_and_colour_table # Determine type of polygon from low two bits &5d8e 29 03 AND #&03 # 0: triangle, 1: quadrilateral &5d90 18 CLC &5d91 69 03 ADC #&03 &5d93 85 17 STA &17 ; last_vertex_offset &5d95 b9 e0 4f LDA &4fe0,Y ; polygons_vertices_address_low_table # Set vertex data address to use stored polygon data &5d98 85 3c STA &3c ; polygon_vertex_data_address_low &5d9a b9 20 51 LDA &5120,Y ; polygons_vertices_address_high_table &5d9d 85 3d STA &3d ; polygon_vertex_data_address_high &5d9f 20 79 2a JSR &2a79 ; plot_polygon # Plot the polygon &5da2 a4 4e LDY &4e ; polygon_offset ; skip_plotting_polygon &5da4 c8 INY &5da5 c4 4f CPY &4f ; last_polygon_offset &5da7 90 d0 BCC &5d79 ; plot_object_polygons_loop &5da9 c6 53 DEC &53 ; plot_pass &5dab 30 09 BMI &5db6 ; leave_after_resetting_data_address # Leave after one pass if plotting in single pass &5dad f0 07 BEQ &5db6 ; leave_after_resetting_data_address # Leave after two passes if plotting in two passes &5daf a9 80 LDA #&80 # Skip polygons not skipped on first pass on second pass &5db1 8d 47 0c STA &0c47 ; polygon_skip_in_pass_mask &5db4 30 b9 BMI &5d6f ; plot_object_pass_loop # Always branches ; leave_after_resetting_polygon_data_address &5db6 a9 40 LDA #&40 &5db8 85 3c STA &3c ; polygon_vertex_data_address_low &5dba a9 0c LDA #&0c ; &0c40 = temporary_vertex_data # Reset vertex data address for plotting tiles &5dbc 85 3d STA &3d ; polygon_vertex_data_address_high &5dbe 4e 7a 0c LSR &0c7a ; suppress_lines # Clear top bit to permit line plotting for tiles &5dc1 a4 6f LDY &6f ; target_object &5dc3 60 RTS ; calculate_object_relative_x_and_y &5dc4 a9 00 LDA #&00 &5dc6 85 80 STA &80 ; x_fraction &5dc8 38 SEC &5dc9 b9 00 09 LDA &0900,Y ; objects_x &5dcc fd 00 09 SBC &0900,X ; objects_x &5dcf 38 SEC &5dd0 ed 78 0c SBC &0c78 ; global_objects_x_offset # Non-zero only for title screen text &5dd3 85 86 STA &86 ; signed_x &5dd5 10 05 BPL &5ddc ; skip_x_inversion &5dd7 38 SEC &5dd8 a9 00 LDA #&00 &5dda e5 86 SBC &86 ; signed_x ; skip_x_inversion &5ddc 85 83 STA &83 ; x &5dde a9 00 LDA #&00 &5de0 85 82 STA &82 ; y_fraction &5de2 38 SEC &5de3 b9 80 09 LDA &0980,Y ; objects_y &5de6 fd 80 09 SBC &0980,X ; objects_y &5de9 85 88 STA &88 ; signed_y &5deb 10 05 BPL &5df2 ; skip_y_inversion &5ded 38 SEC &5dee a9 00 LDA #&00 &5df0 e5 88 SBC &88 ; signed_y ; skip_y_inversion &5df2 85 85 STA &85 ; y &5df4 60 RTS ; calculate_object_relative_z &5df5 b9 00 0a LDA &0a00,Y ; objects_z_fraction &5df8 38 SEC &5df9 fd 00 0a SBC &0a00,X ; objects_z_fraction &5dfc 85 81 STA &81 ; z_fraction &5dfe b9 40 09 LDA &0940,Y ; objects_z &5e01 fd 40 09 SBC &0940,X ; objects_z &5e04 85 84 STA &84 ; z &5e06 60 RTS ; get_character_after_flushing_keyboard &5e07 20 20 5e JSR &5e20 ; flush_keyboard ; get_character &5e0a 20 e0 ff JSR &ffe0 ; OSRDCH &5e0d 90 10 BCC &5e1f ; leave # Carry clear if a valid character was read &5e0f c9 1b CMP #&1b ; ESCAPE &5e11 d0 0c BNE &5e1f ; leave &5e13 98 TYA &5e14 48 PHA &5e15 a9 7e LDA #&7e ; Acknowledge ESCAPE condition &5e17 20 f4 ff JSR &fff4 ; OSBYTE &5e1a 68 PLA &5e1b a8 TAY &5e1c 4c 0a 5e JMP &5e0a ; get_character ; leave &5e1f 60 RTS ; flush_keyboard &5e20 a9 02 LDA #&02 ; Specify input stream &5e22 a2 00 LDX #&00 ; Use the keyboard for input &5e24 20 f4 ff JSR &fff4 ; OSBYTE &5e27 a2 00 LDX #&00 ; keyboard &5e29 4c 55 35 JMP &3555 ; flush_buffer ; set_palette &5e2c 85 74 STA &74 ; offset &5e2e 29 7f AND #&7f &5e30 a8 TAY &5e31 a9 03 LDA #&03 &5e33 85 75 STA &75 ; colour ; set_palette_loop &5e35 a6 74 LDX &74 ; offset &5e37 10 03 BPL &5e3c ; use_colour # If top bit colour, set all four colours to be offset &5e39 be 57 5e LDX &5e57,Y ; colour_schemes ; use_colour &5e3c a9 13 LDA #&13 # Define logical colour &5e3e 20 ee ff JSR &ffee ; OSWRCH &5e41 a5 75 LDA &75 ; colour # Write logical colour &5e43 20 ee ff JSR &ffee ; OSWRCH &5e46 8a TXA &5e47 a2 04 LDX #&04 ; write_zeros_loop &5e49 20 ee ff JSR &ffee ; OSWRCH # Write physical colour, then padding zeros &5e4c a9 00 LDA #&00 &5e4e ca DEX &5e4f d0 f8 BNE &5e49 ; write_zeros_loop &5e51 88 DEY &5e52 c6 75 DEC &75 ; colour &5e54 10 df BPL &5e35 ; set_palette_loop &5e56 60 RTS ; colour_schemes ; 0 1 2 3 &5e57 04 00 06 03 ; &83 : B K C Y # Level (colours 2 and 3 are modified) &5e5b 04 00 01 03 ; &87 : B K R Y # Title screen ; dither_buffer_to_screen &5e5f a9 ff LDA #&ff &5e61 8d d2 0c STA &0cd2 ; dither_count &5e64 ad 69 0c LDA &0c69 ; buffer_width_in_groups &5e67 0a ASL A &5e68 0a ASL A &5e69 0a ASL A &5e6a 8d d3 0c STA &0cd3 ; buffer_width_in_bytes ; calculate_mask_for_dithering_randomness &5e6d 38 SEC &5e6e e9 01 SBC #&01 &5e70 f0 09 BEQ &5e7b ; skip_calculating_mask &5e72 a0 ff LDY #&ff ; calculate_mask_for_dithering_randomness_loop &5e74 0a ASL A &5e75 c8 INY &5e76 90 fc BCC &5e74 ; calculate_mask_for_dithering_randomness_loop &5e78 b9 b4 15 LDA &15b4,Y ; mask_table ; skip_calculating_mask &5e7b 8d cf 0c STA &0ccf ; mask_for_dithering_randomness &5e7e 78 SEI &5e7f 20 8e 56 JSR &568e ; shuffle_rnd # Pick a random byte within the buffer &5e82 ac d0 56 LDY &56d0 ; rnd_state + 1 &5e85 8c d0 0c STY &0cd0 ; random_value_for_dithering &5e88 58 CLI &5e89 18 CLC &5e8a 6d cd 0c ADC &0ccd ; random_byte_offset_within_buffer &5e8d 2d cf 0c AND &0ccf ; mask_for_dithering_randomness &5e90 cd d3 0c CMP &0cd3 ; buffer_width_in_bytes &5e93 90 03 BCC &5e98 ; skip_overflow &5e95 ed d3 0c SBC &0cd3 ; buffer_width_in_bytes ; skip_overflow &5e98 8d cd 0c STA &0ccd ; random_byte_offset_within_buffer &5e9b ad d0 0c LDA &0cd0 ; random_value_for_dithering &5e9e 29 1f AND #&1f # Bottom six bits of random number set row &5ea0 85 13 STA &13 ; tmp &5ea2 4a LSR A &5ea3 18 CLC &5ea4 65 13 ADC &13 ; tmp &5ea6 29 fe AND #&fe &5ea8 85 13 STA &13 ; tmp &5eaa 0a ASL A &5eab 0a ASL A &5eac 65 13 ADC &13 ; tmp &5eae 85 13 STA &13 ; tmp &5eb0 a9 00 LDA #&00 &5eb2 46 13 LSR &13 ; tmp &5eb4 6a ROR A &5eb5 46 13 LSR &13 ; tmp &5eb7 6a ROR A &5eb8 46 13 LSR &13 ; tmp &5eba 6a ROR A &5ebb 6d cd 0c ADC &0ccd ; random_byte_offset_within_buffer &5ebe 85 62 STA &62 ; buffer_address_low &5ec0 a5 13 LDA &13 ; tmp &5ec2 69 00 ADC #&00 &5ec4 85 63 STA &63 ; buffer_address_high &5ec6 ad 92 20 LDA &2092 ; base_screen_address_low # Calculate the corresponding screen address &5ec9 18 CLC &5eca 65 62 ADC &62 ; buffer_address_low &5ecc 85 64 STA &64 ; screen_address_low &5ece ad 93 20 LDA &2093 ; base_screen_address_high &5ed1 65 63 ADC &63 ; buffer_address_high &5ed3 c9 80 CMP #&80 &5ed5 90 02 BCC &5ed9 ; skip_screen_wraparound &5ed7 e9 20 SBC #&20 ; skip_screen_wraparound &5ed9 85 65 STA &65 ; screen_address_high &5edb a5 63 LDA &63 ; buffer_address_high &5edd 18 CLC &5ede 69 3f ADC #&3f &5ee0 85 63 STA &63 ; buffer_address_high &5ee2 c9 53 CMP #&53 &5ee4 90 0d BCC &5ef3 ; skip_buffer_wraparound &5ee6 a5 62 LDA &62 ; buffer_address_low &5ee8 38 SEC &5ee9 e9 60 SBC #&60 &5eeb 85 62 STA &62 ; buffer_address_low &5eed a5 63 LDA &63 ; buffer_address_high &5eef e9 13 SBC #&13 &5ef1 85 63 STA &63 ; buffer_address_high ; skip_buffer_wraparound &5ef3 ad d0 0c LDA &0cd0 ; random_value_for_dithering &5ef6 0a ASL A &5ef7 2a ROL A &5ef8 2a ROL A &5ef9 29 03 AND #&03 # Top two bits of random number set pixel &5efb aa TAX &5efc a0 00 LDY #&00 ; dither_buffer_to_screen_loop &5efe b1 62 LDA (&62),Y ; buffer_address &5f00 3d 7f 22 AND &227f,X ; colour_3_pixel_values &5f03 85 13 STA &13 ; pixel_from_buffer &5f05 b1 64 LDA (&64),Y ; screen_address &5f07 3d bf 36 AND &36bf,X ; pixel_masks &5f0a 05 13 ORA &13 ; pixel_from_buffer &5f0c 91 64 STA (&64),Y ; screen_address &5f0e 2c 1e 0c BIT &0c1e ; not_ready_to_dither # Top bit set if plotting in progress &5f11 30 10 BMI &5f23 ; leave &5f13 ce d2 0c DEC &0cd2 ; dither_count &5f16 d0 08 BNE &5f20 ; to_dither_buffer_to_screen_loop &5f18 20 5a 35 JSR &355a ; update_sound &5f1b ce 94 20 DEC &2094 ; dithering_frames &5f1e f0 03 BEQ &5f23 ; leave ; to_dither_buffer_to_screen_loop &5f20 4c 7e 5e JMP &5e7e ; dither_buffer_to_screen_loop ; leave &5f23 60 RTS ; death_screen &5f24 48 PHA ; duration &5f25 20 48 35 JSR &3548 ; flush_all_sound_channels &5f28 20 99 36 JSR &3699 ; wipe_status_bar &5f2b a9 06 LDA #&06 ; SOUND_DYING &5f2d 8d 73 0c STA &0c73 ; sound_type &5f30 a9 fa LDA #&fa &5f32 8d 74 0c STA &0c74 ; dying_cooldown &5f35 68 PLA ; duration &5f36 20 68 5f JSR &5f68 ; fade_to_screen_black_for_duration &5f39 a0 00 LDY #&00 &5f3b 8c c9 0c STY &0cc9 ; size_of_sights # Zero to suppress sights being unplotted &5f3e 8c 5f 0c STY &0c5f ; sights_are_active # Zero to remove sights &5f41 ad 1c 0c LDA &0c1c ; title_screen_object &5f44 20 80 5f JSR &5f80 ; initialise_title_or_death_screen_objects # Use cause of death as object &01 &5f47 a9 03 LDA #&03 # Use black background with stars if player died &5f49 8d 4c 0c STA &0c4c ; background_type &5f4c a9 01 LDA #&01 # Object &01 is cause of death &5f4e 85 01 STA &01 ; object_to_update &5f50 a9 c0 LDA #&c0 # &80 to plot only object when dithering, &40 unused &5f52 8d 4d 0c STA &0c4d ; what_to_plot_behind_object_to_update &5f55 8d 6d 0c STA &0c6d ; how_to_plot_object_to_update # &80 to unplot sights, &40 to plot without dithering &5f58 4e 1e 0c LSR &0c1e ; not_ready_to_dither # Clear top bit to permit dithering to occur &5f5b a9 32 LDA #&32 ; tune_3 # Play tune for failure &5f5d 20 f6 5f JSR &5ff6 ; start_tune &5f60 20 84 1f JSR &1f84 ; update_object_on_screen &5f63 a9 1e LDA #&1e # Fade out for 25 &5f65 4c 68 5f JMP &5f68 ; fade_to_screen_black_for_duration ; fade_to_screen_black_for_duration &5f68 85 1e STA &1e ; duration ; fade_to_screen_black_for_duration_loop &5f6a a9 1e LDA #&1e # Repeat 25 times &5f6c 85 15 STA &15 ; count ; fade_to_screen_black_for_duration_inner_loop &5f6e 20 d9 56 JSR &56d9 ; fade_to_black # Change 80 random pixels to black &5f71 20 5a 35 JSR &355a ; update_sound &5f74 c6 15 DEC &15 ; count &5f76 d0 f6 BNE &5f6e ; fade_to_screen_black_for_duration_inner_loop &5f78 c6 1e DEC &1e ; duration &5f7a d0 ee BNE &5f6a ; fade_to_screen_black_for_duration_loop &5f7c 60 RTS ; unused &5f7d 4c # Would be JMP &3f30, possibly obfuscation ; to_preview_screen &5f7e 30 3f BMI &5fbf ; preview_screen ; initialise_title_or_death_screen_objects &5f80 8d 41 0a STA &0a41 ; objects_type + 1 # Use object &01 for displayed object &5f83 b9 bc 5f LDA &5fbc,Y ; title_or_death_screen_objects_y &5f86 18 CLC &5f87 6d 82 09 ADC &0982 ; objects_y + 2 # Position object &01 relative to object &02 &5f8a 8d 81 09 STA &0981 ; objects_y + 1 &5f8d ad 42 09 LDA &0942 ; objects_z + 2 &5f90 18 CLC &5f91 79 dc 5f ADC &5fdc,Y ; title_or_death_screen_objects_z &5f94 8d 41 09 STA &0941 ; objects_z + 1 &5f97 ad 02 09 LDA &0902 ; objects_x + 2 &5f9a 8d 01 09 STA &0901 ; objects_x + 1 &5f9d b9 d9 5f LDA &5fd9,Y ; title_or_death_screen_objects_v_angle &5fa0 8d 42 01 STA &0142 ; objects_v_angle + 2 &5fa3 b9 e2 5f LDA &5fe2,Y ; title_or_death_screen_objects_h_angle &5fa6 8d c2 09 STA &09c2 ; objects_h_angle + 2 &5fa9 a9 00 LDA #&00 &5fab 8d 02 0a STA &0a02 ; objects_z_fraction &5fae 8d 01 0a STA &0a01 ; objects_z_fraction + 1 &5fb1 b9 df 5f LDA &5fdf,Y ; title_or_death_screen_objects_origin_angle &5fb4 8d c1 09 STA &09c1 ; objects_h_angle + 1 &5fb7 a2 02 LDX #&02 # Use object &02 as observer &5fb9 86 6e STX &6e ; object_to_consider # Observer &5fbb 60 RTS ; title_or_death_screen_objects_y ; d th tp (death screen killer, title screen hero, title screen platform) &5fbc 05 07 07 ; preview_screen &5fbf 20 10 14 JSR &1410 ; set_palette_and_initialise_enemies &5fc2 a2 03 LDX #&03 # Use black background with stars &5fc4 a0 00 LDY #&00 # Use preview viewpoint &5fc6 a9 80 LDA #&80 # Don't plot hero &5fc8 20 9b 13 JSR &139b ; plot_title_and_preview_screen_polygons &5fcb a2 04 LDX #&04 # "PRESS ANY KEY", "LANDSCAPE" &5fcd 20 ad 36 JSR &36ad ; plot_text &5fd0 20 ab 33 JSR &33ab ; plot_landscape_number &5fd3 20 40 14 JSR &1440 ; initialise_player_and_trees # Doesn't return if code is valid (see &14d5) &5fd6 4c 74 10 JMP &1074 ; wrong_code_screen ; title_or_death_screen_objects_v_angle ; d th tp (death screen killer, title screen hero, title screen platform) &5fd9 f4 fb fb ; title_or_death_screen_objects_z ; d th tp (death screen killer, title screen hero, title screen platform) &5fdc 00 01 00 ; title_or_death_screen_objects_origin_angle ; d th tp (death screen killer, title screen hero, title screen platform) &5fdf 80 8e ce ; title_or_death_screen_objects_h_angle ; d th tp (death screen killer, title screen hero, title screen platform) &5fe2 00 f8 f8 ; plot_title_screen_object_on_platform &5fe5 a0 01 LDY #&01 # Add title screen platform &5fe7 20 ee 5f JSR &5fee ; plot_title_screen_object &5fea a0 02 LDY #&02 # Add title screen hero &5fec a9 06 LDA #&06 ; TYPE_PLATFORM ; plot_title_screen_object &5fee 20 80 5f JSR &5f80 ; initialise_title_or_death_screen_objects &5ff1 a0 01 LDY #&01 # Object &01 is title screen platform or hero &5ff3 4c 33 5d JMP &5d33 ; plot_object ; start_tune &5ff6 8d e7 0c STA &0ce7 ; tune_position &5ff9 a9 03 LDA #&03 ; SOUND_TUNE &5ffb 8d 73 0c STA &0c73 ; sound_type &5ffe 60 RTS ; unused &5fff 23 # &4900 - &49ff is copied to &6000 to &60ff at &3f8c, but used at original address ; unused &4900 fe fe ff ff ff ff ff ff ff ff ff ff ff ff ff ff &4910 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff &4920 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &4930 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &4940 ff ff ff ff ff 7f ff ff ff ff ff ff ff ff ff ff &4950 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff &4960 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &4970 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 &4980 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff &4990 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ; objects_first_vertex_table &49a0 00 ; TYPE_ROBOT &49a1 1d ; TYPE_SENTRY &49a2 33 ; TYPE_TREE &49a3 44 ; TYPE_BOULDER &49a4 4c ; TYPE_MEANIE &49a5 5e ; TYPE_SENTINEL &49a6 7c ; TYPE_PLATFORM &49a7 88 ; TYPE_BLOCK_RIGHT &49a8 90 ; TYPE_BLOCK_LEFT &49a9 98 ; TYPE_BLOCK_BOTH &49aa a0 ; (end) ; objects_first_polygon_table &49ab 00 ; TYPE_ROBOT &49ac 1b ; TYPE_SENTRY &49ad 34 ; TYPE_TREE &49ae 43 ; TYPE_BOULDER &49af 4d ; TYPE_MEANIE &49b0 66 ; TYPE_SENTINEL &49b1 89 ; TYPE_PLATFORM &49b2 94 ; TYPE_BLOCK_RIGHT &49b3 98 ; TYPE_BLOCK_LEFT &49b4 9c ; TYPE_BLOCK_BOTH &49b5 a0 ; (end) ; objects_plot_pass_method_table &49b6 03 ; TYPE_ROBOT &49b7 03 ; TYPE_SENTRY &49b8 00 ; TYPE_TREE &49b9 00 ; TYPE_BOULDER &49ba 02 ; TYPE_MEANIE &49bb 03 ; TYPE_SENTINEL &49bc 00 ; TYPE_PLATFORM &49bd 00 ; TYPE_BLOCK_RIGHT &49be 00 ; TYPE_BLOCK_LEFT &49bf 00 ; TYPE_BLOCK_BOTH ; unused &49c0 00 00 00 00 00 00 ff ff ff ff ff ff ff ; meanie_vertex_data # Uses vertices &00 - &11 &49cd 47 4c 4b 46 47 ; polygon &4d &49d2 40 45 42 40 ; polygon &4e &49d6 41 44 40 41 ; polygon &4f &49da 40 44 43 40 ; polygon &50 &49de 40 43 45 40 ; polygon &51 &49e2 42 45 44 41 42 ; polygon &52 &49e7 45 46 49 45 ; polygon &53 &49eb 44 48 47 44 ; polygon &54 &49ef 45 49 48 44 45 ; polygon &55 &49f4 43 46 45 43 ; polygon &56 &49f8 44 47 43 44 ; polygon &57 &49fc 43 47 46 43 ; polygon &58 &4a00 4b 4a 46 4b ; polygon &59 &4a04 47 4d 4c 47 ; polygon &5a &4a08 4b 4e 4a 4b ; polygon &5b &4a0c 4c 4d 4f 4c ; polygon &5c &4a10 4c 4f 4e 4b 4c ; polygon &5d &4a15 46 4a 49 46 ; polygon &5e &4a19 47 48 4d 47 ; polygon &5f &4a1d 4a 50 49 4a ; polygon &60 &4a21 48 51 4d 48 ; polygon &61 &4a25 4a 4e 50 4a ; polygon &62 &4a29 4d 51 4f 4d ; polygon &63 &4a2d 49 50 51 48 49 ; polygon &64 &4a32 4f 51 50 4e 4f ; polygon &65 ; unused &4a37 00 00 00 00 00 00 00 00 00 &4a40 ff ff ff ff ff 7f ff ff ff ff ff ff ff ff ff ff &4a50 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff &4a60 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &4a70 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 &4a80 fe fe ff ff ff ff ff ff ff ff ff ff ff ff ff ff &4aa0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff &4aa0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &4ab0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &4ac0 ff ff ff ff ff 7f ff ff ff ff ff ff ff ff ff ff &4ad0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ; vertices_angular_coordinate_data &4ae0 cf 31 5f a1 d9 27 57 a9 cb 00 35 55 ab c5 3b 52 &4af0 ae 00 ec 14 63 9d c0 40 69 97 21 df 00 da 26 5a &4b00 a6 f5 0b 40 5a a6 c0 da 26 5a a6 f5 0b f8 08 e2 &4b10 1e 5c a4 e0 20 60 a0 e0 20 60 a0 00 20 40 60 80 &4b20 a0 c0 e0 00 00 20 40 60 80 a0 c0 e0 00 59 a7 80 &4b30 72 8e e1 1f 59 a7 d5 f6 0a 2b fb 05 e9 17 da 26 &4b40 5a a6 da 26 5a a6 f5 0b 40 5a a6 c0 da 26 5a a6 &4b50 f5 0b 53 ad f8 08 e2 1e dc 24 66 9a e0 20 60 a0 &4b60 d4 ec 14 2c 54 6c 94 ac e0 fd 83 a0 e0 fd 83 a0 &4b70 03 20 60 7d 03 20 60 7d e0 20 60 a0 e0 20 60 a0 ; unused &4b80 fe fe ff ff ff ff ff ff ff ff ff ff ff ff ff ff &4b90 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff &4ba0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &4bb0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &4bc0 ff ff ff ff ff 7f ff ff ff ff ff ff ff ff ff ff &4bd0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff &4be0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &4bf0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 &4c00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff &4c10 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ; vertices_z_coordinate_data &4c20 f0 f0 f0 f0 b8 b8 b8 b8 b8 b8 b8 b8 b8 92 92 92 &4c30 92 8a 8b 8b 8b 8b 08 08 08 08 83 83 95 f0 f0 f0 &4c40 f0 98 98 9b 9b 9b 9b 88 88 88 88 88 88 01 01 08 &4c50 08 05 05 f0 f0 f0 f0 c8 c8 c8 c8 c8 c8 c8 c8 c8 &4c60 c8 c8 c8 78 b0 f0 b0 f0 b0 f0 b0 f0 f0 f0 f0 bc &4c70 bc bc 8a 8a 01 01 88 8e 8e 88 85 85 06 06 f0 f0 &4c80 f0 f0 e0 e0 e0 e0 88 88 8b 8b 8b 8b 08 08 05 05 &4c90 08 08 02 02 11 11 18 18 27 27 27 27 f0 f0 f0 f0 &4ca0 10 10 10 10 10 10 10 10 f0 f0 f0 f0 70 70 70 70 &4cb0 f0 f0 f0 f0 70 70 70 70 f0 f0 f0 f0 70 70 70 70 ; unused &4cc0 ff ff ff ff ff 7f ff ff ff ff ff ff ff ff ff ff &4cd0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff &4ce0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &4cf0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 18 &4d00 fe fe ff ff ff ff ff ff ff ff ff ff ff ff ff ff &4d10 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff &4d20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &4d30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &4d40 ff ff ff ff ff 7f ff ff ff ff ff ff ff ff ff ff &4d50 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ; vertices_radial_coordinate_data &4d60 14 14 1b 1b 1f 1f 23 23 2a 20 2a 30 30 34 34 38 &4d70 38 00 13 13 1d 1d 0c 0c 13 13 1c 1c 20 2a 2a 2a &4d80 2a 32 32 36 2a 2a 36 22 22 22 22 40 40 3f 3f 22 &4d90 22 1f 1f 18 18 18 18 18 18 18 18 68 68 68 68 68 &4da0 68 68 68 00 70 70 70 70 70 70 70 70 34 44 44 1a &4db0 29 29 0c 0c 17 17 2e 36 36 2e 3a 3a 22 22 38 38 &4dc0 38 38 2a 2a 2a 2a 32 32 36 2a 2a 36 22 22 22 22 &4dd0 40 40 46 46 3f 3f 22 22 13 13 18 18 b5 b5 b5 b5 &4de0 91 91 91 91 91 91 91 91 c0 88 88 c0 c0 88 88 c0 &4df0 88 c0 c0 88 88 c0 c0 88 c0 c0 c0 c0 c0 c0 c0 c0 ; unused &4e00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff &4e10 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff &4e20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &4e30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &4e40 ff ff ff ff ff 7f ff ff ff ff ff ff ff ff ff ff &4e50 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff &4e60 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &4e70 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &4e80 fe fe ff ff ff ff ff ff ff ff ff ff ff ff ff ff &4e90 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ; polygons_type_and_colour_table # 8....... plot on first pass if unset, second if set ; 0 1 2 3 4 5 6 7 8 9 a b c d e f # ..2184.. polygon colour &4ea0 15 14 15 99 99 a5 a5 91 91 99 90 90 94 94 90 90 ; &00 # ......21 polygon type (0: triangle, 1: quadrilateral) &4eb0 9c 9c 90 15 3c 3c 19 19 15 15 3d 95 94 94 95 a8 ; &10 &4ec0 a8 a8 a8 a8 a9 a8 a8 95 a8 94 94 15 15 29 29 15 ; &20 &4ed0 1d 1d 1d 1d 15 15 15 15 19 19 15 18 1c 18 1c 18 ; &30 &4ee0 1c 18 1c 14 14 14 14 18 18 18 18 15 15 15 28 28 ; &40 &4ef0 1c 1c 35 bc bc b9 14 14 18 18 18 14 14 3d 18 18 ; &50 &4f00 14 14 3c 3c 35 35 15 14 14 95 a9 95 95 95 95 94 ; &60 &4f10 94 95 a8 a8 a8 a8 a8 a8 a9 94 94 bc bc 95 15 15 ; &70 &4f20 14 14 1d 1d 1d 1d 15 29 29 15 29 15 29 3c 3c 3c ; &80 &4f30 3c 3d 3d 3d 15 15 29 3d 15 15 29 3d 15 15 29 3d ; &90 ; unused &4f40 ff ff ff ff ff 7f ff ff ff ff ff ff ff ff ff ff &4f50 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff &4f60 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &4f70 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 12 &4f80 fe fe ff ff ff ff ff ff ff ff ff ff ff ff ff ff &4f90 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff &4fa0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &4fb0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &4fc0 ff ff ff ff ff 7f ff ff ff ff ff ff ff ff ff ff &4fd0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ; polygons_vertices_address_low_table ; 0 1 2 3 4 5 6 7 8 9 a b c d e f &4fe0 b4 b9 bd c2 c7 cc d1 d6 db e0 e5 e9 ed f1 f5 f9 ; &00 &4ff0 fd 01 05 09 0e 12 16 1b 20 25 2a 2f 34 38 3c 41 ; &10 &5000 45 49 4d 51 55 5a 5e 62 67 6b 6f 73 78 7d 82 87 ; &20 &5010 8c 91 96 9b 00 05 0a 0f 14 19 1e 23 27 2b 2f 33 ; &30 &5020 37 3b 3f 43 47 4b 4f 53 57 5b 5f 63 68 cd d2 d6 ; &40 &5030 da de e2 e7 eb ef f4 f8 fc 00 04 08 0c 10 15 19 ; &50 &5040 1d 21 25 29 2d 32 60 65 69 6d 72 77 7c 81 86 8b ; &60 &5050 8f 93 98 9c a0 a4 a8 ac b0 b5 b9 bd c1 c5 ca cf ; &70 &5060 d4 d8 dc e1 e6 eb f0 f5 fa 6d 72 77 7c 81 85 89 ; &80 &5070 8d 91 96 9b a0 a5 aa af a0 a5 aa af a0 a5 aa af ; &90 ; unused &5080 fe fe ff ff ff ff ff ff ff ff ff ff ff ff ff ff &5090 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff &50a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &50b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &50c0 ff ff ff ff ff 7f ff ff ff ff ff ff ff ff ff ff &50d0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff &50e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &50f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &5000 fe fe ff ff ff ff ff ff ff ff ff ff ff ff ff ff &5010 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ; polygons_vertices_address_high_table ; 0 1 2 3 4 5 6 7 8 9 a b c d e f &5120 53 53 53 53 53 53 53 53 53 53 53 53 53 53 53 53 ; &00 &5130 53 54 54 54 54 54 54 54 54 54 54 54 54 54 54 54 ; &10 &5140 54 54 54 54 54 54 54 54 54 54 54 54 54 54 54 54 ; &20 &5150 54 54 54 54 53 53 53 53 53 53 53 53 53 53 53 53 ; &30 &5160 53 53 53 53 53 53 53 53 53 53 53 53 53 49 49 49 ; &40 &5170 49 49 49 49 49 49 49 49 49 4a 4a 4a 4a 4a 4a 4a ; &50 &5180 4a 4a 4a 4a 4a 4a 52 52 52 52 52 52 52 52 52 52 ; &60 &5190 52 52 52 52 52 52 52 52 52 52 52 52 52 52 52 52 ; &70 &51a0 52 52 52 52 52 52 52 52 52 53 53 53 53 53 53 53 ; &80 &51b0 53 53 53 53 53 53 53 53 53 53 53 53 53 53 53 53 ; &90 ; unused &51c0 ff ff ff ff ff 7f ff ff ff ff ff ff ff ff ff ff &51d0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff &51e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &51f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &5200 fe fe ff ff ff ff ff ff ff ff ff ff ff ff ff ff &5210 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff &5220 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &5230 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &5240 ff ff ff ff ff 7f ff ff ff ff ff ff ff ff ff ff &5250 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ; polygon_vertex_data # Bytes are &40 (PLOTTABLES_VERTICES) + vertex numbers ; sentinel_vertex_data # Uses vertices &00 - &1d &5260 54 50 51 55 54 ; polygon &66 &5265 51 4e 55 51 ; polygon &67 &5269 54 4f 50 54 ; polygon &68 &526d 4f 53 52 4e 4f ; polygon &69 &5272 43 47 46 42 43 ; polygon &6a &5277 40 44 47 43 40 ; polygon &6b &527c 42 46 45 41 42 ; polygon &6c &5281 41 45 44 40 41 ; polygon &6d &5286 47 4c 4b 46 47 ; polygon &6e &528b 44 4d 47 44 ; polygon &6f &528f 46 4a 45 46 ; polygon &60 &5293 45 49 48 44 45 ; polygon &61 &5298 47 4d 4c 47 ; polygon &62 &529c 46 4b 4a 46 ; polygon &63 &52a0 44 48 4d 44 ; polygon &64 &52a4 45 4a 49 45 ; polygon &65 &52a8 4d 51 4c 4d ; polygon &66 &52ac 4b 50 4a 4b ; polygon &67 &52b0 4c 51 50 4b 4c ; polygon &68 &52b5 4d 4e 51 4d ; polygon &69 &52b9 4a 50 4f 4a ; polygon &6a &52bd 48 4e 4d 48 ; polygon &6b &52c1 49 4a 4f 49 ; polygon &6c &52c5 49 4f 4e 48 49 ; polygon &6d &52ca 59 5b 5a 58 59 ; polygon &6e &52cf 55 5d 5c 54 55 ; polygon &6f &52d4 4e 58 55 4e ; polygon &60 &52d8 54 59 4f 54 ; polygon &61 &52dc 52 56 58 4e 52 ; polygon &62 &52e1 4f 59 57 53 4f ; polygon &63 &52e6 57 59 58 56 57 ; polygon &64 &52eb 53 57 56 52 53 ; polygon &65 &52f0 5b 5c 5d 5a 5b ; polygon &66 &52f5 58 5a 5d 55 58 ; polygon &67 &52fa 54 5c 5b 59 54 ; polygon &68 ; unused &52ff 10 ; tree_vertex_data # Uses vertices &00 - &10 &5300 48 4f 4e 49 48 ; polygon &34 &5305 49 4e 4d 4a 49 ; polygon &35 &530a 4a 4d 4c 4b 4a ; polygon &36 &530f 41 45 44 40 41 ; polygon &37 &5314 42 46 45 41 42 ; polygon &38 &5319 40 44 47 43 40 ; polygon &39 &531e 43 47 46 42 43 ; polygon &3a &5323 48 50 4f 48 ; polygon &3b &5327 49 50 48 49 ; polygon &3c &532b 4a 50 49 4a ; polygon &3d &532f 4b 50 4a 4b ; polygon &3e &5333 4c 50 4b 4c ; polygon &3f &5337 4d 50 4c 4d ; polygon &40 &533b 4e 50 4d 4e ; polygon &41 &533f 4f 50 4e 4f ; polygon &42 ; boulder_vertex_data # Uses vertices &00 - &07 &5343 41 40 47 41 ; polygon &43 &5347 43 42 41 43 ; polygon &44 &534b 45 44 43 45 ; polygon &45 &534f 47 46 45 47 ; polygon &46 &5353 41 42 40 41 ; polygon &47 &5357 43 44 42 43 ; polygon &48 &535b 45 46 44 45 ; polygon &49 &535f 47 40 46 47 ; polygon &4a &5363 41 47 45 43 41 ; polygon &4b &5368 40 42 44 46 40 ; polygon &4c ; platform_vertex_data # Uses vertices &00 - &0b &536d 41 46 45 40 41 ; polygon &89 &5372 42 48 47 41 42 ; polygon &8a &5377 43 4a 49 42 43 ; polygon &8b &537c 40 44 4b 43 40 ; polygon &8c &5381 40 45 44 40 ; polygon &8d &5385 41 47 46 41 ; polygon &8e &5389 42 49 48 42 ; polygon &8f &538d 43 4b 4a 43 ; polygon &90 &5391 46 47 48 49 46 ; polygon &91 &5396 46 49 4a 45 46 ; polygon &92 &539b 45 4a 4b 44 45 ; polygon &93 ; block_vertex_data # Uses vertices &00 - &07 &53a0 40 44 47 43 40 ; polygon &94, &98 and &9c &53a5 42 46 45 41 42 ; polygon &95, &99 and &9d &53aa 41 45 44 40 41 ; polygon &96, &9a and &9e &53af 45 46 47 44 45 ; polygon &97, &9b and &9f ; robot_vertex_data # Uses vertices &00 - &1c &53b4 4b 4a 48 4c 4b ; polygon &00 &53b9 4a 49 48 4a ; polygon &01 &53bd 54 53 52 55 54 ; polygon &02 &53c2 40 44 47 43 40 ; polygon &03 &53c7 42 46 45 41 42 ; polygon &04 &53cc 43 47 46 42 43 ; polygon &05 &53d1 41 45 44 40 41 ; polygon &06 &53d6 48 4d 50 4c 48 ; polygon &07 &53db 4b 4f 4e 4a 4b ; polygon &08 &53e0 4c 50 4f 4b 4c ; polygon &09 &53e5 4a 5c 49 4a ; polygon &0a &53e9 49 5c 48 49 ; polygon &0b &53ed 4a 4e 5c 4a ; polygon &0c &53f1 48 5c 4d 48 ; polygon &0d &53f5 4e 51 5c 4e ; polygon &0e &53f9 5c 51 4d 5c ; polygon &0f &53fd 4f 51 4e 4f ; polygon &10 &5401 4d 51 50 4d ; polygon &11 &5405 50 51 4f 50 ; polygon &12 &5409 53 5a 5b 52 53 ; polygon &13 &540e 52 5b 55 52 ; polygon &14 &5412 54 5a 53 54 ; polygon &15 &5416 5b 56 59 55 5b ; polygon &16 &541b 54 58 57 5a 54 ; polygon &17 &5420 55 59 58 54 55 ; polygon &18 &5425 57 58 59 56 57 ; polygon &19 &542a 5a 57 56 5b 5a ; polygon &1a ; sentry_vertex_data # Uses vertices &00 - &15 &542f 43 48 47 42 43 ; polygon &1b &5434 40 49 43 40 ; polygon &1c &5438 42 46 41 42 ; polygon &1d &543c 41 45 44 40 41 ; polygon &1e &5441 43 49 48 43 ; polygon &1f &5445 42 47 46 42 ; polygon &20 &5449 41 46 45 41 ; polygon &21 &544d 40 44 49 40 ; polygon &22 &5451 49 4d 48 49 ; polygon &23 &5455 48 4d 4c 47 48 ; polygon &24 &545a 47 4c 46 47 ; polygon &25 &545e 46 4b 45 46 ; polygon &26 &5462 45 4b 4a 44 45 ; polygon &27 &5467 44 4a 49 44 ; polygon &28 &546b 49 4a 4d 49 ; polygon &29 &546f 46 4c 4b 46 ; polygon &2a &5473 4b 4f 4e 4a 4b ; polygon &2b &5478 4d 55 54 4c 4d ; polygon &2c &547d 4a 52 55 4d 4a ; polygon &2d &5482 4c 54 53 4b 4c ; polygon &2e &5487 53 54 55 52 53 ; polygon &2f &548c 51 53 52 50 51 ; polygon &30 &5491 4e 50 52 4a 4e ; polygon &31 &5496 4b 53 51 4f 4b ; polygon &32 &549b 4f 51 50 4e 4f ; polygon &33 ; plottables_screen_x_low &54a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &54b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &54c0 ff ff ff ff ff 7f ff ff ff ff ff ff ff ff ff ff &54d0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff &54e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &54f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; plottables_relative_h_angle_high &5500 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff &5510 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff &5520 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &5530 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &5540 ff ff ff ff ff 7f ff ff ff ff ff ff ff ff ff ff &5550 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ; leave_with_zero &5560 85 7e STA &7e ; partial_angle_tangent &5562 85 8a STA &8a ; angle_low &5564 85 8b STA &8b ; angle_high &5566 60 RTS ; calculate_angle &5567 a5 85 LDA &85 ; y # Determine which of x and y is the largest &5569 c5 83 CMP &83 ; x # (both are unsigned, sign is in signed_x and signed_y) &556b 90 08 BCC &5575 ; x_is_larger_than_y &556d d0 19 BNE &5588 ; y_is_larger_than_x &556f a5 82 LDA &82 ; y_fraction &5571 c5 80 CMP &80 ; x_fraction &5573 b0 13 BCS &5588 ; y_is_larger_than_x ; x_is_larger_than_y &5575 a5 85 LDA &85 ; y &5577 85 5d STA &5d ; min_x_y &5579 a5 82 LDA &82 ; y_fraction &557b 85 5c STA &5c ; min_x_y_fraction &557d a5 80 LDA &80 ; x_fraction &557f 85 7a STA &7a ; max_x_y_fraction &5581 a5 83 LDA &83 ; x &5583 85 7b STA &7b ; max_x_y &5585 4c a5 55 JMP &55a5 ; scale_using_x # If x is larger, scale x and y using x ; y_is_larger_than_x &5588 a5 83 LDA &83 ; x &558a 85 5d STA &5d ; min_x_y &558c a5 80 LDA &80 ; x_fraction &558e 85 5c STA &5c ; min_x_y_fraction &5590 a5 82 LDA &82 ; y_fraction &5592 85 7a STA &7a ; max_x_y_fraction &5594 a5 85 LDA &85 ; y &5596 85 7b STA &7b ; max_x_y &5598 05 82 ORA &82 ; y_fraction &559a f0 c4 BEQ &5560 ; leave_with_zero # If y = 0 (and so x = 0), leave with zero &559c a5 85 LDA &85 ; y &559e 4c e3 55 JMP &55e3 ; scale_using_y # If y is larger, scale x and y using y ; scale_using_x_loop &55a1 06 82 ASL &82 ; y_fraction # If no overflow, multiply y by 2 &55a3 26 85 ROL &85 ; y ; scale_using_x &55a5 06 80 ASL &80 ; x_fraction # Multiply x by 2 &55a7 2a ROL A &55a8 90 f7 BCC &55a1 ; scale_using_x_loop # Repeat until x overflows &55aa 6a ROR A &55ab 66 80 ROR &80 ; x_fraction # Divide x by 2 to avoid overflow &55ad 85 76 STA &76 ; b &55af a5 82 LDA &82 ; y_fraction &55b1 85 74 STA &74 ; a_fraction &55b3 a5 80 LDA &80 ; x_fraction &55b5 29 fc AND #&fc # Remove noise caused by scaling &55b7 85 77 STA &77 ; b_fraction &55b9 a5 85 LDA &85 ; y (a) # Use y for a, x for b &55bb 20 4a 0d JSR &0d4a ; calculate_partial_angle # Calculate angle (up to 45 degrees) from x / y &55be a5 86 LDA &86 ; signed_x &55c0 45 88 EOR &88 ; signed_y &55c2 30 0d BMI &55d1 ; skip_x_inversion &55c4 a9 00 LDA #&00 # If x and y are the same sign, &55c6 38 SEC &55c7 e5 8a SBC &8a ; angle_low # then angle = -angle &55c9 85 8a STA &8a ; angle_low &55cb a9 00 LDA #&00 &55cd e5 8b SBC &8b ; angle_high &55cf 85 8b STA &8b ; angle_high ; skip_x_inversion &55d1 a9 40 LDA #&40 # Add 90 degrees if x is positive &55d3 24 86 BIT &86 ; signed_x &55d5 10 02 BPL &55d9 ; set_angle_high_x &55d7 a9 c0 LDA #&c0 # or 270 degrees if x is negative ; set_angle_high_x &55d9 18 CLC &55da 65 8b ADC &8b ; angle_high &55dc 85 8b STA &8b ; angle_high &55de 60 RTS ; scale_using_y_loop &55df 06 80 ASL &80 ; x_fraction # If no overflow, multiply x by 2 &55e1 26 83 ROL &83 ; x ; scale_using_y &55e3 06 82 ASL &82 ; y_fraction # Multiply y by 2 &55e5 2a ROL A &55e6 90 f7 BCC &55df ; scale_using_y_loop # Repeat until y overflows &55e8 6a ROR A &55e9 66 82 ROR &82 ; y_fraction # Divide y by 2 to avoid overflow &55eb 85 76 STA &76 ; b &55ed a5 80 LDA &80 ; x_fraction &55ef 85 74 STA &74 ; a_fraction &55f1 a5 82 LDA &82 ; y_fraction &55f3 29 fc AND #&fc # Remove noise caused by scaling &55f5 85 77 STA &77 ; b_fraction &55f7 a5 83 LDA &83 ; x (a) # Use x for a, y for b &55f9 20 4a 0d JSR &0d4a ; calculate_partial_angle # Calculate angle (up to 45 degrees) from y / x &55fc a5 86 LDA &86 ; signed_x &55fe 45 88 EOR &88 ; signed_y &5600 10 0d BPL &560f ; skip_y_inversion &5602 a9 00 LDA #&00 # If x and y are opposite signs, &5604 38 SEC &5605 e5 8a SBC &8a ; angle_low # then angle = -angle &5607 85 8a STA &8a ; angle_low &5609 a9 00 LDA #&00 &560b e5 8b SBC &8b ; angle_high &560d 85 8b STA &8b ; angle_high ; skip_y_inversion &560f a9 00 LDA #&00 &5611 24 88 BIT &88 ; signed_y &5613 10 02 BPL &5617 ; set_angle_high_y &5615 a9 80 LDA #&80 # Add 180 degrees if y is negative ; set_angle_high_y &5617 18 CLC &5618 65 8b ADC &8b ; angle_high &561a 85 8b STA &8b ; angle_high &561c 60 RTS ; calculate_object_relative_vertical_angle &561d 85 86 STA &86 ; signed_x &561f a8 TAY &5620 10 0b BPL &562d ; skip_inversion &5622 a9 00 LDA #&00 # If x is negative, make it positive &5624 38 SEC &5625 e5 80 SBC &80 ; x_fraction &5627 85 80 STA &80 ; x_fraction &5629 a9 00 LDA #&00 &562b e5 86 SBC &86 ; signed_x ; skip_inversion &562d 85 83 STA &83 ; x &562f a5 7c LDA &7c ; h_low &5631 85 82 STA &82 ; y_fraction &5633 a5 7d LDA &7d ; h_high &5635 85 85 STA &85 ; y &5637 a9 00 LDA #&00 &5639 85 88 STA &88 ; signed_y &563b 20 67 55 JSR &5567 ; calculate_angle # Calculate angle from x and h &563e a5 8a LDA &8a ; angle_low &5640 38 SEC &5641 e9 20 SBC #&20 &5643 85 50 STA &50 ; object_relative_v_angle_low &5645 a5 8b LDA &8b ; angle_high &5647 fd 40 01 SBC &0140,X ; objects_v_angle # Apply object vertical rotation &564a 08 PHP &564b 4a LSR A &564c 66 50 ROR &50 ; object_relative_v_angle_low &564e 4a LSR A &564f 66 50 ROR &50 ; object_relative_v_angle_low &5651 4a LSR A &5652 66 50 ROR &50 ; object_relative_v_angle_low &5654 4a LSR A &5655 66 50 ROR &50 ; object_relative_v_angle_low # Divide by 16 &5657 28 PLP &5658 10 02 BPL &565c ; not_negative &565a 09 f0 ORA #&f0 # Keep sign if negative ; not_negative &565c 85 8d STA &8d ; object_relative_v_angle_high &565e 60 RTS ; calculate_hypotenuse # Return a fast approximation to the hypotenuse &565f 8c 8d 56 STY &568d ; tmp_y # Preserve Y on exit &5662 a5 7e LDA &7e ; partial_angle_tangent # Calculated in calculate angle &5664 4a LSR A &5665 69 00 ADC #&00 &5667 a8 TAY &5668 b9 02 3d LDA &3d02,Y ; hypotenuse_approximation_factor_table &566b 85 75 STA &75 ; multiplicand &566d a5 5c LDA &5c ; min_x_y_fraction &566f 85 74 STA &74 ; multiplier_low &5671 a5 5d LDA &5d ; min_x_y &5673 85 76 STA &76 ; multiplier_high &5675 20 4a 0f JSR &0f4a ; multiply_double_by_byte # Multiply f(angle) * min_x_y &5678 46 75 LSR &75 ; a # Halve &567a 66 74 ROR &74 ; a_fraction &567c a5 74 LDA &74 ; a_fraction # Add max_x_y &567e 18 CLC &567f 65 7a ADC &7a ; max_x_y_fraction &5681 85 7c STA &7c ; h_low &5683 a5 75 LDA &75 ; a &5685 65 7b ADC &7b ; max_x_y &5687 85 7d STA &7d ; h_high # Return (f(angle) * min_x_y / 2) + max_x_y &5689 ac 8d 56 LDY &568d ; tmp_y &568c 60 RTS ; tmp_y &568d 00 ; shuffle_rnd # Used only for random screen effects &568e ad cf 56 LDA &56cf ; rnd_state &5691 8d d2 56 STA &56d2 ; rnd_state + 3 &5694 ad d0 56 LDA &56d0 ; rnd_state + 1 &5697 8d d3 56 STA &56d3 ; rnd_state + 4 &569a ad d1 56 LDA &56d1 ; rnd_state + 2 &569d 8d d4 56 STA &56d4 ; rnd_state + 5 &56a0 0e d2 56 ASL &56d2 ; rnd_state + 3 &56a3 2e d3 56 ROL &56d3 ; rnd_state + 4 &56a6 2e d4 56 ROL &56d4 ; rnd_state + 5 &56a9 0e d2 56 ASL &56d2 ; rnd_state + 3 &56ac 2e d3 56 ROL &56d3 ; rnd_state + 4 &56af 2e d4 56 ROL &56d4 ; rnd_state + 5 &56b2 ad cf 56 LDA &56cf ; rnd_state &56b5 18 CLC &56b6 6d d2 56 ADC &56d2 ; rnd_state + 3 &56b9 8d cf 56 STA &56cf ; rnd_state &56bc ad d0 56 LDA &56d0 ; rnd_state + 1 &56bf 6d d3 56 ADC &56d3 ; rnd_state + 4 &56c2 8d d0 56 STA &56d0 ; rnd_state + 1 &56c5 ad d1 56 LDA &56d1 ; rnd_state + 2 &56c8 6d d4 56 ADC &56d4 ; rnd_state + 5 &56cb 8d d1 56 STA &56d1 ; rnd_state + 2 &56ce 60 RTS ; rnd_state &56cf 01 00 00 01 00 00 ; fade_to_white &56d5 a9 80 LDA #&80 # Set top bit to fade to white (colour 2) &56d7 d0 02 BNE &56db ; set_use_eor_for_fade ; fade_to_black &56d9 a9 00 LDA #&00 # Clear top bit to fade to black (colour 1) ; set_use_eor &56db 8d 2f 57 STA &572f ; use_eor_for_fade &56de a9 50 LDA #&50 # Replace 80 pixels &56e0 8d 2e 57 STA &572e ; fade_count ; fade_loop &56e3 20 8e 56 JSR &568e ; shuffle_rnd # Pick a random pixel &56e6 85 22 STA &22 ; random_one &56e8 ad d0 56 LDA &56d0 ; rnd_state + 1 &56eb 85 2a STA &2a ; random_two &56ed 29 1f AND #&1f # Use bottom six bits of random_two for group &56ef c9 1e CMP #&1e &56f1 b0 f0 BCS &56e3 ; fade_loop &56f3 85 23 STA &23 ; screen_address_high &56f5 ad c2 0c LDA &0cc2 ; viewport_screen_address_low &56f8 18 CLC &56f9 65 22 ADC &22 ; random_one # Use random_one for byte offset in group &56fb 85 22 STA &22 ; screen_address_low &56fd ad c3 0c LDA &0cc3 ; viewport_screen_address_high &5700 65 23 ADC &23 ; screen_address_high &5702 c9 80 CMP #&80 &5704 90 02 BCC &5708 ; skip_wraparound &5706 e9 20 SBC #&20 ; skip_wraparound &5708 85 23 STA &23 ; screen_address_high &570a a5 2a LDA &2a ; random_two # Use top two bits of random_two for pixel &570c 2a ROL A &570d 2a ROL A &570e 2a ROL A &570f 29 03 AND #&03 &5711 aa TAX &5712 a0 00 LDY #&00 &5714 bd 7f 22 LDA &227f,X ; colour_3_pixel_values &5717 49 ff EOR #&ff &5719 31 22 AND (&22),Y ; screen_address # Remove chosen pixel &571b 1d 30 57 ORA &5730,X ; fade_pixel_values # Replace with black pixel &571e 2c 2f 57 BIT &572f ; use_eor_for_fade &5721 10 03 BPL &5726 ; skip_eor # If top bit set, &5723 5d 7f 22 EOR &227f,X ; colour_3_pixel_values # Replace with white pixel ; skip_eor &5726 91 22 STA (&22),Y ; screen_address &5728 ce 2e 57 DEC &572e ; fade_count &572b d0 b6 BNE &56e3 ; fade_loop &572d 60 RTS ; fade_count &572e 00 ; use_eor_for_fade &572f 00 ; colour_1_pixel_values ; 0 1 2 3 &5730 08 04 02 01 ; set_bar_state_and_delay &5734 8d 04 0c STA &0c04 ; bar_state &5737 a2 28 LDX #&28 ; delay_loop &5739 88 DEY &573a d0 fd BNE &5739 ; delay_loop &573c ca DEX &573d d0 fa BNE &5739 ; delay_loop &573f 60 RTS ; unused &5740 ff ff ff ff ; plot_small_character &5744 2c 0f 0c BIT &0c0f ; force_single_colour_characters # Top bit set to plot characters in one colour &5747 30 1e BMI &5767 ; to_oswrch &5749 c9 20 CMP #&20 # If it isn't a control character, &574b 90 1a BCC &5767 ; to_oswrch &574d c9 7f CMP #&7f &574f b0 16 BCS &5767 ; to_oswrch &5751 8d 96 57 STA &5796 ; double_colour_character_string # Poke character twice into string &5754 8d a1 57 STA &57a1 ; double_colour_character_string + 11 &5757 8a TXA &5758 48 PHA &5759 a2 16 LDX #&16 ; plot_double_colour_loop &575b bd 96 57 LDA &5796,X ; double_colour_character_string &575e 20 ee ff JSR &ffee ; OSWRCH # Write string &5761 ca DEX &5762 10 f7 BPL &575b ; plot_double_colour_loop &5764 68 PLA &5765 aa TAX &5766 60 RTS ; to_oswrch &5767 4c ee ff JMP &ffee ; OSWRCH ; plot_actual_character &576a c9 19 CMP #&19 # Is it the start of a plot command? &576c f0 07 BEQ &5775 ; is_plot &576e 4c 44 57 JMP &5744 ; plot_small_character ; plot_plot_command_untokenised &5771 c8 INY &5772 b9 96 57 LDA &5796,Y ; text_strings ; is_plot &5775 20 ee ff JSR &ffee ; OSWRCH # If so, plot six characters untokenised &5778 ce 83 57 DEC &5783 ; plot_count &577b d0 f4 BNE &5771 ; plot_plot_command_untokenised &577d a9 06 LDA #&06 # Reset for next time &577f 8d 83 57 STA &5783 ; plot_count &5782 60 RTS ; plot_count &5783 06 ; text_offsets &5784 17 1d 30 39 4e 57 5d 60 67 6e 75 7a 7f 86 90 a2 &5794 a8 ac ; text_strings ; double_colour_character_string # Bytes used in reverse order &5796 43 ; "A" &5797 02 00 12 ; GCOL 0,2 &579a 00 04 00 00 00 19 ; MOVE BY &0000, &0004 &57a0 08 ; Move cursor right &57a1 43 ; "A" &57a2 03 00 12 ; GCOL 0,3 &57a5 ff fc 00 00 00 19 ; MOVE BY &0000, &fffc &57ab 7f 20 ; SPACE, BACKSPACE ; string &00 &57ad d2 ; string &0a : write at graphics cursor, foreground colour 0 &57ae d4 ; string &0c : MOVE &0040, &00a0 &57af d9 ; string &11 : "PRESS ANY KEY" &57b0 11 81 ; COLOUR &81 &57b2 ff ; string &01 &57b3 d4 ; string &0c : MOVE &0040, &00a0 &57b4 d7 ; string &0f : " " &57b5 d7 ; string &0f : " " &57b6 d8 ; string &10 : " " &57b7 cf ; string &07 : MOVE &0040, &0300 &57b8 d5 ; string &0d : "LANDSCAPE" &57b9 20 4e 55 4d 42 45 52 3f ; " NUMBER ?" &57c1 04 ; Write at text cursor &57c2 1f 05 1b ; TAB(&05, &1b) &57c5 ff ; string &02 &57c6 d2 ; string &0a : write at graphics cursor, foreground colour 0 &57c7 cf ; string &07 : MOVE &0040, &0300 &57c8 d6 ; string &0e : "SECRET ENTRY CODE" &57c9 3f ; "?" &57ca 04 ; Write at text cursor &57cb 1f 03 1b ; TAB(&03, &1b) &57ce ff ; string &03 &57cf d2 ; string &0a : write at graphics cursor, foreground colour 0 &57d0 cf ; string &07 : MOVE &0040, &0300 &57d1 57 52 4f 4e 47 20 53 45 43 52 45 54 20 43 4f 44 ; "WRONG SECRET CODE" &57e1 45 &57e2 c8 ; string &00 : "PRESS ANY KEY" &57e3 ff ; string &04 &57e4 d3 ; string &0b : write at graphics cursor, foreground colour 1 &57e5 d1 ; string &09 : MOVE &00c0, &0040 &57e6 d9 ; string &11 : "PRESS ANY KEY" &57e7 cf ; string &07 : MOVE &0040, &0300 &57e8 09 ; cursor right &57e9 09 ; cursor right &57ea d5 ; string &0d : "LANDSCAPE" &57eb 09 ; cursor right &57ec ff ; string &05 &57ed cf ; string &07 : MOVE &0040, &0300 &57ee d6 ; string &0e : "SECRET ENTRY CODE" 757ef d0 ; string &08 : MOVE &00c0, &02c0 &57f0 d5 ; string &0d : "LANDSCAPE" &57f1 09 ; cursor right &57f2 ff ; string &06 &57f3 d4 ; string &0c : MOVE &0040, &00a0 &57f4 d9 ; string &11 : "PRESS ANY KEY" &57f5 ff ; string &07 &57f6 19 04 40 00 00 03 ; MOVE &0040, &0300 &57fc ff ; string &08 &57fd 19 04 c0 ; MOVE &00c0, &02c0 # .. .. .. 00 c0 02 # continued at &5800 ; decrypted_entry_point &6400 a0 00 LDY #&00 &6402 84 70 STY &70 ; target_address_low &6404 84 72 STY &72 ; source_address_low &6406 a9 0d LDA #&0d &6408 85 71 STA &71 ; target_address_high &640a a9 19 LDA #&19 &640c 85 73 STA &73 ; source_address_high &640e a2 4b LDX #&4b ; move_1900_to_63ff_loop # Move &1900 - &63ff to &0d00 - &57ff &6410 b1 72 LDA (&72),Y ; source_address &6412 91 70 STA (&70),Y ; target_address &6414 c8 INY &6415 d0 f9 BNE &6410 ; move_1900_to_63ff_loop &6417 e6 71 INC &71 ; target_address_high &6419 e6 73 INC &73 ; source_address_high &641b ca DEX &641c d0 f2 BNE &6410 ; move_1900_to_63ff_loop &641e 4c 00 3f JMP &3f00 ; relocated_entry_point ; unused &6421 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &6431 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &6441 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &6451 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &6461 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &6471 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &6481 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &6491 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &64a1 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &64b1 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &64c1 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &64d1 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &64e1 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &64f1 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00