/* * This work was originally done by Fred Taft (fred@hp-pcd.cv.hp.com). * Please forward any comments, corrections or additions back to Fred. * * Mine Storm */ .org 0xE000; MS_header: .word #MS_music; /* Mine Storm music block pointer */ .byte 0xF8; /* height = -8 */ .byte 0x50; /* width = 80 */ .byte 0x30; /* rel y = 48 */ .byte 0xE8; /* rel x = -24 */ .byte "MINE",0x80; .byte 0xF8; /* height = -8 */ .byte 0x50; /* width = 80 */ .byte 0x00; /* rel y = 0 */ .byte 0xDE; /* rel x = -34 */ .byte "STORM",0x80,0x00; /* Start of the Mine Storm game */ MS_start: ldx #0xC883; /* Clear C883 - CBC5 */ PE019: clr ,x+; cmpx #0xCBC5; bne PE019; jsr $init_motion_dots; inc $C824; lda #0xBB; sta $C880; /* Store console enable flags in C880 */ ldx #0x0101; stx $C881; /* Store jstick mak in C881 */ restart_game: ldx #0xC883; /* Clear C883 - CB70 */ PE034: clr ,x+; cmpx #0xCB70; bne PE034; bra PE03D; PE03D: jsr $dptoC8; ldd #0x0200; /* Ask for number of players */ jsr $get_players_game; dec 0x79; /* C879 contains # of players selected */ clr 0x56; clr 0x9B; /* C89B specifies which player is active */ ldx #0xC8A8; jsr $set_dft_score; /* Initialize player 1 score */ ldx #0xC8AF; jsr $set_dft_score; /* Initialize player 2 score */ ldx #0xC8F9; jsr $set_dft_score; /* Initialize mine field # for player 1 */ ldd #0x0001; jsr $add_d_to_x_in_bcd; ldx #0xC900; jsr $set_dft_score; /* Initialize mine field # for player 2 */ ldd #0x0001; jsr $add_d_to_x_in_bcd; ldx #mines_at_level_x; /* Initialize player 1 & 2's pointers */ stx 0xC4; /* into the structure describing which */ stx 0xC6; /* mines are at which level. */ lda #0x05; sta 0xD9; /* Initialize # of ships for active player */ sta 0xDA; /* Initialize # of ships for player 1 */ sta 0xDB; /* Initialize # of ships for player 2 */ bra restart_same_level; start_next_mine_level: jsr $bring_ship_to_center; ldy #0xC8C4; /* Increment the pointer into the struct */ lda 0x9B; /* describing which mines are at which */ ldx a,y; /* level, for the active player. */ leax 4,x; stx a,y; ldx #mine_field_level_pointers; lda 0x9B; /* Check the mine field level for */ ldx a,x; /* the active player, and if it is */ lda 5,x; /* a multiple of 4, then award that */ anda #0x03; /* player a bonus ship. */ bne goto_next_mine_level; inc 0xD9; goto_next_mine_level: ldd #0x0001; /* Increment active players mine */ jsr $add_d_to_x_in_bcd; /* field level indicator. */ restart_same_level: jsr $init_RAM_for_new_level; ldx #0xC8C4; /* Use the pointer in C8C4 (player 1) */ lda 0x9B; /* or C8C6 (player 2) to determine */ ldx a,x; /* which mines are at this level. */ lda ,x; bmi check_4_end_of_game; /* After level 13, skip any level whose */ jsr $seed_the_screen; /* first byte is negative. */ bra main_loop; check_4_end_of_game: ldd 0xF0; /* If C8F0 counts down to 0, then */ subd #0x0001; /* start a new game; used as a delay */ std 0xF0; /* after a game is over. */ beq start_a_new_game; /* Display 'game over' string, & both scores */ pshs dp; jsr $dptoD0; jsr $display_both_scores; ldu #game_over; jsr $MS_print_1_string7F; puls dp; lda 0x0F; /* If no buttons pressed, then see if */ beq main_loop; /* we should goto advanced levels. */ /* * Find out if a new hi score is available, * and then start up a new game. */ start_a_new_game: ldx #0xC8A8; /* See if player 1 has new hi score */ ldu #0xCBEB; jsr $check_4_new_hi_score; ldx #0xC8AF; /* See if player 2 has new hi score */ ldu #0xCBEB; jsr $check_4_new_hi_score; ldd 0xF0; lbne restart_game; /* Just start a new game */ jsr $reinit; clr 0x3B; /* Flag that a hi score should be displayed */ lds #0xCBEA; jmp $powerup_loop; /* Go thru a complete reset sequence */ /* * This is the main loop during a players turn. Control will * remain in this loop, until either a player clears a level, * or has his ship destroyed. */ main_loop: pshs dp; jsr $process_indirect_jumps; jsr $draw_non_dead_mines; jsr $process_jstick_and_buttons; jsr $process_bullets; jsr $reseed_screen; puls dp; jsr $check_bullets_for_hits; jsr $check_ship_4_mine_collision; jsr $check_ship_4_enemy_collision; jsr $display_explosion_pattern; blo main_loop; lda 0xBD; /* See if cmd ship is disabled */ lbeq start_next_mine_level; lda 0xBE; /* See if the game is over */ lbne check_4_end_of_game; jmp $restart_same_level; /* * This block of code is responsible for seeding the screen * at the beginning of play. It will draw the enemy ship * as it moves from the top of the screen to the bottom. * After the field has been completely seeded, the mine * field number will be displayed. * I think that the x register, at entry, points to a * structure describing the types of mines for this level. */ seed_the_screen: stx 0xC2; /* Save pointer to mine info */ ldd #0x7F00; std 0xDC; /* C8DC contains enemy ship coords */ sta 0xB7; /* C8B7 is used as loop cntr (0x7F) */ lda #0x20; sta 0x9C; /* Set up 1 of the indirect */ ldx #generate_mine_coords; /* jump ptrs, to generate some */ stx 0x9D; /* mine coordinates. */ ldx #0xC933; /* Load mine buf addr into C8B9-C8BA */ stx 0xB9; lda #0x1D; /* Load # of mines into C8B8 */ sta 0xB8; clr 0x56; ldu #MS_music; jsr $init_sound2; continue_seeding_screen: pshs dp; jsr $PE711; /* Draw active players ship count */ jsr $init_sound; lda 0x26; /* Decrement our loop counter, C8B7 */ bita #0x01; /* every other pass. */ bne PE15C; dec 0xB7; PE15C: jsr $process_indirect_jumps; /* This block draws the enemy ship */ jsr $display_both_scores; jsr $do_sound; jsr $draw_non_dead_mines; jsr $intensity_to_5F; ldb $C8B7; /* Wait until screen is seeded, */ beq activate_4_mines; /* then activate 1st 4 mines. */ ldx #enemy_ship_pt1; ldy $C8DC; jsr $move_y_draw_x; /* Draw the enemy ship */ ldx #enemy_ship_pt2; jsr $move_y_draw_x; ldx #enemy_ship_pt3; jsr $move_y_draw_x; puls dp; dec 0xDC; /* Decrement y coord for enemy ship */ bra continue_seeding_screen; /* * This routine is called at the beginning of a turn for the * active player. It will activate the first 4 mines for * the level. The type of mines activated are controlled * by the array whose pointer is contained in C8C2. */ activate_4_mines: puls dp; clr 0x9C; lda #0x04; /* Use C8B7 as a loop counter */ sta 0xB7; /* Use C8B8 as a delay counter; when it */ lda #0x7F; /* = 0, stop displaying the mine field */ sta 0xB8; /* level, and activate 1st 4 mine. */ PE198: lda 0xB7; beq PE1E6; /* Exit when loop counter = 0 */ ldb 0xB8; beq PE1A4; /* Don't activate the mines until the */ dec 0xB8; /* counter in C8B8 = 0; this allows the */ bra PE1B6; /* mine field level to stay displayed awhile */ PE1A4: ldb 0x26; andb #0x1F; /* After every 31 times thru waitrecal(), */ bne PE1B6; /* activate another mine. */ deca; sta 0xB7; /* Decrement loop counter */ ldx 0xC2; lda a,x; /* Determine what type mine this should be */ ldb #0x03; /* Set generation number to 'largest'. */ jsr $activate_a_mine; PE1B6: pshs dp; jsr $process_indirect_jumps; /* Tell user what mine field number this is */ jsr $intensity_to_7F; ldu #mine_field; jsr $MS_print_1_string7F; ldy #0xE0F8; ldu #mine_field_level_pointers; lda $C89B; ldu a,u; jsr $move_y_print_string; jsr $draw_non_dead_mines; jsr $process_jstick_and_buttons; jsr $process_bullets; puls dp; jsr $check_bullets_for_hits; jsr $display_explosion_pattern; bra PE198; PE1E6: rts; /* * This block of code generates random coordinates * for a single mine. C8B9 contains a pointer to * the appropriate entry in the mine structure. * Before exiting, this routine will update the * pointer in C8B9 to point to the next entry. */ generate_mine_coords: dec 0xB8; /* Decrement the mine counter (C8B8) */ beq PE239; /* and exit when it equals 0. */ inc 0xED; /* Inc mine dot counter */ jsr $get_random_a; anda #0x07; /* Reset the ind jump counter, to */ adda #0x04; /* generate the next set of mine */ sta 0x9C; /* coordinates. */ ldu 0xB9; /* Load u register w/ mine structure ptr */ lda #0x80; /* Flag that a mine is in this slot */ sta ,u; /* Get the coordinates of enemy ship */ ldd 0xDC; adda #0x08; sta 4,u; /* Save y coordinate in 4th slot */ clr 5,u; stb 6,u; /* Save x coordinate in 6th slot */ clr 7,u; generate_final_x_for_mine: jsr $get_random_a; /* Generate the final x position for */ tsta; /* this mine. */ bmi PE21A; cmpa #0x10; bge PE214; adda #0x0C; PE214: cmpa #0x60; ble PE226; bra generate_final_x_for_mine; PE21A: cmpa #0xF0; ble PE220; suba #0x0C; PE220: cmpa #0xA0; bge PE226; bra generate_final_x_for_mine; PE226: sta 0x11,u; tfr a,b; /* Determine what the delta movement */ sex; /* value will be for this mine: -1 or 1 */ ora #0x01; sta 0x10,u; clr 2,u; /* Start with a scale factor of 0 */ leay 0x12,u; /* Update C8B9 to point to next entry */ sty 0xB9; PE239: rts; final_mine_scale_factor: .byte 0x00; .byte 0x02; /* Small */ .byte 0x07; /* Medium */ .byte 0x10; /* Large */ mine_velocity: .byte 0x00; .byte 0x20; /* Small */ .byte 0x18; /* Medium */ .byte 0x10; /* Large */ mine_values: .byte 0x01; /* dumb mine = BCD 100 */ .byte 0x00; .byte 0x05; /* magnetic mine = BCD 500 */ .byte 0x00; .byte 0x03; /* fireball mine = BCD 325 */ .byte 0x25; .byte 0x07; /* magnetic fireball mine = BCD 750 */ .byte 0x50; extra_mine_value: .byte 0x00; .byte 0x00; .byte 0x01; /* Small (100) */ .byte 0x00; .byte 0x00; /* Medium (35) */ .byte 0x35; .byte 0x00; /* Large (0) */ .byte 0x00; mine_sizes: .byte 0x00; .byte 0x00; .byte 0x04; /* Small */ .byte 0x04; .byte 0x08; /* Medium */ .byte 0x08; .byte 0x0D; /* Large */ .byte 0x0D; /* Table of pointers to mine vectors */ mine_vector_ptrs: .word #dumb_mine; .word #magnetic_mine; .word #fireball_mine; .word #magnetic_fireball_mine; /* * This block of code processe the hyperspace and move * buttons. It also checks the state of the joystick, * and updates the command ship rotation value, kept * in C8D4, if the joystick is not centered. If the * joystick is left, then the rotation value is * incremented; if it is to the right, then the rotation * value is decremented. After updating the rotation * value, the command ship will be redrawn at its * current position (specified in C8C8-C8C9). */ process_jstick_and_buttons: pshs dp; lda #0xC8; tfr a,dp; lda 0xBD; /* Skip if cmd ship disabled */ lbne PE30A; lda 0xEE; /* Continue hyperspace, if active */ lbne continue_hyperspace; lda 0x13; /* Check the hyperspace button (2) */ lbne hyperspace; lda 0x14; /* Check the move button (3) */ beq no_ship_movement; /* * The following block of code takes care of * generating the new command coordinates, * because it is moving. It attempts to make * all turns into smooth motion. */ lda 0xD4; cmpa 0xD6; beq PE2A0; cmpa 0xD8; beq PE290; lda 0xD5; beq PE2A0; lda 0xD7; bne no_ship_movement; PE290: lda 0xD7; adda #0x0C; cmpa #0x7F; bhi no_ship_movement; sta 0xD7; lda 0xD4; sta 0xD8; bra PE2AE; PE2A0: lda 0xD5; adda #0x0C; cmpa #0x7F; bhi no_ship_movement; sta 0xD5; lda 0xD4; sta 0xD6; PE2AE: inc 0xF2; /* Flag that a sound should be made */ no_ship_movement: lda 0xD5; beq PE2C2; suba #0x02; sta 0xD5; ldb 0xD6; jsr $calc_rise_run1; sty 0xCC; stx 0xCE; PE2C2: lda 0xD7; beq PE2D4; suba #0x02; sta 0xD7; ldb 0xD8; jsr $calc_rise_run1; /* Generate new rise & run */ sty 0xD0; stx 0xD2; PE2D4: ldd 0xC8; /* Update cmd ships y coord */ addd 0xCC; addd 0xD0; std 0xC8; ldd 0xCA; /* Update cmd ships x coord */ addd 0xCE; addd 0xD2; std 0xCA; /* Check joystick state (in C81B) */ lda 0x1B; beq draw_cmd_ship1; /* Joystick is centered */ bmi PE2EE; dec 0xD4; /* Joystick to right */ bra draw_cmd_ship2; PE2EE: inc 0xD4; /* Joystick to left */ bra draw_cmd_ship2; PE2F2: pshs dp; /* * draw_cmd_ship2() runs the command ships coordinates * thru a rotation transformation, and then draws the * command ship using the transforms coordinates. * draw_cmd_ship1() draws the command ship using the * current transformed ship coordinates. */ draw_cmd_ship2: jsr $rotate_cmd_ship; /* Rotate the command ship */ draw_cmd_ship1: lda #0xD0; tfr a,dp; jsr $intensity_to_5F; ldb #0x0C; ldy #0xC8C8; /* Draw the xformed command ship */ ldx #0xCB89; jsr $move_y_draw_x2; PE30A: puls dp,pc; /* This routine handles the hyperspace request */ hyperspace: lda #0x80; sta 0xEE; jsr $get_random_a; anda #0x03; adda #0x03; sta 0xEF; inc 0xF6; continue_hyperspace: lda 0xEE; bpl process_hyperspace_dots; dec 0xEF; beq PE330; jsr $MS_get_2_random_nums; sta 0xC8; clr 0xC9; /* Generate new coordinates */ stb 0xCA; /* for the command ship. */ clr 0xCB; puls dp,pc; PE330: lsr 0xEE; lda #0x1F; sta 0xEF; puls dp,pc; /* * This code draws the ever decreasing hyperspace dots * during an active hyperspace. The scale factor for * the hyperspace dots, stored in C8EF, is decremented, * and when it finally reaches a minimum value, the * hyperspace is complete. */ process_hyperspace_dots: ldb 0xEF; /* See if scale factor has reached */ cmpb #0xE0; /* the limit. */ ble hyperspace_complete; lda 0xEF; suba #0x04; /* Decrement the scale factor */ sta 0xEF; clra; jsr $draw_hyperspace_motion_dots; puls dp,pc; hyperspace_complete: clr 0xEF; clr 0xEE; jsr $clear_cmd_ship_xformation; puls dp,pc; /* * After the player has cleared all but the small mines * from a layer, the enemy ship will again appear, and * attempt to reseed the screen. This code checks to * see if the enemy ship is visible (C8E7), and if it * is, then it generates new coordinates for it, and * then moves it. */ reseed_screen: lda $C8E7; beq PE383; /* Do only if ship is visible */ pshs dp; lda #0xC8; tfr a,dp; lda 0xE7; beq PE383; ldd 0xDE; addd 0xE2; std 0xDE; sta 0xDC; /* New y coordinate */ ldd 0xE0; addd 0xE4; std 0xE0; sta 0xDD; /* New x coordinate */ puls dp; jsr $intensity_to_5F; ldb #0x08; ldy $C8DC; ldx #simple_enemy_ship; jsr $move_y_draw_x; /* Draw the enemy ship */ PE383: rts; /* * This procedure is invoked only through the indirect jump * pointers. After only small mines are left, this routine * will eventually be called. It will determine which way * the enemy ship will move, and will also set up another * indirect jump to the routine which does the actual * reseeding of the screen. */ ind_init_screen_reseeding: ldx #ind_reseed_screen; stx 0xA3; /* Set up next indirect jump ptr */ jsr $get_random_a; ldx #0xE448; /* Determine where enemy ship will move */ anda #0x06; ldx a,x; ldd ,x++; std 0xDC; /* Save enemy ships new coordinates */ sta 0xDE; clr 0xDF; stb 0xE0; clr 0xE1; bra PE3F9; /* * This routine is invoked only through the indirect jump * mechanism. It will continue to be called, until either * the enemy ship is destroyed, or until the screen has * been reseeded. It will reseed upto 7 new mines. */ ind_reseed_screen: lda 0xBF; /* See if all mines have been seeded */ bne PE3BE; PE3A5: jsr $get_random_a; anda #0x7F; adda #0x30; /* Set up the indirect jump counter 4 */ sta 0xA2; /* the next pass. */ jsr $get_random_a; anda #0x3F; sta 0xE6; jsr $get_random_a; adda #0x10; sta 0xE7; bra PE407; PE3BE: lda 0xBD; bne PE3A5; /* * Load the b register with the number of mines (28), * and load the u register with the pointer to the * mine structure. Then look through the mine structure * for an available slot, and fill it with the newly * seeded mine. */ ldb #0x1C; ldu #0xC933; PE3C7: lda ,u; /* Look for an unused mine */ beq found_avail_mine; leau 0x12,u; decb; bne PE3C7; bra PE407; found_avail_mine: inc 0xED; /* Incr the # of mines which R dots */ dec 0xBF; /* Decrement the reseed counter */ ldx 0xDE; stx 4,u; /* Set y coordinate for mine */ ldx 0xE0; stx 6,u; /* Set x coordinate for mine */ lda #0x40; sta ,u; /* Flag that mine is a stationary dot. */ lda 0xC0; /* See if this is 1st mine activated. */ bne PE3F7; ldx #ind_start_large_mine; stx 0x9D; /* Set things up to eventually */ jsr $get_random_a; /* activate 1 large mine. */ anda #0x7F; adda #0x40; sta 0x9C; inc 0xC0; /* Flag that 1st mine has been activated */ PE3F7: ldx 0xE8; PE3F9: lda ,x+; sta 0xA2; lda ,x+; sta 0xE6; lda ,x+; sta 0xE7; stx 0xE8; PE407: ldb 0xE6; jsr $calc_rise_run1; /* Generate rise & run for mine */ sty 0xE2; stx 0xE4; rts; /* * This routine is called only thru the indirect jump * handler. It is responsible for activating a single * large mine. The type of mine is indicated by the * first entry in the table of mines for this level. */ ind_start_large_mine: ldu #0xC8C4; lda 0x9B; ldu a,u; lda ,u; /* Determine the type of mine */ ldb #0x03; /* Set generation to 'largest' */ jsr $activate_a_mine; ldx #ind_activate_rangen_mine; stx 0x9D; rts; /* * This routine decrements the counter in C8C1, and if * it equal 0, then it activates a mine with a random * generation number; else, it sets the indirect counter * in C89C to -1, and returns. This routine is called * only thru the indirect jump handler. */ ind_activate_rangen_mine: dec 0xC1; beq PE430; lda #0xFF; sta 0x9C; bra PE447; PE430: jsr $get_random_a; tfr a,b; andb #0x03; bne PE43B; addb #0x01; PE43B: ldu #0xC8C4; lda 0x9B; ldu a,u; lda ,u; jsr $activate_a_mine; PE447: rts; /* Some sort of pointer table */ SE448: .word #SE450; .word #SE46A; .word #SE484; .word #SE49E; SE450: .byte 0x7F; .byte 0x00; .byte 0x28; .byte 0x20; .byte 0x30; .byte 0x40; .byte 0x28; .byte 0x30; .byte 0x28; .byte 0x00; .byte 0x10; .byte 0x30; .byte 0x10; .byte 0x40; .byte 0x18; .byte 0x20; .byte 0x50; .byte 0x40; .byte 0x30; .byte 0x28; .byte 0x30; .byte 0x08; .byte 0x60; .byte 0x7F; .byte 0x38; .byte 0x70; SE46A: .byte 0x80; .byte 0x00; .byte 0x40; .byte 0x00; .byte 0x30; .byte 0x20; .byte 0x10; .byte 0x50; .byte 0x20; .byte 0x28; .byte 0x40; .byte 0x30; .byte 0x3E; .byte 0x70; .byte 0x18; .byte 0x30; .byte 0x60; .byte 0x20; .byte 0x18; .byte 0x40; .byte 0x30; .byte 0x24; .byte 0x50; .byte 0x7F; .byte 0x06; .byte 0x70; SE484: .byte 0x00; .byte 0x7F; .byte 0x40; .byte 0x10; .byte 0x60; .byte 0x28; .byte 0x38; .byte 0x30; .byte 0x28; .byte 0x08; .byte 0x40; .byte 0x30; .byte 0x28; .byte 0x7F; .byte 0x20; .byte 0x18; .byte 0x30; .byte 0x30; .byte 0x08; .byte 0x68; .byte 0x40; .byte 0x20; .byte 0x50; .byte 0x7F; .byte 0x38; .byte 0x70; SE49E: .byte 0x00; .byte 0x80; .byte 0x40; .byte 0x30; .byte 0x60; .byte 0x38; .byte 0x18; .byte 0x30; .byte 0x30; .byte 0x20; .byte 0x18; .byte 0x20; .byte 0x38; .byte 0x40; .byte 0x28; .byte 0x10; .byte 0x60; .byte 0x20; .byte 0x00; .byte 0x30; .byte 0x40; .byte 0x38; .byte 0x50; .byte 0x7F; .byte 0x1C; .byte 0x70; /* * This routine is responsible for displaying any active * bullets, and also for firing new bullets, when an * available spot in the bullet structure becomes available. * The maximum number of active bullets is 4. The 40 byte * bullet structure starts at C90B. A bullet will remain * 'active' for at most 0x18 passes thru this routine. */ process_bullets: lda #0x04; /* Loop counter = 4 */ ldu #0xC90B; /* Bullet buffer */ ldx #0xC815; /* State of fire button (4) */ sta $C88F; jsr $intensity_to_7F; next_bullet: lda ,u; /* If the current spot is empty, then */ beq check_fire_button; /* see if a new bullet should be fired */ dec 9,u; /* Decrement the cycle counter for the */ beq erase_bullet; /* and erase bullet if it = 0. */ ldd 5,u; addd 1,u; /* y */ std 5,u; /* Increment bullet coordinates */ ldd 7,u; addd 3,u; /* x */ std 7,u; leay 5,u; jsr $draw_dot7F_ptr_in_y; /* Draw bullet */ update_bufr_ptr: leau 10,u; dec $C88F; /* Point to next entry */ bne next_bullet; rts; erase_bullet: clr ,u; dec $C8EA; /* Decrement active bullet counter */ check_fire_button: lda $C8BD; bne update_bufr_ptr; lda $C8EE; bne update_bufr_ptr; lda ,x; /* Check the 'fire' button state */ beq update_bufr_ptr; /* * This code 'fires' a new bullet, and fills the * available spot in the bullet structure. */ clr ,x; inc $C8B6; /* Flag that a bullet sound should be made */ inc ,u; ldd $C8C8; /* Get ships coordinates */ std 5,u; ldd $C8CA; std 7,u; ldd $C907; /* Get ship rise */ std 1,u; ldd $C909; /* Get ship run */ std 3,u; lda #0x18; /* # of passes before bullet expires */ sta 9,u; inc $C8EA; /* Increment active bullet counter */ bra update_bufr_ptr; /* * This procedure processes each mine which has not been * destroyed. If the mine is a moving dot, then its position * is updated; if the mine is a dot, then it is draw in its * appropriate place; if the mine is active, then the appropriate * mine is drawn; if it is a fireball, then the fireball is drawn. * The state of the mine is determined by the first byte in the * mine structure. The following values are valid: * * 0x00 - Mine is dead. * 0x01 - Mine is in process of being destroyed. * 0x08 - Mine is visible and moving. * 0x10 - Mine is visible, but not moving. * 0x20 - Mine is just becoming visible. * 0x40 - Mine is a stationary dot. * 0x80 - Mine is still moving to final resting place. * */ draw_non_dead_mines: lda #0x1C; sta $C88F; /* Use C88F as a loop counter */ ldu #0xC933; /* Put mine pointer into u register */ PE526: lda ,u; bne non_dead_mine; update_mine_ptr: leau 0x12,u; dec $C88F; bne PE526; rts; non_dead_mine: lbmi move_mine_dot; bita #0x40; lbne draw_mine_as_dot; bita #0x20; lbne mine_becoming_visible; bita #0x10; lbne delay_B4_moving_mine; bita #0x01; lbne process_destroyed_mine; lda 1,u; cmpa #0x04; /* See if this is a fireball */ beq process_fireball; bita #0x01; /* Code below moves visible mine */ beq non_magnetic_mine; lda $C8EE; /* Skip below if hyperspace active */ bne non_magnetic_mine; lda $C8BD; /* Skip below if cmd ship disabled */ bne non_magnetic_mine; pshs dp; jsr $dptoC8; lda 0xC8; /* Draw the mine towards the cmd ship */ suba 4,u; ldb 0xCA; subb 6,u; jsr $PF593; suba #0x10; sta 0x83; ldx #mine_velocity; ldb 3,u; lda b,x; ldb 0x83; jsr $calc_rise_run1; /* Calculate new rise and run values */ sty 8,u; stx 10,u; puls dp; non_magnetic_mine: ldd 4,u; /* This block update mine coordinates */ addd 8,u; std 4,u; ldd 6,u; addd 10,u; std 6,u; /* * This block of code draws a mine, using information in * the mine structure element pointed to by the u register. */ draw_a_visible_mine: jsr $intensity_to_5F; ldx #mine_vector_ptrs; lda 1,u; /* byte 1 = mine type */ asla; ldx a,x; /* Set x = ptr to mine vector list */ leay 4,u; /* Set y = mine coordinates. */ ldb 2,u; /* Set b = scale factor. */ jsr $move_y_draw_x2; jmp $update_mine_ptr; /* * This routine appears to be processing a fireball. * It checks to see if the fireball has reached the * edge of the display, and if it has, then the * fireball is removed; else, the fireball is drawn * on the display. */ process_fireball: ldd 4,u; /* See if the fireball has */ addd 8,u; /* reached the edge of the */ bvs remove_fireball; /* display. */ std 4,u; ldd 6,u; addd 10,u; bvs remove_fireball; std 6,u; jsr $intensity_to_7F; leay 4,u; /* Draw the fireball */ ldx #0xCBA7; ldb #0x04; jsr $move_y_draw_x2; jmp $update_mine_ptr; remove_fireball: clr ,u; /* Flag that the fireball has */ dec $C8EB; /* been removed. */ jmp $update_mine_ptr; /* * move_mine_dot () does some sort of processing on the * mine's coordinates, and then draws the mine as * a dot. I believe that the processing being done * is the movement of the dot in the x direction, * after the mine field has been seeded, but before * the mine dot has reached it's final place. * draw_mine_as_dot() draws a mine as a dot. * * For both these routines, the u register must * point to a structure element describing the * mine. */ move_mine_dot: lda 6,u; /* Update the mines x coordinate, */ adda 0x10,u; /* and check to see if it has reached */ sta 6,u; /* its final resting place. */ cmpa 0x11,u; bne draw_mine_as_dot; /* Flag that the mine dot is at its */ lsr ,u; /* final resting place. */ draw_mine_as_dot: jsr $intensity_to_5F; leay 4,u; jsr $draw_dot7F_ptr_in_y; jmp $update_mine_ptr; /* * This procedure is called when a mine first becomes visible. * If the mine is not a large mine, then the mine is drawn at * full scale factor; otherwise, if the mine is a large mine, * then it will be drawn with a steadily increasing scale factor. * This gives the effect of the mine 'popping' up. */ mine_becoming_visible: lda 3,u; cmpa #0x03; /* See if this is a large mine */ bne use_max_scale_factor; lda 2,u; cmpa 0x10,u; bge use_max_scale_factor; adda #0x08; sta 2,u; /* Increment the scale factor */ bra PE61A; use_max_scale_factor: lsr ,u; /* Flag that mine is full size & visible */ lda 0x10,u; sta 2,u; /* Set final scale factor */ lda #0x18; sta 0x10,u; /* Set up a delay */ lda $C8ED; bne PE61A; /* Check to see if any more mine dots left */ lda $C8C0; bne PE61A; /* See if re-seeding already in progress. */ lda #0x7F; /* Set up indirect counter for reseeding */ sta $C8A2; /* since only small mines are left. */ PE61A: jmp $draw_a_visible_mine; /* * This procedure is used to delay the movement of a mine * right after it become visible. After the delay is over, * the mine will start moving. */ delay_B4_moving_mine: dec 0x10,u; /* Decrement delay counter */ bne PE624; lsr ,u; PE624: jmp $draw_a_visible_mine; /* * This procedure processes the activation of 2 new mines * after a mine has been destroyed. If the destroyed mine * was a small mine, or a fireball, then 2 new mines will * not be activated. At entry time, the 'a' register must * contain the type of mine to be started up. */ process_destroyed_mine: clr ,u; /* Flag that this mine is gone */ lda 1,u; cmpa #0x04; /* If this was a fireball, then don't */ beq PE644; /* start up 2 new mines. */ ldb 3,u; decb; /* If this was a small mine, then don't */ beq PE644; /* start up 2 new mines. */ pshs a,dp; lda #0xC8; tfr a,dp; lda ,s; /* Get the type of mine */ jsr $activate_a_mine; jsr $activate_a_mine; puls a,dp; PE644: jmp $update_mine_ptr; /* * This routine is responsible for displaying the explosion * pattern for all active explosions. If the command ship * is sexploding, then it will also switch over to the * next player (eventually). */ display_explosion_pattern: pshs dp; jsr $dptoD0; jsr $intensity_to_7F; ldu #0xCB2B; /* Get pointer to the explosion struct */ lda #0x0E; sta $C88F; /* Use C88F as a loop counter */ check_next_explosion: lda ,u; lbeq inc_expl_ptr; ldb 4,u; /* If the scale factor for the explosion */ cmpb 1,u; /* has not surpassed the max value, then */ bhs chk_4_cmd_ship_explosion; /* increment it. */ addb #0x03; stb 4,u; ldy 2,u; ldx #explosion; /* Draw the explosion pattern */ jsr $move_y_draw_x; chk_4_cmd_ship_explosion: tsta; /* This is a cmd ship explosion if bit */ lbpl check_4_expired_explosion; /* 0x80 is set. */ dec $C8F7; /* Stop displaying explosion when C8F7 */ lbeq dead_ship; /* reaches zero. */ lda $C826; anda #0x01; /* Every other pass through the loop, */ bne draw_expl_cmd_ship; /* increment the scale factor used to */ inc $C8F8; /* draw the destroyed command ship. */ draw_expl_cmd_ship: lda $C8F8; /* Draw the cmd ship in pieces. */ ldy #0x7F00; ldx #cmd_ship_pt1; jsr $draw_cmd_ship_fragment; ldy #0x6080; ldx #cmd_ship_pt2; jsr $draw_cmd_ship_fragment; ldy #0x8050; ldx #cmd_ship_pt3; jsr $draw_cmd_ship_fragment; ldy #0xA080; ldx #cmd_ship_pt4; jsr $draw_cmd_ship_fragment; bra inc_expl_ptr; /* * This routine is called after a ship has been destroyed. * The number of ships for the active player, kept in C8D9, * will be decremented. If more than one player is playing, * then save the active players ship count in a memory * location (C8DA for player 1, and C8DB for player 2), * and then determine who's turn it now is. */ dead_ship: dec $C8D9; /* Decrement the number of ships */ clr $C8EB; clr $C8ED; lda $C879; /* If more than 1 player is playing, */ beq check_ship_count; /* then save the active players ship */ lda $C89B; /* ship count in RAM. */ lsra; ldx #0xC8DA; ldb $C8D9; stb a,x; lda $C8DA; /* See who's turn it is. Skip a player */ bne next_player; /* if he has no more ships. */ lda $C8DB; beq flag_no_ships_left; next_player: lda $C89B; /* Increment active player indicator */ adda #0x02; anda #0x02; sta $C89B; lsra; /* Load the ship count for new player */ ldx #0xC8DA; ldb a,x; stb $C8D9; beq next_player; /* * This routine checks to see if the active player has any ships * left, after the command ship has exploded. */ check_ship_count: lda $C8D9; bne explosion_expired; flag_no_ships_left: lda #0x01; sta $C8BE; /* Set C8BE when no ships left */ bra explosion_expired; /* * This routine checks to see if the explosion has expired. * An explosion expires when its scale factor surpasses a * preset maximum value. */ check_4_expired_explosion: ldb 4,u; /* See if scale factor has reached */ cmpb 1,u; /* the maximum value allowed. */ blo inc_expl_ptr; explosion_expired: clr ,u; dec $C8EC; /* Flag completion of cmd ship explosion */ inc_expl_ptr: leau 5,u; dec $C88F; lbne check_next_explosion; jsr $make_misc_sounds; bra draw_tiny_ships; /* Draw the tiny ships in lower righthand corner */ PE711: pshs dp; jsr $dptoD0; draw_tiny_ship: jsr $intensity_to_5F; ldx #0x8038; stx $C890; /* Position for first ship */ lda $C8D9; /* Don't bother, if the active */ beq rotate_fireball_vectors; /* player has no ships left. */ sta $C88F; PE727: dec $C88F; beq rotate_fireball_vectors; /* Exit when all ships drawn */ lda $C891; adda #0x06; /* Increment x coordinate */ sta $C891; ldb #0x04; ldy $C890; ldx #command_ship; jsr $move_y_draw_x; /* Draw the tiny command ship */ bra PE727; /* * This routine rotates the fireball vector list. Also, * it checks to see whether this level has been cleared. */ rotate_fireball_vectors: puls dp; lda 0x26; anda #0x01; asla; asla; asla; ldx #fireball; ldu #0xCBA7; jsr $rotate_vector_list1; /* * This block of code determines whether or not the main * loop should be exited. If the cmd ship is disabled, or * if the player has cleared the level, then the main loop * will be exited (this is flagged by clearing the carry bit. */ ldb 0xEC; /* See if cmd ship is being exploded */ bne PE767; lda 0xBD; /* See if cmd ship is disabled */ bne PE764; ldb 0xEB; /* check # of active mines */ bne PE767; ldb 0xED; /* check # of dot mines */ bne PE767; PE764: andcc #0xFE; /* Says 'exit main loop' */ rts; PE767: orcc #0x01; /* Says 'do not exit main loop' */ rts; /* * This routine is responsible for drawing a single fragment * of the exploding command ship. At entry, 'a' = scale factor, * 'y' = pen position, and 'x' = ptr to vector list. */ draw_cmd_ship_fragment: pshs a,x,y; ldx #0xC8C8; /* Move pen to cmd ships location */ jsr $move_pen7F_no_inc; lda ,s; sta 0x04; /* Set scale factor = a register */ tfr y,d; jsr $move_pen_d; /* Move pen to position in y register */ ldb #0x0C; ldx 1,s; jsr $drawl1b; /* Draw vector list pointed to by x reg */ puls a,x,y,pc; /* * This routine is called after a bullet has hit something, * or the command ship collides with something. It will * search through the explosion structure, looking for an * empty slot. When it finds one, it will fill it with the * information passed in. At entry time, 'a' = type of explosion * and the scale factor to use, 'b' = the max scale factor to * be used, and 'x' contains the position for the explosion. * If the hi bit of 'a' is set, then that flags that it is the * command ship exploding; the lower 7 bits are used to specify * the initial scale factor. */ add_an_explosion: : pshs a,b,x; ldx #0xCB2B; lda #0x0E; PE78B: ldb ,x; /* Find an empty spot in structure */ beq found_empty_exp_slot; leax 5,x; deca; bne PE78B; bra PE7B3; found_empty_exp_slot: lda ,s; /* Save the explosion type */ anda #0x80; inca; sta ,x; /* See if this is a cmd ship explosion */ bpl PE7A1; inc 0xBD; /* Flag that cmd ship is disabled */ PE7A1: lda ,s; anda #0x7F; sta 4,x; /* Save starting scale factor */ lda 1,s; sta 1,x; ldd 2,s; std 2,x; /* Save maximum scale factor */ inc 0xEC; /* Flag that cmd ship is exploding */ inc 0xF3; /* Flag that explosion sound should be made */ PE7B3: puls a,b,x,pc; /* * This routine is responsible for calculating a rise and run * for an object whose angle of travel is in the 'b' register, * and whose velocity is in the 'a' register. * The rise is returned in the 'y' register, and the run is * returned in the 'x' register. */ calc_rise_run1: pshs a,b,x,y; jsr $rotate_1_coordinate; sta 4,s; sex; aslb; rola; aslb; rola; aslb; rola; std 2,s; ldb 4,s; sex; aslb; rola; aslb; rola; aslb; rola; std 4,s; puls a,b,x,y,pc; /* * This routine also calculates some sort of rise & run pair. * The same parameters are expected at entry time, and the * values are returned in the same registers. */ calc_rise_run2: pshs a,b,x,y; bsr calc_rise_run1; ldd -4,s; aslb; rola; std 4,s; ldd -6,s; aslb; rola; std 2,s; puls a,b,x,y,pc; /* * This routine is called each time a new mine level is * started. It initializes everything necessary. */ init_RAM_for_new_level: lda #0xD0; tfr a,dp; jsr $clear_sound_chip; lda #0xC8; tfr a,dp; clr 0x9C; /* Clear all 4 indirect jump counters */ clr 0x9F; clr 0xA2; clr 0xA5; ldx #0xC90B; /* Clear C90B - CB71 */ PE7FA: clr ,x+; cmpx #0xCB71; bne PE7FA; ldd #0x0000; std 0xDE; std 0xE0; std 0xE2; std 0xE4; sta 0xE7; sta 0xBD; /* Clear cmd ship disabled flag */ sta 0xBE; /* Clear 'no more ships' flag */ sta 0xEA; /* Clear active bullet counter */ sta 0xEB; sta 0xEC; /* Clear cmd ship exploding flag */ sta 0xF8; /* Clear destroyed cmd ship scale factor */ ldb #0x40; stb 0xF7; /* Init cmd ship exploding loop counter */ sta 0xED; /* Clear # of dot mines */ sta 0xC0; /* Clear mine activated during reseeding flag */ ldx #0x0800; stx 0xF0; /* Set end of game loop counter */ lda #0x07; sta 0xBF; /* Init # of mines to be reseeded */ ldx #ind_init_screen_seeding; stx 0xA3; /* Init indirect jump location for reseeding */ ldd #0x0000; std 0xC8; /* Clear the command ship's */ std 0xCA; /* position indicators. */ clear_cmd_ship_xformation: ldd #0x0000; sta 0xD4; /* Clear rotation counter */ std 0xCC; std 0xCE; sta 0xD5; /* Clear cmd ships velocity */ sta 0xD6; std 0xD0; std 0xD2; sta 0xD7; sta 0xD8; /* * This block of code takes the vector list describing the * command ship, and applies a rotation transformation to * them. The current rotation value is in C8D4, and the * transformed vector list is stored in the buffer * starting at CB89. */ rotate_cmd_ship: lda 0xD4; ldx #command_ship; ldu #0xCB89; jsr $rotate_vector_list1; lda #0x7F; ldb 0xD4; jsr $calc_rise_run2; sty $C907; stx $C909; rts; /* * This routine is called after the active player clears * a mine field level. This routine will slowly bring the * command ship back to the center of the screen. As this * is being done, a series of dots will be drawn to simulate * motion. */ bring_ship_to_center: pshs x,y; pshs dp; jsr $dptoD0; jsr $clear_sound_chip; puls dp; lda #0xA0; sta 0x8F; /* Use C88F as a loop counter */ PE876: lda 0xC8; beq PE884; /* Modify the y coordinate for the */ bmi PE87F; /* command ship, in an effort to */ deca; /* force it back to 0. */ bra PE880; PE87F: inca; PE880: sta 0xC8; clr 0xC9; PE884: lda 0xCA; /* Modify the x coordinate for the */ beq PE892; /* command ship, in an effort to */ bmi PE88D; /* force it back to 0. */ deca; bra PE88E; PE88D: inca; PE88E: sta 0xCA; clr 0xCB; PE892: lda 0xD4; /* Modify the rotation value for the */ beq PE8A2; /* command ship, in an effort to */ cmpa #0x1F; /* force it back to 0. */ bgt PE89D; deca; bra PE89E; PE89D: inca; PE89E: anda #0x3F; sta 0xD4; PE8A2: jsr $PE2F2; /* This loop, repeated 8 times (once */ ldx #0xCB81; /* for each scale factor in the array) */ ldb #0x08; /* increments each of the scale factors */ PE8AA: lda ,x; /* for the motion dots, by 3. */ adda #0x03; sta ,x+; decb; bne PE8AA; pshs dp; jsr $dptoD0; jsr $display_both_scores; clrb; lda #0x20; /* " " */ jsr $simulate_motion1; jsr $modify_motion_scales; puls dp; /* Repeat the above until the ship is centered. */ lda 0xC8; lbne PE876; lda 0xCA; lbne PE876; lda 0xD4; lbne PE876; dec 0x8F; lbne PE876; jsr $init_RAM_for_new_level; puls x,y,pc; /* * This routine initializes the two arrays * (CB71 and CB81) which contain the data * needed to produce the motion dots (the * dots displayed as the ship moves from * one level to the next). The 8 entries * in CB71 are pointers to the 8 sets of * required dots. The 8 entries in CB81 * are the corresponding scale factors. */ init_motion_dots: ldx #motion_dots; ldy #0xCB71; ldu #0xCB81; ldb #0x08; lda #0x16; PE8F1: stx ,y++; leax 8,x; sta ,u+; adda #0x0F; decb; bne PE8F1; rts; /* * modify_motion_scales() increments each of the 8 motion * scale factors, and then draws the new motion dots. * simulate_motion1() and simulate_motion2() both * draw the current motion dots. The motion dots will * only be drawn if the scale factor is > the value specified * in the 'a' register, and if (scale - 'b') > 0. */ modify_motion_scales: pshs a,b,dp,x; ldx #0xCB81; lda #0x08; PE904: inc ,x+; /* Increment each of the scale factors */ deca; bne PE904; bra simulate_motion2; simulate_motion1: pshs a,b,dp,x; simulate_motion2: lda #0xD0; tfr a,dp; lda #0x09; /* Use 'a' as a loop counter */ pshs a; PE915: dec ,s; bne PE920; jsr $reset0ref; puls a; puls a,b,dp,x,pc; PE920: jsr $reset0ref; lda #0x03; /* Use C823 to hold # of dots */ sta $C823; /* to be drawn (4). */ lda ,s; deca; ldx #0xCB81; ldb a,x; andb #0x7F; cmpb 1,s; /* Make sure scale factor is */ bls PE915; /* within the specified range */ subb 2,s; ble PE915; stb 0x04; /* Set the hardware scale factor */ ldx #0xCB71; /* Draw the dot list */ asla; /* Convert loop cntr to a word offset */ ldx a,x; jsr $intensity_to_7F; jsr $dot_list; bra PE915; /* * This routine is responsible for drawing the hyperspace dot * during a hyperspace. It draws the 8 sets of motion dots, * using a decreasing scale factor. At entry, the 'b' register * contains the minimum scale factor to be used. */ draw_hyperspace_motion_dots: pshs a,b,dp,x; lda #0xD0; tfr a,dp; lda #0x09; /* Push the loop cntr on the stack */ pshs a; next_set_of_dots: dec ,s; bne draw_the_dots; jsr $reset0ref; puls a; puls a,b,dp,x,pc; draw_the_dots: jsr $reset0ref; lda #0x03; sta $C823; /* Use C823 to hold # of dots to draw */ ldx #0xC8C8; jsr $move_pen7F_no_inc; ldb ,s; aslb; aslb; addb 2,s; /* Calculate the scale factor */ ble next_set_of_dots; andb #0x7F; stb 0x04; /* Set scale factor */ ldx #0xCB71; lda ,s; /* Draw the appropriate set of dots */ deca; asla; ldx a,x; jsr $intensity_to_7F; jsr $dot_list; bra next_set_of_dots; /* * Generate 2 random numbers (1 byte each), and return * them in a and b. The range for the two numbers is: * * 0x80 <= first # <= 0x7F (a register) * 0xA0 <= second # <= 0x60 (b register) */ MS_get_2_random_nums: pshs a,b; jsr $get_random_a; sta ,s; PE991: jsr $get_random_a; cmpa #0x60; bgt PE991; cmpa #0xA0; blt PE991; sta 1,s; puls a,b; rts; /* * This routine randomly activates a single mine. * At entry, the 'a' register must contain the * type of mine to be activated, while the 'b' * register must contain the generation number * for the mine (i.e. large, medium, or small). */ activate_a_mine: pshs a,b,x,y,u; lda 0xED; /* Check C8ED to see if there are any */ lbeq PEA3C; /* dot mines; if not, then return. */ dec 0xED; jsr $get_random_a; /* Generate a random index into the */ anda #0x1F; /* mine array. */ PE9B0: sta 0x8B; cmpa #0x1B; bls PE9BA; suba #0x04; bra PE9B0; PE9BA: ldb #0x12; /* Multiple offset by element size */ mul; addd #0xC933; tfr d,u; lda ,u; /* Check to see if this mine is already */ anda #0xC0; /* being used. If it is, then check */ bne init_a_visible_mine; /* the next mine in the structure; else */ inc 0x8B; /* use this mine. */ lda 0x8B; cmpa #0x1B; ble PE9BA; clr 0x8B; clra; bra PE9BA; init_a_visible_mine: lda ,s; /* Store the mine type */ sta 1,u; ldx #mine_values; asla; ldy a,x; sty 0x89; /* Store mine's value in C889 */ ldb #0x20; stb ,u; ldx #mine_velocity; lda 1,s; /* Save mine's velocity */ ldb a,x; stb 0x8B; ldx #final_mine_scale_factor; ldb a,x; stb 0x10,u; /* Save mine's max scale factor */ sta 3,u; /* Save mine's generation number */ ldx #mine_sizes; asla; ldy a,x; sty 0x12,u; /* Save mine's size */ ldx #extra_mine_value; ldy a,x; sty 0x87; /* Get any extra value for mine's size */ cmpa #0x06; bne PEA13; inc 0xF4; /* Flag that a sound should be made */ PEA13: lda 0x88; adda 0x8A; daa; sta 0x15,u; /* Generate lo byte of score */ lda 0x87; adca 0x89; daa; sta 0x14,u; /* Generate hi byte of score */ lda 0x8B; jsr $MS_random_num_4_to_3C; jsr $calc_rise_run1; sty 8,u; /* Save the rise */ stx 10,u; /* Save the run */ inc 0xEB; /* Increment 'active mine' counter */ lda 0xC0; beq PEA3C; lda #0xFF; sta 0x9C; lda #0x03; sta 0xC1; PEA3C: puls a,b,x,y,u,pc; /* * Generate a random number between 4 and 0x3C. The * result is returned in the b register. */ MS_random_num_4_to_3C: pshs a,b; jsr $get_random_a; tfr a,b; anda #0x30; /* "0" */ sta 1,s; andb #0x0F; cmpb #0x04; bhs PEA51; addb #0x04; PEA51: cmpb #0x0C; bls PEA57; subb #0x04; PEA57: addb 1,s; stb 1,s; puls a,b,pc; /* * Force the scale factor to 0x7F, then * draw a dot at the position contained * in the y register. */ draw_dot7F_at_y: pshs a,b; lda #0x7F; sta 0x04; tfr y,d; jsr $dot; jsr $reset0ref; puls a,b,pc; /* * Force the scale factor to 0x7F, then draw a * dot at the position pointed to by the y * register (y coord = (0,y), x coord = (2,y)). */ draw_dot7F_ptr_in_y: pshs a,b; lda #0x7F; sta 0x04; lda ,y; ldb 2,y; jsr $dot; jsr $reset0ref; puls a,b,pc; /* * Move the pen to the position specified * in the y register, then draw the vector * list pointed to by the x register; the * scale factor is in the b register. */ move_y_draw_x: pshs a,b,x; tfr y,d; jsr $move_pen7F_to_d; ldb 1,s; jsr $drawl1b; puls a,b,x,pc; /* * Move the pen to the position specified in the y * register, and draw the list of vectors pointed to * by the x register; the scale factor to be used is * in the b register. (y coord = (0,y), x coord = (x,y)) */ move_y_draw_x2: pshs a,b,x; tfr y,x; jsr $move_pen7F_no_inc; ldb 1,s; ldx 2,s; jsr $drawl1b; puls a,b,x,pc; /* * Force the scale factor to 0x7F, then print * a single string (upto a 0x80). The pointer * to the string block is contained in the * u register. */ MS_print_1_string7F: pshs a,b,x,u; lda #0x7F; sta 0x04; jsr $print_1_string; puls a,b,x,u,pc; /* * Move the pen to the position specified in the * y register, then print a string. The pointer * to the string (not a string block) is in the * u register. */ move_y_print_string: pshs a,b,x,u; tfr y,d; jsr $move_pen7F_to_d; jsr $display_string; puls a,b,x,y,pc; /* * Use the value in C89B (the active player) to * determine which players score to display, * and then display the score string. */ display_active_players_score: jsr $intensity_to_7F; ldd #0xFC38; std $C82A; /* Specify height and width */ lda $C89B; ldy #score_string_positions; ldy a,y; /* Load string position into y */ ldu #score_string_pointers; ldu a,u; /* Load score string into u */ bsr move_y_print_string; rts; /* * Display the score for both players */ display_both_scores: jsr $intensity_to_7F; ldd #0xFC38; std $C82A; /* Specify height and width */ ldy #0x7FA0; /* Load y with y&x positions */ ldu #0xC8A8; /* Load u with player 1 score */ bsr move_y_print_string; lda $C879; /* See if we need to display player 2 score */ beq PEAEF; ldy #0x7F10; /* Load y with y&x positions */ ldu #0xC8AF; /* Load u with player 2 score */ bsr move_y_print_string; PEAEF: rts; /* * This routine is responsible for reading the current * states of the joystick and buttons. Next, it will * check each of the four indirect jump counters, and * if any of them equal 0 after being decremented, then * the appropriate indirect handling routine will be * called. The four indirect counters, and their corresponding * handler routines are shown below: * * counter handler * ------- ------- * C89C C89D-C89E * C89F C8A0-C8A1 * C8A2 C8A3-C8A4 * C8A5 C8A6-C8A7 * */ process_indirect_jumps: jsr $waitrecal; pshs dp; jsr $PF2E6; jsr $display_active_players_score; /* Set mask (in C880) and read the switches */ lda $C880; jsr $read_switches; /* Set enable flags (in C881) and read joystick */ ldd $C881; std $C81F; std $C821; jsr $read_jstick; lda #0xC8; tfr a,dp; lda 0x9C; beq PEB1D; dec 0x9C; bne PEB1D; jsr [C89D]; /* (UNKNOWN JUMP) */ PEB1D: lda 0x9F; beq PEB29; dec 0x9F; bne PEB29; jsr [C8A0]; /* (UNKNOWN JUMP) */ PEB29: lda 0xA2; beq PEB35; dec 0xA2; bne PEB35; jsr [C8A3]; /* (UNKNOWN JUMP) */ PEB35: lda 0xA5; beq PEB41; dec 0xA5; bne PEB41; jsr [C8A6]; /* (UNKNOWN JUMP) */ PEB41: puls dp,pc; /* * This routine check to see if any of the * bullets have hit a mine or a fireball or * the enemy ship. If something is hit, then * the active player's score will be modified. */ check_bullets_for_hits: lda 0xEA; /* Don't bother checking if there are */ beq PEB59; /* no active bullets. (C8EA = 0) */ ldy #0xC90B; lda #0x04; sta 0x8F; /* Use C88F as a loop counter */ check_for_active_bullet: tst ,y; /* See if this bullet is active */ bne check_enemy_ship_4_hit; inc_bullet_ptr2: leay 10,y; /* Increment ptr to next bullet */ dec 0x8F; bne check_for_active_bullet; PEB59: rts; check_enemy_ship_4_hit: lda 0xE7; /* Only check for a hit on the enemy */ beq check_mines_for_hits; /* ship if it is showing (C8E7 > 0). */ pshs y; lda 5,y; ldb 7,y; tfr d,x; /* bullets position */ ldd #0x0616; /* height/2 and width/2 of enemy ship */ ldy 0xDC; /* position of enemy ship */ jsr $check_bullet_for_hit; /* See if the enemy ship was hit */ puls y; bhs check_mines_for_hits; clr ,y; /* HIT - flag that bullet is dead */ clr 0xE7; /* Flag that enemy ship no longer there */ clr 0xA2; /* Clear 1 indirect jump counter */ ldx #score_string_pointers; lda 0x9B; ldx a,x; ldd #0x1000; /* Inc players score by 1000 */ jsr $add_d_to_x_in_bcd; lda #0x30; ldb #0x70; ldx 0xDC; jsr $add_an_explosion; dec 0xEA; /* Decrement the # of active bullets */ bra PEB59; /* * This routine checks to see if a bullet hit * any of the active mines or fireballs. */ check_mines_for_hit: ldu #0xC933; /* Load 'u' with ptr to mine info */ lda #0x1C; sta 0x90; /* Use C890 as a counter */ check_for_active_mine: lda ,u; anda #0x3F; /* Make sure mine is active */ bne check_this_mine; inc_mine_ptr2: leau 0x12,u; /* Inc ptr into mine structure */ dec 0x90; bne check_for_active_mine; bra inc_bullet_ptr2; check_this_mine: pshs y; lda 5,y; ldb 7,y; tfr d,x; /* bullet coordinates */ lda 4,u; ldb 6,u; /* mine coordinates */ tfr d,y; ldd 12,u; /* height & width of mine */ jsr $check_bullet_for_hit; /* returns: carry set = hit, else miss */ puls y; bhs inc_mine_ptr2; lda 1,u; /* Check if this is a fireball mine */ anda #0x02; beq not_a_fireball_mine; ldx #score_string_pointers; lda 0x9B; ldx a,x; ldd 14,u; /* Add mines value to players score */ jsr $add_d_to_x_in_bcd; inc 0xF5; lda 4,u; ldb 6,u; tfr d,x; lda 2,u; ldb #0x20; jsr $add_an_explosion; ldd #0x0110; /* Init score for the fireball */ std 14,u; lda 0xC8; suba 4,u; ldb 0xCA; subb 6,u; jsr $PF593; suba #0x10; tfr a,b; pshs y; lda #0x3F; jsr $calc_rise_run1; sty 8,u; /* Calculate rise & run for fireball */ stx 10,u; puls y; clr ,y; /* Flag that the bullet is dead */ ldd #0x0404; std 12,u; /* Set height/2 & width/2 for fireball */ lda 1,u; ldb 3,u; /* Decrement the generation for this mine */ decb; /* and don't start any more if it = 0 */ beq start_a_fireball; jsr $activate_a_mine; jsr $activate_a_mine; start_a_fireball: lda #0x04; /* Flag that this is a fireball */ sta 1,u; dec 0xEA; /* Decrement active bullet counter */ jmp $inc_bullet_ptr2; /* * This processes a hit on a non-fireball type mine. */ not_a_fireball_mine: lda #0x01; sta ,u; clr ,y; /* Flag that the bullet is dead */ ldx #score_string_pointers; lda 0x9B; ldx a,x; /* Add mines value to players score */ ldd 14,u; jsr $add_d_to_x_in_bcd; lda 4,u; ldb 6,u; tfr d,x; lda 2,u; ldb #0x40; jsr $add_an_explosion; dec 0xEB; /* Decrement active mine counter */ dec 0xEA; /* Decrement the active bullet counter */ jmp $inc_bullet_ptr2; /* * This routine check each active mine to see if it has * collided with the command ship. */ check_ship_4_mine_collision: lda 0xBD; bne PEC63; /* Do only if cmd ship not already dead */ lda 0xEE; bne PEC63; /* Do only if hyperspace not active */ ldy #0xC933; lda #0x1C; sta 0x8F; /* Set up a loop counter */ PEC56: lda ,y; anda #0x3F; bne check_this_active_mine; inc_mine_ptr3: leay 0x12,y; dec 0x8F; bne PEC56; PEC63: rts; check_this_active_mine: pshs y; lda 0xC8; ldb 0xCA; tfr d,x; /* Load cmd ship's position */ lda 4,y; ldb 6,y; ldy 12,y; /* Load mine's position */ exg y,d; jsr $check_bullet_for_hit; puls y; bhs inc_mine_ptr3; clr ,y; /* Flag that the mine is dead */ clr 0xED; lda 0xC8; ldb 0xCA; tfr d,x; lda 2,y; ora #0x80; ldb #0x30; jsr $add_an_explosion; inc 0xF3; /* Flag for an explosion sound */ dec 0xEB; /* Decrement active mine counter */ bra PEC63; /* * This routine checks to see if the command ship has collided * with the enemy ship during reseeding. */ check_ship_4_enemy_collision: lda 0xBD; /* Do only if cmd ship not disabled */ bne PECB2; lda 0xEE; /* Don't do if hyperspace active */ bne PECB2; lda 0xE7; /* Check only if enemy ship visible */ beq PECB2; lda 0xC8; ldb 0xCA; tfr d,x; /* Command ship location */ ldd #0x0616; /* Size of enemy ship */ ldy 0xDC; /* Enemy ship's location */ jsr $check_bullet_for_hit; blo cmd_and_enemy_ship_collision; PECB2: rts; cmd_and_enemy_ship_collision: clr 0xE7; /* Flag that enemy ship no longer visible */ clr 0xA2; lda 0xC8; ldb 0xCA; tfr d,x; lda #0x08; ora #0x80; ldb #0x30; jsr $add_an_explosion; inc 0xF3; /* Enable explosion sound */ rts; /* * This routine is responsible for making miscellaneous sound, * such as when an explosion occurs, or a bullet is fired. */ make_misc_sounds: PECC9: lda $C8F2; beq PECD6; clr $C8F2; ldu #cmd_ship_movement_sound; /* Make cmd ship movement sound */ bra make_the_misc_sound; PECD6: lda $C8F3; beq PECE3; clr $C8F3; ldu #explosion_sound; /* Make explosion sound */ bra make_the_misc_sound; PECE3: lda $C8B6; beq PECF0; clr $C8B6; ldu #bullet_sound; /* Make bullet firing sound */ bra make_the_misc_sound; PECF0: lda $C8F4; beq PED00; PECF5: clr $C8F4; clr $C8F6; ldu #mine_pop_sound; /* Make sound of mine 'popping' up */ bra make_the_misc_sound; PED00: lda $C8F6; bne PECF5; /* Make hyperspace sound */ bra PED0A; make_the_misc_sound: jsr $copy_bytes_2_sound_chip; PED0A: ldb $C800; addb #0x10; cmpb #0xA0; bhs PED1A; lda #0x00; jsr $byte_to_sound_chip; bra PED20; PED1A: ldd #0x0800; jsr $byte_to_sound_chip; PED20: ldb $C802; addb #0x20; cmpb #0xF0; bhs PED30; lda #0x02; jsr $byte_to_sound_chip; bra PED36; PED30: ldd #0x0900; jsr $byte_to_sound_chip; PED36: rts; cmd_ship_movement_sound: .byte 0x00; .byte 0x10; .byte 0x01; .byte 0x00; .byte 0x06; .byte 0x1F; .byte 0x07; .byte 0x06; .byte 0x08; .byte 0x0F; .byte 0xFF; bullet_sound: .byte 0x02; .byte 0x39; /* "9" */ .byte 0x03; .byte 0x00; .byte 0x06; .byte 0x1F; .byte 0x07; .byte 0x05; .byte 0x09; .byte 0x0F; .byte 0xFF; explosion_sound: .byte 0x06; .byte 0x1F; .byte 0x07; .byte 0x07; .byte 0x0A; .byte 0x10; .byte 0x0B; .byte 0x00; .byte 0x0C; .byte 0x38; /* "8" */ .byte 0x0D; .byte 0x00; .byte 0xFF; mine_pop_sound: SED5A: .byte 0x00; .byte 0x00; .byte 0x01; .byte 0x00; .byte 0x02; .byte 0x30; /* "0" */ .byte 0x03; .byte 0x00; .byte 0x04; .byte 0x00; .byte 0x05; .byte 0x00; .byte 0x06; .byte 0x1F; .byte 0x07; .byte 0x3D; /* "=" */ .byte 0x08; .byte 0x00; .byte 0x09; .byte 0x0F; .byte 0x0A; .byte 0x00; .byte 0x0B; .byte 0x00; .byte 0x0C; .byte 0x00; .byte 0x0D; .byte 0x00; .byte 0xFF; /* Start of Mine Storm music block */ MS_music: .word MS_music_header1; .word music_header2_a; .byte 0x00; .byte 0x19; .byte 0x01; .byte 0x19; .byte 0x00; .byte 0x19; .byte 0x01; .byte 0x32; .byte 0x00; .byte 0x19; .byte 0x01; .byte 0x19; .byte 0x00; .byte 0x19; .byte 0x06; .byte 0x19; .byte 0x05; .byte 0x19; .byte 0x00; .byte 0x80; MS_music_header1: .byte 0xFF; .byte 0xEE; .byte 0xDD; .byte 0xCC; .byte 0xBB; .byte 0xAA; .byte 0x99; .byte 0x88; .byte 0x77; .byte 0x77; .byte 0x77; .byte 0x77; .byte 0x77; .byte 0x77; .byte 0x77; .byte 0x77; score_string_pointers: .word 0xC8A8; /* Pointer to player 1 score string */ .word 0xC8AF; /* Pointer to player 2 score string */ score_string_positions: .byte 0x7F; /* Player 1 rel y score position */ .byte 0xA0; /* Player 1 rel x score position */ .byte 0x7F; /* Player 2 rel y score position */ .byte 0x10; /* Player 2 rel x score position */ mine_field_level_pointers: .word 0xC8F9; /* Pointer to player 1 mine field # string */ .word 0xC900; /* Pointer to player 2 mine field # string */ /* * This contains the information describing which mines * are to appear at the first 13 mine field levels. The * values map a follows: * * 0 = dumb mine * 1 = magnetic mine * 2 = fireball mine * 3 = magnetic fireball mine * */ mines_at_level_x: .byte 0x00; /* level 1 */ .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x02; /* level 2 */ .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x01; /* level 3 */ .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x03; /* level 4 */ .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x02; /* level 5 */ .byte 0x01; .byte 0x00; .byte 0x00; .byte 0x02; /* level 6 */ .byte 0x03; .byte 0x00; .byte 0x00; .byte 0x01; /* level 7 */ .byte 0x03; .byte 0x00; .byte 0x00; .byte 0x02; /* level 8 */ .byte 0x02; .byte 0x00; .byte 0x00; .byte 0x01; /* level 9 */ .byte 0x01; .byte 0x00; .byte 0x00; .byte 0x03; /* level 10 */ .byte 0x03; .byte 0x00; .byte 0x00; .byte 0x02; /* level 11 */ .byte 0x02; .byte 0x02; .byte 0x00; .byte 0x01; /* level 12 */ .byte 0x01; .byte 0x01; .byte 0x00; .byte 0x03; /* level 13 */ .byte 0x03; .byte 0x03; .byte 0x00; .byte 0x80; motion_dots: .byte 0xC8; .byte 0x40; .byte 0x3F; .byte 0x00; .byte 0x20; .byte 0x80; .byte 0x10; .byte 0x1F; .byte 0x3F; .byte 0x3F; .byte 0x00; .byte 0xBF; .byte 0xBF; .byte 0xBF; .byte 0xC0; .byte 0x20; .byte 0x48; .byte 0x08; .byte 0xF8; .byte 0x30; .byte 0xA8; .byte 0x10; .byte 0xD0; .byte 0xA0; .byte 0xBF; .byte 0xBF; .byte 0x00; .byte 0x3F; .byte 0x3F; .byte 0x48; .byte 0x20; .byte 0x80; .byte 0x00; .byte 0xB0; .byte 0x48; .byte 0x38; .byte 0xFB; .byte 0x38; .byte 0x80; .byte 0x28; .byte 0x30; .byte 0x48; .byte 0x80; .byte 0x80; .byte 0x45; .byte 0xF0; .byte 0x28; .byte 0x7F; .byte 0x3F; .byte 0xBF; .byte 0xA5; .byte 0x00; .byte 0xD0; .byte 0x60; .byte 0x20; .byte 0x28; .byte 0xB8; .byte 0x40; .byte 0x15; .byte 0x80; .byte 0x40; .byte 0xF8; .byte 0x40; .byte 0x18; mine_field: .byte 0xFA; /* height = -6 */ .byte 0x38; /* width = 56 */ .byte 0xE0; /* rel y = -32 */ .byte 0xC8; /* rel x = */ .byte "MINE FIELD", 0x80; game_over: .byte 0xFA; /* height = -6 */ .byte 0x38; /* width = 56 */ .byte 0xE0; /* rel y = -32 */ .byte 0xD8; /* rel x = */ .byte "GAME OVER",0x80; dumb_mine: .byte 0x00; .byte 0x10; .byte 0x00; .byte 0xFF; .byte 0x20; .byte 0xA0; .byte 0xFF; .byte 0xC0; .byte 0x40; .byte 0xFF; .byte 0x90; .byte 0x20; .byte 0xFF; .byte 0x70; .byte 0x20; .byte 0xFF; .byte 0x50; .byte 0x50; .byte 0xFF; .byte 0xD0; .byte 0x90; .byte 0x01; magnetic_mine: .byte 0x00; .byte 0x20; .byte 0x00; .byte 0xFF; .byte 0x30; .byte 0xB0; .byte 0xFF; .byte 0xB0; .byte 0x30; .byte 0xFF; .byte 0xB0; .byte 0xD0; .byte 0xFF; .byte 0x30; .byte 0x50; .byte 0xFF; .byte 0xD0; .byte 0x50; .byte 0xFF; .byte 0x50; .byte 0xD0; .byte 0xFF; .byte 0x50; .byte 0x30; .byte 0xFF; .byte 0xD0; .byte 0xB0; .byte 0x01; fireball_mine: .byte 0xFF; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x30; .byte 0x00; .byte 0xFF; .byte 0x10; .byte 0xC0; .byte 0xFF; .byte 0xC0; .byte 0x10; .byte 0xFF; .byte 0xC0; .byte 0xF0; .byte 0xFF; .byte 0x10; .byte 0x40; .byte 0xFF; .byte 0xF0; .byte 0x40; .byte 0xFF; .byte 0x40; .byte 0xF0; .byte 0xFF; .byte 0x40; .byte 0x10; .byte 0xFF; .byte 0xF0; .byte 0xC0; .byte 0x01; magnetic_fireball_mine: .byte 0xFF; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0xF0; .byte 0xD0; .byte 0xFF; .byte 0xC0; .byte 0x40; .byte 0xFF; .byte 0x20; .byte 0x00; .byte 0xFF; .byte 0x40; .byte 0x40; .byte 0xFF; .byte 0x00; .byte 0xE0; .byte 0xFF; .byte 0x40; .byte 0xC0; .byte 0xFF; .byte 0xE0; .byte 0x00; .byte 0xFF; .byte 0xC0; .byte 0xC0; .byte 0xFF; .byte 0x00; .byte 0x20; .byte 0x01; fireball: .byte 0x00; .byte 0x3F; .byte 0x00; .byte 0xFF; .byte 0x80; .byte 0x00; .byte 0x00; .byte 0x3F; .byte 0x3F; .byte 0xFF; .byte 0x00; .byte 0x80; .byte 0x01; explosion: .byte 0xFF; .byte 0x7F; .byte 0x20; .byte 0x00; .byte 0xC0; .byte 0x10; .byte 0xFF; .byte 0xC0; .byte 0xD0; .byte 0xFF; .byte 0x20; .byte 0x7F; .byte 0x00; .byte 0xE0; .byte 0xC0; .byte 0xFF; .byte 0x00; .byte 0xC0; .byte 0xFF; .byte 0xE0; .byte 0x30; .byte 0x00; .byte 0xC0; .byte 0x00; .byte 0xFF; .byte 0x60; .byte 0xCD; .byte 0xFF; .byte 0xA0; .byte 0x00; .byte 0x00; .byte 0x20; .byte 0xD0; .byte 0xFF; .byte 0x3C; .byte 0x30; .byte 0xFF; .byte 0x00; .byte 0x82; .byte 0x00; .byte 0x30; .byte 0x30; .byte 0xFF; .byte 0xD0; .byte 0x50; .byte 0xFF; .byte 0x20; .byte 0xF0; .byte 0x01; command_ship: .byte 0x00; .byte 0x3F; .byte 0x00; .byte 0xFF; .byte 0xC4; .byte 0x08; .byte 0xFF; .byte 0xD8; .byte 0xD8; .byte 0xFF; .byte 0x20; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x40; .byte 0xFF; .byte 0xE0; .byte 0x00; .byte 0xFF; .byte 0x28; .byte 0xD8; .byte 0xFF; .byte 0x3C; .byte 0x08; .byte 0x01; cmd_ship_pt1: .byte 0x00; .byte 0x3F; .byte 0x00; .byte 0xFF; .byte 0xC4; .byte 0x08; .byte 0x01; cmd_ship_pt2: .byte 0x00; .byte 0x04; .byte 0x08; .byte 0xFF; .byte 0xD8; .byte 0xD8; .byte 0xFF; .byte 0x20; .byte 0x00; .byte 0x01; cmd_ship_pt3: .byte 0x00; .byte 0x3F; .byte 0x00; .byte 0xFF; .byte 0xC4; .byte 0xF8; .byte 0x01; cmd_ship_pt4: .byte 0x00; .byte 0x04; .byte 0xF8; .byte 0xFF; .byte 0xD8; .byte 0x28; .byte 0xFF; .byte 0x20; .byte 0x00; .byte 0x01; enemy_ship_pt1: .byte 0x00; .byte 0x20; .byte 0x00; .byte 0xFF; .byte 0x00; .byte 0xD8; .byte 0xFF; .byte 0xD0; .byte 0xA8; .byte 0xFF; .byte 0xF0; .byte 0x40; .byte 0xFF; .byte 0x08; .byte 0x18; .byte 0xFF; .byte 0x18; .byte 0xF0; .byte 0xFF; .byte 0xF0; .byte 0xB8; .byte 0x00; .byte 0x10; .byte 0x48; .byte 0xFF; .byte 0x08; .byte 0x00; .byte 0xFF; .byte 0xE8; .byte 0x10; .byte 0xFF; .byte 0xF8; .byte 0x00; .byte 0x00; .byte 0x08; .byte 0x00; .byte 0xFF; .byte 0x00; .byte 0x06; .byte 0x00; .byte 0x10; .byte 0xFA; .byte 0xFF; .byte 0x08; .byte 0x00; .byte 0xFF; .byte 0x00; .byte 0xF0; .byte 0x00; .byte 0x10; .byte 0x18; .byte 0xFF; .byte 0xF0; .byte 0x08; .byte 0x01; enemy_ship_pt2: .byte 0x00; .byte 0x20; .byte 0x00; .byte 0xFF; .byte 0x00; .byte 0x28; .byte 0xFF; .byte 0xD0; .byte 0x58; .byte 0xFF; .byte 0xF0; .byte 0xC0; .byte 0xFF; .byte 0x08; .byte 0xE8; .byte 0xFF; .byte 0x18; .byte 0x10; .byte 0xFF; .byte 0xF0; .byte 0x48; .byte 0x00; .byte 0x10; .byte 0xB8; .byte 0xFF; .byte 0x08; .byte 0x00; .byte 0xFF; .byte 0xE8; .byte 0xF0; .byte 0xFF; .byte 0xF8; .byte 0x00; .byte 0xFF; .byte 0x08; .byte 0x00; .byte 0xFF; .byte 0x00; .byte 0xFA; .byte 0x00; .byte 0x10; .byte 0x06; .byte 0xFF; .byte 0x08; .byte 0x00; .byte 0xFF; .byte 0x00; .byte 0x10; .byte 0x00; .byte 0x10; .byte 0xE8; .byte 0xFF; .byte 0xF0; .byte 0xF8; .byte 0x01; enemy_ship_pt3: .byte 0xFF; .byte 0x00; .byte 0xD8; .byte 0xFF; .byte 0xE8; .byte 0x08; .byte 0xFF; .byte 0x00; .byte 0x40; .byte 0xFF; .byte 0x18; .byte 0x08; .byte 0xFF; .byte 0x00; .byte 0xD8; .byte 0x00; .byte 0x08; .byte 0xE0; .byte 0xFF; .byte 0x10; .byte 0x00; .byte 0xFF; .byte 0x00; .byte 0x40; .byte 0xFF; .byte 0xF0; .byte 0x00; .byte 0xFF; .byte 0x00; .byte 0xC0; .byte 0x01; simple_enemy_ship: .byte 0x00; .byte 0x18; .byte 0x00; .byte 0xFF; .byte 0x00; .byte 0x20; .byte 0xFF; .byte 0xC8; .byte 0x70; .byte 0xFF; .byte 0x10; .byte 0xA0; .byte 0xFF; .byte 0x00; .byte 0xA0; .byte 0xFF; .byte 0xEC; .byte 0xA4; .byte 0xFF; .byte 0x39; .byte 0x6D; .byte 0xFF; .byte 0x00; .byte 0x20; .byte 0x01; /* The following is spare space in the ROM */ .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; .byte 0x00; /* * The executive starts executing here, each time the system is reset * or powered up. */ start: lds #0xCBEA; /* Set up the stack pointer */ jsr $reinit; ldd #0x7321; /* Cold start only if CBFE != 0x7321 */ cmpd $CBFE; beq PF06C; std $CBFE; /* Start the cold start here */ inc $C83B; /* Flag not to display score */ ldx #0xCBEB; jsr $set_dft_score; /* Initialize the score string */ powerup_loop: jsr $dptoC8; ldd 0x25; /* Signal that it is OK to start playing */ cmpd #0x0101; /* the music (set C856), after waitrecal() */ bne PF029; /* has been called 257 times. */ stb 0x56; PF029: asrb; /* Determine which line pattern to use */ andb #0x03; /* for drawing the boundary lines. */ /* Load the line drawing pattern */ ldx #line_patterns; ldb b,x; stb 0x29; ldb #0x02; stb 0x24; /*Set intro box redraw count to 2 */ /* Play the introductory song */ ldu #intro_music; jsr $init_sound; jsr $waitrecal; jsr $do_sound; jsr $intensity_to_7F; /* Display the power-up Vectrex message */ lda $C826; ldu #vectrex_string; /* Determine if we should show the double */ bita #0x20; /* or single high VECTREX message. Double */ beq PF052; /* if bit 0x20 of waitrecal() counter is 0 */ leau 12,u; PF052: jsr $printu; ldx #intro_box; /* Draw a 4 sided box */ PF058: jsr $move_penFF; lda #0x03; jsr $dwp_with_count; dec $C824; bne PF058; /* Keep drawing box, until C824 = 0 */ lda $C825; /* Keep performing the above, as long as */ cmpa #0x01; /* the lower byte of the waitrecal() counter */ bls powerup_loop; /* is <= 0. */ PF06C: jsr $dptoC8; lda #0xCC; sta 0x29; /* Set line blanking pattern to 11001100 */ ldd #gce_string; std 0x39; /* Save pointer to gce string in C839 */ clr 0x25; clr 0x26; /* Clear the waitrecal() counter. */ /* See if a valid rom is installed */ ldu #0x0000; ldx #gce_string; ldb #0x0B; PF084: lda ,u+; cmpa ,x+; beq PF097; cmpb #0x01; beq PF092; cmpb #0x05; bls PF097; /* Boot with the MineStorm rom */ PF092: ldu #MS_header; bra PF09E; PF097: decb; bne PF084; stb 0x39; /* Clear C839-C83A, if using a plug-in */ stb 0x3A; /* Prepare to start a new sound (C856 = 1) */ /* Load music block pointer for game into u register */ PF09E: inc 0x56; stu 0x37; ldu ,u; PF0A4: jsr $dptoC8; ldd #0xF848; std 0x2A; /* Produce the specified tune */ jsr $init_sound; jsr $waitrecal; jsr $do_sound; /* Print the 'gce' copyright message */ jsr $intensity_to_7F; ldd #0xC0C0; ldu $C839; jsr $print_at_d; /* Display the current high score, if any */ lda $C83B; bne PF0D2; deca; ldu #0xCBEB; sta 6,u; ldd #0x68D0; jsr $print_at_d; /* Display the name of the game cartridge */ PF0D2: ldu $C837; leau 2,u; jsr $printu; /* Keep playing,until C856 is cleared */ lda $C856; bne PF0A4; ldx $C825; cmpx #0x007D; bls PF0A4; /* Jump to the start of the game */ jmp 1,u; /* (UNKNOWN JUMP) */ intro_box: .byte 0x40; /* "@" */ .byte 0xD6; .byte 0x00; .byte 0x56; /* "V" */ .byte 0x81; .byte 0x00; .byte 0x00; .byte 0xA9; .byte 0x7E; /* "~" */ .byte 0x00; box2: .byte 0x39; /* "9" */ .byte 0xDC; .byte 0x8E; .byte 0x00; .byte 0x00; .byte 0x4A; /* "J" */ .byte 0x72; /* "r" */ .byte 0x00; .byte 0x00; .byte 0xB6; /* Line patterns */ line_patterns: .byte 0xE0; /* Dashes = 11100000 */ .byte 0x38; /* Dashes = 00111000 */ .byte 0x0E; /* Dashes = 00001110 */ .byte 0x03; /* Dashes = 00000011 */ gce_string: .byte "g GCE 1982",0x80; vectrex_string: .byte 0xF1; /* height = -15 */ .byte 0x60; /* width = 96 */ .byte 0x27; /* rel y = 39 */ .byte 0xCF; /* rel x = */ .byte "VECTREX",0x80; .byte 0xF3; /* height = -13 */ .byte 0x60; /* width = 96 */ .byte 0x26; /* rel y = 38 */ .byte 0xCF; /* rel x = */ .byte "VECTREX",0x80; .byte 0xFC; /* height = -4 */ .byte 0x60; /* width = 96 */ .byte 0xDF; /* rel y = */ .byte 0xE9; /* rel x = */ .byte "GCE",0x80; .byte 0xFC; /* height = -4 */ .byte 0x38; /* width = 56 */ .byte 0xCC; /* rel y = */ .byte 0xD1; /* rel x = */ .byte "ENTERTAINING",0x80; .byte 0xFC; /* height = -4 */ .byte 0x38; /* width = 56 */ .byte 0xBC; /* rel y = */ .byte 0xDC; /* rel x = */ .byte "NEW IDEAS",0x80,0x00; init_PIA_chip: bsr dptoD0; ldd #0x9FFF; std 0x02; ldd #0x0100; std 0x00; ldd #0x987F; sta 0x0B; stb 0x04; jsr $reset0ref; bra set_refresh; /* Clear RAM locations C800-C87A */ clear_C800_C87A: bsr dptoC8; ldb #0x7A; /* "z" */ ldx #0xC800; jsr $clear_blockxb; ldd #0xC87D; std 0x7B; /* * Delay until contents of C87D = 0, then * Initialize dot dwell time, refresh timer & joystick enable flags */ init_flags1: inc 0x7D; /* Delay */ beq PF173; lda #0x05; sta 0x28; /* Set dot dwell time to 5 */ ldd #0x3075; std 0x3D; /* Set refresh time to 0x3075 */ ldd #0x0103; std 0x1F; /* Set joystick enable flags */ ldd #0x0507; std 0x21; /* Set joystick enable flags */ rts; reinit: bsr clear_C800_C87A; bsr init_PIA_chip; jmp $clear_sound_chip; /* * Wait for t2 (the refresh timer) to timeout, then restart * it using the value in C83D. Then, recalibrate the vector * generators to the origin (0,0). This routine MUST be called * once every refresh cycle, or your vectors will get out of * whack. This routine calls reset0ref, so the integrators * are left in zero mode. Dp will be set to D0. */ waitrecal: ldx $C825; leax 1,x; stx $C825; /* Increment the waitrecal() call counter */ bsr dptoD0; lda #0x20; PF19E: bita 0x0D; /* Wait for the refresh timer to expire */ beq PF19E; /* * This routine loads the refresh timer (t2), with the value in * C83D-C83E, and recalibrates the vector generators, thus * causing the pen to be left at the origin (0,0). The high * order byte for the timer is loaded from C83E, and the low * order byte is loaded from C83D. The refresh rate is calculated * as follows: rate = 1.5 mhz * (C83E)(C83D). * Dp must be D0. */ set_refresh: ldd $C83D; std 0x08; jmp $PF2E6; /* * Sets the dp register to D0, so that all direct addresses * will start at D000 (the hardware I/O area). The a * register is trashed. */ dptoD0: lda #0xD0; tfr a,dp; rts; /* * Sets the Dp register to C8, so that all direct addresses * start at C800. The a register is trashed. */ dptoC8: lda #0xC8; tfr a,dp; rts; /* * Read the switch values for the two consoles. The switch values * are returned in the following locations: * * console 1, switch 1: C812 * switch 2: C813 * switch 3: C814 * switch 4: C815 * console 2, switch 1: C816 * switch 2: C817 * switch 3: C818 * switch 4: C819 * * Before this routine is called, the a register must be set to * contain a mask, having the following format: * * bit 0 = console 1, switch1; etc. * * If a bit is 0, then the current state of the switch is to be * returned in the appropriate ram location (0 = up, 1 = down). * * If a bit is 1, then the appropriate ram location is set to 1 * only on the depression transition of the button; additional * calls will return 0, until the switch is release and then * depressed again. * * Dp must be set to D0. */ read_switches: anda $C80F; sta $C80F; ldx #0xC812; lda -3,x; sta -2,x; lda #0x0E; sta 0x01; ldd #0x1901; sta 0x00; nop; stb 0x00; clr 0x03; ldd #0x0901; sta 0x00; nop; lda 0x01; coma; sta -3,x; stb 0x00; ldb #0xFF; stb 0x03; coma; ora -2,x; coma; sta -1,x; pshs a; ldb #0x01; PF1EA: tfr b,a; anda ,s; sta ,x+; aslb; bne PF1EA; puls a,pc; PF1F5: dec $C823; /* * Read the joystick positions of the two consoles. This routine * requires some setup before it should be called. First, C81F-C822 * are enable flags, and all must be set to one of the following: * * 0 ignore. * 1 pot 0 (console 1 left/right) * 3 pot 1 (console 1 up/down) * 5 pot 2 (console 2 left/right) * 7 pot 3 (console 2 up/down) * * Locations C81B-C81E will be set with the pot values corresponding * to the enable flags. * * Location C823 is used to determine what kind of input conversion * should be performed. The valid choices are: * * >= 0 The return value will be: * < 0 if joystick is left or down of center. * = 0 if joystick is centered. * > 0 if joystick is right or up of center. * * < 0 A successive approximation algorithm is used to * read the actual value of the joystick pot, a * signed value. In this case, C81A must be set to * power of 2, to control conversion resolution; * 0x80 i sleast accurate, and 0x00 is most accurate. * * Dp must be set to D0. * C823 is cleared before this routine returns. * */ read_jstick: ldx #0xC81F; next_pot: lda ,x+; bne process_a_pot; check_4_done: cmpx #0xC823; bne next_pot; clr ,x; lda #0x01; sta 0x00; rts; process_a_pot: sta 0x00; clr 0x01; dec 0x00; ldb #0x60; PF213: incb; bpl PF213; /* Delay */ lda $C823; /* Get the joystick mode */ bmi use_approx; /* Use successive approximation */ lda #0x20; inc 0x00; bita 0x00; beq l_or_d; ldb #0x40; stb 0x01; bita 0x00; bne save_jstick_state; /* joystick is right or up */ bra centered; /* joystick is centered */ l_or_d: ldb #0xC0; stb 0x01; bita 0x00; beq save_jstick_state; /* joystick is left or down */ centered: clrb; save_jstick_state: stb -5,x; bra check_4_done; try_again: tfr b,a; ora 0x01; sta 0x01; use_approx: lda #0x20; bita 0x00; bne check_resolution; tfr b,a; eora 0x01; sta 0x01; check_resolution: lsrb; cmpb $C81A; /* Get the approximation resolution */ bne try_again; ldb 0x01; bra save_jstick_state; /* * These routines cause a byte to be added to a sound * buffer. At entry, 'x' = base address of RAM area of where to * store the sound byte. 'a' = offset into the 'x' register as * to where to store the byte. 'b' = byte to be processed. */ byte_2_sound_chip: ldx #0xC800; byte_2_sound_chip2: stb a,x; sta 0x01; lda #0x19; sta 0x00; lda #0x01; sta 0x00; lda 0x01; stb 0x01; ldb #0x11; stb 0x00; ldb #0x01; stb 0x00; rts; /* * This routine clear out the 15 byte sound buffer. */ clear_sound_chip: ldd #0x0E00; PF275: bsr byte_to_sound_chip; deca; bpl PF275; jmp $init_music_buf; /* * This routine copies a group of sound information into the * sound chip buffer, at C800. At entry, 'u' = a pointer to * a list of sound pairs. If the first byte >= 0, then it is * used as an index into the sound chip buffer as to where to * save the 2nd byte (the sound data). If the 1st byte is < 0, * then the copy stops; dp must by D0. */ copy_bytes_2_sound_chip: ldx #0xC800; bra PF284; PF282: bsr byte_2_sound_chip2; PF284: ldd ,u++; bpl PF282; rts; /* * This will start making the sound which was set up by your * call to init_sound(). This should be called right after * your call to waitrecal(). */ do_sound: ldx #0xC800; ldu #0xC83F; lda #0x0D; PF291: ldb ,u+; cmpb a,x; beq PF299; bsr byte_2_sound_chip2; PF299: deca; bpl PF291; rts; /* * Sets the intensity (the z axis) to 1F, 3F, 5F or 7F, depending * upon which entry point is called. The intensity must be * reloaded at least every refresh cycle. However, it may be * changed on the fly, if desired. The current intensity value * is saved in C827. Dp must be D0. */ intensity_to_1F: lda #0x1F; bra intensity_to_a; intensity_to_3F: lda #0x3F; /* "?" */ bra intensity_to_a; intensity_to_5F: lda #0x5F; /* "_" */ bra intensity_to_a; intensity_to_7F: lda #0x7F; intensity_to_a: sta 0x01; sta $C827; ldd #0x0504; sta 0x00; stb 0x00; stb 0x00; ldb #0x01; stb 0x00; rts; /* * Draw a dot at the relative y and relative x coordinates pointed * to by the x register, with an intensity proportional to the * value in the b register. The x register is incremented by 2 * afterwards. The Dp must be D0. */ dotixb: stb $C828; dotix: ldd ,x++; /* Use current intensity value */ dot: bsr move_pen_d; /* Use x&y already in d reg */ lda #0xFF; sta 0x0A; ldb $C828; PF2CC: decb; /* Use b (intensity) as loop counter */ bne PF2CC; clr 0x0A; rts; /* * Draw a series of dots. The x register points to a list of * relative y and relative x location pairs. The number of * dots to be drawn is specified in RAM location C823. */ next_dot: dec $C823; dot_list: bsr dotix; lda $C823; bne next_dot; bra reset0ref; /* Draw a series of dots. The x register points to the array * of points. The array has the following format: * * mode, relative y position, relative x position * * If mode > 0, then the drawing ceases, and the integrators are reset. */ dotix_then_reset: lda ,x+; bgt reset0ref; bsr dotix; bra dotix_then_reset; PF2E6: ldx #0xF9F0; bsr move_penFF; jsr $PF36B; bsr move_pen; bra reset0ref; /* * Set the scale factor to 0x7F, then move the pen to the location * pointed to by the x register (y coord = (0,x) x coord = (2,x)). * The x register is not incremented afterwards. */ move_pen7F_no_inc: ldb #0x7F; stb 0x04; lda ,x; ldb 2,x; bra move_pen_d; /* * Force the scale factor to 0x7F, then move the pen to the * position specified in the d register. */ move_pen7F_to_d: sta 0x01; /* Set height for string */ pshs a,b; lda #0x7F; sta 0x04; /* Set scale factor to 0x7F */ clr 0x00; bra PF318; /* * Move the pen to the relative y and relative x positions * pointed to by the x register. The scale factor is set to * 0xFF (170 usec), and then left at this value at exit time. * Dp must be D0. The x register is incremented by 2 before * exiting. */ move_penFF: ldb #0xFF; bra set_scale_factor; /* Same as above, except scale factor is set to 0x7F (85 usec). */ move_pen7F: ldb #0x7F; set_scale_factor: stb 0x04; /* Same as above, except the scale factor is not changed */ move_pen: ldd ,x++; /* Move pen to relative y (in a) and relative x (in b) */ move_pen_d: sta 0x01; clr 0x00; pshs a,b; PF318: lda #0xCE; sta 0x0C; clr 0x0A; inc 0x00; stb 0x01; clr 0x05; puls a,b; jsr $check_ab_for_overflow; stb -1,s; ora -1,s; ldb #0x40; /* "@" */ cmpa #0x40; /* "@" */ bls PF345; cmpa #0x64; /* "d" */ bls PF33B; lda #0x08; bra PF33D; PF33B: lda #0x04; PF33D: bitb 0x0D; beq PF33D; PF341: deca; bne PF341; rts; PF345: bitb 0x0D; beq PF345; rts; /* Set the dp to D0, then reset the integrators */ set_dp_and_reset0ref: jsr $dptoD0; bra reset0ref; /* * If 0xC824 is non-zero, call reset0ref, else return. * Dp must be D0. */ check0ref: lda $C824; beq rtn1; /* * This routine zeros the integrators. It resets the origin to * (0,0). This routine leaves the integrators in zero mode, so * nothing can be drawn until a move is done, or 0xD00C is set * to 0xCE. Dp must be D0. This routine should be called * every once in awhile, to make sure your vectors don't get off. */ reset0ref: ldd #0x00CC; stb 0x0C; sta 0x0A; ldd #0x0302; clr 0x01; sta 0x00; stb 0x00; stb 0x00; ldb #0x01; stb 0x00; rtn1: rts; PF36B: ldd #0x00CC; stb 0x0C; sta 0x0A; rts; /* * The following are a series of subroutines which may be used to * accomplish several different methods of printing a string. * Each one expects the string (and parameter block) to be pointed * to by the u register. * * print_1_string() - Prints a string (upto a 0x80). The * header for the string must include the * height and width for the string, and * also the relative y & x positions. * * print_with_dft_hw()- Prints a string (upto a 0x80). The * header for the string must include * only the relative y & x positions. The * current height & width is used. * * print_at_d() -Prints a string (upto a 0x80). The * string contains no header. The current * height & width are used. The rely & rel x * are specified in the d register. */ print_1_string: ldd ,u++; /* Save height and width */ std $C82A; print_with_dft_hw: ldd ,u++; /* Get the rel y & rel x positions */ print_at_d: jsr $move_pen7F_to_d; /* Move pen to starting positions */ jsr $delay_b_1; jmp $display_string;/* Print string, until 0x80 encountered */ initiate_printing: bsr print_1_string; /* * Display the string whose block is pointed to by the u register. * Dp must be D0. The block for the string must have the following * format: * * string height, string width, * rel y, rel x, STRING, 0x80, * rel y, rel x, STRING, 0x80, * 0x00 */ printu: lda ,u; bne initiate_printing; /* Keep printing till 0x00 found */ rts; /* * Display the string whose block is pointed to by the u register. * Dp must be D0. The current height and width values for which * the hardware is set are used. The block for the string must * have the following format: * * rel y, rel x, STRING, 0x80, * rel y, rel x, STRING, 0x80, * 0x00 */ printu2: bsr print_with_dft_hw: lda ,u; /* Stop when 0x00 encountered */ bne printu2; rts; PF391: ldx ,x; pshs b; ldb #0x80; leau -8,s; pshu a,b; puls a; cmpa #0x09; bls PF3A3; lda #0x3C; /* "<" */ PF3A3: adda #0x30; /* "0" */ ldb #0x2D; /* "-" */ pshu a,b; pshu x; bra print_with_dft_hw: PF3AD: lda ,x+; bra PF3B9; PF3B1: stb 0x04; bra move_to_pt; PF3B5: ldd ,x++; stb 0x04; PF3B9: sta $C823; move_to_pt: ldd ,x; sta 0x01; clr 0x00; leax 2,x; nop; inc 0x00; stb 0x01; ldd #0x0000; /* Set line pattern to invisible */ bra PF3ED; PF3CE: lda ,x+; bra PF3DA; PF3D2: stb 0x04; bra draw_to_pt; PF3D6: ldd ,x++; stb 0x04; PF3DA: sta $C823; draw_to_pt: ldd ,x; sta 0x01; clr 0x00; leax 2,x; nop; inc 0x00; stb 0x01; ldd #0xFF00; /* Set line pattern to solid line */ PF3ED: sta 0x0A; stb 0x05; ldd #0x0040; PF3F4: bitb 0x0D; beq PF3F4; nop; sta 0x0A; lda $C823; deca; bpl PF3DA; jmp $check0ref; PF404: ldb #0xFF; bra drawl1b; PF408: ldb #0x7F; bra drawl1b; /* * Draw a list of vectors. The x register contains the pointer * to the vector list. The form for the vector list is a follows: * * .byte scale factor * .byte mode, relative y, relative x * . * . * .byte mode, relative y, relative x * * where mode means the following: * * < 0 visible vector. * = 0 invissible vector. * > 0 end of list. * */ drawl1: ldb ,x+; /* Same as drawl1, except scale factor is in b register */ drawl1b: stb 0x04; next_pt: ldd 1,x; /* Load d register with endpoints */ sta 0x01; clr 0x00; lda ,x; /* Get the mode for this vector */ leax 3,x; /* Increment pointer to next pair */ inc 0x00; stb 0x01; sta 0x0A; clr 0x05; ldd #0x0040; PF425: bitb 0x0D; beq PF425; nop; sta 0x0A; lda ,x; ble next_pt; jmp $check0ref; /* * Draw a series of patterned vectors. The x register points to * the list of endpoints, and the list has the following format: * * relative y, relative x * * dwp_with_count() uses the value in the a register to determine * how many vectors to draw (number = a + 1), while draw_with_pattern() * uses the value in RAM location C823 to determine this. */ dwp_loop: deca; dwp_with_count: sta $C823; draw_with_pattern: ldd ,x; sta 0x01; clr 0x00; leax 2,x; inc 0x00; stb 0x01; lda $C829; /* Retrieve the line pattern */ ldb #0x40; sta 0x0A; clr 0x05; bitb $D00D; beq PF45C; clr 0x0A; lda $C823; bne dwp_loop; rts; PF459: lda $C829; PF45C: sta 0x0A; nop; bitb 0x0D; beq PF459; lda $C823; clr 0x0A; tsta; bne dwp_loop; jmp $check0ref; /* * Draw a list of vectors, pointed to by the x register. * The scale factor is not changed, and whatever the current * value is is used. The list is organized as follows: * * .byte mode, relative y, relative x; * * where mode has the following meaning: * * < 0 use mode value in 0xC829 ( 0 = invis; 0xFF is visible). * = 0 invisible vector. * = 1 end of list, so return. * > 1 visible vector. * */ drawl2: lda $C824; pshs a; clr $C824; /* Temporarily clear check0ref() enable flag */ PF476: lda ,x+; /* Grab the drawing mode desired */ bpl move_or_draw; bsr draw_with_pattern; bra PF476; move_or_draw: bne draw_or_end; jsr $move_to_pt; bra PF476; draw_or_end: deca; beq end_drawl2; jsr $draw_to_pt; bra PF476; end_drawl2: puls a; sta $C824; /* Restore the check0ref() enable flag */ jmp $check0ref; /* * This is the routine which does the actual printing of a string * onto the display. The u register points to the start of the * string. The end of the string is designated by the 0x80 byte. * The string is displayed by drawing 6 horizontal rows of dots. * The first row is drawn for each character, then the second, etc. * The character generation table is located at 0xF9D4 + 0x20. * NOTE: only characters 0x20 - 0x6F are defined, and lower case * characters a-o produce special icons. */ display_string: stu $C82C; /* Save pointer to start of string */ ldx #0xF9D4; /* Actual character table is at F9F4 */ ldd #0x1883; clr 0x01; sta 0x0B; ldx #0xF9D4; PF4A5: stb 0x00; dec 0x00; ldd #0x8081; nop; inc 0x00; stb 0x00; sta 0x00; tst $C800; inc 0x00; lda $C82B; /* Set the width */ sta 0x01; ldd #0x0100; ldu $C82C; /* Set u to point to 1st char */ sta 0x00; bra PF4CB; PF4C7: lda a,x; sta 0x0A; PF4CB: lda ,u+; bpl PF4C7; /* Display next row of pixels */ lda #0x81; sta 0x00; neg 0x01; lda #0x01; sta 0x00; cmpx #0xFBB4; /* If we have not reached the end of the char */ beq PF50A; /* table, then proceed to next row of pixels. */ leax 0x50,x; tfr u,d; subd $C82C; /* Point u back to the 1st character */ subb #0x02; aslb; brn PF4EB; PF4EB: lda #0x81; /* Timing loop */ nop; decb; bne PF4EB; sta 0x00; ldb $C82A; /* Grab the height */ stb 0x01; dec 0x00; ldd #0x8101; nop; sta 0x00; clr 0x01; stb 0x00; sta 0x00; ldb #0x03; bra PF4A5; /* Repeat, using next row pixel patterns */ PF50A: lda #0x98; sta 0x0B; /* Done with this string */ jmp $reset0ref; /* * Generate a random number, and place it into the a register. * This uses a different seed (contained in register b) than * get_random_a(). */ get_random_a2: pshs b,x; ldb #0x02; bra ran_start; /* * Generate a random number, and place it into the a register. */ get_random_a: pshs b,x; clrb; ran_start: ldx $C87B; ran_loop: lda 1,x; rola; rola; rola; rola; eora 2,x; rora; rol ,x; rol 1,x; rol 2,x; decb; bpl ran_loop; lda ,x; puls b,x,pc; /* Clear the block of memory C83F - C84C, then set C845 = 0x3F */ init_music_buf: ldb #0x0D; ldx #0xC83F; bsr clear_blockxb; lda #0x3F; sta 6,x; rts; /* * Clear a block of memory. The x register points to the * start of the memory block to be cleared, while the b * register specifies the number of bytes to be cleared. * The a register is altered. */ clear_blockxb: clra; bra clear_block; clear_C8_ram: ldx #0xC800; ldd #0x00FF; clear_block: clr d,x; subd #0x0001; bpl PF548; rts; /* * Set the block of memory pointed to by the x register to 0x80. * The number of memory locations to be set is specified by the * b register + 1. */ clear_block_to_0x80: lda #0x80; PF552: sta b,x; decb; bne PF552; sta ,x; rts; /* * Decrement the specified counters */ decrement_counters_C82E_C830: ldb #0x02; bra PF560; decrement_counters_C82E_C833: ldb #0x05; PF560: ldx #0xC82E; PF563: tst b,x; beq PF569; dec b,x; PF569: decb; bpl PF563; rts; /* Delay until the b register < 0 */ delay_b_3: ldb #0x03; bra start_b_delay; delay_b_2: ldb #0x02; bra start_b_delay; delay_b_1: ldb #0x01; bra start_b_delay; delay_b_0: clrb; start_b_delay: decb; bpl start_b_delay; rts; /* * On entry, the a register equals the bits number for which * a mask is desired. On exit, the a register equals the * requested bit mask. For example: * * Entry Exit * ----- ---- * 0 0x01 * 1 0x02 * etc etc */ get_bit_mask: ldx #bit_masks; lda a,x; rts; /* * If a is negative, then negate it and check to see if the * overflow bit is set; if it is, then decrement a. Repeat for * the b register. ( Overflow is set only if the # = 0x80). */ check_ab_for_overflow: tsta; bpl PF58B; nega; bvc PF58B; deca; PF58B: tstb; bpl PF592; negb; bvc PF592; decb; PF592: rts; PF593: pshs x; std 0x34; rolb; ldb #0x00; rolb; rola; rolb; aslb; stb 0x36; ldd 0x34; bsr check_ab_for_overflow; sta 0x34; cmpb 0x34; bls PF5B2; inc 0x36; exg a,b; bra PF5B2; PF5B0: lsra; lsrb; PF5B2: cmpa #0x09; bhi PF5B0; std 0x34; ldb 0x36; ldx #0xFC24; ldb b,x; ldx #0xFC2C; lda a,x; adda 0x35; adda #0x0A; bitb #0x01; bne PF5D0; addb a,x; bra PF5D3; PF5D0: decb; subb a,x; PF5D3: stb 0x36; lda 0x36; puls x,pc; /* * get_2nd_index_pair() and get_1st_index_pair() are used * by the rotation algorithm. They expect the rotation * value to be specified in the 'a' register. Upon exit, * the rotation index pair is returned in the 'd' register. */ get_2nd_index_pair: adda #0x10; get_1st_index_pair: ldx #0xFC6D; clrb; bita #0x20; beq PF5E5; ldb #0x80; PF5E5: anda #0x1F; cmpa #0x10; bne PF5EC; incb; PF5EC: lda a,x; rts; /* * This routine is called at at the beginning of the * rotation transformation process. It is responsible * for generating the 2 rotation index pairs. The * first rotation pair is saved in C837-C838, and the * second pair is aved in C839-C83A. This routine * expects the rotation value to be stored in C836. */ get_rotation_index_pairs: pshs x; lda 0x36; /* get rotation value */ bsr get_1st_index_pair; std 0x37; /* Save first rotation index pair */ lda 0x36; /* get rotation value */ bsr get_2nd_index_pair; std 0x39; /* Save second rotation index pair */ puls x,pc; /* * This routine is responsible for rotating a single * coordinate. At entry, the 'b' register must contain * the rotation value, and the 'a' register must contain * the coordinate. The value is returned in the 'd' register. */ PF5FF: subb #0x10; rotate_1_coordinate: stb 0x36; /* Save rotation value */ sta 0x3B; /* Save the coordinate */ bsr get_rotation_index_pairs; bsr xform_1a; nega; pshs a; bsr xform_2a; puls b,pc; /* * This routine rotates a vector list of length 'n', where * 'n' is specified by the 'b' register. The 'a' register * contains the rotation value; the 'x' register contains * a pointer to the vector list to be transformed; and * the 'u' register containss a pointer to a buffer into * which the transformed points are to be saved. * The vector list is composed of only y,x pairs. */ rotate_vector_list2: sta $C836; /* Save the rotation value */ stb $C823; /* Save the byte counter */ pshs dp; jsr $dptoC8; bsr get_rotation_index_pairs; bra rotate_a_pt: /* Process the vector list */ /* * This procedure is responible for rotating a vector list * having the following format: * * mode, relative y, relative x, * . * . * 1 * * At entry, the 'a' register must contain the rotation value, * the 'x' register must point to the vector list to be * transformed, and the 'u' register must point to a buffer * which is to be used to store the transformed coordinates. */ rotate_vector_list1: sta $C836; /* Save rotation value */ pshs dp; jsr $dptoC8; sta 0x23; bsr get_rotation_index_pairs; process_next_point: lda ,x+; /* Copy the mode, and process the */ sta ,u+; /* next point if mode <= 0. */ ble rotate_a_pt: clr 0x23; puls dp,pc; dec_vl_counter: dec 0x23; /* Decrement the vector list counter */ rotate_a_pt: lda ,x+; /* Load 'a' reg with the y coord */ bsr xform_2; sta ,u; lda ,x; /* Load 'a' reg with the x coord */ bsr xform_1; adda ,u; /* Save transformed y coord */ sta ,u+; lda -1,x; /* Load 'a' reg with the y coord */ bsr xform_1; sta ,u; lda ,x+; /* Load 'a' reg with the x coord */ bsr xform_2; suba ,u; sta ,u+; /* Save transformed x coord */ lda 0x23; bmi process_next_point; bne dec_vl_counter; puls dp,pc; /* * The following routines all do a transformation of a * point. xform_1() and xform_2() expect the coordinate * to be specified in the 'a' register. xform_1a() and * xform_2a() expect the coordinate to already be stored * in C83B. */ xform_1: sta 0x3B; xform_1a: ldd 0x37; /* Use table index values 1 */ bra PF665; xform_2: sta 0x3B; xform_2a: ldd 0x39; /* Use table index values 2 */ PF665: stb 0x3C; bitb #0x01; beq PF66F; lda 0x3B; bra PF679; PF66F: ldb 0x3B; bpl PF676; com 0x3C; negb; PF676: mul; adca #0x00; PF679: ldb 0x3C; bpl PF67E; nega; PF67E: rts; /* * Move a block of memory, starting at hi memory, and * working down to low memory. The source address is * specified in the u register, and the destination is * specified in the x register. The number of bytes * moved is equal to the value in the a register + 1. * The maximum value which can be specified in the a * register is 0x80. */ move_block: ldb a,u; stb a,x; deca; bpl move_block; rtn2: rts; /* * This should be called before you attempt to make a sound. * It should be called every refresh cycle, just before you * call waitrecal(). If you want to start a new sound, then * you must set C856 to 1, and point the u register to the * sound block. If no sound is in progress (C856 = 0), then * it returns immediately. While a sound is in progress, C856 * will be set to 0x80. * This routine processes a single note at a time, and calculates * the amplitude and course/fine tuning values for the 3 sound * channels. The values calculated are stored in the array located * at C83F-C84C; C83F = reg D, C840 = regC, C841 = regB, etc.... * Dp must be set to C8. */ init_sound: lda 0x56; /* See if a sound is in progress */ bmi continue_sound; /* or if we should start a new one */ beq rtn2; init_sound2: ldx #music_stuff3; stx 0x4D; lda #0x80; sta 0x56; /* Flag that init_sound was called */ ldd ,u++; std 0x4F; /* Save 1st param in music block */ ldd ,u++; std 0x51; /* Save 2nd param in music block */ stu 0x53; /* Save ptr to block of music */ jsr $init_music_buf; ldd #0x1F1F; std 0x5F; ldd #0x0000; std 0x63; std 0x65; sta 0x55; bra PF6EC; continue_sound: ldu #0xC85E; /* Continue current sound */ ldb #0x02; PF6B8: lda b,u; cmpa #0x1F; beq PF6C0; inc b,u; PF6C0: decb; bpl PF6B8; ldx 0x51; ldu #0xC858; lda #0x07; PF6CA: inc ,u; cmpa ,u; bge PF6D2; clr ,u; PF6D2: ldb ,u+; andb #0x07; ldb b,x; stb ,u+; inca; cmpa #0x09; bls PF6CA; dec 0x57; bne PF74E; music_delay: lda 0x55; /* This does something like changing */ deca; /* octives! I really am quessing */ bpl PF6EA; lda #0x02; PF6EA: sta 0x55; PF6EC: ldb [C853]; /* Load the next music note */ ldu #0xC85E; clr a,u; bitb #0x40; /* "@" */ beq PF712; ldx #music_stuff1; lda a,x; anda 0x45; sta 0x45; lda 0x55; adda #0x03; lda a,x; ora 0x45; sta 0x45; andb #0x1F; stb 0x46; bra PF735; PF712: ldx #music_stuff2; lda a,x; anda 0x45; sta 0x45; lda 0x55; adda #0x03; lda a,x; ora 0x45; sta 0x45; lda 0x55; asla; adda #0x03; leau a,u; andb #0x3F; /* "?" */ aslb; ldx 0x4D; ldd b,x; std ,u; PF735: ldx 0x53; /* Grab next note, & update ptr */ ldb ,x+; stx 0x53; tstb; bmi music_delay; /* Negative note = ?? */ ldb ,x+; /* Grab the duration */ bpl PF748; jsr $init_music_buf; clr 0x56; /* All done when duration = 0x80 */ rts; PF748: stx 0x53; andb #0x3F; /* "?" */ stb 0x57; PF74E: ldy 0x4F; ldu #0xC85E; ldx #0xC842; lda #0x02; PF759: ldb ,u+; bitb #0x01; beq PF766; lsrb; ldb b,y; andb #0x0F; bra PF76D; PF766: lsrb; ldb b,y; lsrb; lsrb; lsrb; lsrb; PF76D: stb a,x; deca; bpl PF759; ldu #0xC867; ldx #0xC847; PF778: ldd ,--u; tst -8,u; bpl PF788; neg -8,u; subb -8,u; sbca #0x00; neg -8,u; bra PF78C; PF788: addb -8,u; adca #0x00; PF78C: std ,x++; cmpx #0xC84D; /* Temporarily stop when we have filled */ bne PF778; /* the music buffer completely. */ PF793: rts; player_string: .byte 0x20; /* rel y = 32 (for score) */ .byte 0xC0; /* rel x = -64 (for score) */ .byte 0x40; /* rel y = 64 (player string) */ .byte 0xC0; /* rel x = -64 (player string) */ .byte "PLAYER",0x80; game_string: .byte 0xE0; /* rel y = -32 (for game #) */ .byte 0xC0; /* rel x = -64 (for game #) */ .byte 0x01; /* rel y = 1 (for game string) */ .byte 0xC0; /* rel x = -64 (for game string) */ .byte " GAME",0x80; /* * Ask the player for the number of players, and the game * desired to play. The maximum number of players allowed * should be specified in the a register, and the maximum * game number should be specified in the b register. If * either a or b is zero, then that question will not be * asked. The number of players selected is returned in * C879, and the game number selected is returned in C87A. */ get_players_game: std $C84F; tsta; beq PF7B1; lda #0x01; PF7B1: tstb; beq PF7B6; ldb #0x01; PF7B6: std $C879; jsr $dptoC8; ldd #0xF850; std 0x2A; /* Set string height & width */ sta 0x3C; bra handle_buttons; /* * This routine allows the user a given amount of time to modiy * the game # and number of players. Each time a button is pressed, * the timer will be reset. Control will be relinquished when the * timer finally expires. */ button_handler_loop: jsr $waitrecal; clra; jsr $read_switches; /* Read switches, & decrement loop counter */ jsr $decrement_counters_C82E_C830; jsr $intensity_to_7F; lda $C879; ldy #player_string; bsr display_option_string; /* Ask user for the number of players */ lda $C87A; ldy #game_string; bsr display_option_string; /* Ask user for the game number */ jsr $dptoC8; lda 0x3C; beq check_loop_counter; lda 0x0F; bne handle_buttons; clr 0x3C; /* Reset loop counter when button pressed */ check_loop_counter: lda 0x2F; /* Check counter in C82F and exit */ beq PF793; /* when it reaches zero. */ lda 0x2E; bne button_handler_loop; lda 0x15; /* Start the game, if switch 4 has */ bne PF793; /* been presses. */ lda 0x12; beq check_button2; /* Increment the number of players */ lda 0x79; /* if switch 1 has been pressed. Be*/ beq check_button2; /* sure to handle wrap. */ inca; cmpa 0x4F; bls PF80C; lda #0x01; PF80C: sta 0x79; bra handle_buttons; check_button2: lda 0x7A; /* If multiple games can be selected, */ beq button_handler_loop; /* then increment the game number if */ ldb 0x13; /* switch 2 has been pressed. */ beq check_button3; inca; cmpa 0x50; bls PF82A; lda #0x01; bra PF82A; check_button3: ldb 0x14; /* Decrement the game number, if */ beq button_handler_loop; /* switch 3 has been pressed. */ deca; bne PF82A; lda 0x50; PF82A: sta 0x7A; handle_buttons: lda #0xF3; /* Reset the loop counters. */ sta 0x2F; coma; sta 0x2E; bra button_handler_loop; /* Go back to start of loop. */ /* * Display the player or game string, and the current * value for that option. The a register must contain * the value of the option, and the y register must * point to a block of the following form: * * rel y and rel x for value string * rel y and rel x for player/game string * player/game string * 0x80 */ display_option_string: ldx #0xC85E; /* C85E is a temp work variable */ pshs a; bsr set_dft_score; lda ,s+; beq dos_rtn; bsr convert_a_to_bcd_and_add; tfr x,u; ldd ,y++; jsr $print_at_d; tfr y,u; jsr $print_with_dft_hw: dos_rtn: rts; /* * Initialize the score string, pointed to by the x register, * to the string " 0",0x80 . */ set_dft_score: ldd #0x2020; std ,x; std 2,x; sta 4,x; ldd #0x3080; std 5,x; rts; /* * convert_a_to_bcd_and_add() takes the value in the a register, and * converts it to a string of BCD characters, which are then * added to the BCD string in the buffer pointed to by the x register. */ convert_a_to_bcd_and_add: ldu #0x0000; PF861: cmpa #0x63; /* 99 */ bls PF86D; suba #0x64; /* 100 */ leau 0x0100,u; bra PF861; PF86D: cmpa #0x09; /* 9 */ bls PF878; suba #0x0A; /* 10 */ leau 0x10,u; bra PF86D; PF878: leau a,u; tfr u,d; /* Add the BCD value in d register to ascii string stored in x reg */ add_d_to_x_in_bcd: pshs a; pshs b; ldb #0x05; PF882: clra; cmpb #0x01; bls PF897; bitb #0x01; beq PF88F; lda ,s; bra PF895; PF88F: lda ,s+; lsra; lsra; lsra; lsra; PF895: anda #0x0F; PF897: adda $C823; clr $C823; adda b,x; cmpa #0x2F; /* "/" */ bgt PF8A5; adda #0x10; PF8A5: cmpa #0x39; /* "9" */ bls PF8AE; suba #0x0A; inc $C823; PF8AE: sta b,x; /* Strip leading 0's */ decb; bpl PF882; clr $C823; clrb; PF8B7: lda b,x; cmpa #0x30; /* Replace leading 0 with */ bne PF8C6; /* a space. */ lda #0x20; sta b,x; incb; cmpb #0x05; blt PF8B7; PF8C6: rts; /* * This procedure compares the two score strings pointed to by the * x and u registers. It returns one of the following values, * depending upon which score was higher: * * 1) The scores are the same: a = 0; * 2) x score > u score: a = 1; * 3) x score