LIST OFF ; *** M I S S I L E C O M M A N D *** ; Copyright 1981 Atari, Inc. ; Designer: Rob Fulop ; Analyzed, labeled and commented ; by Dennis Debro ; Last Update: September 7, 2020 ; ; *** 124 BYTES OF RAM USED 4 BYTES FREE ; *** 13 BYTES OF ROM FREE ; ; ============================================================================== ; = THIS REVERSE-ENGINEERING PROJECT IS BEING SUPPLIED TO THE PUBLIC DOMAIN = ; = FOR EDUCATIONAL PURPOSES ONLY. THOUGH THE CODE WILL ASSEMBLE INTO THE = ; = EXACT GAME ROM, THE LABELS AND COMMENTS ARE THE INTERPRETATION OF MY OWN = ; = AND MAY NOT REPRESENT THE ORIGINAL VISION OF THE AUTHOR. = ; = = ; = THE ASSEMBLED CODE IS © 1981, ATARI, INC. = ; = = ; ============================================================================== processor 6502 ; ; NOTE: You must compile this with vcs.h version 105 or greater. ; TIA_BASE_READ_ADDRESS = $30 ; set the read address base so this runs on ; the real VCS and compiles to the exact ; ROM image include "vcs.h" include "macro.h" include "tia_constants.h" ; ; Make sure we are using vcs.h version 1.05 or greater. ; IF VERSION_VCS < 105 echo "" echo "*** ERROR: vcs.h file *must* be version 1.05 or higher!" echo "*** Updates to this file, DASM, and associated tools are" echo "*** available at https://dasm-assembler.github.io/" echo "" err ENDIF ; ; Make sure we are using macro.h version 1.01 or greater. ; IF VERSION_MACRO < 101 echo "" echo "*** ERROR: macro.h file *must* be version 1.01 or higher!" echo "*** Updates to this file, DASM, and associated tools are" echo "*** available at https://dasm-assembler.github.io/" echo "" err ENDIF LIST ON ;=============================================================================== ; A S S E M B L E R - S W I T C H E S ;=============================================================================== NTSC = 0 PAL50 = 1 PAL60 = 2 TRUE = 1 FALSE = 0 IFNCONST COMPILE_REGION COMPILE_REGION = NTSC ; change to compile for different regions ENDIF IF !(COMPILE_REGION = NTSC || COMPILE_REGION = PAL50 || COMPILE_REGION = PAL60) echo "" echo "*** ERROR: Invalid COMPILE_REGION value" echo "*** Valid values: NTSC = 0, PAL50 = 1, PAL60 = 2" echo "" err ENDIF ;=============================================================================== ; F R A M E - T I M I N G S ;=============================================================================== IF COMPILE_REGION = NTSC || COMPILE_REGION = PAL60 FPS = 60 ; ~60 frames per second VBLANK_TIME = 48 OVERSCAN_TIME = 26 INIT_IBM_VERT_DELAY = 32 ; ~ 7.5 pixels per frame INIT_IBM_VERT_DELAY_CHILD = 8 ; ~ 1.875 pixels per frame IBM_VERT_DELAY_INCREMENT_CHILD = 8 ELSE FPS = 50 ; ~50 frames per second VBLANK_TIME = 58 OVERSCAN_TIME = 32 INIT_IBM_VERT_DELAY = 45 ; ~ 8.79 pixels per frame INIT_IBM_VERT_DELAY_CHILD = 21 ; ~ 4.1 pixels per frame IBM_VERT_DELAY_INCREMENT_CHILD = 7 ENDIF ;=============================================================================== ; C O L O R - C O N S T A N T S ;=============================================================================== BLACK = $00 WHITE = $0E IF COMPILE_REGION = NTSC YELLOW = $10 RED_ORANGE = $20 RED = $40 ULTRAMARINE_BLUE = $70 BLUE = $80 GREEN = $C0 DK_GREEN = $D0 COLOR_FIRST_WAVE_CLUSTER_CITIES = BLUE + 4 COLOR_FIRST_WAVE_CLUSTER_IBM = RED + 8 COLOR_FIRST_WAVE_CLUSTER_SKY = BLACK COLOR_FIRST_WAVE_CLUSTER_MISSILE_BASE = RED_ORANGE + 4 COLOR_FIRST_WAVE_CLUSTER_SCORE = RED + 7 COLOR_SECOND_WAVE_CLUSTER_CITIES = BLUE + 4 COLOR_SECOND_WAVE_CLUSTER_IBM = GREEN + 14 COLOR_SECOND_WAVE_CLUSTER_SKY = BLACK COLOR_SECOND_WAVE_CLUSTER_MISSILE_BASE = DK_GREEN + 8 COLOR_SECOND_WAVE_CLUSTER_SCORE = WHITE COLOR_THIRD_WAVE_CLUSTER_CITIES = DK_GREEN + 10 COLOR_THIRD_WAVE_CLUSTER_IBM = RED + 8 COLOR_THIRD_WAVE_CLUSTER_SKY = BLACK COLOR_THIRD_WAVE_CLUSTER_MISSILE_BASE = BLUE + 4 COLOR_THIRD_WAVE_CLUSTER_SCORE = BLUE + 8 COLOR_FOURTH_WAVE_CLUSTER_CITIES = BLUE + 10 COLOR_FOURTH_WAVE_CLUSTER_IBM = YELLOW + 10 COLOR_FOURTH_WAVE_CLUSTER_SKY = BLACK COLOR_FOURTH_WAVE_CLUSTER_MISSILE_BASE = RED + 4 COLOR_FOURTH_WAVE_CLUSTER_SCORE = RED_ORANGE + 4 COLOR_FIFTH_WAVE_CLUSTER_CITIES = RED_ORANGE + 8 COLOR_FIFTH_WAVE_CLUSTER_IBM = RED + 12 COLOR_FIFTH_WAVE_CLUSTER_SKY = BLUE + 4 COLOR_FIFTH_WAVE_CLUSTER_MISSILE_BASE = DK_GREEN + 10 COLOR_FIFTH_WAVE_CLUSTER_SCORE = DK_GREEN + 10 COLOR_SIXTH_WAVE_CLUSTER_CITIES = BLUE + 8 COLOR_SIXTH_WAVE_CLUSTER_IBM = WHITE COLOR_SIXTH_WAVE_CLUSTER_SKY = GREEN + 4 COLOR_SIXTH_WAVE_CLUSTER_MISSILE_BASE = BLACK COLOR_SIXTH_WAVE_CLUSTER_SCORE = WHITE COLOR_SEVENTH_WAVE_CLUSTER_CITIES = YELLOW + 10 COLOR_SEVENTH_WAVE_CLUSTER_IBM = WHITE COLOR_SEVENTH_WAVE_CLUSTER_SKY = ULTRAMARINE_BLUE + 4 COLOR_SEVENTH_WAVE_CLUSTER_MISSILE_BASE = GREEN + 8 COLOR_SEVENTH_WAVE_CLUSTER_SCORE = GREEN + 10 COLOR_EIGHTH_WAVE_CLUSTER_CITIES = RED + 4 COLOR_EIGHTH_WAVE_CLUSTER_IBM = BLACK COLOR_EIGHTH_WAVE_CLUSTER_SKY = YELLOW + 10 COLOR_EIGHTH_WAVE_CLUSTER_MISSILE_BASE = GREEN + 8 COLOR_EIGHTH_WAVE_CLUSTER_SCORE = RED + 4 ELSE YELLOW = $20 BRICK_RED = $40 DK_GREEN = $50 RED = $60 PURPLE_2 = $70 PURPLE = $80 BLUE = $D0 COLOR_FIRST_WAVE_CLUSTER_CITIES = BLUE + 2 COLOR_FIRST_WAVE_CLUSTER_IBM = RED + 8 COLOR_FIRST_WAVE_CLUSTER_SKY = BLACK COLOR_FIRST_WAVE_CLUSTER_MISSILE_BASE = BRICK_RED + 4 COLOR_FIRST_WAVE_CLUSTER_SCORE = RED + 6 COLOR_SECOND_WAVE_CLUSTER_CITIES = BLUE + 2 COLOR_SECOND_WAVE_CLUSTER_IBM = DK_GREEN + 14 COLOR_SECOND_WAVE_CLUSTER_SKY = BLACK COLOR_SECOND_WAVE_CLUSTER_MISSILE_BASE = PURPLE_2 + 8 COLOR_SECOND_WAVE_CLUSTER_SCORE = WHITE COLOR_THIRD_WAVE_CLUSTER_CITIES = PURPLE_2 + 10 COLOR_THIRD_WAVE_CLUSTER_IBM = RED + 10 COLOR_THIRD_WAVE_CLUSTER_SKY = BLACK COLOR_THIRD_WAVE_CLUSTER_MISSILE_BASE = BLUE + 2 COLOR_THIRD_WAVE_CLUSTER_SCORE = BLUE + 8 COLOR_FOURTH_WAVE_CLUSTER_CITIES = BLUE + 10 COLOR_FOURTH_WAVE_CLUSTER_IBM = YELLOW + 12 COLOR_FOURTH_WAVE_CLUSTER_SKY = BLACK COLOR_FOURTH_WAVE_CLUSTER_MISSILE_BASE = RED + 4 COLOR_FOURTH_WAVE_CLUSTER_SCORE = BRICK_RED + 4 COLOR_FIFTH_WAVE_CLUSTER_CITIES = BRICK_RED + 6 COLOR_FIFTH_WAVE_CLUSTER_IBM = RED + 12 COLOR_FIFTH_WAVE_CLUSTER_SKY = BLUE + 2 COLOR_FIFTH_WAVE_CLUSTER_MISSILE_BASE = PURPLE_2 + 10 COLOR_FIFTH_WAVE_CLUSTER_SCORE = PURPLE_2 + 10 COLOR_SIXTH_WAVE_CLUSTER_CITIES = BLUE + 8 COLOR_SIXTH_WAVE_CLUSTER_IBM = WHITE COLOR_SIXTH_WAVE_CLUSTER_SKY = DK_GREEN + 4 COLOR_SIXTH_WAVE_CLUSTER_MISSILE_BASE = BLACK COLOR_SIXTH_WAVE_CLUSTER_SCORE = WHITE COLOR_SEVENTH_WAVE_CLUSTER_CITIES = YELLOW + 12 COLOR_SEVENTH_WAVE_CLUSTER_IBM = WHITE COLOR_SEVENTH_WAVE_CLUSTER_SKY = PURPLE + 2 COLOR_SEVENTH_WAVE_CLUSTER_MISSILE_BASE = DK_GREEN + 8 COLOR_SEVENTH_WAVE_CLUSTER_SCORE = DK_GREEN + 10 COLOR_EIGHTH_WAVE_CLUSTER_CITIES = RED + 4 COLOR_EIGHTH_WAVE_CLUSTER_IBM = BLACK COLOR_EIGHTH_WAVE_CLUSTER_SKY = YELLOW + 12 COLOR_EIGHTH_WAVE_CLUSTER_MISSILE_BASE = DK_GREEN + 8 COLOR_EIGHTH_WAVE_CLUSTER_SCORE = RED + 4 ENDIF ;=============================================================================== ; U S E R - C O N S T A N T S ;=============================================================================== ROM_BASE = $F000 W_SCREEN = 160 IF COMPILE_REGION = PAL50 H_MISSILE_KERNEL = 102 ELSE H_MISSILE_KERNEL = 84 ENDIF H_FONT = 7 H_CITY = 9 H_MISSILE_BASE = 4 H_SMALL_EXPLOSION = 8 H_LARGE_EXPLOSION = 16 XMIN = 0 XMAX = 159 XMID = (XMAX / 2) XMIN_TARGET_CONTROL = XMIN + 9 XMAX_TARGET_CONTROL = XMAX - 8 YMIN_TARGET_CONTROL = 10 YMAX_TARGET_CONTROL = H_MISSILE_KERNEL - 1 MAX_CITIES = 6 MAX_LAUNCHED_ABM = 3 MAX_WAVE_NUMBER = 15 MAX_GAME_SELECTION = $34 ; BCD INIT_NUMBER_LAUNCH_ABM = 10 INIT_TARGET_CONTROL_POSITION = 50 INIT_ABM_VERT_POSITION = 1 HORIZ_POSITION_CITY_01 = 24 HORIZ_POSITION_CITY_02 = 40 HORIZ_POSITION_CITY_03 = 56 HORIZ_POSITION_MISSILE_BASE = 80 HORIZ_POSITION_CITY_04 = 106 HORIZ_POSITION_CITY_05 = 122 HORIZ_POSITION_CITY_06 = 138 ; ; Point values in BCD ; UNUSED_ABM_POINTS = $0005 IBM_POINTS = $0025 SAVED_CITY_POINTS = $0100 ENEMY_CRUISE_MISSILE_POINTS = $0125 ; ; Game variation flags ; NUM_PLAYERS_MASK = %10000000 SMART_CRUISE_MISSILES = %01000000 STARTING_WAVE_MASK = %00001110 FAST_TARGET_CONTROL = %00000001 TARGET_CONTROL_MASK = %00000001 ; Game variation bits ONE_PLAYER_GAME = 0 << 7 TWO_PLAYER_GAME = 1 << 7 SMART_CRUISE_MISSILE = 1 << 6 DUMB_CRUISE_MISSILE = 0 << 6 FAST_TARGET_CONTROL = 1 SLOW_TARGET_CONTROL = 0 ; ; Game state flags ; GAME_OVER = %10000000 RESET_DEBOUNCE_MASK = %01000000 GAME_SELECTION_SCREEN = %00000100 RESERVED_MISSILE_DUMP_MASK = %00000011 RESET_DOWN = 0 << 6 RESET_RELEASED = 1 << 6 ; ; Sound engine mask values ; SOUND_ENGINE_LEFT_CHANNEL_MASK = %00001111 SOUND_ENGINE_RIGHT_CHANNEL_MASK = %11110000 ; ; Sound engine values ; SOUND_TURN_OFF_VOLUME = 0 SOUND_BEGINNING_WAVE_ALARM = 1 SOUND_ABM_EXHAUSTION = 2 SOUND_ENEMY_CRUISE_MISSILE = 3 SOUND_REMAINING_ABM_TALLY = 4 SOUND_REMAINING_CITIES_TALLY = 5 SOUND_BONUS_CITY = 6 SOUND_THE_END = 7 SOUND_ABM_LAUNCHING = 16 SOUND_ABM_EXPLODING = 32 SOUND_WORLD_EXPLOSION = 48 SOUND_CITY_EXPLOSION = 64 ; ; Initial sound frequency values ; SOUND_BEGINNING_WAVE_ALARM_FREQUENCY = 4 SOUND_POINT_TALLY_FREQUENCY = 24 ; ; Initial sound tone values ; SOUND_ABM_LAUNCHING_TONE = 8 SOUND_CRUISE_MISSILE_TONE = 5 SOUND_POINT_TALLY_TONE = 8 SOUND_BONUS_CITY_TONE = 12 ; ; Initial sound volume values ; SOUND_ABM_LAUNCHING_VOLUME = 6 SOUND_ABM_EXHAUSTION_VOLUME = 8 SOUND_BONUS_CITY_VOLUME = 8 INIT_CRUISE_MISSILE_VOLUME = 2 IF COMPILE_REGION = PAL50 INIT_ABM_TALLY_VOLUME = 6 MAX_BEGINNING_WAVE_ALARM_FREQUENCY = 16 INIT_WAVE_TRANSITION_TIMER_ALARM = 127 + 33 WAVE_TRANSITION_INIT_TARGET_CONTROL = 127 + 25 WAVE_TRANSITION_NEW_WAVE = 127 + 41 ELSE INIT_ABM_TALLY_VOLUME = 7 MAX_BEGINNING_WAVE_ALARM_FREQUENCY = 20 INIT_WAVE_TRANSITION_TIMER_ALARM = 127 + 41 WAVE_TRANSITION_INIT_TARGET_CONTROL = 127 + 33 WAVE_TRANSITION_NEW_WAVE = 127 + 49 ENDIF ; ; Missile status values ; ACTIVE_EXPLOSION_SIZE_MASK = %10000000 ACTION_BUTTON_DEBOUNCE_MASK = %00001000 ACTIVE_SMALL_EXPLOSION = 0 << 7 ACTIVE_LARGE_EXPLOSION = 1 << 7 ACTION_BUTTON_DOWN = 1 << 3 ACTION_BUTTON_UP = 0 << 3 ; ; Game option flags ; WORLD_EXPLOSION_MASK = %10000000 CHILD_GAME_MASK = %01000000 CHILD_GAME = 1 << 6 ; ; Launched ABM attribute flags ; ABM_ACTIVE = %10000000 ABM_DISTANCE_MASK = %01000000 ABM_EXPLODING_FLAG = %00100000 ABM_HORIZ_DISTANCE_GREATER = 0 << 6 ABM_VERT_DISTANCE_GREATER = 1 << 6 ; ; Pixel offset for ABM launch ; ABM_EXPERT_MOVEMENT_VALUE = 1 ABM_AMATEUR_MOVEMENT_VALUE = 2 ; ; Current Enemy Missile flags ; ENEMY_MISSILE_DIRECTION_MASK = %11110000 ENEMY_MISSILE_TYPE_MASK = %00000010 ENEMY_MISSILE_TYPE_IBM = 0 << 1 ENEMY_MISSILE_TYPE_CRUISE = 1 << 1 ;=============================================================================== ; Z P - V A R I A B L E S ;=============================================================================== SEG.U variables .org $80 frameCount ds 1 tmpDeltaY ds 1 ;-------------------------------------- tmpEnemyMissileSlopeIntegerValue = tmpDeltaY ;-------------------------------------- tmpCurrentIBMVertPos = tmpEnemyMissileSlopeIntegerValue ;-------------------------------------- tmpIBMPlanetCollisionHorizPosition = tmpCurrentIBMVertPos tmpHundredsValueHolder ds 1 ;-------------------------------------- tmpScoreMultiplier = tmpHundredsValueHolder ;-------------------------------------- tmpColorTableIndexMulti = tmpScoreMultiplier ;-------------------------------------- tmpTargetControlDeltaX = tmpColorTableIndexMulti ;-------------------------------------- tmpTargetContolMinDistValue = tmpTargetControlDeltaX ;-------------------------------------- tmpMaximumMissileCount = tmpTargetContolMinDistValue ;-------------------------------------- tmpDeltaX = tmpMaximumMissileCount ;-------------------------------------- tmpCollisionBoxArea = tmpDeltaX ;-------------------------------------- tmpGreatestDistance = tmpCollisionBoxArea ;-------------------------------------- tmpEnemyTargetPosition = tmpGreatestDistance ;-------------------------------------- tmpIBMHorizPosition = tmpEnemyTargetPosition tmpEnemyMissileVertPos ds 1 ;-------------------------------------- tmpActiveExplosionVertPos = tmpEnemyMissileVertPos ;-------------------------------------- tmpTargetContolMaxDistValue = tmpActiveExplosionVertPos ;-------------------------------------- tmpDiv4 = tmpTargetContolMaxDistValue ;-------------------------------------- tmpIBMFarthestOffsetValue = tmpDiv4 activeABMVertPos ds 1 targetControlScanline ds 1 hueMask ds 1 ;-------------------------------------- objectHorizPositions = hueMask ;-------------------------------------- tmpSpawnedIBMSlopeFraction = objectHorizPositions activeExplodingABMHorizPos ds 1 ;-------------------------------------- tmpIBMHorizAdjustment = activeExplodingABMHorizPos tmpActiveIBMHorizPos ds 1 ;-------------------------------------- tmpMissileIndex = tmpActiveIBMHorizPos activeABMHorizPos ds 1 targetControlHorizPos ds 1 missileGraphicsPointer ds 2 ;-------------------------------------- activeExplosionGraphicPointer = missileGraphicsPointer ;-------------------------------------- ibmHorizOffsetPointer = activeExplosionGraphicPointer ;-------------------------------------- ibmNUSIZPointer = ibmHorizOffsetPointer ;-------------------------------------- ibmHorizPositionPointer = ibmNUSIZPointer targetControlVertPosValues ds 2 ;-------------------------------------- targetControlVertFracPos = targetControlVertPosValues targetControlVertIntPos = targetControlVertFracPos + 1 targetControlHorizPosValues ds 2 ;-------------------------------------- targetControlHorizFracPos = targetControlHorizPosValues targetControlHorizIntPos = targetControlHorizFracPos + 1 enemyMissileNUSIZValues ds 2 ;-------------------------------------- currentIBMNUSIZValue = enemyMissileNUSIZValues alternateIBMNUSIZValue = currentIBMNUSIZValue + 1 enemyMissileVertPos ds 2 ;-------------------------------------- currentIBMVertPos = enemyMissileVertPos alternateIBMVertPos = currentIBMVertPos + 1 enemyMissileSlopeFractionValues ds 2 ;-------------------------------------- currentIBMSlopeFraction = enemyMissileSlopeFractionValues alternativeIBMSlopeFraction = currentIBMSlopeFraction + 1 enemyMissileValues ds 2 ;-------------------------------------- currentIBMValues = enemyMissileValues alternativeIBMValues = currentIBMValues + 1 enemyMissileHorizPos ds 2 ;-------------------------------------- currentIBMHorizPos = enemyMissileHorizPos alternativeIBMHorizPos = currentIBMHorizPos + 1 ibmFractionalDelay ds 1 ibmVerticalDelay ds 2 explodingABMAnimationIndexes ds 3 activeExplosionHeight ds 1 explodingABMState ds 1 explodingABMVertPos ds 3 explodingABMHorizPos ds 3 cityGraphicPointers ds 12 ;-------------------------------------- _1stCityGraphicPointer = cityGraphicPointers _2ndCityGraphicPointer = _1stCityGraphicPointer + 2 _3rdCityGraphicPointer = _2ndCityGraphicPointer + 2 _4thCityGraphicPointer = _3rdCityGraphicPointer + 2 _5thCityGraphicPointer = _4thCityGraphicPointer + 2 _6thCityGraphicPointer = _5thCityGraphicPointer + 2 launchedABMAttributes ds 3 launchedABMGreatestDistance ds 3 abmOriginToTargetDiffChange ds 3 abmDistanceChangeValues ds 3 abmOriginToTargetDifference ds 3 launchedABMHorizPositions ds 3 launchedABMVertPositions ds 3 launchedABMIndex ds 1 digitGraphicPointers ds 12 explodingABMColor ds 1 random ds 2 tmpLoopCount ds 1 ;-------------------------------------- tmpJoystickValue = tmpLoopCount ;-------------------------------------- tmpGameSelectionDelay = tmpJoystickValue ;-------------------------------------- tmpABMPositionIncrement = tmpGameSelectionDelay ;-------------------------------------- tmpPointsOnesValue = tmpABMPositionIncrement ;-------------------------------------- tmpColorXOR = tmpPointsOnesValue ;-------------------------------------- tmpMod16 = tmpColorXOR ;-------------------------------------- tmpIBMSlopeFraction = tmpMod16 ;-------------------------------------- tmpIBMDirectionValue = tmpIBMSlopeFraction ;-------------------------------------- tmpBonusCityPosition = tmpIBMDirectionValue ;-------------------------------------- tmpActiveExplosionHeight = tmpBonusCityPosition ;-------------------------------------- tmpIBMDeltaX = tmpActiveExplosionHeight gameState ds 1 abmMoveFromReserveDelay ds 1 launchingBaseABMs ds 1 remainingWaveIBMs ds 1 ;-------------------------------------- waveTransitionTimerValue = remainingWaveIBMs soundEngineValues ds 1 leftAudioValues ds 1 ;-------------------------------------- remainingCityTallyIndex = leftAudioValues ;-------------------------------------- remainingABMTallyVolume = remainingCityTallyIndex ;-------------------------------------- beginningWaveAlarmFrequency = remainingABMTallyVolume ;-------------------------------------- abmExhaustionFrequencyIndex = beginningWaveAlarmFrequency ;-------------------------------------- enemyCruiseMissleVolume = abmExhaustionFrequencyIndex ;-------------------------------------- theEndLeftAudioVolume = enemyCruiseMissleVolume ;-------------------------------------- bonusCityLeftSoundIndex = theEndLeftAudioVolume rightAudioValues ds 1 ;-------------------------------------- explodingABMVolumeIndex = rightAudioValues ;-------------------------------------- abmLaunchingSoundIndex = explodingABMVolumeIndex ;-------------------------------------- explosionAudioValues = abmLaunchingSoundIndex objectColorValues ds 5 ;-------------------------------------- cityColor = objectColorValues ibmColor = cityColor + 1 skyColor = ibmColor + 1 missileBaseColor = skyColor + 1 scoreColor = missileBaseColor + 1 waveNumber ds 1 colorCycleMode ds 1 gameSelection ds 1 selectDebounce ds 1 remainingWaveCruiseMissiles ds 1 playerCityArray ds 2 ;-------------------------------------- player1CityArray = playerCityArray player2CityArray = player1CityArray + 1 activeABMNUSIZValue ds 1 playerScore ds 6 currentPlayerNumber ds 1 bonusCitiesRewarded ds 2 ;-------------------------------------- player1BonusCitiesRewarded = bonusCitiesRewarded player2BonusCitiesRewarded = player1BonusCitiesRewarded + 1 gameVariation ds 1 cruiseMissileVerticalDelay ds 2 gameOptions ds 1 echo "***",(* - $80)d, "BYTES OF RAM USED", ($100 - *)d, "BYTES FREE" ;=============================================================================== ; R O M - C O D E ;=============================================================================== SEG Bank0 .org ROM_BASE Start jmp ColdStart PositionObjectsHorizontally ldx #<[targetControlHorizPos - objectHorizPositions] .positionObjectsHorizontally lda #2 ; adjustment value for missile or BALL cpx #<[tmpActiveIBMHorizPos - objectHorizPositions] bcs .adjustObjectHorizPosition ; branch if object is a missile or BALL lda #1 ldy activeExplosionHeight ; get active explosion 2's complement height cpy #~(H_LARGE_EXPLOSION - 1) bne .adjustObjectHorizPosition lda #<-4 ; adjust for large explosion .adjustObjectHorizPosition clc adc objectHorizPositions,x ldy #3 - 1 ; minimum coarse position color clock is 69 sec .divideBy15 iny ; increment for coarse positioning sbc #15 ; divide horizontal position by 15 bcs .divideBy15 eor #$FF ; get 1's complement of remainder sbc #7 - 1 ; subtract value by 7 (i.e. carry clear) asl ; shift to upper nybbles for fine motion asl asl asl sta WSYNC ; force to next scan line .coarseMoveObject dey bpl .coarseMoveObject sta RESP0,x ; set object's coarse position sta HMP0,x ; set object's fine motion value lda activeExplosionHeight bne .donePositionObjectsHorizontally dex bpl .positionObjectsHorizontally .donePositionObjectsHorizontally sta WSYNC sta HMOVE sta WSYNC rts ColdStart sei ; disable interrupts cld ; clear decimal mode ldx #0 stx SWACNT ; set all of SWCHA as input txa ; a = 0 .clearLoop sta VSYNC,x inx bne .clearLoop dex ; x = #$FF txs ; set stack to beginning lda #GAME_OVER | GAME_SELECTION_SCREEN | 3 sta gameState lda #1 sta gameSelection ; set to first game selection MainLoop VerticalSync lda #DISABLE_TIA sta VBLANK ; disable TIA (D1 = 1) sta VSYNC ; start vertical sync (D1 = 1) sta WSYNC sta WSYNC sta WSYNC lda #STOP_VERT_SYNC sta VSYNC ; end vertical sync (D1 = 0) lda #VBLANK_TIME sta TIM64T ; set timer for vertical blanking period inc frameCount ; increment frame count bne .checkToMoveABMsFromReserve inc colorCycleMode ; incremented ~ every 4.2 seconds lda gameState ; get current game state bpl .checkToMoveABMsFromReserve ; branch if not GAME_OVER lda gameVariation ; get the current game variation bpl .checkToMoveABMsFromReserve ; branch if ONE_PLAYER_GAME lda currentPlayerNumber ; get the current player number eor #1 ; flip D0 sta currentPlayerNumber ; set new current player number .checkToMoveABMsFromReserve lda abmMoveFromReserveDelay ; get move from reserve delay value beq TallyPointsForRemainingABMsAndCities;branch if timer done dec abmMoveFromReserveDelay ; decrement move from reserve delay bne TallyPointsForRemainingABMsAndCities jsr MoveABMsFromReserve ; move remaining ABMs from reserve TallyPointsForRemainingABMsAndCities bit gameState ; check current game state bpl .checkToTallyPointsForRemainingABMs;branch if not GAME_OVER lda #127 + 128 sta waveTransitionTimerValue ; set wave transition timer .checkToTallyPointsForRemainingABMs lda waveTransitionTimerValue ; get wave transition timer cmp #127 + 105 bne .tallyPointsForRemainingCities;branch if not talling for remaining ABMs lda remainingCityTallyIndex ; get remaining city tally index value bne .checkForGameReset lda #INIT_ABM_TALLY_VOLUME sta remainingABMTallyVolume lda launchingBaseABMs ; get number of ABMs left at launch base bne .recordPointsForRemainingABMs; branch if ABMs left to launch jsr MoveABMsFromReserve ; move reserve ABMs to be counted lda launchingBaseABMs ; get number of ABMs left at launch base beq TallyPointsForRemainingCities; branch if no more ABMs left to launch .recordPointsForRemainingABMs lda #SOUND_REMAINING_ABM_TALLY sta soundEngineValues ; set to play SOUND_REMAINING_ABM_TALLY dec launchingBaseABMs ; reduce number of ABMs left at launch base lda #UNUSED_ABM_POINTS jsr IncrementScore ; increase score for remaining ABMs .checkForGameReset jmp CheckForGameReset TallyPointsForRemainingCities lda #127 + 97 sta waveTransitionTimerValue ; set wave transition timer for 97 ticks lda #SOUND_TURN_OFF_VOLUME sta soundEngineValues sta remainingCityTallyIndex .tallyPointsForRemainingCities lda waveTransitionTimerValue ; get wave transition timer value cmp #127 + 97 bne .decrementWaveTransitionTimerValue lda frameCount ; get current frame count and #$0F bne CheckForGameReset ; tally Cities every 16 frames ldx remainingCityTallyIndex ; get remaining City tally index value .lookForRemainingCities cpx #MAX_CITIES beq .setToPlayBeginningWaveAlarm ; branch if all City points tallied ldy CityBonusTallyOrder,x ; get index for City bonus tally lda cityGraphicPointers,y ; get City graphic pointer LSB value cmp #SAVED_CITY_POINTS jsr IncrementScore ; increment score for remaining cities lda #SOUND_REMAINING_CITIES_TALLY sta soundEngineValues ; set to play SOUND_REMAINING_CITIES_TALLY bne CheckForGameReset ; unconditional branch .setToPlayBeginningWaveAlarm lda #INIT_WAVE_TRANSITION_TIMER_ALARM sta waveTransitionTimerValue lda #SOUND_BEGINNING_WAVE_ALARM_FREQUENCY sta beginningWaveAlarmFrequency lda #SOUND_BEGINNING_WAVE_ALARM sta soundEngineValues ; set to play SOUND_BEGINNING_WAVE_ALARM jsr CheckToRewardBonusCities jmp DetermineCurrentLaunchedABMIndex .decrementWaveTransitionTimerValue lda soundEngineValues ; get sound engine values cmp #SOUND_BONUS_CITY beq CheckForGameReset ; branch if playing SOUND_BONUS_CITY bit waveTransitionTimerValue ; check wave transition timer value bpl CheckForGameReset ; branch if done transitioning wave time lda frameCount ; get current frame count and #3 bne CheckForGameReset dec waveTransitionTimerValue bmi .checkToInitTargetControlPosition;branch if still transitioning wave jmp InitializeWaveValues .checkToInitTargetControlPosition lda waveTransitionTimerValue ; get wave transition timer value cmp #WAVE_TRANSITION_INIT_TARGET_CONTROL bne CheckForGameReset ; skip init Target Control position lda #INIT_TARGET_CONTROL_POSITION sta targetControlHorizIntPos sta targetControlVertIntPos jsr CheckToIncrementWaveNumber CheckForGameReset bit gameState ; check current game state bvc ResetCityAndDigitSprites ; branch if RESET_DOWN lda SWCHB ; read console switches and #SELECT_MASK | RESET_MASK ; mask to get SELECT and RESET values beq .selectAndResetPressed ; branch if SELECT and RESET are pressed lsr ; RESET now in carry bcc StartNewGame ; branch if RESET down .selectAndResetPressed jmp CheckForGameSelect StartNewGame lda #0 sta gameState ResetCityAndDigitSprites lda #CitySprite ldx #10 .resetCityAndDigitSpriteLoop sta cityGraphicPointers,x ; set City sprite LSB sty cityGraphicPointers + 1,x ; set City sprite MSB sty digitGraphicPointers + 1,x ; set digit graphic pointer MSB dex dex bpl .resetCityAndDigitSpriteLoop iny ; y = 0 ldx #5 .clearPlayerScores sty playerScore,x ; set player score to 0 dex bpl .clearPlayerScores sty player2CityArray ; clear player 2 city array sty currentPlayerNumber ; set new current player number sty gameOptions sty player1BonusCitiesRewarded sty player2BonusCitiesRewarded ldy gameSelection ; get current game selection lda GameVariationTable,y ; get game selection game variation ldx #$3F stx player1CityArray ; reset player 1 City array cpy #$18 bcc .setGameVariation ; branch if ONE_PLAYER_GAME tya ; move game selection to accumulator sed sec sbc #$17 ; subtract to get game variation tay cld lda GameVariationTable,y ; get game selection game variation stx player2CityArray ; reset player 2 City array ora #TWO_PLAYER_GAME ; set D7 for TWO_PLAYER_GAME ldx #1 stx currentPlayerNumber ; set new current player number .setGameVariation sta gameVariation ; set current game variation cpy #$17 bne .determineStartingWaveNumber ; branch if not CHILD_GAME ldy #CHILD_GAME sty gameOptions .determineStartingWaveNumber and #STARTING_WAVE_MASK ; mask starting wave from game variation tax ; move starting wave number to x dex ; reduce value by 1 stx waveNumber ; save the starting wave number lda #H_MISSILE_KERNEL sta currentIBMVertPos sta alternateIBMVertPos lda gameState ; get current game state ora #RESET_RELEASED sta gameState ; set to show RESET_RELEASED bit gameState ; check current game state bmi .checkForGameSelect ; branch if GAME_OVER lda #INIT_TARGET_CONTROL_POSITION sta targetControlVertIntPos sta targetControlHorizIntPos ldx #SOUND_BEGINNING_WAVE_ALARM stx soundEngineValues ; set to play SOUND_BEGINNING_WAVE_ALARM ldx #SOUND_BEGINNING_WAVE_ALARM_FREQUENCY stx beginningWaveAlarmFrequency lda #INIT_IBM_VERT_DELAY - 13 bit gameOptions bvc DetermineIBMFractionDelayValue;branch if not CHILD_GAME lda #INIT_IBM_VERT_DELAY_CHILD - 13 DetermineIBMFractionDelayValue ldy waveNumber ; get current wave number iny .determineIBMFractionDelayValue clc adc #13 dey bpl .determineIBMFractionDelayValue sta ibmFractionalDelay ; set initial IBM fractional delay lda #WAVE_TRANSITION_NEW_WAVE sta waveTransitionTimerValue lda frameCount ; get current frame count ora #2 sta random ; seed random number sta random + 1 .checkForGameSelect jmp CheckForGameSelect InitializeWaveValues ldy #MAX_WAVE_NUMBER lda waveNumber ; get current wave number cmp #MAX_WAVE_NUMBER + 1 bcs .determineNumberOfIBMsForWave tay .determineNumberOfIBMsForWave lda MaximumWaveIBMValues,y ; get maximum IBMs for wave bit gameOptions bvc .setNumberOfIBMsForWave ; branch if not CHILD_GAME lsr ; divide value by 2 .setNumberOfIBMsForWave sta remainingWaveIBMs ; set remaining wave IBMs lda MaximumWaveCruiseMissileValues,y sta remainingWaveCruiseMissiles ; set remaining wave cruise missiles lda #INIT_NUMBER_LAUNCH_ABM sta launchingBaseABMs ; initialize number of ABMs at missile base lda #XMID ldy #INIT_ABM_VERT_POSITION ldx #MAX_LAUNCHED_ABM - 1 .initABMValues sta launchedABMHorizPositions,x sty launchedABMVertPositions,x sty launchedABMAttributes,x dec launchedABMAttributes,x dex bpl .initABMValues stx currentIBMNUSIZValue stx alternateIBMNUSIZValue inx ; x = 0 stx soundEngineValues ; set to SOUND_TURN_OFF_VOLUME lda #RESET_RELEASED sta gameState CheckForGameSelect ldy #31 lda SWCHB ; read console switches and #SELECT_MASK | RESET_MASK ; keep SELECT and RESET values bne .setGameSelectionDelayValue ldy #7 .setGameSelectionDelayValue sty tmpGameSelectionDelay lda SWCHB ; read console switches lsr ; RESET now in carry lsr ; SELECT now in carry lda #$FF bcc .selectSwitchDown sta selectDebounce ; reset select debounce value bne .doneCheckForGameSelect ; unconditional branch .selectSwitchDown sta gameState lda selectDebounce ; get select debounce value bmi .incrementGameSelectionValue eor frameCount and tmpGameSelectionDelay bne .doneCheckForGameSelect .incrementGameSelectionValue lda frameCount ; get current frame count and tmpGameSelectionDelay sta selectDebounce sed clc lda gameSelection ; get current game selection adc #1 ; increment game selection by 1 cmp #MAX_GAME_SELECTION + 1 ; make sure game selection is within bounds bne .setNewGameSelection lda #1 ; set to wrap game selection to 1 .setNewGameSelection sta gameSelection cld lda #SOUND_TURN_OFF_VOLUME sta soundEngineValues ; set to SOUND_TURN_OFF_VOLUME sta colorCycleMode sta waveNumber sta targetControlVertIntPos sta targetControlHorizIntPos .doneCheckForGameSelect bit gameOptions bpl CheckForActionButtonPressed ; branch if not WORLD_EXPLOSION jmp DetermineCurrentLaunchedABMIndex CheckForActionButtonPressed jsr NextRandom ; get next random number ldx currentPlayerNumber ; get the current player number lda INPT4,x ; read player action button bpl .actionButtonPressed ; branch if player pressing action button lda explodingABMState ; get exploding ABM state and #~ACTION_BUTTON_DEBOUNCE_MASK; clear ACTION_BUTTON_DEBOUNCE value sta explodingABMState jmp DetermineABMMovementValue .actionButtonPressed lda explodingABMState ; get exploding ABM state and #ACTION_BUTTON_DEBOUNCE_MASK ; keep ACTION_BUTTON_DEBOUNCE value bne DetermineABMMovementValue ; branch if action button held ldx #MAX_LAUNCHED_ABM - 1 .searchForNonActiveABM lda launchedABMAttributes,x ; get launched ABM attribute bpl LaunchABM ; branch if ABM not active dex bpl .searchForNonActiveABM .abmLaunchExhausted lda soundEngineValues ; get sound engine values and #SOUND_ENGINE_LEFT_CHANNEL_MASK;keep left channel sound values bne DetermineABMMovementValue ; branch if sounds set for left channel sta abmExhaustionFrequencyIndex lda soundEngineValues ; get sound engine values ora #SOUND_ABM_EXHAUSTION sta soundEngineValues ; set to play SOUND_ABM_EXHAUSTION bne DetermineABMMovementValue ; unconditional branch LaunchABM lda #ACTION_BUTTON_DOWN ora explodingABMState ; combine with exploding ABM state sta explodingABMState ; set to show ACTION_BUTTON_DOWN lda waveTransitionTimerValue ; get wave transition timer value bmi DetermineABMMovementValue ; branch if transitioning waves lda launchingBaseABMs ; get number of ABMs left at missile base beq .abmLaunchExhausted ; branch if no more ABMs at missile base dec launchingBaseABMs ; reduce number of ABMs at missile base bne .setSoundEngineForLaunchingABM;branch if more ABMs at missile base jsr MoveABMsFromReserve .setSoundEngineForLaunchingABM lda soundEngineValues ; get sound engine values cmp #SOUND_ABM_EXPLODING bcs DetermineLaunchedABMTargetValues;branch if playing higher priority sound and #SOUND_ENGINE_LEFT_CHANNEL_MASK;keep left channel sound values ora #SOUND_ABM_LAUNCHING sta soundEngineValues ; set to play SOUND_ABM_LAUNCHING lda #10 sta abmLaunchingSoundIndex DetermineLaunchedABMTargetValues lda #XMID sec sbc targetControlHorizIntPos ; subtract Target Control position bcs .setTargetControlHorizDistance;branch if on left side of screen inc launchedABMHorizPositions,x eor #$FF ; get distance absolute value adc #1 .setTargetControlHorizDistance sta tmpTargetControlDeltaX ; set horizontal distance from center lda targetControlVertIntPos ; get Target Control vertical position sec sbc #INIT_ABM_VERT_POSITION cmp tmpTargetControlDeltaX bcc .greaterTargetHorizontalDistance;branch if horizontal distance greater sta tmpTargetContolMaxDistValue ; set from vertical distance from target lda #ABM_ACTIVE | ABM_VERT_DISTANCE_GREATER sta launchedABMAttributes,x jmp .setLaunchedABMTargetValues .greaterTargetHorizontalDistance ldy tmpTargetControlDeltaX ; get horizontal distance from center sta tmpTargetContolMinDistValue ; set from vertical distance from target sty tmpTargetContolMaxDistValue lda #ABM_ACTIVE | ABM_HORIZ_DISTANCE_GREATER sta launchedABMAttributes,x .setLaunchedABMTargetValues lda tmpTargetContolMaxDistValue sta launchedABMGreatestDistance,x sta abmOriginToTargetDiffChange,x lda tmpTargetContolMinDistValue sta abmDistanceChangeValues,x lda #0 sta abmOriginToTargetDifference,x;reset origin to target points difference DetermineABMMovementValue lda SWCHB ; read console switches asl ; shift player 2 difficulty setting to carry ldx currentPlayerNumber ; get the current player number bne .determineABMMovement ; branch if player 2 is active asl ; shift player 1 difficulty setting to carry .determineABMMovement ldy #ABM_AMATEUR_MOVEMENT_VALUE ; assume difficulty setting set to AMATEUR bcc .setABMMovementValue dey ; reduce movement if set to EXPERT .setABMMovementValue sty tmpABMPositionIncrement ldx #MAX_LAUNCHED_ABM - 1 .moveActiveABMLoop lda launchedABMAttributes,x ; get launched ABM attribute bmi MoveActiveABM ; branch if ABM_ACTIVE .jmpToNextLaunchedABM jmp .nextLaunchedABM MoveActiveABM and #ABM_EXPLODING_FLAG ; keep ABM_EXPLODING value bne .jmpToNextLaunchedABM ; branch if ABM exploding lda abmOriginToTargetDifference,x; get origin to target point difference clc adc abmDistanceChangeValues,x ; increment by distance change value sta abmOriginToTargetDifference,x; set new origin to target difference cmp abmOriginToTargetDiffChange,x bcc .moveActiveABM sbc abmOriginToTargetDiffChange,x sta abmOriginToTargetDifference,x; restore origin to target point difference lda launchedABMAttributes,x ; get launched ABM attribute and #ABM_DISTANCE_MASK ; keep ABM_DISTANCE value bne .doubleChangeX ; branch if ABM_VERT_DISTANCE_GREATER .doubleChangeY lda launchedABMVertPositions,x ; get the launched ABM vertical position clc adc tmpABMPositionIncrement ; move launched ABM up the screen sta launchedABMVertPositions,x jmp .moveActiveABM .doubleChangeX lda #XMID ; get horizontal mid value cmp launchedABMHorizPositions,x ; compare with ABM horizontal position bcs .doubleChangeXLeft ; branch if ABM on left side of screen lda launchedABMHorizPositions,x ; get launched ABM horizontal position clc adc tmpABMPositionIncrement ; increment ABM horizontal position sta launchedABMHorizPositions,x jmp .moveActiveABM .doubleChangeXLeft lda launchedABMHorizPositions,x ; get launched ABM horizontal position sec sbc tmpABMPositionIncrement ; subtract ABM horizontal position sta launchedABMHorizPositions,x .moveActiveABM lda launchedABMAttributes,x ; get launched ABM attribute and #ABM_DISTANCE_MASK ; keep ABM_DISTANCE value beq .moveActiveABMHorizontally ; branch if ABM_HORIZ_DISTANCE_GREATER lda launchedABMVertPositions,x ; get launched ABM vertical position clc adc tmpABMPositionIncrement ; move launched ABM up the screen sta launchedABMVertPositions,x jmp .checkABMReachingTargetPosition .moveActiveABMHorizontally lda #XMID ; get horizontal mid value cmp launchedABMHorizPositions,x ; compare with ABM horizontal position bcs .abmTravelingLeft ; branch if ABM on left side of screen lda launchedABMHorizPositions,x ; get launched ABM horizontal position clc adc tmpABMPositionIncrement ; increment ABM horizontal position sta launchedABMHorizPositions,x jmp .checkABMReachingTargetPosition .abmTravelingLeft lda launchedABMHorizPositions,x ; get launched ABM horizontal position sec sbc tmpABMPositionIncrement ; subtract ABM horizontal position sta launchedABMHorizPositions,x .checkABMReachingTargetPosition lda launchedABMGreatestDistance,x sec sbc tmpABMPositionIncrement sta launchedABMGreatestDistance,x bcc .setABMAttributeToExploding bne .nextLaunchedABM .setABMAttributeToExploding lda #ABM_EXPLODING_FLAG ora launchedABMAttributes,x sta launchedABMAttributes,x ; show launched ABM as exploding ldy #0 sty explodingABMAnimationIndexes,x;set to start of exploding animation lda launchedABMHorizPositions,x ; get launched ABM horizontal position sec sbc #4 sta explodingABMHorizPos,x ; set exploding ABM horizontal position lda launchedABMVertPositions,x ; get launched ABM vertical position sbc #4 sta explodingABMVertPos,x ; set exploding ABM vertical position .nextLaunchedABM dex bmi .doneMoveActiveABM jmp .moveActiveABMLoop .doneMoveActiveABM ldx launchedABMIndex ; get current launched ABM index ldy #H_MISSILE_KERNEL lda launchedABMHorizPositions,x ; get launched ABM horizontal position sta activeABMHorizPos lda launchedABMAttributes,x ; get launched ABM attribute and #ABM_EXPLODING_FLAG ; keep ABM_EXPLODING value bne .setActiveABMVerticalPosition; branch if ABM exploding ldy launchedABMVertPositions,x ; get launched ABM vertical position .setActiveABMVerticalPosition sty activeABMVertPos lda waveTransitionTimerValue ; get wave transition timer value bpl ReadJoystickValues ; branch if not transitioning waves cmp #WAVE_TRANSITION_INIT_TARGET_CONTROL bcc ReadJoystickValues ; branch allowed to move Target Control jmp .setTargetControlDisplayPositions ReadJoystickValues lda SWCHA ; get the joystick values ldy currentPlayerNumber ; get the current player number beq .setCurrentJoystickValue ; branch if player 1 is active dey ; reduce so y = 0 asl ; shift player 2's joystick value to MSB asl asl asl .setCurrentJoystickValue sta tmpJoystickValue bit gameOptions bvc .determineTargetControlSpeed ; branch if not CHILD_GAME ldy #4 ; target control speed index for CHILD_GAME bne .moveTargetControl ; unconditional branch .determineTargetControlSpeed lda gameVariation ; get the current game variation lsr ; shift TARGET_CONTROL_MASK to carry bcc .moveTargetControl ; branch if SLOW_TARGET_CONTROL ldy #2 .moveTargetControl lda targetControlHorizFracPos ; get Target Control horizontal fraction rol tmpJoystickValue ; shift MOVE_RIGHT value to carry bcs .checkToMoveTargetLeft ; branch if joystick not moved right adc TargetControlFractionalDelayValues,y sta targetControlHorizFracPos ; set new fraction value lda targetControlHorizIntPos ; get Target Control horizontal integer adc TargetControlFractionalDelayValues + 1,y sta targetControlHorizIntPos ; set new integer value .checkToMoveTargetLeft rol tmpJoystickValue ; shift MOVE_LEFT value to carry bcs .checkToMoveTargetDown ; branch if joystick not moved left sec lda targetControlHorizFracPos sbc TargetControlFractionalDelayValues,y sta targetControlHorizFracPos lda targetControlHorizIntPos sbc TargetControlFractionalDelayValues + 1,y sta targetControlHorizIntPos .checkToMoveTargetDown lda targetControlVertFracPos rol tmpJoystickValue ; shift MOVE_DOWN value to carry bcs .checkToMoveTargetUp ; branch if joystick not moved down sec sbc TargetControlFractionalDelayValues,y sta targetControlVertFracPos lda targetControlVertIntPos sbc TargetControlFractionalDelayValues + 1,y sta targetControlVertIntPos .checkToMoveTargetUp rol tmpJoystickValue ; shift MOVE_UP value to carry bcs .checkTargetControlPositionBoundaries;branch if joystick not moved up adc TargetControlFractionalDelayValues,y sta targetControlVertFracPos lda targetControlVertIntPos adc TargetControlFractionalDelayValues + 1,y sta targetControlVertIntPos .checkTargetControlPositionBoundaries ldy #XMAX_TARGET_CONTROL ; get Target Control horizontal maximum cpy targetControlHorizIntPos bcs .checkTargetControlHorizontalMinimum;branch if within horizontal maximum sty targetControlHorizIntPos ; set horizontal position to maximum .checkTargetControlHorizontalMinimum ldy #XMIN_TARGET_CONTROL ; get Target Control horizontal minimum cpy targetControlHorizIntPos bcc .checkTargetControlVerticalMinimum;branch if within horizontal minimum sty targetControlHorizIntPos ; set horizontal position to minimum .checkTargetControlVerticalMinimum ldy #YMIN_TARGET_CONTROL ; get Target Control vertical minimum cpy targetControlVertIntPos bcc .checkTargetControlVerticalMaximum;branch if within vertical minimum sty targetControlVertIntPos ; set vertical position to minimum .checkTargetControlVerticalMaximum ldy #YMAX_TARGET_CONTROL ; get Target Control vertical maximum cpy targetControlVertIntPos bcs .setTargetControlDisplayPositions;branch if within vertical maximum sty targetControlVertIntPos ; set vertical position to maximum .setTargetControlDisplayPositions lda targetControlHorizIntPos sta targetControlHorizPos lda targetControlVertIntPos sta targetControlScanline ldy alternateIBMNUSIZValue lda alternativeIBMValues ; get alternate IBM value and #ENEMY_MISSILE_TYPE_MASK ; keep ENEMY_MISSILE_TYPE value beq .alternateEnemyMissileValuesForDisplay;branch if IBM tya ; move alternate IBM NUSIZ value ora #MSBL_SIZE2 tay .alternateEnemyMissileValuesForDisplay lda currentIBMNUSIZValue sty currentIBMNUSIZValue sta alternateIBMNUSIZValue lda currentIBMVertPos ldy alternateIBMVertPos sty currentIBMVertPos sta alternateIBMVertPos lda currentIBMValues ; get current IBM value ldy alternativeIBMValues ; get alternate IBM value sty currentIBMValues ; set current IBM value from alternate sta alternativeIBMValues ; set alternate IBM value from current lda currentIBMSlopeFraction ldy alternativeIBMSlopeFraction sty currentIBMSlopeFraction sta alternativeIBMSlopeFraction lda alternativeIBMHorizPos sta tmpActiveIBMHorizPos ldy currentIBMHorizPos sty alternativeIBMHorizPos sta currentIBMHorizPos lda currentIBMNUSIZValue ; get current IBM NUSIZ value cmp #<-1 bne MoveEnemyMissileVertically ; branch if enemy missile active ldx #10 .determineToTargetRandomCity lda cityGraphicPointers,x ; get City graphic pointer LSB value cmp #ABMExplosionSprites sta activeExplosionGraphicPointer + 1;set active exploding ABM graphic MSB lda explodingABMState ; get exploding ABM state and #~ACTIVE_EXPLOSION_SIZE_MASK ; clear ACTIVE_EXPLOSION_SIZE value sta explodingABMState ldy #MSBL_SIZE2 | ONE_COPY lda #~(H_SMALL_EXPLOSION - 1) cpx #4 bcc .setActiveExplosionKernelValues cpx #12 bcs .setActiveExplosionKernelValues lda explodingABMState ; get exploding ABM state ora #ACTIVE_LARGE_EXPLOSION ; combine with ACTIVE_LARGE_EXPLOSION sta explodingABMState lda #~(H_LARGE_EXPLOSION - 1) ldy #MSBL_SIZE2 | DOUBLE_SIZE .setActiveExplosionKernelValues sta activeExplosionHeight sty activeABMNUSIZValue ldx launchedABMIndex ; get current launched ABM index lda explodingABMVertPos,x ; get exploding ABM vertical position bit explodingABMState ; check exploding ABM state bpl .setActiveExplosionVerticalPosition;branch if ACTIVE_SMALL_EXPLOSION clc adc #<-4 .setActiveExplosionVerticalPosition sta tmpActiveExplosionVertPos lda frameCount ; get current frame count and #7 bne AnimateCityExplosions ; ABM explosion animated every 8 frames ldx #MAX_LAUNCHED_ABM - 1 .animateABMExplosions inc explodingABMAnimationIndexes,x;increment ABM explosion animation index lda explodingABMAnimationIndexes,x;get ABM explosion animation index value cmp #16 bne .animateNextABMExplosion lda #0 sta launchedABMAttributes,x lda #XMID sta launchedABMHorizPositions,x lda #INIT_ABM_VERT_POSITION sta launchedABMVertPositions,x .animateNextABMExplosion dex bpl .animateABMExplosions AnimateCityExplosions ldx #10 .animateCityExplosions lda cityGraphicPointers,x ; get City graphic pointer LSB value cmp #MissileGraphics ; 2 sta missileGraphicsPointer + 1;3 ldy #H_MISSILE_BASE - 1 ; 2 sta RESP1 ; 3 = @45 lda skyColor ; 3 sta COLUP1 ; 3 = @51 sta COLUPF ; 3 = @54 lda gameState ; 3 get current game state and #RESERVED_MISSILE_DUMP_MASK;2 keep used missile dump count tax ; 2 .drawReservedMissileDump sta WSYNC ;-------------------------------------- lda (missileGraphicsPointer),y;5 sta GRP1 ; 3 = @08 lda ReservedMissileDumpGraphicValues,x;4 sta PF0 ; 3 = @15 jsr ShiftMSBToLSB ; 6 sta PF0 ; 3 = @40 dey ; 2 bpl .drawReservedMissileDump;2³ sta WSYNC ;-------------------------------------- lda missileBaseColor ; 3 sta COLUBK ; 3 = @06 lda #OVERSCAN_TIME ; 2 sta TIM64T ; 4 iny ; 2 y = 0 sty GRP1 ; 3 = @17 lda currentIBMNUSIZValue ; get current IBM NUSIZ value cmp #<-1 bne DetermineIBMCollisions ; branch if enemy missile active bit waveTransitionTimerValue ; check wave transition timer value bmi DetermineIBMCollisions ; branch if transitioning waves jsr SpawnNextGroupOfMissiles bcc .nextFrame ; branch if missile spawned lda alternateIBMVertPos ; get alternate IBM vertical position cmp #H_MISSILE_KERNEL bne .nextFrame ; branch if alternate IBM present jsr SetCityArrayValues .nextFrame jmp .overscanWait DetermineIBMCollisions ldy currentIBMVertPos ; get current IBM vertical position bne DetermineIfIBMCollidedWithExplosion jsr DetermineIBMCollisionWithPlanetObjects jmp .overscanWait DetermineIfIBMCollidedWithExplosion and #7 ; keep object size value tax ; move object NUSIZ value to x register lda MaximumMissileCountValues,x ; get maximum missile count for NUSIZ value sta tmpMissileIndex ldy IBMHorizOffsetIndexValues,x lda IBMHorizOffsetPointerValues,y sta ibmHorizOffsetPointer lda IBMHorizOffsetPointerValues + 1,y sta ibmHorizOffsetPointer + 1 lda #H_MISSILE_KERNEL - 1 ; get IBM starting vertical position sec sbc currentIBMVertPos ; subtract current IBM vertical position sta tmpDeltaY lda currentIBMSlopeFraction sta tmpIBMSlopeFraction jsr DetermineIBMHorizontalAdjustmentValue .determineIfIBMCollidedWithExplosion ldy tmpMissileIndex lda currentIBMHorizPos ; get current IBM horizontal position clc adc (ibmHorizOffsetPointer),y ; increment by missile index offset ldy currentIBMVertPos ; get current IBM vertical position sty tmpEnemyMissileVertPos ldy currentIBMValues ; get current IBM value cpy #HMOVE_R1 | ENEMY_MISSILE_TYPE_IBM bcc .adjustLeftTravelingIBMHorizPosition;branch if traveling left clc adc tmpIBMHorizAdjustment ; adjustment for right traveling IBM jmp .determineABMAndIBMCollisionBoxArea .adjustLeftTravelingIBMHorizPosition sec sbc tmpIBMHorizAdjustment .determineABMAndIBMCollisionBoxArea sta tmpIBMHorizPosition ldx launchedABMIndex ; get current launched ABM index lda launchedABMAttributes,x ; get launched ABM attribute and #ABM_EXPLODING_FLAG ; keep ABM_EXPLODING value beq .doneCheckExplosionDestroyingEnemyMissile;branch if ABM not exploding ldy launchedABMVertPositions,x ; get launched ABM vertical position lda launchedABMHorizPositions,x ; get launched ABM horizontal position tax jsr ComputeABMAndIBMCollsionBoxArea sta tmpCollisionBoxArea ldx launchedABMIndex ; get current launched ABM index ldy explodingABMAnimationIndexes,x;get ABM explosion animation index value lda currentIBMValues ; get current IBM value and #ENEMY_MISSILE_TYPE_MASK ; keep ENEMY_MISSILE_TYPE value beq .checkMissileInExplosionArea ; branch if IBM bit gameVariation ; check current game variation bvc .checkMissileInExplosionArea ; branch if DUMB_CRUISE_MISSILES lda ExplosionBoundingBoxArea,y ; get explosion bounding box area cmp tmpCollisionBoxArea bcc .doneCheckExplosionDestroyingEnemyMissile lda tmpCollisionBoxArea ; get collision box area cmp #3 bcc ScorePointsForDestroyingEnemyMissile;branch if cruise missile within range lda frameCount ; get current frame count and #1 tax lda #2 sta cruiseMissileVerticalDelay,x ; set cruise missile vertical delay value bne .doneCheckExplosionDestroyingEnemyMissile;unconditional branch .byte $90, $07 ; two unreferenced bytes...never executed .checkMissileInExplosionArea lda ExplosionBoundingBoxArea,y ; get explosion bounding box area cmp tmpCollisionBoxArea bcs ScorePointsForDestroyingEnemyMissile .doneCheckExplosionDestroyingEnemyMissile jmp .checkNextIBMForExplosionCollision ScorePointsForDestroyingEnemyMissile ldy #>IBM_POINTS lda currentIBMValues ; get current IBM value and #ENEMY_MISSILE_TYPE_MASK ; keep ENEMY_MISSILE_TYPE value beq .scorePointsForDestroyingEnemyMissile;branch if IBM iny ; increment for CRUISE_MISSILE .scorePointsForDestroyingEnemyMissile lda #<(IBM_POINTS | ENEMY_CRUISE_MISSILE_POINTS) jsr IncrementScore lda soundEngineValues ; get sound engine values cmp #SOUND_WORLD_EXPLOSION bcs .advanceIBMPointer and #SOUND_ENGINE_LEFT_CHANNEL_MASK;keep left channel sound values ora #SOUND_ABM_EXPLODING sta soundEngineValues ; set to play SOUND_ABM_EXPLODING lda #0 sta explodingABMVolumeIndex .advanceIBMPointer lda ibmHorizOffsetPointer clc adc #13 sta ibmNUSIZPointer ldy tmpMissileIndex lda (ibmNUSIZPointer),y sta currentIBMNUSIZValue ; set current IBM NUSIZ value cmp #<-1 bne .prepareCheckNextIBMForExplosionCollision;branch if enemy missile active lda #H_MISSILE_KERNEL + 1 sta currentIBMVertPos bne .overscanWait ; unconditional branch .prepareCheckNextIBMForExplosionCollision lda ibmNUSIZPointer clc adc #13 sta ibmHorizPositionPointer lda currentIBMHorizPos clc adc (ibmHorizPositionPointer),y sta currentIBMHorizPos lda ibmHorizOffsetPointer ; get IBM offset pointer LSB value sec sbc #(13 * 2) ; subtract increment to restore value sta ibmHorizOffsetPointer .checkNextIBMForExplosionCollision dec tmpMissileIndex bmi .overscanWait jmp .determineIfIBMCollidedWithExplosion .overscanWait lda INTIM bne .overscanWait lda #DISABLE_TIA sta WSYNC ; wait for next scanline sta VBLANK ; disable TIA (D1 = 1) lda #BLACK sta COLUBK ; set background color to BLACK jmp MainLoop SpawnNextGroupOfMissiles ldy #HMOVE_0 | ENEMY_MISSILE_TYPE_IBM lda currentIBMValues ; get current IBM value and #ENEMY_MISSILE_TYPE_MASK ; keep ENEMY_MISSILE_TYPE value beq .determineTypeOfMissileToSpawn;branch if IBM lda alternativeIBMValues ; get alternate IBM value and #ENEMY_MISSILE_TYPE_MASK ; keep ENEMY_MISSILE_TYPE value bne .determineTypeOfMissileToSpawn;branch if alternate IBM is CRUISE_MISSILE lda soundEngineValues ; get sound engine values and #SOUND_ENGINE_RIGHT_CHANNEL_MASK;keep right channel sound values sta soundEngineValues .determineTypeOfMissileToSpawn sty currentIBMValues bit waveTransitionTimerValue ; check wave transition timer value bmi .setInitialIBMVerticalPosition;branch if transitioning waves lda remainingWaveIBMs ; get remaining wave IBMs bne .spawnEnemyMissileOrCruiseMissile;branch if IBMs remaining for wave lda remainingWaveCruiseMissiles ; get remaining wave cruise missiles bne .spawnEnemyCruiseMissile ; branch if cruise missiles remaining .setInitialIBMVerticalPosition lda #H_MISSILE_KERNEL sta currentIBMVertPos sec ; set to show no missile spawned rts .spawnEnemyCruiseMissile ldy #INIT_CRUISE_MISSILE_VOLUME sty enemyCruiseMissleVolume ldy #<-1 sty currentIBMValues ; set to show IBM not active lda frameCount ; get current frame count and #1 tax iny ; y = 0 sty cruiseMissileVerticalDelay,x dec remainingWaveCruiseMissiles ; decrement remaining cruise missile count lda soundEngineValues ; get sound engine values and #SOUND_ENGINE_RIGHT_CHANNEL_MASK;keep right channel sound values ora #SOUND_ENEMY_CRUISE_MISSILE sta soundEngineValues ; set to play SOUND_ENEMY_CRUISE_MISSILE bne .spawnEnemyMissiles ; unconditional branch .spawnEnemyMissileOrCruiseMissile lda random ; get current random number value and #$18 bne .spawnEnemyMissiles lda remainingWaveCruiseMissiles ; get remaining wave cruise missiles bne .spawnEnemyCruiseMissile ; branch if cruise missiles remaining .spawnEnemyMissiles ldy currentIBMSlopeFraction lda ObjectHorizontalPositionValues,y sta tmpEnemyTargetPosition bit currentIBMValues bmi .spawnSingleIBM ; branch if spawning ENEMY_MISSILE_TYPE_CRUISE lda remainingWaveIBMs ; get remaining wave IBMs cmp #4 bcs .determineSpawnedIBMNUSIZValue;branch if more than 3 IBMs remaining tay ; 1 <= y <= 3 lda IBMFinalGroupingNUSIZValues,y; get NUSIZ values for final IBM grouping jmp .setEnemyMissileNUSIZValue .determineSpawnedIBMNUSIZValue cpy #6 bcc DetermineSpawnedIBMNUSIZValue; branch if IBM targeting cities .spawnSingleIBM lda #MSBL_SIZE1 | ONE_COPY beq .setEnemyMissileNUSIZValue DetermineSpawnedIBMNUSIZValue lda random ; get current random number value lsr ; divide value by 8 lsr lsr and #7 ; 0 <= a <= 7 tay lda InitialIBMNUSIZValues,y .setEnemyMissileNUSIZValue tay ; move missile NUSIZ value to y register sta currentIBMNUSIZValue lda IBMGroupingFarthestOffsetValues,y sta tmpIBMFarthestOffsetValue lda random ; get current random number value cmp #XMAX + 1 bcc .checkIfFarthestGroupedIBMOutOfRange;branch if within horizontal range lsr ; divide value by 2 (i.e. 0 <= a <= 79) .checkIfFarthestGroupedIBMOutOfRange clc adc tmpIBMFarthestOffsetValue ; increment by farthest IBM offset value cmp #XMAX + 1 bcc .setSpawnedIBMHorizontalPosition;branch if within horizontal range lsr ; divide value by 2 (i.e. 0 <= a <= 79) .setSpawnedIBMHorizontalPosition sec sbc tmpIBMFarthestOffsetValue ; subtract farthest IBM offset value sta currentIBMHorizPos ; set IBM horizontal position cmp tmpEnemyTargetPosition bcs .setEnemyMissileToTravelLeft ; branch if starting right of target clc adc tmpIBMFarthestOffsetValue ; increment by farthest IBM offset value cmp tmpEnemyTargetPosition ; compare farthest IBM with target position bcc .farthestIBMLeftOfTarget ; branch if left of target lda tmpEnemyTargetPosition ; get target position value clc adc tmpIBMFarthestOffsetValue ; increment by farthest IBM offset value cmp #XMAX + 1 bcs .checkForFarthestIBMToTargetObject;branch if not within range lda tmpIBMFarthestOffsetValue ; get farthest IBM offset value lsr ; divide value by 2 clc adc currentIBMHorizPos ; increment by IBM horizontal position cmp tmpEnemyTargetPosition bcs .setEnemyMissileToTravelRight;branch if to the right of target .checkForFarthestIBMToTargetObject lda tmpEnemyTargetPosition ; get target position value cmp tmpIBMFarthestOffsetValue ; compare with farthest IBM offset value bcc .setEnemyMissileToTravelRight ldx #HMOVE_L1 .setFarthestIBMToTargetObject lda tmpIBMFarthestOffsetValue ; get farthest IBM offset value clc adc currentIBMHorizPos ; increment by IBM horizontal position bne .setEnemyMissileHorizontalDirectionValue;unconditional branch .setEnemyMissileToTravelRight ldx #HMOVE_R1 lda currentIBMHorizPos ; left most IBM to target object jmp .setEnemyMissileHorizontalDirectionValue .farthestIBMLeftOfTarget ldx #HMOVE_R1 bne .setFarthestIBMToTargetObject; unconditional branch .setEnemyMissileToTravelLeft ldx #HMOVE_L1 lda currentIBMHorizPos ; left most IBM to target object .setEnemyMissileHorizontalDirectionValue stx tmpIBMDirectionValue tax ; move IBM horizontal position to x register lda currentIBMValues ; get current IBM value and #ENEMY_MISSILE_TYPE_MASK ; keep ENEMY_MISSILE_TYPE value ora tmpIBMDirectionValue ; combine with IBM direction value sta currentIBMValues ; set new IBM value txa ; move IBM horizontal position to accumulator ldy #H_MISSILE_KERNEL - 1 sty currentIBMVertPos ; set IBM starting vertical position sec sbc tmpEnemyTargetPosition ; subtract to get horizontal distance bcs .determineSpawnedIBMSlope eor #$FF adc #1 .determineSpawnedIBMSlope sty tmpCurrentIBMVertPos jsr DetermineIBMTargetEndPointDifference stx tmpIBMHorizAdjustment ldx #0 sta tmpIBMDeltaX jsr DetermineSpawnedIBMSlope stx tmpSpawnedIBMSlopeFraction jmp .setSpawnedMissileGroupValues DetermineIBMTargetEndPointDifference sta tmpIBMDeltaX lda #0 DetermineSpawnedIBMSlope ldy #7 .determineIBMHorizDistFromTarget rol tmpIBMDeltaX ; multiply by 2 (carry set if > 127) rol ; 2x + C bcs .addInError cmp tmpCurrentIBMVertPos bcc .nextIteration ; branch if not reached vertical target sbc tmpCurrentIBMVertPos ; subtract vertical target position .nextIteration dey bpl .determineIBMHorizDistFromTarget rol tmpIBMDeltaX ; multiply by 2 plus carry ldx tmpIBMDeltaX rts .addInError sbc tmpCurrentIBMVertPos ; subtract vertical target position sec ; set to increment horiz distance by 1 bcs .nextIteration ; unconditional branch .setSpawnedMissileGroupValues lda tmpSpawnedIBMSlopeFraction sta currentIBMSlopeFraction lda currentIBMValues ; get current IBM value and #ENEMY_MISSILE_TYPE_MASK ; keep ENEMY_MISSILE_TYPE value bne .doneSpawnNextGroupOfMissiles; branch if CRUISE_MISSILE ldy currentIBMNUSIZValue ; get current IBM NUSIZ value lda remainingWaveIBMs ; get remaining wave IBMs clc ; clear carry sbc MaximumMissileCountValues,y ; subtract missile count for size plus one sta remainingWaveIBMs ; set remaining wave IBMs .doneSpawnNextGroupOfMissiles clc rts DetermineIBMCollisionWithPlanetObjects lda currentIBMNUSIZValue ; get current IBM NUSIZ value and #7 ; keep object size value tay lda MaximumMissileCountValues,y ; get maximum missile count for NUSIZ value sta tmpMaximumMissileCount lda IBMHorizOffsetIndexValues,y tay lda IBMHorizOffsetPointerValues,y sta ibmHorizOffsetPointer lda IBMHorizOffsetPointerValues + 1,y sta ibmHorizOffsetPointer + 1 lda #H_MISSILE_KERNEL - 1 ; get IBM starting vertical position sta tmpDeltaY lda currentIBMSlopeFraction sta tmpIBMSlopeFraction jsr DetermineIBMHorizontalAdjustmentValue .determineIfIBMCollidedWithPlanetObjects ldy tmpMaximumMissileCount lda currentIBMHorizPos clc adc (ibmHorizOffsetPointer),y ldx currentIBMValues ; get current IBM value cpx #HMOVE_R1 | ENEMY_MISSILE_TYPE_IBM bcs .adjustRightTravelingIBMHorizPosition;branch if traveling right sec sbc tmpIBMHorizAdjustment ; adjustment for left traveling IBM jmp .determinePlanetObjectAndIBMCollisionBoxArea .adjustRightTravelingIBMHorizPosition clc adc tmpIBMHorizAdjustment .determinePlanetObjectAndIBMCollisionBoxArea sta tmpIBMPlanetCollisionHorizPosition ldy #0 .determineEnemyMissileObjectCollision lda ObjectHorizontalPositionValues,y;get object horizontal position sec sbc #4 cmp tmpIBMPlanetCollisionHorizPosition bcs .checkNextObjectCollision ; branch if IBM left of Planet object adc #8 cmp tmpIBMPlanetCollisionHorizPosition bcs .enemyMissileCollidedWithTarget;branch if IBM hit Planet object .checkNextObjectCollision iny cpy #7 bne .determineEnemyMissileObjectCollision jmp .checkNextMissile .enemyMissileCollidedWithTarget cpy #6 bne .enemyDestroyedCity ; branch if not collided with Missile Base lda #32 sta abmMoveFromReserveDelay ; set delay to move ABMs from reserve lda soundEngineValues ; get sound engine values and #SOUND_ENGINE_LEFT_CHANNEL_MASK;keep left channel sound values ora #SOUND_WORLD_EXPLOSION sta soundEngineValues ; set to play SOUND_WORLD_EXPLOSION lda #80 sta explosionAudioValues lda #0 sta launchingBaseABMs ; clear number of ABMs left at launch base beq .checkNextMissile ; unconditional branch .enemyDestroyedCity tya ; move city position index to accumulator asl ; multiply value by 2 tax ; set to read city graphic data ldy CityExplosionSpritePointerValues_2;get CityExplosion_2 LSB value lda cityGraphicPointers,x ; get city graphic pointer LSB value cmp DestroyedCityPointer beq .setDestroyedCityLSBValue ; branch if a destroyed city sprite ldy CityExplosionSpritePointerValues_0; get CityExplosion_0 LSB value lda soundEngineValues ; get sound engine values and #SOUND_ENGINE_LEFT_CHANNEL_MASK;keep left channel sound values ora #SOUND_CITY_EXPLOSION sta soundEngineValues ; set to play SOUND_CITY_EXPLOSION lda #80 sta explosionAudioValues .setDestroyedCityLSBValue sty cityGraphicPointers,x .checkNextMissile dec tmpMaximumMissileCount bpl .determineIfIBMCollidedWithPlanetObjects lda #<-1 sta currentIBMNUSIZValue lda #H_MISSILE_KERNEL + 1 sta currentIBMVertPos rts ComputeABMAndIBMCollsionBoxArea txa ; move ABM horizontal position to accumulator sec sbc tmpIBMHorizPosition ; subtract IBM horizontal position bcs .setIBMAndABMHorizDistance eor #$FF ; get horizontal distance absolute value adc #1 .setIBMAndABMHorizDistance sta tmpDeltaX tya ; move ABM vertical position to accumulator sec sbc tmpEnemyMissileVertPos ; subtract enemy missile vertical position bcs .determineSmallestDistance eor #$FF ; get vertical distance absolute value adc #1 .determineSmallestDistance cmp tmpDeltaX bcc .computeABMAndIBMCollisionBoxArea;branch if horizontal distance greater ldx tmpDeltaX ; move horizontal distance to x register sta tmpGreatestDistance txa ; move horizontal distance to accumulator .computeABMAndIBMCollisionBoxArea lsr ; divide smallest distance by 4 lsr sta tmpDiv4 asl ; multiply value by 2 clc adc tmpDiv4 ; multiply by 3 [i.e. x * 3 = (x * 2) + x] lsr ; divide value by 2 [i.e. 3x / 8] clc adc tmpGreatestDistance ; increment by greatest distance rts DetermineIBMHorizontalAdjustmentValue SUBROUTINE lda #0 sta tmpIBMHorizAdjustment ; clear horizontal adjustment value ldx #8 .determineIBMHorizontalAdjustmentValue asl ; shift D7 to carry rol tmpIBMHorizAdjustment ; shift carry into D0 asl tmpDeltaY ; multiply vertical delta by 2 bcc .nextIteration clc adc tmpIBMSlopeFraction bcc .nextIteration ; branch if no difference overflow inc tmpIBMHorizAdjustment .nextIteration dex bne .determineIBMHorizontalAdjustmentValue sta tmpSpawnedIBMSlopeFraction ; not used rts NextRandom asl random ; multiply random seed by 2 rol random + 1 ; shift random seed bits bpl .checkRandomSeedD1Bit inc random ; increment random seed value .checkRandomSeedD1Bit lda random ; get current random number value bit RandomBitTapValue beq .checkToReseedRandom ; branch if D1 is low eor #1 ; flip random seed D0 value sta random .checkToReseedRandom ora random + 1 ; combine with random high bne .doneNextRandom ; branch if either value not 0 inc random ; increment random seed (i.e. value now 1) .doneNextRandom lda random ; get current random number value rts RandomBitTapValue .byte 2 MoveABMsFromReserve ldy #1 ; initial missile reserve count lda gameState ; get current game state and #RESERVED_MISSILE_DUMP_MASK ; keep used missile dump count beq .moveABMsFromReserve ; branch if no missile reserves used cmp #1 beq .moveLastMissileReserve ; branch if one missile reserve used lda #0 sta launchingBaseABMs ; clear number of ABMs left at missile base rts .moveLastMissileReserve iny ; increment missile reserve count .moveABMsFromReserve tya ; move missile reserve count to accumulator ora gameState ; combine with game state sta gameState ; set number of missile reserves used lda #INIT_NUMBER_LAUNCH_ABM sta launchingBaseABMs ; set initial number of ABMs at missile base rts IncrementScore sta tmpPointsOnesValue ldx #5 ; assume score multiplier of 5 lda waveNumber ; get current wave number cmp #12 bcs .setScoreMultiplier ; branch if greater than 11 lsr ; divide wave number by 2 tax ; set score multiplier .setScoreMultiplier stx tmpScoreMultiplier .incrementScore ldx currentPlayerNumber ; get the current player number lda tmpPointsOnesValue ; get points ones value sed clc adc playerScore,x ; increment score ones value sta playerScore,x tya ; move points hundreds value to accumulator adc playerScore + 2,x ; increment score hundreds value sta playerScore + 2,x lda #1 - 1 adc playerScore + 4,x ; increment score ten thousands value sta playerScore + 4,x cld dec tmpScoreMultiplier bpl .incrementScore rts SetCityArrayValues ldx #0 txa .determineCityArrayValues ldy cityGraphicPointers,x ; get city graphic pointer LSB value sec ; set carry to assume city present cpy #