.irqh1 JMP (irq1Address) \ Jump to the original address from IRQ1V to pass \ control to the next interrupt handler .IRQHandler SEI \ Disable interrupts so the following process isn't \ interrupted by another interrupt LDA SHEILA+&6D \ Set A to the 6522 User VIA interrupt flag register IFR \ (SHEILA &6D) AND #%01000000 \ If bit 6 of the IFR is clear then this interrupt has BEQ irqh1 \ not been triggered by the 6522 User VIA timer 1 \ reaching zero, so jump to irqh1 to pass the interrupt \ on to the next interrupt handler \ If we get here then bit 6 of the IFR is set, so this \ interrupt has been triggered by the 6522 User VIA \ timer 1 reaching zero STA SHEILA+&6D \ The AND instruction above leaves A containing bit 6 \ set and all other bits clear, so we can write this to \ the IFR at SHEILA &6D to clear the timer 1 interrupt LDA &FC \ Set A to the interrupt accumulator save register, \ which restores A to the value it had on entering the \ interrupt PHA \ Store A, X and Y on the stack so we can preserve them TXA \ across calls to the interrupt handler PHA TYA PHA CLD \ Clear the D flag to switch arithmetic to normal, in \ case the interrupt was triggered during any BCD \ calculations DEC soundCounter \ Decrement the sound counter so we can detect when the BPL irqh2 \ current sound effect has finished, making sure it INC soundCounter \ doesn't go below zero .irqh2 LDA gameInProgress \ If bit 7 of gameInProgress is set then a game is not BMI irqh8 \ currently in progress and we are in the title and \ preview screens, so jump to irqh8 to skip the \ following and return from the interrupt handler \ without updating the game state LDA sentinelHasWon \ If bit 7 of sentinelHasWon is set then the Sentinel BMI irqh7 \ has won the game, so jump to irqh7 to draw black dots \ on the game over screen (when it is showing) LDA gamePaused \ If bit 7 of gamePaused is set then the game is paused, BMI irqh5 \ so jump to irqh5 to scan for the unpause and volume \ keys and return from the interrupt handler LDA scrollCounter \ If scrollCounter = 0 then we do not need to scroll the BEQ irqh3 \ player's landscape view, so jump to irqh3 to skip the \ scrolling process \ If we get here then we still have scrollCounter steps \ to complete in the current scrolling process, so we \ now do one scrolling step (ScrollPlayerView decrements \ the counter in scrollCounter) JSR ScrollPlayerView \ Scroll the screen and copy data from the view screen \ buffer into screen memory to implement the player's \ scrolling landscape view JSR ShowIconBuffer \ Display the contents of the icon screen buffer by \ copying it into screen memory, as the scrolling \ process will have scrolled this part of the screen, so \ we need to redraw it to prevent the energy icons and \ scanner from moving .irqh3 LDA activateSentinel \ If bit 7 of activateSentinel is set then the Sentinel BMI irqh4 \ has not yet been activated at the start of the game, \ so jump to irqh4 to skip the following JSR UpdateEnemyTimers \ Update the timers that control the enemy tactics JSR UpdateScanner \ Update the scanner, if required .irqh4 LDA focusOnKeyAction \ If bit 7 of focusOnKeyAction is set then the game is BMI irqh8 \ currently focusing effort on implementing a key action \ such as a landscape pan, so jump to irqh8 to skip the \ following keyboard scan JSR CheckForKeyPresses \ Check for various game key presses and update the key \ logger and relevant variables JMP irqh8 \ Jump to irqh8 to return from the interrupt handler .irqh5 \ If we get here then the game is paused, so we scan for \ key presses and discard them all except for the \ unpause and volume keys LDY #13 \ Scan the keyboard for all game keys in the gameKeys JSR ScanForGameKeys \ table except for the last one ("U" for U-turn) \ We now reset the first three entries in the key \ logger (i.e. entries 0 to 2), leaving the last entry \ populated (i.e. entry 3, which records the volume, \ pause and unpause key presses) LDX #2 \ Set a loop counter in X for resetting three entries LDA #%10000000 \ Set A = %10000000 to reset the three entries, as the \ set bit 7 indicates an empty entry in the logger .irqh6 STA keyLogger,X \ Reset the X-th entry in the key logger DEX \ Decrement the loop counter BPL irqh6 \ Loop back until we have reset all four entries JMP irqh8 \ Jump to irqh8 to return from the interrupt handler .irqh7 \ If we get here then the Sentinel has won the game, so \ we draw black dots on the game over screen (when it is \ showing) LDA drawLandscape \ If bit 7 of drawLandscape is clear, skip the following BPL irqh8 \ to return from the interrupt handler \ If bit 7 of drawLandscape is set then we are showing \ the game over screen, which combines dithering of an \ object to the screen while at the same time fading the \ screen to black, to create the hypnotic effect of the \ winning entity fading in and out of the screen as the \ game ends JSR DrawBlackDots \ Draw 80 randomly positioned dots on the screen in \ colour 1 (black) to fade the screen to black in a \ slowly decaying manner .irqh8 PLA \ Restore A, X and Y from the stack TAY PLA TAX PLA RTI \ Return from the interrupt handlerName: IRQHandler [Show more] Type: Subroutine Category: Main game loop Summary: The main interrupt handler, which gets run 50 times a second to update the game state and check for game key pressesContext: See this subroutine in context in the source code References: This subroutine is called as follows: * ConfigureMachine calls IRQHandler
This routine is called exactly 50 times a second, and it does the following: * Count down the sound timer * If a game is not in progress (i.e. we are in the title screen rather than playing a landscape), return from the handler * If the Sentinel has won and bit 7 of drawLandscape is set, then call DrawBlackDots to draw 80 random black dots for the game over screen, and return from the handler * If the game is paused, scan for the pause and volume keys and return from the handler * If a game is in progress and the Sentinel has not won and the game is not paused, then: * If scrollCounter is non-zero then call ScrollPlayerView (which decrements scrollCounter) and ShowIconBuffer * If the Sentinel has been activated, call UpdateEnemyTimers and UpdateScanner * If bit 7 of focusOnKeyAction is clear then the game is not currently focusing effort on implementing a key action such as a landscape pan, so scan for and process all the game keys
[X]
Subroutine CheckForKeyPresses (category: Keyboard)
Check for various game key presses and update the key logger and relevant variables (during the interrupt handler)
[X]
Subroutine DrawBlackDots (category: Graphics)
Draw 80 randomly positioned dots on the screen in colour 1 (black)
[X]
Configuration variable SHEILA = &FE00
Memory-mapped space for accessing internal hardware, such as the video ULA, 6845 CRTC and 6522 VIAs (also known as SHEILA)
[X]
Subroutine ScanForGameKeys (category: Keyboard)
Scan for game key presses and update the key logger
[X]
Subroutine ScrollPlayerView (category: Graphics)
Scroll the screen and copy data from the screen buffer into screen memory to implement the player's scrolling landscape view
[X]
Subroutine ShowIconBuffer (category: Screen buffer)
Display the redrawn icon and scanner row by copying the contents of the icon screen buffer into screen memory
[X]
Subroutine UpdateEnemyTimers (category: Gameplay)
Update the timers that control the enemy tactics
[X]
Subroutine UpdateScanner (category: Scanner/energy row)
Update the scanner, if required
[X]
Variable activateSentinel in workspace Main variable workspace
A flag to record when the Sentinel is activated at the start of the game (by the player pressing a key that expends or absorbs energy
[X]
Variable drawLandscape in workspace Main variable workspace
Configures whether to draw the landscape behind the object in the DrawUpdatedObject routine
[X]
Variable focusOnKeyAction in workspace Main variable workspace
A flag that determines whether the game should focus effort on implementing a key action, such as a pan of the landscape view
[X]
Variable gameInProgress in workspace Main variable workspace
Flags whether or not a game is in progress (i.e. the player is playing a landscape rather than interacting with the title and preview screens)
[X]
Variable gamePaused in workspace Main variable workspace
A flag to record whether the game is paused
[X]
Variable irq1Address (category: Main game loop)
Stores the previous value of IRQ1V before we install our custom IRQ handler
[X]
Label irqh1 is local to this routine
[X]
Label irqh2 is local to this routine
[X]
Label irqh3 is local to this routine
[X]
Label irqh4 is local to this routine
[X]
Label irqh5 is local to this routine
[X]
Label irqh6 is local to this routine
[X]
Label irqh7 is local to this routine
[X]
Label irqh8 is local to this routine
[X]
Variable keyLogger in workspace Main variable workspace
The four-byte key logger for logging game key presses
[X]
Variable scrollCounter in workspace Main variable workspace
A counter for the number of columns or rows we still need to scroll in the player's scrolling landscape view when the player pans
[X]
Variable sentinelHasWon in workspace Main variable workspace
A flag to record when the player runs out of energy (i.e. the energy level goes negative), at which point the Sentinel wins
[X]
Variable soundCounter in workspace Main variable workspace
A counter for the sound currently being made, which counts down in the IRQHandler routine at a rate of 50 times a second