Add Galaxian, no NMI yet
authorNick Downing <nick@ndcode.org>
Sun, 3 Jul 2022 09:30:57 +0000 (19:30 +1000)
committerNick Downing <nick@ndcode.org>
Sun, 3 Jul 2022 09:30:57 +0000 (19:30 +1000)
.gitignore
doc/galaxian.asm [new file with mode: 0644]
emu_z80/emu_z80.c
emu_z80/test.asm
galaxian/Makefile [new file with mode: 0644]
orig/Makefile

index 5a49f2f..d6792a8 100644 (file)
 /emu_z80/cg_default/*.png
 /emu_z80/cg_default/*.ppm
 /emu_z80/emu_z80
+/galaxian/1h
+/galaxian/1k
+/galaxian/7l
+/galaxian/galaxian.clr
+/galaxian/galmidw.u
+/galaxian/galmidw.v
+/galaxian/galmidw.w
+/galaxian/galmidw.y
 /pacman/82s123.7f
 /pacman/82s126.1m
 /pacman/82s126.3m
@@ -21,4 +29,6 @@
 /pacman/pacman.6f
 /pacman/pacman.6h
 /pacman/pacman.6j
+/orig/galaxian.zip
+/orig/mspacman.zip
 /orig/pacman.zip
diff --git a/doc/galaxian.asm b/doc/galaxian.asm
new file mode 100644 (file)
index 0000000..b793dde
--- /dev/null
@@ -0,0 +1,8327 @@
+;\r
+; Galaxian (C) 1979 NAMCO.\r
+;\r
+; Reverse engineering work by Scott Tunstall, Paisley, Scotland. \r
+; Tools used: MAME debugger & Visual Studio Code text editor.\r
+; Date: 7 July 2019.  \r
+; \r
+; Please send any questions, corrections and updates to scott.tunstall@ntlworld.com\r
+;\r
+; Be sure to check out my reverse engineering work for Robotron 2084 and Scramble too, \r
+; at http://seanriddle.com/robomame.asm and http://seanriddle.com/scramble.asm asm respectively.\r
+;\r
+; Finally:\r
+; If you'd like to show appreciation for this work by buying me a coffee, feel free: https://ko-fi.com/scotttunstall\r
+; I'd be equally happy if you donated to Parkinsons UK or Chest Heart And Stroke (CHAS) Scotland.\r
+; Thanks.  \r
+\r
+/*\r
+Conventions: \r
+\r
+NUMBERS\r
+=======\r
+\r
+The term "@ $" means "at memory address in hexadecimal". \r
+e.g. @ $1234 means "refer to memory address 1234" or "program code @ memory location 1234" \r
+\r
+The term "#$" means "immediate value in hexadecimal". It's a habit I have kept from 6502 days.\r
+e.g. #$60 means "immediate value of 60 hex" (96 decimal)\r
+\r
+If I don't prefix a number with $ or #$ in my comments, treat the value as a decimal number.\r
+\r
+\r
+LABELS\r
+======\r
+I have a labelling convention in place to help you identify the important parts of the code quicker.\r
+Any subroutine labelled with the SCRIPT_ , DISPLAY_ or HANDLE_ prefix are critical "top-level" functions responsible \r
+for calling a series of "lower-level" functions to achieve a given result.   \r
+\r
+If this helps you any, think of the "top level" as the main entry point to code that achieves a specific purpose.  \r
+\r
+Routines prefixed HANDLE_ manage a particular aspect of the game.\r
+    For example, HANDLE_PLAYER_MOVE is the core routine for reading the player joystick and moving the player ship. \r
+    HANDLE_PLAYER_SHOOT is the core routine for reading the player fire button and spawning a bullet.\r
+\r
+I expect the purpose of DISPLAY_ is obvious.\r
+\r
+SCRIPTS are documented below - see docs for SCRIPT_NUMBER ($4005)\r
+\r
+\r
+ARRAYS, LISTS, TABLES\r
+=====================\r
+\r
+The terms "entry", "slot", "item", "record" when used in an array, list or table context all mean the same thing.\r
+I try to be consistent with my terminology but obviously with a task this size that might not be the case.\r
+\r
+Unless I specify otherwise, I all indexes into arrays/lists/tables are zero-based, \r
+meaning element [0] is the first element, [1] the second, [2] the third and so on.\r
+\r
+FLAGS\r
+=====\r
+The terms "Clear", "Reset", "Unset" in a flag context all mean the flag is set to zero.\r
+                                                                               \r
+\r
+COORDINATES\r
+===========\r
+\r
+X,Y refer to the X and Y axis in a 2D coordinate system, where X is horizontal and Y is vertical.\r
+\r
+The Galaxian monitor is rotated 90 degrees. This means that:\r
+a) updating the hardware Y position of a sprite presents itself to the player as changing the horizontal position.\r
+   To make a sprite appear to move left, you would increment its Y position.\r
+   To make a sprite appear to move right, you would decrement its Y position.\r
+\r
+b) updating the hardware X position of a sprite presents itself to the player as changing the vertical position. \r
+   To make a sprite appear to move up, you would decrement its X position.\r
+   To make a sprite appear to move down, you would increment its X position.\r
+\r
+So when you see code updating the Y coordinate when you would expect X to be updated, or vice versa, you now know why.\r
+\r
+For info about the Galaxian video hardware see: https://github.com/mamedev/mame/blob/master/src/mame/video/galaxian.cpp\r
+*/\r
+\r
+\r
+Copied from MAME4All documentation: https://github.com/squidrpi/mame4all-pi/blob/master/src/drivers/galaxian.cpp\r
+Some corrections applied from: https://github.com/mamedev/mame/blob/master/src/mame/drivers/galaxian.cpp\r
+\r
+Galaxian/Moon Cresta memory map.\r
+Compiled from information provided by friends and Uncles on RGVAC.\r
+\r
+Add 0x4000 to all addresses except for the ROM for Moon Cresta.\r
+            AAAAAA\r
+            111111AAAAAAAAAA     DDDDDDDD   Schem   function\r
+HEX         5432109876543210 R/W 76543210   name\r
+0000-3FFF                                           Game ROM\r
+4000-47FF                                           Working ram\r
+5000-57FF   01010AAAAAAAAAAA R/W DDDDDDDD   !Vram   Character ram           \r
+5800-583F   01011AAAAAAAAAAA R/W DDDDDDDD   !OBJRAM Screen attributes\r
+5840-585F   01011AAAAAAAAAAA R/W DDDDDDDD   !OBJRAM Sprites\r
+5860-5FFF   01011AAAAAAAAAAA R/W DDDDDDDD   !OBJRAM Bullets\r
+6000        0110000000000000 R   -------D   !SW0    coin1\r
+6000        0110000000000000 R   ------D-   !SW0    coin2\r
+6000        0110000000000000 R   -----D--   !SW0    p1 left\r
+6000        0110000000000000 R   ----D---   !SW0    p1 right\r
+6000        0110000000000000 R   ---D----   !SW0    p1shoot\r
+6000        0110000000000000 R   --D-----   !SW0    upright/cocktail\r
+6000        0110000000000000 R   -D------   !SW0    test\r
+6000        0110000000000000 R   D-------   !SW0    service\r
+6000        0110000000000001 W   -------D   !DRIVER lamp 1\r
+6001        0110000000000001 W   -------D   !DRIVER lamp 2\r
+6002        0110000000000001 W   -------D   !DRIVER coin lockout\r
+6003        0110000000000011 W   -------D   !DRIVER coin control\r
+6004        0110000000000100 W   -------D   !DRIVER Background lfo freq bit0\r
+6005        0110000000000101 W   -------D   !DRIVER Background lfo freq bit1\r
+6006        0110000000000110 W   -------D   !DRIVER Background lfo freq bit2\r
+6007        0110000000000111 W   -------D   !DRIVER Background lfo freq bit3\r
+6800        0110100000000000 R   -------D   !SW1    1p start\r
+6800        0110100000000000 R   ------D-   !SW1    2p start\r
+6800        0110100000000000 R   -----D--   !SW1    p2 left\r
+6800        0110100000000000 R   ----D---   !SW1    p2 right\r
+6800        0110100000000000 R   ---D----   !SW1    p2 shoot\r
+6800        0110100000000000 R   --D-----   !SW1    no used\r
+6800        0110100000000000 R   -D------   !SW1    dip sw1\r
+6800        0110100000000000 R   D-------   !SW1    dip sw2\r
+6800        0110100000000000 W   -------D   !SOUND  reset background F1\r
+                                                    (1=reset ?)\r
+6801        0110100000000001 W   -------D   !SOUND  reset background F2\r
+6802        0110100000000010 W   -------D   !SOUND  reset background F3\r
+6803        0110100000000011 W   -------D   !SOUND  player hit\r
+6804        0110100000000100 W   -------D   !SOUND  not used\r
+6805        0110100000000101 W   -------D   !SOUND  shoot on/off\r
+6806        0110100000000110 W   -------D   !SOUND  Vol of f1\r
+6807        0110100000000111 W   -------D   !SOUND  Vol of f2\r
+\r
+7000        0111000000000000 R   -------D   !DIPSW  dip sw 3\r
+7000        0111000000000000 R   ------D-   !DIPSW  dip sw 4\r
+7000        0111000000000000 R   -----D--   !DIPSW  dip sw 5\r
+7000        0111000000000000 R   ----D---   !DIPSW  dip s2 6\r
+7001/B000/1 0111000000000001 W   -------D   9Nregen NMIon\r
+7002        Unused - thanks to Phil Murray for letting me know\r
+7003        Unused\r
+7004        0111000000000100 W   -------D   9Nregen stars on  \r
+7006        0111000000000110 W   -------D   9Nregen hflip\r
+7007        0111000000000111 W   -------D   9Nregen vflip\r
+Note: 9n reg,other bits  used on moon cresta for extra graphics rom control.\r
+7800        0111100000000000 R   --------   !wdr    watchdog reset\r
+7800        0111100000000000 W   DDDDDDDD   !pitch  Sound Fx base frequency\r
+*/\r
+\r
+/*\r
+DIP SWITCH SETTINGS\r
+\r
+Taken from: http://arcarc.xmission.com/PDF_Arcade_Bally_Midway/Galaxian_Parts_and_Operating_Manual_(Feb_1980).pdf\r
+\r
+METHOD OF PLAY:\r
+                              SW.1          SW.2\r
+1 COIN = 1 PLAY               OFF           OFF\r
+2 COINS = 1 PLAY              ON            OFF\r
+1 COIN = 2 PLAYS              OFF           ON\r
+FREE PLAY                     ON            ON \r
+\r
+\r
+BONUS GALIXIP (PLAYER SHIP) - the manual above is not correct with the Namco Galaxian ROM. After doing some research,\r
+here are the correct DIP switch settings: \r
+\r
+\r
+                              SW.3          SW.4\r
+7000                          OFF           OFF  \r
+10000                         ON            OFF\r
+12000                         OFF           ON\r
+20000                         ON            ON\r
+\r
+\r
+NUMBER OF GALIXIP PER GAME\r
+                               SW.5\r
+2 GALIXIP PER GAME             OFF\r
+3 GALIXIP PER GAME             ON\r
+\r
+*/\r
+\r
+\r
+/*\r
+And now, the main game code.... enjoy.\r
+*/\r
+\r
+DIP_SWITCH_1_2_STATE                EQU $4000         ; holds state of dip switches 1 & 2 in bits 0 & 1.\r
+COIN_COUNT                          EQU $4001         ; counts up to number of coins per credit as set by dip switches. When it reaches that value, resets to 0 \r
+NUM_CREDITS                         EQU $4002         ; number of credits\r
+COIN_CONTROL                        EQU $4003         ; is used to output to DRIVER|COIN CONTROL (see $1974)\r
+UNPROCESSED_COINS                   EQU $4004         ; bumps up when coin inserted. See $190B and $1931.\r
+\r
+;\r
+; The game follows what I call "scripts". A SCRIPT is a predefined sequence of STAGES (ie: subroutines) that implement an overall goal.\r
+; The whole game is script-driven, from attract mode to the game itself.\r
+;\r
+; The NMI interrupt handler uses SCRIPT_NUMBER ($4005) to identify what script to run and, depending on the script, SCRIPT_STAGE ($400A) to \r
+; determine what subroutine to call to do the work for that stage of the script.  When the subroutine has completed its work, \r
+; it increments SCRIPT_STAGE which is akin to, "OK, I'm done; proceed to next stage of script".\r
+;\r
+; For example, a script for HELLO WORLD might be implemented as three stages:\r
+; 1. Display Hello World on screen. Set SCRIPT_STAGE to 2.\r
+; 2. Wait for key. Set SCRIPT_STAGE to 3 after key pressed.\r
+; 3. Terminate program.\r
+;\r
+; When I've finished working out what all the scripts do, I'll replace the Hello World above with a real example from the game.\r
+;\r
+;\r
+; The main take-aways from the above are:\r
+; 1. The whole game is driven by the NMI interrupt.\r
+; 2. Script stage and number are really just indexes into jump tables. \r
+;\r
+; see $00CA for the NMI script handler. \r
+\r
+SCRIPT_NUMBER                       EQU $4005         ; 0-based index into pointer table beginning @ $00CE\r
+IS_GAME_IN_PLAY                     EQU $4006         ; If set to 1, game is in play with a human in control.\r
+IS_GAME_OVER                        EQU $4007         ; Set to 1 when GAME OVER message appears. TODO: Check if set any other place than GAME OVER \r
+TEMP_COUNTER_1                      EQU $4008         ; temporary counter used for delays, such as waiting before transitioning to next stage of a script\r
+TEMP_COUNTER_2                      EQU $4009         ; temporary counter used for delays\r
+\r
+SCRIPT_STAGE                        EQU $400A         ; Identifies what stage of the script we are at.  \r
+                                                      ; 0-based index into script tables located @ $0164, $0400, $0540, $0785\r
+TEMP_CHAR_RAM_PTR                   EQU $400B         ; pointer to character RAM. Used by screen-related routines (e.g. power on colour test) to remember where to plot characters on next call.\r
+                                                                                                           \r
+CURRENT_PLAYER                      EQU $400D         ; 0 = PLAYER ONE, 1 = PLAYER TWO\r
+IS_TWO_PLAYER_GAME                  EQU $400E         ; 0 = One player game, 1 = 2 player game \r
+IS_COCKTAIL                         EQU $400F         ; 0 = upright, 1 = Cocktail \r
+PORT_STATE_6000                     EQU $4010         ; copy of state for memory address 6000 (SW0)          \r
+PORT_STATE_6800                     EQU $4011         ; copy of state for memory address 6800 (SW1 & SOUND)\r
+PORT_STATE_7000                     EQU $4012         ; copy of state for memory address 7000 (DIPSW)\r
+\r
+PREV_PORT_STATE_6000                EQU $4013         ; holds the previous state of memory address 6000 (SW0)  \r
+PREV_PORT_STATE_6800                EQU $4014         ; holds the previous state of memory address 6800 (SW1 & SOUND)\r
+PREV_PREV_PORT_STATE_6000           EQU $4015         ; holds the previous, previous (!) state of memory address 6000 (SW0) \r
+PREV_PREV_PREV_STATE_6000           EQU $4016         ; holds the previous, previous, previous state of memory address 6000 (SW0)\r
+\r
+DISPLAY_IS_COCKTAIL_P2              EQU $4018         ; set to 1 when in cocktail mode and it's player 2's turn, so the screen's upside down.\r
+PUSH_START_BUTTON_COUNTER           EQU $4019         ; On inserting credit or GAME OVER: if you have credit, how long to wait before PUSH START BUTTON appears.  \r
+DIAGNOSTIC_MESSAGE_TYPE             EQU $401A         ; Read by the NMI handler. Refer to code @1BCD for docs.  \r
+\r
+RAND_NUMBER                         EQU $401E         ; TENTATIVE NAME. Random number used in tests and in-game \r
+DIP_SWITCH_5_STATE                  EQU $401F         ; holds cached state of dip switch 5 in bit 0\r
+\r
+; Object RAM back buffer. \r
+; Colour attributes, scroll offsets and sprite state are held in this buffer and updated by the game. \r
+; When all the updates are complete and ready to be presented on screen to the player, \r
+; the back buffer is copied to the hardware's OBJRAM by an LDIR operation - see $0079.\r
+; Effectively all colours, scroll and sprites are updated as part of a single operation.\r
+; This back buffering technique is still used today in modern games.\r
+;\r
+; The back buffer is organised thus:\r
+;\r
+; From $4020 - 405f: column scroll and colour attributes. Maps directly to $5800 - $583F. \r
+;    Note: Even numbered addresses hold scroll offsets, odd numbered addresses colour attributes. \r
+; From $4060 - 407F: 8 entries of type INFLIGHT_ALIEN_SPRITE. Maps directly to $5840 - $585F.\r
+; From $4080 - 409F: alien bullets and player bullet sprite state. Maps directly to $5860 - $587F. \r
+\r
+OBJRAM_BACK_BUF                     EQU $4020            \r
+OBJRAM_BACK_BUF_SPRITES             EQU $4060 \r
+\r
+//struct INFLIGHT_ALIEN_SPRITE\r
+//{\r
+//   BYTE Y;                      \r
+//   BYTE Code;                   // bits 0..5: sprite frame. bit 6 set = XFlip. bit 7 set = YFlip\r
+//   BYTE Colour;\r
+//   BYTE X;                      \r
+//} - sizeof(INFLIGHT_ALIEN_SPRITE) is 4 bytes\r
+\r
+\r
+OBJRAM_BACK_BUF_BULLETS             EQU $4080\r
+    OBJRAM_BUF_PLAYER_BULLET_Y      EQU $409D\r
+    OBJRAM_BUF_PLAYER_BULLET_X      EQU $409F\r
+OBJRAM_BACK_BUF_END                 EQU $409F                        \r
+\r
+\r
+PLAYER_ONE_SCORE                      EQU $40A2       ; stored as 3 BCD bytes, 2 digits per byte: $40A2 = last 2 digits of score (tens), $40A3 = 3rd & 4th digits, $40A4 = 1st & 2nd\r
+                                                      ; e.g. a score of 123456 would be stored like so:\r
+                                                      ; $40A2: 56\r
+                                                      ; $40A3: 34\r
+                                                      ; $40A4: 12\r
+\r
+PLAYER_TWO_SCORE                      EQU $40A5       ; stored as 3 BCD bytes, 2 digits per byte: same format & order as PLAYER_ONE_SCORE\r
+HI_SCORE                            EQU $40A8         ; stored as 3 BCD bytes, 2 digits per byte: same format & order as PLAYER_ONE_SCORE\r
+\r
+CAN_BLINK_1UP_2UP                   EQU $40AB         ; When IS_GAME_IN_PLAY is set to 1, this flag is set to 1 to allow 1UP or 2UP to "blink". See @$20A7\r
+BONUS_GALIXIP_FOR                   EQU $40AC         ; stored as BCD in 1 byte. e.g. 07 = bonus galixip for 7000, 20 = bonus galixip for 20000. \r
+PLAYER_ONE_AWARDED_EXTRA_LIFE         EQU $40AD       ; Set to 1 if player one has been awarded an extra life. No more extra lives will be given. \r
+PLAYER_TWO_AWARDED_EXTRA_LIFE         EQU $40AE       ; Set to 1 if player two has been awarded an extra life. No more extra lives will be given. \r
+\r
+\r
+IS_COLUMN_SCROLLING                 EQU $40B0         ; Set to 1 if a column is being scrolled. For example when points are scrolled into view on the WE ARE THE GALAXIANS screen\r
+COLUMN_SCROLL_ATTR_BACKBUF_PTR      EQU $40B1         ; pointer to scroll attribute data to update in OBJRAM_BACK_BUF. \r
+COLUMN_SCROLL_NEXT_CHAR_PTR         EQU $40B3         ; pointer to ordinal of next character to scroll on\r
+COLUMN_SCROLL_CHAR_RAM_PTR          EQU $40B5         ; pointer to character RAM where next character will be plotted. \r
+\r
+\r
+; Phil Murray (PhilMurr on UKVAC) gave me a heads up on this.  \r
+;\r
+; $40C0 to $40FF is reserved for a circular queue. The queue is comprised of byte pairs representing a command and parameter.\r
+; NB: I term the byte pair a *Queue Entry* in the code @$08f2 and $200A.\r
+;\r
+; As 64 bytes are reserved for the queue, that means 32 commands and parameters can be stored. \r
+;\r
+; The memory layout of the queue is quite simple.\r
+; \r
+; $40C0: command A\r
+; $40C1: parameter for command A \r
+; $40C2: command B\r
+; $40C3: parameter for command B\r
+; $40C4: command C\r
+; $40C5: parameter for command C\r
+; ..and so on.\r
+;\r
+; See docs @ $08f2 for info about what commands are available, and how to add commands to the queue.\r
+; See docs @ $200A for info about how commands are processed.\r
+;\r
+\r
+\r
+CIRC_CMD_QUEUE_PTR_LO           EQU $40A0             ; low byte of a pointer to a (hopefully) vacant entry in the circular queue. See $08F2 \r
+CIRC_CMD_QUEUE_PROC_LO          EQU $40A1             ; low byte of a pointer to the next entry in the circular queue to be processed. See $200C\r
+CIRC_CMD_QUEUE_START            EQU $40C0\r
+CIRC_CMD_QUEUE_END              EQU $40FF\r
+\r
+\r
+;\r
+; ALIEN_SWARM_FLAGS (name subject to change) is an array 128 bytes in size.   \r
+; Each byte contains a bit flag indicating the presence of an alien at a given position.\r
+; If you start a new game in MAME, then open the debugger and view memory location 4100 (hex) you will see this:\r
+;\r
+; 4100:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  \r
+; 4110:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  \r
+; 4120:  00 00 00 01 01 01 01 01 01 01 01 01 01 00 00 00  \r
+; 4130:  00 00 00 01 01 01 01 01 01 01 01 01 01 00 00 00  \r
+; 4140:  00 00 00 01 01 01 01 01 01 01 01 01 01 00 00 00  \r
+; 4150:  00 00 00 00 01 01 01 01 01 01 01 01 00 00 00 00  \r
+; 4160:  00 00 00 00 00 01 01 01 01 01 01 00 00 00 00 00  \r
+; 4170:  00 00 00 00 00 00 01 00 00 01 00 00 00 00 00 00     \r
+;\r
+; This is the representation of the swarm in memory! 01 means "an alien is here" and 00 means "nothing here".\r
+; The memory representation is upside down *and* flipped horizontally.  \r
+;\r
+; To visualise it properly, turn the dump above upside down using your favourite text editor, erase the "00"s and you get:\r
+; 4170:                    01       01                       ; flagships\r
+; 4160:                 01 01 01 01 01 01                    ; red\r
+; 4150:              01 01 01 01 01 01 01 01                 ; purple\r
+; 4140:           01 01 01 01 01 01 01 01 01 01              ; blue\r
+; 4130:           01 01 01 01 01 01 01 01 01 01              ; blue\r
+; 4120:           01 01 01 01 01 01 01 01 01 01              ; blue\r
+; \r
+; Look familiar? \r
+;\r
+;  \r
+\r
+ALIEN_SWARM_FLAGS                   EQU $4100         ; 128 bytes, occupying $4100 to $417F in RAM\r
+\r
+\r
+; When it's player 1's turn, the packed swarm definition PLAYER_ONE_PACKED_SWARM_DEF is unpacked to ALIEN_SWARM_FLAGS and the fields comprising PLAYER_ONE_STATE ($4190-4197) \r
+; are written to their counterparts in CURRENT_PLAYER_STATE (see docs @$4218)                \r
+PLAYER_ONE_PACKED_SWARM_DEF           EQU $4180       ; Used to track state of the swarm for player one, e.g. so swarm can be restored after player two's turn is over \r
+PLAYER_ONE_STATE                      EQU $4190\r
+PLAYER_ONE_DIFFICULTY_COUNTER_1       EQU $4190         \r
+PLAYER_ONE_DIFFICULTY_COUNTER_2       EQU $4191\r
+PLAYER_ONE_DIFFICULTY_EXTRA_VALUE     EQU $4192           \r
+PLAYER_ONE_DIFFICULTY_BASE_VALUE      EQU $4193         \r
+PLAYER_ONE_LEVEL                      EQU $4194\r
+PLAYER_ONE_LIVES                      EQU $4195\r
+PLAYER_ONE_FLAGSHIP_SURVIVOR_COUNT    EQU $4196         \r
+PLAYER_ONE_LFO_FREQ_BITS              EQU $4197         \r
+\r
+\r
+; When it's player 2's turn, the packed swarm definition PLAYER_TWO_PACKED_SWARM_DEF is unpacked to ALIEN_SWARM_FLAGS and the fields comprising PLAYER_TWO_STATE ($41B0-41B7) \r
+; are written to their counterparts in CURRENT_PLAYER_STATE (see docs @$4218)                \r
+PLAYER_TWO_PACKED_SWARM_DEF           EQU $41A0       ; Used to track state of the swarm for player two, e.g. so swarm can be restored after player one's turn is over\r
+PLAYER_TWO_STATE                      EQU $41B0\r
+PLAYER_TWO_DIFFICULTY_COUNTER_1       EQU $41B0         \r
+PLAYER_TWO_DIFFICULTY_COUNTER_2       EQU $41B1\r
+PLAYER_TWO_DIFFICULTY_EXTRA_VALUE     EQU $41B2           \r
+PLAYER_TWO_DIFFICULTY_BASE_VALUE      EQU $41B3         \r
+PLAYER_TWO_LEVEL                      EQU $41B4\r
+PLAYER_TWO_LIVES                      EQU $41B5\r
+PLAYER_TWO_FLAGSHIP_SURVIVOR_COUNT    EQU $41B6         \r
+PLAYER_TWO_LFO_FREQ_BITS              EQU $41B7         \r
+\r
+\r
+SOUND_VOL                           EQU $41C0         ; Bit 0 and 1 are written to !SOUND Vol of F1 and !SOUND Vol of F2 respectively. See $1712\r
+PITCH_SOUND_FX_BASE_FREQ            EQU $41C1         ; used to write to !pitch  Sound Fx base frequency. See $171F\r
+ENABLE_ALIEN_ATTACK_SOUND           EQU $41C2         ; When set to 1, turns on alien attack noise, see $17D0\r
+UNKNOWN_SOUND_41C3                  EQU $41C3          \r
+UNKNOWN_SOUND_41C4                  EQU $41C4         ; Seems to affect the pitch of the alien attack noise. \r
+\r
+PLAY_EXTRA_LIFE_SOUND               EQU $41C7         ; when set to 1, play the sound of an extra life being awarded. See $184F\r
+EXTRA_LIFE_SOUND_COUNTER            EQU $41C8            \r
+PLAY_PLAYER_CREDIT_SOUND            EQU $41C9         ; when set to 1, play the sound of player credits being added. See $1876\r
+PLAYER_CREDIT_SOUND_COUNTER         EQU $41CA         ; The higher the value, the longer the player credit sound plays.\r
+                                    EQU $41CB          \r
+PLAY_PLAYER_SHOOT_SOUND             EQU $41CC         ; When set to 1, play the sound of the player's bullet. See $1723\r
+IS_COMPLEX_SOUND_PLAYING            EQU $41CD         ; When set to 1, a sequence of sounds, or a melody, is playing. \r
+PLAYER_SHOOT_SOUND_COUNTER          EQU $41CE         ; The higher the value, the longer the player spaceship bullet sound plays.\r
+                                    EQU $41CF \r
+RESET_SWARM_SOUND_TEMPO             EQU $41D0         ; When set to 1, resets the tempo of the "swarm" sound to slow again. See $1898\r
+PLAY_GAME_START_MELODY              EQU $41D1         ; When set to 1, plays the game start tune. \r
+                                    EQU $41D2         ; sound related\r
+COMPLEX_SOUND_POINTER               EQU $41D3         ; If music or complex sound effect is playing, this points to the current sound/musical note being played. See $1782\r
+                                    EQU $41D5         ; Used to set !Pitch Sound FX base frequency\r
+DELAY_BEFORE_NEXT_SOUND             EQU $41D6         ; counter. When counts to zero the next sound/musical note is played. See $177B\r
+ALIEN_DEATH_SOUND                   EQU $41DF         ; Tentative name. When set to $06: plays alien death sound. When set to $16, plays flagship death sound. See @$1819\r
+                                    EQU $41E8\r
+\r
+; HAVE_ALIENS_IN_ROW_FLAGS is an array of 6 bytes. Each byte contains a bit flag specifying if there are any aliens on a given row.\r
+HAVE_ALIENS_IN_ROW_FLAGS            EQU $41E8\r
+NEVER_USED_ROW_1                    EQU $41E8\r
+NEVER_USED_ROW_2                    EQU $41E9\r
+\r
+HAVE_ALIENS_IN_6TH_ROW              EQU $41EA         ; flag set to 1 if there are any aliens in the bottom row (blue aliens)\r
+HAVE_ALIENS_IN_5TH_ROW              EQU $41EB         ; flag set to 1 if there are any aliens in the 5th row (blue aliens)\r
+HAVE_ALIENS_IN_4TH_ROW              EQU $41EC         ; flag set to 1 if there are any aliens in the 4th row (blue aliens)\r
+HAVE_ALIENS_IN_3RD_ROW              EQU $41ED         ; flag set to 1 if there are any aliens in the 3rd row (purple aliens)\r
+HAVE_ALIENS_IN_2ND_ROW              EQU $41EE         ; flag set to 1 if there are any aliens in the 2nd row (red aliens)\r
+HAVE_ALIENS_IN_TOP_ROW              EQU $41EF         ; flag set to 1 if there are any aliens in the top row (flagships)\r
+\r
+\r
+; ALIEN_IN_COLUMN_FLAGS is an array 16 bytes in size. Each byte contains a bit flag specifying if there are any aliens in a specific column. \r
+; IMPORTANT: The flags are ordered from rightmost column of aliens to the leftmost. Only 10 of the flags are used.\r
+; \r
+; In a nutshell:\r
+; $41F0..$41F2: unused. Always set to 0.\r
+; $41F3: set to 1 if any aliens are in the rightmost column of the swarm.\r
+; $41F4: set to 1 if any aliens are in the 2nd rightmost column of the swarm.\r
+; $41F5: set to 1 if any aliens are in the 3rd rightmost column of the swarm.\r
+; ..\r
+; $41FC: set to 1 if any aliens are in the leftmost column of the swarm.\r
+; $41FD..$41FF: unused. Always set to 0.\r
+;\r
+; The flags have three purposes: \r
+; 1: To halt the swarm when a bullet is getting too close (see $0936)\r
+; 2: to calculate how far the swarm can scroll before it needs to change direction (see $093E)\r
+; 3: to find aliens at the swarm edges to attack the player (see code from $137B onwards) \r
+;\r
+;\r
+; To further clarify in case there's any confusion, let's assume you've just started the game and you're on the first level. \r
+; You haven't shot anything yet. The alien swarm will be in the following formation:\r
+;\r
+;      F  F                     F = Flagship row  \r
+;     RRRRRR                    R = Red alien row\r
+;    PPPPPPPP                   P = Purple alien row\r
+;   BBBBBBBBBB                  B = Blue alien row\r
+;   BBBBBBBBBB\r
+;   BBBBBBBBBB \r
+;\r
+; Press PAUSE in MAME and open the memory debugger at location $41F0.\r
+; The flags will look like so in the MAME memory window:\r
+; 00 00 00 01 01 01 01 01 01 01 01 01 01 00 00 00\r
+;\r
+; You'll note that there are 10 flags set to TRUE (01) in a row. That is because the bottommost row has 10 blue aliens. \r
+; If you were to shoot the blue aliens in the rightmost column, you would see the first 01 (at memory address $41F3) turn into a 0, \r
+; meaning that column no longer contains any aliens. \r
+; \r
+; 0 is also written to the flags when the only alien in a column breaks off from the swarm to attack the player.\r
+; \r
+                                           \r
+ALIEN_IN_COLUMN_FLAGS               EQU $41F0          \r
+ALIEN_IN_COLUMN_FLAGS_END           EQU $41FF     \r
+\r
+\r
+HAS_PLAYER_SPAWNED                  EQU $4200         ; set to 1 when player has spawned. (Also set in attract mode) \r
+IS_PLAYER_DYING                     EQU $4201         ; set to 1 when player is in the process of exploding horribly. See $1327\r
+PLAYER_Y                            EQU $4202         ; Player Y coordinate. Used to set scroll offsets for column containing ship characters. See $0865\r
\r
+IS_PLAYER_HIT                       EQU $4204         ; When set to 1, player has been hit by a missile or collided with an alien.         \r
+PLAYER_EXPLOSION_COUNTER            EQU $4205         ; Only evaluated when IS_PLAYER_DYING is set to 1. Determines how long the player explosion animation lasts. \r
+                                                      ; When it counts down to 0, explosion animation stops. See $132C\r
+PLAYER_EXPLOSION_ANIM_FRAME         EQU $4206         ; Set by $12FE \r
+HAS_PLAYER_BULLET_BEEN_FIRED        EQU $4208         ; set 1 when the player has fired a bullet and the bullet is still onscreen. See $08BC\r
+PLAYER_BULLET_X                     EQU $4209         ; Current X coordinate of player bullet. \r
+PLAYER_BULLET_Y                     EQU $420A         ; Current Y coordinate of player bullet. \r
+IS_PLAYER_BULLET_DONE               EQU $420B         ; set 1 when player bullet goes as far as it can upscreen (see $08CD), or hits an alien (see $0B4F & $125B).\r
+\r
+SWARM_DIRECTION                     EQU $420D         ; Direction of swarm (really? ;) )  0 = Moving left, 1 = moving right . See $0945              \r
+SWARM_SCROLL_VALUE                  EQU $420E         ; 16 bit value. Used to set the scroll values for the character columns containing the swarm.                            \r
+SWARM_SCROLL_MAX_EXTENTS            EQU $4210         ; Used to limit the scrolling of the swarm so no alien goes "off screen". See $09CE \r
+\r
+; INFLIGHT_ALIEN_SHOOT_EXACT_X and MINFLIGHT_ALIEN_SHOOT_RANGE_MUL are used to determine if an alien can shoot a bullet. See $0E54 for information.\r
+INFLIGHT_ALIEN_SHOOT_RANGE_MUL      EQU $4213         ; Range multiplier.   \r
+INFLIGHT_ALIEN_SHOOT_EXACT_X        EQU $4214         ; Exact X coordinate that calculated value must match for alien to shoot.\r
+\r
+ALIENS_ATTACK_FROM_RIGHT_FLANK      EQU $4215         ; Flag used to determine what side of swarm aliens break off from. (0=break from left, 1=break from right). See $136f and $1426 \r
+                                    EQU $4217         ; \r
+\r
+; $4218 - $421F holds important, albeit transient, state for the current player such as number of lives and difficulty level.\r
+CURRENT_PLAYER_STATE                EQU $4218                    \r
+\r
+; These 2 counters are used to gradually increase the DIFFICULTY_EXTRA_VALUE over time. See $14F3 for algorithm details.\r
+DIFFICULTY_COUNTER_1                EQU $4218         ; Counts down to zero. \r
+DIFFICULTY_COUNTER_2                EQU $4219         ; Counts down to zero. When it reaches zero, DIFFICULTY_EXTRA_VALUE is incremented.\r
+\r
+; These values determine how often aliens attack (see $1524 and $1583), and how many can attack at one time (see $1352). \r
+DIFFICULTY_EXTRA_VALUE              EQU $421A         ; DIFFICULTY_EXTRA_VALUE is incremented during the level. Maximum value of 7. See $1509.  \r
+DIFFICULTY_BASE_VALUE               EQU $421B         ; DIFFICULTY_BASE_VALUE is incremented when you complete a level. Maximum value of 7. See $1656.\r
+\r
+PLAYER_LEVEL                        EQU $421C         ; Current player's level. Starts from 0. Add 1 to get true value. See $252C.\r
+PLAYER_LIVES                        EQU $421D         ; current player's lives\r
+FLAGSHIP_SURVIVOR_COUNT             EQU $421E         ; When starting a new level, how many surviving flagships can we bring over from the previous level? Maximum value 2.  See $166C\r
+LFO_FREQ_BITS                       EQU $421F         ; Value used to set !DRIVER Background lfo frequency ports (0-3) for the "swarm" noise\r
+\r
+CURRENT_PLAYER_STATE_END            EQU $421F                \r
+\r
+HAVE_NO_ALIENS_IN_SWARM             EQU $4220         ; Set to 1 when $4100 - $417F are set to 0. Aliens are either all dead, or are in flight and out of the swarm. See $0A0F\r
+HAVE_NO_BLUE_OR_PURPLE_ALIENS       EQU $4221         ; When set to 1, all the blue and purple aliens have died, or are in flight. See $09FA and $1571  \r
+LEVEL_COMPLETE                      EQU $4222         ; When set to 1, the level is treated as complete. See @$1621, $1637\r
+NEXT_LEVEL_DELAY_COUNTER            EQU $4223         ; After all aliens have fled or been killed, this counts down to give the player breathing space. When it hits 0, the next wave starts. See $1637\r
+HAVE_AGGRESSIVE_ALIENS              EQU $4224         ; when set to 1, inflight aliens will not return to swarm and keep attacking player until they die - or you die. See $16B8\r
+HAVE_NO_INFLIGHT_OR_DYING_ALIENS    EQU $4225         ; When set to 1, there are no aliens inflight, or dying. See $06BC\r
+HAVE_NO_INFLIGHT_ALIENS             EQU $4226         ; When set to 1, no aliens have broken off from the swarm to attack the player.\r
+CAN_ALIEN_ATTACK                    EQU $4228         ; When set to 1, a single alien should break off from the swarm to attack the player. See $1344.\r
+CAN_FLAGSHIP_OR_RED_ALIENS_ATTACK   EQU $4229         ; When set to 1, a flagship should attack the player, with an escort if possible. If no flagships alive, send red aliens.  See $140C.\r
+FLAGSHIP_ESCORT_COUNT               EQU $422A         ; Number of red aliens escorting the flagship. Max value of 2. See $0D58.\r
+\r
+; When you shoot an enemy flagship in flight that this puts the aliens into a state of "shock" where they are afraid to leave the swarm for a while.\r
+; No aliens will leave the swarm while $422B is set to 1 and $422C is non-zero. \r
+IS_FLAGSHIP_HIT                     EQU $422B         ; Set to 1 when you've shot a flagship in flight. See $127C  \r
+ALIENS_IN_SHOCK_COUNTER             EQU $422C         ; When $422B is set to 1, this counter decrements. When it hits 0, $422B will be set to 0, meaning aliens can leave the swarm again.  \r
+FLAGSHIP_SCORE_FACTOR               EQU $422D         ; When you shoot a flagship, this is used to compute your score. Couldn't think of a better name! See $127C\r
+\r
+ENABLE_FLAGSHIP_ATTACK_SECONDARY_COUNTER      EQU $422E         ; when set to 1, FLAGSHIP_ATTACK_SECONDARY_COUNTER is allowed to decrement.             \r
+FLAGSHIP_ATTACK_SECONDARY_COUNTER   EQU $422F         ; Counts down to 0. When reaches zero, CAN_FLAGSHIP_OR_RED_ALIENS_ATTACK will be set to 1.\r
+\r
+DISABLE_SWARM_ANIMATION             EQU $4238         ; When set to 1, alien swarm won't animate. See $2067 for docs. \r
+ATTRACT_MODE_FAKE_CONTROLLER        EQU $423F         ; used to simulate a players movements on the ATTRACT MODE screen. Contains bit values that map to SW0.\r
+ATTRACT_MODE_SCROLL_ID              EQU $4241         ; Identifies what points values are being scrolled in on attract mode. 1:Flagship. 2: Red Alien. 3: Purple alien. 4: Blue alien. \r
+\r
+; These 2 counters are used to determine when a flagship is permitted to attack.  See $156A.\r
+FLAGSHIP_ATTACK_MASTER_COUNTER_1    EQU $4245          \r
+FLAGSHIP_ATTACK_MASTER_COUNTER_2    EQU $4246          \r
+\r
+; ALIEN_ATTACK_COUNTERS is an array of BYTE counters that control when aliens (but not flagships) break off from the swarm to attack. \r
+; ALIEN_ATTACK_MASTER_COUNTER at $424A is the first element of the array. The secondary counters are stored in $424B to $425A. \r
+; The ALIEN_ATTACK_MASTER_COUNTER acts as a gateway to the secondary counters; only when the master counter reaches zero will the secondary counters in the array be decremented.\r
+; If any of the secondary counters reach zero, an alien will attack the player. See $1532 for more info.\r
+ALIEN_ATTACK_COUNTERS               EQU $424A  \r
+ALIEN_ATTACK_MASTER_COUNTER         EQU $424A\r
+ALIEN_ATTACK_SECONDARY_COUNTERS     EQU $425B         \r
+ALIEN_ATTACK_SECONDARY_COUNTERS_END EQU $425A     \r
+\r
+\r
+TIMING_VARIABLE                     EQU $425F         ; Perpetually decremented by the NMI handler. Routines use this variable to determine when to execute.\r
+                                                         \r
+\r
+; ENEMY_BULLETS is an array of type ENEMY_BULLET. \r
+;\r
+; The array occupies memory locations $4260 - $42A5; It is thus 70 bytes in size. \r
+; As an ENEMY_BULLET record only requires 5 bytes, this means that there's room for 14 enemy bullets in the array.\r
+;\r
+\r
+struct ENEMY_BULLET\r
+{\r
+    BYTE IsActive;\r
+    BYTE X;\r
+    BYTE YL;                                       // low byte of the Y coordinate. Used to represent "fractional part" of Y coordinate\r
+    BYTE YH;                                       // high byte of the Y coordinate.  \r
+    BYTE YDelta;                                   // packed delta to add to YH *and* YL. Bit 7 = sign bit. Bits 0-6 = delta. See @$0AA1.                                  \r
+} - sizeof(ENEMY_BULLET) is 5 bytes\r
+\r
+ENEMY_BULLETS                       EQU $4260\r
+ENEMY_BULLETS_START                 EQU $4260                                                                        \r
+ENEMY_BULLETS_END                   EQU $42A5\r
+\r
+\r
+\r
+; INFLIGHT_ALIENS is an array of type INFLIGHT_ALIEN. \r
+; An "Inflight alien" is my term for an alien that has broken off from the main swarm body to attack the player. \r
+;\r
+; The array occupies memory locations $42B0 - $43AF; It is thus 256 bytes in size. \r
+; As the INFLIGHT_ALIEN type is 32 bytes in size, this means that there's room for 8 entries in the array. \r
+;\r
+; Slot 0 in the array is actually reserved for misc use, such as when you shoot an alien in the swarm body and an \r
+; explosion animation needs a free sprite to play. (See: $0B52 for an example of this)\r
+;\r
+; Slot  1 is reserved for the flagship. \r
+; Slots 2 and 3 are reserved for the flagship's escorts.\r
+; Slots 4,5,6,7 are reserved for individual attacking aliens.\r
+;\r
+; This means there can be 7 aliens in flight maximum. \r
+;\r
+;  \r
+\r
+;\r
+; struct INFLIGHT_ALIEN\r
+{\r
+  0    BYTE IsActive;                        // Set to 1 when the alien is to be processed. \r
+  1    BYTE IsDying;                         // Set to 1 when the alien is in the process of exploding.\r
+  2    BYTE StageOfLife                      // See $0CD6 for details. \r
+  3    BYTE X;                               // X coordinate\r
+  4    BYTE Y;                               // Y coordinate. \r
+  5    BYTE AnimationFrame; \r
+  6    BYTE ArcClockwise                     // Set to 1 if the alien will rotate clockwise as it leaves the swarm or loops the loop. See $0D71 and $101F\r
+  7    BYTE IndexInSwarm                     // index of alien within ALIEN_SWARM_FLAGS array\r
+  8    BYTE ???                              // Unused\r
+  9    BYTE PivotYValue                      // When alien is attacking, this value + $19 produces INFLIGHT_ALIEN.Y coordinate. See $0DF6\r
+  A    BYTE ???                              // Unused\r
+  B    BYTE ???                              // Unused\r
+  C    BYTE ???                              // Unused \r
+  D    BYTE ???                              // Unused\r
+  E    BYTE ???                              // Unused\r
+  F    BYTE AnimFrameStartCode               // Base animation frame number to which a number is added to compute sprite "code"\r
+  10   BYTE TempCounter1                     // Counter used for various timing purposes\r
+  11   BYTE TempCounter2                     // Secondary counter for various timing purposes\r
+  12   BYTE DeathAnimCode                    // when IsDying is set to 1, specifies the animation frame to display. See @$0C9F\r
+  13   BYTE ArcTableLsb                      // LSB of pointer into INFLIGHT_ALIEN_ARC_TABLE @$1E00. See docs @$0D71 and $1E00.\r
+  14   BYTE ???                              // Unused  \r
+  15   BYTE ???                              // Unused\r
+  16   BYTE Colour\r
+  17   BYTE SortieCount                      // Number of times the alien has reached the bottom of the screen then resumed attack on the player. Reset to 0 when rejoins swarm. See $0E9D.\r
+  18   BYTE Speed                            // Value from 0..3. The higher the number the faster the alien moves. See $116B. \r
+  19   BYTE PivotYValueAdd                   // Signed number which is added to INFLIGHT_ALIEN.PivotYValue to produce INFLIGHT_ALIEN.Y. See $0DF6\r
+  1A   BYTE ???                              \r
+  1B   BYTE ???                                \r
+  1C   BYTE ???\r
+  1D   BYTE ???\r
+  1E   BYTE ???                                \r
+  1F   BYTE ???                              \r
+}  - sizeof(INFLIGHT_ALIEN) is 32 bytes\r
+\r
+\r
+INFLIGHT_ALIENS                     EQU $42B0\r
+INFLIGHT_ALIENS_END                 EQU $43AF\r
+\r
+\r
+0000: AF            xor  a\r
+0001: 32 01 70      ld   ($7001),a           ; write to regen NMIon\r
+0004: C3 55 1A      jp   $1A55\r
+0007: FF            rst  $38\r
+\r
+\r
+\r
+ASSERT_NOT_GAME_OVER:\r
+0008: 3A 07 40      ld   a,($4007)           ; read IS_GAME_OVER flag\r
+000B: 0F            rrca                     ; move flag into carry\r
+000C: D0            ret  nc                  ; if flag was 1, carry is set, it's GAME OVER, return\r
+000D: 33            inc  sp                  ; increment sp by 2..  \r
+000E: 33            inc  sp                  ; ..effectively discarding the return address on the stack\r
+000F: C9            ret                      ; and we're done\r
+\r
+;\r
+; Fill memory from HL to HL+B with value A.\r
+;\r
+; expects:\r
+; A = value to write\r
+; B = count\r
+; HL = pointer \r
+;\r
+; Returns:\r
+; A same as on entry\r
+; HL will be HL+ B\r
+; B will be 0\r
+\r
+0010: 77            ld   (hl),a\r
+0011: 23            inc  hl\r
+0012: 10 FC         djnz $0010\r
+0014: C9            ret\r
+\r
+\r
+0015: FF            rst  $38\r
+0016: 9F            sbc  a,a\r
+0017: FF            rst  $38\r
+\r
+\r
+0018: 77            ld   (hl),a\r
+0019: 23            inc  hl\r
+001A: 10 FC         djnz $0018\r
+001C: 0D            dec  c\r
+001D: 20 F9         jr   nz,$0018\r
+001F: C9            ret\r
+\r
+\r
+;\r
+; Return the byte at HL + A.\r
+; i.e: in BASIC this would be akin to: result = PEEK (HL + A)\r
+;\r
+; expects:\r
+; A = offset\r
+; HL = pointer\r
+;\r
+; returns:\r
+; A = the contents of (HL + A)\r
+;\r
+\r
+0020: 85            add  a,l                 ; a+=l\r
+0021: 6F            ld   l,a                 \r
+0022: 3E 00         ld   a,$00               \r
+0024: 8C            adc  a,h                 \r
+0025: 67            ld   h,a                 ; effectively: HL = HL + A. Now hl is set to point to byte to read\r
+0026: 7E            ld   a,(hl)              ; load a with contents of (HL)\r
+0027: C9            ret\r
+\r
+;\r
+; Jump to instruction in table.\r
+;\r
+; Immediately after the RST 28 call, there must be a table of pointers to code.\r
+; A is a zero-based index into the table.\r
+; \r
+; Expects:\r
+; A = index. Is multiplied by 2 to form an offset into the succeeding table.\r
+; \r
+;\r
+\r
+0028: 87            add  a,a                 ; multiply A by 2. \r
+0029: E1            pop  hl                  ; pop return address off stack into HL\r
+002A: 5F            ld   e,a\r
+002B: 16 00         ld   d,$00               ; extend A into DE. Now DE = offset into table\r
+002D: 19            add  hl,de               ; Effectively, HL = HL + offset into table\r
+002E: 5E            ld   e,(hl)              ; load E from table\r
+002F: 23            inc  hl                \r
+0030: 56            ld   d,(hl)              ; load D from table. Now DE = a pointer to code.\r
+0031: EB            ex   de,hl               ; \r
+0032: E9            jp   (hl)                ; Jump to code specified by table entry.\r
+\r
+0033: FF            rst  $38\r
+0034: FF            rst  $38\r
+0035: FF            rst  $38\r
+0036: FF            rst  $38\r
+0037: FF            rst  $38\r
+\r
+\r
+0038: C3 00 00      jp   $0000\r
+003B: FF            rst  $38\r
+\r
+\r
+;\r
+; A very primitive pseudo-random number generator.\r
+;\r
+\r
+GENERATE_RANDOM_NUMBER:\r
+003C: 3A 1E 40      ld   a,($401E)          \r
+003F: 47            ld   b,a\r
+0040: 87            add  a,a\r
+0041: 87            add  a,a\r
+0042: 80            add  a,b\r
+0043: 3C            inc  a\r
+0044: 32 1E 40      ld   ($401E),a\r
+0047: C9            ret\r
+\r
+;\r
+; Used by the aliens to determine what way to face when flying down, and what delta enemy bullets take\r
+;\r
+; Expects:\r
+; A = distance\r
+; D = X coordinate\r
+;\r
+\r
+CALCULATE_TANGENT:\r
+0048: 0E 00         ld   c,$00\r
+004A: 06 08         ld   b,$08\r
+004C: BA            cp   d\r
+004D: 38 01         jr   c,$0050\r
+004F: 92            sub  d\r
+0050: 3F            ccf\r
+0051: CB 11         rl   c\r
+0053: CB 1A         rr   d\r
+0055: 10 F5         djnz $004C\r
+0057: C9            ret\r
+\r
+0058: FF            rst  $38\r
+0059: FF            rst  $38\r
+005A: FF            rst  $38\r
+005B: FF            rst  $38\r
+005C: FF            rst  $38\r
+005D: FF            rst  $38\r
+005E: FF            rst  $38\r
+005F: FF            rst  $38\r
+0060: FF            rst  $38\r
+0061: FF            rst  $38\r
+0062: FF            rst  $38\r
+0063: FF            rst  $38\r
+0064: FF            rst  $38\r
+0065: 8D            adc  a,l\r
+\r
+\r
+;\r
+; Non Maskable Interrupt (NMI) handler.\r
+;\r
+; Thanks to Phil Murray (PhilMurr on UKVAC) for pointing out the significance of memory address $66 to me.\r
+; It's been a while since I looked at Z80 hardware... :)\r
+;\r
+\r
+0066: F5            push af\r
+0067: C5            push bc\r
+0068: D5            push de\r
+0069: E5            push hl\r
+006A: DD E5         push ix\r
+006C: FD E5         push iy\r
+006E: AF            xor  a\r
+006F: 32 01 70      ld   ($7001),a           ; Write to regen NMIon. This must disable further NMIs until the handler is done.\r
+0072: 3A 1A 40      ld   a,($401A)           ; read DIAGNOSTIC_MESSAGE_TYPE\r
+0075: A7            and  a                   ; test if zero\r
+0076: C2 CD 1B      jp   nz,$1BCD            ; if not zero, goto $1BCD - perform a diagnostic test\r
+\r
+; update screen in one go - IMPORTANT\r
+0079: 21 20 40      ld   hl,$4020            ; pointer to OBJRAM_BACK_BUF buffer held in RAM\r
+007C: 11 00 58      ld   de,$5800            ; start of screen attribute RAM\r
+007F: 01 80 00      ld   bc,$0080            ; number of bytes to copy from OBJRAM_BACK_BUF \r
+0082: ED B0         ldir                     ; update screen & sprites in one go\r
+\r
+; read ports and stash values read in RAM\r
+0084: 3A 00 78      ld   a,($7800)           ; kick the watchdog\r
+0087: 3A 15 40      ld   a,($4015)           ; read previous, previous state of port 6000 (SW0)\r
+008A: 32 16 40      ld   ($4016),a           ; and write to PREV_PREV_PREV_STATE_6000 \r
+008D: 3A 13 40      ld   a,($4013)           ; read previous state of port 6000 (SW0)\r
+0090: 32 15 40      ld   ($4015),a           ; and write to PREV_PREV_PORT_STATE_6000  \r
+0093: 2A 10 40      ld   hl,($4010)          ; read state of 6000 (SW0) and 6800 (SW1 & SOUND)\r
+0096: 22 13 40      ld   ($4013),hl          ; and write to previous state value\r
+0099: 3A 00 70      ld   a,($7000)           ; read state of DIPSW\r
+009C: 32 12 40      ld   ($4012),a           ; and write to PORT_STATE_7000 holder\r
+009F: 3A 00 68      ld   a,($6800)           ; read start button, p2 control, dipsw 1/2 state \r
+00A2: 32 11 40      ld   ($4011),a           ; and write to PORT_STATE_6800 holder\r
+00A5: 3A 00 60      ld   a,($6000)           ; read coin, p1 control, test & service state\r
+00A8: 32 10 40      ld   ($4010),a           ; and write to PORT_STATE_6000 holder\r
+\r
+; check if TEST pressed\r
+00AB: CB 77         bit  6,a                 ; read TEST bit\r
+00AD: C2 00 00      jp   nz,$0000            ; if bit set, goto $0000 - redo test sequence\r
+\r
+; if TEST not pressed\r
+00B0: 21 5F 42      ld   hl,$425F            ; pointer to TIMING_VARIABLE\r
+00B3: 35            dec  (hl)                ; decrement value\r
+                     \r
+00B4: CD EF 18      call $18EF               ; call CHECK_IF_COIN_INSERTED\r
+00B7: CD 31 19      call $1931               ; call HANDLE_UNPROCESSED_COINS\r
+00BA: CD 7C 19      call $197C               ; call HANDLE_COIN_LOCKOUT\r
+00BD: CD F5 16      call $16F5               ; call HANDLE_SOUND\r
+00C0: CD 98 18      call $1898               ; call HANDLE_SWARM_SOUND\r
+00C3: CD C0 18      call $18C0               ; call HANDLE_TEXT_SCROLL\r
+\r
+; invoke script [SCRIPT_NUMBER]\r
+00C6: 21 D8 00      ld   hl,$00D8            ; return address\r
+00C9: E5            push hl                  ; save it on the stack. RET will return to $00D8 \r
+00CA: 3A 05 40      ld   a,($4005)           ; read SCRIPT_NUMBER\r
+00CD: EF            rst  $28                 ; jump to code @ $00CE + (A*2)\r
+\r
+SCRIPT_TABLE:\r
+00CE: \r
+     E6 00          ; $00E6   (SCRIPT_ZERO)    \r
+     56 01          ; $0156   (SCRIPT_ONE)\r
+     F2 03          ; $03F2   (SCRIPT_TWO)\r
+     36 05          ; $0536   (SCRIPT_THREE)\r
+     7B 07          ; $077B   (SCRIPT_FOUR)\r
+\r
+\r
+00D8: FD E1         pop  iy\r
+00DA: DD E1         pop  ix\r
+00DC: E1            pop  hl\r
+00DD: D1            pop  de\r
+00DE: C1            pop  bc\r
+00DF: 3E 01         ld   a,$01\r
+00E1: 32 01 70      ld   ($7001),a           ; Write to regen NMIon. I think this will re-enable NMIs.\r
+00E4: F1            pop  af\r
+00E5: C9            ret\r
+\r
+\r
+\r
+;\r
+; \r
+; \r
+;\r
+;\r
+\r
+SCRIPT_ZERO:\r
+; clear [TEMP_COUNTER_1] rows on screen.\r
+00E6: 2A 0B 40      ld   hl,($400B)          ; Read TEMP_CHAR_RAM_PTR. This holds character RAM to start clearing from\r
+00E9: 06 20         ld   b,$20               ; #$20 (32 decimal) bytes to fill in a row\r
+00EB: 3E 10         ld   a,$10               ; ordinal of empty character\r
+00ED: D7            rst  $10                 ; Clear entire row of characters\r
+00EE: 22 0B 40      ld   ($400B),hl          ; save in TEMP_CHAR_RAM_PTR\r
+00F1: 21 08 40      ld   hl,$4008            ; load HL with address of TEMP_COUNTER_1\r
+00F4: 35            dec  (hl)                ; decrement value\r
+00F5: C0            ret  nz                  ; if counter hasn't hit zero, return\r
+\r
+00F6: 2D            dec  l                   ; point HL to IS_GAME_OVER\r
+00F7: 36 01         ld   (hl),$01               \r
+00F9: 2D            dec  l                   ; point HL to IS_GAME_IN_PLAY\r
+00FA: 36 00         ld   (hl),$00             \r
+00FC: 2D            dec  l                   ; point HL to SCRIPT_NUMBER\r
+00FD: 36 01         ld   (hl),$01 \r
+\r
+00FF: AF            xor  a\r
+0100: 32 0A 40      ld   ($400A),a           ; reset SCRIPT_STAGE to 0\r
+\r
+0103: 3A 11 40      ld   a,($4011)           ; read PORT_STATE_6800\r
+0106: 07            rlca                     ; move dip sw1 & dip sw2 state...\r
+0107: 07            rlca                     ; ...into bits 0 & 1 of register a\r
+0108: E6 03         and  $03\r
+010A: 32 00 40      ld   ($4000),a           ; and store into DIP_SWITCH_1_2_STATE\r
+\r
+010D: 3A 12 40      ld   a,($4012)           ; read PORT_STATE_7000 \r
+0110: E6 04         and  $04                 ; mask in state of dip switch 5\r
+0112: 0F            rrca                     ; move bit into...\r
+0113: 0F            rrca                     ; bit 0 of register a\r
+0114: 32 1F 40      ld   ($401F),a           ; and store it in DIP_SWITCH_5_STATE\r
+\r
+0117: 11 1B 05      ld   de,$051B            ; load DE with address of PACKED_DEFAULT_SWARM_DEFINITION\r
+011A: CD 46 06      call $0646               ; call UNPACK_ALIEN_SWARM\r
+\r
+; set IS_COCKTAIL flag from !SW0    upright/cocktail\r
+011D: 3A 10 40      ld   a,($4010)           ; read PORT_STATE_6000\r
+0120: E6 20         and  $20                 ; read upright/cocktail bit                  \r
+0122: 07            rlca                     ; move bit from bit 5.. \r
+0123: 07            rlca\r
+0124: 07            rlca                     ; ..to bit 0.\r
+0125: 32 0F 40      ld   ($400F),a           ; and store to IS_COCKTAIL\r
+\r
+; read DIP switches to calculate value of BONUS GALAXIP\r
+0128: 3A 00 70      ld   a,($7000)           ; read state of dip switch 3,4,5,6\r
+012B: E6 03         and  $03                 ; mask in state of dip switches 3 & 4\r
+012D: 21 52 01      ld   hl,$0152\r
+0130: E7            rst  $20                 ; call routine to fetch value @ HL + A \r
+0131: 32 AC 40      ld   ($40AC),a           ; write BONUS GALIXIP @ value  \r
+\r
+; Set screen attribute colours then display "1UP" and "HIGH SCORE" \r
+0134: CD 95 05      call $0595               ; call SET_COLOUR_ATTRIBUTES_TABLE_1\r
+0137: 3E 01         ld   a,$01\r
+0139: 32 40 53      ld   ($5340),a           ; poke "1" to character RAM\r
+013C: 3E 25         ld   a,$25\r
+013E: 32 20 53      ld   ($5320),a           ; poke "U" to character RAM\r
+0141: 3E 20         ld   a,$20\r
+0143: 32 00 53      ld   ($5300),a           ; poke "P" to character RAM - text "1UP" now drawn\r
+0146: 11 04 06      ld   de,$0604            ; command: PRINT_TEXT, parameter: 4 (index of "HIGH SCORE")\r
+0149: CD F2 08      call $08F2               ; call QUEUE_COMMAND \r
+014C: 11 03 05      ld   de,$0503            : command: DISPLAY_SCORE_COMMAND , parameter: 3 (Displays player scores and high score)\r
+014F: C3 F2 08      jp   $08F2               ; jump to QUEUE COMMAND\r
+\r
+; Values for BONUS GALIXIP. 7 = 7000, 10 = 10000, 12 =12000, 20 = 20000\r
+0152: 07 10 12 20 \r
+\r
+\r
+;\r
+; Script ONE is responsible for managing the attract mode.\r
+;\r
+;\r
+;\r
+\r
+SCRIPT_ONE:\r
+0156: CD 0D 09      call $090D               ; call HANDLE_SWARM_MOVEMENT  \r
+0159: CD 8E 09      call $098E               ; call SET_ALIEN_PRESENCE_FLAGS\r
+015C: 21 D7 03      ld   hl,$03D7            ; return address \r
+015F: E5            push hl                  ; push return address onto the stack\r
+0160: 3A 0A 40      ld   a,($400A)           ; read SCRIPT_STAGE       \r
+0163: EF            rst  $28                 ; jump to code @ $0164 + (A*2)\r
+\r
+0164: \r
+      8C 01         ; $018C (DISPLAY_GAME_OVER_AND_REMAINING_CREDIT_1)\r
+      BE 01         ; $01BE (SET_PUSH_START_BUTTON_COUNTER)\r
+      C6 01         ; $01C6 (HIDE_SWARM_AND_PREPARE_TO_CLEAR_SCREEN) \r
+      E1 01         ; $01E1 (CLEAR_SCREEN_BEFORE_WE_ARE_THE_GALAXIANS_INTRO)\r
+      18 02         ; $0218 (DISPLAY_WE_ARE_THE_GALAXIANS_INTRO)\r
+      3F 02         ; $023F (SCROLL_ON_CONVOY_CHARGER_POINTS)\r
+      67 02         ; $0267 (DISPLAY_NAMCO_LOGO) \r
+      8E 02         ; $028E (BLINK_CONVOY_CHARGER_POINTS)\r
+      C6 01         ; $01C6 (HIDE_SWARM_AND_PREPARE_TO_CLEAR_SCREEN) \r
+      9D 02         ; $029D (CLEAR_WE_ARE_GALAXIANS_SCREEN_AND_DISPLAY_GAME_OVER)\r
+      D1 02         ; $02D1 (DISPLAY_GAME_OVER_AND_REMAINING_CREDIT_2)\r
+      2E 03         ; $032E (WAIT_FOR_TEMP_COUNTER_2_THEN_ADVANCE_TO_NEXT_STAGE) \r
+      E8 02         ; $02E8 (CLEAR_ALIEN_SWARM_AND_SUSPEND_SWARM_ANIMATION)\r
+      FD 02         ; $02FD (CREATE_ATTRACT_MODE_ALIEN_SWARM)\r
+      14 06         ; $0614 (HANDLE_SPAWN_PLAYER)\r
+      61 06         ; $0661 (HANDLE_MAIN_GAME_LOGIC)\r
+      D8 06         ; $06D8 (HANDLE_PLAYER_ONE_KILLED)\r
+      2E 03         ; $032E (WAIT_FOR_TEMP_COUNTER_2_THEN_ADVANCE_TO_NEXT_STAGE)\r
+      22 03         ; $0322 (SET_SCRIPT_STAGE_TO_1)\r
+      00 00         \r
+\r
+\r
+\r
+;\r
+; Enables starfield, displays GAME OVER and the amount of credit remaining.\r
+;\r
+;\r
+\r
+DISPLAY_GAME_OVER_AND_REMAINING_CREDIT_1:      \r
+018C: 11 01 07      ld   de,$0701            ; command: BOTTOM_OF_SCREEN_INFO_COMMAND, parameter: 1 (invokes DISPLAY_AVAILABLE_CREDIT)\r
+018F: CD F2 08      call $08F2               ; call QUEUE_COMMAND\r
+0192: 11 00 06      ld   de,$0600            ; command: PRINT_TEXT, parameter: 0 (index of GAME OVER)\r
+0195: CD F2 08      call $08F2               ; call QUEUE_COMMAND\r
+0198: 3E 01         ld   a,$01\r
+019A: 32 07 40      ld   ($4007),a           ; set IS_GAME_OVER flag\r
+019D: 32 04 70      ld   ($7004),a           ; enable stars\r
+01A0: 32 02 70      ld   ($7002),a           ; Does nothing      \r
+01A3: 32 03 70      ld   ($7003),a           ; Does nothing\r
+01A6: 21 0A 40      ld   hl,$400A            ; pointer to SCRIPT_STAGE\r
+01A9: 34            inc  (hl)                ; advance to next stage\r
+01AA: AF            xor  a\r
+01AB: 32 19 40      ld   ($4019),a           ; clear PUSH_START_BUTTON_COUNTER\r
+01AE: 32 0D 40      ld   ($400D),a           ; set CURRENT_PLAYER to 0 (player one)\r
+01B1: 32 0E 40      ld   ($400E),a           ; clear IS_TWO_PLAYER_GAME\r
+01B4: 32 06 40      ld   ($4006),a           ; clear IS_GAME_IN_PLAY\r
+01B7: 21 60 10      ld   hl,$1060\r
+01BA: 22 08 40      ld   ($4008),hl          ; set TEMP_COUNTER_1 and TEMP_COUNTER_2 \r
+01BD: C9            ret\r
+\r
+\r
+;\r
+;\r
+;\r
+\r
+SET_PUSH_START_BUTTON_COUNTER:\r
+01BE: 3E 01         ld   a,$01\r
+01C0: 32 19 40      ld   ($4019),a           ; set PUSH_START_BUTTON_COUNTER\r
+01C3: C3 36 03      jp   $0336               ; jump to WAIT_FOR_TEMP_COUNTERS\r
+\r
+\r
+;\r
+;\r
+;\r
+\r
+HIDE_SWARM_AND_PREPARE_TO_CLEAR_SCREEN:\r
+01C6: 21 00 41      ld   hl,$4100            ; load HL with address of ALIEN_SWARM_FLAGS\r
+01C9: 06 80         ld   b,$80               ; sizeof(ALIEN_SWARM_FLAGS) array\r
+01CB: AF            xor  a                   \r
+01CC: D7            rst  $10                 ; Clear all alien swarm flags \r
+01CD: 32 5F 42      ld   ($425F),a           ; set TIMING_VARIABLE\r
+01D0: 32 24 42      ld   ($4224),a           ; reset HAVE_AGGRESSIVE_ALIENS flag\r
+01D3: 21 02 50      ld   hl,$5002            ; address of column 2 in character RAM\r
+01D6: 22 0B 40      ld   ($400B),hl          ; write to TEMP_CHAR_RAM_PTR\r
+01D9: 21 09 40      ld   hl,$4009            ; load HL with address of TEMP_COUNTER_2\r
+01DC: 36 20         ld   (hl),$20            ; set counter \r
+01DE: 2C            inc  l                   ; bump HL to $400A (SCRIPT_STAGE)\r
+01DF: 34            inc  (hl)                ; advance to next part of script.\r
+01E0: C9            ret\r
+\r
+\r
+;\r
+; This piece of code clears all of the screen except the HUD (score, credits inserted etc)\r
+;\r
+\r
+CLEAR_SCREEN_BEFORE_WE_ARE_THE_GALAXIANS_INTRO:\r
+01E1: 2A 0B 40      ld   hl,($400B)          ; load HL with contents of TEMP_CHAR_RAM_PTR\r
+01E4: 06 1C         ld   b,$1C               ; We want to clear #$1C (28 characters) on this row \r
+01E6: 3E 10         ld   a,$10               ; ordinal for empty character\r
+01E8: D7            rst  $10                 ; Clear 28 characters from row\r
+01E9: 11 04 00      ld   de,$0004            ; As a row is 32 characters wide, to get to start of next row...\r
+01EC: 19            add  hl,de               ; ... we need to add 4 characters.\r
+01ED: 22 0B 40      ld   ($400B),hl          ; write to TEMP_CHAR_RAM_PTR\r
+01F0: 21 09 40      ld   hl,$4009            ; load HL with address of TEMP_COUNTER_2  \r
+01F3: 35            dec  (hl)                ; decrement value of counter\r
+01F4: C0            ret  nz                  ; if value is not zero then exit \r
+\r
+01F5: 2C            inc  l                   ; bump HL to $400A (SCRIPT_STAGE)\r
+01F6: 34            inc  (hl)                ; advance to next part of script.\r
+01F7: 21 40 04      ld   hl,$0440\r
+01FA: 22 08 40      ld   ($4008),hl          ; set TEMP_COUNTER_1 and TEMP_COUNTER_2 in one go\r
+\r
+01FD: AF            xor  a\r
+01FE: 06 30         ld   b,$30\r
+0200: 21 00 42      ld   hl,$4200\r
+0203: D7            rst  $10                 ; Clear from $4200-422F\r
+0204: 32 06 70      ld   ($7006),a           ; reset regen hflip\r
+0207: 32 07 70      ld   ($7007),a           ; reset regen vflip\r
+020A: 32 18 40      ld   ($4018),a           ; reset DISPLAY_IS_COCKTAIL_P2\r
+020D: 3E 01         ld   a,$01\r
+020F: 32 38 42      ld   ($4238),a           ; set DISABLE_SWARM_ANIMATION flag. \r
+0212: 21 B1 1D      ld   hl,$1DB1            ; pointer to COLOUR_ATTRIBUTE_TABLE_3\r
+0215: C3 98 05      jp   $0598               ; jump to SET_COLOUR_ATTRIBUTES\r
+\r
+\r
+;\r
+; Displays the following:\r
+;\r
+; WE ARE THE GALAXIANS\r
+; MISSION: DESTROY ALIENS\r
+; - SCORE ADVANCE TABLE -\r
+; CONVOY CHARGER\r
+\r
+DISPLAY_WE_ARE_THE_GALAXIANS_INTRO:\r
+0218: CD 63 03      call $0363               ; call HANDLE_ALIEN_SWARM_SCROLL_RESET\r
+021B: 21 08 40      ld   hl,$4008            ; load HL with address of TEMP_COUNTER_1\r
+021E: 35            dec  (hl)\r
+021F: C0            ret  nz\r
+0220: 36 50         ld   (hl),$50            ; reset counter\r
+0222: 2C            inc  l                   ; bump HL to TEMP_COUNTER_2\r
+0223: 16 06         ld   d,$06               ; Command: PRINT_TEXT\r
+\r
+; HL now points to a number between 1 and 4. This identifies a text string we want to print: \r
+; 1: CONVOY CHARGER             \r
+; 2: SCORE ADVANCE TABLE\r
+; 3: MISSION: DESTROY ALIENS    \r
+; 4: WE ARE THE GALAXIANS\r
+0225: 7E            ld   a,(hl)              ; read value from TEMP_COUNTER_2 \r
+0226: 82            add  a,d                 ; add 6 to it to give us an index for PRINT_TEXT\r
+0227: 5F            ld   e,a                 ; set parameter\r
+0228: CD F2 08      call $08F2               ; call QUEUE_COMMAND\r
+022B: 35            dec  (hl)                ; bump TEMP_COUNTER_2 to index of next string to print\r
+022C: C0            ret  nz\r
+\r
+022D: 2C            inc  l                   ; bump HL to $400A (SCRIPT_STAGE)\r
+022E: 34            inc  (hl)                ; advance to next stage\r
+022F: 21 20 04      ld   hl,$0420\r
+0232: 22 08 40      ld   ($4008),hl          ; set TEMP_COUNTER_1 and TEMP_COUNTER_2 in one go\r
+\r
+; hide all sprites \r
+0235: 21 B0 42      ld   hl,$42B0            ; address of INFLIGHT_ALIENS\r
+0238: AF            xor  a\r
+0239: 47            ld   b,a\r
+023A: D7            rst  $10                 ; Fill the entire INFLIGHT_ALIENS array with zero\r
+023B: 32 41 42      ld   ($4241),a           ; set ATTRACT_MODE_SCROLL_ID\r
+023E: C9            ret\r
+\r
+\r
+\r
+;\r
+;\r
+;\r
+;\r
+\r
+SCROLL_ON_CONVOY_CHARGER_POINTS:\r
+023F: CD 63 03      call $0363               ; call HANDLE_ALIEN_SWARM_SCROLL_RESET\r
+0242: CD BE 0B      call $0BBE               ; call HANDLE_INFLIGHT_ALIEN_SPRITE_UPDATE\r
+0245: CD C3 0C      call $0CC3               ; call HANDLE_INFLIGHT_ALIENS\r
+0248: CD 67 03      call $0367               ; call HANDLE_DRAW_CONVOY_CHARGER_POINTS\r
+024B: 21 08 40      ld   hl,$4008            ; load HL with address of TEMP_COUNTER_1\r
+024E: 35            dec  (hl)\r
+024F: C0            ret  nz\r
+0250: 36 D2         ld   (hl),$D2\r
+\r
+; get ready to scroll the next alien sprite and associated points values on screen\r
+0252: 2C            inc  l                   ; bump HL to point to TEMP_COUNTER_2\r
+0253: CD 41 03      call $0341               ; call INIT_CONVOY_CHARGER_SPRITE\r
+0256: EB            ex   de,hl\r
+0257: 21 41 42      ld   hl,$4241            ; pointer to ATTRACT_MODE_SCROLL_ID\r
+025A: 34            inc  (hl)                ; set id to next thing to scroll on\r
+025B: EB            ex   de,hl\r
+025C: 35            dec  (hl)                ; dec TEMP_COUNTER_2\r
+025D: C0            ret  nz                  ; return if not zero\r
+\r
+025E: 36 D2         ld   (hl),$D2\r
+0260: 2C            inc  l                   ; point HL to SCRIPT_STAGE\r
+0261: 34            inc  (hl)                ; increment script stage \r
+0262: AF            xor  a\r
+0263: 32 58 40      ld   ($4058),a           ; write to scroll offset in OBJRAM_BACK_BUF\r
+0266: C9            ret\r
+\r
+\r
+\r
+DISPLAY_NAMCO_LOGO:\r
+0267: CD 63 03      call $0363               ; call HANDLE_ALIEN_SWARM_SCROLL_RESET\r
+026A: CD BE 0B      call $0BBE               ; call HANDLE_INFLIGHT_ALIEN_SPRITE_UPDATE\r
+026D: CD C3 0C      call $0CC3               ; call HANDLE_INFLIGHT_ALIENS \r
+0270: CD 67 03      call $0367               ; call HANDLE_DRAW_CONVOY_CHARGER_POINTS\r
+\r
+; wait until TEMP_COUNTER_2 reaches 0\r
+0273: 21 09 40      ld   hl,$4009            ; load HL with address of TEMP_COUNTER_2\r
+0276: 35            dec  (hl)\r
+0277: C0            ret  nz\r
+0278: 2C            inc  l                   ; bump HL to point to SCRIPT_STAGE\r
+0279: 34            inc  (hl)                ; advance to next stage\r
+027A: AF            xor  a\r
+027B: 32 58 40      ld   ($4058),a           ; set scroll offset in OBJRAM_BACK_BUF\r
+027E: 21 40 11      ld   hl,$1140\r
+0281: 22 08 40      ld   ($4008),hl          ; set TEMP_COUNTER_1 and TEMP_COUNTER_2 in one go\r
+0284: 21 41 42      ld   hl,$4241            ; pointer to ATTRACT_MODE_SCROLL_ID\r
+0287: 34            inc  (hl)\r
+0288: 11 0F 06      ld   de,$060F            ; command: PRINT_TEXT, parameter: #$0F (Displays NAMCO logo)\r
+028B: C3 F2 08      jp   $08F2               ; jump to QUEUE COMMAND               \r
+\r
+\r
+;\r
+;\r
+;\r
+;\r
+;\r
+\r
+BLINK_CONVOY_CHARGER_POINTS:\r
+028E: CD 63 03      call $0363               ; call HANDLE_ALIEN_SWARM_SCROLL_RESET\r
+0291: CD BE 0B      call $0BBE               ; call HANDLE_INFLIGHT_ALIEN_SPRITE_UPDATE\r
+0294: CD C3 0C      call $0CC3               ; call HANDLE_INFLIGHT_ALIENS\r
+0297: CD 67 03      call $0367               ; call DISPLAY_CONVOY_CHARGER_POINTS\r
+029A: C3 36 03      jp   $0336               ; jump to WAIT_FOR_TEMP_COUNTERS\r
+\r
+\r
+\r
+;\r
+; Cleanup for the "WE ARE THE GALAXIANS" page.\r
+;\r
+; Clears the screen. Hides the sprites. Displays GAME OVER.\r
+;\r
+\r
+CLEAR_WE_ARE_GALAXIANS_SCREEN_AND_DISPLAY_GAME_OVER:\r
+029D: CD 63 03      call $0363               ; call HANDLE_ALIEN_SWARM_SCROLL_RESET\r
+; clear everything except HUD\r
+02A0: 2A 0B 40      ld   hl,($400B)          ; read contents of TEMP_CHAR_RAM_PTR. Now HL = pointer to row to clear \r
+02A3: 06 1C         ld   b,$1C               ; we want to clear $1C (28 decimal) characters\r
+02A5: 3E 10         ld   a,$10               ; ordinal of empty character\r
+02A7: D7            rst  $10                 ; Clear 28 characters on column   \r
+02A8: 11 04 00      ld   de,$0004            ; As the screen is 32 characters wide, we need to add 4 to get to...\r
+02AB: 19            add  hl,de               ; the start of the next column\r
+02AC: 22 0B 40      ld   ($400B),hl          ; Update TEMP_CHAR_RAM_PTR \r
+\r
+; wait until TEMP_COUNTER_2 reaches 0\r
+02AF: 21 09 40      ld   hl,$4009            ; load HL with address of TEMP_COUNTER_2\r
+02B2: 35            dec  (hl)\r
+02B3: C0            ret  nz\r
+02B4: 2C            inc  l                   ; bump HL to point to SCRIPT_STAGE\r
+02B5: 34            inc  (hl)                ; advance to next stage of script\r
+\r
+; clear INFLIGHT_ALIENS array. \r
+02B6: 21 B0 42      ld   hl,$42B0            ; load HL with address of INFLIGHT_ALIENS\r
+02B9: AF            xor  a\r
+02BA: 47            ld   b,a\r
+02BB: D7            rst  $10                 ; Fill B bytes of memory from HL with value in A.  \r
+\r
+; hide all sprites & bullets\r
+02BC: 21 60 40      ld   hl,$4060            ; pointer to OBJRAM_BACK_BUF_SPRITES\r
+02BF: 06 40         ld   b,$40               ; \r
+02C1: D7            rst  $10                 ; clear sprite and bullet information from back buffer  \r
+\r
+; display GAME OVER\r
+02C2: 21 40 04      ld   hl,$0440 \r
+02C5: 22 08 40      ld   ($4008),hl          ; set TEMP_COUNTER_1 and TEMP_COUNTER_2\r
+02C8: CD 95 05      call $0595               ; call SET_COLOUR_ATTRIBUTES_TABLE_1\r
+02CB: 11 00 06      ld   de,$0600            ; command: PRINT_TEXT, parameter: 0 (index of "GAME OVER")\r
+02CE: C3 F2 08      jp   $08F2               ; jump to QUEUE COMMAND               \r
+\r
+\r
+\r
+;\r
+; Displays GAME over and the amount of credit remaining.\r
+;\r
+; See also: DISPLAY_GAME_OVER_AND_REMAINING_CREDIT_1 @ $018C\r
+;\r
+\r
+DISPLAY_GAME_OVER_AND_REMAINING_CREDIT_2:\r
+02D1: 11 01 07      ld   de,$0701            ; command: BOTTOM_OF_SCREEN_INFO_COMMAND , calls DISPLAY_AVAILABLE_CREDIT\r
+02D4: CD F2 08      call $08F2               ; call QUEUE_COMMAND\r
+02D7: 11 00 06      ld   de,$0600            ; command: PRINT_TEXT, parameter: 0 (index of "GAME OVER")\r
+02DA: CD F2 08      call $08F2               ; call QUEUE_COMMAND\r
+02DD: 21 0A 40      ld   hl,$400A            ; load HL with address of SCRIPT_STAGE\r
+02E0: 34            inc  (hl)                ; advance to next stage of script\r
+02E1: 21 60 10      ld   hl,$1060\r
+02E4: 22 08 40      ld   ($4008),hl          ; set TEMP_COUNTER_1 and TEMP_COUNTER_2 \r
+02E7: C9            ret\r
+\r
+\r
+\r
+;\r
+; Clears the alien swarm from the screen.\r
+;\r
+;\r
+\r
+CLEAR_ALIEN_SWARM_AND_SUSPEND_SWARM_ANIMATION:\r
+02E8: 21 00 41      ld   hl,$4100            ; load HL with address of ALIEN_SWARM_FLAGS\r
+02EB: 06 80         ld   b,$80               ; sizeof(ALIEN_SWARM_FLAGS)\r
+02ED: AF            xor  a\r
+02EE: D7            rst  $10                 ; Reset all flags in ALIEN_SWARM_FLAGS array  \r
+02EF: 32 5F 42      ld   ($425F),a           ; clear TIMING_VARIABLE\r
+02F2: 32 38 42      ld   ($4238),a           ; clear DISABLE_SWARM_ANIMATION\r
+02F5: 21 09 40      ld   hl,$4009            ; load HL with address of TEMP_COUNTER_2\r
+02F8: 36 40         ld   (hl),$40\r
+02FA: C3 93 05      jp   $0593               ; advance to next stage of script\r
+\r
+\r
+\r
+;\r
+;\r
+;\r
+;\r
+;\r
+\r
+CREATE_ATTRACT_MODE_ALIEN_SWARM:\r
+02FD: 11 1B 05      ld   de,$051B            ; load DE with address of PACKED_DEFAULT_SWARM_DEFINITION\r
+0300: CD 46 06      call $0646               ; call UNPACK_ALIEN_SWARM\r
+0303: EB            ex   de,hl               ; now HL = pointer to DEFAULT_PLAYER_STATE ($052B)\r
+0304: 11 18 42      ld   de,$4218            ; load DE with address of CURRENT_PLAYER_STATE \r
+0307: 01 08 00      ld   bc,$0008            ; sizeof (CURRENT_PLAYER_STATE)\r
+030A: ED B0         ldir                     ; reset current player state to default\r
+030C: AF            xor  a\r
+030D: 32 5F 42      ld   ($425F),a           ; reset TIMING_VARIABLE\r
+\r
+; Give the demo player 1 life \r
+0310: 3C            inc  a\r
+0311: 32 1D 42      ld   ($421D),a           ; set PLAYER_LIVES\r
+0314: 21 0A 40      ld   hl,$400A            ; pointer to SCRIPT_STAGE\r
+0317: 34            inc  (hl)                ; advance to next stage of script            \r
+0318: 2C            inc  l                   ; bump HL to point to TEMP_CHAR_RAM_PTR\r
+0319: 36 96         ld   (hl),$96\r
+031B: 21 40 06      ld   hl,$0640\r
+031E: 22 45 42      ld   ($4245),hl          ; set FLAGSHIP_ATTACK_MASTER_COUNTER_1 and FLAGSHIP_ATTACK_MASTER_COUNTER_2\r
+0321: C9            ret\r
+\r
+\r
+;\r
+;\r
+;\r
+;\r
+;\r
+\r
+SET_SCRIPT_STAGE_TO_1:\r
+0322: 3E 01         ld   a,$01\r
+0324: 32 0A 40      ld   ($400A),a           ; set SCRIPT_STAGE to 1\r
+0327: 21 03 03      ld   hl,$0303            \r
+032A: 22 08 40      ld   ($4008),hl          ; set TEMP_COUNTER_1 and TEMP_COUNTER_2\r
+032D: C9            ret\r
+\r
+\r
+;\r
+; Decrements value in TEMP_COUNTER_2. When counter value hits zero, advance script to next stage.\r
+; \r
+\r
+WAIT_FOR_TEMP_COUNTER_2_THEN_ADVANCE_TO_NEXT_STAGE:\r
+032E: 21 09 40      ld   hl,$4009            ; load HL with address of TEMP_COUNTER_2\r
+0331: 35            dec  (hl)                ; decrement value of counter\r
+0332: C0            ret  nz                  ; return if counter is !=0\r
+0333: 2C            inc  l                   ; bump HL to point to $401A (SCRIPT_STAGE)\r
+0334: 34            inc  (hl)                ; advance script to next stage\r
+0335: C9            ret\r
+\r
+\r
+\r
+;\r
+; Decrements value in TEMP_COUNTER_1. When counter value hits zero, reset value of TEMP_COUNTER_1 to $3C (60 decimal)\r
+; and then decrement value of TEMP_COUNTER_2. \r
+;\r
+; When value of TEMP_COUNTER_2 hits zero, advance script to next stage.\r
+;\r
+\r
+WAIT_FOR_TEMP_COUNTERS:\r
+0336: 21 08 40      ld   hl,$4008             ; load HL with address of TEMP_COUNTER_1\r
+0339: 35            dec  (hl)                 ; decrement value of counter\r
+033A: C0            ret  nz                   ; return if counter is !=0\r
+033B: 36 3C         ld   (hl),$3C             ; reset counter to $3C (50 decimal)\r
+033D: 2C            inc  l                    ; bump HL to point to TEMP_COUNTER_2\r
+033E: C3 31 03      jp   $0331                ; and go check if that counter has counted down to 0 yet \r
+\r
+\r
+;\r
+; This routine is responsible for positioning alien sprites off screen ready to be scrolled onto the CONVOY CHARGER points table.\r
+; Once the positioning is done, the sprite is "handed over" to the routine @ $109B.\r
+;\r
+; Expects: HL points to TEMP_COUNTER_2\r
+;\r
+; The value in TEMP_COUNTER_2 specifies what type of alien we are scrolling on:\r
+;\r
+; 4: Flagship\r
+; 3: Red alien\r
+; 2: Purple alien\r
+; 1: Blue alien\r
+;\r
+\r
+INIT_CONVOY_CHARGER_SPRITE:\r
+0341: 7E            ld   a,(hl)              ; read type of alien to scroll on          \r
+0342: D9            exx                      ; note: alternate register switch doesn't affect A\r
+0343: 3D            dec  a                   ; convert A into a 0-based index                   \r
+0344: 47            ld   b,a                                   \r
+0345: 0F            rrca                     ; multiply A..\r
+0346: 0F            rrca\r
+0347: 0F            rrca                     ; ..by 32 (which is sizeof(INFLIGHT_ALIEN))\r
+0348: 5F            ld   e,a                 ;\r
+0349: 16 00         ld   d,$00               ; Now DE = offset \r
+034B: 21 30 43      ld   hl,$4330            ; HL = address of INFLIGHT_ALIENS[3]\r
+034E: 19            add  hl,de               ; Add offset to HL. HL now points to INFLIGHT_ALIEN record we're using to scroll sprite on with\r
+034F: 36 01         ld   (hl),$01            ; set INFLIGHT_ALIEN.IsActive to 1 \r
+0351: 2C            inc  l                 \r
+0352: 36 00         ld   (hl),$00            ; reset INFLIGHT_ALIEN.IsDying \r
+0354: 2C            inc  l\r
+0355: 36 0D         ld   (hl),$0D            ; set INFLIGHT_ALIEN.StageOfLife to INFLIGHT_ALIEN_CONVOY_CHARGER_SET_COLOUR_POS_ANIM\r
+0357: 2C            inc  l\r
+0358: 2C            inc  l\r
+0359: 36 00         ld   (hl),$00            ; set INFLIGHT_ALIEN.Y to position offscreen\r
+035B: 2C            inc  l\r
+035C: 36 0C         ld   (hl),$0C            ; set INFLIGHT_ALIEN.AnimationFrame\r
+035E: 2C            inc  l\r
+035F: 2C            inc  l\r
+0360: 70            ld   (hl),b              ; set INFLIGHT_ALIEN.IndexInSwarm \r
+0361: D9            exx\r
+0362: C9            ret\r
+\r
+\r
+;\r
+; The alien swarm scrolling in the attract mode has changed scroll values for some columns, \r
+; and these scroll values need to be reset before we can print text like "WE ARE THE GALAXIANS" "MISSION: DESTROY ALIENS"\r
+; in those columns. If we don't reset the scroll values, the text will probably be off-centre and not look good.\r
+;\r
+\r
+HANDLE_ALIEN_SWARM_SCROLL_RESET:\r
+0363: AF            xor  a                   ; reset scroll offset to 0\r
+0364: C3 72 09      jp   $0972               ; call SET_SWARM_SCROLL_OFFSET\r
+\r
+\r
+;\r
+; Handles the drawing and blinking of the CONVOY CHARGER points values in the demo.\r
+;\r
+\r
+DISPLAY_CONVOY_CHARGER_POINTS:\r
+0367: 3A 41 42      ld   a,($4241)           ; read ATTRACT_MODE_SCROLL_ID\r
+036A: A7            and  a                   ; test if zero\r
+036B: C8            ret  z                   ; if its zero, not time to scroll anything in yet, return\r
+036C: 3D            dec  a                   \r
+036D: C8            ret  z\r
+\r
+036E: 47            ld   b,a\r
+036F: 3A 5F 42      ld   a,($425F)           ; read TIMING_VARIABLE\r
+0372: 4F            ld   c,a                 ; save it in C \r
+0373: E6 3F         and  $3F                 ; mask in bits 0..5. Now A is a value from 0..63 decimal.\r
+0375: 28 49         jr   z,$03C0             ; if bits 0..5 are not set, call CLEAR_DEMO_CONVOY_CHARGER_POINTS\r
+\r
+0377: FE 20         cp   $20                 ; If A is not exactly $20..\r
+0379: C0            ret  nz                  ; .. exit\r
+\r
+; When we get here, we need to compute what flagship scores we are going to draw. \r
+; We basically take the value in TIMING_VARIABLE, AND the value by 3 to give an index value in range of 0..3, then\r
+; multiply the index by 3 to give an offset into the flagship score table @$039A.  \r
+; The end result is cycling flagship values.\r
+037A: 79            ld   a,c                 ; A = TIMING_VARIABLE saved in C\r
+037B: 07            rlca                     \r
+037C: 07            rlca               \r
+037D: E6 03         and  $03                 ; now A is a value from 0..3. A is now an index into table @$039A \r
+037F: 4F            ld   c,a                 ; multiply A...\r
+0380: 87            add  a,a                 ;   \r
+0381: 81            add  a,c                 ; .. by 3\r
+0382: 5F            ld   e,a \r
+0383: 16 00         ld   d,$00               ; extend A into DE. Now DE is an offset to add to HL\r
+\r
+0385: 21 9A 03      ld   hl,$039A            ; pointer to Flagship score table\r
+0388: 19            add  hl,de\r
+0389: 11 93 51      ld   de,$5193            ; address in character RAM\r
+038C: CD AF 03      call $03AF               ; call DRAW_3_CHARACTERS\r
+038F: 05            dec  b\r
+0390: C8            ret  z\r
+\r
+; The alien scores are static and don't cycle, so we just draw them straight from the table \r
+0391: 21 A6 03      ld   hl,$03A6            ; pointer to Alien score table\r
+0394: CD AF 03      call $03AF               ; call DRAW_3_CHARACTERS\r
+0397: 10 FB         djnz $0394\r
+0399: C9            ret\r
+\r
+\r
+; The tables @$039A and $03A6 represent the points values displayed beneath the SCORE ADVANCE TABLE.\r
+; These values are NOT BCD! They are ordinals for characters to be POKEd directly to character RAM.\r
+\r
+; This table is for the Flagship\r
+039A: \r
+01 05 00            ; 150 \r
+02 00 00            ; 200\r
+03 00 00            ; 300\r
+08 00 00            ; 800\r
+\r
+; This table is for the normal aliens\r
+; Note: 10 is a space (empty) character\r
+03A6: \r
+01 00 00            ; 100   \r
+10 08 00            ;  80   \r
+10 06 00            ;  60    \r
+\r
+\r
+;\r
+; Draw 3 characters in the same *column*.\r
+; Because the Galaxian monitor is turned on its side, the characters look like they are on the same row.\r
+; \r
+; Expects:\r
+; HL to point to 3 bytes defining the characters to draw \r
+; DE to point to character RAM to draw to\r
+;\r
+\r
+DRAW_3_CHARACTERS:\r
+03AF: 0E 03         ld   c,$03               ; number of characters to draw       \r
+03B1: 7E            ld   a,(hl)              ; read character to draw  \r
+03B2: 12            ld   (de),a              ; write to character RAM\r
+03B3: 23            inc  hl                  ; bump to next character\r
+03B4: 7B            ld   a,e                 ; get LSB of character RAM address \r
+03B5: D6 20         sub  $20                 ; subtract #$20 (32 decimal) from it. Now DE points to character in same column, row above \r
+03B7: 5F            ld   e,a                 ; \r
+03B8: 0D            dec  c                   ; decrement counter of characters to draw. \r
+03B9: C2 B1 03      jp   nz,$03B1            ; if counter !=0, more characters are to be drawn, goto $03B1\r
+03BC: C6 62         add  a,$62               \r
+03BE: 5F            ld   e,a                 ; Add #$62 (98 decimal) to DE. Now DE is back on row we started drawing from.\r
+03BF: C9            ret\r
+\r
+\r
+;\r
+; In the demo mode, this erases all of the points values underneath the text "CHARGER"\r
+;\r
+\r
+CLEAR_DEMO_CONVOY_CHARGER_POINTS:\r
+03C0: 21 93 51      ld   hl,$5193            ; address in character RAM\r
+03C3: 11 E0 FF      ld   de,$FFE0            ; offset to add to character RAM address  (-32 decimal.)\r
+03C6: 0E 03         ld   c,$03               ; 3 characters to erase\r
+03C8: 3E 10         ld   a,$10               ; ordinal of empty character\r
+03CA: 77            ld   (hl),a              ; write empty character to screen       \r
+03CB: 19            add  hl,de               ; add offset. HL now points to character a row above, same column               \r
+03CC: 0D            dec  c                   ; decrement count of characters to erase\r
+03CD: C2 CA 03      jp   nz,$03CA            ; if not done goto $03CA\r
+03D0: 7D            ld   a,l\r
+03D1: C6 62         add  a,$62\r
+03D3: 6F            ld   l,a\r
+03D4: 10 F0         djnz $03C6\r
+03D6: C9            ret\r
+\r
+\r
+;\r
+;\r
+;\r
+;\r
+;\r
+\r
+03D7: 3A 02 40      ld   a,($4002)           ; read NUM_CREDITS\r
+03DA: A7            and  a                   ; check if zero\r
+03DB: C8            ret  z                   ; return if no credits\r
+\r
+03DC: 21 05 40      ld   hl,$4005            ; load HL with address of SCRIPT_NUMBER\r
+03DF: 34            inc  (hl)                ; advance to next script\r
+03E0: 2C            inc  l              \r
+03E1: 2C            inc  l                   ; bump HL to point to IS_GAME_OVER\r
+03E2: 36 00         ld   (hl),$00            ; set IS_GAME_OVER to 0\r
+03E4: AF            xor  a\r
+03E5: 32 0A 40      ld   ($400A),a           ; set SCRIPT_STAGE to 0\r
+03E8: 32 C2 41      ld   ($41C2),a           ; set ENABLE_ALIEN_ATTACK_SOUND to 0\r
+03EB: 32 DF 41      ld   ($41DF),a           ; set ALIEN_DEATH_SOUND to 0\r
+03EE: 32 B0 40      ld   ($40B0),a           ; clear IS_COLUMN_SCROLLING flag\r
+03F1: C9            ret\r
+\r
+\r
+\r
+;\r
+; Credit has been inserted. Now wait for a start button to be pushed.\r
+;\r
+;\r
+;\r
+\r
+SCRIPT_TWO:\r
+03F2: CD 0D 09      call $090D               ; call HANDLE_SWARM_MOVEMENT\r
+03F5: CD 8E 09      call $098E               ; call SET_ALIEN_PRESENCE_FLAGS\r
+03F8: 21 92 04      ld   hl,$0492            ; address of HANDLE_START_BUTTONS\r
+03FB: E5            push hl\r
+03FC: 3A 0A 40      ld   a,($400A)           ; read SCRIPT_STAGE\r
+03FF: EF            rst  $28                 ; jump to code @ $0400 + (A*2)\r
+\r
+0400: \r
+    08 04           ; $0408                  ; INIT_0408\r
+    30 04           ; $0430                  ; WAIT_BEFORE_DISPLAYING_PUSH_START_BUTTON\r
+    43 04           ; $0443                  ; DISPLAY_PUSH_START_BUTTON_AND_BONUS_GALIXIP_FOR\r
+    73 04           ; $0473                  ; BLINK_LAMPS_IF_CREDIT_INSERTED\r
+\r
+\r
+\r
+\r
+\r
+INIT_0408:\r
+0408: 21 91 1D      ld   hl,$1D91            ; pointer to COLOUR_ATTRIBUTE_TABLE_2\r
+040B: CD 98 05      call $0598               ; call SET_COLOUR_ATTRIBUTES\r
+\r
+; hide all sprites\r
+040E: 21 60 40      ld   hl,$4060            ; load HL with address of OBJRAM_BACK_BUF_SPRITES\r
+0411: 06 40         ld   b,$40               \r
+0413: AF            xor  a\r
+0414: D7            rst  $10                 ; Fill B bytes of memory from HL with value in A. \r
+\r
+; hide all bullets and inflight aliens\r
+0415: 21 60 42      ld   hl,$4260            ; load HL with address of ENEMY_BULLETS_START\r
+0418: D7            rst  $10                 ; Fill B bytes of memory from HL with value in A.  \r
+0419: 06 50         ld   b,$50\r
+041B: D7            rst  $10                 ; Fill B bytes of memory from HL with value in A. \r
+\r
+041C: 32 38 42      ld   ($4238),a           ; clear DISABLE_SWARM_ANIMATION     \r
+041F: 32 B0 40      ld   ($40B0),a           ; clear IS_COLUMN_SCROLLING  \r
+0422: 21 02 50      ld   hl,$5002            ; point to address in character RAM (3rd character of top row)\r
+0425: 22 0B 40      ld   ($400B),hl          ; set TEMP_CHAR_RAM_PTR\r
+0428: 21 09 40      ld   hl,$4009            ; load HL with address of TEMP_COUNTER_2\r
+042B: 36 10         ld   (hl),$10            ; set countdown to $10 (16 decimal)\r
+042D: 2C            inc  l                   ; bump HL to point to SCRIPT_STAGE\r
+042E: 34            inc  (hl)                ; advance to next stage of script\r
+042F: C9            ret\r
+\r
+\r
+\r
+WAIT_BEFORE_DISPLAYING_PUSH_START_BUTTON:\r
+0430: 21 19 40      ld   hl,$4019            ; pointer to PUSH_START_BUTTON_COUNTER\r
+0433: 35            dec  (hl)                ; decrement counter\r
+0434: C2 73 04      jp   nz,$0473            ; if non zero, goto BLINK_LAMPS_IF_CREDIT_INSERTED \r
+\r
+; counter's hit zero, go to next stage of script.\r
+0437: 21 0A 40      ld   hl,$400A            ; pointer to SCRIPT_STAGE\r
+043A: 34            inc  (hl)                ; advance to next stage of script\r
+043B: 21 00 41      ld   hl,$4100            ; load HL with address of ALIEN_SWARM_FLAGS\r
+043E: 06 80         ld   b,$80               ; sizeof(ALIEN_SWARM_FLAGS)\r
+0440: AF            xor  a                  \r
+0441: D7            rst  $10                 ; Fill B bytes of memory from HL with value in A.  \r
+0442: C9            ret\r
+\r
+\r
+\r
+\r
+DISPLAY_PUSH_START_BUTTON_AND_BONUS_GALIXIP_FOR:\r
+0443: 2A 0B 40      ld   hl,($400B)          ; Get pointer to row to clear from TEMP_CHAR_RAM_PTR\r
+0446: 06 1C         ld   b,$1C               ; #$1C (28 decimal) characters to clear\r
+0448: 3E 10         ld   a,$10               ; Ordinal for empty space character\r
+044A: D7            rst  $10                 ; Fill B bytes of memory from HL with value in A. \r
+044B: 11 04 00      ld   de,$0004            ; We've done 28 characters. Need 4 more to move to next row.\r
+044E: 19            add  hl,de               ; Adjust HL to point to start of next row\r
+044F: 06 1C         ld   b,$1C               ; #$1C (28 decimal) characters to clear\r
+0451: D7            rst  $10                 ; Fill B bytes of memory from HL with value in A.  \r
+0452: 19            add  hl,de               ; Adjust HL to point to start of next row\r
+0453: 22 0B 40      ld   ($400B),hl          ; And update TEMP_CHAR_RAM_PTR pointer \r
+\r
+; wait for TEMP_COUNTER_2 to reach 0\r
+0456: 21 09 40      ld   hl,$4009            ; load HL with address of TEMP_COUNTER_2\r
+0459: 35            dec  (hl)\r
+045A: C0            ret  nz\r
+\r
+; then move to next stage of script\r
+045B: 2C            inc  l                   ; bump HL to point to SCRIPT_STAGE\r
+045C: 34            inc  (hl)                ; advance to next stage of script\r
+\r
+; reset screen orientation to defaults                                                          \r
+045D: AF            xor  a\r
+045E: 32 06 70      ld   ($7006),a           ; set regen hflip\r
+0461: 32 07 70      ld   ($7007),a           ; set regen vflip\r
+0464: 32 18 40      ld   ($4018),a           ; set DISPLAY_IS_COCKTAIL_P2\r
+\r
+; display BONUS GALIXIP FOR and PUSH START BUTTON messages\r
+0467: 11 02 07      ld   de,$0702            ; command: BOTTOM_OF_SCREEN_INFO_COMMAND , parameter: 2 (invokes DISPLAY_BONUS_GALAXIP_FOR)\r
+046A: CD F2 08      call $08F2               ; call QUEUE_COMMAND\r
+046D: 11 01 06      ld   de,$0601            ; command: PRINT_TEXT, parameter: 1 (index of "PUSH START BUTTON")\r
+0470: CD F2 08      call $08F2               ; call QUEUE_COMMAND\r
+\r
+\r
+\r
+\r
+;\r
+; \r
+; Make the lamps on the arcade cabinet blink if credit(s) available. \r
+;\r
+;\r
+\r
+BLINK_LAMPS_IF_CREDITS:\r
+0473: 3A 5F 42      ld   a,($425F)           ; read TIMING_VARIABLE\r
+0476: E6 20         and  $20                 ; mask in bit 5\r
+0478: 28 11         jr   z,$048B             ; if bit 5 is not set, turn off the lamps\r
+\r
+; Does player have credit? If not, then don't turn the lamps on.\r
+047A: 3A 02 40      ld   a,($4002)           ; read NUM_CREDITS\r
+047D: A7            and  a                   ; test if credits is 0\r
+047E: C8            ret  z                   ; return if no credits \r
+\r
+; turn the lamps on to show you have credit\r
+047F: 47            ld   b,a\r
+0480: 3E 01         ld   a,$01\r
+0482: 32 00 60      ld   ($6000),a           ; turn on lamp 1         \r
+0485: 05            dec  b\r
+0486: C8            ret  z\r
+0487: 32 01 60      ld   ($6001),a           ; turn on lamp 2\r
+048A: C9            ret\r
+\r
+; turn lamps off\r
+048B: 32 00 60      ld   ($6000),a           ; turn on/off lamp 1\r
+048E: 32 01 60      ld   ($6001),a           ; turn on/off lamp 2\r
+0491: C9            ret\r
+\r
+\r
+\r
+\r
+;\r
+;\r
+; Handle 1P or 2P being pressed.\r
+;\r
+; Notes:\r
+; 1P = deducts 1 credit.\r
+; 2P = deducts 2 credits\r
+;\r
+\r
+HANDLE_START_BUTTONS:\r
+0492: 3A 11 40      ld   a,($4011)           ; read PORT_STATE_6800\r
+0495: CB 47         bit  0,a                 ; test for 1P START button being hit\r
+0497: 20 59         jr   nz,$04F2            ; if button is hit, goto 1P_START_BUTTON_HANDLER\r
+0499: CB 4F         bit  1,a                 ; test for 2P START button being hit\r
+049B: C8            ret  z\r
+\r
+; The following piece of code executes when 2P START button is pushed\r
+2P_START_BUTTON_HANDLER:\r
+049C: 3A 02 40      ld   a,($4002)           ; read NUM_CREDITS\r
+049F: FE 02         cp   $02                 ; do we have at least 2 credits for a 2 player game?\r
+04A1: D8            ret  c                   ; if credits < 2, then we don't have enough credit, return\r
+04A2: D6 02         sub  $02                 ; otherwise, reduce credits by 2\r
+04A4: 32 02 40      ld   ($4002),a           ; and update NUM_CREDITS with remainder\r
+\r
+; initialise player 2's state (lives etc) to defaults\r
+04A7: 21 1B 05      ld   hl,$051B            ; load HL with address of DEFAULT_PLAYER_STATE\r
+04AA: 11 A0 41      ld   de,$41A0            ; load DE with address of PLAYER_TWO_STATE\r
+04AD: 01 20 00      ld   bc,$0020            ; sizeof (PLAYER_TWO_STATE)\r
+04B0: ED B0         ldir\r
+\r
+; if dip switch 5 is on, then player 2 gets 3 lives\r
+04B2: 3A 1F 40      ld   a,($401F)           ; read DIP_SWITCH_5_STATE\r
+04B5: 0F            rrca                     ; move bit 0 into carry\r
+04B6: DC 0F 05      call c,$050F             ; if carry is set, call AWARD_PLAYER_TWO_THREE_LIVES\r
+04B9: 21 00 01      ld   hl,$0100            ; CURRENT_PLAYER will be set to 0, IS_TWO_PLAYER_GAME set to 1\r
+04BC: 22 0D 40      ld   ($400D),hl          ; set CURRENT_PLAYER, and IS_TWO_PLAYER_GAME \r
+\r
+; Create alien swarm and load default settings for player 1 \r
+04BF: 21 1B 05      ld   hl,$051B            ; load HL with address of PACKED_DEFAULT_SWARM_DEFINITION\r
+04C2: 11 80 41      ld   de,$4180            ; load DE with address of PLAYER_ONE_PACKED_SWARM_DEF\r
+04C5: 01 20 00      ld   bc,$0020            ; sizeof (PLAYER_ONE_PACKED_SWARM_DEF)+sizeof(PLAYER_ONE_STATE)\r
+04C8: ED B0         ldir\r
+\r
+; if dip switch 5 is on, then player 1 gets 3 lives\r
+04CA: 3A 1F 40      ld   a,($401F)           ; read DIP_SWITCH_5_STATE\r
+04CD: 0F            rrca                     ; move bit 0 into carry\r
+04CE: DC 15 05      call c,$0515             ; if carry is set, call AWARD_PLAYER_ONE_THREE_LIVES\r
+\r
+04D1: AF            xor  a\r
+04D2: 32 0A 40      ld   ($400A),a           ; set SCRIPT_STAGE to 0\r
+04D5: 3E 03         ld   a,$03\r
+04D7: 32 05 40      ld   ($4005),a           ; set SCRIPT_NUMBER to 3\r
+04DA: 3E 01         ld   a,$01\r
+04DC: 32 06 40      ld   ($4006),a           ; set IS_GAME_IN_PLAY\r
+04DF: 32 D1 41      ld   ($41D1),a\r
+\r
+; Display high score, reset P1 and P2's score to 0\r
+04E2: 11 04 06      ld   de,$0604            ; command: PRINT_TEXT, parameter: 4 (index of "HIGH SCORE")\r
+04E5: CD F2 08      call $08F2               ; call QUEUE_COMMAND\r
+04E8: 11 00 04      ld   de,$0400            ; command: RESET_SCORE_COMMAND, parameter: 0 (reset player 1's score and clear life awarded)\r
+04EB: CD F2 08      call $08F2               ; call QUEUE_COMMAND\r
+04EE: 1C            inc  e                   ; command: RESET_SCORE_COMMAND, parameter: 1 (reset player 2's score and clear life awarded)\r
+04EF: C3 F2 08      jp   $08F2               ; jump to QUEUE COMMAND\r
+\r
+\r
+;\r
+; Called when 1P START is pressed\r
+;\r
+\r
+1P_START_BUTTON_HANDLER:\r
+04F2: 3A 02 40      ld   a,($4002)           ; read number of credits\r
+04F5: A7            and  a                   ; test if number of credits is zero\r
+04F6: 28 11         jr   z,$0509             ; if number of credits is zero, goto $0509\r
+04F8: 3D            dec  a                   ; otherwise reduce number of credits by 1\r
+04F9: 32 02 40      ld   ($4002),a           ; and store back in credit counter\r
+04FC: 21 A0 41      ld   hl,$41A0            ; load HL with address of PLAYER_TWO_PACKED_SWARM_DEF\r
+04FF: 06 20         ld   b,$20               ; sizeof(PLAYER_TWO_PACKED_SWARM_DEF) + sizeof(PLAYER_TWO_STATE)\r
+0501: AF            xor  a                   ; \r
+0502: D7            rst  $10                 ; clear player 2 swarm & state as we're not using them\r
+0503: 21 00 00      ld   hl,$0000            ; CURRENT_PLAYER will be set to 0, IS_TWO_PLAYER_GAME set to 0\r
+0506: C3 BC 04      jp   $04BC               ; go set CURRENT_PLAYER, and IS_TWO_PLAYER_GAME.\r
+\r
+0509: 3E 01         ld   a,$01\r
+050B: 32 05 40      ld   ($4005),a           ; set SCRIPT_NUMBER to 1\r
+050E: C9            ret\r
+\r
+\r
+\r
+; Only called if Dip Switch 5 is set to ON\r
+AWARD_PLAYER_TWO_THREE_LIVES:\r
+050F: 3E 03         ld   a,$03\r
+0511: 32 B5 41      ld   ($41B5),a           ; Set number of lives for player 2\r
+0514: C9            ret\r
+\r
+\r
+; Only called if Dip Switch 5 is set to ON\r
+AWARD_PLAYER_ONE_THREE_LIVES:\r
+0515: 3E 03         ld   a,$03\r
+0517: 32 95 41      ld   ($4195),a           ; Set number of lives for player 1\r
+051A: C9            ret\r
+\r
+\r
+\r
+DEFAULT_SWARM_DEFINITION_AND_PLAYER_STATE    ; EQU $051B:\r
+    PACKED_DEFAULT_SWARM_DEFINITION:\r
+    ; The first 16 bytes defining the default alien swarm. \r
+    ; For information on how the bytes are unpacked, please see docs @ $0646\r
+    051B: 00 00 00 00 F8 1F F8 1F F8 1F F0 0F E0 07 40 02            \r
+\r
+    ; When starting a new game, these are the default values \r
+    DEFAULT_PLAYER_STATE:\r
+    052B: 3C                                 ; Default value for DIFFICULTY_COUNTER_1 \r
+    052C: 14                                 ; Default value for DIFFICULTY_COUNTER_2\r
+    00 02 00 02 00 0F 00 00 00            \r
+\r
+\r
+;\r
+; This script is responsible for managing PLAYER 1's game. \r
+;\r
+;\r
+;\r
+\r
+SCRIPT_THREE:\r
+0536: CD 0D 09      call $090D               ; call HANDLE_SWARM_MOVEMENT\r
+0539: CD 8E 09      call $098E               ; call SET_ALIEN_PRESENCE_FLAGS\r
+053C: 3A 0A 40      ld   a,($400A)\r
+053F: EF            rst  $28                 ; jump to code @ $0540 + (A*2)\r
+0540: \r
+      50 05         ; $0550   \r
+      83 05         ; $0583 (CLEAR_ROW_OF_SCREEN)\r
+      A5 05         ; $05A5 (PLAYER_ONE_INIT)\r
+      05 06         ; $0605 (CLEAR_PLAYER_TEXT)\r
+      14 06         ; $0614 (HANDLE_SPAWN_PLAYER)\r
+      61 06         ; $0661 (HANDLE_MAIN_GAME_LOGIC)                  \r
+      D8 06         ; $06D8 (HANDLE_PLAYER_ONE_KILLED)\r
+      3D 07         ; $073D (SWITCH_TO_PLAYER_TWO)\r
+\r
+\r
+;\r
+0550: 21 00 41      ld   hl,$4100            ; load HL with address of ALIEN_SWARM_FLAGS\r
+0553: 06 80         ld   b,$80               ; sizeof (ALIEN_SWARM_FLAGS)\r
+0555: AF            xor  a\r
+0556: 32 00 60      ld   ($6000),a           ; clear !DRIVER lamp 1\r
+0559: 32 01 60      ld   ($6001),a           ; clear !DRIVER lamp 2\r
+055C: D7            rst  $10                 ; Clear the alien swarm \r
+\r
+055D: 32 5F 42      ld   ($425F),a           ; set TIMING_VARIABLE\r
+\r
+0560: 21 00 42      ld   hl,$4200            ; pointer to HAS_PLAYER_SPAWNED. Also start of player state\r
+0563: 06 17         ld   b,$17               ; $17 (23 decimal) bytes to clear \r
+0565: D7            rst  $10                 ; Clear all player state    \r
+0566: 2C            inc  l\r
+0567: 06 18         ld   b,$18\r
+0569: D7            rst  $10                 ; Fill B bytes of memory from HL with value in A.\r
+056A: 21 60 42      ld   hl,$4260            ; load HL with address of ENEMY_BULLETS_START\r
+056D: 06 46         ld   b,$46\r
+056F: D7            rst  $10                 ; Fill B bytes of memory from HL with value in A.\r
+0570: 3E 01         ld   a,$01\r
+0572: 32 26 42      ld   ($4226),a           ; set HAVE_NO_INFLIGHT_ALIENS flag\r
+\r
+0575: 21 0A 40      ld   hl,$400A            ; load HL with address of SCRIPT_STAGE\r
+0578: 34            inc  (hl)                ; advance to next stage of script\r
+0579: 2D            dec  l                   ; now HL points to TEMP_COUNTER_2\r
+057A: 36 20         ld   (hl),$20            ; load counter with $20 (32 decimal)\r
+057C: 21 00 50      ld   hl,$5000            ; pointer to start of character RAM\r
+057F: 22 0B 40      ld   ($400B),hl          ; store in TEMP_CHAR_RAM_PTR\r
+0582: C9            ret\r
+\r
+\r
+\r
+CLEAR_ROW_OF_SCREEN:\r
+0583: 2A 0B 40      ld   hl,($400B)          ; read TEMP_CHAR_RAM_PTR\r
+0586: 06 20         ld   b,$20               ; $20 (32 decimal) characters in a row\r
+0588: 3E 10         ld   a,$10               ; ordinal of empty character\r
+058A: D7            rst  $10                 ; Clear row of characters\r
+058B: 22 0B 40      ld   ($400B),hl          ; update TEMP_CHAR_RAM_PTR\r
+058E: 21 09 40      ld   hl,$4009            ; load HL with address of TEMP_COUNTER_2\r
+0591: 35            dec  (hl)                ; decrement counter\r
+0592: C0            ret  nz                  ; return if not zero\r
+0593: 2C            inc  l                   ; bump HL to point to $400A (SCRIPT_STAGE)\r
+0594: 34            inc  (hl)                ; advance to next stage of script\r
+\r
+SET_COLOUR_ATTRIBUTES_TABLE_1:\r
+0595: 21 71 1D      ld   hl,$1D71            ; pointer to COLOUR_ATTRIBUTE_TABLE_1\r
+\r
+SET_COLOUR_ATTRIBUTES:\r
+0598: 11 21 40      ld   de,$4021            ; address of first attribute in OBJRAM_BACK_BUF \r
+059B: 06 20         ld   b,$20               ; we're setting attributes for all 32 columns in the row\r
+059D: 7E            ld   a,(hl)              ; read attribute value from ROM \r
+059E: 12            ld   (de),a              ; write to attribute value in OBJRAM_BACK_BUF\r
+059F: 23            inc  hl                  ; bump HL to next value in ROM\r
+05A0: 1C            inc  e                   ; Add 2 to DE..\r
+05A1: 1C            inc  e                   ; .. so that it points to the next attribute value\r
+05A2: 10 F9         djnz $059D               ; and do until b==0\r
+05A4: C9            ret\r
+\r
+\r
+;\r
+; Player one's turn is about to commence. \r
+;\r
+\r
+PLAYER_ONE_INIT:\r
+; restore alien swarm to what it was last turn \r
+05A5: 11 80 41      ld   de,$4180            ; load DE with address of PLAYER_ONE_PACKED_SWARM_DEF\r
+05A8: CD 46 06      call $0646               ; call UNPACK_ALIEN_SWARM\r
+; copy player 1's state (ie: game settings) to current player state\r
+05AB: EB            ex   de,hl               ; now HL = pointer to PLAYER_ONE_STATE\r
+05AC: 11 18 42      ld   de,$4218            ; load DE with address of CURRENT_PLAYER_STATE\r
+05AF: 01 08 00      ld   bc,$0008            ; sizeof(CURRENT_PLAYER_STATE)\r
+05B2: ED B0         ldir                     ; write P1 player state \r
+; reset any game settings \r
+05B4: AF            xor  a\r
+05B5: 32 5F 42      ld   ($425F),a           ; set TIMING_VARIABLE\r
+05B8: 32 20 42      ld   ($4220),a           ; clear HAVE_NO_ALIENS_IN_SWARM flag\r
+05BB: 32 06 70      ld   ($7006),a           ; reset regen hflip\r
+05BE: 32 07 70      ld   ($7007),a           ; reset regen vflip\r
+05C1: 32 18 40      ld   ($4018),a           ; reset DISPLAY_IS_COCKTAIL_P2\r
+05C4: 21 0A 40      ld   hl,$400A            ; load HL with address of SCRIPT_STAGE\r
+05C7: 34            inc  (hl)                ; advance to next stage in script\r
+05C8: 2D            dec  l                   ; bump HL to address of TEMP_COUNTER_2\r
+05C9: 36 96         ld   (hl),$96            ; set counter value\r
+05CB: 21 40 06      ld   hl,$0640\r
+05CE: 22 45 42      ld   ($4245),hl          ; set FLAGSHIP_ATTACK_MASTER_COUNTER_1 and FLAGSHIP_ATTACK_MASTER_COUNTER_2\r
+; if its not demo mode, display the scores\r
+05D1: 3A 06 40      ld   a,($4006)           ; read IS_GAME_IN_PLAY\r
+05D4: 0F            rrca                     ; move flag into carry\r
+05D5: D0            ret  nc                  ; return if game is not in play\r
+05D6: 3A 0E 40      ld   a,($400E)           ; read IS_TWO_PLAYER_GAME\r
+05D9: 0F            rrca                     ; move bit 0 into carry\r
+05DA: 38 20         jr   c,$05FC             ; if carry is set, we're in a 2 player game, goto $05FC\r
+; the next 2 lines are only executed if its a one player game\r
+05DC: 11 00 05      ld   de,$0500            ; command: DISPLAY_SCORE_COMMAND, parameter: 0  (Displays Player 1's score)\r
+05DF: CD F2 08      call $08F2               ; call QUEUE_COMMAND\r
+; the remaining lines are executed regardless of number of players \r
+05E2: 1E 02         ld   e,$02               ; command: DISPLAY_SCORE_COMMAND, parameter: 2  (invokes DISPLAY_HIGH_SCORE)\r
+05E4: CD F2 08      call $08F2               ; call QUEUE_COMMAND\r
+05E7: 14            inc  d                   ; command: PRINT_TEXT, parameter: 2 (index of "PLAYER ONE") \r
+05E8: CD F2 08      call $08F2               ; call QUEUE_COMMAND\r
+05EB: 1E 04         ld   e,$04               ; command: PRINT_TEXT, parameter: 4 (index of "HIGH SCORE")\r
+05ED: CD F2 08      call $08F2               ; call QUEUE_COMMAND\r
+05F0: 11 03 07      ld   de,$0703            ; command: BOTTOM_OF_SCREEN_INFO_COMMAND, calls DISPLAY_PLAYER_SHIPS_REMAINING\r
+05F3: CD F2 08      call $08F2               ; call QUEUE_COMMAND\r
+05F6: 11 00 07      ld   de,$0700            ; command: BOTTOM_OF_SCREEN_INFO_COMMAND, calls DISPLAY_LEVEL_FLAGS\r
+05F9: C3 F2 08      jp   $08F2               ; jump to QUEUE COMMAND\r
+\r
+; Called when we're in a two player game. Displays player one's score, player two's score and high score.\r
+05FC: 11 03 05      ld   de,$0503            ; command: DISPLAY_SCORE_COMMAND, parameter: 3 (invokes DISPLAY_ALL_SCORES)\r
+05FF: CD F2 08      call $08F2               ; call QUEUE_COMMAND\r
+0602: C3 E2 05      jp   $05E2               ; go display high score, ships remaining\r
+\r
+\r
+\r
+\r
+;\r
+;\r
+; Remove the text "PLAYER ONE" / "PLAYER TWO" from the screen.\r
+;\r
+;\r
+\r
+CLEAR_PLAYER_TEXT:\r
+0605: 21 09 40      ld   hl,$4009            ; load HL with address of TEMP_COUNTER_2\r
+0608: 35            dec  (hl)\r
+0609: C0            ret  nz\r
+060A: 36 14         ld   (hl),$14\r
+060C: 2C            inc  l                   ; bump HL to point to $400A (SCRIPT_STAGE)\r
+060D: 34            inc  (hl)                ; advance to next stage of script\r
+; the text "PLAYER ONE" and "PLAYER TWO" are plotted to the same character cells  \r
+060E: 11 82 06      ld   de,$0682            ; command: PRINT_TEXT, parameter: #$82 - clear "PLAYER ONE" from screen \r
+0611: C3 F2 08      jp   $08F2               ; jump to QUEUE COMMAND\r
+\r
+\r
+\r
+\r
+HANDLE_SPAWN_PLAYER:\r
+; Wait until its time to spawn the player\r
+0614: 21 09 40      ld   hl,$4009            ; load HL with address of TEMP_COUNTER_2\r
+0617: 35            dec  (hl)               \r
+0618: C0            ret  nz\r
+0619: 36 0A         ld   (hl),$0A            ; reset TEMP_COUNTER_2\r
+061B: 2C            inc  l                   ; bump HL to point to $400A (SCRIPT_STAGE)\r
+061C: 34            inc  (hl)                ; advance to next stage of script\r
+\r
+; Spawn the player\r
+061D: 21 01 00      ld   hl,$0001\r
+0620: 22 00 42      ld   ($4200),hl          ; set HAS_PLAYER_SPAWNED to 1, IS_PLAYER_DYING to 0\r
+0623: 3E 80         ld   a,$80\r
+0625: 32 02 42      ld   ($4202),a           ; set PLAYER_Y        \r
\r
+0628: 21 E3 15      ld   hl,$15E3            ; load HL with address of ALIEN_ATTACK_COUNTER_DEFAULT_VALUES\r
+062B: 11 4A 42      ld   de,$424A            ; load DE with address of ALIEN_ATTACK_COUNTERS\r
+062E: 01 10 00      ld   bc,$0010            ; sizeof(ALIEN_ATTACK_COUNTERS) array\r
+0631: ED B0         ldir                     ; reset all counters to their default values\r
+\r
+0633: AF            xor  a\r
+0634: 32 58 40      ld   ($4058),a           ; reset scroll offset in OBJRAM_BACK_BUF\r
+0637: 32 5A 40      ld   ($405A),a           ; reset scroll offset in OBJRAM_BACK_BUF\r
+\r
+; Draw lives left\r
+063A: 11 03 07      ld   de,$0703            ; command: BOTTOM_OF_SCREEN_INFO_COMMAND, calls DISPLAY_PLAYER_SHIPS_REMAINING\r
+063D: CD F2 08      call $08F2               ; call QUEUE_COMMAND\r
+0640: 11 00 02      ld   de,$0200            ; command: DISPLAY_PLAYER_COMMAND, parameter: 0 (invokes DRAW_PLAYER_SHIP)\r
+0643: C3 F2 08      jp   $08F2               ; jump to QUEUE COMMAND\r
+\r
+\r
+; This code unpacks a packed swarm definition to the ALIEN_SWARM_FLAGS array.\r
+;\r
+; Expects:\r
+; DE = pointer to 16 packed bytes that define the swarm.    \r
+;\r
+; Returns:\r
+; HL = $4180  (pointer to PLAYER_ONE_STATE)\r
+;\r
+\r
+\r
+; Let's take a look at the default swarm definition located @$051B. This is the "template" that is used to define the swarm when you\r
+; start the game or reach the next level.\r
+; \r
+; The swarm definition is as follows:\r
+; 00 00 00 00 F8 1F F8 1F F8 1F F0 0F E0 07 40 02  \r
+;\r
+; Not much there, is there? In order to understand how these bytes define the swarm, pair bytes like so:\r
+; 00 00 \r
+; 00 00 \r
+; 00 00 \r
+; 00 00 \r
+; F8 1F \r
+; F8 1F \r
+; F8 1F \r
+; F0 0F \r
+; E0 07 \r
+; 40 02  \r
+;\r
+; Next take each pair and *swap* the bytes. So "F8 1F" becomes "1F F8", "F0 0F" becomes "0F F0" and so on.\r
+; Treat the byte pairs as 16 bit WORDs, convert into their binary equivalents and you get: \r
+; 0000 ->   0000000000000000\r
+; 0000 ->   0000000000000000\r
+; 0000 ->   0000000000000000\r
+; 0000 ->   0000000000000000\r
+; 1FF8 ->   0001111111111000 \r
+; 1FF8 ->   0001111111111000 \r
+; 1FF8 ->   0001111111111000 \r
+; 0FF0 ->   0000111111110000 \r
+; 07E0 ->   0000011111100000 \r
+; 0240 ->   0000001001000000 \r
+;\r
+; Notice the pattern? That's the outline of the alien swarm upside down. See the 2 flagships?\r
+;\r
+;\r
+; for (byte b=0; b<16; b++)\r
+; {\r
+;     for (byte i=0; i<8; i++)\r
+;     {\r
+;        Test bit i of byte read from (de)\r
+;\r
+;        If bit set, write 1 to (hl) - this creates an alien in the swarm\r
+;        else write 0 to (hl)\r
+;\r
+;        increment hl\r
+;     }\r
+;     increment de\r
+; }\r
+\r
+UNPACK_ALIEN_SWARM:\r
+0646: 21 00 41      ld   hl,$4100            ; load HL with address of ALIEN_SWARM_FLAGS\r
+0649: 06 10         ld   b,$10               ; There's 16 bytes to be unpacked to 128 flags\r
+064B: 0E 01         ld   c,$01\r
+\r
+064D: 1A            ld   a,(de)              ; read from (de) \r
+064E: A1            and  c                   ; test if bit is set\r
+064F: 28 0B         jr   z,$065C             ; if bit is not set, goto $065C \r
+0651: 36 01         ld   (hl),$01            ; bit is set, write 1 to (hl) - create an alien \r
+0653: 23            inc  hl                  ; bump hl to next byte\r
+0654: CB 01         rlc  c                   ; rotate c left one bit\r
+0656: 30 F5         jr   nc,$064D            ; if bit 7 of C wasn't set, goto $064D\r
+0658: 13            inc  de                  ; move to next byte\r
+0659: 10 F2         djnz $064D               ; do until b ==0\r
+065B: C9            ret\r
+065C: 36 00         ld   (hl),$00            ; write 0 to (hl) \r
+065E: C3 53 06      jp   $0653               ; \r
+\r
+\r
+HANDLE_MAIN_GAME_LOGIC:\r
+0661: CD 37 08      call $0837               ; call HANDLE_PLAYER_MOVE\r
+0664: CD 98 08      call $0898               ; call HANDLE_PLAYER_BULLET\r
+0667: CD 74 0A      call $0A74               ; call HANDLE_ENEMY_BULLETS\r
+066A: CD C3 0C      call $0CC3               ; call HANDLE_INFLIGHT_ALIENS\r
+066D: CD BE 0B      call $0BBE               ; call HANDLE_INFLIGHT_ALIEN_SPRITE_UPDATE\r
+0670: CD 32 0A      call $0A32               ; call HANDLE_PLAYER_SHOOT\r
+0673: CD 0B 0B      call $0B0B               ; call HANDLE_SWARM_ALIEN_TO_PLAYER_BULLET_COLLISION_DETECTION\r
+0676: CD 77 0B      call $0B77               ; call HANDLE_PLAYER_TO_ENEMY_BULLET_COLLISION_DETECTION\r
+0679: CD 27 12      call $1227               ; call HANDLE_INFLIGHT_ALIEN_TO_PLAYER_BULLET_COLLISION_DETECTION\r
+067C: CD 9E 12      call $129E               ; call HANDLE_PLAYER_TO_INFLIGHT_ALIEN_COLLISION_DETECTION\r
+067F: CD E5 08      call $08E5               ; call HANDLE_PLAYER_BULLET_EXPIRED\r
+0682: CD 0C 14      call $140C               ; call HANDLE_FLAGSHIP_ATTACK\r
+0685: CD 44 13      call $1344               ; call HANDLE_SINGLE_ALIEN_ATTACK\r
+0688: CD E1 13      call $13E1               ; call SET_ALIEN_ATTACK_FLANK\r
+068B: CD F3 14      call $14F3               ; call HANDLE_LEVEL_DIFFICULTY\r
+068E: CD ED 12      call $12ED               ; call HANDLE_PLAYER_HIT\r
+0691: CD 27 13      call $1327               ; call HANDLE_PLAYER_DYING\r
+0694: CD A6 16      call $16A6               ; Doesn't actually do anything\r
+0697: CD 15 15      call $1515               ; call CHECK_IF_ALIEN_CAN_ATTACK\r
+069A: CD 55 15      call $1555               ; call UPDATE_ATTACK_COUNTERS\r
+069D: CD C3 15      call $15C3               ; call CHECK_IF_FLAGSHIP_CAN_ATTACK\r
+06A0: CD F4 15      call $15F4               ; call HANDLE_CALC_INFLIGHT_ALIEN_SHOOTING_DISTANCE\r
+06A3: CD 21 16      call $1621               ; call CHECK_IF_LEVEL_IS_COMPLETE\r
+06A6: CD 37 16      call $1637               ; call HANDLE_LEVEL_COMPLETE\r
+06A9: CD B8 16      call $16B8               ; call HANDLE_ALIEN_AGGRESSIVENESS\r
+06AC: CD 88 16      call $1688               ; call HANDLE_SHOCKED_SWARM\r
+06AF: CD 8E 19      call $198E               ; call HANDLE_SIMULATE_PLAYER_IN_ATTRACT_MODE\r
+\r
+; OK, this part of the main game loop determines under what circumstances we can end the level.\r
+; First we check the status of the player and the player bullet. Are they visible?\r
+06B2: 3A 08 42      ld   a,($4208)           ; read HAS_PLAYER_BULLET_BEEN_FIRED flag\r
+06B5: 2A 00 42      ld   hl,($4200)          ; read both HAS_PLAYER_SPAWNED and IS_PLAYER_DYING flags in one go              \r
+06B8: B4            or   h                   ; combine all three flags..\r
+06B9: B5            or   l                   ; into 1. If any of the three is set, A will be 1\r
+06BA: 0F            rrca                     ; Test if A was set to 1.\r
+06BB: D8            ret  c                   ; Return if player bullet has been fired, or player has spawned or is dying \r
+\r
+; Wait until all aliens are dead.\r
+06BC: 3A 25 42      ld   a,($4225)           ; read HAVE_NO_INFLIGHT_OR_DYING_ALIENS\r
+06BF: 0F            rrca                     ; move flag value into carry. \r
+06C0: D0            ret  nc                  ; if carry flag is not set, then that means there are still aliens attacking or going through a death animation\r
+\r
+; wait until all enemy bullets are off screen.\r
+06C1: 21 60 42      ld   hl,$4260            ; load HL with address of ENEMY_BULLETS_START\r
+06C4: 11 05 00      ld   de,$0005            ; sizeof (ENEMY_BULLET)\r
+06C7: 06 0E         ld   b,$0E               ; #$0E (14 decimal) bullets max to process\r
+06C9: AF            xor  a                   ; clear A\r
+06CA: B6            or   (hl)                ; read ENEMY_BULLET.IsActive flag. set A to 1 if bullet is active. \r
+06CB: 19            add  hl,de\r
+06CC: 10 FC         djnz $06CA               ; repeat until B==0\r
+06CE: 0F            rrca                     ; are there any bullets still active on screen? If so, carry will be set\r
+06CF: D8            ret  c                   ; yes, bullets still active, return\r
+\r
+; OK, when we get here, the level is complete. We can go to the next level. \r
+06D0: 21 09 40      ld   hl,$4009            ; load HL with address of TEMP_COUNTER_2\r
+06D3: 35            dec  (hl)\r
+06D4: C0            ret  nz\r
+06D5: 2C            inc  l                   ; bump HL to point to address of SCRIPT_STAGE\r
+06D6: 34            inc  (hl)                ; advance to next stage of script\r
+06D7: C9            ret\r
+\r
+\r
+\r
+;\r
+; Player 1 has just been killed.\r
+; \r
+; See also: $07E8\r
+                               \r
+HANDLE_PLAYER_ONE_KILLED:\r
+06D8: 21 0A 40      ld   hl,$400A            ; load HL with pointer to SCRIPT_STAGE\r
+\r
+06DB: 3A 1D 42      ld   a,($421D)           ; read PLAYER_LIVES \r
+06DE: A7            and  a                   ; test if zero\r
+06DF: 20 20         jr   nz,$0701            ; if player 1 has lives remaining, goto $0701\r
+\r
+; OK, player 1 has no lives. What about player 2?\r
+06E1: 3A B5 41      ld   a,($41B5)           ; read PLAYER_TWO_LIVES\r
+06E4: A7            and  a                   ; test if zero\r
+06E5: 28 3B         jr   z,$0722             ; if player 2 has no lives, goto GAME_OVER\r
+\r
+; player 1 has no lives. If its not a 2 player game, then its GAME OVER.\r
+06E7: 3A 0E 40      ld   a,($400E)           ; read IS_TWO_PLAYER_GAME\r
+06EA: A7            and  a                   ; test if zero\r
+06EB: 28 35         jr   z,$0722             ; if zero, it's not a 2 player game, goto GAME_OVER\r
+\r
+; OK, its a 2 player game and player 2 has some lives. \r
+06ED: 34            inc  (hl)                ; bump to next stage of the script\r
+06EE: 2D            dec  l                   ; now HL points to $4009 (TEMP_COUNTER_2)\r
+06EF: 36 82         ld   (hl),$82            ; set counter \r
+                                                       \r
+06F1: 3A 06 40      ld   a,($4006)           ; read IS_GAME_IN_PLAY\r
+06F4: 0F            rrca                     ; move flag into carry\r
+06F5: D0            ret  nc                  ; return if game is not in play\r
+\r
+; Display PLAYER ONE GAME OVER\r
+06F6: 11 02 06      ld   de,$0602            ; command: PRINT_TEXT, parameter: 2 (index of "PLAYER ONE")\r
+06F9: CD F2 08      call $08F2               ; call QUEUE_COMMAND\r
+06FC: 1E 00         ld   e,$00               ; index of "GAME OVER"\r
+06FE: C3 F2 08      jp   $08F2               ; jump to QUEUE COMMAND\r
+\r
+; Player one's been killed. Is it player two's turn now? \r
+0701: 3A B5 41      ld   a,($41B5)           ; read PLAYER_TWO_LIVES\r
+0704: A7            and  a                   ; test if zero\r
+0705: 28 0B         jr   z,$0712             ; \r
+0707: 3A 0E 40      ld   a,($400E)           ; read IS_TWO_PLAYER_GAME\r
+070A: A7            and  a                   ; test if flag is set\r
+070B: 28 05         jr   z,$0712             ; if its not a two player game goto $0712\r
+070D: 34            inc  (hl)                ; bump to next stage of the script\r
+070E: 2D            dec  l                   ; now HL points to $4009 (TEMP_COUNTER_2)\r
+070F: 36 50         ld   (hl),$50            ; set counter\r
+0711: C9            ret\r
+\r
+; If we get here, either its a single player game or player two has lost all their lives.\r
+0712: 3A 06 40      ld   a,($4006)           ; read IS_GAME_IN_PLAY\r
+0715: 0F            rrca                     ; move bit 0 into carry\r
+0716: 30 05         jr   nc,$071D            ; if carry is not set, game is not in play, goto $071D\r
+0718: 36 04         ld   (hl),$04            ; set SCRIPT_STAGE to 4\r
+071A: C3 0E 07      jp   $070E\r
+\r
+071D: 36 0E         ld   (hl),$0E            ;set SCRIPT_STAGE to $0E (14 decimal) \r
+071F: C3 0E 07      jp   $070E\r
+\r
+\r
+;\r
+; Player 1 and player 2 have used up all their lives.\r
+;\r
+\r
+GAME_OVER:\r
+0722: 3A 06 40      ld   a,($4006)           ; read IS_GAME_IN_PLAY\r
+0725: 0F            rrca                     ; move bit 0 into carry\r
+0726: 30 E5         jr   nc,$070D            ; if carry is not set, game is not in play, goto $070D\r
+0728: 3E 01         ld   a,$01\r
+072A: 32 05 40      ld   ($4005),a           ; set SCRIPT_NUMBER to 1\r
+072D: AF            xor  a\r
+072E: 32 06 40      ld   ($4006),a           ; clear IS_GAME_IN_PLAY\r
+0731: 32 0A 40      ld   ($400A),a           ; clear SCRIPT_STAGE\r
+0734: CD B5 1C      call $1CB5               ; call RESET_SOUND\r
+0737: 11 00 06      ld   de,$0600            ; command: PRINT_TEXT, parameter: 0 (index of "GAME OVER")\r
+073A: C3 F2 08      jp   $08F2               ; jump to QUEUE COMMAND  \r
+\r
+\r
+\r
+;\r
+; Player one has died. It's player two's turn now.\r
+;\r
+\r
+SWITCH_TO_PLAYER_TWO:\r
+073D: 21 09 40      ld   hl,$4009            ; load HL with address of TEMP_COUNTER_2\r
+0740: 35            dec  (hl)                ; decrement counter\r
+0741: C0            ret  nz                  ; return if count !=0\r
+0742: 2C            inc  l                   ; bump HL to point to SCRIPT_STAGE\r
+0743: AF            xor  a\r
+0744: 77            ld   (hl),a              \r
+0745: 32 22 42      ld   ($4222),a           ; reset LEVEL_COMPLETE flag\r
+0748: 32 2B 42      ld   ($422B),a           ; reset IS_FLAGSHIP_HIT flag\r
+\r
+; preserve state of the swarm so that it can be restored for player one's next turn. \r
+074B: 11 80 41      ld   de,$4180            ; load DE with pointer to PLAYER_ONE_PACKED_SWARM_DEF\r
+074E: CD 64 07      call $0764               ; call PACK_ALIEN_SWARM\r
+\r
+; save rest of player state so it can be restored too.\r
+0751: 21 18 42      ld   hl,$4218            ; load HL with address of CURRENT_PLAYER_STATE\r
+0754: 01 08 00      ld   bc,$0008\r
+0757: ED B0         ldir                     ; update player one's state\r
+\r
+; OK, now execute script to handle player two.\r
+0759: 3E 01         ld   a,$01\r
+075B: 32 0D 40      ld   ($400D),a           ; set CURRENT_PLAYER to 1 (Player TWO)\r
+075E: 3E 04         ld   a,$04\r
+0760: 32 05 40      ld   ($4005),a           ; set SCRIPT_NUMBER to 4\r
+0763: C9            ret\r
+\r
+\r
+;\r
+; Packs the 128 byte array that defines the alien swarm into a 16 byte buffer.\r
+;\r
+; This is used when a player dies and the game has to persist (ie: remember) the current state of the alien swarm \r
+; before the other player's turn starts. \r
+;\r
+; See also UNPACK_ALIEN_SWARM which unpacks the 16 byte buffer back into ALIEN_SWARM_FLAGS.\r
+;\r
+; \r
+; Expects:\r
+; DE = pointer to 16 byte buffer.  \r
+;\r
+\r
+PACK_ALIEN_SWARM:\r
+0764: 21 00 41      ld   hl,$4100            ; load HL with address of ALIEN_SWARM_FLAGS\r
+0767: 06 10         ld   b,$10               ; buffer is $10 (16 decimal) bytes in length\r
+0769: 0E 01         ld   c,$01               \r
+076B: AF            xor  a                   \r
+076C: CB 46         bit  0,(hl)              \r
+076E: 28 01         jr   z,$0771             \r
+0770: B1            or   c                   \r
+0771: 23            inc  hl                  \r
+0772: CB 01         rlc  c                   \r
+0774: 30 F6         jr   nc,$076C            \r
+0776: 12            ld   (de),a        \r
+0777: 13            inc  de            \r
+0778: 10 F1         djnz $076B\r
+077A: C9            ret\r
+\r
+\r
+\r
+;\r
+; This script is responsible for managing PLAYER 2's game.\r
+;\r
+;\r
+;\r
+\r
+SCRIPT_FOUR:\r
+077B: CD 0D 09      call $090D               ; call HANDLE_SWARM_MOVEMENT\r
+077E: CD 8E 09      call $098E               ; call SET_ALIEN_PRESENCE_FLAGS\r
+0781: 3A 0A 40      ld   a,($400A)\r
+0784: EF            rst  $28                 ; jump to code @ $0785 + (A*2)\r
+\r
+0785: \r
+      50 05         ; $0550   \r
+      83 05         ; $0583 (CLEAR_ROW_OF_SCREEN)\r
+      95 07         ; $0795 (PLAYER_TWO_INIT)\r
+      05 06         ; $0605 (CLEAR_PLAYER_TEXT)\r
+      14 06         ; $0614 (HANDLE_SPAWN_PLAYER)\r
+      61 06         ; $0661 (HANDLE_MAIN_GAME_LOGIC)\r
+      E8 07         ; $07E8 (HANDLE_PLAYER_TWO_KILLED)\r
+      18 08         ; $0818 (SWITCH_TO_PLAYER_ONE)\r
+\r
+\r
+;\r
+; Player two's turn is about to commence. \r
+;\r
+\r
+\r
+PLAYER_TWO_INIT:\r
+; restore alien swarm to what it was last turn \r
+0795: 11 A0 41      ld   de,$41A0            ; address of PLAYER_TWO_PACKED_SWARM_DEF\r
+0798: CD 46 06      call $0646               ; call UNPACK_ALIEN_SWARM\r
+; copy player two's state (ie: game settings) to current player state\r
+079B: EB            ex   de,hl               ; now HL = pointer to PLAYER_TWO_STATE\r
+079C: 11 18 42      ld   de,$4218            ; load DE with address of CURRENT_PLAYER_STATE \r
+079F: 01 08 00      ld   bc,$0008\r
+07A2: ED B0         ldir                     ; move player 2's state to CURRENT_PLAYER_STATE \r
+\r
+; reset any game settings \r
+07A4: AF            xor  a\r
+07A5: 32 5F 42      ld   ($425F),a           ; set TIMING_VARIABLE\r
+07A8: 32 20 42      ld   ($4220),a           ; clear HAVE_NO_ALIENS_IN_SWARM flag.\r
+\r
+; if the cabinet is cocktail, flip the screen for player two\r
+07AB: 3A 0F 40      ld   a,($400F)           ; read IS_COCKTAIL\r
+07AE: A7            and  a                   ; test if its zero (meaning UPRIGHT)\r
+07AF: 28 09         jr   z,$07BA             ; if its upright, goto $07BA          \r
+07B1: 32 18 40      ld   ($4018),a           ; set DISPLAY_IS_COCKTAIL_P2 to true\r
+07B4: 32 06 70      ld   ($7006),a           ; enable "regen hflip" which hardware flips the screen horizontally \r
+07B7: 32 07 70      ld   ($7007),a           ; enable "regen vflip" which hardware flips the screen vertically\r
+\r
+07BA: 21 0A 40      ld   hl,$400A            ; load HL with address of SCRIPT_STAGE\r
+07BD: 34            inc  (hl)                ; advance to next stage\r
+07BE: 2D            dec  l                   ; bump HL to point to TEMP_COUNTER_2\r
+07BF: 36 96         ld   (hl),$96            ; set counter\r
+07C1: 21 30 08      ld   hl,$0830\r
+07C4: 22 45 42      ld   ($4245),hl          ; set FLAGSHIP_ATTACK_MASTER_COUNTER_1 and FLAGSHIP_ATTACK_MASTER_COUNTER_2\r
+\r
+; if its not demo mode, display the scores\r
+07C7: 3A 06 40      ld   a,($4006)           ; read IS_GAME_IN_PLAY\r
+07CA: 0F            rrca                     ; move bit 0 into carry\r
+07CB: D0            ret  nc                  ; return if game is not in play\r
+07CC: 11 03 05      ld   de,$0503            ; command: DISPLAY_SCORE_COMMAND, parameter: 3 (invokes DISPLAY_ALL_SCORES)\r
+07CF: CD F2 08      call $08F2               ; call QUEUE_COMMAND\r
+07D2: 11 03 06      ld   de,$0603            ; command: PRINT_TEXT, parameter: 3 (index of "PLAYER TWO")\r
+07D5: CD F2 08      call $08F2               ; call QUEUE_COMMAND\r
+07D8: 1C            inc  e                   ; index of "HIGH SCORE" \r
+07D9: CD F2 08      call $08F2               ; call QUEUE_COMMAND\r
+07DC: 11 03 07      ld   de,$0703            ; command: BOTTOM_OF_SCREEN_INFO_COMMAND, parameter: 3 (invokes DISPLAY_PLAYER_SHIPS_REMAINING)\r
+07DF: CD F2 08      call $08F2               ; call QUEUE_COMMAND\r
+07E2: 11 00 07      ld   de,$0700            ; command: BOTTOM_OF_SCREEN_INFO_COMMAND, parameter: 0 (invokes DISPLAY_LEVEL_FLAGS) \r
+07E5: C3 F2 08      jp   $08F2               ; jump to QUEUE COMMAND\r
+\r
+\r
+;\r
+; Player 2 has just been killed.\r
+;\r
+; See also: $06D8 (HANDLE_PLAYER_ONE_KILLED)\r
+;\r
+\r
+HANDLE_PLAYER_TWO_KILLED:\r
+07E8: 21 0A 40      ld   hl,$400A            ; load HL with address of SCRIPT_STAGE\r
+07EB: 3A 1D 42      ld   a,($421D)           ; read PLAYER_LIVES\r
+07EE: A7            and  a                   ; test if zero \r
+07EF: 20 1B         jr   nz,$080C            ; if player 2 has lives remaining, goto $080C\r
+\r
+; OK, player 1 has no lives. What about player 1?\r
+07F1: 3A 95 41      ld   a,($4195)           ; read PLAYER_ONE_LIVES \r
+07F4: A7            and  a                   ; test if zero\r
+07F5: CA 22 07      jp   z,$0722             ; if player one is out of lives then that means GAME OVER for both players, goto $0722\r
+\r
+; player 1 has some lives left\r
+07F8: 34            inc  (hl)                ; increment SCRIPT_STAGE\r
+07F9: 2D            dec  l                   ; bump HL to point to TEMP_COUNTER_2\r
+07FA: 36 82         ld   (hl),$82            ; set value of counter\r
+\r
+; are we in demo mode?\r
+07FC: 3A 06 40      ld   a,($4006)           ; read IS_GAME_IN_PLAY flag \r
+07FF: 0F            rrca                     ; move flag into carry\r
+0800: D0            ret  nc                  ; return if game is not in play\r
+\r
+; Display PLAYER TWO GAME OVER\r
+0801: 11 03 06      ld   de,$0603            ; command: PRINT_TEXT, parameter: 3 (index of "PLAYER TWO") \r
+0804: CD F2 08      call $08F2               ; call QUEUE_COMMAND\r
+0807: 1E 00         ld   e,$00               ; index of text string "GAME OVER"\r
+0809: C3 F2 08      jp   $08F2               ; jump to QUEUE COMMAND\r
+\r
+; Player two's been killed. Is it player one's turn now? \r
+080C: 3A 95 41      ld   a,($4195)           ; read PLAYER_ONE_LIVES        \r
+080F: A7            and  a                   ; test if zero\r
+0810: CA 12 07      jp   z,$0712             ; if zero, goto $0712\r
+0813: 34            inc  (hl)                ; increment SCRIPT_STAGE\r
+0814: 2D            dec  l                   ; bump HL to point to TEMP_COUNTER_2  \r
+0815: 36 50         ld   (hl),$50\r
+0817: C9            ret\r
+\r
+\r
+;\r
+;\r
+; Called when player two has died and it's now player one's turn.\r
+;\r
+;\r
+\r
+SWITCH_TO_PLAYER_ONE:\r
+0818: 21 09 40      ld   hl,$4009            ; load HL with address of TEMP_COUNTER_2\r
+081B: 35            dec  (hl)\r
+081C: C0            ret  nz\r
+\r
+081D: 2C            inc  l                   ; bump HL to point to SCRIPT_STAGE\r
+081E: AF            xor  a\r
+081F: 77            ld   (hl),a              ; set SCRIPT_STAGE to 0\r
+\r
+0820: 32 0D 40      ld   ($400D),a           ; set CURRENT_PLAYER to 0 (player one)\r
+0823: 3E 03         ld   a,$03\r
+0825: 32 05 40      ld   ($4005),a           ; set SCRIPT_NUMBER\r
+\r
+; preserve state of the swarm so that it can be restored for player two's next turn. \r
+0828: 11 A0 41      ld   de,$41A0            ; Address of PLAYER_TWO_PACKED_SWARM_DEF\r
+082B: CD 64 07      call $0764               ; call PACK_ALIEN_SWARM to convert the swarm into bit flags and write to DE\r
+\r
+; save rest of player state so it can be restored too.\r
+082E: 21 18 42      ld   hl,$4218            ; load HL with address of CURRENT_PLAYER_STATE\r
+0831: 01 08 00      ld   bc,$0008\r
+0834: ED B0         ldir                     ; preserve player two's state\r
+0836: C9            ret\r
+\r
+\r
+\r
+;\r
+; Read player joystick/ movement controls and move player ship accordingly.\r
+; As stated in DISPLAY_PLAYER_COMMAND ($215F), the player ship isn't a sprite, its 4x4 characters.\r
+; When you move the ship, the columns containing the ship are scrolled.\r
+; \r
+; See also:\r
+; HANDLE_PLAYER_SHOOT.\r
+;\r
+\r
+HANDLE_PLAYER_MOVE:\r
+0837: 21 00 42      ld   hl,$4200            ; load HL with address of HAS_PLAYER_SPAWNED flag\r
+083A: CB 46         bit  0,(hl)              ; has player spawned?\r
+083C: 28 39         jr   z,$0877             ; no, goto SPAWN_PLAYER_OR_DIE\r
+083E: 2C            inc  l\r
+083F: 2C            inc  l                   ; now HL points to PLAYER_Y\r
+\r
+; are we in demo mode?\r
+0840: 3A 06 40      ld   a,($4006)           ; read IS_GAME_IN_PLAY      \r
+0843: 0F            rrca                     ; move bit 0 into carry. If carry is now set, the game is in play.\r
+0844: D2 92 08      jp   nc,$0892            ; carry not set, game is not in play, goto $0892 to simulate moving player ship in attract mode\r
+0847: 3A 18 40      ld   a,($4018)           ; read DISPLAY_IS_COCKTAIL_P2 flag\r
+084A: 0F            rrca                     ; move bit 0 into carry\r
+084B: 38 3F         jr   c,$088C             ; if carry is set, we're in cocktail mode and it's player 2 in control. Goto $088C\r
+\r
+; read movement controls\r
+084D: 3A 10 40      ld   a,($4010)           ; read PORT_STATE_6000\r
+0850: 47            ld   b,a                 ; save a copy in B\r
+0851: CB 5F         bit  3,a                 ; has MOVE RIGHT been pushed?\r
+0853: 28 06         jr   z,$085B             ; no, goto TEST_JOYSTICK_PUSHED_LEFT\r
+\r
+; player pushed stick right\r
+0855: 7E            ld   a,(hl)              ; load PLAYER_Y into A           \r
+0856: FE 17         cp   $17                 ; compare to #$17 (23 decimal) \r
+0858: 38 01         jr   c,$085B             ; if A < #$17 then player is at far right screen edge and can go no further, goto $085B\r
+085A: 35            dec  (hl)                ; decrement PLAYER_Y (moving ship RIGHT: remember, the monitor is flipped 90 degrees)\r
+\r
+TEST_JOYSTICK_PUSHED_LEFT:\r
+085B: CB 50         bit  2,b                 ; has MOVE LEFT been pushed?  \r
+085D: 28 06         jr   z,$0865             ; No, goto $0865\r
+\r
+; player pushed stick left\r
+085F: 7E            ld   a,(hl)              ; get Y coordinate of player ship into A\r
+0860: FE E9         cp   $E9                 ; compare to #$E9 (233 decimal)\r
+0862: 30 01         jr   nc,$0865            ; if A>= #$E9 then player is at far left screen edge and can go no further, goto $0865\r
+0864: 34            inc  (hl)                ; increment PLAYER_Y (moving ship LEFT)\r
+\r
+; set scroll value for columns containing the player ship characters\r
+SET_PLAYER_SHIP_SCROLL_OFFSET:\r
+0865: 7E            ld   a,(hl)              ; get ship Y coordinate into A            \r
+0866: 2F            cpl                      ; flip the bits\r
+0867: C6 80         add  a,$80               ; add an offset. Now we have a scroll value in A.\r
+0869: 0E 06         ld   c,$06               ; set colour of player ship\r
+086B: 21 54 40      ld   hl,$4054            ; set hl to point to first column containing player ship pseudosprite. \r
+\r
+; the players ship is 2x2 characters when alive, 4x4 when its exploding. We need to set the scroll offset for the 4 columns.\r
+086E: 06 04         ld   b,$04               ; we're doing 4 columns\r
+0870: 77            ld   (hl),a              ; write scroll offset to OBJRAM_BACK_BUF (see docs @top)\r
+0871: 2C            inc  l                   ; bump pointer to colour value for column\r
+0872: 71            ld   (hl),c              ; write colour to OBJRAM_BACK_BUF (see docs @ top) \r
+0873: 2C            inc  l                   ; bump pointer to scroll offset value for column\r
+0874: 10 FA         djnz $0870               ; do until b==0\r
+0876: C9            ret\r
+\r
+; if we get here, then either the player hasn't spawned, or the player is hit.\r
+SPAWN_PLAYER_OR_DIE:\r
+0877: 2C            inc  l                   ; point HL to IS_PLAYER_DYING flag\r
+0878: CB 46         bit  0,(hl)              ; test flag\r
+087A: 20 06         jr   nz,$0882            ; if flag is set, player will die, goto PLAYER_EXPLOSION_INIT\r
+\r
+; spawn player\r
+087C: 2C            inc  l                   ; otherwise, point HL to PLAYER_Y\r
+087D: 36 00         ld   (hl),$00            ; set value of PLAYER_Y to 0\r
+087F: C3 65 08      jp   $0865               ; and set the scroll for the player ship\r
+\r
+; if we get here, the player is about to explode. \r
+PLAYER_EXPLOSION_INIT: \r
+0882: 2C            inc  l                   ; point HL to PLAYER_Y\r
+0883: 7E            ld   a,(hl)              ; read value of PLAYER_Y\r
+0884: 2F            cpl                        \r
+0885: C6 80         add  a,$80               ; add an offset. Now we have a scroll value in A.\r
+0887: 0E 07         ld   c,$07               ; colour value (see $0872)\r
+0889: C3 6B 08      jp   $086B               ; and set the scroll offsets and colour values for the exploding ship\r
+\r
+\r
+088C: 3A 11 40      ld   a,($4011)           ; read PORT_STATE_6800\r
+088F: C3 50 08      jp   $0850               ; now test player 2's movement stick\r
+\r
+; In ATTRACT MODE, this piece of code supplies faked joystick movements and FIRE button presses to the player move logic.\r
+; The player spaceship looks like someone is controlling it.\r
+0892: 3A 3F 42      ld   a,($423F)           ; read bit flags from ATTRACT_MODE_FAKE_CONTROLLER.\r
+0895: C3 50 08      jp   $0850\r
+\r
+\r
+\r
+\r
+;\r
+; This routine is responsible for moving the player bullet, and positioning the player bullet sprite.\r
+;\r
+;\r
+\r
+HANDLE_PLAYER_BULLET:\r
+0898: CD BC 08      call $08BC               ; call POSITION_PLAYER_BULLET\r
+089B: 2A 09 42      ld   hl,($4209)          ; load value of PLAYER_BULLET_Y  into H, PLAYER_BULLET_X into L        \r
+089E: 3A 18 40      ld   a,($4018)           ; read DISPLAY_IS_COCKTAIL_P2\r
+08A1: 0F            rrca                     ; move bit 0 into carry. \r
+08A2: 38 0D         jr   c,$08B1             ; If carry is set, then player 2 is playing and its a cocktail cab: goto $08B1.\r
+\r
+; This code updates the bullet sprite state in the OBJRAM back buffer. \r
+08A4: 7D            ld   a,l                 ; load A with PLAYER_BULLET_X\r
+08A5: 2F            cpl\r
+08A6: C6 FC         add  a,-4                ; subtract 4 \r
+08A8: 32 9F 40      ld   ($409F),a           ; update OBJRAM_BUF_PLAYER_BULLET_X \r
+08AB: 7C            ld   a,h                 ; load A with PLAYER_BULLET_Y\r
+08AC: 2F            cpl\r
+08AD: 32 9D 40      ld   ($409D),a           ; update OBJRAM_BUF_PLAYER_BULLET_Y\r
+08B0: C9            ret\r
+\r
+; this code only runs when the game is in cocktail mode and it's player 2 playing.\r
+; It takes into account the screen is upside down and positions the player bullet properly.\r
+08B1: 7D            ld   a,l\r
+08B2: 3D            dec  a\r
+08B3: 32 9F 40      ld   ($409F),a           ; update OBJRAM_BUF_PLAYER_BULLET_X\r
+08B6: 7C            ld   a,h\r
+08B7: 2F            cpl\r
+08B8: 32 9D 40      ld   ($409D),a           ; update OBJRAM_BUF_PLAYER_BULLET_Y\r
+08BB: C9            ret\r
+\r
+\r
+\r
+;\r
+; If the player bullet has not been fired, position it just above the player ship.\r
+;\r
+; Otherwise, move player bullet upscreen. When bullet reaches its limit, set IS_PLAYER_BULLET_DONE flag to 1.\r
+;\r
+\r
+POSITION_PLAYER_BULLET:\r
+08BC: 21 08 42      ld   hl,$4208            ; pointer to HAS_PLAYER_BULLET_BEEN_FIRED flag \r
+08BF: CB 46         bit  0,(hl)              ; test if flag is set\r
+08C1: 23            inc  hl                  ; HL now points to PLAYER_BULLET_X\r
+08C2: 28 0F         jr   z,$08D3             ; if player has not fired, goto POSITION_PLAYER_BULLET_ABOVE_SHIP\r
+\r
+; player bullet has been fired. Subtract 4 from its X coordinate and check if its gone off screen.\r
+08C4: 7E            ld   a,(hl)              ; get X coordinate of bullet\r
+08C5: D6 04         sub  $04                 ; subtract 4, moving bullet UP the screen\r
+08C7: 77            ld   (hl),a              ; update X coordinate of bullet\r
+08C8: D6 0E         sub  $0E                 ; \r
+08CA: D6 04         sub  $04h                ; subtract 18 (decimal) total from X coordinate\r
+08CC: D0            ret  nc                  ; if there's no carry, then the players bullet has not reached its limit\r
+\r
+; player bullet has gone offscreen\r
+08CD: 3E 01         ld   a,$01               \r
+08CF: 32 0B 42      ld   ($420B),a           ; set IS_PLAYER_BULLET_DONE flag\r
+08D2: C9            ret\r
+\r
+; the player bullet isn't fired, so place it above the player ship.\r
+POSITION_PLAYER_BULLET_ABOVE_SHIP:\r
+08D3: 36 DC         ld   (hl),$DC            ; set PLAYER_BULLET_X\r
+08D5: 2C            inc  l                   ; point HL to PLAYER_BULLET_Y\r
+08D6: 3A 00 42      ld   a,($4200)           ; read HAS_PLAYER_SPAWNED flag\r
+08D9: CB 47         bit  0,a                 ; test if player has spawned\r
+08DB: 28 05         jr   z,$08E2             ; no, player has not spawned, goto $08E2\r
+08DD: 3A 02 42      ld   a,($4202)           ; read PLAYER_Y\r
+08E0: 77            ld   (hl),a              ; and write to PLAYER_BULLET_Y\r
+08E1: C9            ret\r
+\r
+; player hasn't spawned. Hide the bullet!\r
+08E2: 36 00         ld   (hl),$00            ; write 0 to PLAYER_BULLET_Y\r
+08E4: C9            ret\r
+\r
+\r
+;\r
+; Check if the player bullet has gone all the way upscreen. If so, allow player to shoot again.\r
+;\r
+\r
+CHECK_IF_PLAYER_BULLET_IS_DONE:\r
+08E5: 3A 0B 42      ld   a,($420B)           ; read IS_PLAYER_BULLET_DONE flag\r
+08E8: 0F            rrca                     ; move bit 0 into carry. If carry is set then the player bullet's gone as far as it can.\r
+08E9: D0            ret  nc                  ; if no carry, then return. \r
+08EA: AF            xor  a\r
+08EB: 32 0B 42      ld   ($420B),a           ; reset the IS_PLAYER_BULLET_DONE flag\r
+08EE: 32 08 42      ld   ($4208),a           ; reset HAS_PLAYER_BULLET_BEEN_FIRED flag. Player can shoot again.\r
+08F1: C9            ret\r
+\r
+\r
+\r
+;\r
+; Try to insert into the circular command queue located @ $40C0. (CIRC_CMD_QUEUE_START)\r
+; if insert is not possible, exit function immediately.\r
+;\r
+; Expects:\r
+; D is a command number (0..7) \r
+; E is a parameter to pass to the command. \r
+;\r
+; $40A0 contains the low byte of a pointer to a (hopefully) free entry in the queue.  \r
+;\r
+; REMARKS:\r
+;\r
+; Value in D                Action it invokes \r
+; ===============================================================\r
+; 0                         Invokes DRAW_ALIEN_COMMAND\r
+; 1                         Invokes DELETE_ALIEN_COMMAND\r
+; 2:                        Invokes DISPLAY_PLAYER_COMMAND\r
+; 3:                        Invokes UPDATE_PLAYER_SCORE_COMMAND\r
+; 4:                        Invokes RESET_SCORE_COMMAND\r
+; 5:                        Invokes DISPLAY_SCORE_COMMAND\r
+; 6:                        Invokes PRINT_TEXT \r
+; 7:                        Invokes BOTTOM_OF_SCREEN_INFO_COMMAND\r
+; \r
+; The purpose of the parameter in E depends on the command.\r
+;\r
+; SEE ALSO:\r
+; The code @ $200C which processes the entries in the queue.\r
+\r
+; ALGORITHM:\r
+; 1. Form a pointer to an entry in the circular queue using #$40 as the high byte of the pointer\r
+;    and the contents of $40A0 (CIRC_CMD_QUEUE_PTR_LO) as the low byte. \r
+; 2. Read a byte from the queue entry the pointer points to \r
+; 3. IF bit 7 of the byte is unset, then the queue entry is in use, we can't insert. Exit function.  \r
+; 4. ELSE:\r
+;    4a) store register DE at the pointer\r
+;    4b) bump pointer to next queue entry \r
+; 5. Exit function\r
+\r
+QUEUE_COMMAND:\r
+08F2: E5            push hl\r
+08F3: 26 40         ld   h,$40               ; set high byte of address\r
+08F5: 3A A0 40      ld   a,($40A0)           ; read CIRC_CMD_QUEUE_PTR_LO          \r
+08F8: 6F            ld   l,a                 ; set low byte of address. Now HL = pointer to entry in circular queue.\r
+08F9: CB 7E         bit  7,(hl)              ; read byte from address and test bit 7\r
+08FB: 28 0E         jr   z,$090B             ; if bit 7 not set, this entry cannot be used, goto $090B and exit\r
+08FD: 72            ld   (hl),d              ; write DE...\r
+08FE: 2C            inc  l\r
+08FF: 73            ld   (hl),e\r
+0900: 2C            inc  l                   ; to (HL)\r
+0901: 7D            ld   a,l                 ; \r
+0902: FE C0         cp   $C0                 ; compare low byte of address in HL to #$C0. \r
+0904: 30 02         jr   nc,$0908            ; if A > #$C0 (192 decimal) then we've not hit the end of the circular queue, goto $0908\r
+0906: 3E C0         ld   a,$C0               ; otherwise, we're past the end of the queue, reset queue pointer high byte to #$C0 (192 decimal)\r
+0908: 32 A0 40      ld   ($40A0),a           ; update CIRC_CMD_QUEUE_PTR_LO to point to next queue entry\r
+090B: E1            pop  hl\r
+090C: C9            ret\r
+\r
+\r
+\r
+; This routine does two things:\r
+;\r
+; 1. Stops the swarm from moving if the player bullet gets too close to an alien in the swarm \r
+; 2. Sets the scroll registers for the columns containing the swarm\r
+; \r
+; Before I investigated this code, I never noticed the swarm stops - now I can't not notice it.\r
+; If you want the aliens to just not care about their own safety, type the following into the debugger: maincpu.mw@$093C=0 \r
+;\r
+\r
+HANDLE_SWARM_MOVEMENT:\r
+090D: 21 08 42      ld   hl,$4208            ; point HL to HAS_PLAYER_BULLET_BEEN_FIRED flag\r
+0910: CB 46         bit  0,(hl)              ; test bit 0. \r
+0912: 28 2A         jr   z,$093E             ; If player hasn't fired, goto $093E \r
+0914: 2C            inc  l                   ; point HL to PLAYER_BULLET_X\r
+0915: 7E            ld   a,(hl)              ; read X coordinate of bullet\r
+0916: D6 22         sub  $22\r
+0918: FE 50         cp   $50                 ; compare to $50 (80 decimal)\r
+091A: 30 22         jr   nc,$093E            ; if greater than $50, goto $093E\r
+\r
+091C: 2C            inc  l                   ; point HL to PLAYER_BULLET_Y\r
+091D: 3A 0E 42      ld   a,($420E)           ; read SWARM_SCROLL_VALUE\r
+0920: 96            sub  (hl)                ; subtract from value in PLAYER_BULLET_Y\r
+0921: ED 44         neg                      ; A = 256-A   \r
+0923: 47            ld   b,a\r
+0924: C6 02         add  a,$02\r
+0926: E6 0F         and  $0F\r
+0928: FE 03         cp   $03\r
+092A: 30 12         jr   nc,$093E            ; if A >= #$03 goto $093E\r
+\r
+092C: 78            ld   a,b\r
+092D: 0F            rrca\r
+092E: 0F            rrca\r
+092F: 0F            rrca\r
+0930: 0F            rrca\r
+0931: E6 0F         and  $0F                 ; now A identifies the column of the swarm the player bullet is in\r
+0933: 5F            ld   e,a\r
+0934: 16 00         ld   d,$00\r
+0936: 21 F0 41      ld   hl,$41F0            ; load HL with address of ALIEN_IN_COLUMN_FLAGS\r
+0939: 19            add  hl,de              \r
+093A: CB 46         bit  0,(hl)              ; are there any aliens in this column?\r
+093C: 20 4A         jr   nz,$0988            ; yes, make the swarm stand still!\r
+\r
+; move the swarm, and make it change direction once its hit a screen edge \r
+093E: 2A 0E 42      ld   hl,($420E)          ; read SWARM_SCROLL_VALUE\r
+0941: ED 5B 10 42   ld   de,($4210)          ; read SWARM_SCROLL_MAX_EXTENTS\r
+0945: 3A 0D 42      ld   a,($420D)           ; read SWARM_DIRECTION\r
+0948: A7            and  a                   ; test if its zero\r
+0949: 20 12         jr   nz,$095D            ; if not zero, swarm is moving right, goto $095D\r
+\r
+; swarm is moving left\r
+094B: CB 7C         bit  7,h                 \r
+094D: 20 04         jr   nz,$0953\r
+094F: 7D            ld   a,l\r
+0950: BB            cp   e\r
+0951: 30 2A         jr   nc,$097D            ; jp to MAKE_SWARM_MOVE_RIGHT\r
+0953: 3A 5F 42      ld   a,($425F)           ; read TIMING_VARIABLE\r
+0956: E6 03         and  $03                 ; mask in bits 0 & 1\r
+0958: C0            ret  nz                  ; if either bit is set, return\r
+0959: 23            inc  hl                  ; increment scroll value. Swarm will move left (but in reality, a pixel down).\r
+095A: C3 6C 09      jp   $096C\r
+\r
+; swarm is moving right\r
+095D: CB 7C         bit  7,h                 ; test bit 7\r
+095F: 28 04         jr   z,$0965\r
+0961: 7D            ld   a,l\r
+0962: BA            cp   d\r
+0963: 38 1E         jr   c,$0983             ; jp to MAKE_SWARM_MOVE_LEFT\r
+0965: 3A 5F 42      ld   a,($425F)           ; read TIMING_VARIABLE\r
+0968: E6 03         and  $03                 ; mask in bits 0 & 1\r
+096A: C0            ret  nz                  ; if either bit is set, return. \r
+096B: 2B            dec  hl                  ; decrement scroll value. Swarm will move right (in reality, a pixel up).\r
+\r
+\r
+; if you want the swarm to be static, or tinker with the scroll, fill $096C to $096E with NOP instructions. \r
+096C: 22 0E 42      ld   ($420E),hl          ; set SWARM_SCROLL_VALUE \r
+096F: 7D            ld   a,l                 \r
+0970: ED 44         neg                      ; A = 256-A\r
+\r
+\r
+;\r
+; This is used to scroll the alien swarm from side to side.\r
+;\r
+; Expects:\r
+; register A: Scroll offset value\r
+;\r
+\r
+SET_SWARM_SCROLL_OFFSET:\r
+0972: 21 28 40      ld   hl,$4028            ; pointer to attribute and column scroll data held in OBJRAM_BACK_BUF\r
+0975: 06 09         ld   b,$09               ; we're doing 9 columns.\r
+0977: 77            ld   (hl),a              ; write value into scroll offset in OBJRAM_BACK_BUF. \r
+0978: 2C            inc  l\r
+0979: 2C            inc  l                   ; bump HL to next scroll offset address in OBJRAM_BACK_BUF\r
+097A: 10 FB         djnz $0977\r
+097C: C9            ret\r
+\r
+\r
+MAKE_SWARM_MOVE_RIGHT:\r
+097D: 3E 01         ld   a,$01\r
+097F: 32 0D 42      ld   ($420D),a           ; set SWARM_DIRECTION to 1. Swarm now moves right.\r
+0982: C9            ret\r
+\r
+MAKE_SWARM_MOVE_LEFT:\r
+0983: AF            xor  a\r
+0984: 32 0D 42      ld   ($420D),a           ; set SWARM_DIRECTION to 0. Swarm now moves left.\r
+0987: C9            ret\r
+\r
+0988: 2A 0E 42      ld   hl,($420E)          ; read SWARM_SCROLL_VALUE\r
+098B: C3 6F 09      jp   $096F\r
+\r
+\r
+;\r
+; This routine sets the following flags:  \r
+;\r
+; ALIEN_IN_COLUMN_FLAGS\r
+; HAVE_ALIENS_IN_6TH_ROW\r
+; HAVE_ALIENS_IN_5TH_ROW\r
+; HAVE_ALIENS_IN_4TH_ROW\r
+; HAVE_ALIENS_IN_3RD_ROW\r
+; HAVE_ALIENS_IN_2ND_ROW\r
+; HAVE_ALIENS_IN_TOP_ROW\r
+; HAVE_NO_BLUE_OR_PURPLE_ALIENS\r
+; HAVE_NO_ALIENS_IN_SWARM \r
+; HAVE_NO_INFLIGHT_ALIENS\r
+; HAVE_NO_INFLIGHT_OR_DYING_ALIENS\r
+; \r
+; It also sets the values for SWARM_SCROLL_MAX_EXTENTS. \r
+\r
+SET_ALIEN_PRESENCE_FLAGS:     // TENTATIVE NAME - If anyone can think of anything better, give me a shout\r
+098E: AF            xor  a\r
+098F: 11 E8 41      ld   de,$41E8     \r
+0992: 12            ld   (de),a              ; clear $41E8\r
+0993: 1C            inc  e                    \r
+0994: 12            ld   (de),a              ; clear $41E9\r
+0995: 1C            inc  e                   ; DE now = $41EA (address of HAVE_ALIENS_IN_IN_ROW_FLAGS)\r
+\r
+; This part of the code determines if there are any aliens on a given row.\r
+; It will set the corresponding flag in the HAVE_ALIENS_IN_ROW_FLAGS array.\r
+; it works from the bottom row of aliens to the top.\r
+0996: 0E 06         ld   c,$06               ; there are 6 rows of aliens. Used as a row counter.\r
+0998: 21 23 41      ld   hl,$4123            ; pointer to bottom right alien in ALIEN_SWARM_FLAGS\r
+\r
+099B: 06 0A         ld   b,$0A               ; There's $0A (10 decimal) aliens max per row\r
+099D: AF            xor  a                   ; clear A. \r
+099E: B6            or   (hl)                ; If an alien is present, A will now be set to 1.\r
+099F: 2C            inc  l                   ; move to next flag\r
+09A0: 10 FC         djnz $099E               ; repeat tests until B == 0.\r
+\r
+09A2: 12            ld   (de),a              ; store alien presence flag in HAVE_ALIENS_IN_[]_ROW flag\r
+09A3: 1C            inc  e                   ; bump DE to point to next HAVE_ALIENS_IN_[]_ROW flag\r
+09A4: 7D            ld   a,l          \r
+09A5: C6 06         add  a,$06\r
+09A7: 6F            ld   l,a                 ; Add 6 to HL. Now HL points to flags for row of aliens above previous    \r
+09A8: 0D            dec  c                   ; decrement row counter\r
+09A9: C2 9B 09      jp   nz,$099B            ; if not all rows of aliens have been processed, goto $099B\r
+\r
+; when we get here, DE points to $41F0, which is the start of the ALIEN_IN_COLUMN_FLAGS array.\r
+09AC: AF            xor  a\r
+09AD: 12            ld   (de),a              ; clear first entry of ALIEN_IN_COLUMN_FLAGS. \r
+09AE: 1C            inc  e\r
+09AF: 12            ld   (de),a              ; clear second entry of ALIEN_IN_COLUMN_FLAGS.\r
+09B0: 1C            inc  e\r
+09B1: 12            ld   (de),a              ; clear second entry of ALIEN_IN_COLUMN_FLAGS.\r
+09B2: 1C            inc  e\r
+\r
+09B3: 21 23 41      ld   hl,$4123            ; pointer to bottom right alien in ALIEN_SWARM_FLAGS \r
+09B6: 0E 0A         ld   c,$0A               ; There's $0A (10 decimal) columns of aliens \r
+\r
+; Working from the rightmost column of aliens to the left, check each column for presence of aliens and\r
+; set/clear respective flag in ALIEN_IN_COLUMN_FLAGS array accordingly. \r
+09B8: D5            push de\r
+09B9: 11 10 00      ld   de,$0010            ; offset to add to HL to point to alien in row above, same column.\r
+09BC: 06 06         ld   b,$06               ; 6 rows of aliens. Used as a row counter.\r
+09BE: AF            xor  a\r
+09BF: B6            or   (hl)                ; If an alien is present, A will now be set to 1.\r
+09C0: 19            add  hl,de               ; Point HL to alien in row above, same column.\r
+09C1: 10 FC         djnz $09BF               ; Repeat until all 6 rows of aliens have been scanned\r
+09C3: D1            pop  de\r
+09C4: 12            ld   (de),a              ; set/clear flag in ALIEN_IN_COLUMN_FLAGS\r
+09C5: 1C            inc  e                   ; bump DE to point to next entry in ALIEN_IN_COLUMN_FLAGS\r
+\r
+; we've scanned all the aliens in a column. \r
+; We now want to scan the next column of aliens to the immediate *left* of the column we just scanned. \r
+09C6: 7D            ld   a,l\r
+09C7: D6 5F         sub  $5F\r
+09C9: 6F            ld   l,a                 ; now HL points to bottom alien in next column of aliens to check  \r
+09CA: 0D            dec  c                   ; decrement counter for number of columns left to process            \r
+09CB: C2 B8 09      jp   nz,$09B8            ; if we've not done all the columns, goto $09B8\r
+\r
+; the following code works out how far to the left the swarm can move. Or should I say, how far the swarm can be scrolled down.\r
+; TODO: I'll come back to this code later, but at the moment there's bigger fish to fry with this game, so I'll just leave bare bones here.\r
+09CE: 21 FC 41      ld   hl,$41FC            ; load HL with a pointer to flag for the leftmost column of aliens in ALIEN_IN_COLUMN_FLAGS\r
+09D1: 06 0A         ld   b,$0A               ; There's $0A (10 decimal) columns of aliens \r
+09D3: 1E 22         ld   e,$22\r
+09D5: CB 46         bit  0,(hl)              ; Test the flag. Is there an alien in the column?\r
+09D7: 20 09         jr   nz,$09E2            ; yes, goto $09E2\r
+09D9: 2D            dec  l                   ; bump HL to point to flag for column to left\r
+09DA: 7B            ld   a,e\r
+09DB: C6 10         add  a,$10\r
+09DD: 5F            ld   e,a\r
+09DE: 10 F5         djnz $09D5\r
+\r
+; now work out how far to the right the swarm can move. \r
+09E0: 1E 22         ld   e,$22\r
+09E2: 21 F3 41      ld   hl,$41F3            ; load HL with a pointer to flag for the rightmost column of aliens in ALIEN_IN_COLUMN_FLAGS\r
+09E5: 06 0A         ld   b,$0A               ; There's $0A (10 decimal) columns of aliens\r
+09E7: 16 E0         ld   d,$E0\r
+09E9: CB 46         bit  0,(hl)              ; Test the flag. Is there an alien in the column?\r
+09EB: 20 09         jr   nz,$09F6            ; yes, goto $09F6\r
+09ED: 2C            inc  l\r
+09EE: 7A            ld   a,d\r
+09EF: D6 10         sub  $10\r
+09F1: 57            ld   d,a\r
+09F2: 10 F5         djnz $09E9\r
+09F4: 16 E0         ld   d,$E0\r
+09F6: ED 53 10 42   ld   ($4210),de          ; set SWARM_SCROLL_MAX_EXTENTS\r
+\r
+; Check if any of the bottom 4 rows of aliens (blue & purple) have any aliens in them. *Aliens from those rows that are in flight don't count*\r
+09FA: 21 EA 41      ld   hl,$41EA            ; load HL with pointer to HAVE_ALIENS_IN_6TH_ROW\r
+09FD: 0E 01         ld   c,$01\r
+09FF: 06 04         ld   b,$04               ; we want to do 4 rows of aliens\r
+0A01: AF            xor  a\r
+0A02: B6            or   (hl)                ; test if there's an alien present on the row\r
+0A03: 2C            inc  l                   ; bump HL to point to flag for row above \r
+0A04: 10 FC         djnz $0A02               ; repeat until b==0\r
+0A06: A9            xor  c                   ; \r
+0A07: 32 21 42      ld   ($4221),a           ; set HAVE_NO_BLUE_OR_PURPLE_ALIENS flag\r
+\r
+; HL = pointer to HAVE_ALIENS_IN_2ND_ROW\r
+0A0A: A9            xor  c                   ; if A was 1, set it to 0, and vice versa\r
+0A0B: B6            or   (hl)                ; if any aliens in red row set A to 1  *Red aliens in flight don't count*\r
+0A0C: 2C            inc  l                   ; bump HL to point to HAVE_ALIENS_IN_TOP_ROW\r
+0A0D: B6            or   (hl)                ; if any aliens in flagship row set A to 1   *Flagships that are in flight don't count*\r
+0A0E: A9            xor  c                   ; if A was 1, set it to 0, and vice versa\r
+0A0F: 32 20 42      ld   ($4220),a           ; set/reset HAVE_NO_ALIENS_IN_SWARM flag.\r
+\r
+; Check if we have any aliens "in-flight" attacking the player.\r
+; We skip the first entry in the INFLIGHT_ALIENS array because the first entry is reserved for misc use (see docs above INFLIGHT_ALIEN struct)\r
+; and should not be treated as a "real" flying alien.\r
+0A12: 21 D0 42      ld   hl,$42D0            ; pointer to INFLIGHT_ALIENS_START+sizeof(INFLIGHT_ALIEN). Effectively skipping first INFLIGHT_ALIEN. \r
+0A15: 11 20 00      ld   de,$0020            ; sizeof(INFLIGHT_ALIEN)\r
+0A18: 06 07         ld   b,$07               ; 7 aliens to process in the list\r
+0A1A: AF            xor  a                   ; clear A\r
+0A1B: B6            or   (hl)                ; Set A to 1 if alien is active\r
+0A1C: 19            add  hl,de               ; bump HL to point to next INFLIGHT_ALIEN structure in the array\r
+0A1D: 10 FC         djnz $0A1B               ; repeat until B==0\r
+0A1F: A9            xor  c                   ; if no aliens are in flight, A will be set to 1.  Else A is set 0. \r
+0A20: 32 26 42      ld   ($4226),a           ; set/reset HAVE_NO_INFLIGHT_ALIENS flag \r
+\r
+; Check if we have any aliens "in-flight" or dying \r
+0A23: A9            xor  c                   ; \r
+0A24: 21 B1 42      ld   hl,$42B1            ; pointer to first IsDying flag of INFLIGHT_ALIENS array.\r
+0A27: 06 08         ld   b,$08               ; test all 8 slots\r
+0A29: B6            or   (hl)                ; if INFLIGHT_ALIEN.IsDying is set to 1, set A to 1.\r
+0A2A: 19            add  hl,de               ; bump HL to point to next INFLIGHT_ALIEN structure in the array\r
+0A2B: 10 FC         djnz $0A29               ; repeat until B==0\r
+0A2D: A9            xor  c                   ; \r
+0A2E: 32 25 42      ld   ($4225),a           ; set/reset HAVE_NO_INFLIGHT_OR_DYING_ALIENS\r
+0A31: C9            ret\r
+\r
+\r
+\r
+\r
+HANDLE_PLAYER_SHOOT:\r
+0A32: 3A 00 42      ld   a,($4200)           ; read HAS_PLAYER_SPAWNED flag\r
+0A35: 0F            rrca                     ; move bit 0 into carry\r
+0A36: D0            ret  nc                  ; if player has not spawned, return\r
+0A37: 3A 08 42      ld   a,($4208)           ; read HAS_PLAYER_BULLET_BEEN_FIRED flag\r
+0A3A: 0F            rrca                     ; move bit 0 into carry\r
+0A3B: D8            ret  c                   ; if carry is set, missile has already been fired, can't shoot again.\r
+0A3C: 3A 06 40      ld   a,($4006)           ; read IS_GAME_IN_PLAY flag\r
+0A3F: 0F            rrca                     ; move bit 0 into carry\r
+0A40: 30 26         jr   nc,$0A68            ; if no carry, game is not in play, so this is demo mode, goto $0A68\r
+0A42: 3A 18 40      ld   a,($4018)           ; read DISPLAY_IS_COCKTAIL_P2 flag\r
+0A45: 0F            rrca                     ; move bit 0 into carry\r
+0A46: 38 15         jr   c,$0A5D             ; if we're player 2 and we're playing on a cocktail machine, goto $0A5D \r
+0A48: 3A 13 40      ld   a,($4013)           ; read PREV_PORT_STATE_6000 \r
+0A4B: 2F            cpl\r
+0A4C: 47            ld   b,a\r
+0A4D: 3A 10 40      ld   a,($4010)           ; read PORT_STATE_6000\r
+0A50: A0            and  b\r
+0A51: E6 10         and  $10                 ; test state of SHOOT button\r
+0A53: C8            ret  z                   ; return if not held down\r
+0A54: 3E 01         ld   a,$01\r
+0A56: 32 08 42      ld   ($4208),a           ; set HAS_PLAYER_BULLET_BEEN_FIRED flag\r
+0A59: 32 CC 41      ld   ($41CC),a           ; set PLAY_PLAYER_SHOOT_SOUND flag \r
+0A5C: C9            ret\r
+\r
+; We come here if it's player 2's turn and the game is in cocktail mode.\r
+0A5D: 3A 14 40      ld   a,($4014)           ; read PREV_PORT_STATE_6800\r
+0A60: 2F            cpl\r
+0A61: 47            ld   b,a\r
+0A62: 3A 11 40      ld   a,($4011)           ; read PORT_STATE_6800\r
+0A65: C3 50 0A      jp   $0A50               ; go check if shoot button for player 2's controls is held down.\r
+\r
+\r
+; We're in demo mode. We need to simulate the player firing at the aliens.\r
+0A68: 3A 5F 42      ld   a,($425F)           ; read TIMING_VARIABLE\r
+0A6B: E6 1F         and  $1F                 ; mask in bits 0..5\r
+0A6D: C0            ret  nz                  ; if result is not zero, then return\r
+0A6E: 3E 01         ld   a,$01\r
+0A70: 32 08 42      ld   ($4208),a           ; set HAS_PLAYER_BULLET_BEEN_FIRED flag\r
+0A73: C9            ret\r
+\r
+\r
+\r
+;\r
+; Move enemy bullets and position enemy bullet sprites\r
+;\r
+;\r
+;\r
+\r
+HANDLE_ENEMY_BULLETS:\r
+0A74: DD 21 60 42   ld   ix,$4260            ; load IX with address of ENEMY_BULLETS_START\r
+0A78: 3A 5F 42      ld   a,($425F)           ; read TIMING_VARIABLE\r
+0A7B: 0F            rrca                     ; move bit 0 into carry\r
+0A7C: 38 0B         jr   c,$0A89             ; if TIMING_VARIABLE is an odd number, goto $0A89\r
+0A7E: DD 34 01      inc  (ix+$01)            ; Increment ENEMY_BULLET.X by 2.. \r
+0A81: DD 34 01      inc  (ix+$01)            ; \r
+\r
+0A84: 11 05 00      ld   de,$0005            ; sizeof(ENEMY_BULLET)\r
+0A87: DD 19         add  ix,de\r
+0A89: FD 21 81 40   ld   iy,$4081            ; pointer to OBJRAM_BACK_BUF_BULLETS\r
+0A8D: 06 07         ld   b,$07               ; number of bullets\r
+\r
+; main bullet loop\r
+0A8F: DD CB 00 46   bit  0,(ix+$00)          ; test ENEMY_BULLET.IsActive flag\r
+0A93: 28 27         jr   z,$0ABC             ; if enemy bullet is not active, goto $0ABC\r
+0A95: DD 7E 01      ld   a,(ix+$01)          ; read ENEMY_BULLET.X \r
+0A98: C6 02         add  a,$02               ; bullet will move 2 pixels\r
+0A9A: DD 77 01      ld   (ix+$01),a          ; update ENEMY_BULLET.X\r
+0A9D: C6 04         add  a,$04               ; tentatively add 4 to the X coordinate. If a carry occurs, enemy bullet is at bottom of screen \r
+0A9F: 38 1B         jr   c,$0ABC             ; enemy bullet is at bottom of screen so needs to be deactivated, goto $0ABC\r
+\r
+; split ENEMY_BULLET.YDelta into its sign and delta, then add to YH and YL respectively. \r
+0AA1: DD 6E 02      ld   l,(ix+$02)          ; read ENEMY_BULLET.YL\r
+0AA4: DD 66 03      ld   h,(ix+$03)          ; read ENEMY_BULLET.YH\r
+0AA7: DD 5E 04      ld   e,(ix+$04)          ; read ENEMY_BULLET.YDelta \r
+0AAA: CB 13         rl   e                   ; move bit 7 of E (sign bit) into carry. Shift YDelta bits left into bits 1..7.                  \r
+0AAC: 9F            sbc  a,a                 ; A = 0 - carry\r
+0AAD: 57            ld   d,a                 ; if bit 7 of E was set, D will be $FF, else 0.\r
+0AAE: 19            add  hl,de               \r
+0AAF: DD 75 02      ld   (ix+$02),l          ; set ENEMY_BULLET.YL\r
+0AB2: DD 74 03      ld   (ix+$03),h          ; set ENEMY_BULLET.YH\r
+0AB5: 7C            ld   a,h                 ; get ENEMY_BULLET.YH coordinate into A\r
+0AB6: C6 10         add  a,$10               ; add #$10 (16 decimal) . \r
+0AB8: FE 20         cp   $20                 ; compare to $20 (32 decimal)\r
+0ABA: 30 0A         jr   nc,$0AC6            ; if >= 32 decimal, bullet is still onscrene, goto $0AC6\r
+\r
+; bullet is offscreen, deactivate it\r
+0ABC: AF            xor  a\r
+0ABD: DD 77 00      ld   (ix+$00),a          ; set ENEMY_BULLET.IsActive flag (disables bullet)\r
+0AC0: DD 77 01      ld   (ix+$01),a          ; set ENEMY_BULLET.X to 0 \r
+0AC3: DD 77 03      ld   (ix+$03),a          ; set ENEMY_BULLET.YH to 0\r
+\r
+; we now need to position the actual enemy bullet sprites.\r
+0AC6: 3A 18 40      ld   a,($4018)           ; read DISPLAY_IS_COCKTAIL_P2\r
+0AC9: 0F            rrca                     ; move flag into carry\r
+0ACA: 38 29         jr   c,$0AF5             ; if carry is set, it's a cocktail setup and player 2's turn, goto $0AF5\r
+\r
+0ACC: DD 7E 01      ld   a,(ix+$01)          ; read ENEMY_BULLET.X \r
+0ACF: 2F            cpl                      ; A = (255 - A) \r
+0AD0: 3D            dec  a                   ; A = A-1\r
+0AD1: FD 77 02      ld   (iy+$02),a          ; write to OBJRAM_BACK_BUF_BULLETS sprite state\r
+\r
+; looks to me like there's a hardware "feature" where the Y coordinate of alien bullets 5-7 needs adjusted by 1 so the sprite is positioned correctly.\r
+; if anyone can tell me why, drop me a line. Thanks!\r
+0AD4: DD 7E 03      ld   a,(ix+$03)          ; read ENEMY_BULLET.YH \r
+0AD7: 2F            cpl                      ; A = (255 - A) \r
+0AD8: 4F            ld   c,a\r
+0AD9: 78            ld   a,b                 ; get index of enemy bullet we are processing into A\r
+0ADA: FE 05         cp   $05                 ; are we processing bullet #5 or more?\r
+0ADC: 38 01         jr   c,$0ADF             ; no, goto $0ADF\r
+0ADE: 0C            inc  c                   ; adjust Y coordinate\r
+0ADF: FD 71 00      ld   (iy+$00),c          ; write to OBJRAM_BACK_BUF_BULLETS sprite Y coordinate\r
+\r
+0AE2: 11 05 00      ld   de,$0005            ; sizeof(ENEMY_BULLET)\r
+0AE5: DD 19         add  ix,de               ; bump IX to point to next ENEMY_BULLET in ENEMY_BULLETS array\r
+0AE7: DD 34 01      inc  (ix+$01)            ; increment ENEMY_BULLET.X\r
+0AEA: DD 34 01      inc  (ix+$01)            ; twice, to make it move 2 pixels\r
+\r
+0AED: DD 19         add  ix,de               ; bump IX to point to next ENEMY_BULLET in ENEMY_BULLETS array\r
+0AEF: 1D            dec  e                   ; DE is now 4  \r
+0AF0: FD 19         add  iy,de               ; bump IY to point to state of next sprite in OBJRAM_BACK_BUF_BULLETS\r
+0AF2: 10 9B         djnz $0A8F               ; repeat until B ==0\r
+0AF4: C9            ret\r
+\r
+; called if we have a cocktail display and it's player 2's turn. \r
+0AF5: DD 7E 01      ld   a,(ix+$01)\r
+0AF8: D6 04         sub  $04\r
+0AFA: FD 77 02      ld   (iy+$02),a\r
+0AFD: DD 7E 03      ld   a,(ix+$03)\r
+0B00: 2F            cpl\r
+0B01: 4F            ld   c,a\r
+0B02: 78            ld   a,b\r
+0B03: FE 05         cp   $05\r
+0B05: 38 D8         jr   c,$0ADF\r
+0B07: 0D            dec  c\r
+0B08: C3 DF 0A      jp   $0ADF\r
+\r
+\r
+\r
+;\r
+; Check if the player's bullet has hit any aliens in the swarm.\r
+; If so, delete the shot alien from the swarm, kick off a dying animation, and update player score with relevant points value. \r
+;\r
+\r
+HANDLE_SWARM_ALIEN_TO_PLAYER_BULLET_COLLISION_DETECTION:\r
+0B0B: 21 08 42      ld   hl,$4208            ; pointer to HAS_PLAYER_BULLET_BEEN_FIRED  \r
+0B0E: CB 46         bit  0,(hl)              ; test bit 0. If it's set, player is shooting.\r
+0B10: C8            ret  z                   ; if zero flag is set, that means player is not shooting. Return.\r
+0B11: 23            inc  hl                  ; bump HL to point to PLAYER_BULLET_X\r
+0B12: 7E            ld   a,(hl)              ; read value of PLAYER_BULLET_X\r
+0B13: FE 68         cp   $68                 ; \r
+0B15: D0            ret  nc                  ; if X coordinate >= #$68 (104 decimal) then bullet is not near swarm yet, so exit\r
+0B16: D6 1E         sub  $1E                 ; if X coordinate < #$1E (30 decimal) then bullet has passed the swarm, this subtraction will cause a carry.. \r
+0B18: D8            ret  c                   ; .. so exit.\r
+\r
+; OK, we have to check if an alien has been hit\r
+0B19: 06 06         ld   b,$06               ; number of rows of aliens\r
+0B1B: D6 07         sub  $07\r
+0B1D: D8            ret  c\r
+0B1E: D6 05         sub  $05\r
+0B20: 38 03         jr   c,$0B25             \r
+0B22: 10 F7         djnz $0B1B\r
+0B24: C9            ret\r
+\r
+; B =  row index from 1 to 6. Identifies which row of aliens to check for player bullet collisions. \r
+; 1 = blue row of aliens closest to player; 6 = flagship row\r
+0B25: 23            inc  hl                  ; bump HL to point to PLAYER_BULLET_Y\r
+0B26: 3A 0E 42      ld   a,($420E)           ; read SWARM_SCROLL_VALUE\r
+0B29: 96            sub  (hl)\r
+0B2A: ED 44         neg\r
+0B2C: 4F            ld   c,a\r
+0B2D: E6 0F         and  $0F                 ; mask in low nibble\r
+0B2F: D6 02         sub  $02\r
+0B31: FE 0B         cp   $0B\r
+0B33: D0            ret  nc\r
+0B34: 04            inc  b\r
+0B35: 79            ld   a,c\r
+0B36: E6 F0         and  $F0                 ; mask in high nibble\r
+0B38: 80            add  a,b\r
+0B39: 0F            rrca\r
+0B3A: 0F            rrca\r
+0B3B: 0F            rrca\r
+0B3C: 0F            rrca\r
+0B3D: 5F            ld   e,a                \r
+0B3E: 16 00         ld   d,$00               ; DE is now an offset into the ALIEN_SWARM_FLAGS table\r
+0B40: 21 00 41      ld   hl,$4100            ; load HL with address of ALIEN_SWARM_FLAGS\r
+0B43: 19            add  hl,de               ; add offset to HL. Now HL points to a flag which determines if an alien is present\r
+0B44: CB 46         bit  0,(hl)              ; test flag. If bit 0 is set, our bullet has hit an alien. \r
+0B46: C8            ret  z                   ; bit 0 is not set, we haven't shot an alien, so exit\r
+0B47: 72            ld   (hl),d              ; We've hit an alien! Clear flag to indicate alien is dead.\r
+0B48: 16 01         ld   d,$01               ; command id for DELETE_ALIEN_COMMAND\r
+0B4A: 5D            ld   e,l                 ; parameter: index of alien to delete from swarm\r
+0B4B: CD F2 08      call $08F2               ; call QUEUE_COMMAND \r
+0B4E: 7A            ld   a,d\r
+0B4F: 32 0B 42      ld   ($420B),a           ; set IS_PLAYER_BULLET_DONE flag to 1, so player can shoot again\r
+\r
+; we'll use the misc entry in the INFLIGHT_ALIENS array to display our explosion as a sprite\r
+0B52: 32 B1 42      ld   ($42B1),a           ; set INFLIGHT_ALIEN.IsDying to 1\r
+0B55: AF            xor  a\r
+0B56: 32 B2 42      ld   ($42B2),a           ; set INFLIGHT_ALIEN.StageOfLife to 0\r
+0B59: 2A 09 42      ld   hl,($4209)          ; read PLAYER_BULLET_X and PLAYER_BULLET_Y  in one go\r
+0B5C: 22 B3 42      ld   ($42B3),hl          ; set INFLIGHT_ALIEN.X and INFLIGHT_ALIEN.Y  to bullet coordinates\r
+0B5F: 16 03         ld   d,$03               ; command id for UPDATE_PLAYER_SCORE_COMMAND (see $08f2)\r
+0B61: 7B            ld   a,e                 ; read index of alien that was just killed\r
+0B62: FE 50         cp   $50                 ; was the alien killed in the bottom 3 ranks? (Blue aliens, most common type)\r
+0B64: 38 0C         jr   c,$0B72             ; if index is < $50 (128 decimal) then yes, its a blue alien, goto $0B72\r
+\r
+; only get here if you've shot a higher-ranking alien\r
+0B66: E6 70         and  $70                 ; Mask out the column number of the killed alien from the index. \r
+; Now A is $50 (purple alien row),$60 (red row),$70 (flagship row)\r
+0B68: 0F            rrca\r
+0B69: 0F            rrca\r
+0B6A: 0F            rrca\r
+0B6B: 0F            rrca                     ; Divide A by 16 decimal. \r
+0B6C: D6 04         sub  $04                 ; Subtract 4 to compute parameter for UPDATE_PLAYER_SCORE_COMMAND  \r
+0B6E: 5F            ld   e,a                 ; set parameter for UPDATE_PLAYER_SCORE_COMMAND \r
+0B6F: C3 F2 08      jp   $08F2               ; jump to QUEUE COMMAND. Player score will be updated.\r
+\r
+; You've just shot a lowly blue alien. \r
+0B72: 1E 00         ld   e,$00               ; parameter for UPDATE_PLAYER_SCORE_COMMAND - adds 30 points to player score\r
+0B74: C3 F2 08      jp   $08F2               ; jump to QUEUE COMMAND. Player score will be updated.\r
+\r
+\r
+;\r
+; Iterate through list of active enemy bullets and test if they have hit the player.\r
+; \r
+\r
+HANDLE_PLAYER_TO_ENEMY_BULLET_COLLISION_DETECTION:\r
+0B77: 3A 00 42      ld   a,($4200)           ; read HAS_PLAYER_SPAWNED flag\r
+0B7A: 0F            rrca                     ; move bit 0 into carry\r
+0B7B: D0            ret  nc                  ; if carry is not set, then player has not spawned. Return.\r
+0B7C: DD 21 60 42   ld   ix,$4260            ; point IX to ENEMY_BULLETS_START\r
+0B80: 11 05 00      ld   de,$0005            ; sizeof(ENEMY_BULLET) struct\r
+0B83: 06 0E         ld   b,$0E               ; length of ENEMY_BULLETS array \r
+0B85: CD 8D 0B      call $0B8D               ; call TEST_IF_ENEMY_BULLET_HIT_PLAYER\r
+0B88: DD 19         add  ix,de\r
+0B8A: 10 F9         djnz $0B85\r
+0B8C: C9            ret\r
+\r
+\r
+;\r
+; Check if an enemy bullet hit the player's ship.\r
+;\r
+; Expects: \r
+; E = 5\r
+; IX  = pointer to ENEMY_BULLET structure\r
+;\r
+\r
+TEST_IF_ENEMY_BULLET_HIT_PLAYER:\r
+0B8D: DD CB 00 46   bit  0,(ix+$00)          ; read ENEMY_BULLET.IsActive\r
+0B91: C8            ret  z                   ; return if bullet is not active\r
+\r
+0B92: DD 7E 01      ld   a,(ix+$01)          ; read ENEMY_BULLET.X coordinate \r
+0B95: C6 1F         add  a,$1F               ; player ship is 32 pixels high.. \r
+0B97: 93            sub  e                   ; subtract 5\r
+0B98: 38 10         jr   c,$0BAA\r
+0B9A: D6 09         sub  $09\r
+0B9C: D0            ret  nc\r
+0B9D: 3A 02 42      ld   a,($4202)           ; read PLAYER_Y \r
+0BA0: DD 96 03      sub  (ix+$03)            ; subtract ENEMY_BULLET.Y coordinate\r
+0BA3: 83            add  a,e                 ; add 5\r
+0BA4: FE 0B         cp   $0B\r
+0BA6: D0            ret  nc\r
+0BA7: C3 B4 0B      jp   $0BB4               ; bullet has hit player\r
+0BAA: 3A 02 42      ld   a,($4202)           ; read PLAYER_Y\r
+0BAD: DD 96 03      sub  (ix+$03)            ; subtract ENEMY_BULLET.Y coordinate\r
+0BB0: C6 02         add  a,$02\r
+0BB2: BB            cp   e\r
+0BB3: D0            ret  nc\r
+\r
+; Player's been hit.. deactivate enemy bullet and set hit flag.\r
+0BB4: DD 36 00 00   ld   (ix+$00),$00        ; clear ENEMY_BULLET.IsActive flag. \r
+0BB8: 3E 01         ld   a,$01\r
+0BBA: 32 04 42      ld   ($4204),a           ; set IS_PLAYER_HIT flag. Player will explode.          \r
+0BBD: C9            ret\r
+\r
+\r
+\r
+;\r
+; This important routine is responsible for handling the enemy sprites in the game. \r
+; It reads the position, colour and animation frame of each item in the INFLIGHT_ALIENS array and \r
+; projects it into the relevant INFLIGHT_ALIEN_SPRITE of OBJRAM_BACK_BUF_SPRITES. \r
+;\r
+; In plain English: the sprite back buffer is filled by this routine :) \r
+; \r
+\r
+HANDLE_INFLIGHT_ALIEN_SPRITE_UPDATE:\r
+0BBE: 3A 18 40      ld   a,($4018)           ; read DISPLAY_IS_COCKTAIL_P2\r
+0BC1: 0F            rrca                     ; move flag into carry\r
+0BC2: 38 2E         jr   c,$0BF2             ; if flag is set, goto $0BF2\r
+\r
+0BC4: DD 21 B0 42   ld   ix,$42B0            ; load IX with address of INFLIGHT_ALIENS\r
+0BC8: FD 21 60 40   ld   iy,$4060            ; load IY with address of OBJRAM_BACK_BUF_SPRITES\r
+\r
+; for the first 3 alien sprites, their Y coordinates need to be offset 7 pixels vertically so that the hardware can render them correctly.\r
+0BCC: 06 03         ld   b,$03               ; number of sprites to set sprite state for\r
+0BCE: 0E 07         ld   c,$07               ; set pixel offset to 7\r
+0BD0: CD 20 0C      call $0C20               ; call SET_SPRITE_STATE               \r
+0BD3: 11 20 00      ld   de,$0020            ; sizeof (INFLIGHT_ALIEN)\r
+0BD6: DD 19         add  ix,de               ; bump IX to point to next alien entry\r
+0BD8: 11 04 00      ld   de,$0004            ; sizeof (INFLIGHT_ALIEN_SPRITE)\r
+0BDB: FD 19         add  iy,de               ; bump IY to point to next sprite entry in OBJRAM_BACK_BUF_SPRITES\r
+0BDD: 10 F1         djnz $0BD0\r
+\r
+; for the next 5 alien sprites, their Y coordinates need to be offset 8 pixels vertically.\r
+0BDF: 06 05         ld   b,$05               ; number of sprites to set sprite state for\r
+0BE1: 0C            inc  c                   ; adjust pixel offset to be 8\r
+0BE2: CD 20 0C      call $0C20               ; call SET_SPRITE_STATE\r
+0BE5: 11 20 00      ld   de,$0020            ; sizeof (INFLIGHT_ALIEN)\r
+0BE8: DD 19         add  ix,de               ; bump IX to point to next alien entry\r
+0BEA: 11 04 00      ld   de,$0004            ; sizeof (INFLIGHT_ALIEN_SPRITE)\r
+0BED: FD 19         add  iy,de               ; bump IY to point to next sprite entry in OBJRAM_BACK_BUF_SPRITES\r
+0BEF: 10 F1         djnz $0BE2\r
+0BF1: C9            ret\r
+\r
+; called when display is cocktail. I'm not going to look at this routine until everything else is done...\r
+0BF2: DD 21 B0 42   ld   ix,$42B0            ; load IX with address of INFLIGHT_ALIENS\r
+0BF6: FD 21 60 40   ld   iy,$4060            ; load IY with address of OBJRAM_BACK_BUF_SPRITES\r
+0BFA: 06 03         ld   b,$03\r
+0BFC: 0E 09         ld   c,$09\r
+0BFE: CD 20 0C      call $0C20               ; call SET_SPRITE_STATE\r
+0C01: 11 20 00      ld   de,$0020\r
+0C04: DD 19         add  ix,de\r
+0C06: 11 04 00      ld   de,$0004\r
+0C09: FD 19         add  iy,de\r
+0C0B: 10 F1         djnz $0BFE\r
+0C0D: 06 05         ld   b,$05\r
+0C0F: 0D            dec  c\r
+0C10: CD 20 0C      call $0C20               ; call SET_SPRITE_STATE\r
+0C13: 11 20 00      ld   de,$0020\r
+0C16: DD 19         add  ix,de\r
+0C18: 11 04 00      ld   de,$0004\r
+0C1B: FD 19         add  iy,de\r
+0C1D: 10 F1         djnz $0C10\r
+0C1F: C9            ret\r
+\r
+\r
+;\r
+; Extract the colour, position, animation frame information from an INFLIGHT_ALIEN structure\r
+; and project it into a INFLIGHT_ALIEN_SPRITE.\r
+;\r
+; Expects:\r
+; C = pixel adjustment for Y coordinate\r
+; IX = pointer to INFLIGHT_ALIEN structure to extract information from\r
+; IY = pointer to INFLIGHT_ALIEN_SPRITE structure to be filled\r
+;\r
+\r
+SET_SPRITE_STATE:\r
+0C20: DD CB 00 46   bit  0,(ix+$00)          ; test INFLIGHT_ALIEN.IsActive\r
+0C24: CA 98 0C      jp   z,$0C98             ; if alien is not active, goto SET_INACTIVE_OR_DYING_SPRITE_STATE\r
+0C27: DD 7E 16      ld   a,(ix+$16)          ; read INFLIGHT_ALIEN.Colour\r
+0C2A: FD 77 02      ld   (iy+$02),a          ; write to INFLIGHT_ALIEN_SPRITE.Colour\r
+0C2D: DD 7E 03      ld   a,(ix+$03)          ; read INFLIGHT_ALIEN.X\r
+0C30: D6 08         sub  $08\r
+0C32: FD 77 03      ld   (iy+$03),a          ; write to INFLIGHT_ALIEN_SPRITE.X\r
+0C35: DD 7E 04      ld   a,(ix+$04)          ; read INFLIGHT_ALIEN.Y \r
+0C38: 2F            cpl                      ; flip bits  \r
+0C39: 91            sub  c                   ; subtract pixel adjustment\r
+0C3A: FD 77 00      ld   (iy+$00),a          ; write to INFLIGHT_ALIEN_SPRITE.Y \r
+\r
+; Determine what way the alien is facing and set the sprite XFlip/YFlip/Code properties accordingly\r
+;\r
+; Important point to remember: non-flagship aliens are like bats. They hang upside down in the swarm.\r
+; If you want to see what the sprites look like before being flipped, refer to my sprite grabs @ \r
+; http://seanriddle.com/galaxiansprites.html\r
+\r
+0C3D: DD 7E 05      ld   a,(ix+$05)          ; read INFLIGHT_ALIEN.AnimationFrame\r
+0C40: A7            and  a                   ; set flags \r
+0C41: F2 58 0C      jp   p,$0C58\r
+\r
+0C44: FE FA         cp   $FA                 ; compare to -6 \r
+0C46: FA 82 0C      jp   m,$0C82\r
+\r
+; alien is between an angle of 90 and 180 degrees (as player sees it)\r
+0C49: 2F            cpl\r
+0C4A: C6 12         add  a,$12\r
+0C4C: F6 40         or   $40                 ; set X-Flip bit for sprite\r
+0C4E: DD 86 0F      add  a,(ix+$0f)          ; add in INFLIGHT_ALIEN.AnimFrameStartCode\r
+0C51: FD 77 01      ld   (iy+$01),a          ; write to INFLIGHT_ALIEN_SPRITE.Code\r
+0C54: FD 34 03      inc  (iy+$03)            ; increment INFLIGHT_ALIEN_SPRITE.X\r
+0C57: C9            ret\r
+\r
+0C58: FE 06         cp   $06\r
+0C5A: F2 6E 0C      jp   p,$0C6E\r
+\r
+; alien is between an angle of 180 and 270 degrees (as player sees it)\r
+0C5D: C6 11         add  a,$11\r
+0C5F: F6 C0         or   $C0                 ; set X-Flip and Y-Flip bits for sprite\r
+0C61: DD 86 0F      add  a,(ix+$0f)          ; add in INFLIGHT_ALIEN.AnimFrameStartCode\r
+0C64: FD 77 01      ld   (iy+$01),a          ; write to INFLIGHT_ALIEN_SPRITE.Code\r
+0C67: FD 34 03      inc  (iy+$03)            ; increment INFLIGHT_ALIEN_SPRITE.X\r
+0C6A: FD 34 00      inc  (iy+$00)            ; increment INFLIGHT_ALIEN_SPRITE.Y \r
+0C6D: C9            ret\r
+\r
+0C6E: FE 0C         cp   $0C\r
+0C70: F2 90 0C      jp   p,$0C90\r
+\r
+; alien is between an angle of 270-360 degrees (as player sees it)\r
+0C73: 2F            cpl\r
+0C74: C6 1E         add  a,$1E\r
+0C76: F6 80         or   $80                  ; set Y-Flip bit for sprite\r
+0C78: DD 86 0F      add  a,(ix+$0f)           ; add in INFLIGHT_ALIEN.AnimFrameStartCode\r
+0C7B: FD 77 01      ld   (iy+$01),a           ; write to INFLIGHT_ALIEN_SPRITE.Code\r
+0C7E: FD 34 00      inc  (iy+$00)             ; increment INFLIGHT_ALIEN_SPRITE.Y \r
+0C81: C9            ret\r
+\r
+0C82: FE F4         cp   $F4\r
+0C84: FA 94 0C      jp   m,$0C94\r
+\r
+; alien is between an angle of 0-90 degrees (as player sees it)\r
+0C87: C6 1D         add  a,$1D\r
+0C89: DD 86 0F      add  a,(ix+$0f)           ; add in INFLIGHT_ALIEN.AnimFrameStartCode\r
+0C8C: FD 77 01      ld   (iy+$01),a           ; write to INFLIGHT_ALIEN_SPRITE.Code\r
+0C8F: C9            ret\r
+\r
+0C90: D6 18         sub  $18\r
+0C92: 18 AC         jr   $0C40\r
+\r
+0C94: C6 18         add  a,$18\r
+0C96: 18 A8         jr   $0C40\r
+\r
+\r
+;\r
+; Jumped to from SET_SPRITE_STATE when the INFLIGHT_ALIEN is inactive or dying.\r
+;\r
+; Expects:\r
+; C = pixel adjustment for Y coordinate\r
+; IX = pointer to INFLIGHT_ALIEN structure\r
+; IY = pointer to INFLIGHT_ALIEN_SPRITE structure\r
+\r
+SET_INACTIVE_OR_DYING_SPRITE_STATE:\r
+0C98: DD CB 01 46   bit  0,(ix+$01)          ; test INFLIGHT_ALIEN.IsDying flag\r
+0C9C: CA BA 0C      jp   z,$0CBA             ; if the alien has finally expired, goto $0CBA\r
+\r
+; alien is dying\r
+0C9F: FD 36 02 07   ld   (iy+$02),$07        ; set INFLIGHT_ALIEN_SPRITE.Colour\r
+0CA3: DD 7E 03      ld   a,(ix+$03)          ; read INFLIGHT_ALIEN.X\r
+0CA6: D6 08         sub  $08\r
+0CA8: FD 77 03      ld   (iy+$03),a          ; set INFLIGHT_ALIEN_SPRITE.X\r
+0CAB: DD 7E 04      ld   a,(ix+$04)          ; read INFLIGHT_ALIEN.Y \r
+0CAE: 2F            cpl\r
+0CAF: 91            sub  c                   ; subtract pixel adjustment value\r
+0CB0: FD 77 00      ld   (iy+$00),a          ; set INFLIGHT_ALIEN_SPRITE.Y \r
+0CB3: DD 7E 12      ld   a,(ix+$12)          ; read INFLIGHT_ALIEN.DyingAnimFrameCode\r
+0CB6: FD 77 01      ld   (iy+$01),a          ; set INFLIGHT_ALIEN_SPRITE.Code \r
+0CB9: C9            ret\r
+\r
+; This alien has died. Move sprite off-screen\r
+0CBA: FD 36 03 F8   ld   (iy+$03),$F8        ; set INFLIGHT_ALIEN_SPRITE.X to value offscreen\r
+0CBE: FD 36 00 F8   ld   (iy+$00),$F8        ; set INFLIGHT_ALIEN_SPRITE.Y to value offscreen\r
+0CC2: C9            ret\r
+\r
+\r
+\r
+;\r
+; This routine is responsible for processing all 8 elements in the INFLIGHT_ALIENS array. \r
+;\r
+\r
+HANDLE_INFLIGHT_ALIENS:\r
+0CC3: DD 21 B0 42   ld   ix,$42B0            ; load IX with address of INFLIGHT_ALIENS\r
+0CC7: 11 20 00      ld   de,$0020            ; sizeof(INFLIGHT_ALIEN)\r
+0CCA: 06 08         ld   b,$08               ; 1 misc + 7 attacking aliens to process\r
+0CCC: D9            exx\r
+0CCD: CD D6 0C      call $0CD6               ; call HANDLE_INFLIGHT_ALIEN_STAGE_OF_LIFE\r
+0CD0: D9            exx\r
+0CD1: DD 19         add  ix,de               ; bump IX to point to next INFLIGHT_ALIEN structure\r
+0CD3: 10 F7         djnz $0CCC               ; do while b!=0\r
+0CD5: C9            ret\r
+\r
+\r
+\r
+\r
+;\r
+; Like humans, inflight aliens go through stages of life. They leave home, attack humans, maybe do a loop the loop,\r
+; then (maybe) return home. Just like we do!\r
+; \r
+; This routine is used to invoke actions appropriate for the alien's stage of life.\r
+;\r
+; Expects:\r
+; IX = pointer to INFLIGHT_ALIEN structure.\r
+;\r
+\r
+HANDLE_INFLIGHT_ALIEN_STAGE_OF_LIFE:\r
+0CD6: DD CB 01 46   bit  0,(ix+$01)          ; test INFLIGHT_ALIEN.IsDying flag\r
+0CDA: C2 E4 10      jp   nz,$10E4            ; if alien is dying, goto HANDLE_INFLIGHT_ALIEN_DYING\r
+0CDD: DD CB 00 46   bit  0,(ix+$00)          ; test INFLIGHT_ALIEN.IsActive flag \r
+0CE1: C8            ret  z                   ; exit if not active\r
+\r
+; We need to determine what stage of life the inflight alien is at, then call the appropriate function to\r
+; tell it how to behave. \r
+\r
+0CE2: DD 7E 02      ld   a,(ix+$02)          ; read INFLIGHT_ALIEN.StageOfLife \r
+0CE5: EF            rst  $28                 ; jump to code @ $0CE6 + (A*2)\r
+0CE6: \r
+      06 0D         ; $0D06                  ; INFLIGHT_ALIEN_PACKS_BAGS\r
+      71 0D         ; $0D71                  ; INFLIGHT_ALIEN_FLIES_IN_ARC\r
+      D1 0D         ; $0DD1                  ; INFLIGHT_ALIEN_READY_TO_ATTACK\r
+      2B 0E         ; $0E2B                  ; INFLIGHT_ALIEN_ATTACKING_PLAYER\r
+      6B 0E         ; $0E6B                  ; INFLIGHT_ALIEN_NEAR_BOTTOM_OF_SCREEN\r
+      99 0E         ; $0E99                  ; INFLIGHT_ALIEN_REACHED_BOTTOM_OF_SCREEN\r
+      07 0F         ; $0F07                  ; INFLIGHT_ALIEN_RETURNING_TO_SWARM\r
+      3C 0F         ; $0F3C                  ; INFLIGHT_ALIEN_CONTINUING_ATTACK_RUN_FROM_TOP_OF_SCREEN \r
+      66 0F         ; $0F66                  ; INFLIGHT_ALIEN_FULL_SPEED_CHARGE \r
+      AF 0F         ; $0FAF                  ; INFLIGHT_ALIEN_ATTACKING_PLAYER_AGGRESSIVELY\r
+      1F 10         ; $101F                  ; INFLIGHT_ALIEN_LOOP_THE_LOOP\r
+      8E 10         ; $108E                  ; INFLIGHT_ALIEN_COMPLETE_LOOP\r
+      91 10         ; $1091                  ; INFLIGHT_ALIEN_UNKNOWN_1091\r
+      9B 10         ; $109B                  ; INFLIGHT_ALIEN_CONVOY_CHARGER_SET_COLOUR_POS_ANIM\r
+      C2 10         ; $10C2                  ; INFLIGHT_ALIEN_CONVOY_CHARGER_START_SCROLL  \r
+      D8 10         ; $10D8                  ; INFLIGHT_ALIEN_CONVOY_CHARGER_DO_SCROLL\r
+\r
+\r
+\r
+;\r
+; An alien's just about to break away from the swarm. It's leaving home! \r
+; Before it can do so, we need to set up an INFLIGHT_ALIEN structure with defaults. \r
+;\r
+; Expects:\r
+; IX = pointer to INFLIGHT_ALIEN structure\r
+;\r
+\r
+INFLIGHT_ALIEN_PACKS_BAGS:\r
+0D06: DD 36 17 00   ld   (ix+$17),$00        ; clear INFLIGHT_ALIEN.SortieCount \r
+0D0A: 3E 01         ld   a,$01\r
+0D0C: 32 C2 41      ld   ($41C2),a           ; set ENABLE_ALIEN_ATTACK_SOUND to 1.\r
+0D0F: CD 47 11      call $1147               ; call SET_INFLIGHT_ALIEN_START_POSITION\r
+0D12: DD 5E 07      ld   e,(ix+$07)          ; set command parameter to INFLIGHT_ALIEN.IndexInSwarm\r
+0D15: 16 01         ld   d,$01               ; command: DELETE_ALIEN_COMMAND\r
+0D17: CD F2 08      call $08F2               ; call QUEUE_COMMAND\r
+0D1A: 7B            ld   a,e                 ; load A with INFLIGHT_ALIEN.IndexInSwarm\r
+0D1B: E6 70         and  $70                 ; keep the row start, remove the column number\r
+0D1D: 21 D1 1D      ld   hl,$1DD1\r
+0D20: 0F            rrca                     ; divide the row offset...\r
+0D21: 0F            rrca\r
+0D22: 0F            rrca                     ; .. by 8.\r
+0D23: 5F            ld   e,a\r
+0D24: 16 00         ld   d,$00               ; Extend A into DE\r
+0D26: 19            add  hl,de               ; HL = $1DD1 + (row number of alien /8)\r
+0D27: 7E            ld   a,(hl)\r
+0D28: DD 77 16      ld   (ix+$16),a          ; set INFLIGHT_ALIEN.Colour\r
+\r
+0D2B: 23            inc  hl\r
+0D2C: 7E            ld   a,(hl)\r
+0D2D: DD 77 18      ld   (ix+$18),a          ; set INFLIGHT_ALIEN.Speed\r
+\r
+0D30: 7B            ld   a,e\r
+0D31: FE 0E         cp   $0E                 ; flagship?\r
+0D33: 28 23         jr   z,$0D58             ; yes, goto $0D58\r
+\r
+0D35: DD 36 0F 00   ld   (ix+$0f),$00        ; set INFLIGHT_ALIEN.AnimFrameStartCode\r
+0D39: DD 36 10 03   ld   (ix+$10),$03        ; set INFLIGHT_ALIEN.TempCounter1 to speed of animation (higher number = slower)\r
+0D3D: DD 36 11 0C   ld   (ix+$11),$0C        ; set INFLIGHT_ALIEN.TempCounter2 to total number of animation frames \r
+0D41: DD 36 13 00   ld   (ix+$13),$00        ; set INFLIGHT_ALIEN.ArcTableLsb\r
+0D45: DD 34 02      inc  (ix+$02)            ; set INFLIGHT_ALIEN.StageOfLife to INFLIGHT_ALIEN_FLIES_IN_ARC\r
+\r
+0D48: DD CB 06 46   bit  0,(ix+$06)          ; test INFLIGHT_ALIEN.ArcClockwise\r
+0D4C: 20 05         jr   nz,$0D53            ; if alien will be facing right when it breaks away from swarm, goto $0D53\r
+0D4E: DD 36 05 0C   ld   (ix+$05),$0C        ; set INFLIGHT_ALIEN.AnimationFrame\r
+0D52: C9            ret\r
+\r
+0D53: DD 36 05 F4   ld   (ix+$05),$F4        ; set INFLIGHT_ALIEN.AnimationFrame\r
+0D57: C9            ret\r
+\r
+; This code is called for flagships. We need to count how many escorts we have.\r
+0D58: DD 36 0F 18   ld   (ix+$0f),$18        ; set INFLIGHT_ALIEN.AnimFrameStartCode\r
+0D5C: AF            xor  a\r
+0D5D: DD CB 20 46   bit  0,(ix+$20)          ; test if we have an escort\r
+0D61: 28 01         jr   z,$0D64             ; no, goto $0D64\r
+0D63: 3C            inc  a                   ; increment escort count \r
+0D64: DD CB 40 46   bit  0,(ix+$40)          ; test if we have an escort\r
+0D68: 28 01         jr   z,$0D6B             ; no, goto $0D6B\r
+0D6A: 3C            inc  a                   ; increment escort count\r
+0D6B: 32 2A 42      ld   ($422A),a           ; set FLAGSHIP_ESCORT_COUNT\r
+0D6E: C3 39 0D      jp   $0D39               ; finalise setting up flagship\r
+\r
+\r
+\r
+;\r
+; This function is used to animate an inflight alien flying in a 90 degree arc. \r
+; It is called when an alien is breaking off from the swarm to attack the player, \r
+; or when it is completing the last 90 degrees of a 360 degree loop the loop. \r
+; As soon as the arc is complete, the alien's stage of life is set to\r
+; INFLIGHT_ALIEN_READY_TO_ATTACK.\r
+;\r
+; Expects:\r
+; IX = pointer to INFLIGHT_ALIEN that is breaking away from swarm.\r
+;\r
+\r
+INFLIGHT_ALIEN_FLIES_IN_ARC:\r
+0D71: DD 6E 13      ld   l,(ix+$13)          ; read INFLIGHT_ALIEN.ArcTableLsb\r
+0D74: 26 1E         ld   h,$1E               ; Now HL points to an entry in INFLIGHT_ALIEN_ARC_TABLE at $1E00. \r
+0D76: DD 7E 03      ld   a,(ix+$03)          ; read INFLIGHT_ALIEN.X \r
+0D79: 86            add  a,(hl)              ; add in X delta from table\r
+0D7A: DD 77 03      ld   (ix+$03),a          ; update INFLIGHT_ALIEN.X\r
+0D7D: 2C            inc  l                   ; bump HL to Y coordinate in table \r
+0D7E: DD CB 06 46   bit  0,(ix+$06)          ; test INFLIGHT_ALIEN.ArcClockwise\r
+0D82: 20 24         jr   nz,$0DA8            ; if alien is facing right, goto $0DA8\r
+\r
+; alien that is leaving swarm to attack player will arc up and left.\r
+; HL = pointer to table defining arc (see $1E00 for table layout)\r
+0D84: DD 7E 04      ld   a,(ix+$04)          ; load A with INFLIGHT_ALIEN.Y \r
+0D87: 86            add  a,(hl)              ; add in Y delta from table                       \r
+0D88: DD 77 04      ld   (ix+$04),a          ; update INFLIGHT_ALIEN.Y \r
+0D8B: C6 07         add  a,$07\r
+0D8D: FE 0E         cp   $0E                 ; is the alien off-screen?\r
+0D8F: 38 3B         jr   c,$0DCC             ; if A< #$0E, its gone off screen, so make alien return to swarm from top of screen.\r
+0D91: 2C            inc  l                   ; bump HL to point to next X,Y coordinate pair in table\r
+0D92: DD 75 13      ld   (ix+$13),l          ; and update INFLIGHT_ALIEN.ArcTableLsb\r
+; Tempcounter1 = delay before changing animation frame\r
+; Tempcounter2 = number of animation frames left to do \r
+0D95: DD 35 10      dec  (ix+$10)            ; decrement INFLIGHT_ALIEN.TempCounter1\r
+0D98: C0            ret  nz\r
+0D99: DD 36 10 04   ld   (ix+$10),$04        ; reset INFLIGHT_ALIEN.TempCounter1\r
+0D9D: DD 35 05      dec  (ix+$05)            ; update INFLIGHT_ALIEN.AnimationFrame to rotate the alien left\r
+0DA0: DD 35 11      dec  (ix+$11)            ; decrement INFLIGHT_ALIEN.TempCounter2 \r
+0DA3: C0            ret  nz                  ; if we've not done all of the animation frames, exit\r
+\r
+; OK, we've done all of the animation frames. The alien's ready to attack the player.\r
+0DA4: DD 34 02      inc  (ix+$02)            ; set stage of alien's life to INFLIGHT_ALIEN_READY_TO_ATTACK\r
+0DA7: C9            ret\r
+\r
+; alien that is leaving swarm to attack player is arcing up and right\r
+; HL = pointer to table defining arc\r
+; IX = pointer to INFLIGHT_ALIEN structure\r
+0DA8: DD 7E 04      ld   a,(ix+$04)          ; read INFLIGHT_ALIEN.Y \r
+0DAB: 96            sub  (hl)                ; read Y delta from table and subtract from INFLIGHT_ALIEN.Y \r
+0DAC: DD 77 04      ld   (ix+$04),a          ; update INFLIGHT_ALIEN.Y \r
+0DAF: C6 07         add  a,$07\r
+0DB1: FE 0E         cp   $0E                 ; is the alien off-screen?\r
+0DB3: 38 17         jr   c,$0DCC             ; if A < #$0E, its gone off screen, so make alien return to swarm from top of screen. \r
+0DB5: 2C            inc  l                   ; bump HL to point to next X,Y coordinate pair in table\r
+0DB6: DD 75 13      ld   (ix+$13),l          ; and update INFLIGHT_ALIEN.ArcTableLsb\r
+; Tempcounter1 = delay before changing animation frame\r
+; Tempcounter2 = number of animation frames left to do \r
+0DB9: DD 35 10      dec  (ix+$10)            ; decrement INFLIGHT_ALIEN.TempCounter1\r
+0DBC: C0            ret  nz\r
+0DBD: DD 36 10 04   ld   (ix+$10),$04        ; reset INFLIGHT_ALIEN.TempCounter1\r
+0DC1: DD 34 05      inc  (ix+$05)            ; update INFLIGHT_ALIEN.AnimationFrame to rotate the alien right\r
+0DC4: DD 35 11      dec  (ix+$11)            ; decrement INFLIGHT_ALIEN.TempCounter2\r
+0DC7: C0            ret  nz                  ; if we've not done all of the animation frames, exit\r
+\r
+; OK, we've done all of the animation frames. The alien's ready to attack the player.\r
+0DC8: DD 34 02      inc  (ix+$02)            ; move to next stage of alien's life\r
+0DCB: C9            ret                 \r
+\r
+; if we get here, an alien leaving the swarm has gone offscreen. It will return to the swarm from the top of the screen.\r
+0DCC: DD 36 02 05   ld   (ix+$02),$05        ; set INFLIGHT_ALIEN.StageOfLife \r
+0DD0: C9            ret\r
+\r
+\r
+\r
+;\r
+; An alien that has just completed an arc animation (see docs @ $0D71 and $101F) is now ready to attack the player. \r
+;\r
+; Expects:\r
+; IX = pointer to INFLIGHT_ALIEN that will attack\r
+;\r
+\r
+INFLIGHT_ALIEN_READY_TO_ATTACK:\r
+0DD1: DD 34 03      inc  (ix+$03)            ; increment INFLIGHT_ALIEN.X\r
+0DD4: DD 7E 07      ld   a,(ix+$07)          ; read INFLIGHT_ALIEN.IndexInSwarm\r
+0DD7: E6 70         and  $70                 ; keep the row, remove the column\r
+0DD9: FE 60         cp   $60                 ; is this a red alien?\r
+0DDB: 28 43         jr   z,$0E20             ; yes, goto $0E20\r
+\r
+INFLIGHT_ALIEN_DEFINE_FLIGHTPATH:\r
+0DDD: 3A 02 42      ld   a,($4202)           ; read PLAYER_Y \r
+0DE0: 47            ld   b,a\r
+0DE1: DD 7E 04      ld   a,(ix+$04)          ; read INFLIGHT_ALIEN.Y \r
+0DE4: 90            sub  b                   ; A = INFLIGHT_ALIEN.Y  - PLAYER_Y\r
+0DE5: 38 28         jr   c,$0E0F             ; if alien is to right of player, goto $0E0F\r
+\r
+; alien is to left of player\r
+; A = signed number representing distance in pixels between alien Y and player Y. \r
+0DE7: 1F            rra                      ; divide distance by 2     \r
+0DE8: C6 10         add  a,$10               ; add $10 (16 decimal) to product\r
+; clamp A between $30 and $70\r
+0DEA: FE 30         cp   $30                 ; compare to 48 (decimal)\r
+0DEC: 30 02         jr   nc,$0DF0            ; if A>=48 goto $0DF0\r
+0DEE: 3E 30         ld   a,$30\r
+0DF0: FE 70         cp   $70                 ; compare to 112 (decimal)  NB: 112 is half the screen height in pixels\r
+0DF2: 38 02         jr   c,$0DF6\r
+0DF4: 3E 70         ld   a,$70\r
+\r
+; PivotYValue is a Y coordinate to pivot around. You could think of it like the "origin" Y coordinate. \r
+; PivotYValueAdd is a delta (offset) to add to PivotYValue to produce the correct Y coordinate of the alien.\r
+;\r
+; PivotYValueAdd will increment if the player is to the left of the alien when it leaves the swarm,\r
+; or decrement if the player is to the right. \r
+\r
+0DF6: DD 77 19      ld   (ix+$19),a          ; set INFLIGHT_ALIEN.PivotYValueAdd\r
+0DF9: DD 96 04      sub  (ix+$04)            ; subtract INFLIGHT_ALIEN.Y \r
+0DFC: ED 44         neg\r
+0DFE: DD 77 09      ld   (ix+$09),a          ; set INFLIGHT_ALIEN.PivotYValue. Now PivotYValue + PivotYValueAdd = INFLIGHT_ALIEN.Y\r
+0E01: AF            xor  a\r
+0E02: DD 77 1A      ld   (ix+$1a),a\r
+0E05: DD 77 1B      ld   (ix+$1b),a\r
+0E08: DD 77 1C      ld   (ix+$1c),a\r
+\r
+0E0B: DD 34 02      inc  (ix+$02)             ; set stage of life to INFLIGHT_ALIEN_ATTACKING_PLAYER or INFLIGHT_ALIEN_ATTACKING_PLAYER_AGGRESSIVELY\r
+0E0E: C9            ret\r
+\r
+; alien is to right of player\r
+; A = signed number representing distance in pixels between alien and player. \r
+0E0F: 1F            rra                       ; perform a shift right, with sign bit preserved\r
+0E10: D6 10         sub  $10\r
+; clamp A between -48 and -112 decimal\r
+0E12: FE D0         cp   $D0                  ; compare to -48 (decimal)\r
+0E14: 38 02         jr   c,$0E18\r
+0E16: 3E D0         ld   a,$D0             \r
+0E18: FE 90         cp   $90                  ; compare to -112 (decimal)  NB: 112 is half the screen height in pixels\r
+0E1A: 30 DA         jr   nc,$0DF6\r
+0E1C: 3E 90         ld   a,$90\r
+0E1E: 18 D6         jr   $0DF6\r
+\r
+\r
+0E20: 3A D0 42      ld   a,($42D0)           ; address of INFLIGHT_ALIENS[1].IsActive\r
+0E23: 0F            rrca                     ; move flag into carry\r
+0E24: 30 B7         jr   nc,$0DDD            ; if not set then we are not part of a convoy, goto INFLIGHT_ALIEN_DEFINE_FLIGHTPATH\r
+\r
+; make the alien accompany the flagship as part of a convoy. The PivotYValueAdd of the alien is the same as the flagship,\r
+; so it will fly the same path.\r
+0E26: 3A E9 42      ld   a,($42E9)           ; read flagship INFLIGHT_ALIENS[1].PivotYValueAdd  \r
+0E29: 18 CB         jr   $0DF6\r
+\r
+\r
+;\r
+; This is probably the most important routine for the INFLIGHT_ALIEN. \r
+;\r
+; It is responsible for making an INFLIGHT_ALIEN fly down the screen, dropping bombs when it can.\r
+;\r
+; Expects:\r
+; IX = pointer to INFLIGHT_ALIEN structure\r
+;\r
+\r
+INFLIGHT_ALIEN_ATTACKING_PLAYER:\r
+0E2B: DD 34 03      inc  (ix+$03)            ; increment INFLIGHT_ALIEN.X \r
+0E2E: CD 6B 11      call $116B               ; call UPDATE_INFLIGHT_ALIEN_YADD\r
+0E31: DD 7E 09      ld   a,(ix+$09)          ; load A with INFLIGHT_ALIEN.PivotYValue\r
+0E34: DD 86 19      add  a,(ix+$19)          ; add in INFLIGHT_ALIEN.PivotYValueAdd to produce a Y coordinate                                                   \r
+0E37: DD 77 04      ld   (ix+$04),a          ; write to INFLIGHT_ALIEN.Y \r
+0E3A: C6 07         add  a,$07\r
+0E3C: FE 0E         cp   $0E\r
+0E3E: 38 24         jr   c,$0E64             ; if the alien has gone off the side of the screen, return to swarm\r
+0E40: DD 7E 03      ld   a,(ix+$03)          ; load A with INFLIGHT_ALIEN.X\r
+0E43: C6 48         add  a,$48\r
+0E45: 38 20         jr   c,$0E67             ; if the alien is nearing the bottom of the screen, speed it up!\r
+0E47: 3A 00 42      ld   a,($4200)           ; read HAS_PLAYER_SPAWNED\r
+0E4A: 0F            rrca                     ; move flag into carry\r
+0E4B: D0            ret  nc                  ; return if player has not spawned\r
+0E4C: CD B0 11      call $11B0               ; call CALCULATE_INFLIGHT_ALIEN_LOOKAT_ANIM_FRAME\r
+\r
+; alien won't shoot at you if a flagship has been hit\r
+0E4F: 3A 2B 42      ld   a,($422B)           ; read IS_FLAGSHIP_HIT\r
+0E52: 0F            rrca                     ; move flag into carry\r
+0E53: D8            ret  c                   ; return if flagship was hit\r
+\r
+; Can this alien start shooting at you?\r
+;\r
+; code from $0E54-0E63 is akin to:\r
+;\r
+; byte yToCheck = INFLIGHT_ALIEN.X;\r
+; for (byte l=0; l<INFLIGHT_ALIEN_SHOOT_RANGE_MUL;l++)\r
+; {\r
+;     if (yToCheck == INFLIGHT_ALIEN_SHOOT_EXACT_X)\r
+;        goto TRY SPAWN_ENEMY_BULLET;\r
+;     else\r
+;        yToCheck+=0x19;\r
+; }\r
+;\r
+\r
+0E54: 2A 13 42      ld   hl,($4213)          ; get INFLIGHT_ALIEN_SHOOT_EXACT_X into H and INFLIGHT_ALIEN_SHOOT_RANGE_MUL into L\r
+0E57: DD 7E 03      ld   a,(ix+$03)          ; read INFLIGHT_ALIEN.X\r
+\r
+0E5A: BC            cp   h                   ; compare A to INFLIGHT_ALIEN_SHOOT_EXACT_X \r
+0E5B: CA E0 11      jp   z,$11E0             ; if equal, jump to TRY_SPAWN_ENEMY_BULLET\r
+0E5E: C6 19         add  a,$19               ; add $19 (25 decimal) to A\r
+0E60: 2D            dec  l                   ; and try again...\r
+0E61: 20 F7         jr   nz,$0E5A            ; until L is 0.\r
+0E63: C9            ret\r
+\r
+\r
+; If only one of these INCs are called (see $0E45), INFLIGHT_ALIEN.StageOfLife will be set to INFLIGHT_ALIEN_NEAR_BOTTOM_OF_SCREEN.\r
+; If both these INCs are called (see $0E3E), set INFLIGHT_ALIEN.StageOfLife to INFLIGHT_ALIEN_REACHED_BOTTOM_OF_SCREEN. \r
+0E64: DD 34 02      inc  (ix+$02)      \r
+0E67: DD 34 02      inc  (ix+$02)\r
+0E6A: C9            ret\r
+\r
+\r
+;\r
+; When an alien is close to the horizontal plane where the player resides, it speeds up to zoom by (or into) the player.\r
+;\r
+; Expects:\r
+; IX = pointer to INFLIGHT_ALIEN structure\r
+;\r
+\r
+INFLIGHT_ALIEN_NEAR_BOTTOM_OF_SCREEN:\r
+0E6B: 3A 5F 42      ld   a,($425F)           ; read TIMING_VARIABLE\r
+0E6E: E6 01         and  $01                 ; ..now A is either 0 or 1.\r
+0E70: 3C            inc  a                   ; ..now A is either 1 or 2. \r
+0E71: DD 86 03      add  a,(ix+$03)          ; Add either 1 or 2 pixels to INFLIGHT_ALIEN.X\r
+0E74: DD 77 03      ld   (ix+$03),a          ; and update INFLIGHT_ALIEN.X\r
+0E77: D6 06         sub  $06\r
+0E79: FE 03         cp   $03                 ; has alien gone off the bottom of the screen?\r
+0E7B: 38 18         jr   c,$0E95             ; yes, goto $0E95\r
+\r
+0E7D: CD 6B 11      call $116B               ; call UPDATE_INFLIGHT_ALIEN_YADD \r
+0E80: DD 7E 19      ld   a,(ix+$19)          ; read INFLIGHT_ALIEN.PivotYValueAdd        \r
+0E83: A7            and  a                   ; set flags - we are interested if its a minus value\r
+0E84: FA 90 0E      jp   m,$0E90             ; if the PivotYValueAdd is a negative value, goto $0E90\r
+\r
+0E87: DD 86 09      add  a,(ix+$09)          ; add INFLIGHT_ALIEN.PivotYValue \r
+0E8A: 38 09         jr   c,$0E95             ; carry flag set if alien has gone off side of screen,  goto $0E95\r
+\r
+0E8C: DD 77 04      ld   (ix+$04),a          ; set INFLIGHT_ALIEN.Y \r
+0E8F: C9            ret\r
+\r
+0E90: DD 86 09      add  a,(ix+$09)          ; add INFLIGHT_ALIEN.PivotYValue\r
+0E93: 38 F7         jr   c,$0E8C\r
+\r
+; alien's went off the bottom or the side of the screen. \r
+0E95: DD 34 02      inc  (ix+$02)            ; now call INFLIGHT_ALIEN_REACHED_BOTTOM_OF_SCREEN stage of life.\r
+0E98: C9            ret\r
+\r
+\r
+;\r
+; An inflight alien has flown past the player and left the bottom of the visible screen. \r
+; \r
+;\r
+; If the alien is not a flagship, it will always return to the top of the screen.\r
+; Then, its behaviour is determined by flag state:\r
+; \r
+;    If the HAS_PLAYER_SPAWNED flag is clear, the alien will rejoin the swarm. \r
+;\r
+;    If both of the HAVE_AGGRESSIVE_ALIENS and HAVE_NO_BLUE_OR_PURPLE_ALIENS flags are clear, \r
+;    the alien will rejoin the swarm.\r
+;\r
+;    Otherwise, if the criteria above is not satisfied, the alien will keep attacking the player.  \r
+;    \r
+;        \r
+; If the alien is a flagship, then the rules described @ $0EDA (INFLIGHT_ALIEN_FLAGSHIP_REACHED_BOTTOM_OF_SCREEN) apply. \r
+;\r
+; Expects:\r
+; IX = pointer to INFLIGHT_ALIEN structure\r
+;\r
+\r
+INFLIGHT_ALIEN_REACHED_BOTTOM_OF_SCREEN:\r
+0E99: DD 36 03 08   ld   (ix+$03),$08        ; set INFLIGHT_ALIEN.X to position at very top of screen\r
+0E9D: DD 34 17      inc  (ix+$17)            ; increment INFLIGHT_ALIEN.SortieCount\r
+0EA0: DD 36 05 00   ld   (ix+$05),$00        ; clear INFLIGHT_ALIEN.AnimationFrame\r
+\r
+; what type of alien are we dealing with?\r
+0EA4: DD 7E 07      ld   a,(ix+$07)          ; read INFLIGHT_ALIEN.IndexInSwarm  \r
+0EA7: E6 70         and  $70                 ; remove the column number from the index, keep the row\r
+0EA9: FE 70         cp   $70                 ; is this alien a flagship?\r
+0EAB: 28 2D         jr   z,$0EDA             ; yes, goto INFLIGHT_ALIEN_FLAGSHIP_REACHED_BOTTOM_OF_SCREEN\r
+\r
+;if the player has not spawned, the alien will return to the swarm.\r
+0EAD: 3A 00 42      ld   a,($4200)           ; read HAS_PLAYER_SPAWNED\r
+0EB0: 0F            rrca                     ; move flag into carry\r
+0EB1: 30 23         jr   nc,$0ED6            ; if player has not spawned yet, goto $0ED6 - aliens return to swarm\r
+\r
+;  if HAVE_AGGRESSIVE_ALIENS OR HAVE_NO_BLUE_OR_PURPLE_ALIENS flags are set, the alien will keep attacking (see $0EBF).\r
+;  otherwise the alien returns to the swarm (see $0ED3 and $0F07)\r
+0EB3: 3A 24 42      ld   a,($4224)           ; read HAVE_AGGRESSIVE_ALIENS flag\r
+0EB6: A7            and  a                   ; test flag\r
+0EB7: 20 06         jr   nz,$0EBF            ; if aliens are aggressive, make alien reappear at top of screen, keep attacking \r
+0EB9: 3A 21 42      ld   a,($4221)           ; read HAVE_NO_BLUE_OR_PURPLE_ALIENS\r
+0EBC: A7            and  a                   ; test flag             \r
+0EBD: 28 17         jr   z,$0ED6             ; if we do have any blue or purple aliens, goto $0ED6 - aliens return to swarm\r
+\r
+; alien reappears at top of screen and will keep attacking - it will not return to swarm. \r
+; add some unpredictability to where it reappears, so that player can't wait for it and shoot it easily\r
+0EBF: DD 7E 04      ld   a,(ix+$04)          ; read INFLIGHT_ALIEN.Y \r
+0EC2: 1F            rra                      ; divide by 2 (don't worry about carry, it was cleared by AND above)\r
+0EC3: 4F            ld   c,a                 ; preserve in C              \r
+0EC4: CD 3C 00      call $003C               ; call GENERATE_RANDOM_NUMBER\r
+0EC7: E6 1F         and  $1F                 ; ensure random number is between 0..31 decimal\r
+0EC9: 81            add  a,c                 \r
+0ECA: C6 20         add  a,$20               \r
+0ECC: DD 77 04      ld   (ix+$04),a          ; set INFLIGHT_ALIEN.Y   \r
+0ECF: DD 36 10 28   ld   (ix+$10),$28        ; set INFLIGHT_ALIEN.TempCounter1 for INFLIGHT_ALIEN_UNKNOWN_OF3C to use.\r
+\r
+; if both of these incs are called, the stage of life will be set to INFLIGHT_ALIEN_CONTINUING_ATTACK_RUN_FROM_TOP_OF_SCREEN. \r
+; if only the inc @ $0ED6 is invoked (see $0EB1), then the stage of life will be set to INFLIGHT_ALIEN_RETURNING_TO_SWARM.\r
+0ED3: DD 34 02      inc  (ix+$02)            ; increment INFLIGHT_ALIEN.StageOfLife\r
+0ED6: DD 34 02      inc  (ix+$02)            ; increment INFLIGHT_ALIEN.StageOfLife\r
+0ED9: C9            ret\r
+\r
+\r
+;\r
+; A flagship has gone off screen.\r
+;\r
+; If the flagship had an escort, it will return to the top of the screen to fight again.\r
+; If the flagship had no escort, it will flee the level. \r
+; A maximum of 2 fleeing flagships can be carried over to the next level.\r
+;\r
+; Expects:\r
+; IX = pointer to INFLIGHT_ALIEN structure\r
+;\r
+\r
+INFLIGHT_ALIEN_FLAGSHIP_REACHED_BOTTOM_OF_SCREEN:\r
+0EDA: 3A 2A 42      ld   a,($422A)           ; read FLAGSHIP_ESCORT_COUNT\r
+0EDD: A7            and  a                   ; test if flagship actually had any escort!\r
+0EDE: 20 12         jr   nz,$0EF2            ; if flagship has escort, goto INFLIGHT_ALIEN_COUNT_FLAGSHIP_ESCORTS\r
+\r
+; This flagship has no escort. It has escaped the level. \r
+; Deactivate the INFLIGHT_ALIEN record, and check if this flagship can be carried over to the next wave.\r
+0EE0: DD 36 00 00   ld   (ix+$00),$00        ; reset INFLIGHT_ALIEN.IsActive\r
+0EE4: 3A 1E 42      ld   a,($421E)           ; read FLAGSHIP_SURVIVOR_COUNT\r
+0EE7: 3C            inc  a                   ; add another one to the survivor count!\r
+0EE8: FE 03         cp   $03                 ; have we got 3 surviving flagships?\r
+0EEA: 38 02         jr   c,$0EEE             ; if we have less than 3, that's OK, goto $0EEE\r
+\r
+; We seem to have 3 flagships but only 2 flagships are allowed to be carried over...\r
+0EEC: 3E 02         ld   a,$02               ; clamp surviving flagship count to 2.\r
+\r
+0EEE: 32 1E 42      ld   ($421E),a           ; set FLAGSHIP_SURVIVOR_COUNT\r
+0EF1: C9            ret\r
+\r
+; count how many aliens were escorting the flagship. \r
+INFLIGHT_ALIEN_COUNT_FLAGSHIP_ESCORTS:\r
+0EF2: AF            xor  a\r
+0EF3: DD CB 20 46   bit  0,(ix+$20)          ; test IsActive flag of first escort  \r
+0EF7: 28 01         jr   z,$0EFA             ; \r
+0EF9: 3C            inc  a                   ; increment escort count\r
+0EFA: DD CB 40 46   bit  0,(ix+$40)          ; test IsActive flag of second escort\r
+0EFE: 28 01         jr   z,$0F01\r
+0F00: 3C            inc  a                   ; increment escort count\r
+0F01: 32 2A 42      ld   ($422A),a           ; set FLAGSHIP_ESCORT_COUNT\r
+0F04: C3 AD 0E      jp   $0EAD               ; make flagship reappear at top of screen\r
+\r
+\r
+\r
+;\r
+; An alien has either flown off the side or the bottom of the screen, and is returning to the swarm.\r
+; \r
+; Expects:\r
+; IX = pointer to INFLIGHT_ALIEN structure           \r
+;\r
+\r
+INFLIGHT_ALIEN_RETURNING_TO_SWARM:\r
+0F07: DD 46 03      ld   b,(ix+$03)          ; keep copy of INFLIGHT_ALIEN.X in B as SET_INFLIGHT_ALIEN_START_POSITION changes it \r
+0F0A: 04            inc  b                   \r
+0F0B: CD 47 11      call $1147               ; call SET_INFLIGHT_ALIEN_START_POSITION to determine where alien needs to go  \r
+\r
+; INFLIGHT_ALIEN.Y  and INFLIGHT_ALIEN.X have been changed by SET_INFLIGHT_ALIEN_START_POSITION\r
+0F0E: DD 7E 03      ld   a,(ix+$03)          ; A = destination INFLIGHT_ALIEN.X\r
+0F11: DD 70 03      ld   (ix+$03),b          ; restore INFLIGHT_ALIEN.X back to what it was before\r
+0F14: 90            sub  b                   ; OK, how far away is this alien from where it wants to be?\r
+0F15: 28 14         jr   z,$0F2B             ; distance is zero, it's got where it wants to be, goto INFLIGHT_ALIEN_BACK_IN_SWARM\r
+0F17: FE 19         cp   $19                 ; 25 pixels away?\r
+0F19: D0            ret  nc                  ; if distance is more than $19 (25 decimal), not near enough to destination, so exit\r
+0F1A: E6 01         and  $01                 ; is distance an odd number?\r
+0F1C: C0            ret  nz                  ; yes, so exit\r
+\r
+; Alien is less than 25 pixels away from its destination back in the swarm.\r
+; We now need to determine what way to rotate the sprite so that it returns to the swarm upside-down, bat-style. \r
+0F1D: DD CB 06 46   bit  0,(ix+$06)          ; read INFLIGHT_ALIEN.ArcClockwise\r
+0F21: 20 04         jr   nz,$0F27             \r
+\r
+0F23: DD 34 05      inc  (ix+$05)            ; update INFLIGHT_ALIEN.AnimationFrame to rotate the alien right\r
+0F26: C9            ret\r
+\r
+0F27: DD 35 05      dec  (ix+$05)            ; update INFLIGHT_ALIEN.AnimationFrame to rotate the alien left\r
+0F2A: C9            ret\r
+\r
+; alien has returned to swarm. Remove sprite and substitute sprite with characters.\r
+INFLIGHT_ALIEN_BACK_IN_SWARM:\r
+0F2B: DD 36 00 00   ld   (ix+$00),$00        ; set INFLIGHT_ALIEN.IsActive to 0 - will hide sprite (see $0C98)\r
+0F2F: 26 41         ld   h,$41               ; MSB of ALIEN_SWARM_FLAGS address \r
+0F31: DD 6E 07      ld   l,(ix+$07)          ; Now HL = pointer to address in ALIEN_SWARM_FLAGS where alien belongs\r
+0F34: 16 00         ld   d,$00               ; command: DRAW_ALIEN_COMMAND\r
+0F36: 36 01         ld   (hl),$01            ; mark flag in ALIEN_SWARM_FLAGS as "occupied". Our alien's back in the swarm!\r
+0F38: 5D            ld   e,l                 ; E = index of alien in swarm\r
+0F39: C3 F2 08      jp   $08F2               ; jump to QUEUE COMMAND. Alien will be drawn in its place in the swarm.\r
+\r
+\r
+;\r
+; Called when aliens are aggressive and refuse to return to the swarm.\r
+;\r
+; This routine makes the alien fly from the top of the screen for [TempCounter1] pixels vertically.\r
+; During this time it won't shoot, but it will gravitate towards the player's horizontal position (as the player sees it).\r
+; \r
+; The trigger for this stage of life is when:\r
+;     HAVE_AGGRESSIVE_ALIENS is set OR \r
+;     HAVE_NO_BLUE_OR_PURPLE_ALIENS flag is set \r
+; \r
+;\r
+\r
+INFLIGHT_ALIEN_CONTINUING_ATTACK_RUN_FROM_TOP_OF_SCREEN:\r
+0F3C: DD 34 03      inc  (ix+$03)            ; increment INFLIGHT_ALIEN.X\r
+\r
+; \r
+0F3F: 3A 02 42      ld   a,($4202)           ; read PLAYER_Y      \r
+0F42: DD 96 04      sub  (ix+$04)            ; subtract INFLIGHT_ALIEN.Y \r
+\r
+0F45: ED 44         neg                      \r
+0F47: 17            rla                      ; A = A * 2\r
+0F48: 5F            ld   e,a\r
+0F49: 9F            sbc  a,a                 ; A= 0 - Carry flag\r
+0F4A: 57            ld   d,a\r
+0F4B: CB 13         rl   e\r
+0F4D: CB 12         rl   d                   ; DE = DE * 2\r
+0F4F: DD 66 04      ld   h,(ix+$04)\r
+0F52: DD 6E 09      ld   l,(ix+$09)          ; INFLIGHT_ALIEN.PivotYValue\r
+0F55: A7            and  a                   ; Clear carry flag because..\r
+0F56: ED 52         sbc  hl,de               ; ..there's no sub hl,de instruction in Z80 and we dont want a carry\r
+0F58: DD 74 04      ld   (ix+$04),h          ; update INFLIGHT_ALIEN.Y \r
+0F5B: DD 75 09      ld   (ix+$09),l          ; update INFLIGHT_ALIEN.PivotYValue\r
+0F5E: DD 35 10      dec  (ix+$10)            ; counter was set @ $0ECF\r
+0F61: C0            ret  nz\r
+\r
+0F62: DD 34 02      inc  (ix+$02)            ; set INFLIGHT_ALIEN.StageOfLife to INFLIGHT_ALIEN_FULL_SPEED_CHARGE\r
+0F65: C9            ret\r
+\r
+\r
+\r
+;\r
+; The inflight alien is now going to fly at full speed and zigzag to make it harder to shoot. \r
+; It won't drop bombs, but it will gravitate towards the player.\r
+;\r
+; When the alien gets to the vertical (as the player sees it) centre of the screen, the alien will loop\r
+; the loop if there's enough space to do so. \r
+;\r
+; After the loop is complete, the alien will start shooting.\r
+;\r
+\r
+INFLIGHT_ALIEN_FULL_SPEED_CHARGE:\r
+0F66: DD 34 03      inc  (ix+$03)            ; increment INFLIGHT_ALIEN.X\r
+\r
+; first check the X coordinate to see if the alien is in the centre \r
+0F69: DD 7E 03      ld   a,(ix+$03)          ; read INFLIGHT_ALIEN.X \r
+0F6C: D6 60         sub  $60                                         \r
+0F6E: FE 40         cp   $40\r
+0F70: 30 09         jr   nc,$0F7B            ; if INFLIGHT_ALIEN.X-$60 >= $40, we're not centre horizontally   \r
+\r
+; next thing we need to do is check if we have enough space for a loop.\r
+0F72: DD 7E 04      ld   a,(ix+$04)          ; read INFLIGHT_ALIEN.Y \r
+0F75: D6 60         sub  $60\r
+0F77: FE 40         cp   $40\r
+0F79: 38 0C         jr   c,$0F87             ; yes, we have space, make alien loop the loop\r
+\r
+; otherwise, make the alien veer erratically. \r
+0F7B: CD DD 0D      call $0DDD               ; call INFLIGHT_ALIEN_DEFINE_FLIGHTPATH\r
+0F7E: DD 36 18 03   ld   (ix+$18),$03        ; set INFLIGHT_ALIEN.Speed to maximum!\r
+0F82: DD 36 10 64   ld   (ix+$10),$64        ; set INFLIGHT_ALIEN.TempCounter1\r
+0F86: C9            ret\r
+                     \r
\r
+0F87: DD 34 02      inc  (ix+$02)\r
+0F8A: DD 34 02      inc  (ix+$02)            ; set INFLIGHT_ALIEN.StageOfLife to INFLIGHT_ALIEN_LOOP_THE_LOOP \r
+0F8D: DD 36 10 03   ld   (ix+$10),$03        ; set INFLIGHT_ALIEN.TempCounter1 to delay before changing animation frame\r
+0F91: DD 36 11 0C   ld   (ix+$11),$0C        ; set INFLIGHT_ALIEN.TempCounter2 to number of animation frames in total\r
+0F95: DD 36 05 00   ld   (ix+$05),$00        ; set INFLIGHT_ALIEN.AnimationFrame\r
+0F99: DD 36 13 00   ld   (ix+$13),$00        ; set INFLIGHT_ALIEN.ArcTableLsb \r
+0F9D: 3A 02 42      ld   a,($4202)           ; read PLAYER_Y \r
+0FA0: DD 96 04      sub  (ix+$04)            ; subtract INFLIGHT_ALIEN.Y \r
+0FA3: 38 05         jr   c,$0FAA             ; if player to right of alien, make alien loop the loop clockwise\r
+\r
+; alien will perform an anti-clockwise loop\r
+0FA5: DD 36 06 00   ld   (ix+$06),$00        ; reset INFLIGHT_ALIEN.ArcClockwise\r
+0FA9: C9            ret\r
+\r
+0FAA: DD 36 06 01   ld   (ix+$06),$01        ; set INFLIGHT_ALIEN.ArcClockwise \r
+0FAE: C9            ret\r
+\r
+\r
+\r
+;\r
+; You've killed a lot of the alien's friends. It's going to keep coming after you until one of you dies.\r
+;\r
+;\r
+;\r
+\r
+INFLIGHT_ALIEN_ATTACKING_PLAYER_AGGRESSIVELY:\r
+0FAF: DD 34 03      inc  (ix+$03)            ; increment INFLIGHT_ALIEN.X\r
+0FB2: CD 6B 11      call $116B               ; call UPDATE_INFLIGHT_ALIEN_YADD\r
+0FB5: DD 7E 17      ld   a,(ix+$17)          ; read INFLIGHT_ALIEN.SortieCount           \r
+0FB8: FE 04         cp   $04                 ; has the alien made it past the player 4 times?\r
+0FBA: 28 48         jr   z,$1004             ; yes, exactly 4 times, *maybe* make alien closer to player                                                                 \r
+0FBC: 30 4D         jr   nc,$100B            ; more than 4 times, make aliens hug player closer \r
+\r
+0FBE: DD 7E 09      ld   a,(ix+$09)          ; INFLIGHT_ALIEN.PivotYValue\r
+0FC1: DD 86 19      add  a,(ix+$19)          ; Add INFLIGHT_ALIEN.PivotYValueAdd \r
+0FC4: DD 77 04      ld   (ix+$04),a          ; set INFLIGHT_ALIEN.Y \r
+\r
+; has alien wandered off left or right side of screen as player sees it?\r
+0FC7: C6 07         add  a,$07\r
+0FC9: FE 0E         cp   $0E\r
+0FCB: 38 29         jr   c,$0FF6             ; alien has gone off side of screen, goto $0FF6\r
+\r
+; is alien near bottom of the screen?\r
+0FCD: DD 7E 03      ld   a,(ix+$03)          ; read INFLIGHT_ALIEN.X\r
+0FD0: C6 40         add  a,$40               ; \r
+0FD2: 38 27         jr   c,$0FFB             ; if adding $40 pixels to X gives a result >255, then alien is near bottom of screen, goto $0FFB\r
+0FD4: DD 35 10      dec  (ix+$10)\r
+0FD7: 28 27         jr   z,$1000\r
+0FD9: 3A 00 42      ld   a,($4200)           ; read HAS_PLAYER_SPAWNED\r
+0FDC: 0F            rrca                     ; move flag into carry\r
+0FDD: D0            ret  nc                  ; return if player has not spawned\r
+0FDE: CD B0 11      call $11B0               ; call CALCULATE_INFLIGHT_ALIEN_LOOKAT_ANIM_FRAME\r
+0FE1: 3A 2B 42      ld   a,($422B)           ; read IS_FLAGSHIP_HIT\r
+0FE4: 0F            rrca                     ; move flag into carry\r
+0FE5: D8            ret  c                   ; return if flagship has been hit\r
+\r
+; OK, can this alien start firing at you? Exact duplicate of code @$0E54, look there for docs on how algorithm works. \r
+0FE6: 2A 13 42      ld   hl,($4213)          ; get INFLIGHT_ALIEN_SHOOT_EXACT_X into H and INFLIGHT_ALIEN_SHOOT_RANGE_MUL into L\r
+0FE9: DD 7E 03      ld   a,(ix+$03)          \r
+0FEC: BC            cp   h\r
+0FED: CA E0 11      jp   z,$11E0             ; jump to TRY_SPAWN_ENEMY_BULLET\r
+0FF0: C6 19         add  a,$19\r
+0FF2: 2D            dec  l                   \r
+0FF3: 20 F7         jr   nz,$0FEC\r
+0FF5: C9            ret\r
+\r
+0FF6: DD 36 02 05   ld   (ix+$02),$05        ; set INFLIGHT_ALIEN.StageOfLife to INFLIGHT_ALIEN_REACHED_BOTTOM_OF_SCREEN\r
+0FFA: C9            ret\r
+\r
+0FFB: DD 36 02 04   ld   (ix+$02),$04        ; set INFLIGHT_ALIEN.StageOfLife to INFLIGHT_ALIEN_NEAR_BOTTOM_OF_SCREEN\r
+0FFF: C9            ret\r
+\r
+1000: DD 35 02      dec  (ix+$02)\r
+1003: C9            ret\r
+\r
+\r
+; If we get here, the alien has survived exactly 4 continuous sorties.     \r
+1004: 3A 5F 42      ld   a,($425F)           ; read TIMING_VARIABLE\r
+1007: E6 01         and  $01                 ; is the number odd?\r
+1009: 28 B3         jr   z,$0FBE             ; no, the number's even, it's business as usual, goto $0FBE\r
+\r
+; If we get here, the alien is going to "hug" the player a little bit closer than he might like.\r
+; Note: This routine is *always* called if the alien survives 5 continuous sorties or more. \r
+100B: 3A 02 42      ld   a,($4202)           ; read PLAYER_Y \r
+100E: DD 96 09      sub  (ix+$09)            ; subtract INFLIGHT_ALIEN.PivotYValue\r
+1011: 38 06         jr   c,$1019             if a carry occurred, alien is, as player sees it, to left of player ship\r
+\r
+; Make the alien's pivot Y coordinate a bit closer to the player...\r
+1013: DD 34 09      inc  (ix+$09)            ; Update INFLIGHT_ALIEN.PivotYValue\r
+1016: C3 BE 0F      jp   $0FBE\r
+\r
+1019: DD 35 09      dec  (ix+$09)            ; Update INFLIGHT_ALIEN.PivotYValue\r
+101C: C3 BE 0F      jp   $0FBE\r
+\r
+\r
+\r
+;\r
+; Aggressive aliens sometimes do a 360 degree loop to taunt the player.\r
+;\r
+; This routine rotates the alien 270 degrees. The remaining 90 degrees is done by the INFLIGHT_ALIEN_FLIES_IN_ARC routine. \r
+;\r
+; Expects:  \r
+; IX = pointer to INFLIGHT_ALIEN structure\r
+;\r
+\r
+INFLIGHT_ALIEN_LOOP_THE_LOOP:\r
+101F: DD 6E 13      ld   l,(ix+$13)          ; load L with INFLIGHT_ALIEN.ArcTableLsb\r
+1022: 26 1E         ld   h,$1E               ; MSB of INFLIGHT_ALIEN_ARC_TABLE                 \r
+\r
+; Now HL is a pointer to an entry in the INFLIGHT_ALIEN_ARC_TABLE (see docs @ $1E00)\r
+1024: DD 7E 03      ld   a,(ix+$03)          ; load INFLIGHT_ALIEN.X          \r
+1027: 96            sub  (hl)                ; subtract X component from table  \r
+1028: DD 77 03      ld   (ix+$03),a\r
+\r
+102B: 2C            inc  l\r
+\r
+102C: DD CB 06 46   bit  0,(ix+$06)          ; is this alien going to do a clockwise loop?\r
+1030: 20 2E         jr   nz,$1060            ; yes, goto INFLIGHT_ALIEN_LOOPING_CLOCKWISE\r
+\r
+; Alien is performing a counter-clockwise loop-the-loop maneuvre\r
+1032: DD 7E 04      ld   a,(ix+$04)          ; read INFLIGHT_ALIEN.Y\r
+1035: 96            sub  (hl)                ; subtract Y component from INFLIGHT_ALIEN_ARC_TABLE\r
+1036: DD 77 04      ld   (ix+$04),a          ; set INFLIGHT_ALIEN.Y \r
+1039: 2C            inc  l                   ; bump HL to point to next X,Y pair in INFLIGHT_ALIEN_ARC_TABLE\r
+103A: DD 75 13      ld   (ix+$13),l          ; set INFLIGHT_ALIEN.ArcTableLsb\r
+103D: DD 35 10      dec  (ix+$10)            ; decrement INFLIGHT_ALIEN.TempCounter1 \r
+1040: C0            ret  nz\r
+\r
+; When INFLIGHT_ALIEN.TempCounter1 counts down to zero, its time to change the animation frame\r
+1041: DD 36 10 04   ld   (ix+$10),$04        ; reset INFLIGHT_ALIEN.TempCounter1 \r
+1045: DD 35 05      dec  (ix+$05)            ; change sprite frame to appear to rotate LEFT\r
+\r
+; INFLIGHT_ALIEN.TempCounter2 is used to count down number of animation frames left\r
+1048: DD 35 11      dec  (ix+$11)            ; decrement INFLIGHT_ALIEN.TempCounter2\r
+104B: C0            ret  nz                  ; return if we haven't done \r
+\r
+; we've done 270 degrees rotation, hand off the remaining 90 to the INFLIGHT_ALIEN_FLIES_IN_ARC \r
+104C: DD 34 02      inc  (ix+$02)            ; bump INFLIGHT_ALIEN.StageOfLife to INFLIGHT_ALIEN_COMPLETE_LOOP\r
+104F: DD 36 10 03   ld   (ix+$10),$03        ; set INFLIGHT_ALIEN.TempCounter1 to delay before changing animation frame\r
+1053: DD 36 11 0C   ld   (ix+$11),$0C        ; set INFLIGHT_ALIEN.TempCounter2 to number of animation frames\r
+1057: DD 36 05 0C   ld   (ix+$05),$0C        ; set INFLIGHT_ALIEN.AnimationFrame\r
+105B: DD 36 13 00   ld   (ix+$13),$00        ; set INFLIGHT_ALIEN.ArcTableLsb\r
+105F: C9            ret\r
+\r
+\r
+; Alien is performing a clockwise loop-the-loop maneuvre\r
+INFLIGHT_ALIEN_LOOPING_CLOCKWISE:\r
+1060: DD 7E 04      ld   a,(ix+$04)          ; read INFLIGHT_ALIEN.Y\r
+1063: 86            add  a,(hl)              ; add Y component from INFLIGHT_ALIEN_ARC_TABLE\r
+1064: DD 77 04      ld   (ix+$04),a          ; set INFLIGHT_ALIEN.Y\r
+1067: 2C            inc  l                   ; bump HL to point to next X,Y pair in INFLIGHT_ALIEN_ARC_TABLE\r
+1068: DD 75 13      ld   (ix+$13),l          ; set INFLIGHT_ALIEN.ArcTableLsb\r
+106B: DD 35 10      dec  (ix+$10)\r
+106E: C0            ret  nz\r
+\r
+; When INFLIGHT_ALIEN.TempCounter1 counts down to zero, its time to change the animation frame\r
+106F: DD 36 10 04   ld   (ix+$10),$04        reset INFLIGHT_ALIEN.TempCounter1 \r
+1073: DD 34 05      inc  (ix+$05)            ; change sprite frame to appear to rotate RIGHT\r
+1076: DD 35 11      dec  (ix+$11)\r
+1079: C0            ret  nz\r
+\r
+; we've done 270 degrees rotation, hand off the remaining 90 to the INFLIGHT_ALIEN_FLIES_IN_ARC\r
+107A: DD 34 02      inc  (ix+$02)            ; bump INFLIGHT_ALIEN.StageOfLife to INFLIGHT_ALIEN_COMPLETE_LOOP\r
+107D: DD 36 10 03   ld   (ix+$10),$03        ; set INFLIGHT_ALIEN.TempCounter1 to delay before changing animation frame\r
+1081: DD 36 11 0C   ld   (ix+$11),$0C        ; set INFLIGHT_ALIEN.TempCounter2 to number of animation frames\r
+1085: DD 36 05 F4   ld   (ix+$05),$F4        ; set INFLIGHT_ALIEN.AnimationFrame\r
+1089: DD 36 13 00   ld   (ix+$13),$00        ; set INFLIGHT_ALIEN.ArcTableLsb\r
+108D: C9            ret\r
+\r
+\r
+INFLIGHT_ALIEN_COMPLETE_LOOP:\r
+108E: C3 71 0D      jp   $0D71               ; jump to INFLIGHT_ALIEN_FLIES_IN_ARC\r
+\r
+\r
+INFLIGHT_ALIEN_UNKNOWN_1091:\r
+1091: DD 34 03      inc  (ix+$03)            ; update INFLIGHT_ALIEN.X\r
+1094: DD 36 02 08   ld   (ix+$02),$08        ; set INFLIGHT_ALIEN.StageOfLife to INFLIGHT_ALIEN_FULL_SPEED_CHARGE\r
+1098: C3 7B 0F      jp   $0F7B               \r
+\r
+\r
+\r
+;\r
+; Set the colour, X coordinate and animation frame of the INFLIGHT_ALIEN to be scrolled on\r
+; beneath CONVOY CHARGER.\r
+; \r
+; Expects:\r
+; IX = pointer to INFLIGHT_ALIEN structure \r
+;\r
+;\r
+\r
+INFLIGHT_ALIEN_CONVOY_CHARGER_SET_COLOUR_POS_ANIM:\r
+109B: DD 7E 07      ld   a,(ix+$07)          ; read INFLIGHT_ALIEN.IndexInSwarm\r
+; Value in A now identifies type of alien:\r
+; 3: Flagship\r
+; 2: Red alien\r
+; 1: Purple alien\r
+; 0: Blue alien\r
+109E: 2F            cpl                       ; flip the bits\r
+109F: E6 03         and  $03\r
+\r
+; now A is 0..3, where:\r
+; 0: Flagship\r
+; 1: Red alien\r
+; 2: Purple alien\r
+; 3: Blue alien\r
+10A1: 47            ld   b,a\r
+10A2: 3C            inc  a                   ; ensure colour value is nonzero \r
+10A3: DD 77 16      ld   (ix+$16),a          ; set INFLIGHT_ALIEN.Colour\r
+10A6: 07            rlca                     ; multiply A..\r
+10A7: 07            rlca\r
+10A8: 07            rlca\r
+10A9: 07            rlca                     ; .. by 16\r
+10AA: C6 8C         add  a,$8C               ; X coord = $8C + (A * 16)\r
+10AC: DD 77 03      ld   (ix+$03),a          ; set INFLIGHT_ALIEN.X\r
+10AF: DD 36 10 18   ld   (ix+$10),$18        ; set INFLIGHT_ALIEN.TempCounter1\r
+10B3: DD 34 02      inc  (ix+$02)            ; advance alien to its next stage of life\r
+10B6: DD 36 0F 00   ld   (ix+$0f),$00        ; set INFLIGHT_ALIEN.AnimFrameStartCode\r
+10BA: 78            ld   a,b\r
+10BB: A7            and  a                   ; flagship?\r
+10BC: C0            ret  nz                  ; return if not\r
+\r
+; This is a flagship, the animation frame start is different from the other aliens\r
+10BD: DD 36 0F 18   ld   (ix+$0f),$18        ; set INFLIGHT_ALIEN.AnimFrameStartCode\r
+10C1: C9            ret\r
+\r
+\r
+\r
+; On the WE ARE THE GALAXIANS.. CONVOY CHARGER attract mode page,\r
+; start scrolling an alien sprite onto screen, and print its associated points values  \r
+;\r
+; Expects:\r
+; IX = pointer to INFLIGHT_ALIEN structure\r
+;\r
+\r
+INFLIGHT_ALIEN_CONVOY_CHARGER_START_SCROLL:\r
+10C2: DD 34 04      inc  (ix+$04)            ; keep incrementing INFLIGHT_ALIEN.Y..            \r
+10C5: DD 35 10      dec  (ix+$10)            ; ..until this counter hits zero.   \r
+10C8: C0            ret  nz\r
+\r
+10C9: DD 7E 07      ld   a,(ix+$07)          ; read INFLIGHT_ALIEN.IndexInSwarm\r
+10CC: C6 4B         add  a,$4B               ; set bit 6 to indicate the text needs to be scrolled on\r
+10CE: 5F            ld   e,a\r
+10CF: 16 06         ld   d,$06               ; command is PRINT_TEXT\r
+10D1: CD F2 08      call $08F2               ; call QUEUE_COMMAND\r
+10D4: DD 34 02      inc  (ix+$02)            ; advance to next stage of alien's life\r
+10D7: C9            ret\r
+\r
+\r
+\r
+;\r
+; Scroll alien sprite onto screen    \r
+;\r
+; Expects:\r
+; IX = pointer to INFLIGHT_ALIEN structure\r
+;\r
+\r
+INFLIGHT_ALIEN_CONVOY_CHARGER_DO_SCROLL:\r
+10D8: DD 7E 04      ld   a,(ix+$04)          ; read INFLIGHT_ALIEN.Y\r
+10DB: D6 C8         sub  $C8\r
+10DD: FE 05         cp   $05                  \r
+10DF: D8            ret  c\r
+10E0: DD 34 04      inc  (ix+$04)            ; update INFLIGHT_ALIEN.Y\r
+10E3: C9            ret\r
+\r
+\r
+\r
+;\r
+; Called when the alien is dying.\r
+;\r
+\r
+HANDLE_INFLIGHT_ALIEN_DYING:\r
+10E4: DD 7E 02      ld   a,(ix+$02)\r
+10E7: EF            rst  $28                 ; jump to code @ $10E8 + (A*2)\r
+10E8: \r
+\r
+      F0 10         ; $10F0                  ; INFLIGHT_ALIEN_DYING_SETUP_ANIM_AND_SOUND    \r
+      12 11         ; $1112                  ; INFLIGHT_ALIEN_DYING_DISPLAY_EXPLOSION\r
+      3D 11         ; $113D                  ; INFLIGHT_ALIEN_DYING_FINALLY_BUYS_FARM\r
+      46 11         ; $1146                  ; just a pointer to a RET command\r
+\r
+\r
+;\r
+; Set up a dying alien's death animation and sound effect.\r
+;\r
+; Expects: \r
+; IX = pointer to INFLIGHT_ALIEN structure\r
+\r
+INFLIGHT_ALIEN_DYING_SETUP_ANIM_AND_SOUND:\r
+10F0: DD 36 10 04   ld   (ix+$10),$04        ; set INFLIGHT_ALIEN.TempCounter1 to speed of death animation (lower = faster)\r
+10F4: DD 36 11 04   ld   (ix+$11),$04        ; set INFLIGHT_ALIEN.TempCounter2 to number of times to repeat death animation\r
+10F8: DD 36 12 1C   ld   (ix+$12),$1C        ; set INFLIGHT_ALIEN.DyingAnimFrameCode\r
+10FC: DD 34 02      inc  (ix+$02)            ; bump INFLIGHT_ALIEN.StageOfLife to next stage\r
+10FF: DD 7E 07      ld   a,(ix+$07)          ; read INFLIGHT_ALIEN.IndexInSwarm\r
+1102: FE 70         cp   $70                 ; is this a flagship?\r
+1104: 30 06         jr   nc,$110C            ; yes, goto $110C to play flagship hit sound\r
+1106: 3E 07         ld   a,$07\r
+1108: 32 DF 41      ld   ($41DF),a           ; play sound effect of alien hit\r
+110B: C9            ret\r
+\r
+; play flagship hit sound\r
+110C: 3E 17         ld   a,$17\r
+110E: 32 DF 41      ld   ($41DF),a           ; play sound effect of flagship hit\r
+1111: C9            ret\r
+\r
+\r
+;\r
+; Show the dying alien explosion animation.\r
+;\r
+; Expects: \r
+; IX = point to INFLIGHT_ALIEN structure\r
+;\r
+\r
+INFLIGHT_ALIEN_DYING_DISPLAY_EXPLOSION:\r
+1112: DD 35 10      dec  (ix+$10)            ; decrement INFLIGHT_ALIEN.TempCounter1 \r
+1115: C0            ret  nz                  ; if counter hasn't reached zero, not time to update explosion animation frame yet\r
+1116: DD 36 10 04   ld   (ix+$10),$04        ; reset animation counter. Higher number = slower explosion animation speed\r
+111A: DD 34 12      inc  (ix+$12)            ; bump INFLIGHT_ALIEN.DyingAnimFrameCode to next frame\r
+111D: DD 35 11      dec  (ix+$11)            ; decrement INFLIGHT_ALIEN.TempCounter1 which holds count of frames left to show \r
+1120: C0            ret  nz                  ; if we've not shown all the explosion frames, exit\r
+\r
+1121: DD 7E 07      ld   a,(ix+$07)          ; read INFLIGHT_ALIEN.IndexInSwarm\r
+1124: FE 70         cp   $70                 ; is this a flagship?\r
+1126: 30 05         jr   nc,$112D            ; yes, goto DISPLAY_FLAGSHIP_POINTS_VALUE\r
+1128: DD 36 01 00   ld   (ix+$01),$00        ; alien's not a flagship, so just clear INFLIGHT_ALIEN.IsDying to say "we've finished dying, thanks". \r
+112C: C9            ret\r
+\r
+\r
+;\r
+; After you shoot a flagship, display its points value for a while.\r
+;\r
+\r
+DISPLAY_FLAGSHIP_POINTS_VALUE:\r
+112D: DD 36 10 32   ld   (ix+$10),$32        ; set INFLIGHT_ALIEN.TempCounter1 to length of time to keep points on screen\r
+1131: 3A 2D 42      ld   a,($422D)           ; read FLAGSHIP_SCORE_FACTOR. 3 = full (800) points\r
+1134: C6 20         add  a,$20\r
+1136: DD 77 12      ld   (ix+$12),a          ; set INFLIGHT_ALIEN.DyingAnimFrameCode \r
+1139: DD 34 02      inc  (ix+$02)            ; set INFLIGHT_ALIEN.StageOfLife (or death, should I say.)\r
+113C: C9            ret\r
+\r
+\r
+; keeps points value on screen until counter hits zero.\r
+;\r
+; After that, the alien is officially dead and the INFLIGHT_ALIEN structure is ready for re-use by another (living) attacking alien.\r
+;\r
+INFLIGHT_ALIEN_DYING_FINALLY_BUYS_FARM:\r
+113D: DD 35 10      dec  (ix+$10)            ; decrement INFLIGHT_ALIEN.TempCounter1 \r
+1140: C0            ret  nz                  ; if points value countdown isn't zero, exit\r
+1141: DD 36 01 00   ld   (ix+$01),$00        ; clear INFLIGHT_ALIEN.IsDying flag.   \r
+1145: C9            ret\r
+\r
+\r
+1146: C9            ret\r
+\r
+\r
+;\r
+; When an alien breaks off from the swarm to attack the player, the characters it occupies in the swarm are deleted and an alien sprite is substituted.\r
+; This function calculates a starting X and Y coordinate for the sprite.\r
+;\r
+; This function is also used by INFLIGHT_ALIEN_RETURNING_TO_SWARM (see $0F07) to determine where in the swarm a returning alien should fly to. \r
+;\r
+;\r
+; Expects:\r
+; IX to point to an INFLIGHT_ALIEN structure.\r
+;     (IX + 7) to be the index of the alien in the ALIEN_SWARM_FLAGS array.\r
+;\r
+; \r
+\r
+SET_INFLIGHT_ALIEN_START_POSITION:\r
+1147: DD 7E 07      ld   a,(ix+$07)          ; read INFLIGHT_ALIEN.IndexInSwarm\r
+114A: E6 70         and  $70                 ; compute row that alien is in\r
+114C: 0F            rrca\r
+114D: 4F            ld   c,a\r
+114E: 0F            rrca\r
+114F: 81            add  a,c\r
+1150: ED 44         neg\r
+1152: C6 7C         add  a,$7C\r
+1154: DD 77 03      ld   (ix+$03),a          ; set INFLIGHT_ALIEN.X\r
+\r
+1157: DD 7E 07      ld   a,(ix+$07)          ; read INFLIGHT_ALIEN.IndexInSwarm\r
+115A: E6 0F         and  $0F                 ; compute column that alien is in\r
+115C: 07            rlca\r
+115D: 07            rlca\r
+115E: 07            rlca\r
+115F: 07            rlca                     ; multiply by 16..\r
+1160: C6 07         add  a,$07\r
+1162: 4F            ld   c,a\r
+1163: 3A 0E 42      ld   a,($420E)           ; read SWARM_SCROLL_VALUE   \r
+1166: 81            add  a,c                    \r
+1167: DD 77 04      ld   (ix+$04),a          ; set INFLIGHT_ALIEN.Y \r
+116A: C9            ret\r
+\r
+\r
+;\r
+; This routine helps move an attacking INFLIGHT_ALIEN.\r
+;\r
+; I'll be honest, I don't know exactly how it works. I've had the guys from my work (Lambo & Phil) look at this with me,\r
+; and some of the guys from the https://www.facebook.com/groups/z80asm/ as well. When I figure it out, I'll document it.\r
+;\r
+; The key is in the mutation of IX+$19, INFLIGHT_ALIEN.PivotYValueAdd \r
+; \r
+; Expects:\r
+; IX = pointer to INFLIGHT_ALIEN structure\r
+; \r
+\r
+;\r
+; To monitor the input parameters type the following into the MAME debugger:\r
+; bp 117E,1,{printf "IX=%04X:   H=%01X L=%01X D=%01X E=%01X", IX,H,L,D,E; g}\r
+;\r
+\r
+UPDATE_INFLIGHT_ALIEN_YADD:\r
+116B: DD 7E 18      ld   a,(ix+$18)          ; read INFLIGHT_ALIEN.Speed\r
+116E: E6 03         and  $03                 \r
+1170: 3C            inc  a                   ; now A is between 1 and 4.\r
+1171: 47            ld   b,a\r
+\r
+1172: DD 66 19      ld   h,(ix+$19)          ; read INFLIGHT_ALIEN.PivotYValueAdd\r
+1175: DD 6E 1A      ld   l,(ix+$1a)\r
+1178: DD 56 1B      ld   d,(ix+$1b)\r
+117B: DD 5E 1C      ld   e,(ix+$1c)\r
+\r
+117E: 7D            ld   a,l\r
+\r
+; Part 1 - do H\r
+117F: 4C            ld   c,h                 ; preserve H in C\r
+1180: 87            add  a,a           \r
+1181: 30 01         jr   nc,$1184\r
+\r
+1183: 25            dec  h\r
+1184: 82            add  a,d\r
+1185: 57            ld   d,a\r
+1186: 3E 00         ld   a,$00\r
+1188: 8C            adc  a,h\r
+\r
+; I *think* this is to ensure that the signed byte in H never loses its sign.\r
+; If it's positive it'll stay positive. If it's negative, it'll stay negative.\r
+1189: FE 80         cp   $80                 \r
+118B: 20 01         jr   nz,$118E\r
+\r
+118D: 79            ld   a,c\r
+118E: 67            ld   h,a\r
+\r
+; Part 2 - now do L\r
+118F: 4D            ld   c,l                 ; preserve L in C  \r
+1190: ED 44         neg\r
+1192: 87            add  a,a\r
+1193: 30 01         jr   nc,$1196\r
+\r
+1195: 2D            dec  l\r
+\r
+1196: 83            add  a,e\r
+1197: 5F            ld   e,a\r
+1198: 3E 00         ld   a,$00\r
+119A: 8D            adc  a,l\r
+119B: FE 80         cp   $80\r
+119D: 20 01         jr   nz,$11A0\r
+\r
+119F: 79            ld   a,c\r
+\r
+11A0: 6F            ld   l,a                 ; restore L from C\r
+\r
+11A1: 10 DB         djnz $117E\r
+\r
+11A3: DD 74 19      ld   (ix+$19),h\r
+11A6: DD 75 1A      ld   (ix+$1a),l\r
+11A9: DD 72 1B      ld   (ix+$1b),d\r
+11AC: DD 73 1C      ld   (ix+$1c),e\r
+11AF: C9            ret\r
+\r
+\r
+\r
+;\r
+; Calculate the animation frame that makes an attacking alien "look" directly at the player. \r
+; \r
+; Expects:\r
+; IX = pointer to INFLIGHT_ALIEN structure with valid X and Y fields.\r
+;\r
+; Returns:\r
+;  INFLIGHT_ALIEN.AnimationFrame is updated\r
+;\r
+\r
+CALCULATE_INFLIGHT_ALIEN_LOOKAT_ANIM_FRAME:\r
+11B0: 3E F0         ld   a,$F0               ;  \r
+11B2: DD 96 03      sub  (ix+$03)            ; subtract from INFLIGHT_ALIEN.X\r
+11B5: 57            ld   d,a\r
+11B6: 3A 02 42      ld   a,($4202)           ; read PLAYER_Y \r
+11B9: DD 96 04      sub  (ix+$04)            ; subtract from INFLIGHT_ALIEN.Y \r
+11BC: 38 07         jr   c,$11C5             \r
+11BE: CD D0 11      call $11D0\r
+11C1: DD 77 05      ld   (ix+$05),a          ; set INFLIGHT_ALIEN.AnimationFrame          \r
+11C4: C9            ret\r
+\r
+11C5: ED 44         neg\r
+11C7: CD D0 11      call $11D0\r
+11CA: ED 44         neg\r
+11CC: DD 77 05      ld   (ix+$05),a          ; set INFLIGHT_ALIEN.AnimationFrame\r
+11CF: C9            ret\r
+\r
+\r
+11D0: CD 48 00      call $0048               ; call CALCULATE_TANGENT\r
+11D3: 79            ld   a,c\r
+11D4: A7            and  a\r
+11D5: F2 DA 11      jp   p,$11DA\r
+11D8: 3E 80         ld   a,$80\r
+11DA: 07            rlca\r
+11DB: 07            rlca\r
+11DC: 07            rlca\r
+11DD: E6 07         and  $07\r
+11DF: C9            ret\r
+\r
+\r
+;\r
+; Try to spawn an enemy bullet. \r
+;\r
+; Expects:\r
+; IX = pointer to INFLIGHT_ALIEN struct\r
+;\r
+; Cheat:\r
+; If you want to stop the aliens from firing, type the following into the MAME debugger:\r
+; maincpu.mb@11E0 = C9 \r
+\r
+TRY_SPAWN_ENEMY_BULLET:\r
+11E0: 11 05 00      ld   de,$0005            ; sizeof(ENEMY_BULLET)\r
+11E3: 21 60 42      ld   hl,$4260            ; load HL with address of ENEMY_BULLETS_START\r
+11E6: 06 0E         ld   b,$0E               ; there are 14 elements in the ENEMY_BULLETS_START array\r
+11E8: CB 46         bit  0,(hl)              ; test if bullet is active\r
+11EA: 28 04         jr   z,$11F0             ; if its not active, then we can use this slot to spawn an enemy bullet, goto $11F0\r
+11EC: 19            add  hl,de               ; otherwise bump HL to point to next enemy bullet in the array\r
+11ED: 10 F9         djnz $11E8               ; repeat until B==0\r
+11EF: C9            ret\r
+\r
+\r
+;\r
+; Spawn an enemy bullet.\r
+;\r
+; Expects:\r
+; IX = pointer to INFLIGHT_ALIEN structure. Identifies the alien firing the bullet.\r
+; HL = pointer to ENEMY_BULLET structure. Contains info about the spawned bullet. \r
+;\r
+\r
+SPAWN_ENEMY_BULLET:\r
+11F0: 36 01         ld   (hl),$01            ; set ENEMY_BULLET.IsActive to 1 (true)\r
+11F2: 23            inc  hl                  ; bump HL to point to ENEMY_BULLET.X\r
+11F3: DD 7E 03      ld   a,(ix+$03)          ; read INFLIGHT_ALIEN.X coordinate\r
+11F6: 77            ld   (hl),a              ; set X coordinate of bullet to be same as alien\r
+11F7: 3E F0         ld   a,$F0               ; load A with -16 (decimal)\r
+11F9: 96            sub  (hl)                ; A = X coordinate of bullet + 16 \r
+11FA: 57            ld   d,a\r
+11FB: 23            inc  hl\r
+11FC: 23            inc  hl                  ; bump HL to point to ENEMY_BULLET.YH\r
+11FD: DD 7E 04      ld   a,(ix+$04)          ; read INFLIGHT_ALIEN.Y  coordinate\r
+1200: 77            ld   (hl),a              ; set Y coordinate of bullet to be same as alien\r
+1201: 23            inc  hl                  ; bump HL to point to ENEMY_BULLET.YDelta\r
+1202: 3A 02 42      ld   a,($4202)           ; read PLAYER_Y   \r
+1205: DD 96 04      sub  (ix+$04)            ; subtract from INFLIGHT_ALIEN.Y  coordinate       \r
+1208: 38 05         jr   c,$120F             ; \r
+120A: CD 18 12      call $1218               ; call COMPUTE_ENEMY_BULLET_DELTA\r
+120D: 77            ld   (hl),a              ; set ENEMY_BULLET.YDelta \r
+120E: C9            ret\r
+\r
+\r
+120F: ED 44         neg                      ; A = Math.Abs(A)\r
+1211: CD 18 12      call $1218               ; call COMPUTE_ENEMY_BULLET_DELTA\r
+1214: ED 44         neg                      ; make bullet fly to right \r
+1216: 77            ld   (hl),a              ; set ENEMY_BULLET.YDelta \r
+1217: C9            ret\r
+\r
+\r
+;\r
+; Unlike the player's bullet, enemy bullets don't always fly in a straight line. \r
+;\r
+\r
+COMPUTE_ENEMY_BULLET_DELTA:\r
+1218: CD 48 00      call $0048               ; call CALCULATE_TANGENT\r
+121B: CD 3C 00      call $003C               ; call GENERATE_RANDOM_NUMBER\r
+121E: E6 1F         and  $1F                 ; clamp number to 0..31 decimal\r
+1220: 81            add  a,c\r
+1221: C6 06         add  a,$06\r
+1223: F0            ret  p\r
+1224: 3E 7F         ld   a,$7F\r
+1226: C9            ret\r
+\r
+\r
+\r
+\r
+HANDLE_INFLIGHT_ALIEN_TO_PLAYER_BULLET_COLLISION_DETECTION:\r
+1227: 3A 08 42      ld   a,($4208)           ; read HAS_PLAYER_BULLET_BEEN_FIRED flag\r
+122A: 0F            rrca                     ; move flag into carry\r
+122B: D0            ret  nc                  ; return if player is not shooting\r
+122C: DD 21 D0 42   ld   ix,$42D0            ; pointer to INFLIGHT_ALIENS_START\r
+1230: 11 20 00      ld   de,$0020            ; sizeof(INFLIGHT_ALIEN)\r
+1233: 06 07         ld   b,$07               ; length of INFLIGHT_ALIENS array\r
+1235: D9            exx\r
+1236: CD 3F 12      call $123F               ; call TEST_IF_PLAYER_BULLET_HIT_INFLIGHT_ALIEN   \r
+1239: D9            exx\r
+123A: DD 19         add  ix,de               ; bump IX to point to next INFLIGHT_ALIEN\r
+123C: 10 F7         djnz $1235\r
+123E: C9            ret\r
+\r
+\r
+\r
+\r
+;\r
+; Player bullet to attacking alien collision detection.\r
+;\r
+; IX = pointer to INFLIGHT_ALIEN structure\r
+;\r
+\r
+TEST_IF_PLAYER_BULLET_HIT_INFLIGHT_ALIEN:\r
+123F: DD CB 00 46   bit  0,(ix+$00)          ; Test INFLIGHT_ALIEN.IsActive flag\r
+1243: C8            ret  z                   ; return if not set\r
+1244: 2A 09 42      ld   hl,($4209)          ; read PLAYER_BULLET_X and PLAYER_BULLET_Y \r
+1247: DD 7E 03      ld   a,(ix+$03)          ; read INFLIGHT_ALIEN.X\r
+124A: 95            sub  l                   ; subtract from value of PLAYER_BULLET_X \r
+124B: C6 02         add  a,$02               \r
+124D: FE 06         cp   $06\r
+124F: D0            ret  nc\r
+1250: DD 7E 04      ld   a,(ix+$04)          ; read INFLIGHT_ALIEN.Y  \r
+1253: 94            sub  h                   ; subtract from value of PLAYER_BULLET_Y \r
+1254: C6 05         add  a,$05\r
+1256: FE 0C         cp   $0C\r
+1258: D0            ret  nc\r
+\r
+; if we get here, the player bullet has hit an alien inflight.\r
+1259: 3E 01         ld   a,$01\r
+125B: 32 0B 42      ld   ($420B),a           ; set IS_PLAYER_BULLET_DONE to 1.\r
+125E: DD 36 00 00   ld   (ix+$00),$00        ; set INFLIGHT_ALIEN.IsActive to 0.  \r
+1262: DD 36 01 01   ld   (ix+$01),$01        ; set INFLIGHT_ALIEN.IsDying to 1.\r
+1266: DD 36 02 00   ld   (ix+$02),$00        ; set INFLIGHT_ALIEN.StageOfLife to 0.\r
+126A: 11 04 03      ld   de,$0304            ; command ID: UPDATE_PLAYER_SCORE_COMMAND, parameter: 4 \r
+\r
+; We now need to identify what rank this alien is so we can add its points value to the player score.\r
+126D: 01 50 03      ld   bc,$0350            ; B= count, C = index into ALIEN_SWARM_FLAGS array to compare against\r
+1270: DD 7E 07      ld   a,(ix+$07)          ; load a with INFLIGHT_ALIEN.IndexInSwarm to find out what rank this alien is.\r
+1273: B9            cp   c                   ; compare A with $50 (80 decimal)  \r
+1274: DA F2 08      jp   c,$08F2             ; if A<$50 jump to QUEUE_COMMAND with command ID: UPDATE_PLAYER_SCORE_COMMAND \r
+1277: 1C            inc  e                   ; increment parameter passed to command - giving a higher score value\r
+1278: D6 10         sub  $10                 ; subtract 10 (16 decimal) to "go down" a rank\r
+127A: 10 F7         djnz $1273               ; repeat until B==0. Only flagships will go to B==0.\r
+\r
+; If we get here, we've shot an attacking flagship. \r
+; First we activate a timer that prevents aliens from leaving the swarm for a while. This simulates the swarm being "stunned". \r
+; We then calculate how many of the flagships escorts have been killed so we can update the player score accordingly. \r
+; For max points, a flagship must have 2 escorts and the escorts must be killed before the flagship.\r
+127C: 21 01 F0      ld   hl,$F001\r
+127F: 22 2B 42      ld   ($422B),hl          ; set IS_FLAGSHIP_HIT to 1, and ALIENS_IN_SHOCK_COUNTER to $F0 (240 decimal)                    \r
+1282: 3A 2A 42      ld   a,($422A)           ; read FLAGSHIP_ESCORT_COUNT \r
+1285: FE 02         cp   $02                 ; do we have 2 aliens escorting the flagship?\r
+1287: CC 92 12      call z,$1292             ; yes, call ASSERT_BOTH_FLAGSHIP_ESCORTS_ARE_ALIVE\r
+128A: 32 2D 42      ld   ($422D),a           ; set FLAGSHIP_SCORE_FACTOR. If 3, then you get full points for killing the flagship + escort.               \r
+128D: 83            add  a,e\r
+128E: 5F            ld   e,a                 ; set command parameter\r
+128F: C3 F2 08      jp   $08F2               ; QUEUE_COMMAND with command ID: UPDATE_PLAYER_SCORE_COMMAND\r
+\r
+; We need to test if both the flagship's escorts are alive.\r
+; If both are dead, then when this function returns, A will be set to 3. \r
+ASSERT_BOTH_FLAGSHIP_ESCORTS_ARE_ALIVE:\r
+1292: DD CB 20 46   bit  0,(ix+$20)          ; test if first escort is alive   \r
+1296: C0            ret  nz                  ; return if alive\r
+1297: DD CB 40 46   bit  0,(ix+$40)          ; test if second escort is alive   \r
+129B: C0            ret  nz                  ; return if alive\r
+\r
+; both escorts have been killed (or the flagship didn't have 2 escorts to start with)\r
+129C: 3C            inc  a                   ; both \r
+129D: C9            ret\r
+\r
+\r
+;\r
+; Iterate through list of inflight aliens and test if they have hit the player.\r
+;\r
+\r
+HANDLE_PLAYER_TO_INFLIGHT_ALIEN_COLLISION_DETECTION:\r
+129E: 3A 00 42      ld   a,($4200)           ; read HAS_PLAYER_SPAWNED\r
+12A1: 0F            rrca                     ; move flag into carry\r
+12A2: D0            ret  nc                  ; return if carry not set\r
+12A3: DD 21 D0 42   ld   ix,$42D0            ; pointer to INFLIGHT_ALIENS[1]\r
+12A7: 11 20 00      ld   de,$0020            ; sizeof(INFLIGHT_ALIEN)\r
+12AA: 06 07         ld   b,$07               ; 7 aliens to test collision with\r
+12AC: D9            exx\r
+12AD: CD B6 12      call $12B6               ; call TEST_IF_INFLIGHT_ALIEN_HIT_PLAYER\r
+12B0: D9            exx\r
+12B1: DD 19         add  ix,de               ; bump IX to point to next INFLIGHT_ALIEN in array\r
+12B3: 10 F7         djnz $12AC               ; repeat until B==0\r
+12B5: C9            ret\r
+\r
+\r
+\r
+;\r
+; Check if a flying alien has hit the player's ship.\r
+;\r
+; If so:\r
+; IS_PLAYER_HIT will be set to 1.\r
+;\r
+; Expects: \r
+; IX = pointer to INFLIGHT_ALIEN structure \r
+;\r
+\r
+TEST_IF_INFLIGHT_ALIEN_HIT_PLAYER:\r
+12B6: DD CB 00 46   bit  0,(ix+$00)          ; read INFLIGHT_ALIEN.IsActive flag\r
+12BA: C8            ret  z                   ; exit if alien isn't active\r
+\r
+12BB: DD 7E 03      ld   a,(ix+$03)          ; read INFLIGHT_ALIEN.X\r
+12BE: C6 21         add  a,$21\r
+12C0: D6 05         sub  $05\r
+12C2: 38 16         jr   c,$12DA\r
+12C4: D6 0C         sub  $0C\r
+12C6: D0            ret  nc                  ; return if >=$0C\r
+\r
+12C7: 3A 02 42      ld   a,($4202)           ; read PLAYER_Y \r
+12CA: DD 96 04      sub  (ix+$04)            ; subtract INFLIGHT_ALIEN.Y \r
+12CD: C6 0A         add  a,$0A\r
+12CF: FE 15         cp   $15\r
+12D1: D0            ret  nc                  ; return if >=$15\r
+\r
+; kill player and alien\r
+12D2: 3E 01         ld   a,$01\r
+12D4: 32 04 42      ld   ($4204),a           ; set IS_PLAYER_HIT flag. \r
+12D7: C3 5E 12      jp   $125E               ; and we need to kill the alien as well\r
+\r
+12DA: 3A 02 42      ld   a,($4202)           ; read PLAYER_Y \r
+12DD: DD 96 04      sub  (ix+$04)            ; subtract INFLIGHT_ALIEN.Y \r
+12E0: C6 07         add  a,$07\r
+12E2: FE 0F         cp   $0F\r
+12E4: D0            ret  nc                  ; return if >=$0f\r
+\r
+; kill player and alien\r
+12E5: 3E 01         ld   a,$01\r
+12E7: 32 04 42      ld   ($4204),a           ; set IS_PLAYER_HIT flag. \r
+12EA: C3 5E 12      jp   $125E               ; and we need to kill the alien as well\r
+\r
+\r
+\r
+; \r
+;  Handles the player being hit by an INFLIGHT_ALIEN (see $12B6) or ENEMY_BULLET (see $0B8D).\r
+; \r
+\r
+HANDLE_PLAYER_HIT:\r
+12ED: 21 04 42      ld   hl,$4204            ; pointer to IS_PLAYER_HIT flag\r
+12F0: CB 46         bit  0,(hl)              ; test flag to see if player has been hit. \r
+12F2: C8            ret  z                   ; bit is not set so player not hit, return\r
+\r
+; OK, player's hit. \r
+12F3: 36 00         ld   (hl),$00            ; clear IS_PLAYER_HIT flag\r
+12F5: 21 00 01      ld   hl,$0100             \r
+12F8: 22 00 42      ld   ($4200),hl          ; Clear HAS_PLAYER_SPAWNED and set IS_PLAYER_DYING flags\r
+\r
+; Draw first frame of player exploding\r
+12FB: 21 0A 04      ld   hl,$040A\r
+12FE: 22 05 42      ld   ($4205),hl          ; set PLAYER_EXPLOSION_COUNTER and PLAYER_EXPLOSION_ANIM_FRAME\r
+1301: 11 05 02      ld   de,$0205            ; command: DISPLAY_PLAYER_COMMAND, parameter: 5 (invokes DRAW_PLAYER_SHIP_EXPLODING)\r
+1304: CD F2 08      call $08F2               ; call QUEUE_COMMAND\r
+\r
+; reduce level of difficulty\r
+1307: 3A 1A 42      ld   a,($421A)           ; read DIFFICULTY_EXTRA_VALUE\r
+130A: A7            and  a                   ; test if its zero\r
+130B: 28 01         jr   z,$130E             ; if zero, then - wait a second, why is it updating an already zero field? Should be: jr z, $1311     \r
+130D: 3D            dec  a                   ; reduce game difficulty slightly\r
+130E: 32 1A 42      ld   ($421A),a           ; update DIFFICULTY_EXTRA_VALUE\r
+\r
+; decrement number of player lives\r
+1311: 21 1D 42      ld   hl,$421D            ; pointer to address of PLAYER_LIVES\r
+1314: 35            dec  (hl)                ; reduce number of lives\r
+1315: 7E            ld   a,(hl)              ; read number of lives\r
+1316: FE 06         cp   $06                 ; compare to 6\r
+1318: 38 02         jr   c,$131C             ; if < 6 then goto $131C\r
+131A: 36 05         ld   (hl),$05            ; otherwise, clamp number of lives max to 5 (is this anti-hack code?)\r
+131C: 3A 06 40      ld   a,($4006)           ; read IS_GAME_IN_PLAY\r
+131F: 0F            rrca                     ; move flag into carry\r
+1320: D0            ret  nc                  ; return if game is not in play\r
+\r
+; Make player BOOM! sound\r
+1321: 3E 01         ld   a,$01\r
+1323: 32 03 68      ld   ($6803),a           ; make PLAYER HIT noise\r
+1326: C9            ret\r
+\r
+\r
+\r
+\r
+; Draws player explosion, stops player hit sound when done.\r
+\r
+HANDLE_PLAYER_DYING:\r
+1327: 3A 01 42      ld   a,($4201)           ; read IS_PLAYER_DYING flag\r
+132A: 0F            rrca                     ; move flag into carry. If player is dying carry is set\r
+132B: D0            ret  nc                  ; return if player is not dying. \r
+; wait for explosion delay to count to zero\r
+132C: 21 05 42      ld   hl,$4205            ; point HL to PLAYER_EXPLOSION_COUNTER\r
+132F: 35            dec  (hl)                ; decrement value\r
+1330: C0            ret  nz                  ; if counter hasn't hit 0, then explosion animation can continue, return.\r
+1331: 36 0A         ld   (hl),$0A            ; otherwise reset PLAYER_EXPLOSION_COUNTER value to its default #$0A (10 decimal)\r
+\r
+; draw explosion animation\r
+1333: 23            inc  hl                  ; bump HL to point to PLAYER_EXPLOSION_ANIM_FRAME     \r
+1334: 16 02         ld   d,$02               ; command 2: DISPLAY_PLAYER_COMMAND\r
+1336: 5E            ld   e,(hl)              ; read the animation frame value  \r
+1337: CD F2 08      call $08F2               ; call QUEUE_COMMAND\r
+133A: 35            dec  (hl)                ; decrement the animation frame value\r
+133B: C0            ret  nz                  ; if its not zero, the player dying animation hasn't finished, so return \r
+133C: AF            xor  a                   ; Otherwise, the player explosion animation has reached its end, so....\r
+\r
+; clear IS_PLAYER_DYING flag when animation done and stop player explosion sound\r
+133D: 32 01 42      ld   ($4201),a           ; clear IS_PLAYER_DYING flag\r
+1340: 32 03 68      ld   ($6803),a           ; clear !SOUND  player hit\r
+1343: C9            ret\r
+\r
+\r
+\r
+;\r
+; Try to send a single alien to attack the player.\r
+;\r
+; If we have flagships in the swarm, then only purple and blue aliens can be sent to attack by this routine.\r
+; If we have no flagships in the swarm, then red aliens can also be sent to attack. (See $13BD)\r
+;\r
+; Flagships and escorts are handled by HANDLE_FLAGSHIP_ATTACK.\r
+;\r
+;\r
+; Cheat (of a sort):\r
+; if you want to make this game very difficult, type the following into the MAME debugger: \r
+; maincpu.mb@1359 = 8\r
+; maincpu.pb@421A = 7\r
+; maincpu.pb@421B = 7\r
+;\r
+\r
+HANDLE_SINGLE_ALIEN_ATTACK:\r
+1344: 3A 28 42      ld   a,($4228)           ; read CAN_ALIEN_ATTACK flag\r
+1347: 0F            rrca                     ; move flag into carry\r
+1348: D0            ret  nc                  ; return if flag is not set\r
+1349: AF            xor  a\r
+134A: 32 28 42      ld   ($4228),a           ; reset flag\r
+134D: 3A 20 42      ld   a,($4220)           ; read HAVE_NO_ALIENS_IN_SWARM flag.\r
+1350: 0F            rrca                     ; move flag into carry\r
+1351: D8            ret  c                   ; return if no aliens are in the swarm.\r
+\r
+; The difficulty level specifies how many aliens can be attacking the player at one time.\r
+1352: 2A 1A 42      ld   hl,($421A)          ; load H with DIFFICULTY_BASE_VALUE and L with DIFFICULTY_EXTRA_VALUE\r
+1355: 7C            ld   a,h                 ; \r
+1356: 85            add  a,l                 ; add DIFFICULTY_EXTRA_VALUE to DIFFICULTY_BASE_VALUE \r
+1357: 1F            rra                      ; divide by 2.\r
+1358: FE 04         cp   $04                 ; is result < 4?\r
+135A: 38 02         jr   c,$135E             ; yes, goto $135E.\r
+135C: 3E 03         ld   a,$03               ; Clamp maximum number of INFLIGHT_ALIEN slots to scan to 3.\r
+135E: 3C            inc  a                   ; ensure that slots to scan is in range of 1..4\r
+\r
+; Scan a specified number of slots (up to 4) in the INFLIGHT_ALIENS array, starting from the *last* slot and working back.\r
+; Take the first slot that has clear IsActive and IsDying flags.\r
+; A = number of slots to scan\r
+135F: 47            ld   b,a                  ; save number of slots to scan in B\r
+1360: 21 91 43      ld   hl,$4391             ; point HL to last INFLIGHT_ALIEN.IsDying flag in INFLIGHT_ALIENS array\r
+1363: 11 E1 FF      ld   de,$FFE1             ; load DE with -31, which is sizeof(INFLIGHT_ALIEN)-1\r
+1366: 7E            ld   a,(hl)               ; read INFLIGHT_ALIEN.IsDying flag\r
+1367: 2B            dec  hl                   ; bump HL to point to INFLIGHT_ALIEN.IsActive flag  \r
+1368: B6            or   (hl)                 ; combine flags. We want A to be 0, to indicate INFLIGHT_ALIEN slot is not in use. \r
+1369: 28 04         jr   z,$136F              ; OK, we have an unused slot, goto $136F\r
+136B: 19            add  hl,de\r
+136C: 10 F8         djnz $1366                ; repeat until we've scanned all the slots we're allowed to\r
+136E: C9            ret\r
+\r
+; If we get here, HL points to an unused INFLIGHT_ALIEN record that will be repurposed for our soon-to-be attacking alien. \r
+; HL = pointer to unused INFLIGHT_ALIEN structure\r
+136F: E5            push hl\r
+1370: DD E1         pop  ix                   ; IX = HL\r
+1372: 3A 15 42      ld   a,($4215)            ; read ALIENS_ATTACK_FROM_RIGHT_FLANK flag\r
+1375: DD 77 06      ld   (ix+$06),a           ; update INFLIGHT_ALIEN.ArcClockwise flag\r
+1378: A7            and  a                    ; test if flag is set  \r
+1379: 20 30         jr   nz,$13AB             ; if flag is set, goto FIND_FIRST_OCCUPIED_SWARM_COLUMN_START_FROM_RIGHT\r
+\r
+; If we get here, we want an alien to break off from the left flank of the swarm.\r
+; We now need to find an alien in the swarm able to attack the player. \r
+; Find first occupied column of aliens starting from the leftmost column.\r
+FIND_FIRST_OCCUPIED_SWARM_COLUMN_START_FROM_LEFT:\r
+137B: 21 FC 41      ld   hl,$41FC             ; address of flag for leftmost alien in ALIEN_IN_COLUMN_FLAGS \r
+137E: 01 0A 00      ld   bc,$000A             ; 10 aliens maximum on a row         \r
+1381: 3E 01         ld   a,$01                ; we are scanning for a value of 1, meaning "column occupied"\r
+1383: ED B9         cpdr                      ; scan $41FC down to $41F3 for value #$01. \r
+1385: C0            ret  nz                   ; if we have no aliens in the swarm (all flags are 0) - return\r
+1386: E0            ret  po                   ; if BC has overflowed, return\r
+\r
+1387: 1E 3F         ld   e,$3F\r
+1389: 2C            inc  l                    ; adjust L because CPDR will have decremented it one time too many \r
+\r
+; HL now points to an entry in ALIEN_IN_COLUMN_FLAGS where we have an alien present.\r
+; If we have flagships in the swarm, then only purple and blue aliens can be sent to attack by this routine.\r
+; If we have no flagships in the swarm, then any remaining red aliens are also considered. (See $13BD)\r
+TRY_FIND_ALIEN_TO_ATTACK:\r
+138A: 3A EF 41      ld   a,($41EF)            ; load a with HAVE_ALIENS_IN_TOP_ROW flag\r
+138D: 0F            rrca                      ; move flag into carry\r
+138E: 30 2D         jr   nc,$13BD             ; if no flagships in swarm, goto INIT_SCAN_FROM_RED_ALIEN_ROW\r
+\r
+; we have flagships, so send a purple or blue alien.\r
+INIT_SCAN_FROM_PURPLE_ALIEN_ROW:\r
+1390: 16 04         ld   d,$04                ; number of rows to scan (1 purple + 3 blue)\r
+1392: 26 41         ld   h,$41                ; MSB of ALIEN_SWARM_FLAGS address \r
+1394: 7D            ld   a,l                  \r
+1395: E6 0F         and  $0F                  ; A = index of column containing alien \r
+1397: C6 50         add  a,$50                ; effectively: HL = $4150 + (L & 0x0f)       \r
+1399: 6F            ld   l,a                  ; HL now points to slot for purple alien in ALIEN_SWARM_FLAGS\r
+\r
+; HL now points to a slot in ALIEN_SWARM_FLAGS. D is a row counter.\r
+; If the slot is occupied, the occupying alien will be sent to attack the player.\r
+; If the slot is unoccupied, we'll scan the same column in the rows beneath until we find an occupied slot or we've done D rows.  \r
+; If we find an alien, we'll send it to attack the player.\r
+SCAN_SPECIFIC_COLUMN_FOR_D_ROWS:\r
+139A: 42            ld   b,d                  ; set B to number of rows to scan\r
+139B: CB 46         bit  0,(hl)               ; test for presence of alien in ALIEN_SWARM_FLAGS              \r
+139D: 20 2F         jr   nz,$13CE             ; if there's an alien present, its "volunteered" to attack, goto $13CE \r
+139F: 7D            ld   a,l                  \r
+13A0: D6 10         sub  $10                  ; sizeof(row in ALIEN_SWARM_FLAGS)\r
+13A2: 6F            ld   l,a                  ; bump HL to point to alien in row beneath\r
+13A3: 10 F6         djnz $139B                ; repeat until B==0\r
+\r
+; OK, We've scanned the entire column and not found an alien. This means that ALIEN_IN_COLUMN_FLAGS isn't truthful,\r
+; and we need to resort to desperate measures. \r
+; \r
+; ** I've not seen this block of code called, and I think it might be legacy or debug **  \r
+;\r
+; Bump HL to point to the purple alien in the column to the right of the one we just scanned. We'll scan that column.  \r
+13A5: 83            add  a,e                  ; add $3F to A.  \r
+13A6: 6F            ld   l,a                  ; Now HL points to purple alien slot\r
+13A7: 0D            dec  c                    ; decrement count of columns remaining that we *can* scan\r
+13A8: 20 F0         jr   nz,$139A             ; if non-zero, repeat the column scan\r
+13AA: C9            ret\r
+\r
+\r
+; If we get here, we want an alien to break off from the right flank of the swarm.\r
+; We now need to find an alien in the swarm willing to attack the player. \r
+; Find first occupied column of aliens starting from the rightmost column.\r
+FIND_FIRST_OCCUPIED_SWARM_COLUMN_START_FROM_RIGHT:\r
+13AB: 21 F3 41      ld   hl,$41F3            ; address of flag for rightmost column of aliens \r
+13AE: 01 0A 00      ld   bc,$000A            ; 10 aliens maximum on a row  \r
+13B1: 3E 01         ld   a,$01               ; we are scanning for a value of 1, meaning "column occupied"\r
+13B3: ED B1         cpir                     ; scan $41F3 up to $41F3 for value #$01. \r
+13B5: C0            ret  nz                  ; if we have no aliens in the swarm - return\r
+13B6: E0            ret  po                  ; if BC has overflowed, return\r
+\r
+; we've found an occupied column\r
+13B7: 1E 41         ld   e,$41\r
+13B9: 2D            dec  l\r
+13BA: C3 8A 13      jp   $138A               ; jump to TRY_FIND_ALIEN_TO_ATTACK:\r
+\r
+\r
+; Called when no flagships present in flagship row. This means we can send any alien, including red, into the attack.\r
+INIT_SCAN_FROM_RED_ALIEN_ROW:\r
+13BD: 16 05         ld   d,$05                ; number of rows of aliens to scan \r
+13BF: 26 41         ld   h,$41                ; MSB of ALIEN_SWARM_FLAGS address \r
+13C1: 7D            ld   a,l\r
+13C2: E6 0F         and  $0F                  ; A = index of column   \r
+13C4: C6 60         add  a,$60                ; effectively: HL = $4150 + (L & 0x0f)\r
+13C6: 6F            ld   l,a                  ; HL now points to slot for red alien in ALIEN_SWARM_FLAGS\r
+13C7: 7B            ld   a,e\r
+13C8: C6 10         add  a,$10\r
+13CA: 5F            ld   e,a\r
+13CB: C3 9A 13      jp   $139A                ; jump to SCAN_SPECIFIC_COLUMN_FOR_D_ROWS\r
+\r
+\r
+;\r
+; Expects:\r
+; HL = pointer to occupied entry in ALIEN_SWARM_FLAGS\r
+; IX = pointer to vacant INFLIGHT_ALIEN structure\r
+;\r
+\r
+13CE: 36 00         ld   (hl),$00\r
+13D0: DD 75 07      ld   (ix+$07),l          ; set INFLIGHT_ALIEN.IndexInSwarm\r
+13D3: DD 36 00 01   ld   (ix+$00),$01        ; set INFLIGHT_ALIEN.IsActive \r
+13D7: DD 36 02 00   ld   (ix+$02),$00        ; set INFLIGHT_ALIEN.StageOfLife\r
+13DB: 16 01         ld   d,$01               ; command: DELETE_ALIEN_COMMAND\r
+13DD: 5D            ld   e,l                 ; parameter: index of alien in swarm\r
+13DE: C3 F2 08      jp   $08F2               ; jump to QUEUE COMMAND\r
+\r
+\r
+\r
+;\r
+; Sets the flank that aliens, including flagships, will attack from.\r
+; \r
+; If you replace $13F3-13F5, $13FF-1401, $1408-140A with zero (NOP), you can then tinker with the flag in $4215 and control \r
+; what side the aliens attack from.\r
+;\r
+\r
+SET_ALIEN_ATTACK_FLANK:\r
+13E1: 2A 0E 42      ld   hl,($420E)          ; read SWARM_SCROLL_VALUE\r
+13E4: ED 5B 10 42   ld   de,($4210)          ; read SWARM_SCROLL_MAX_EXTENTS\r
+13E8: CB 7C         bit  7,h                 \r
+13EA: 28 0B         jr   z,$13F7\r
+\r
+13EC: 7D            ld   a,l\r
+13ED: 92            sub  d\r
+13EE: FE 1C         cp   $1C\r
+13F0: 30 11         jr   nc,$1403            ; if A>$1C, attack from a random flank\r
+13F2: AF            xor  a\r
+13F3: 32 15 42      ld   ($4215),a           ; reset ALIENS_ATTACK_FROM_RIGHT_FLANK flag. Aliens will now attack from left side of swarm.\r
+13F6: C9            ret\r
+\r
+13F7: 7B            ld   a,e\r
+13F8: 95            sub  l\r
+13F9: FE 1C         cp   $1C\r
+13FB: 30 06         jr   nc,$1403            ; if A>$1C, attack from a random flank\r
+13FD: 3E 01         ld   a,$01\r
+13FF: 32 15 42      ld   ($4215),a           ; set ALIENS_ATTACK_FROM_RIGHT_FLANK flag. Aliens will now attack from right side of swarm.\r
+1402: C9            ret\r
+\r
+; Attack from left or right flank, chosen at random\r
+1403: CD 3C 00      call $003C               ; call GENERATE_RANDOM_NUMBER\r
+1406: E6 01         and  $01                 ; mask in bit 0, so A is either 0 or 1\r
+1408: 32 15 42      ld   ($4215),a           ; set/reset ALIENS_ATTACK_FROM_RIGHT_FLANK flag. \r
+140B: C9            ret\r
+\r
+\r
+\r
+\r
+;\r
+; This routine checks if a flagship and escort can break from a given flank to attack the player.\r
+; \r
+; The flank is determined by the ALIENS_ATTACK_FROM_RIGHT_FLANK flag ($4215).\r
+;\r
+; If a flagship exists on the specified flank, send the flagship to attack.  \r
+; If there's red aliens in *close proximity* to the flagship, send a maximum of 2 as an escort.\r
+;\r
+; If there are no flagships on the specified flank, try to send a single red alien from the flank instead.\r
+;\r
+; Notes:\r
+; A flagship can attack when:\r
+;     HAVE_NO_ALIENS_IN_SWARM is set to 0 AND\r
+;     HAS_PLAYER_SPAWNED is set to 1 AND\r
+;     The CAN_FLAGSHIP_OR_RED_ALIENS_ATTACK is set to 1 AND\r
+;     INFLIGHT_ALIENS[1] is available for use\r
+;\r
+;\r
+; Cheat:\r
+; If you type into the MAME debugger: \r
+; maincpu.mb@140C=C9\r
+;\r
+; The flagships stop attacking you completely.\r
+;\r
+\r
+HANDLE_FLAGSHIP_ATTACK:\r
+140C: 3A 20 42      ld   a,($4220)           ; read HAVE_NO_ALIENS_IN_SWARM flag           \r
+140F: 0F            rrca                     ; move flag into carry\r
+1410: D8            ret  c                   ; return if no aliens in the swarm.\r
+1411: 3A 00 42      ld   a,($4200)           ; read HAS_PLAYER_SPAWNED\r
+1414: 0F            rrca                     ; move flag into carry\r
+1415: D0            ret  nc                  ; return if player has not spawned.\r
+1416: 3A 29 42      ld   a,($4229)           ; read CAN_FLAGSHIP_OR_RED_ALIENS_ATTACK\r
+1419: 0F            rrca                     ; move flag into carry\r
+141A: D0            ret  nc                  ; return if flag is not set\r
+141B: AF            xor  a\r
+141C: 32 29 42      ld   ($4229),a           ; reset CAN_FLAGSHIP_OR_RED_ALIENS_ATTACK flag\r
+\r
+; Test if the slot in INFLIGHT_ALIENS reserved for the flagship is in use. If so - do nothing.\r
+141F: 2A D0 42      ld   hl,($42D0)          ; read from INFLIGHT_ALIENS[1] which is the 2nd array element\r
+1422: 7C            ld   a,h                 ; Load A with INFLIGHT_ALIEN.IsDying flag\r
+1423: B5            or   l                   ; OR with INFLIGHT_ALIEN.IsActive flag\r
+1424: 0F            rrca                     ; if alien is active or dying, carry will be set\r
+1425: D8            ret  c                   ; return if alien is active or dying - the slot for the flagship is in use.\r
+\r
+; from what side should the flagship/red aliens attack from?\r
+1426: 3A 15 42      ld   a,($4215)           ; read ALIENS_ATTACK_FROM_RIGHT_FLANK flag\r
+1429: 4F            ld   c,a                 ; C is used to set INFLIGHT_ALIEN.ArcClockwise flag @ $1466\r
+142A: 0F            rrca                     ; move flag into carry\r
+142B: DA BE 14      jp   c,$14BE             ; if attacking from right flank, jump to TRY_FIND_FLAGSHIP_OR_RED_ALIEN_TO_ATTACK_FROM_RIGHT_FLANK\r
+\r
+\r
+TRY_FIND_FLAGSHIP_OR_RED_ALIEN_TO_ATTACK_FROM_LEFT_FLANK:\r
+142E: 21 79 41      ld   hl,$4179            ; load HL with pointer to leftmost flagship in ALIEN_SWARM_FLAGS\r
+1431: 06 04         ld   b,$04               ; scan 4 slots max in the ALIEN_SWARM_FLAGS array to find a flagship\r
+1433: CB 46         bit  0,(hl)              ; test if a flagship is present\r
+1435: 20 3B         jr   nz,$1472            ; if we have found a flagship, goto INIT_FLAGSHIP_ATTACK_FROM_LEFT_FLANK\r
+1437: 2D            dec  l                   ; move to next potential flagship\r
+1438: 10 F9         djnz $1433               ; repeat until B==0\r
+\r
+; If we can't get a flagship, then we scan the red alien row from left to right to find a red alien to attack.\r
+143A: 2E 6A         ld   l,$6A               ; load HL with pointer to leftmost red alien in ALIEN_SWARM_FLAGS\r
+143C: 06 04         ld   b,$04               ; scan first 4 red aliens \r
+143E: CB 46         bit  0,(hl)              ; test if an alien is present\r
+1440: 20 04         jr   nz,$1446            ; if we have found a red alien, goto TRY_INIT_INFLIGHT_ALIEN\r
+1442: 2D            dec  l                   ; bump HL to point to slot of sibling alien\r
+1443: 10 F9         djnz $143E               ; repeat until B==0\r
+1445: C9            ret\r
+\r
+; \r
+; Scan the last 4 entries in the INFLIGHT_ALIENS array for an unused slot. \r
+; If all 4 slots at the end of the array are already in use, exit.\r
+; Otherwise re-use the lastmost free slot for an attacking alien.\r
+;\r
+; Expects:\r
+; HL = pointer to a bit flag in ALIEN_IN_SWARM_FLAGS\r
+;\r
+\r
+TRY_INIT_INFLIGHT_ALIEN:\r
+1446: DD 21 90 43   ld   ix,$4390            ; address of very last INFLIGHT_ALIEN record in INFLIGHT_ALIENS array \r
+144A: 11 E0 FF      ld   de,$FFE0            ; -32 decimal, which is -sizeof(INFLIGHT_ALIEN)\r
+144D: 06 04         ld   b,$04               \r
+144F: DD 7E 00      ld   a,(ix+$00)          ; load A with INFLIGHT_ALIEN.IsActive flag\r
+1452: DD B6 01      or   (ix+$01)            ; OR A with INFLIGHT_ALIEN.IsDying flag\r
+1455: 28 05         jr   z,$145C             ; if the slot is not used for an active or dying alien, goto INIT_INFLIGHT_ALIEN\r
+1457: DD 19         add  ix,de               ; subtract sizeof(INFLIGHT_ALIEN) from IX, to bump IX to previous INFLIGHT_ALIEN record\r
+1459: 10 F4         djnz $144F\r
+145B: C9            ret\r
+\r
+;\r
+; Remove an alien from the swarm, and create an inflight alien in its place.\r
+; \r
+; Expects:\r
+; C = direction alien will break away from swarm. 0 = left, 1 = right\r
+; HL = pointer to entry in ALIEN_SWARM_FLAGS \r
+; IX = pointer to INFLIGHT_ALIEN struct of alien \r
+;\r
+\r
+INIT_INFLIGHT_ALIEN:\r
+145C: 36 00         ld   (hl),$00            ; clear flag in ALIEN_SWARM_FLAGS - effectively removing it from swarm            \r
+145E: DD 36 00 01   ld   (ix+$00),$01        ; set INFLIGHT_ALIEN.IsActive\r
+1462: DD 36 02 00   ld   (ix+$02),$00        ; reset INFLIGHT_ALIEN.StageOfLife\r
+1466: DD 71 06      ld   (ix+$06),c          ; set INFLIGHT_ALIEN.ArcClockwise\r
+1469: DD 75 07      ld   (ix+$07),l          ; set INFLIGHT_ALIEN.IndexInSwarm\r
+146C: 16 01         ld   d,$01               ; command: DELETE_ALIEN_COMMAND\r
+146E: 5D            ld   e,l                 ; parameter: index of alien to delete from the swarm \r
+146F: C3 F2 08      jp   $08F2               ; jump to QUEUE_COMMAND\r
+\r
+\r
+;\r
+; Given a pointer to a flagship entry in the ALIEN_SWARM_FLAGS array, \r
+; scan for red aliens in close proximity to the flagship that can be used as an escort.\r
+; Initialise INFLIGHT_ALIEN records for the flagship and any escort as well. \r
+;\r
+; Expects:\r
+; HL = pointer to entry in flagship row of ALIEN_SWARM_FLAGS\r
+;\r
+\r
+INIT_FLAGSHIP_ATTACK_FROM_LEFT_FLANK:\r
+1472: DD 21 D0 42   ld   ix,$42D0            ; pointer to INFLIGHT_ALIENS_START+sizeof(INFLIGHT_ALIEN)\r
+1476: CD 5C 14      call $145C               ; call INIT_INFLIGHT_ALIEN to make flagship take flight and leave the swarm \r
+1479: 7D            ld   a,l\r
+147A: D6 0F         sub  $0F     \r
+147C: 6F            ld   l,a                 ; bump HL to point at red alien directly below and to right of flagship\r
+147D: FD 21 F0 42   ld   iy,$42F0            ; pointer to INFLIGHT_ALIENS_START+(sizeof(INFLIGHT_ALIEN) * 2)\r
+1481: 06 03         ld   b,$03               ; we're scanning 3 entries in red aliens row max             \r
+1483: 0E 02         ld   c,$02               ; But we only want 2 red aliens as an escort.  \r
+1485: CB 46         bit  0,(hl)              ; test for presence of red alien\r
+1487: C4 8E 14      call nz,$148E            ; if we have a red alien, try to create an inflight alien  \r
+148A: 2D            dec  l                   ; bump HL to point to slot of sibling alien \r
+148B: 10 F8         djnz $1485               ; repeat until B==0\r
+148D: C9            ret\r
+\r
+; HL = pointer to entry in ALIEN_SWARM_FLAGS\r
+148E: CD 9B 14      call $149B               ; call TRY_INIT_ESCORT_INFLIGHT_ALIEN\r
+1491: 11 20 00      ld   de,$0020            ; sizeof(INFLIGHT_ALIEN)\r
+1494: FD 19         add  iy,de               ; bump IY to point to next member of INFLIGHT_ALIENS array\r
+1496: 0D            dec  c                   ; reduce count of red aliens left to check for use as escort\r
+1497: C0            ret  nz                  ; return if we have all the escort we need\r
+1498: 06 01         ld   b,$01\r
+149A: C9            ret\r
+\r
+\r
+;\r
+; Try to create an escort for a flagship. \r
+;\r
+; Expects:\r
+; HL = pointer to red alien in ALIEN_SWARM_FLAGS that could be escort\r
+; IX = pointer to INFLIGHT_ALIEN structure (used for flagship)\r
+; IY = pointer to INFLIGHT_ALIEN structure (will be used for escort) \r
+;\r
+; If the INFLIGHT_ALIEN pointed to by IY is not occupied by an active or dying alien, then\r
+; the record is re-used and marked as active. \r
+; Otherwise this routine exits.\r
+\r
+TRY_INIT_ESCORT_INFLIGHT_ALIEN:\r
+149B: FD CB 00 46   bit  0,(iy+$00)          ; test INFLIGHT_ALIEN.IsActive\r
+149F: C0            ret  nz                  ; return if flag is set\r
+14A0: FD CB 01 46   bit  0,(iy+$01)          ; test INFLIGHT_ALIEN.IsDying\r
+14A4: C0            ret  nz                  ; return if flag is set\r
+\r
+; OK, we can use the INFLIGHT_ALIEN slot at IY. Let's remove the alien from the swarm\r
+; and create \r
+14A5: 36 00         ld   (hl),$00            ; clear flag in ALIEN_SWARM_FLAGS\r
+14A7: FD 36 00 01   ld   (iy+$00),$01        ; set INFLIGHT_ALIEN.IsActive\r
+14AB: FD 36 02 00   ld   (iy+$02),$00        ; reset INFLIGHT_ALIEN.StageOfLife\r
+14AF: DD 7E 06      ld   a,(ix+$06)          ; read flagship's INFLIGHT_ALIEN.ArcClockwise\r
+14B2: FD 77 06      ld   (iy+$06),a          ; set escort INFLIGHT_ALIEN.ArcClockwise so it breaks away in formation.\r
+14B5: FD 75 07      ld   (iy+$07),l          ; set escort INFLIGHT_ALIEN.IndexInSwarm\r
+14B8: 16 01         ld   d,$01               ; command: DELETE_ALIEN_COMMAND\r
+14BA: 5D            ld   e,l                 ; parameter: index of alien to delete from the swarm\r
+14BB: C3 F2 08      jp   $08F2               ; jump to QUEUE_COMMAND\r
+\r
+\r
+TRY_FIND_FLAGSHIP_OR_RED_ALIEN_TO_ATTACK_FROM_RIGHT_FLANK:\r
+14BE: 21 76 41      ld   hl,$4176            ; load HL with pointer to rightmost flagship in ALIEN_SWARM_FLAGS\r
+14C1: 06 04         ld   b,$04               ; scan max of 4 flagships in array\r
+14C3: CB 46         bit  0,(hl)              ; test if a flagship is present\r
+14C5: 20 10         jr   nz,$14D7            ; if we have found a flagship, goto INIT_FLAGSHIP_ATTACK_FROM_RIGHT_FLANK\r
+14C7: 2C            inc  l                   ; otherwise try looking for a flagship to immediate left\r
+14C8: 10 F9         djnz $14C3               ; repeat until B==0\r
+\r
+; If we can't find a single flagship, then we try the red alien row. \r
+14CA: 2E 65         ld   l,$65               ; load HL with pointer to rightmost red alien in ALIEN_SWARM_FLAGS array\r
+14CC: 06 04         ld   b,$04               ; scan max of 4 slots in array \r
+14CE: CB 46         bit  0,(hl)              ; test if red alien is present\r
+14D0: C2 46 14      jp   nz,$1446            ; if we have found a red alien, goto $1446\r
+14D3: 2C            inc  l                   ; bump HL to point to slot of sibling alien\r
+14D4: 10 F8         djnz $14CE               ; repeat until B==0\r
+14D6: C9            ret\r
+\r
+\r
+; Near duplicate of INIT_FLAGSHIP_ATTACK_FROM_LEFT_FLANK @$1472, except for the right flank. \r
+;\r
+; Given a pointer to a flagship entry in the ALIEN_SWARM_FLAGS array, \r
+; scan for red aliens in close proximity to the flagship that can be used as an escort.\r
+; Initialise INFLIGHT_ALIEN records for the flagship and any escort as well. \r
+;\r
+; Expects:\r
+; HL = pointer to flag in ALIEN_SWARM_FLAGS representing flagship\r
+;\r
+\r
+INIT_FLAGSHIP_ATTACK_FROM_RIGHT_FLANK:\r
+14D7: DD 21 D0 42   ld   ix,$42D0            ; pointer to INFLIGHT_ALIENS_START+sizeof(INFLIGHT_ALIEN) \r
+14DB: CD 5C 14      call $145C               ; Remove an alien from the swarm, and create an inflight alien in its place.\r
+14DE: 7D            ld   a,l\r
+14DF: D6 11         sub  $11                 ; bump HL to point at red alien directly below and to right of flagship\r
+14E1: 6F            ld   l,a                 \r
+14E2: FD 21 F0 42   ld   iy,$42F0            ; pointer to INFLIGHT_ALIENS_START+(sizeof(INFLIGHT_ALIEN) * 2)\r
+14E6: 06 03         ld   b,$03\r
+14E8: 0E 02         ld   c,$02\r
+14EA: CB 46         bit  0,(hl)              ; do we have a red alien?\r
+14EC: C4 8E 14      call nz,$148E\r
+14EF: 2C            inc  l\r
+14F0: 10 F8         djnz $14EA\r
+14F2: C9            ret\r
+\r
+\r
+\r
+;\r
+; Increase game difficulty as the level goes on.\r
+;\r
+\r
+HANDLE_LEVEL_DIFFICULTY:\r
+14F3: 3A 00 42      ld   a,($4200)           ; read HAS_PLAYER_SPAWNED\r
+14F6: 0F            rrca                     ; move flag into carry\r
+14F7: D0            ret  nc                  ; return if player has not spawned\r
+14F8: 3A 2B 42      ld   a,($422B)           ; read IS_FLAGSHIP_HIT\r
+14FB: 0F            rrca                     ; move flag into carry\r
+14FC: D8            ret  c                   ; return if flagship has been hit\r
+\r
+; wait until DIFFICULTY_COUNTER_1 counts down to zero.\r
+14FD: 21 18 42      ld   hl,$4218            ; load HL with address of DIFFICULTY_COUNTER_1\r
+1500: 35            dec  (hl)                ; decrement counter\r
+1501: C0            ret  nz\r
+1502: 36 3C         ld   (hl),$3C            ; reset counter\r
+\r
+; DIFFICULTY_COUNTER_1 has reached zero and reset. Decrement DIFFICULTY_COUNTER_2.\r
+1504: 23            inc  hl                  ; bump HL to DIFFICULTY_COUNTER_2\r
+1505: 35            dec  (hl)                ; decrement counter\r
+1506: C0            ret  nz\r
+1507: 36 14         ld   (hl),$14            ; reset counter \r
+\r
+; DIFFICULTY_COUNTER_2 has reached zero. Now up the difficulty level, if we can.\r
+1509: 23            inc  hl                  ; bump HL to $421A (DIFFICULTY_EXTRA_VALUE)\r
+150A: 7E            ld   a,(hl)              ; read DIFFICULTY_EXTRA_VALUE\r
+150B: FE 07         cp   $07                 ; has it reached its maximum value of 7?\r
+150D: C8            ret  z                   ; return if so\r
+150E: 30 02         jr   nc,$1512            ; if A >= 7 , goto $1512\r
+\r
+1510: 34            inc  (hl)                ; increment DIFFICULTY_EXTRA_VALUE  \r
+1511: C9            ret\r
+\r
+1512: 36 07         ld   (hl),$07            ; clamp DIFFICULTY_EXTRA_VALUE to 7\r
+1514: C9            ret\r
+\r
+\r
+;\r
+; Check if an alien can attack the player.\r
+; For flagships, see $15C3 \r
+;\r
+\r
+CHECK_IF_ALIEN_CAN_ATTACK:\r
+1515: 3A 00 42      ld   a,($4200)           ; read HAS_PLAYER_SPAWNED\r
+1518: 0F            rrca                     ; move flag into carry\r
+1519: D0            ret  nc                  ; return if player has not spawned\r
+151A: 3A 20 42      ld   a,($4220)           ; read HAVE_NO_ALIENS_IN_SWARM flag\r
+151D: 0F            rrca                     ; move flag into carry\r
+151E: D8            ret  c                   ; return if we don't have any aliens in the swarm\r
+151F: 3A 2B 42      ld   a,($422B)           ; read IS_FLAGSHIP_HIT\r
+1522: 0F            rrca                     ; move flag into carry\r
+1523: D8            ret  c                   ; return if the flagship has been hit\r
+\r
+; Use DIFFICULTY_EXTRA_VALUE and DIFFICULTY_BASE_VALUE to calculate how many secondary counters in the ALIEN_ATTACK_COUNTERS array\r
+; we can decrement. The more counters = the higher probability one of them will count down to zero = higher probability an alien attacks.\r
+1524: 2A 1A 42      ld   hl,($421A)          ; load H with DIFFICULTY_BASE_VALUE and L with DIFFICULTY_EXTRA_VALUE\r
+1527: 7C            ld   a,h                 ; A = DIFFICULTY_BASE_VALUE value\r
+1528: FE 02         cp   $02\r
+152A: 30 01         jr   nc,$152D            ; if DIFFICULTY_BASE_VALUE >=2, goto $152D\r
+152C: AF            xor  a\r
+152D: 85            add  a,l                 ; Add DIFFICULTY_EXTRA_VALUE to DIFFICULTY_BASE_VALUE \r
+152E: E6 0F         and  $0F                 ; Ensure value is between 0 and 15\r
+1530: 3C            inc  a                   ; Add 1 to ensure it's between 1..16 \r
+1531: 47            ld   b,a                 ; B now contains number of counters to decrement\r
+\r
+; Decrement ALIEN_ATTACK_MASTER_COUNTER. When it hits zero, we can decrement secondary counters in the ALIEN_ATTACK_MASTER_COUNTERS array.\r
+1532: 21 4A 42      ld   hl,$424A            ; load HL with address of ALIEN_ATTACK_MASTER_COUNTER\r
+1535: 11 E3 15      ld   de,$15E3            ; load DE with address of ALIEN_ATTACK_COUNTER_DEFAULT_VALUES\r
+1538: 35            dec  (hl)                ; decrement ALIEN_ATTACK_MASTER_COUNTER \r
+1539: 28 05         jr   z,$1540             ; if its hit zero, goto $1540 to decrement [B] counters \r
+153B: AF            xor  a\r
+153C: 32 28 42      ld   ($4228),a           ; reset CAN_ALIEN_ATTACK flag. No alien will attack.\r
+153F: C9            ret\r
+\r
+; When we get here, ALIEN_ATTACK_MASTER_COUNTER is zero. \r
+; B specifies how many secondary counters in the ALIEN_ATTACK_COUNTERS array we can decrement. (Max value of 16)\r
+; DE points to a default value to reset the ALIEN_ATTACK_MASTER_COUNTER back to. \r
+1540: 0E 00         ld   c,$00\r
+1542: 1A            ld   a,(de)              ; read default value from table @ $15E3      \r
+1543: 77            ld   (hl),a              ; Reset ALIEN_ATTACK_MASTER_COUNTER to its default value\r
+\r
+; Decrement B counters in the ALIEN_ATTACK_COUNTERS array. \r
+; If any of the counters hit zero, reset the counter to its default value and set the CAN_ALIEN_ATTACK flag to 1.\r
+1544: 23            inc  hl                  ; bump HL to next secondary counter \r
+1545: 13            inc  de                  ; bump DE to address containing default value to reset secondary counter to when zero \r
+1546: 35            dec  (hl)                ; decrement secondary counter  \r
+1547: CC DF 15      call z,$15DF             ; if the secondary counter reaches zero, reset the counter and increment C. Alien will attack!\r
+154A: 10 F8         djnz $1544               ; repeat until B==0\r
+\r
+; if C is set to a nonzero value then that means that a secondary counter has reached zero. Its time for an alien to attack. \r
+154C: 79            ld   a,c\r
+154D: A7            and  a                   ; test if A is zero \r
+154E: C8            ret  z                   ; exit if so\r
+154F: 3E 01         ld   a,$01 \r
+1551: 32 28 42      ld   ($4228),a           ; set CAN_ALIEN_ATTACK flag. Alien will break off from the swarm\r
+1554: C9            ret\r
+\r
+\r
+\r
+;\r
+; This routine is responsible for determining when flagships can attack.\r
+;\r
+;\r
+\r
+UPDATE_ATTACK_COUNTERS:\r
+1555: 3A 00 42      ld   a,($4200)           ; read HAS_PLAYER_SPAWNED\r
+1558: 0F            rrca                     ; move flag into carry\r
+1559: D0            ret  nc                  ; return if player has not spawned\r
+155A: 3A EF 41      ld   a,($41EF)           ; read HAVE_ALIENS_IN_TOP_ROW \r
+155D: 0F            rrca                     ; move flag into carry\r
+155E: D0            ret  nc                  ; return if we have no flagships\r
+155F: 3A 2B 42      ld   a,($422B)           ; read IS_FLAGSHIP_HIT \r
+1562: 0F            rrca                     ; move flag into carry\r
+1563: D8            ret  c                   ; return if a flagship has been hit\r
+1564: 3A 06 40      ld   a,($4006)           ; read IS_GAME_IN_PLAY\r
+1567: 0F            rrca                     ; move flag into carry\r
+1568: 30 3D         jr   nc,$15A7            ; if game is not in play, goto $15A7      \r
+\r
+; wait until FLAGSHIP_ATTACK_MASTER_COUNTER_1 counts down to zero.\r
+156A: 21 45 42      ld   hl,$4245            ; load HL with address of FLAGSHIP_ATTACK_MASTER_COUNTER_1\r
+156D: 35            dec  (hl)                ; decrement counter\r
+156E: C0            ret  nz                  ; exit if counter is not zero\r
+156F: 36 3C         ld   (hl),$3C            ; reset counter\r
+\r
+; if we have no blue or purple aliens, we don't need to bother with the FLAGSHIP_ATTACK_MASTER_COUNTER_2 countdown. \r
+1571: 3A 21 42      ld   a,($4221)           ; read HAVE_NO_BLUE_OR_PURPLE_ALIENS \r
+1574: 0F            rrca                     ; move flag into carry\r
+1575: 38 2C         jr   c,$15A3             ; if there's no blue or purple aliens left, goto $15A3\r
+\r
+; otherwise, wait until FLAGSHIP_ATTACK_MASTER_COUNTER_2 counts down to 0.\r
+1577: 23            inc  hl                  ; bump HL to FLAGSHIP_ATTACK_MASTER_COUNTER_2\r
+1578: 35            dec  (hl)                ; decrement counter\r
+1579: C0            ret  nz                  ; return if its not counteed down to zero.\r
+157A: 34            inc  (hl)                ; set FLAGSHIP_ATTACK_MASTER_COUNTER_2 to 1\r
+\r
+; count how many "extra" flagships we have carried over from previous waves (maximum of 2)\r
+157B: 2A 77 41      ld   hl,($4177)          ; point to usually empty flagship entry in ALIEN_SWARM_FLAGS. \r
+157E: 7C            ld   a,h                 \r
+157F: 85            add  a,l                 ; A now = number of *extra* flagships we have                 \r
+1580: E6 03         and  $03                 ; ensure that number is between 0..3. (it should be between 0..2 anyway)\r
+1582: 4F            ld   c,a                 ; save count of extra flagships in C\r
+\r
+; use difficulty settings and count of extra flagships to compute countdown before flagship attack\r
+1583: 2A 1A 42      ld   hl,($421A)          ; load H with DIFFICULTY_BASE_VALUE and L with DIFFICULTY_EXTRA_VALUE\r
+1586: 7C            ld   a,h\r
+1587: 85            add  a,l                 ; Add DIFFICULTY_BASE_VALUE to DIFFICULTY_EXTRA_VALUE\r
+1588: C8            ret  z                   ; exit if both DIFFICULTY_BASE_VALUE and DIFFICULTY_EXTRA_VALUE are 0\r
+\r
+1589: 0F            rrca                     ; divide A..                     \r
+158A: 0F            rrca                     ; by 4\r
+158B: E6 03         and  $03                 ; clamp A to 3 maximum.\r
+158D: 2F            cpl                      ; A = 255-A.\r
+158E: C6 0A         add  a,$0A               ; ensure that A is between $06 and $09\r
+1590: 91            sub  c                   ; subtract count of extra flagships\r
+1591: 32 46 42      ld   ($4246),a           ; set FLAGSHIP_ATTACK_MASTER_COUNTER_2\r
+\r
+; set timer for when flagship will definitely attack.\r
+1594: 07            rlca\r
+1595: 07            rlca\r
+1596: 32 2F 42      ld   ($422F),a           ; set FLAGSHIP_ATTACK_SECONDARY_COUNTER\r
+\r
+1599: 07            rlca\r
+159A: 32 4A 42      ld   ($424A),a           ; set ALIEN_ATTACK_MASTER_COUNTER\r
+\r
+; enable timer for flagship to attack.\r
+159D: 3E 01         ld   a,$01\r
+159F: 32 2E 42      ld   ($422E),a           ; set ENABLE_FLAGSHIP_ATTACK_SECONDARY_COUNTER\r
+15A2: C9            ret\r
+\r
+15A3: 3E 02         ld   a,$02\r
+15A5: 18 ED         jr   $1594\r
+\r
+\r
+; Called when game is not in play\r
+15A7: 21 45 42      ld   hl,$4245            ; load HL with address of FLAGSHIP_ATTACK_MASTER_COUNTER_1\r
+15AA: 35            dec  (hl)               \r
+15AB: C0            ret  nz\r
+15AC: 36 3C         ld   (hl),$3C \r
+15AE: 23            inc  hl                  ; load HL with address of FLAGSHIP_ATTACK_MASTER_COUNTER_2\r
+15AF: 35            dec  (hl)\r
+15B0: C0            ret  nz\r
+15B1: 36 05         ld   (hl),$05\r
+\r
+15B3: 3E 5A         ld   a,$5A\r
+15B5: 32 2F 42      ld   ($422F),a           ; set FLAGSHIP_ATTACK_SECONDARY_COUNTER\r
+\r
+15B8: 3E 2D         ld   a,$2D\r
+15BA: 32 4A 42      ld   ($424A),a           ; set ALIEN_ATTACK_MASTER_COUNTER\r
+\r
+15BD: 3E 01         ld   a,$01\r
+15BF: 32 2E 42      ld   ($422E),a           ; set ENABLE_FLAGSHIP_ATTACK_SECONDARY_COUNTER\r
+15C2: C9            ret\r
+\r
+\r
+;\r
+; Determines if a flagship can be permitted to attack.\r
+;\r
+; If so, CAN_FLAGSHIP_OR_RED_ALIENS_ATTACK is set to 1.\r
+;\r
+\r
+\r
+CHECK_IF_FLAGSHIP_CAN_ATTACK:\r
+15C3: 21 2E 42      ld   hl,$422E            ; read ENABLE_FLAGSHIP_ATTACK_SECONDARY_COUNTER\r
+15C6: CB 46         bit  0,(hl)              ; test flag\r
+15C8: C8            ret  z                   ; return if not allowed to count down\r
+\r
+; wait until FLAGSHIP_ATTACK_SECONDARY_COUNTER counts down to zero.\r
+15C9: 23            inc  hl                  ; bump HL to FLAGSHIP_ATTACK_SECONDARY_COUNTER\r
+15CA: 35            dec  (hl)                ; decrement counter\r
+15CB: C0            ret  nz                  ; return if counter hasn't reached zero\r
+\r
+15CC: 2B            dec  hl                  ; bump HL to ENABLE_FLAGSHIP_ATTACK_SECONDARY_COUNTER flag \r
+15CD: 36 00         ld   (hl),$00            ; reset flag\r
+15CF: 3A 00 42      ld   a,($4200)           ; read HAS_PLAYER_SPAWNED\r
+15D2: 0F            rrca                     ; move flag into carry\r
+15D3: D0            ret  nc                  ; return if player has not spawned\r
+\r
+; check if we have any flagship\r
+15D4: 3A EF 41      ld   a,($41EF)           ; read HAVE_ALIENS_IN_TOP_ROW flag\r
+15D7: 0F            rrca                     ; move flag bit into carry\r
+15D8: D0            ret  nc                  ; return if no flagships\r
+\r
+; yes, we have flagships, set CAN_FLAGSHIP_OR_RED_ALIENS_ATTACK flag\r
+15D9: 3E 01         ld   a,$01\r
+15DB: 32 29 42      ld   ($4229),a           ; set CAN_FLAGSHIP_OR_RED_ALIENS_ATTACK\r
+15DE: C9            ret\r
+\r
+\r
+; A= *DE;\r
+; *HL = A;\r
+; C++\r
+15DF: 1A            ld   a,(de)\r
+15E0: 77            ld   (hl),a\r
+15E1: 0C            inc  c\r
+15E2: C9            ret\r
+\r
+\r
+; Default values for the corresponding entries in the ALIEN_ATTACK_COUNTERS array.\r
+; e.g. $424A's default value is 5, $424B's default value is $2F, $424C's default is $43...\r
+; When any counter hits zero, it is reset to its default value.\r
+ALIEN_ATTACK_COUNTER_DEFAULT_VALUES: \r
+15E3:  05 2F 43 77 71 6D 67 65 4F 49 43 3D 3B 35 2B 29\r
+\r
+\r
+;\r
+; This routine calculates how far away from the player inflight aliens can be before they can start shooting at you.\r
+; \r
+; The minimum shooting distance increases as more aliens are killed, making the aliens shoot more often.\r
+;  \r
+; See also: $0E54\r
+\r
+HANDLE_CALC_INFLIGHT_ALIEN_SHOOTING_DISTANCE:\r
+15F4: 21 E8 41      ld   hl,$41E8            ; load HL with address of HAVE_ALIENS_IN_ROW_FLAGS\r
+15F7: 06 04         ld   b,$04               ; we're testing potentially 4 pairs of rows.\r
+15F9: 3A 1B 42      ld   a,($421B)           ; read DIFFICULTY_BASE_VALUE\r
+15FC: A7            and  a                   ; test if zero\r
+15FD: 20 16         jr   nz,$1615            ; if non-zero, which it always is, goto $1615\r
+\r
+; These two lines of code appear never to be called. This must be for an EASY difficulty level we've not seen.\r
+15FF: 1E 01         ld   e,$01               ; multiplier = 1\r
+1601: 16 84         ld   d,$84               ; exact X coordinate  \r
+\r
+1603: CB 46         bit  0,(hl)              ; test for alien presence\r
+1605: 20 09         jr   nz,$1610            ; if alien is present, goto $1610\r
+\r
+1607: 23            inc  hl                  ; bump HL to flag for next row \r
+1608: CB 46         bit  0,(hl)              ; test flag \r
+160A: 20 04         jr   nz,$1610            ; if flag is set, goto $1610\r
+\r
+160C: 23            inc  hl                  ; bump to next entry in HAVE_ALIENS_IN_ROW_FLAGS\r
+160D: 1C            inc  e                   ; increment multiplier (see $0E54 for clarification on how its used)\r
+160E: 10 F3         djnz $1603\r
+\r
+1610: ED 53 13 42   ld   ($4213),de          ; set INFLIGHT_ALIEN_SHOOT_EXACT_X to D, INFLIGHT_ALIEN_SHOOT_RANGE_MUL to E\r
+1614: C9            ret\r
+\r
+1615: 1E 02         ld   e,$02               ; multiplier = 2\r
+1617: 16 9D         ld   d,$9D               ; exact X coordinate \r
+1619: 18 E8         jr   $1603\r
+\r
+; TODO: I can't find anything calling this. Is this debug code left over?\r
+161B: 1E 03         ld   e,$03\r
+161D: 16 B6         ld   d,$B6\r
+161F: 18 E2         jr   $1603\r
+\r
+\r
+;\r
+; LEVEL_COMPLETE is set to 1 by this function when: \r
+; HAVE_NO_ALIENS_IN_SWARM is set to 1 AND\r
+; HAVE_NO_INFLIGHT_OR_DYING_ALIENS is set to 1 AND\r
+; LEVEL_COMPLETE is clear \r
+;\r
+\r
+CHECK_IF_LEVEL_IS_COMPLETE:\r
+1621: 3A 20 42      ld   a,($4220)           ; read HAVE_NO_ALIENS_IN_SWARM\r
+1624: 0F            rrca                     ; move flag bit into carry\r
+1625: D0            ret  nc                  ; return if flag is not set, meaning that there are aliens left in the swarm\r
+1626: 3A 25 42      ld   a,($4225)           ; read HAVE_NO_INFLIGHT_OR_DYING_ALIENS\r
+1629: 0F            rrca                     ; move flag bit into carry\r
+162A: D0            ret  nc                  ; return if flag is not set, meaning that there are aliens attacking, or dying\r
+162B: 3A 22 42      ld   a,($4222)           ; read LEVEL_COMPLETE\r
+162E: 0F            rrca                     ; move flag bit into carry\r
+162F: D8            ret  c                   ; return if flag is not set\r
+1630: 21 01 00      ld   hl,$0001\r
+1633: 22 22 42      ld   ($4222),hl          ; set LEVEL_COMPLETE to 1 and NEXT_LEVEL_DELAY_COUNTER to 0.                  \r
+1636: C9            ret\r
+\r
+\r
+\r
+HANDLE_LEVEL_COMPLETE:\r
+1637: 21 22 42      ld   hl,$4222            ; load HL with address of LEVEL_COMPLETE\r
+163A: CB 46         bit  0,(hl)              ; test flag \r
+163C: C8            ret  z                   ; return if level is not complete\r
+\r
+; OK, level is complete. Wait until NEXT_LEVEL_DELAY_COUNTER to reach 0. \r
+163D: 23            inc  hl                  ; bump HL to point to NEXT_LEVEL_DELAY_COUNTER\r
+163E: 35            dec  (hl)                ; decrement count\r
+163F: C0            ret  nz                  ; return if count is !=0\r
+\r
+1640: 2B            dec  hl                  ; bump HL to point to LEVEL_COMPLETE again.\r
+1641: 36 00         ld   (hl),$00            ; clear LEVEL_COMPLETE flag.\r
+\r
+1643: 11 1B 05      ld   de,$051B            ; load DE with address of PACKED_DEFAULT_SWARM_DEFINITION\r
+1646: CD 46 06      call $0646               ; call UNPACK_ALIEN_SWARM \r
+1649: AF            xor  a\r
+164A: 32 1A 42      ld   ($421A),a           ; reset DIFFICULTY_EXTRA_VALUE\r
+164D: 32 5F 42      ld   ($425F),a           ; reset TIMING_VARIABLE\r
+1650: 21 01 00      ld   hl,$0001\r
+1653: 22 0E 42      ld   ($420E),hl          ; set SWARM_SCROLL_VALUE\r
+\r
+; increase game difficulty level, if we can.\r
+1656: 2A 1B 42      ld   hl,($421B)          ; load H with PLAYER_LEVEL and L with DIFFICULTY_BASE_VALUE\r
+1659: 24            inc  h                   ; increment player level \r
+165A: 7D            ld   a,l                 ; load A with DIFFICULTY_BASE_VALUE\r
+165B: FE 07         cp   $07                 ; are we at max difficulty?\r
+165D: 28 03         jr   z,$1662             ; yes, goto $1662\r
+165F: 30 22         jr   nc,$1683            ; edge case: we're above max difficulty! So clamp difficulty level to 7.\r
+1661: 3C            inc  a                   ; otherwise, increment DIFFICULTY_BASE_VALUE\r
+1662: 6F            ld   l,a\r
+1663: 22 1B 42      ld   ($421B),hl          ; update PLAYER_LEVEL and DIFFICULTY_BASE_VALUE\r
+\r
+1666: 11 00 07      ld   de,$0700            ; command: BOTTOM_OF_SCREEN_INFO_COMMAND, parameter: 0 (DISPLAY_LEVEL_FLAGS)\r
+1669: CD F2 08      call $08F2               ; call QUEUE_COMMAND. \r
+\r
+; How many flagships survived from the last round? If so, they need to be added into the swarm before the level starts.\r
+166C: 3A 1E 42      ld   a,($421E)           ; get value of FLAGSHIP_SURVIVOR_COUNT into A\r
+166F: A7            and  a                   ; Did any flagships survive from the last round?\r
+1670: C8            ret  z                   ; Return if no flagships survived.\r
+1671: 21 77 41      ld   hl,$4177            ; load HL with address of free slot in flagship row of ALIEN_SWARM_FLAGS\r
+1674: 36 01         ld   (hl),$01            ; create a flagship!\r
+1676: 3D            dec  a                   ;   \r
+1677: 32 1E 42      ld   ($421E),a           ; set value of FLAGSHIP_SURVIVOR_COUNT\r
+167A: C8            ret  z                   ; return if zero. \r
+167B: 23            inc  hl                  ; bump HL to address of next free slot in flagship row \r
+167C: 36 01         ld   (hl),$01            ; create a flagship!\r
+167E: AF            xor  a                \r
+167F: 32 1E 42      ld   ($421E),a           ; clear value of FLAGSHIP_SURVIVOR_COUNT\r
+1682: C9            ret\r
+\r
+\r
+\r
+CLAMP_DIFFICULTY_LEVEL:\r
+1683: 3E 07         ld   a,$07               ; maximum value for DIFFICULTY_BASE_VALUE\r
+1685: C3 62 16      jp   $1662               ; set DIFFICULTY_BASE_VALUE \r
+\r
+\r
+;\r
+; When you shoot a flagship, the swarm goes into shock for a short period of time. No aliens will break off to attack you.\r
+;\r
+\r
+HANDLE_SHOCKED_SWARM:\r
+1688: 21 2B 42      ld   hl,$422B            ; load HL with address of IS_FLAGSHIP_HIT flag     \r
+168B: CB 46         bit  0,(hl)              ; test flag\r
+168D: C8            ret  z                   ; return if flagship has not been hit\r
+168E: 3A 24 42      ld   a,($4224)           ; read HAVE_AGGRESSIVE_ALIENS flag\r
+1691: A7            and  a                   ; test flag\r
+1692: 20 0B         jr   nz,$169F            ; if flag is set, goto $169F \r
+1694: 3A 21 42      ld   a,($4221)           ; read HAVE_NO_BLUE_OR_PURPLE_ALIENS\r
+1697: A7            and  a                   ; test flag\r
+1698: 20 05         jr   nz,$169F            ; if flag is set, goto $169F\r
+169A: 3A 26 42      ld   a,($4226)           ; read HAVE_NO_INFLIGHT_ALIENS\r
+169D: 0F            rrca                     ; move flag into carry\r
+169E: D0            ret  nc                  ; return if some aliens are inflight\r
+169F: 23            inc  hl                  ; bump HL to address of ALIENS_IN_SHOCK_COUNTER\r
+16A0: 35            dec  (hl)                ; decrement counter. When it hits zero, aliens will snap out of it!\r
+16A1: C0            ret  nz                  ; exit routine if counter non-zero\r
+16A2: 2B            dec  hl                  ; bump HL to address of IS_FLAGSHIP_HIT\r
+16A3: 36 00         ld   (hl),$00            ; clear flag. Aliens can break off from the swarm to attack again.\r
+16A5: C9            ret\r
+\r
+;\r
+; Looks like this might be legacy code imported from an older game; it writes to a port that does nothing\r
+;\r
+\r
+16A6: 3A 07 40      ld   a,($4007)            ; read IS_GAME_OVER flag\r
+16A9: 0F            rrca                      ; move bit 0 into carry\r
+16AA: D8            ret  c                    ; if carry set, return\r
+16AB: 21 DF 41      ld   hl,$41DF\r
+16AE: 7E            ld   a,(hl)\r
+16AF: A7            and  a\r
+16B0: C8            ret  z\r
+16B1: 0F            rrca\r
+16B2: 0F            rrca\r
+16B3: 32 04 68      ld   ($6804),a            ; Does nothing - this port is not connected\r
+16B6: 35            dec  (hl)\r
+16B7: C9            ret\r
+\r
+\r
+;\r
+; You may have noticed that when you're close to obliterating the swarm, that the background swarm noises\r
+; get fewer and fewer, until there's no background noise, just the sound of attacking aliens and your bullets.\r
+; This is the routine that handles the background noises. But this isn't the most important thing the routine does. \r
+; \r
+; Tucked away here is more important code, which affects the aliens aggressiveness. If you have 3 aliens or less\r
+; in the swarm (inflight aliens don't count), the aliens are enraged and will be far more aggressive.\r
+; Any aliens that take flight to attack you (inflight aliens) will never return to the swarm and keep attacking\r
+; until either you or they are dead.\r
+;\r
+; If you wish to artificially enforce aggressiveness, pause the game and input the following into the MAME debugger:\r
+;\r
+; maincpu.mb@16e3=c9\r
+; maincpu.mb@16e7=c9\r
+; maincpu.pb@4224=1       // note the .pb, not .mb\r
+;\r
+; This will make the aliens attack you constantly - even when you start a new level.\r
+\r
+HANDLE_ALIEN_AGGRESSIVENESS:\r
+16B8: 3A 07 40      ld   a,($4007)           ; read IS_GAME_OVER flag\r
+16BB: 0F            rrca                     ; move flag into carry\r
+16BC: D8            ret  c                   ; return if GAME OVER   \r
+16BD: 21 23 41      ld   hl,$4123            ; load HL with address of very first alien in ALIEN_SWARM_FLAGS\r
+16C0: 11 06 00      ld   de,$0006            ; DE is an offset to add to HL after processing a row of aliens\r
+16C3: 4B            ld   c,e                 ; Conveniently, E is also number of rows of aliens in swarm! (6) \r
+16C4: 3E 01         ld   a,$01               ; A is going to be used to total the number of aliens in the swarm \r
+16C6: 06 0A         ld   b,$0A               ; 10 aliens maximum per row\r
+16C8: 86            add  a,(hl)              \r
+16C9: 2C            inc  l                   ; bump HL to point to next alien in ALIEN_SWARM_FLAGS\r
+16CA: 10 FC         djnz $16C8               ; repeat until all aliens in the row have been done\r
+16CC: 19            add  hl,de               ; make HL point to first alien in row above \r
+16CD: 0D            dec  c                   ; do rows until C==0\r
+16CE: C2 C6 16      jp   nz,$16C6\r
+\r
+; When we get here, A = total number of aliens left alive in the swarm + 1\r
+16D1: 21 00 68      ld   hl,$6800            ; load HL with address of !SOUND  reset background F1 port\r
+16D4: 06 03         ld   b,$03               ; number of ports to write to maximum \r
+16D6: 3D            dec  a                   ; decrement total by 1 \r
+16D7: 28 14         jr   z,$16ED             ; if total is zero, goto $16ED \r
+\r
+; This piece of code writes 1 to !SOUND  reset background F1 to F3 \r
+16D9: 36 01         ld   (hl),$01            ; \r
+16DB: 2C            inc  l\r
+16DC: 10 F8         djnz $16D6\r
+\r
+16DE: FE 02         cp   $02                 ;                  \r
+16E0: 38 05         jr   c,$16E7             ; \r
+16E2: AF            xor  a\r
+16E3: 32 24 42      ld   ($4224),a           ; clear HAVE_AGGRESSIVE_ALIENS flag\r
+16E6: C9            ret\r
+\r
+; This piece of code is only called when there are 3 aliens or less in the swarm.\r
+; It makes the aliens extremely aggressive!\r
+16E7: 3E 01         ld   a,$01\r
+16E9: 32 24 42      ld   ($4224),a           ; set HAVE_AGGRESSIVE_ALIENS flag\r
+16EC: C9            ret\r
+\r
+; This piece of code writes 0 to !SOUND  reset background F1 to F3\r
+16ED: 36 00         ld   (hl),$00\r
+16EF: 2C            inc  l\r
+16F0: 10 FB         djnz $16ED\r
+16F2: C3 DE 16      jp   $16DE\r
+\r
+\r
+\r
+;\r
+; Main sound handler \r
+;\r
+;\r
+\r
+HANDLE_SOUND:\r
+16F5: AF            xor  a\r
+16F6: 32 C0 41      ld   ($41C0),a           ; clear SOUND_VOL\r
+16F9: 3D            dec  a\r
+16FA: 32 C1 41      ld   ($41C1),a           ; set PITCH_SOUND_FX_BASE_FREQ value\r
+16FD: CD 47 17      call $1747               ; call HANDLE_GAME_START_MELODY\r
+1700: CD D0 17      call $17D0               ; call HANDLE_ALIEN_ATTACK_SOUND\r
+1703: CD 19 18      call $1819               ; call HANDLE_ALIEN_DEATH_SOUND\r
+1706: CD 5D 17      call $175D               ; call HANDLE_COMPLEX_SOUNDS\r
+1709: CD 4F 18      call $184F               ; call HANDLE_EXTRA_LIFE_SOUND\r
+170C: CD 76 18      call $1876               ; call HANDLE_COIN_INSERT_SOUND\r
+170F: CD 23 17      call $1723               ; call HANDLE_PLAYER_SHOOTING_SOUND\r
+1712: 3A C0 41      ld   a,($41C0)           ; load A with value of SOUND_VOL\r
+1715: 32 06 68      ld   ($6806),a           ; Write to !SOUND Vol of F1\r
+1718: 0F            rrca\r
+1719: 32 07 68      ld   ($6807),a           ; Write to !SOUND Vol of f2\r
+171C: 3A C1 41      ld   a,($41C1)           ; read PITCH_SOUND_FX_BASE_FREQ value\r
+171F: 32 00 78      ld   ($7800),a           ; Write to !Pitch Sound FX base frequency\r
+1722: C9            ret\r
+\r
+\r
+\r
+HANDLE_PLAYER_SHOOTING_SOUND:\r
+1723: 3A CC 41      ld   a,($41CC)           ; read PLAY_PLAYER_SHOOT_SOUND flag \r
+1726: 3D            dec  a                   ; decrement value\r
+1727: C2 33 17      jp   nz,$1733            ; if the value is nonzero now then it wasn't set to 1 before, so don't play the sound. Goto $1733\r
+172A: 32 CC 41      ld   ($41CC),a           ; a is zero, so this sets the shoot flag to false. \r
+172D: 3E 08         ld   a,$08               ; this value here appears to affect the length of the shoot sound. Higher value = longer\r
+172F: 32 CE 41      ld   ($41CE),a           ; set PLAYER_SHOOT_SOUND_COUNTER\r
+1732: C9            ret\r
+\r
+1733: 3A CE 41      ld   a,($41CE)           ; read PLAYER_SHOOT_SOUND_COUNTER \r
+1736: A7            and  a                   ; test if zero\r
+1737: CA 43 17      jp   z,$1743             ; if it is zero, goto $1743, which will turn the player shoot sound off\r
+173A: 3D            dec  a                   ; reduce counter value by 1.\r
+173B: 32 CE 41      ld   ($41CE),a           ; and write updated count back.\r
+173E: 3A 07 40      ld   a,($4007)           ; read IS_GAME_OVER flag\r
+1741: EE 01         xor  $01\r
+1743: 32 05 68      ld   ($6805),a           ; !SOUND shoot on/off\r
+1746: C9            ret\r
+\r
+\r
+;\r
+; Plays the GAME START tune.\r
+;\r
+\r
+HANDLE_GAME_START_MELODY:\r
+1747: 3A D1 41      ld   a,($41D1)           ; read PLAY_GAME_START_MELODY flag\r
+174A: 3D            dec  a                   ; if was set to 1, this dec will set zero flag\r
+174B: C0            ret  nz                  ; return if PLAY_GAME_START_MELODY wasn't set\r
+174C: 32 D1 41      ld   ($41D1),a           ; clear PLAY_GAME_START_MELODY flag\r
+174F: 3C            inc  a\r
+1750: 32 D2 41      ld   ($41D2),a\r
+1753: 32 D6 41      ld   ($41D6),a\r
+1756: 21 68 1E      ld   hl,$1E68\r
+1759: 22 D3 41      ld   ($41D3),hl          ; set COMPLEX_SOUND_POINTER\r
+175C: C9            ret\r
+\r
+\r
+\r
+\r
+182D: 32 CF 41      ld   ($41CF),a\r
+1830: 32 D6 41      ld   ($41D6),a\r
+1833: 21 BD 1E      ld   hl,$1EBD\r
+1836: 22 D3 41      ld   ($41D3),hl\r
+\r
+\r
+;\r
+; TODO: Wondering if I should change this label to HANDLE_MONOPHONIC_SOUNDS... maybe best to KISS. \r
+;\r
+;\r
+\r
+HANDLE_COMPLEX_SOUNDS:\r
+175D: 21 D2 41      ld   hl,$41D2\r
+1760: CD 6C 17      call $176C\r
+1763: 21 CF 41      ld   hl,$41CF\r
+1766: CD 6C 17      call $176C\r
+1769: 21 CD 41      ld   hl,$41CD            ; pointer to address of IS_COMPLEX_SOUND_PLAYING\r
+176C: 7E            ld   a,(hl)              ; read flag\r
+176D: A7            and  a                   ; is flag set?\r
+176E: C8            ret  z                   ; No, a complex sound isn't playing, so return\r
+176F: EB            ex   de,hl               ; OK, now DE = $41CD\r
+1770: 3E 02         ld   a,$02\r
+1772: 32 C0 41      ld   ($41C0),a           ; Set value of SOUND_VOL\r
+1775: 3A D5 41      ld   a,($41D5)\r
+1778: 32 C1 41      ld   ($41C1),a           ; Set PITCH_SOUND_FX_BASE_FREQ\r
+\r
+; wait until counter has hit 0 before getting next musical note or sound effect to play.\r
+177B: 3A D6 41      ld   a,($41D6)           ; read DELAY_BEFORE_NEXT_SOUND\r
+177E: 3D            dec  a                   ; decrement countdown\r
+177F: C2 A2 17      jp   nz,$17A2            ; if count hasn't hit zero, then goto $17A2\r
+\r
+; OK, counter is zero, we get musical note or sound effect to play.\r
+1782: 2A D3 41      ld   hl,($41D3)          ; read COMPLEX_SOUND_POINTER\r
+1785: 7E            ld   a,(hl)              ; read sound to play\r
+1786: FE E0         cp   $E0                 ; is this the end of sound marker?\r
+1788: 28 1C         jr   z,$17A6             ; if so, then we've finished playing our sounds, goto $17A6\r
+178A: 23            inc  hl                  ; bump HL to point to next sound\r
+178B: 22 D3 41      ld   ($41D3),hl          ; update COMPLEX_SOUND_POINTER\r
+178E: 47            ld   b,a\r
+178F: E6 1F         and  $1F\r
+1791: 21 A9 17      ld   hl,$17A9\r
+1794: E7            rst  $20                 ; call routine to fetch value @ HL + A\r
+1795: 32 D5 41      ld   ($41D5),a\r
+1798: 78            ld   a,b\r
+1799: E6 E0         and  $E0\r
+179B: 07            rlca\r
+179C: 07            rlca\r
+179D: 07            rlca\r
+179E: 21 C8 17      ld   hl,$17C8\r
+17A1: E7            rst  $20                 ; call routine to fetch value @ HL + A\r
+17A2: 32 D6 41      ld   ($41D6),a           ; set DELAY_BEFORE_NEXT_SOUND\r
+17A5: C9            ret\r
+\r
+; DE = $41CD\r
+17A6: AF            xor  a\r
+17A7: 12            ld   (de),a              ; Indicate free to play more sounds\r
+17A8: C9            ret\r
+\r
+;\r
+;\r
+;\r
+;\r
+;\r
+\r
+17A9: FF            rst  $38\r
+17AA: 00            nop\r
+17AB: 40            ld   b,b\r
+17AC: 55            ld   d,l\r
+17AD: 5F            ld   e,a\r
+17AE: 68            ld   l,b\r
+17AF: 70            ld   (hl),b\r
+17B0: 80            add  a,b\r
+17B1: 8E            adc  a,(hl)\r
+17B2: 9A            sbc  a,d\r
+17B3: A0            and  b\r
+17B4: AA            xor  d\r
+17B5: B4            or   h\r
+17B6: B8            cp   b\r
+17B7: C0            ret  nz\r
+17B8: C7            rst  $00\r
+17B9: CD D0 D5      call $D5D0\r
+17BC: DA DC E0      jp   c,$E0DC\r
+17BF: 1C            inc  e\r
+17C0: 35            dec  (hl)\r
+17C1: 87            add  a,a\r
+17C2: A5            and  l\r
+17C3: C4 D3 CA      call nz,$CAD3\r
+17C6: E3            ex   (sp),hl\r
+17C7: E6 \r
+\r
+\r
+17C8: 01 02 04 08 10 20 40 00            \r
+\r
+\r
+;\r
+; This routine is responsible for making the "Wheeew" noise as the alien attackers fly down the screen.\r
+; No, I'm not going to call this routine "HANDLE_WHEEW_SOUND" although I was tempted ;)\r
+;\r
+\r
+HANDLE_ALIEN_ATTACK_SOUND:\r
+17D0: 3A 06 40      ld   a,($4006)           ; read IS_GAME_IN_PLAY\r
+17D3: 0F            rrca                     ; move bit 0 into carry. If game is in play, carry is set\r
+17D4: D0            ret  nc                  ; if carry is not set, game's not in play, return\r
+17D5: 21 C2 41      ld   hl,$41C2\r
+17D8: 7E            ld   a,(hl)\r
+17D9: 3D            dec  a\r
+17DA: C2 E5 17      jp   nz,$17E5\r
+17DD: 77            ld   (hl),a\r
+17DE: 21 02 A0      ld   hl,$A002\r
+17E1: 22 C3 41      ld   ($41C3),hl\r
+17E4: C9            ret\r
+\r
+17E5: 3A 26 42      ld   a,($4226)           ; read HAVE_NO_INFLIGHT_ALIENS\r
+17E8: 0F            rrca                     ; move flag into carry\r
+17E9: D8            ret  c                   ; return if there are no aliens in flight\r
+17EA: 23            inc  hl                  ; bump HL to point to UNKNOWN_SOUND_41C3\r
+17EB: 3A 5F 42      ld   a,($425F)           ; read TIMING_VARIABLE\r
+17EE: 0F            rrca                     ; move bit 0 into carry\r
+17EF: 38 10         jr   c,$1801             \r
+17F1: 3A C4 41      ld   a,($41C4)\r
+17F4: FE 60         cp   $60\r
+17F6: 30 01         jr   nc,$17F9\r
+17F8: 34            inc  (hl)\r
+17F9: A7            and  a\r
+17FA: CA 01 18      jp   z,$1801\r
+17FD: 3D            dec  a\r
+17FE: 32 C4 41      ld   ($41C4),a\r
+1801: 7E            ld   a,(hl)\r
+1802: E6 03         and  $03\r
+1804: C2 0C 18      jp   nz,$180C\r
+1807: 3E 60         ld   a,$60\r
+1809: C3 15 18      jp   $1815\r
+180C: 0F            rrca\r
+180D: 3A C4 41      ld   a,($41C4)\r
+1810: 30 03         jr   nc,$1815\r
+1812: C6 60         add  a,$60\r
+1814: 1F            rra\r
+1815: 32 C1 41      ld   ($41C1),a           ; Set PITCH_SOUND_FX_BASE_FREQ\r
+1818: C9            ret\r
+\r
+\r
+;\r
+; Plays the sound of an alien or a flagship when hit.\r
+;\r
+; No death sound will be played if either of the IS_GAME_IN_PLAY or IS_COMPLEX_SOUND_PLAYING flags are set.\r
+;\r
+\r
+HANDLE_ALIEN_DEATH_SOUND:\r
+1819: 3A 06 40      ld   a,($4006)           ; read IS_GAME_IN_PLAY\r
+181C: 0F            rrca                     ; move flag into carry                     \r
+181D: D0            ret  nc                  ; if game is not in play, we won't play sound.                \r
+\r
+181E: 3A DF 41      ld   a,($41DF)\r
+1821: FE 06         cp   $06                 ; Do we want to play the ALIEN DEATH sound?       \r
+1823: C2 3A 18      jp   nz,$183A            ; no, goto $183A\r
+\r
+1826: 3A CD 41      ld   a,($41CD)           ; read IS_COMPLEX_SOUND_PLAYING flag    \r
+1829: 0F            rrca                     ; move                                                          \r
+182A: D8            ret  c\r
+\r
+182B: 3E 01         ld   a,$01\r
+182D: 32 CF 41      ld   ($41CF),a\r
+1830: 32 D6 41      ld   ($41D6),a\r
+1833: 21 BD 1E      ld   hl,$1EBD\r
+1836: 22 D3 41      ld   ($41D3),hl\r
+1839: C9            ret\r
+\r
+183A: FE 16         cp   $16                 ; Do we want to play the FLAGSHIP DEATH sound?                 \r
+183C: C0            ret  nz                  ; if not, return\r
+183D: AF            xor  a\r
+183E: 32 CF 41      ld   ($41CF),a\r
+1841: 3C            inc  a\r
+1842: 32 CD 41      ld   ($41CD),a\r
+1845: 32 D6 41      ld   ($41D6),a\r
+1848: 21 DF 1E      ld   hl,$1EDF\r
+184B: 22 D3 41      ld   ($41D3),hl\r
+184E: C9            ret\r
+\r
+;\r
+;\r
+;\r
+;\r
+;\r
+\r
+HANDLE_EXTRA_LIFE_SOUND:\r
+184F: 2A C7 41      ld   hl,($41C7)\r
+1852: CB 45         bit  0,l                 ; read PLAY_EXTRA_LIFE_SOUND flag.\r
+1854: CA 5E 18      jp   z,$185E             ; if flag is not set, goto $185E.\r
+1857: 21 00 80      ld   hl,$8000            ; store 0 in PLAY_EXTRA_LIFE_SOUND and $80 in EXTRA_LIFE_SOUND_COUNTER\r
+185A: 22 C7 41      ld   ($41C7),hl      \r
+185D: C9            ret\r
+\r
+; play the extra life sound\r
+185E: 7C            ld   a,h                 ; read EXTRA_LIFE_SOUND_COUNTER\r
+185F: A7            and  a                   ; test if value is 0.\r
+1860: C8            ret  z                   ; return if value is indeed 0.\r
+1861: 3D            dec  a                   ; decrement counter\r
+1862: 32 C8 41      ld   ($41C8),a           ; update EXTRA_LIFE_SOUND_COUNTER\r
+1865: E6 04         and  $04             \r
+1867: CA 6C 18      jp   z,$186C\r
+186A: 3E 81         ld   a,$81\r
+186C: 3D            dec  a\r
+186D: 32 C1 41      ld   ($41C1),a           ; Set PITCH_SOUND_FX_BASE_FREQ\r
+1870: 3E 01         ld   a,$01\r
+1872: 32 C0 41      ld   ($41C0),a           ; set SOUND_VOL\r
+1875: C9            ret\r
+\r
+\r
+\r
+\r
+HANDLE_COIN_INSERT_SOUND:\r
+1876: 21 C9 41      ld   hl,$41C9            ; address of PLAY_PLAYER_CREDIT_SOUND flag\r
+1879: 7E            ld   a,(hl)              ; read flag\r
+187A: 3D            dec  a                   \r
+187B: C2 86 18      jp   nz,$1886            ; if PLAY_PLAYER_CREDIT_SOUND was not set to 1, goto $1886\r
+187E: 77            ld   (hl),a              ; clear flag. \r
+187F: 21 20 00      ld   hl,$0020            ; duration to play sound\r
+1882: 22 CA 41      ld   ($41CA),hl\r
+1885: C9            ret\r
+\r
+; play the credit sound\r
+1886: 23            inc  hl                  ; bump HL to $41CA, which is address of PLAYER_CREDIT_SOUND_COUNTER               \r
+1887: 7E            ld   a,(hl)              ; read value of count\r
+1888: A7            and  a                   ; test if its zero\r
+1889: C8            ret  z                   ; if its zero, return \r
+188A: 35            dec  (hl)                ; reduce counter\r
+188B: 23            inc  hl                  ; bump HL to $41CB\r
+188C: 7E            ld   a,(hl)\r
+188D: C6 04         add  a,$04             \r
+188F: 77            ld   (hl),a\r
+1890: 32 C1 41      ld   ($41C1),a           ; Set PITCH_SOUND_FX_BASE_FREQ\r
+1893: AF            xor  a\r
+1894: 32 C0 41      ld   ($41C0),a           ; set SOUND_VOL\r
+1897: C9            ret\r
+\r
+;\r
+;\r
+; This piece of code is used to make the sound of the swarm "angrier" as the level goes on.\r
+; The longer the level takes to complete, the faster and angrier the swarm is.\r
+; \r
+;\r
+\r
+HANDLE_SWARM_SOUND:\r
+1898: 3A D0 41      ld   a,($41D0)           ; read RESET_SWARM_SOUND_TEMPO flag\r
+189B: A7            and  a                   ; test if zero\r
+189C: 28 08         jr   z,$18A6             ; if zero, goto $18A6\r
+189E: AF            xor  a                   ; clear A\r
+189F: 32 D0 41      ld   ($41D0),a           ; reset RESET_SWARM_SOUND_TEMPO flag\r
+18A2: 3E 0F         ld   a,$0F               ; Maximum (slowest) Tempo setting\r
+18A4: 18 0C         jr   $18B2\r
+\r
+18A6: 3A 5F 42      ld   a,($425F)           ; read TIMING_VARIABLE\r
+18A9: C6 01         add  a,$01               ; add 1 to it\r
+18AB: D0            ret  nc                  ; return if no carry. This means the following code will only fire when A == #$FF (255 decimal)\r
+18AC: 3A 1F 42      ld   a,($421F)           ; read LFO_FREQ_BITS\r
+18AF: A7            and  a                   ; test if zero\r
+18B0: C8            ret  z                   ; return if zero\r
+18B1: 3D            dec  a                   ; otherwise reduce by 1\r
+18B2: 32 1F 42      ld   ($421F),a           ; and update LFO_FREQ_BITS \r
+18B5: 06 04         ld   b,$04               ; We're writing to 4 ports\r
+18B7: 21 04 60      ld   hl,$6004            ; !DRIVER Background lfo freq bit0\r
+18BA: 77            ld   (hl),a              ; write to port. Only bit 0 of register A matters\r
+18BB: 23            inc  hl                  ; bump to next port\r
+18BC: 0F            rrca                     ; shift all bits in A one place to right\r
+18BD: 10 FB         djnz $18BA              \r
+18BF: C9            ret\r
+\r
+\r
+;\r
+; This routine is responsible for scrolling on the SCORE ADVANCE TABLE items.  \r
+; It is not responsible for scrolling the actual alien swarm.\r
+; \r
+\r
+HANDLE_TEXT_SCROLL:\r
+18C0: 3A B0 40      ld   a,($40B0)           ; read IS_COLUMN_SCROLLING flag\r
+18C3: 0F            rrca                     ; move flag value into carry\r
+18C4: D0            ret  nc                  ; return if flag was not set\r
+18C5: 2A B1 40      ld   hl,($40B1)          ; Load HL with pointer to scroll attribute data in OBJRAM_BACK_BUF.  \r
+18C8: 7E            ld   a,(hl)              ; read scroll value\r
+\r
+; We only want to scroll on a new character every 8th pixel. The code below effectively checks if (scroll offset MODULO 8 == 0)\r
+18C9: E6 07         and  $07                 ; mask in bits 0..2. We now have a value from 0..7 in A.\r
+18CB: 20 1B         jr   nz,$18E8            ; if A is not zero, we don't scroll on a new character yet, goto $18E8.  \r
+\r
+18CD: EB            ex   de,hl               ; swap HL and DE. This preserves value of HL elsewhere without requiring a PUSH or a store\r
+18CE: 2A B3 40      ld   hl,($40B3)          ; HL now is pointer to a character to be scrolled onto screen      \r
+18D1: 7E            ld   a,(hl)              ; read character to scroll on\r
+18D2: FE 3F         cp   $3F                 ; is this a terminating byte marking the end of the characters to scroll on?\r
+18D4: 28 11         jr   z,$18E7             ; if so, goto $18E7\r
+\r
+18D6: 23            inc  hl                  ; bump pointer to next character \r
+18D7: 22 B3 40      ld   ($40B3),hl          ; and update COLUMN_SCROLL_NEXT_CHAR_PTR pointer \r
+18DA: D6 30         sub  $30\r
+18DC: 2A B5 40      ld   hl,($40B5)          ; get character RAM address to plot character at \r
+18DF: 77            ld   (hl),a              ; store character into character RAM\r
+\r
+18E0: 01 E0 FF      ld   bc,$FFE0            ; load BC with -32 decimal \r
+18E3: 09            add  hl,bc               ; Add offset to HL. HL now points to character in row above, same column. \r
+18E4: 22 B5 40      ld   ($40B5),hl          ; And update pointer \r
+\r
+18E7: EB            ex   de,hl               ; now HL points to scroll attribute data in OBJRAM_BACK_BUF  \r
+18E8: 35            dec  (hl)                ; update scroll offset value\r
+18E9: C0            ret  nz                  ; exit if scroll offset value is not zero\r
+\r
+18EA: AF            xor  a                   ; We've scrolled as much as we need to. Stop the scroll.\r
+18EB: 32 B0 40      ld   ($40B0),a           ; clear IS_COLUMN_SCROLLING flag\r
+18EE: C9            ret\r
+\r
+\r
+\r
+CHECK_IF_COIN_INSERTED:\r
+18EF: 3A 00 40      ld   a,($4000)           ; read stored state of dip switch 1 & 2\r
+18F2: FE 03         cp   $03                 ; are dip switches set to FREE PLAY?\r
+18F4: 28 21         jr   z,$1917             ; yes, free play enabled, so goto $1917: we don't need to check if coins are inserted\r
+18F6: 21 10 40      ld   hl,$4010            ; pointer to PORT_STATE_6000 value\r
+18F9: 7E            ld   a,(hl)              ; read value\r
+18FA: 2C            inc  l \r
+18FB: 2C            inc  l\r
+18FC: 2C            inc  l                   ; bump HL to $4013, which is PREV_PORT_STATE_6000 value\r
+18FD: B6            or   (hl)                ; combine bits set for current state of port 6000 with bits set from previous state\r
+18FE: 2C            inc  l\r
+18FF: 2C            inc  l                   ; bump HL to $4015, which is PREV_PREV_PORT_STATE_6000 value\r
+1900: 2F            cpl                      ; flip bits\r
+1901: A6            and  (hl)\r
+1902: 2C            inc  l                   ; bump HL to $4016, which is PREV_PREV_PREV_PORT_STATE_6000 value.\r
+1903: A6            and  (hl)\r
+1904: CB 7F         bit  7,a                 ; read SERVICE state\r
+1906: 20 16         jr   nz,$191E            ; if SERVICE is pressed, goto $191E\r
+1908: E6 03         and  $03                 ; mask in COIN 1 & COIN 2 bits, discard rest\r
+190A: C8            ret  z                   ; if neither bits are set, meaning no coins inserted, return\r
+190B: 21 04 40      ld   hl,$4004            ; address of UNPROCESSED_COINS counter\r
+190E: 34            inc  (hl)                ; increment UNPROCESSED_COINS counter\r
+190F: CB 47         bit  0,a                 ; test COIN 1 state\r
+1911: C8            ret  z                   ; return if no coin inserted\r
+1912: E6 02         and  $02                 ; test COIN 2 state\r
+1914: C8            ret  z                   ; return if no coin inserted\r
+1915: 34            inc  (hl)                ; increment UNPROCESSED_COINS counter\r
+1916: C9            ret\r
+\r
+; Only comes here if we have FREE PLAY enabled in the dip switches\r
+1917: 21 00 09      ld   hl,$0900\r
+191A: 22 01 40      ld   ($4001),hl          ; set COIN_COUNT to 1 and NUM_CREDITS to 9.\r
+191D: C9            ret\r
+\r
+;\r
+; This is called when SERVICE is pressed. \r
+; \r
+\r
+191E: 21 02 40      ld   hl,$4002            ; address of NUM_CREDITS\r
+1921: 7E            ld   a,(hl)              ; read number of credits\r
+1922: FE 63         cp   $63                 ; compare to 99 (decimal)\r
+1924: D0            ret  nc                  ; if A < 99 decimal, return\r
+1925: 34            inc  (hl)                ; otherwise, increment number of credits\r
+1926: 3E 01         ld   a,$01\r
+1928: 32 C9 41      ld   ($41C9),a           ; Play sound of credit being added\r
+192B: 11 01 07      ld   de,$0701            ; command: BOTTOM_OF_SCREEN_INFO_COMMAND, parameter: 1 (invokes DISPLAY_AVAILABLE_CREDIT)\r
+192E: C3 F2 08      jp   $08F2               ; jump to QUEUE_COMMAND\r
+\r
+\r
+\r
+HANDLE_UNPROCESSED_COINS:\r
+1931: 21 03 40      ld   hl,$4003            ; COIN_CONTROL\r
+1934: 7E            ld   a,(hl)              ; Read value\r
+1935: A7            and  a                   ; test if zero. Zero means we process unprocessed coins\r
+1936: 20 3C         jr   nz,$1974            ; if non-zero goto $1974 - don't bother processing coins.\r
+1938: 2C            inc  l                   ; point HL to UNPROCESSED_COINS \r
+1939: B6            or   (hl)                ; Do we have any coins left unprocessed?\r
+193A: C8            ret  z                   ; Return if no coins left to be processed.\r
+193B: 35            dec  (hl)                ; Otherwise, decrement UNPROCESSED_COINS\r
+193C: 2D            dec  l                   ; point HL to $4003, COIN_CONTROL\r
+193D: 36 0F         ld   (hl),$0F            ; reset COIN_CONTROL\r
+193F: 3A 00 40      ld   a,($4000)           ; read stored state of dip switches 1 & 2\r
+1942: FE 03         cp   $03                 ; are both switches set, meaning FREE PLAY?\r
+1944: C8            ret  z                   ; yes, so return - we don't bother about coins or credits in this case.\r
+1945: 3D            dec  a                   ; have we set dip switches to TWO COINS = 1 PLAY? If so, A will be 0\r
+1946: 28 1C         jr   z,$1964             ; yes, goto $1964 to handle TWO COINS 1 PLAY\r
+\r
+; Nice piece of code @$194C to add two credits if required with only one CALL. Spot it?   \r
+1948: 21 02 40      ld   hl,$4002            ; point HL to NUM_CREDITS\r
+194B: 3D            dec  a                   ; have we set dip switches to ONE COIN = 2 PLAYS? If so, A will be 0  \r
+194C: CC 4F 19      call z,$194F             ; call code immediately after to add 1 credit, then return from CALL to add another credit!\r
+194F: 7E            ld   a,(hl)              ; read number of credits              \r
+1950: FE 63         cp   $63                 ; have we reached 99 credits (decimal)?\r
+1952: C8            ret  z                   ; yes, so exit, no more credits allowed\r
+1953: 30 0C         jr   nc,$1961            ; if we have more than 99 credits (decimal) then goto $1961 - clamp credits to 99.\r
+1955: 34            inc  (hl)                ; increment credit count\r
+1956: 3E 01         ld   a,$01\r
+1958: 32 C9 41      ld   ($41C9),a           ; Set PLAY_PLAYER_CREDIT_SOUND flag to 1. I think you can guess what this does ;)\r
+195B: 11 01 07      ld   de,$0701            ; command: BOTTOM_OF_SCREEN_INFO_COMMAND, parameter: 1 (invokes DISPLAY_AVAILABLE_CREDIT)\r
+195E: C3 F2 08      jp   $08F2               ; jump to QUEUE_COMMAND\r
+\r
+1961: 36 63         ld   (hl),$63            ; set NUM_CREDITS to 99 decimal.\r
+1963: C9            ret\r
+\r
+; Called when dip switches are set to TWO COINS ONE PLAY\r
+1964: 21 01 40      ld   hl,$4001            ; load HL with address of COIN_COUNT\r
+1967: CB 46         bit  0,(hl)              ; Have we only inserted one coin? \r
+1969: 28 06         jr   z,$1971             ; if bit test fails, we've not inserted any coins yet, goto $1971\r
+196B: 36 00         ld   (hl),$00            ; reset COIN_COUNT to say we've acknowledged coins and awarded extra credit\r
+196D: 2C            inc  l                   ; bump HL to point to NUM_CREDITS      \r
+196E: C3 4F 19      jp   $194F               ; call routine to add a credit\r
+\r
+; Called to reset COIN_COUNT to 1. \r
+1971: 36 01         ld   (hl),$01\r
+1973: C9            ret\r
+\r
+\r
+; HL = $4003 (COIN_CONTROL)\r
+1974: 0F            rrca\r
+1975: 0F            rrca\r
+1976: 0F            rrca\r
+1977: 32 03 60      ld   ($6003),a        ; write to DRIVER|COIN CONTROL\r
+197A: 35            dec  (hl)\r
+197B: C9            ret\r
+\r
+\r
+;\r
+; Coin lockout is where an arcade cabinet can physically stop more coins from being inserted.\r
+; This code here prevents the user inserting coins to gain more than 9 credits. \r
+;\r
+\r
+HANDLE_COIN_LOCKOUT:\r
+197C: 3A 02 40      ld   a,($4002)           ; read NUM_CREDITS\r
+197F: FE 09         cp   $09                 ; compare to 9\r
+1981: 30 06         jr   nc,$1989            ; if we have >= 9 credits, goto $1989\r
+1983: 3E 01         ld   a,$01\r
+1985: 32 02 60      ld   ($6002),a           ; write to !DRIVER | COIN LOCKOUT\r
+1988: C9            ret\r
+1989: AF            xor  a\r
+198A: 32 02 60      ld   ($6002),a           ; write to !DRIVER | COIN LOCKOUT\r
+198D: C9            ret\r
+\r
+\r
+\r
+\r
+HANDLE_SIMULATE_PLAYER_IN_ATTRACT_MODE:\r
+198E: 3A 5F 42      ld   a,($425F)           ; read TIMING_VARIABLE\r
+1991: C6 09         add  a,$09\r
+1993: E6 1F         and  $1F\r
+1995: C0            ret  nz\r
+1996: 3A 07 40      ld   a,($4007)           ; read IS_GAME_OVER flag\r
+1999: 0F            rrca                     ; move flag into carry\r
+199A: D0            ret  nc                  ; return if its not GAME OVER\r
+199B: 3A 00 42      ld   a,($4200)           ; read HAS_PLAYER_SPAWNED\r
+199E: 0F            rrca                     ; move flag into carry\r
+199F: D0            ret  nc                  ; return if player has not spawned yet\r
+\r
+19A0: DD 21 D0 42   ld   ix,$42D0            ; pointer to INFLIGHT_ALIENS_START + sizeof(INFLIGHT_ALIEN)\r
+19A4: 06 00         ld   b,$00\r
+19A6: D9            exx\r
+19A7: 11 20 00      ld   de,$0020            ; sizeof(INFLIGHT_ALIEN)\r
+19AA: 06 07         ld   b,$07               ; 7 aliens to process\r
+19AC: D9            exx\r
+19AD: DD 66 03      ld   h,(ix+$03)          ; H = INFLIGHT_ALIEN.X\r
+19B0: DD 6E 04      ld   l,(ix+$04)          ; L = INFLIGHT_ALIEN.Y \r
+19B3: DD 4E 1A      ld   c,(ix+$1a)\r
+19B6: CD 12 1A      call $1A12\r
+19B9: D9            exx\r
+19BA: DD 19         add  ix,de               ; bump IX to next INFLIGHT_ALIEN \r
+19BC: 10 EE         djnz $19AC\r
+\r
+\r
+19BE: DD 21 60 42   ld   ix,$4260            ; load IX with address of ENEMY_BULLETS_START\r
+19C2: 11 05 00      ld   de,$0005            ; sizeof(ENEMY_BULLET)\r
+19C5: 06 07         ld   b,$07               ; number of enemy bullets\r
+19C7: D9            exx\r
+19C8: DD 66 01      ld   h,(ix+$01)          ; H = ENEMY_BULLET.X\r
+19CB: DD 6E 03      ld   l,(ix+$03)          ; L = ENEMY_BULLET.YH\r
+19CE: DD 4E 04      ld   c,(ix+$04)          ; C = ENEMY_BULLET.YDelta\r
+19D1: CD 12 1A      call $1A12\r
+19D4: D9            exx\r
+19D5: DD 19         add  ix,de               ; bump IX to next ENEMY_BULLET\r
+19D7: 10 EE         djnz $19C7\r
+\r
+19D9: D9            exx\r
+19DA: 3A 02 42      ld   a,($4202)           ; read PLAYER_Y\r
+19DD: 4F            ld   c,a\r
+19DE: 3A 0E 42      ld   a,($420E)           ; read SWARM_SCROLL_VALUE\r
+19E1: C6 80         add  a,$80\r
+19E3: 91            sub  c                   ; subtract player Y \r
+19E4: CB 2F         sra  a                   ; divide A.... \r
+19E6: CB 2F         sra  a                   ; \r
+19E8: CB 2F         sra  a                   ; \r
+19EA: CB 2F         sra  a                   ; \r
+19EC: CB 2F         sra  a                   ; .. by 32, preserving sign bit\r
+19EE: 80            add  a,b\r
+19EF: CB 2F         sra  a\r
+19F1: 4F            ld   c,a\r
+19F2: CD 3C 00      call $003C               ; call GENERATE_RANDOM_NUMBER. Now A = pseudorandom number\r
+19F5: 41            ld   b,c\r
+19F6: 87            add  a,a\r
+19F7: 9F            sbc  a,a\r
+19F8: 20 01         jr   nz,$19FB\r
+19FA: 3C            inc  a\r
+19FB: 80            add  a,b\r
+19FC: C6 01         add  a,$01\r
+19FE: FA 0E 1A      jp   m,$1A0E\r
+1A01: FE 02         cp   $02\r
+1A03: 30 05         jr   nc,$1A0A            \r
+1A05: AF            xor  a\r
+1A06: 32 3F 42      ld   ($423F),a           ; set ATTRACT_MODE_FAKE_CONTROLLER value. Ship will move under AI control\r
+1A09: C9            ret\r
+\r
+1A0A: 3E 04         ld   a,$04               ; simulates player moving LEFT (bit maps to !SW0   p1 left)\r
+1A0C: 18 F8         jr   $1A06               ; go set ATTRACT_MODE_FAKE_CONTROLLER \r
+\r
+1A0E: 3E 08         ld   a,$08               ; simulates player moving RIGHT (bit maps to !SW0    p1 right)\r
+1A10: 18 F4         jr   $1A06               ; go set ATTRACT_MODE_FAKE_CONTROLLER \r
+\r
+\r
+\r
+\r
+;\r
+; This is used in the attract mode to help the simulated player dodge bullets.\r
+;\r
+; Expects:\r
+; IX = pointer to INFLIGHT_ALIEN or ENEMY_BULLET\r
+; H = X coordinate of alien or bullet\r
+; L = Y coordinate of alien or bullet\r
+; C = YDelta of alien/bullet\r
+;\r
+; Returns:\r
+;\r
+\r
+1A12: DD CB 00 46   bit  0,(ix+$00)          ; test INFLIGHT_ALIEN.IsActive flag\r
+1A16: C8            ret  z                   ; exit if alien is not active\r
+1A17: 7C            ld   a,h                 ; get X coordinate of alien/bullet\r
+1A18: D6 80         sub  $80                 ; subtract 128 decimal\r
+1A1A: D8            ret  c                   ; if there's a carry, the alien's not far enough down the screen to be a threat, return                       \r
+\r
+1A1B: 1E 00         ld   e,$00\r
+1A1D: D6 34         sub  $34                 ; subtract $34 from X coordinate\r
+1A1F: 38 04         jr   c,$1A25\r
+1A21: 1C            inc  e\r
+1A22: D6 34         sub  $34\r
+1A24: D0            ret  nc\r
+\r
+1A25: 3A 02 42      ld   a,($4202)           ; read PLAYER_Y \r
+1A28: 95            sub  l                   ; subtract Y coordinate of alien/bullet\r
+1A29: D6 40         sub  $40\r
+1A2B: FE 80         cp   $80\r
+1A2D: D0            ret  nc                  ; return if A >= $80\r
+\r
+1A2E: E6 60         and  $60\r
+1A30: 6F            ld   l,a\r
+                            \r
+1A31: 79            ld   a,c                 ; A = YDelta\r
+1A32: E6 80         and  $80\r
+1A34: B5            or   l\r
+1A35: 0F            rrca\r
+1A36: 0F            rrca\r
+1A37: 0F            rrca\r
+1A38: 0F            rrca\r
+1A39: B3            or   e\r
+1A3A: 5F            ld   e,a\r
+1A3B: 16 00         ld   d,$00\r
+1A3D: 21 45 1A      ld   hl,$1A45\r
+1A40: 19            add  hl,de\r
+1A41: 7E            ld   a,(hl)\r
+1A42: 80            add  a,b\r
+1A43: 47            ld   b,a\r
+1A44: C9            ret\r
+\r
+\r
+\r
+1A45: 02 03 FE 02 FF FE 00 FF 00 01 01 02 02 FE FE 03            \r
+\r
+\r
+\r
+;\r
+; Called from $0004.\r
+; \r
+;\r
+;\r
+\r
+INITIALISE_SYSTEM:\r
+\r
+; Clear screen \r
+1A55: 21 00 50      ld   hl,$5000            ; address of character RAM\r
+1A58: 06 04         ld   b,$04               ; the number of bytes we need to write to clear the screen, divided by 256\r
+1A5A: 3E 10         ld   a,$10               ; ordinal of empty character \r
+1A5C: 77            ld   (hl),a              ; write character to screen\r
+1A5D: 2C            inc  l                   ; increment low byte of character RAM address to write to\r
+1A5E: C2 5C 1A      jp   nz,$1A5C            ; do while low byte is !=0 \r
+1A61: 24            inc  h                   ; increment high byte of screen address to write to\r
+1A62: 3A 00 78      ld   a,($7800)           ; Kick the watchdog\r
+1A65: 10 F3         djnz $1A5A               ; decrement b and if !=0 goto $1A5A\r
+\r
+; clear attributes and reset scroll values\r
+1A67: 21 00 58      ld   hl,$5800            ; start of screen attribute RAM\r
+1A6A: AF            xor  a                   ; clear a \r
+1A6B: 77            ld   (hl),a              ; write 0 to screen attribute RAM. Will both set colour to 0 and reset column scroll.\r
+1A6C: 2C            inc  l                   ; increment low byte of attribute RAM address to write to                    \r
+1A6D: C2 6B 1A      jp   nz,$1A6B            ; if low byte of pointer has not wrapped to 0, goto $1A6B\r
+\r
+; Disable lamps and reset coin control\r
+1A70: AF            xor  a                   ; clear a\r
+1A71: 21 00 60      ld   hl,$6000            ; pointer to !DRIVER lamp 1\r
+1A74: 06 04         ld   b,$04               ; 4 ports to write to, from $6000 - $6003\r
+1A76: 77            ld   (hl),a              ; write 0 to port. \r
+1A77: 23            inc  hl                  ; bump HL to point to next port\r
+1A78: 10 FC         djnz $1A76               ; do until all ports written to\r
+\r
+; Set !DRIVER Background lfo freq bits 0-3 to 1\r
+; HL currently points to $6004, which is !DRIVER Background lfo freq bit0\r
+1A7A: 3C            inc  a                   ; set A to 1\r
+1A7B: 06 04         ld   b,$04               ; 4 ports to write to, from $6004 - $6007\r
+1A7D: 77            ld   (hl),a              ; write to port\r
+1A7E: 23            inc  hl                  ; bump HL to point to next port\r
+1A7F: 10 FC         djnz $1A7D               ; do until all ports written to\r
+\r
+; writes zero to all !SOUND ports\r
+1A81: AF            xor  a                   ; clear a\r
+1A82: 06 08         ld   b,$08               ; 8 ports to write to, from $6800 - $6807\r
+1A84: 21 00 68      ld   hl,$6800            ; pointer to !SOUND reset background F1   \r
+1A87: 77            ld   (hl),a              ; write to port\r
+1A88: 23            inc  hl                  ; bump HL to point to next port\r
+1A89: 10 FC         djnz $1A87               ; do until all ports written to\r
+\r
+; writes zero to 9Nregen NMIon, 9Nregen stars on, 9Nregen hflip, 9Nregen vflip\r
+1A8B: 06 08         ld   b,$08\r
+1A8D: 21 01 70      ld   hl,$7001            ; pointer to 9Nregen NMIon\r
+1A90: 77            ld   (hl),a              ; write to port\r
+1A91: 23            inc  hl                  ; bump HL to point to next port\r
+1A92: 10 FC         djnz $1A90\r
+\r
+1A94: 3D            dec  a                   ; set a to #$FF (255 decimal)\r
+1A95: 32 00 78      ld   ($7800),a           ; write to !pitch  Sound Fx base frequency\r
+\r
+; Write a batch of bytes with calculated values to an area of working RAM ($4000 - $43FF).\r
+; When the batch has been written, read each byte back and compare to calculated values.\r
+; if the bytes written and read back don't match, you have a RAM error and "BAD RAM 1" will be displayed.\r
+\r
+; do the write phase\r
+1A98: 0E 20         ld   c,$20               ; number of times to repeat RAM tests\r
+1A9A: 21 00 40      ld   hl,$4000            ; pointer to start of RAM\r
+1A9D: 06 04         ld   b,$04               ; total number of bytes to write, divided by 256\r
+1A9F: 79            ld   a,c                 ; initial seed value \r
+1AA0: C6 2F         add  a,$2F               ; Add #$2F (47 decimal) to seed value\r
+\r
+1AA2: 77            ld   (hl),a              ; write byte to working RAM\r
+1AA3: 2C            inc  l                   ; increment low byte of RAM pointer\r
+1AA4: C2 A0 1A      jp   nz,$1AA0            ; if low byte of pointer has not wrapped to 0, goto $1AA0\r
+1AA7: 3C            inc  a                   ; increment value of byte to write to RAM\r
+1AA8: 24            inc  h                   ; increment high byte of RAM pointer \r
+1AA9: 10 F5         djnz $1AA0               ; do until B==0\r
+\r
+; now do the read (verify) phase\r
+1AAB: 21 00 40      ld   hl,$4000            ; pointer to start of RAM\r
+1AAE: 06 04         ld   b,$04               ; total number of bytes to verify, divided by 256\r
+1AB0: 79            ld   a,c                 ; initial seed value \r
+1AB1: C6 2F         add  a,$2F               ; add #$2F (47 decimal) to A, as write phase did\r
+1AB3: BE            cp   (hl)                ; compare byte to test RAM integrity\r
+1AB4: 20 45         jr   nz,$1AFB            ; if they don't match, there's a RAM error, goto $1AFB\r
+1AB6: 2C            inc  l                   ; increment low byte of RAM pointer\r
+1AB7: C2 B1 1A      jp   nz,$1AB1            ; if low byte of pointer has not wrapped to 0, goto $1AA0\r
+1ABA: 3C            inc  a                   ; increment value of byte expected to be read from RAM\r
+1ABB: 24            inc  h                   ; increment high byte of RAM pointer \r
+1ABC: 10 F3         djnz $1AB1               ; do until B==0\r
+\r
+1ABE: 3A 00 78      ld   a,($7800)           ; Kick the watchdog\r
+1AC1: 0D            dec  c                   ; decrement counter of how many times to repeat RAM write and test phases\r
+1AC2: C2 9A 1A      jp   nz,$1A9A            ; if the counter !=0, then goto $1A9A\r
+\r
+; Write a batch of bytes with calculated values to character RAM ($5000 - $53FF) \r
+; When the batch has been written, read each byte back and compare to calculated values. \r
+; If the bytes written and read back don't match, you have a character RAM error and "BAD RAM 2" will be displayed.\r
+; \r
+\r
+; do the character RAM write phase\r
+1AC5: 31 00 44      ld   sp,$4400\r
+1AC8: 0E 20         ld   c,$20               ; number of times to repeat RAM tests\r
+1ACA: 21 00 50      ld   hl,$5000            ; pointer to character RAM\r
+1ACD: 06 04         ld   b,$04               ; total number of bytes to write, divided by 256\r
+1ACF: 79            ld   a,c                 ; initial seed value \r
+1AD0: C6 2F         add  a,$2F               ; Add #$2F (47 decimal) to seed value\r
+1AD2: 77            ld   (hl),a              ; write byte to character RAM\r
+1AD3: 2C            inc  l                   ; increment low byte of RAM pointer\r
+1AD4: C2 D0 1A      jp   nz,$1AD0            ; if low byte of pointer has not wrapped to 0, goto $1AD0\r
+1AD7: 3C            inc  a                   ; increment value of byte to write to character RAM\r
+1AD8: 24            inc  h                   ; increment high byte of RAM pointer \r
+1AD9: 10 F5         djnz $1AD0               ; do until B==0\r
+1ADB: 3A 00 78      ld   a,($7800)           ; Kick the watchdog\r
+\r
+; now do the character RAM read (verify) phase\r
+1ADE: 21 00 50      ld   hl,$5000            ; pointer to character RAM\r
+1AE1: 06 04         ld   b,$04               ; total number of bytes to verify, divided by 256\r
+1AE3: 79            ld   a,c                 ; initial seed value \r
+1AE4: C6 2F         add  a,$2F               ; add #$2F (47 decimal) to A, as write phase did \r
+1AE6: BE            cp   (hl)                ; compare byte to test character RAM integrity\r
+1AE7: 20 16         jr   nz,$1AFF            ; if they don't match, there's a character RAM error, goto $1AFB\r
+1AE9: 2C            inc  l                   ; increment low byte of character RAM pointer\r
+1AEA: C2 E4 1A      jp   nz,$1AE4            ; if low byte of pointer has not wrapped to 0, goto $1AE4\r
+1AED: 3C            inc  a                   ; increment value of byte expected to be read from RAM\r
+1AEE: 24            inc  h                   ; increment high byte of RAM pointer \r
+1AEF: 10 F3         djnz $1AE4               ; do until B==0\r
+1AF1: 3A 00 78      ld   a,($7800)           ; Kick the watchdog\r
+1AF4: 0D            dec  c\r
+1AF5: C2 CA 1A      jp   nz,$1ACA\r
+1AF8: C3 70 1B      jp   $1B70\r
+\r
+\r
+; \r
+; Display "BAD RAM 1" on screen.\r
+;\r
+; This message means there's an error in working memory.\r
+;\r
+\r
+DISPLAY_BAD_RAM_1:\r
+1AFB: 3E 01         ld   a,$01               ; Error code for BAD RAM 1\r
+1AFD: 18 05         jr   $1B04               ; jump to DISPLAY_BAD_RAM_MESSAGE\r
+\r
+;\r
+; Display "BAD RAM 2" on screen.\r
+;\r
+; This message means there's an error in the character RAM memory.\r
+; \r
+\r
+BAD_RAM_2:\r
+1AFF: CD 5D 1B      call $1B5D               ; call CLEAR_SCREEN\r
+1B02: 3E 02         ld   a,$02               ; Error code for BAD RAM 2\r
+\r
+;\r
+; Displays "BAD RAM [value in register A]" \r
+;\r
+\r
+DISPLAY_BAD_RAM_MESSAGE:\r
+1B04: 32 F3 51      ld   ($51F3),a           ; write number stored in A to screen \r
+1B07: 11 2D 1B      ld   de,$1B2D            ; address of text string "BAD RAM" in reverse\r
+\r
+; Called to display "BAD RAM [n]" or "BAD ROM [n]""\r
+DISPLAY_BAD_RAM_OR_ROM_MESSAGE:\r
+1B0A: 21 33 52      ld   hl,$5233            ; character RAM address to print text at\r
+1B0D: 01 20 00      ld   bc,$0020            ; offset to add to character RAM address after every character drawn\r
+1B10: D9            exx                      ; swap to external registers. We want to use B in the other set for a counter\r
+1B11: 06 07         ld   b,$07               ; there's 7 characters in "BAD RAM" (including the space between words)\r
+1B13: D9            exx                      ; swap back to "normal" registers. \r
+1B14: 1A            ld   a,(de)              ; read character          \r
+1B15: 77            ld   (hl),a              ; write to character RAM\r
+1B16: 09            add  hl,bc               ; add offset. Now HL points to character beneath. \r
+1B17: 13            inc  de                  ; bump pointer to next character \r
+1B18: D9            exx                      ; swap to external registers. B now has count of characters left to draw.\r
+1B19: 10 F8         djnz $1B13               ; Decrement B and if not 0, goto $1B13 to plot remaining characters.\r
+1B1B: AF            xor  a\r
+1B1C: 32 01 70      ld   ($7001),a           ; Set flag for NMI to read\r
+1B1F: 3A 00 78      ld   a,($7800)           ; Kick the watchdog\r
+1B22: 3A 00 60      ld   a,($6000)           ; read coin, p1 control, test & service state\r
+1B25: E6 40         and  $40                 ; mask in !SW0 TEST button bit\r
+1B27: C2 1B 1B      jp   nz,$1B1B            ; if TEST button is pressed, goto $1B1B. I think this is a bug. Should be jp Z.\r
+1B2A: C3 00 00      jp   $0000               ; ... do self test again. Doesn't give user long to see the text!!!\r
+\r
+1B2D:\r
+; Text for "BAD RAM" - in reverse. Not ASCII so you won't see it in the MAME debugger.\r
+; M  A  R     D  A  B\r
+ 1D 11 22 10 14 11 12\r
\r
+\r
+ ;\r
+ ; A = checksum that caused ROM test to fail\r
+ ;\r
+ ;\r
+\r
+ROM_CHECKSUM_ERROR:\r
+1B34: 4F            ld   c,a                 ; load C with incorrect checksum\r
+1B35: 3A 00 60      ld   a,($6000)           ; read coin, p1 control, test & service state \r
+1B38: 47            ld   b,a\r
+1B39: 3A 00 68      ld   a,($6800)           ; read start button, p2 control, dipsw 1/2 state \r
+1B3C: A0            and  b\r
+1B3D: E6 04         and  $04\r
+1B3F: 28 10         jr   z,$1B51\r
+1B41: 79            ld   a,c                 ; load A with incorrect checksum\r
+1B42: E6 0F         and  $0F                 ; mask in lower nibble\r
+1B44: 32 D3 51      ld   ($51D3),a           ; write to character RAM\r
+1B47: 79            ld   a,c\r
+1B48: 0F            rrca\r
+1B49: 0F            rrca\r
+1B4A: 0F            rrca\r
+1B4B: 0F            rrca\r
+1B4C: E6 0F         and  $0F\r
+1B4E: 32 F3 51      ld   ($51F3),a           ; write to character RAM\r
+1B51: 11 56 1B      ld   de,$1B56            ; address of text "BAD ROM" in reverse\r
+1B54: 18 B4         jr   $1B0A               ; call DISPLAY_BAD_RAM_OR_ROM_MESSAGE\r
+\r
+\r
+1B56: \r
+; Text for "BAD ROM" - in reverse. Not ASCII so you won't see it in the MAME debugger.\r
+; M  O  R     D  A  B\r
+ 1D 1F 22 10 14 11 12 \r
+\r
+\r
+\r
+CLEAR_SCREEN:\r
+1B5D: 21 00 50      ld   hl,$5000            ; start address of character RAM \r
+1B60: 06 04         ld   b,$04               ; total number of bytes in character RAM, divided by 256\r
+1B62: 3E 10         ld   a,$10               ; ordinal for empty character \r
+1B64: 77            ld   (hl),a              ; write character\r
+1B65: 2C            inc  l\r
+1B66: C2 64 1B      jp   nz,$1B64\r
+1B69: 24            inc  h\r
+1B6A: 3A 00 78      ld   a,($7800)           ; Kick the watchdog\r
+1B6D: 10 F3         djnz $1B62\r
+1B6F: C9            ret\r
+\r
+\r
+\r
+;\r
+; ROM checksum\r
+;\r
+;\r
+\r
+ROM_CHECKSUM:\r
+1B70: CD 5D 1B      call $1B5D               ; call CLEAR_SCREEN\r
+1B73: 21 00 00      ld   hl,$0000            ; point HL to start of ROM\r
+1B76: 06 28         ld   b,$28               ; B holds number of bytes we need to read  \r
+1B78: AF            xor  a                   ; A will be used to hold checksum\r
+1B79: 86            add  a,(hl)              ; add byte read from ROM to checksum value\r
+1B7A: 2C            inc  l                   ; increment low byte of ROM pointer\r
+1B7B: C2 79 1B      jp   nz,$1B79            ; if low byte has not wrapped to 0, goto $1B79\r
+1B7E: 24            inc  h                   ; Otherwise increment high byte of ROM pointer\r
+1B7F: 4F            ld   c,a                 ; Save current checksum value in C\r
+1B80: 3A 00 78      ld   a,($7800)           ; Kick the watchdog\r
+1B83: 79            ld   a,c                 ; Restore current checksum value from C\r
+1B84: 10 F3         djnz $1B79               ; Loop until B == 0\r
+1B86: A7            and  a                   ; Test if checksum total is 0\r
+1B87: C2 34 1B      jp   nz,$1B34            ; If checksum total is not 0, then display BAD ROM message\r
+\r
+; When we get here, all the diagnostic tests have succeeded. \r
+; There are no errors, and the game can start proper.\r
+\r
+ALL_TESTS_HAVE_PASSED:\r
+1B8A: 21 00 40      ld   hl,$4000            ; Start of working RAM\r
+1B8D: 06 C0         ld   b,$C0\r
+1B8F: D7            rst  $10                 ; Fill B bytes of memory from HL with value in A.\r
+1B90: 3D            dec  a\r
+1B91: 06 40         ld   b,$40\r
+1B93: D7            rst  $10                 ; Fill B bytes of memory from HL with value in A.\r
+1B94: AF            xor  a\r
+1B95: D7            rst  $10                 ; Fill B bytes of memory from HL with value in A.\r
+1B96: D7            rst  $10                 ; Fill B bytes of memory from HL with value in A.\r
+1B97: 06 A0         ld   b,$A0\r
+1B99: D7            rst  $10                 ; Fill B bytes of memory from HL with value in A.\r
+1B9A: 32 01 70      ld   ($7001),a           ; clear 9Nregen NMIon\r
+1B9D: 32 05 70      ld   ($7005),a\r
+1BA0: 32 06 70      ld   ($7006),a           ; set "regen hflip" to 0\r
+1BA3: 32 07 70      ld   ($7007),a           ; set "regen vflip" to 0\r
+1BA6: 32 18 40      ld   ($4018),a           ; set DISPLAY_IS_COCKTAIL_P2 to 0\r
+1BA9: 3A 00 78      ld   a,($7800)           ; Kick the watchdog\r
+1BAC: 3E 20         ld   a,$20\r
+1BAE: 32 08 40      ld   ($4008),a           ; set TEMP_COUNTER_1\r
+1BB1: 3E 03         ld   a,$03\r
+1BB3: 32 1A 40      ld   ($401A),a           ; set DIAGNOSTIC_MESSAGE_TYPE to 1.\r
+1BB6: 21 C0 C0      ld   hl,$C0C0            \r
+1BB9: 22 A0 40      ld   ($40A0),hl          ; reset CIRC_CMD_QUEUE_PTR_LO and CIRC_CMD_QUEUE_PROC_LO\r
+1BBC: 3E 01         ld   a,$01\r
+1BBE: 32 04 70      ld   ($7004),a           ; Set 9Nregen stars on. Starry background now appears \r
+1BC1: 32 02 70      ld   ($7002),a           ; Does nothing\r
+1BC4: 32 03 70      ld   ($7003),a           ; Does nothing\r
+1BC7: 32 01 70      ld   ($7001),a           ; Enable 9Nregen NMIon\r
+1BCA: C3 00 20      jp   $2000\r
+\r
+\r
+;\r
+;\r
+; This is invoked by the NMI when the contents of $401A (DIAGNOSTIC_MESSAGE_TYPE) are non-zero.\r
+;\r
+; Value in A                        Action taken\r
+; =================================================================================================================\r
+; 1                                 Invokes HANDLE_SERVICE_MODE (displays Dip switch settings, performs sound test)\r
+; 2                                 Invokes CHARACTER_RAM_COLOUR_TEST (displays a coloured grid)\r
+; 3                                 Invokes OBJRAM_TEST: test attributes & sprites for (value held in $4008) times\r
+; Any other value                   Back to self-test\r
+;\r
+;\r
+\r
+HANDLE_DIAGNOSTICS:\r
+1BCD: 21 D8 00      ld   hl,$00D8            ; address of exit code in NMI handler\r
+1BD0: E5            push hl                  ; push return address onto stack. When RET hit, this will be jumped to\r
+1BD1: 3D            dec  a\r
+1BD2: CA 3A 1C      jp   z,$1C3A             ; if A was 1 on entry, goto $1C3A (HANDLE_SERVICE_MODE)\r
+1BD5: 3D            dec  a\r
+1BD6: CA 28 1D      jp   z,$1D28             ; if A was 2 on entry, goto $1D28\r
+1BD9: 3D            dec  a\r
+1BDA: C2 00 00      jp   nz,$0000            ; if value is !=0 then back to self-test\r
+\r
+\r
+; Write a batch of bytes with calculated values to an area of OBJRAM ($5800 - $58FF).\r
+; When the batch has been written, read each byte back and compare to calculated values.\r
+; if the bytes written and read back don't match, you have an OBJECT RAM error and "BAD RAM 3" will be displayed.\r
+\r
+OBJRAM_TEST:\r
+; First write random values to OBJRAM\r
+1BDD: 21 00 58      ld   hl,$5800            ; Start of OBJRAM\r
+1BE0: 3A 1E 40      ld   a,($401E)           ; use RAND_NUMBER as our seed number.\r
+1BE3: 77            ld   (hl),a              ; write to OBJRAM\r
+1BE4: C6 2F         add  a,$2F               ; add #$2F (47 decimal) to number just written\r
+1BE6: 2C            inc  l                   ; increment low byte of OBJRAM pointer\r
+1BE7: C2 E3 1B      jp   nz,$1BE3            ; if low byte has not wrapped to 0, goto $1BE3\r
+\r
+; Second, read values from OBJRAM and verify.\r
+1BEA: 3A 1E 40      ld   a,($401E)           ; use RAND_NUMBER as our seed number.            \r
+1BED: BE            cp   (hl)                ; read from OBJRAM and compare \r
+1BEE: 20 3C         jr   nz,$1C2C            ; if the value read from OBJRAM doesn't match what we expect, goto DISPLAY_BAD_RAM_3\r
+1BF0: C6 2F         add  a,$2F               ; add #$2F (47 decimal) to number just written\r
+1BF2: 2C            inc  l                   ; increment low byte of OBJRAM pointer\r
+1BF3: C2 ED 1B      jp   nz,$1BED            ; if low byte has not wrapped to 0, goto $1BED\r
+\r
+1BF6: 3A 00 78      ld   a,($7800)           ; Kick the watchdog\r
+1BF9: CD 3C 00      call $003C               ; call GENERATE_RANDOM_NUMBER\r
+1BFC: 21 08 40      ld   hl,$4008            ; pointer to count of how many times to repeat test\r
+1BFF: 35            dec  (hl)                ; decrement value \r
+1C00: C0            ret  nz                  ; if value is 0, return, no more tests to do\r
+1C01: AF            xor  a\r
+1C02: 21 00 58      ld   hl,$5800            ; Start of OBJRAM\r
+1C05: 47            ld   b,a                 ; \r
+1C06: D7            rst  $10                 ; Fill $5800 to $5900 inclusive with value in A.\r
+1C07: 3E 01         ld   a,$01\r
+1C09: 32 06 40      ld   ($4006),a           ; set IS_GAME_IN_PLAY\r
+1C0C: 32 1A 40      ld   ($401A),a\r
+1C0F: 32 00 60      ld   ($6000),a           ; write to !DRIVER lamp 1\r
+1C12: 32 01 60      ld   ($6001),a           ; write to !DRIVER lamp 2\r
+1C15: 32 02 60      ld   ($6002),a           ; write to !DRIVER lamp 3\r
+1C18: 32 26 42      ld   ($4226),a\r
+1C1B: 32 5F 42      ld   ($425F),a           ; set TIMING_VARIABLE\r
+1C1E: 32 38 42      ld   ($4238),a\r
+1C21: 3E 1F         ld   a,$1F\r
+1C23: 32 13 52      ld   ($5213),a           ; write "O" to screen\r
+1C26: 3E 1B         ld   a,$1B\r
+1C28: 32 F3 51      ld   ($51F3),a           ; write "K" to screen\r
+1C2B: C9            ret\r
+\r
+\r
+\r
+DISPLAY_BAD_RAM_3:\r
+1C2C: 21 00 58      ld   hl,$5800            ; Start of OBJRAM\r
+1C2F: AF            xor  a                   ; value to write\r
+1C30: 77            ld   (hl),a              ; write to OBJRAM\r
+1C31: 2C            inc  l                   ; increment low byte of pointer\r
+1C32: C2 30 1C      jp   nz,$1C30            ; if low byte has not wrapped to 0, goto $1C30\r
+1C35: 3E 03         ld   a,$03               ; error number\r
+1C37: C3 04 1B      jp   $1B04               ; Jump to DISPLAY_BAD_RAM_MESSAGE, to display BAD RAM 3\r
+\r
+\r
+\r
+\r
+HANDLE_SERVICE_MODE:\r
+1C3A: CD F5 16      call $16F5               ; call HANDLE_SOUND\r
+1C3D: CD A6 16      call $16A6               ; does nothing - must be legacy.\r
+1C40: 3A 00 78      ld   a,($7800)           ; Kick the watchdog \r
+1C43: 3A 00 60      ld   a,($6000)           ; read coin, p1 control, test & service state\r
+1C46: 47            ld   b,a                 ; save bits in B as A will be corrupted by succeeding operations\r
+1C47: E6 83         and  $83                 ; mask in coin1, coin2 and service bits\r
+1C49: 28 05         jr   z,$1C50             ; if coin1 or coin2 are not held down, goto $1C50\r
+1C4B: 3E 01         ld   a,$01\r
+1C4D: 32 C9 41      ld   ($41C9),a           ; set PLAY_PLAYER_CREDIT_SOUND\r
+1C50: 3A 00 68      ld   a,($6800)           ; read start button, p2 control, dipsw 1/2 state \r
+1C53: 4F            ld   c,a                 ; save state in C\r
+1C54: E6 03         and  $03                 ; mask in the 1p START and 2p START button states\r
+1C56: 28 05         jr   z,$1C5D             ; if no buttons held down, goto $1C5D\r
+1C58: 3E 16         ld   a,$16\r
+1C5A: 32 DF 41      ld   ($41DF),a           ; play the sound made when you shoot a Flagship in flight\r
+1C5D: 78            ld   a,b                 ; Now A = coin, p1 control, test & service state\r
+1C5E: B1            or   c                   ; combine bits with C, which holds start button, p2 control, dipsw 1/2 state \r
+1C5F: E6 0C         and  $0C                 ; mask in the controller bits. Tests if P1 or P2's controllers have been moved left or right\r
+1C61: 28 05         jr   z,$1C68             ; if no controllers have been moved, goto $1C68\r
+1C63: 3E 06         ld   a,$06               ; otherwise... \r
+1C65: 32 DF 41      ld   ($41DF),a           ; play the sound made when you shoot an alien\r
+1C68: 78            ld   a,b                 ; Now A = coin, p1 control, test & service state\r
+1C69: B1            or   c                   ; combine bits with C, which holds start button, p2 control, dipsw 1/2 state \r
+1C6A: E6 10         and  $10                 ; mask in the bit for SHOOT \r
+1C6C: 28 05         jr   z,$1C73             ; if no shoot button is pressed, goto $1C73\r
+1C6E: 3E 01         ld   a,$01               ; \r
+1C70: 32 CC 41      ld   ($41CC),a           ; set PLAY_PLAYER_SHOOT_SOUND flag\r
+\r
+; handle METHOD OF PLAY\r
+1C73: 3A 00 68      ld   a,($6800)           ; read start button, p2 control, dipsw 1/2 state \r
+1C76: 07            rlca                     ; move bits 6 & 7 (the state of dip switches 1 & 2)..   \r
+1C77: 07            rlca                     ; .. to bits 0 & 1.\r
+1C78: E6 03         and  $03                 ; mask in bits 0 & 1, discard everything else.\r
+1C7A: CD CF 1C      call $1CCF               ; call DISPLAY_DIP_SWITCH_SETTINGS\r
+\r
+; handle BONUS GALIXIP\r
+1C7D: 3A 00 70      ld   a,($7000)           ; read state of dip switch 3,4,5,6  \r
+1C80: E6 03         and  $03                 ; mask in state of dip switches 3 & 4\r
+1C82: C6 04         add  a,$04\r
+1C84: CD CF 1C      call $1CCF               ; call DISPLAY_DIP_SWITCH_SETTINGS\r
+\r
+; handle NUMBER OF GALIXIP PER GAME\r
+1C87: 3A 00 70      ld   a,($7000)           ; read state of dip switch 3,4,5,6\r
+1C8A: 0F            rrca                     ; move bit 2 (the state of dip switch 5)..   \r
+1C8B: 0F            rrca                     ; .. to bit 0\r
+1C8C: E6 01         and  $01                 ; mask in bit 0, discard everything else\r
+1C8E: C6 08         add  a,$08\r
+1C90: CD CF 1C      call $1CCF               ; call DISPLAY_DIP_SWITCH_SETTINGS\r
+\r
+1C93: 3A 00 60      ld   a,($6000)           ; read coin, p1 control, test & service state   \r
+1C96: E6 40         and  $40                 ; read TEST bit\r
+1C98: C0            ret  nz                  ; if bit set, return\r
+1C99: AF            xor  a\r
+1C9A: 32 06 40      ld   ($4006),a           ; clear IS_GAME_IN_PLAY\r
+1C9D: 3E 02         ld   a,$02\r
+1C9F: 32 1A 40      ld   ($401A),a           ; set SCRIPT_STAGE\r
+1CA2: 21 10 30      ld   hl,$3010\r
+1CA5: 22 08 40      ld   ($4008),hl          ; set TEMP_COUNTER_1 and TEMP_COUNTER_2 in one go\r
+1CA8: 21 00 50      ld   hl,$5000            ; HL = pointer to character RAM\r
+1CAB: 22 0B 40      ld   ($400B),hl          ; set TEMP_CHAR_RAM_PTR\r
+1CAE: AF            xor  a\r
+1CAF: 21 00 60      ld   hl,$6000            ; point HL to !SW0 \r
+1CB2: 06 04         ld   b,$04\r
+1CB4: D7            rst  $10                 ; Fill B bytes of memory from HL with value in A.    \r
+\r
+RESET_SOUND:\r
+1CB5: 3E 01         ld   a,$01\r
+1CB7: 21 04 60      ld   hl,$6004            ; Address of !DRIVER Background lfo freq bit0\r
+1CBA: 06 04         ld   b,$04\r
+1CBC: D7            rst  $10                 ; Fill B bytes of memory from HL with value in A.\r
+1CBD: AF            xor  a\r
+1CBE: 06 08         ld   b,$08\r
+1CC0: 21 00 68      ld   hl,$6800\r
+1CC3: D7            rst  $10                 ; Fill B bytes of memory from HL with value in A.\r
+1CC4: 06 05         ld   b,$05\r
+1CC6: 21 01 70      ld   hl,$7001\r
+1CC9: D7            rst  $10                 ; Fill B bytes of memory from HL with value in A.\r
+1CCA: 3D            dec  a\r
+1CCB: 32 00 78      ld   ($7800),a\r
+1CCE: C9            ret\r
+\r
+;\r
+; Expects:\r
+;\r
+; a = a value from 0..9.\r
+;    \r
+; Value in register A               Displayed on screen\r
+; =====================================================\r
+; 0                                 "1 COIN 1 CREDIT"  \r
+; 1                                 "2 COINS 1 CREDIT" \r
+; 2                                 "1 COIN 2 CREDITS"\r
+; 3                                 "FREE PLAY"" \r
+; 4                                 "BONUS  7000" \r
+; 5                                 "BONUS 10000" \r
+; 6                                 "BONUS 12000"  \r
+; 7                                 "BONUS 20000" \r
+; 8                                 "GALIXIP 2" \r
+; 9                                 "GALIXIP 3"\r
+\r
+DISPLAY_DIP_SWITCH_SETTINGS:\r
+1CCF: 47            ld   b,a\r
+1CD0: 87            add  a,a                 ; \r
+1CD1: 87            add  a,a                 ; \r
+1CD2: 80            add  a,b                 ; a*=5. (There's 5 bytes per entry in the DIP_SWITCH_SETTINGS_TEXT table.)\r
+1CD3: 5F            ld   e,a\r
+1CD4: 16 00         ld   d,$00               ; DE is now offset into DIP_SWITCH_SETTINGS_TEXT table.\r
+1CD6: 21 F6 1C      ld   hl,$1CF6            ; pointer to DIP_SWITCH_SETTINGS_TEXT table\r
+1CD9: 19            add  hl,de               ; hl+=de. Now HL points to an entry in the table @ 1CF6.\r
+\r
+1CDA: 06 02         ld   b,$02               ; we want to push 2 values onto stack\r
+1CDC: 5E            ld   e,(hl)              ; load E from DIP_SWITCH_SETTINGS_TEXT table               \r
+1CDD: 23            inc  hl\r
+1CDE: 56            ld   d,(hl)              ; load D from DIP_SWITCH_SETTINGS_TEXT table\r
+1CDF: 23            inc  hl\r
+1CE0: D5            push de                  ; save de on stack. \r
+1CE1: 10 F9         djnz $1CDC               ; do until values pushed on stack\r
+\r
+1CE3: 46            ld   b,(hl)              ; read count of characters to draw from table\r
+1CE4: D9            exx                      ; swap to external Z80 registers\r
+1CE5: E1            pop  hl                  ; and pop the 2 values from the stack\r
+1CE6: D1            pop  de\r
+; now HL = pointer to character RAM\r
+; and DE = pointer to text to write\r
+1CE7: 01 E0 FF      ld   bc,$FFE0            ; BC = -32 decimal\r
+1CEA: D9            exx                      ; swap to "normal" Z80 registers\r
+1CEB: D9            exx\r
+1CEC: 1A            ld   a,(de)              ; read character to print  \r
+1CED: D6 30         sub  $30\r
+1CEF: 77            ld   (hl),a              ; poke character into character RAM\r
+1CF0: 13            inc  de                       \r
+1CF1: 09            add  hl,bc               ; bump character RAM pointer to point to character above one just plotted\r
+1CF2: D9            exx\r
+1CF3: 10 F6         djnz $1CEB\r
+1CF5: C9            ret\r
+\r
+\r
+;\r
+; Each entry in the table takes 5 bytes:\r
+;\r
+; The first 2 bytes are a pointer to characters to be drawn on screen.\r
+; The next 2 bytes are a pointer to character RAM. The characters will be plotted vertically from that address.\r
+; The last byte is the number of characters to draw.\r
+;\r
+\r
+DIP_SWITCH_SETTINGS_TEXT:\r
+\r
+1CF6:  \r
+12 1F D6 52 10                               ; 1 COIN 1 CREDIT  \r
+22 1F D6 52 10                               ; 2 COINS 1 CREDIT \r
+32 1F D6 52 10                               ; 1 COIN 2 CREDITS\r
+42 1F D6 52 10                               ; FREE PLAY \r
+\r
+52 1F D8 52 0B                               ; BONUS  7000 \r
+5D 1F D8 52 0B                               ; BONUS 10000 \r
+68 1F D8 52 0B                               ; BONUS 12000  \r
+73 1F D8 52 0B                               ; BONUS 20000 \r
+\r
+\r
+7E 1F DA 52 09                               ; GALIXIP 2 \r
+87 1F DA 52 09                               ; GALIXIP 3\r
+\r
+\r
+\r
+;\r
+; Character RAM and colour test.\r
+; Displayed, very briefly, on startup.\r
+;\r
+; Value in $4008: represents how many columns of grids you want to display.\r
+\r
+CHARACTER_RAM_COLOUR_TEST: \r
+1D28: 21 08 40      ld   hl,$4008            ; load HL with address of TEMP_COUNTER_1\r
+1D2B: 3A 00 78      ld   a,($7800)           ; Kick the watchdog\r
+1D2E: 7E            ld   a,(hl)             \r
+1D2F: A7            and  a                   ; test if zero\r
+1D30: CA 51 1D      jp   z,$1D51    \r
+1D33: D9            exx\r
+1D34: 2A 0B 40      ld   hl,($400B)          ; \r
+1D37: 06 10         ld   b,$10               ; Render 16 pairs of characters on a row..\r
+1D39: 36 30         ld   (hl),$30            ; plot top right of square character\r
+1D3B: 23            inc  hl\r
+1D3C: 36 32         ld   (hl),$32            ; plot bottom right of square character\r
+1D3E: 23            inc  hl\r
+1D3F: 10 F8         djnz $1D39\r
+\r
+1D41: 06 10         ld   b,$10\r
+1D43: 36 34         ld   (hl),$34            ; plot top left of square character\r
+1D45: 23            inc  hl\r
+1D46: 36 36         ld   (hl),$36            ; plot bottom left of square character\r
+1D48: 23            inc  hl\r
+1D49: 10 F8         djnz $1D43\r
+\r
+1D4B: 22 0B 40      ld   ($400B),hl          ; set TEMP_CHAR_RAM_PTR\r
+1D4E: D9            exx\r
+1D4F: 35            dec  (hl)                ; decrement value in TEMP_COUNTER_1\r
+1D50: C0            ret  nz\r
+\r
+1D51: 23            inc  hl                  ; bump HL to address of TEMP_COUNTER_2\r
+1D52: 7E            ld   a,(hl)              ; read value of TEMP_COUNTER_2\r
+1D53: A7            and  a                   ; test if its zero\r
+1D54: 28 02         jr   z,$1D58             ; if it's zero, goto $1D58\r
+1D56: 35            dec  (hl)                ; otherwise decrement value of TEMP_COUNTER_2\r
+1D57: C0            ret  nz                  ; return if value isn't zero.\r
+\r
+1D58: 3A 00 60      ld   a,($6000)           ; read coin, p1 control, test & service state\r
+1D5B: E6 40         and  $40                 ; mask in TEST bit\r
+1D5D: C0            ret  nz                  ; return if TEST is on\r
+1D5E: 21 00 50      ld   hl,$5000            ; pointer to start of character RAM \r
+1D61: 22 0B 40      ld   ($400B),hl          ; set TEMP_CHAR_RAM_PTR\r
+1D64: 3E 20         ld   a,$20\r
+1D66: 32 08 40      ld   ($4008),a           ; set TEMP_COUNTER_1\r
+1D69: AF            xor  a\r
+1D6A: 32 1A 40      ld   ($401A),a           ; set DIAGNOSTIC_MESSAGE_TYPE\r
+1D6D: 32 05 40      ld   ($4005),a           ; set SCRIPT_NUMBER\r
+1D70: C9            ret\r
+\r
+\r
+\r
+; Referenced by code @ $0595\r
+COLOUR_ATTRIBUTE_TABLE_1:\r
+1D71:  00 05 00 00 01 01 02 03 03 04 04 04 04 00 00 00  \r
+1D81:  00 00 00 05 05 05 05 05 00 00 06 06 06 06 06 06          \r
+\r
+; Referenced by code @ $0408\r
+COLOUR_ATTRIBUTE_TABLE_2:\r
+1D91:  00 05 00 00 01 01 02 03 03 04 04 04 04 00 00 00  ................\r
+1DA1:  06 06 06 06 06 06 05 06 06 06 06 06 06 06 06 06  ................\r
+\r
+; Referenced by code @ $0212\r
+COLOUR_ATTRIBUTE_TABLE_3:\r
+1DB1:  00 05 00 00 01 01 02 03 05 04 05 04 04 00 00 00  ................\r
+1DC1:  00 06 06 06 06 06 06 06 06 06 00 00 07 07 06 06  ................\r
+\r
+; Referenced by code @ $0D1D\r
+1DD1:  00 00 00 00 04 01 04 02 04 01 03 03 02 02 01 02  ................\r
+\r
+1DE1: 00            nop\r
+1DE2: 00            nop\r
+1DE3: 00            nop\r
+1DE4: 00            nop\r
+1DE5: 00            nop\r
+1DE6: 00            nop\r
+1DE7: 00            nop\r
+1DE8: 00            nop\r
+1DE9: 00            nop\r
+1DEA: 00            nop\r
+1DEB: 00            nop\r
+1DEC: 00            nop\r
+1DED: 00            nop\r
+1DEE: 00            nop\r
+1DEF: 00            nop\r
+1DF0: 00            nop\r
+1DF1: 00            nop\r
+1DF2: 00            nop\r
+1DF3: 00            nop\r
+1DF4: 00            nop\r
+1DF5: 00            nop\r
+1DF6: 00            nop\r
+1DF7: 00            nop\r
+1DF8: 00            nop\r
+1DF9: 00            nop\r
+1DFA: 00            nop\r
+1DFB: 00            nop\r
+1DFC: 00            nop\r
+1DFD: 00            nop\r
+1DFE: 00            nop\r
+1DFF: 00            nop\r
+\r
+\r
+;\r
+; Defines the arc to perform a loop the loop maneuvre. \r
+; Referenced by code @$0D71 and $101F.\r
+;\r
+; The table comprises byte pairs:\r
+;   byte 0: signed offset to add to INFLIGHT_ALIEN.X\r
+;   byte 1: unsigned offset to add to *or* subtract from (depends on which way alien is facing when it breaks off from swarm) INFLIGHT_ALIEN.Y \r
+;\r
+INFLIGHT_ALIEN_ARC_TABLE:\r
+1E00:  FF 00 FF 00 FF 00 FF 01 FF 00 FF 00 FF 01 FF 00  \r
+1E10:  FF 01 FF 00 00 01 FF 00 FF 01 00 01 FF 00 00 01  \r
+1E20:  FF 01 00 01 FF 01 00 01 00 01 FF 01 00 01 00 01  \r
+1E30:  00 01 00 01 00 01 00 01 01 01 00 01 00 01 01 01  \r
+1E40:  00 01 01 01 00 01 01 00 00 01 01 01 01 00 00 01  \r
+1E50:  01 00 01 01 01 00 01 01 01 00 01 00 01 01 01 00  \r
+1E60:  01 00 01 00 01 00 01                           \r
+\r
+;\r
+; GAME START tune. Referenced by $1756\r
+;\r
+\r
+1E68:  11 10 0F 0E 0D 0C 0B 0A 09 08 07 41 42 41 42 45  \r
+1E78:  42 45 47 45 47 6A 60 41 42 41 42 45 42 45 47 45  \r
+1E88:  47 6A 60 45 23 24 23 24 23 24 23 24 23 24 23 24  \r
+1E98:  23 24 23 24 02 03 05 06 07 08 09 0A 02 03 05 06  \r
+1EA8:  07 08 09 0A 02 03 05 06 07 08 09 0A 02 03 05 06  \r
+1EB8:  07 08 09 0A E0 \r
+\r
+;\r
+; ALIEN DEATH sound effect. Referenced by $1833\r
+;\r
+\r
+1EBD:  08 07 06 05 03 02 08 07 06 05 03 02 02 03 05 06  \r
+1ECD:  07 08 09 0A 0B 0C 0D 0E 0F 10 0F 0E 0D 0C 0B 0C  \r
+1EDD:  0D E0 \r
+\r
+\r
+;\r
+; FLAGSHIP DEATH sound effect. Referenced by $1848\r
+;\r
+\r
+1EDF:  02 17 16 01 16 02 03 05 06 07 18 20 07 06 05 03  \r
+1EEF:  02 03 06 07 08 09 0A 19 20 0A 09 08 07 08 0A 0B  \r
+1EFF:  0C 0D 0E 1A 20 0E 0D 0C 0B 0A 0B 0D 0E 0F 10 11  \r
+1F0F:  1B 3C E0 \r
+\r
+\r
+\r
+1F12:  31 40 43 4F 49 4E 40 31 40 43 52 45 44 49 54 40  1@COIN@1@CREDIT@\r
+1F22:  32 40 43 4F 49 4E 53 40 31 40 43 52 45 44 49 54  2@COINS@1@CREDIT\r
+1F32:  31 40 43 4F 49 4E 40 32 40 43 52 45 44 49 54 53  1@COIN@2@CREDITS\r
+1F42:  46 52 45 45 40 50 4C 41 59 40 40 40 40 40 40 40  FREE@PLAY@@@@@@@\r
+1F52:  42 4F 4E 55 53 40 40 37 30 30 30 42 4F 4E 55 53  BONUS@@7000BONUS\r
+1F62:  40 31 30 30 30 30 42 4F 4E 55 53 40 31 32 30 30  @10000BONUS@1200\r
+1F72:  30 42 4F 4E 55 53 40 32 30 30 30 30 47 41 4C 41  0BONUS@20000GALA\r
+1F82:  58 49 50 40 32 47 41 4C 41 58 49 50 40 33        XIP@2GALAXIP@3\r
+\r
+1F90: 00            nop\r
+1F91: 00            nop\r
+1F92: 00            nop\r
+1F93: 00            nop\r
+1F94: 00            nop\r
+1F95: 00            nop\r
+1F96: 00            nop\r
+1F97: 00            nop\r
+1F98: 00            nop\r
+1F99: 00            nop\r
+1F9A: 00            nop\r
+1F9B: 00            nop\r
+1F9C: 00            nop\r
+1F9D: 00            nop\r
+1F9E: 00            nop\r
+1F9F: 00            nop\r
+1FA0: 00            nop\r
+1FA1: 00            nop\r
+1FA2: 00            nop\r
+1FA3: 00            nop\r
+1FA4: 00            nop\r
+1FA5: 00            nop\r
+1FA6: 00            nop\r
+1FA7: 00            nop\r
+1FA8: 00            nop\r
+1FA9: 00            nop\r
+1FAA: 00            nop\r
+1FAB: 00            nop\r
+1FAC: 00            nop\r
+1FAD: 00            nop\r
+1FAE: 00            nop\r
+1FAF: 00            nop\r
+1FB0: 00            nop\r
+1FB1: 00            nop\r
+1FB2: 00            nop\r
+1FB3: 00            nop\r
+1FB4: 00            nop\r
+1FB5: 00            nop\r
+1FB6: 00            nop\r
+1FB7: 00            nop\r
+1FB8: 00            nop\r
+1FB9: 00            nop\r
+1FBA: 00            nop\r
+1FBB: 00            nop\r
+1FBC: 00            nop\r
+1FBD: 00            nop\r
+1FBE: 00            nop\r
+1FBF: 00            nop\r
+1FC0: 00            nop\r
+1FC1: 00            nop\r
+1FC2: 00            nop\r
+1FC3: 00            nop\r
+1FC4: 00            nop\r
+1FC5: 00            nop\r
+1FC6: 00            nop\r
+1FC7: 00            nop\r
+1FC8: 00            nop\r
+1FC9: 00            nop\r
+1FCA: 00            nop\r
+1FCB: 00            nop\r
+1FCC: 00            nop\r
+1FCD: 00            nop\r
+1FCE: 00            nop\r
+1FCF: 00            nop\r
+1FD0: 00            nop\r
+1FD1: 00            nop\r
+1FD2: 00            nop\r
+1FD3: 00            nop\r
+1FD4: 00            nop\r
+1FD5: 00            nop\r
+1FD6: 00            nop\r
+1FD7: 00            nop\r
+1FD8: 00            nop\r
+1FD9: 00            nop\r
+1FDA: 00            nop\r
+1FDB: 00            nop\r
+1FDC: 00            nop\r
+1FDD: 00            nop\r
+1FDE: 00            nop\r
+1FDF: 00            nop\r
+1FE0: 00            nop\r
+1FE1: 00            nop\r
+1FE2: 00            nop\r
+1FE3: 00            nop\r
+1FE4: 00            nop\r
+1FE5: 00            nop\r
+1FE6: 00            nop\r
+1FE7: 00            nop\r
+1FE8: 00            nop\r
+1FE9: 00            nop\r
+1FEA: 00            nop\r
+1FEB: 00            nop\r
+1FEC: 00            nop\r
+1FED: 00            nop\r
+1FEE: 00            nop\r
+1FEF: 00            nop\r
+1FF0: 00            nop\r
+1FF1: 00            nop\r
+1FF2: 00            nop\r
+1FF3: 00            nop\r
+1FF4: 00            nop\r
+1FF5: 00            nop\r
+1FF6: 00            nop\r
+1FF7: 00            nop\r
+1FF8: 00            nop\r
+1FF9: 00            nop\r
+1FFA: 00            nop\r
+1FFB: 00            nop\r
+1FFC: 00            nop\r
+1FFD: 00            nop\r
+1FFE: 00            nop\r
+1FFF: 00            nop\r
+\r
+\r
+; reset all player-related state including score, high score\r
+2000: 21 A2 40      ld   hl,$40A2            ; pointer to player 1 state\r
+2003: 06 1E         ld   b,$1E               ; number of bytes to write\r
+2005: 36 00         ld   (hl),$00            ; write 0 \r
+2007: 23            inc  hl\r
+2008: 10 FB         djnz $2005               ; repeat until b==0\r
+\r
+\r
+\r
+;\r
+; Process the circular command queue starting @ $40C0 (CIRC_CMD_QUEUE_START)\r
+;\r
+; Notes:\r
+; The value in $40A1 (I have named it CIRC_CMD_QUEUE_PROC_LO) is the low byte of a pointer to the first entry in \r
+; the queue to be processed. The high byte of the pointer is always #$40.\r
+; \r
+; In a circular queue, the first entry to be processed is not necessarily the head of the queue. \r
+; The first entry to be processed could be anywhere in the queue. \r
+;\r
+;\r
+; TODO: Detail algorithm\r
+\r
+PROCESS_CIRCULAR_COMMAND_QUEUE:\r
+200A: 26 40         ld   h,$40               ; high byte of pointer to queue entry be processed\r
+200C: 3A A1 40      ld   a,($40A1)           ; read CIRC_CMD_QUEUE_PROC_LO\r
+200F: 6F            ld   l,a                 ; now HL = pointer to a queue entry in the queue to be processed \r
+2010: 7E            ld   a,(hl)              ; read command number from queue entry into A. \r
+2011: 87            add  a,a                 ; multiply A by 2 to form an offset into jump table @$203D\r
+2012: 30 05         jr   nc,$2019            ; if no carry, then we have a valid command number, goto $2019\r
+2014: CD 67 20      call $2067               ; call HANDLE_SWARM_ANIMATION\r
+2017: 18 F1         jr   $200A               ; process next entry in circular queue\r
+\r
+2019: E6 0F         and  $0F                 ; mask in lower nibble\r
+201B: 4F            ld   c,a\r
+201C: 06 00         ld   b,$00               ; extend A into BC. BC is now the offset to add to $203D (see code @ $2030)\r
+201E: 36 FF         ld   (hl),$FF            ; write #$FF (255 decimal) to first byte of byte pair, to mark it as "free"\r
+2020: 2C            inc  l\r
+2021: 5E            ld   e,(hl)              ; read parameter value from queue entry into E. \r
+2022: 36 FF         ld   (hl),$FF            ; write #$FF (255 decimal) to second byte of byte pair, to mark it as "free"\r
+2024: 2C            inc  l\r
+2025: 7D            ld   a,l                 \r
+2026: FE C0         cp   $C0                 ; is HL == $4100? If so, comparing L (which will be 0) to #$C0 (192 decimal) will set the carry flag. \r
+2028: 30 02         jr   nc,$202C            ; if carry is not set, then we have not reached the end of the queue ($4100), goto $202C\r
+202A: 3E C0         ld   a,$C0               ; otherwise, we have reached end of queue. \r
+202C: 32 A1 40      ld   ($40A1),a           ; Set lo byte of pointer to $C0 ($40C0 = start of circular queue)\r
+202F: 7B            ld   a,e                 ; Now A = parameter to command\r
+2030: 21 3D 20      ld   hl,$203D            ; pointer to jump table beginning @ $203D\r
+2033: 09            add  hl,bc               ; now HL = pointer to entry in jump table\r
+2034: 5E            ld   e,(hl)\r
+2035: 23            inc  hl\r
+2036: 56            ld   d,(hl)              ; DE = pointer read from jump table\r
+2037: 21 0A 20      ld   hl,$200A            ; return address to go to (entry point of PROCESS_CIRCULAR_COMMAND_QUEUE)\r
+203A: E5            push hl                  ; push it onto stack, so when we hit a RET it'll return to $200A\r
+203B: EB            ex   de,hl               ; Swap HL and DE round so that HL is pointer read from jump table\r
+203C: E9            jp   (hl)                ; jump to code pointed to by (HL)\r
+\r
+\r
+203D: \r
+    55 20           ; pointer to code @ $2055  (DRAW_ALIEN_COMMAND)\r
+    5E 20           ; pointer to code @ $205E  (DELETE_ALIEN_COMMAND) \r
+    5F 21           ; pointer to code @ $215F  (DISPLAY_PLAYER_COMMAND)\r
+    A6 21           ; pointer to code @ $21A6  (UPDATE_PLAYER_SCORE_COMMAND)\r
+    FE 21           ; pointer to code @ $21FE  (RESET_SCORE_COMMAND)\r
+    31 22           ; pointer to code @ $2231  (DISPLAY_SCORE_COMMAND)\r
+    F1 22           ; pointer to code @ $22F1  (PRINT_TEXT)\r
+    B7 24           ; pointer to code @ $24B7  (DISPLAY_BOTTOM_OF_SCREEN)\r
+\r
+\r
+204D: E4 7B 73      call po,$737B\r
+2050: 8E            adc  a,(hl)\r
+2051: 10 FF         djnz $2052\r
+2053: FF            rst  $38\r
+2054: FF            rst  $38\r
+\r
+\r
+\r
+;\r
+; Called when an alien rejoins the swarm.\r
+; \r
+\r
+DRAW_ALIEN_COMMAND:\r
+2055: CD E1 20      call $20E1               ; call GET_ALIEN_CHAR_RAM_ADDR\r
+2058: CD 04 21      call $2104               ; call CHOOSE_ANIMATION_FRAME_FOR_ALIEN_REJOINING_SWARM\r
+205B: C3 31 21      jp   $2131               ; jump to DRAW_ALIEN\r
+\r
+\r
+\r
+;\r
+; Called to delete an alien because it's either been killed or it's broken off for an attack.\r
+;\r
+\r
+DELETE_ALIEN_COMMAND:\r
+205E: CD E1 20      call $20E1               ; call GET_ALIEN_CHAR_RAM_ADDR\r
+2061: DA 83 25      jp   c,$2583             ; jump to PLOT_CHARACTERS_2_BY_2_ASCENDING\r
+2064: C3 A7 25      jp   $25A7               ; plot two spaces in the same column \r
+\r
+\r
+;\r
+; Animates the alien swarm. \r
+; \r
+; Can be disabled by setting $4238 (DISABLE_SWARM_ANIMATION) flag to 1.\r
+; \r
+; Note:\r
+; The WE ARE THE GALAXIANS attract mode screen requires the DISABLE_SWARM_ANIMATION flag set\r
+; because the text overlays where the swarm would be and we don't want it corrupted by the animation routine.\r
+;\r
+\r
+HANDLE_SWARM_ANIMATION:\r
+2067: 3A 5F 42      ld   a,($425F)           ; read TIMING_VARIABLE\r
+206A: 47            ld   b,a\r
+206B: E6 0F         and  $0F                 ; mask in lower nibble\r
+206D: 28 2D         jr   z,$209C             ; if lower nibble is 0, goto HANDLE_1UP_2UP_BLINKING \r
+\r
+; A = column of aliens to animate (0-15)\r
+206F: 21 20 41      ld   hl,$4120            ; load HL with pointer to bottommost row of aliens in ALIEN_SWARM_FLAGS\r
+2072: 85            add  a,l\r
+2073: 6F            ld   l,a                 ; Now HL points to bottommost alien in the column\r
+2074: 3A 38 42      ld   a,($4238)           ; read DISABLE_SWARM_ANIMATION flag  \r
+2077: 0F            rrca                     ; move bit 0 into carry\r
+2078: D8            ret  c                   ; if flag was set, return\r
+\r
+; animate each alien in the column\r
+2079: 0E 10         ld   c,$10               ; sizeof a row of aliens in ALIEN_SWARM_FLAGS\r
+207B: 06 06         ld   b,$06               ; number of rows of aliens to process. \r
+207D: C5            push bc\r
+207E: E5            push hl\r
+207F: 7D            ld   a,l\r
+2080: CB 46         bit  0,(hl)              ; test flag in ALIEN_SWARM_FLAGS to see if alien is present\r
+2082: 20 05         jr   nz,$2089            ; if alien is present, goto $2089\r
+\r
+2084: CD 5E 20      call $205E               ; call DELETE_ALIEN_COMMAND to erase the alien from the screen\r
+2087: 18 0B         jr   $2094\r
+\r
+2089: CD E1 20      call $20E1               ; call GET_ALIEN_CHAR_RAM_ADDR\r
+208C: 0E 00         ld   c,$00               ; load randomness value (see docs for CALCULATE_ALIEN_ANIMATION_FRAME_INDEX)\r
+208E: CD 1D 21      call $211D               ; call CALCULATE_ALIEN_ANIMATION_FRAME_INDEX\r
+2091: CD 31 21      call $2131               ; call DRAW_ALIEN\r
+\r
+2094: E1            pop  hl\r
+2095: C1            pop  bc\r
+2096: 7D            ld   a,l\r
+2097: 81            add  a,c                 \r
+2098: 6F            ld   l,a                 ; HL = HL + 16 decimal. Now HL points to alien above, in same column\r
+2099: 10 E2         djnz $207D               ; repeat until all rows of aliens done\r
+209B: C9            ret\r
+\r
+\r
+\r
+;\r
+; When the game has a human in control, make 1UP or 2UP "blink".\r
+;\r
+; 1UP never blinks during attract mode, presumably because it distracts the viewer's attention.\r
+;\r
+\r
+HANDLE_1UP_2UP_BLINKING:\r
+209C: 3A 06 40      ld   a,($4006)           ; read IS_GAME_IN_PLAY flag\r
+209F: A7            and  a                   ; test if zero\r
+20A0: 28 05         jr   z,$20A7             ; if flag is not set, goto $20A7\r
+20A2: 32 AB 40      ld   ($40AB),a           ; set CAN_BLINK_1UP_2UP flag \r
+20A5: 18 05         jr   $20AC               ; goto BLINK_1UP_or_2UP_TEXT\r
+\r
+\r
+20A7: 3A AB 40      ld   a,($40AB)           ; read CAN_BLINK_1UP_2UP\r
+20AA: A7            and  a                   ; test if zero\r
+20AB: C8            ret  z                   ; if flag is not set, return\r
+\r
+;\r
+; This code makes the 1UP or 2UP text blink during the game.\r
+;\r
+; Expects: \r
+; B = any number (sourced from TIMING_VARIABLE @ $425F). \r
+;\r
+\r
+BLINK_1UP_OR_2UP_TEXT:\r
+20AC: 3A 0D 40      ld   a,($400D)           ; read CURRENT_PLAYER\r
+20AF: CD 4E 21      call $214E               ; get character RAM address for where 1UP or 2UP is printed\r
+20B2: 11 E0 FF      ld   de,$FFE0            ; load DE with -32 decimal\r
+20B5: CB 60         bit  4,b                 ; test bit 4 of timing variable value\r
+20B7: 28 14         jr   z,$20CD             ; if not set, then draw "1UP" or "2UP"\r
+\r
+; erase "1UP" or "2UP" for current player\r
+20B9: 3E 10         ld   a,$10               ; ordinal for empty space character\r
+20BB: 77            ld   (hl),a              ; erase first character of "1UP" or "2UP"\r
+20BC: 19            add  hl,de               ; bump HL to point to character directly above\r
+20BD: 77            ld   (hl),a              ; erase second character  \r
+20BE: 19            add  hl,de               ; bump HL to point to character directly above\r
+20BF: 77            ld   (hl),a              ; erase third character \r
+\r
+20C0: 3A 0E 40      ld   a,($400E)           ; read IS_TWO_PLAYER_GAME \r
+20C3: A7            and  a                   ; test flag\r
+20C4: C8            ret  z                   ; return if not a two-player game \r
+20C5: 3A 0D 40      ld   a,($400D)           ; read CURRENT_PLAYER \r
+20C8: EE 01         xor  $01                 ; swap between player one and two (0= Player One, 1=Player 2)\r
+20CA: CD 4E 21      call $214E               ; get character RAM address for where 1UP or 2UP is printed\r
+\r
+; plot "1UP" or "2UP" \r
+20CD: 3C            inc  a                   ; A now is the ordinal for the "1" or "2" character \r
+20CE: 77            ld   (hl),a              ; write "1" or "2"\r
+20CF: 19            add  hl,de\r
+20D0: 36 25         ld   (hl),$25            ; 'U'\r
+20D2: 19            add  hl,de\r
+20D3: 36 20         ld   (hl),$20            ; 'P'\r
+20D5: CB 60         bit  4,b\r
+20D7: C0            ret  nz\r
+20D8: 3A 06 40      ld   a,($4006)           ; read IS_GAME_IN_PLAY\r
+20DB: A7            and  a                   \r
+20DC: C0            ret  nz                  ; if game is in play, return\r
+20DD: 32 AB 40      ld   ($40AB),a           ; clear CAN_BLINK_1UP_2UP flag.\r
+20E0: C9            ret\r
+\r
+\r
+; Get character RAM address of alien in swarm. \r
+;\r
+; Expects:\r
+; A = index of alien in swarm (0-127)\r
+;\r
+; Returns: \r
+; B = A on entry\r
+; HL = pointer to screen RAM where alien should be plotted\r
+; Carry flag = set if alien is on an "odd" row\r
+; \r
+; Screen address is calculated as follows:\r
+; Flagship:              $5004 + (column index * $40)      \r
+; Red alien:             $5006 + (column index * $40)      \r
+; Purple alien:          $5007 + (column index * $40)\r
+; Blue alien top row:    $5009 + (column index * $40)\r
+; Blue alien middle row: $500A + (column index * $40)\r
+; Blue alien bottom row: $500C + (column index * $40)\r
+;\r
+; Carry is set when A identifies a Flagship, Purple, Blue Alien Mid row alien. \r
+;\r
+; I could spend more time figuring out exactly how this code works, but I don't think the benefit outweighs the \r
+; effort. It's one of those algorithms where I look at it and just don't "get it". I figure if you want to patch it, \r
+; you could easily create a lookup table - and the lookup table approach would probably be faster than this method.\r
+  \r
+GET_ALIEN_CHAR_RAM_ADDR:\r
+20E1: 47            ld   b,a                 ; preserve A in B \r
+20E2: E6 0F         and  $0F                 ; now A is the column index of the alien (0-15). *Clears carry*\r
+20E4: 0F            rrca                     ; move bit 0 & bit 1..\r
+20E5: 0F            rrca                     ; ..into bits 6 & 7 respectively.\r
+20E6: 4F            ld   c,a\r
+20E7: E6 03         and  $03\r
+20E9: 67            ld   h,a\r
+\r
+20EA: 79            ld   a,c\r
+20EB: E6 C0         and  $C0                 \r
+20ED: 6F            ld   l,a\r
+\r
+20EE: 78            ld   a,b                 ; restore alien index from B (see @$20E1)  \r
+20EF: 0F            rrca                     ; divide A..\r
+20F0: 0F            rrca\r
+20F1: 0F            rrca\r
+20F2: 0F            rrca                     ; by 16. Now A is a row index.\r
+20F3: E6 07         and  $07                 ; Ensure A falls between 0 and 7. >7 is invalid. *also clears carry flag*  \r
+; row 7 = flagship row.  row 2 = blue alien bottom row \r
+20F5: 4F            ld   c,a                 ; save row index in C.\r
+20F6: 1F            rra                      ; divide row index by 2. if row index is odd, then set carry flag\r
+\r
+; very important that we preserve the carry flag here, because DRAW_ALIEN @ 2131 needs it\r
+20F7: F5            push af\r
+20F8: 89            adc  a,c\r
+20F9: 2F            cpl\r
+20FA: E6 0F         and  $0F\r
+20FC: 85            add  a,l\r
+20FD: 6F            ld   l,a\r
+20FE: 11 00 50      ld   de,$5000            ; start of character RAM\r
+2101: 19            add  hl,de\r
+2102: F1            pop  af\r
+\r
+2103: C9            ret\r
+\r
+\r
+;\r
+; This code is called when an alien returns to the swarm and an animation frame needs to be chosen.\r
+;\r
+; Expects:\r
+; B = index of alien (0-127) \r
+;\r
+\r
+CHOOSE_ANIMATION_FRAME_FOR_ALIEN_REJOINING_SWARM:\r
+2104: F5            push af\r
+2105: 78            ld   a,b                 ; A = index of alien\r
+2106: FE 70         cp   $70                 ; is the index < $70 (112 decimal)\r
+2108: 38 04         jr   c,$210E             ; yes, index < $70, goto $210E\r
+210A: 06 80         ld   b,$80               ; this is a flagship row - set bit 7 so code @$213F knows   \r
+210C: F1            pop  af\r
+210D: C9            ret\r
+\r
+210E: E6 0F         and  $0F                 ; A = A MODULO 16. Now A = column of alien\r
+2110: 47            ld   b,a                 ; make B = column of alien\r
+2111: 3A 5F 42      ld   a,($425F)           ; read TIMING_VARIABLE\r
+2114: E6 0F         and  $0F                 ; mask in bits 0..3\r
+2116: 0E 00         ld   c,$00\r
+2118: B8            cp   b\r
+2119: 30 01         jr   nc,$211C            ; if A> B goto $211C\r
+211B: 0D            dec  c                   ;   \r
+211C: F1            pop  af\r
+\r
+\r
+; All aliens, except flagships, have 4 possible animation frames. \r
+;\r
+; This routine calculates an index which is used to look up the correct animation from a table.\r
+;\r
+; Expects: \r
+; B = index of alien (0-127) \r
+; C = value to add to calculated index, to make animation frames vary.\r
+;\r
+; Returns:\r
+; B = animation frame index from 0..3\r
+\r
+CALCULATE_ALIEN_ANIMATION_FRAME_INDEX:\r
+211D: F5            push af\r
+211E: 78            ld   a,b                 ; A now = index of alien\r
+211F: FE 70         cp   $70                 ; is the index >= $70 (112 decimal) ?\r
+2121: 30 E7         jr   nc,$210A            ; yes, then this is a flagship row, they don't animate, goto $210A\r
+2123: 3A 5F 42      ld   a,($425F)           ; read TIMING_VARIABLE\r
+2126: 0F            rrca                     ; rotate A..\r
+2127: 0F            rrca\r
+2128: 0F            rrca\r
+2129: 0F            rrca                     ; 4 bits right.\r
+212A: 80            add  a,b                 ; add in index of alien\r
+212B: 81            add  a,c                 ; add in "randomness" value\r
+212C: E6 03         and  $03                 ; ensure animation frame index is between 0..3\r
+212E: 47            ld   b,a                 ; set B to animation frame index\r
+212F: F1            pop  af\r
+2130: C9            ret\r
+\r
+\r
+\r
+; Draws alien [B] where B is an index into the ALIEN_SWARM_FLAGS array.\r
+;\r
+; For odd rows in the swarm (ie: flagship, purple aliens, blue middle row) the aliens occupy 2 columns x 2 rows of characters.\r
+; For even rows, each alien in the row occupies 1 column and 2rows of characters.\r
+;\r
+;\r
+; Expects:\r
+; B = animation frame index\r
+; HL = character RAM address to plot alien \r
+; Carry flag = if set, draw characters on odd row  \r
+\r
+DRAW_ALIEN:\r
+2131: EB            ex   de,hl\r
+2132: 38 09         jr   c,$213D             ; if carry is set, we're drawing an odd row\r
+\r
+2134: 21 57 21      ld   hl,$2157            ; load HL with pointer to ALIEN_SWARM_CHARACTERS_SET_1x2\r
+2137: 78            ld   a,b                 ; load A with animation frame index\r
+2138: E7            rst  $20                 ; call routine to fetch value @ HL + A\r
+2139: EB            ex   de,hl\r
+213A: C3 A9 25      jp   $25A9               ; jump to PLOT_TWO_CHARACTERS_IN_SAME_COLUMN\r
+\r
+; if we get here, we're drawing an "odd" row \r
+213D: 78            ld   a,b                 ; load A with animation frame index\r
+213E: A7            and  a                   ; test if its zero\r
+213F: F2 46 21      jp   p,$2146             ; if bit 7 is not set (see docs @ $210A) then its not a flagship we're drawing, goto $2146\r
+2142: 3E A4         ld   a,$A4               ; starting character for flagship\r
+2144: 18 04         jr   $214A               ; draw the flagship\r
+\r
+2146: 21 5B 21      ld   hl,$215B            ; load HL with address of ALIEN_SWARM_CHARACTERS_SET_2x2 list \r
+2149: E7            rst  $20                 ; call routine to fetch value @ HL + A\r
+214A: EB            ex   de,hl\r
+214B: C3 85 25      jp   $2585               ; jump to PLOT_CHARACTERS_2_BY_2_ASCENDING\r
+\r
+\r
+;\r
+; Expects:\r
+; A = player index\r
+;\r
+; Returns:\r
+; HL = pointer to where first character of 1UP or 2UP will be plotted\r
+;\r
+\r
+214E: 21 40 53      ld   hl,$5340            ; 1UP\r
+2151: A7            and  a\r
+2152: C8            ret  z\r
+2153: 21 E0 50      ld   hl,$50E0            ; 2UP\r
+2156: C9            ret\r
+\r
+; start ordinals of characters for 1x2 aliens (see docs @ $2131 and $25A9)\r
+ALIEN_SWARM_CHARACTERS_SET_1x2:\r
+2157: 41 35 41 31\r
+\r
+; star ordinals of characters for 2x2 aliens (see docs @ $2131 and $2585)\r
+ALIEN_SWARM_CHARACTERS_SET_2x2:\r
+215B: 44 38 44 3C\r
+\r
+\r
+;\r
+; Value in register A       Action taken\r
+; =================================================================\r
+; 0:                        Draw player ship in normal, alive state\r
+; 1:                        Erase player ship\r
+; Any other:                Render player ship as exploding\r
+;\r
+; NOTE:\r
+; The player ship isn't a sprite. It's comprised of 4x4 characters. \r
+;\r
+;\r
+\r
+DISPLAY_PLAYER_COMMAND:\r
+215F: A7            and  a                   ; test if A is 0\r
+2160: 28 39         jr   z,$219B             ; if A is 0, goto DRAW_PLAYER_SHIP\r
+2162: 3D            dec  a                  \r
+2163: 28 22         jr   z,$2187             ; if A was 1 on entry, goto ERASE_PLAYER_SHIP\r
+2165: 3D            dec  a      \r
+\r
+DRAW_PLAYER_SHIP_EXPLODING:\r
+2166: 87            add  a,a                 ; multiply A... \r
+2167: 87            add  a,a\r
+2168: 87            add  a,a\r
+2169: 87            add  a,a                 ; .. by 16 \r
+216A: 2F            cpl\r
+216B: E6 30         and  $30\r
+216D: C6 C0         add  a,$C0\r
+216F: 21 DA 51      ld   hl,$51DA            ; character RAM address for top right of explosion\r
+2172: CD 85 25      call $2585               ; call PLOT_CHARACTERS_2_BY_2_ASCENDING\r
+2175: 21 DC 51      ld   hl,$51DC            ; character RAM address for bottom right of explosion\r
+2178: CD 85 25      call $2585               ; call PLOT_CHARACTERS_2_BY_2_ASCENDING\r
+217B: 21 1A 52      ld   hl,$521A            ; character RAM address for top left of explosion\r
+217E: CD 85 25      call $2585               ; call PLOT_CHARACTERS_2_BY_2_ASCENDING\r
+2181: 21 1C 52      ld   hl,$521C            ; character RAM address for bottom left of explosion\r
+2184: C3 85 25      jp   $2585               ; call PLOT_CHARACTERS_2_BY_2_ASCENDING\r
+\r
+ERASE_PLAYER_SHIP:\r
+2187: 21 DA 51      ld   hl,$51DA\r
+218A: 11 1C 00      ld   de,$001C            ; offset to add to HL after drawing characters\r
+218D: 0E 04         ld   c,$04               ; row counter: 4 characters high\r
+218F: 06 04         ld   b,$04               ; column counter: 4 characters wide\r
+2191: 36 40         ld   (hl),$40            ; ordinal for empty character\r
+2193: 23            inc  hl                  ; bump to next character on same column\r
+2194: 10 FB         djnz $2191               ; do until B==0\r
+2196: 19            add  hl,de               ; add offset to HL to point to row beneath\r
+2197: 0D            dec  c                   ; decrement row counter\r
+2198: 20 F5         jr   nz,$218F            ; if row counter is not 0, goto $218F\r
+219A: C9            ret\r
+\r
+DRAW_PLAYER_SHIP:\r
+219B: CD 87 21      call $2187               ; erase existing ship or explosion\r
+219E: 3E 60         ld   a,$60               ; ordinal of first character to plot\r
+21A0: 21 FC 51      ld   hl,$51FC            ; character RAM address\r
+21A3: C3 85 25      jp   $2585               ; jump to PLOT_CHARACTERS_2_BY_2_ASCENDING\r
+\r
+\r
+;\r
+;\r
+; Value in register A       Points added to score\r
+; ===============================================\r
+; 0                         30 \r
+; 1                         40 \r
+; 2                         50 \r
+; 3                         60 \r
+; 4                         70 \r
+; 5                         80 \r
+; 6                         90 \r
+; 7                         100 \r
+; 8                         150 \r
+; 9                         200 \r
+; 10                        300 \r
+; 11                        800 \r
+\r
+UPDATE_PLAYER_SCORE_COMMAND:\r
+21A6: 4F            ld   c,a                 ; save A in C as the rst corrupts A\r
+21A7: CF            rst  $08                 ; assert that it's not GAME OVER \r
+\r
+21A8: CD 90 22      call $2290               ; call LEA_DE_OF_CURRENT_PLAYER_SCORE. Now DE = pointer to current player's score\r
+21AB: 79            ld   a,c                 ; restore A (points value ID) from C\r
+21AC: 81            add  a,c                 ; And then multiply ..\r
+21AD: 81            add  a,c                 ; .. A by 3.\r
+21AE: 4F            ld   c,a                   \r
+21AF: 06 00         ld   b,$00               ; Extend A into BC. Now BC is an offset into ALIEN_SCORE_TABLE \r
+21B1: 21 D0 22      ld   hl,$22D0            ; pointer to ALIEN_SCORE_TABLE  \r
+21B4: 09            add  hl,bc               ; now HL points to an entry in the score table. \r
+21B5: A7            and  a                   ; clear carry flag\r
+21B6: 06 03         ld   b,$03               ; Players score is 3 bytes in size\r
+21B8: 1A            ld   a,(de)              ; read byte from players score\r
+21B9: 8E            adc  a,(hl)              ; add byte from the score table\r
+21BA: 27            daa                      ; ensure that the result is valid BCD\r
+21BB: 12            ld   (de),a              ; update player score\r
+21BC: 13            inc  de                  ; bump to next BCD digits in players score\r
+21BD: 23            inc  hl                  ; bump to next BCD digits in alien score\r
+21BE: 10 F8         djnz $21B8               ; repeat until all digits in player score have been updated.\r
+21C0: 1B            dec  de                  ; point to first 2 digits of player score\r
+21C1: D5            push de\r
+21C2: 1B            dec  de                  ; point to 3rd and 4th digits of player score\r
+21C3: 67            ld   h,a                 ; load H with first two digits of player score  \r
+21C4: 1A            ld   a,(de)              \r
+21C5: 6F            ld   l,a                 ; load L with 3rd and 4th digits of player score\r
+21C6: 29            add  hl,hl\r
+21C7: 29            add  hl,hl\r
+21C8: 29            add  hl,hl\r
+21C9: 29            add  hl,hl               ; multiply HL * 16, which shifts all bits left 4 places.  \r
+21CA: 7C            ld   a,h\r
+21CB: 21 AC 40      ld   hl,$40AC            ; read BONUS_GALIXIP_FOR value                \r
+21CE: BE            cp   (hl)                    \r
+21CF: D4 9C 22      call nc,$229C            ; call AWARD_EXTRA_LIFE if our score is higher\r
+21D2: 13            inc  de\r
+21D3: 3A 0D 40      ld   a,($400D)           ; read CURRENT_PLAYER  \r
+21D6: CD 56 22      call $2256               ; call DECIDE_TO_DISPLAY_PLAYER_ONE_OR_PLAYER_TWO_SCORE\r
+21D9: D1            pop  de\r
+21DA: 21 AA 40      ld   hl,$40AA            ; point to last byte (first 2 digits) of HI_SCORE\r
+21DD: 06 03         ld   b,$03               ; Players score is 3 bytes in size\r
+21DF: 1A            ld   a,(de)              ; read byte from player score\r
+21E0: BE            cp   (hl)                ; compare to byte from high score\r
+21E1: D8            ret  c                   ; if byte read is lower than the byte from high score, not new high score, so return \r
+21E2: 20 05         jr   nz,$21E9            ; if byte is different, we have a new high score, goto $21E9\r
+21E4: 1B            dec  de                  ; otherwise, bump de \r
+21E5: 2B            dec  hl                  ; and bump hl\r
+21E6: 10 F7         djnz $21DF               ; repeat until b==0\r
+21E8: C9            ret\r
+\r
+\r
+UPDATE_HIGH_SCORE:\r
+21E9: CD 90 22      call $2290               ; Now DE = pointer to current player score\r
+21EC: 21 A8 40      ld   hl,$40A8            ; address of high score\r
+21EF: 06 03         ld   b,$03               ; high score occupies 3 bytes\r
+21F1: 1A            ld   a,(de)              ; read byte from player score\r
+21F2: 77            ld   (hl),a              ; update byte in high score              \r
+21F3: 13            inc  de                  ; bump DE to point to next byte in player score\r
+21F4: 23            inc  hl                  ; bump HL to point to next byte in high score\r
+21F5: 10 FA         djnz $21F1\r
+21F7: 1B            dec  de\r
+21F8: DD 21 41 52   ld   ix,$5241            ; character RAM address where HIGH SCORE will be drawn\r
+21FC: 18 63         jr   $2261               ; jump to PLOT_SCORE_CHARACTERS\r
+\r
+\r
+;\r
+; Expects:\r
+;\r
+; A is a command.\r
+;                           \r
+; Value in register A       What it represents\r
+; ==================================================================================================\r
+; 0                         Reset player 1's score to 0 and clear PLAYER_ONE_AWARDED_EXTRA_LIFE flag.  \r
+; 1                         Reset player 2's score to 0 and clear PLAYER_TWO_AWARDED_EXTRA_LIFE flag.\r
+; 2                         Reset high score to 0\r
+; 3                         Do all of the above \r
+\r
+RESET_SCORE_COMMAND:\r
+21FE: FE 03         cp   $03                \r
+2200: 30 26         jr   nc,$2228           ; if A>= 3 then goto RESET_ALL_SCORES_AND_EXTRA_LIFE_FLAGS\r
+2202: F5            push af\r
+2203: 21 A2 40      ld   hl,$40A2           ; address of PLAYER_ONE_SCORE\r
+2206: 11 AD 40      ld   de,$40AD           ; address of PLAYER_ONE_AWARDED_EXTRA_LIFE\r
+2209: A7            and  a                  ; test if A is 0\r
+220A: 28 0E         jr   z,$221A            ; if A was 0 on entry, goto $221A and reset player 1's score\r
+\r
+220C: 21 A5 40      ld   hl,$40A5           ; address of PLAYER_TWO_SCORE           \r
+220F: 11 AE 40      ld   de,$40AE           ; address of PLAYER_TWO_AWARDED_EXTRA_LIFE \r
+2212: 3D            dec  a                  ; if A was 1 on entry, then zero flag will be set after dec.\r
+2213: 28 05         jr   z,$221A            ; if zero flag is set, then A was 1 on entry, goto $221A and reset player 2's score\r
+2215: 21 A8 40      ld   hl,$40A8           ; address of HI_SCORE\r
+2218: 5D            ld   e,l\r
+2219: 54            ld   d,h                ; DE = HL\r
+221A: 36 00         ld   (hl),$00           ; clear first byte\r
+221C: 23            inc  hl\r
+221D: 36 00         ld   (hl),$00           ; clear second byte\r
+221F: 23            inc  hl\r
+2220: 36 00         ld   (hl),$00           ; clear third byte\r
+2222: EB            ex   de,hl\r
+2223: 36 00         ld   (hl),$00           ; reset the AWARDED_EXTRA_LIFE flag\r
+2225: F1            pop  af\r
+2226: 18 09         jr   $2231              ; Goto DISPLAY_SCORE_COMMAND to display reset scores.\r
+\r
+RESET_ALL_SCORES_AND_EXTRA_LIFE_FLAGS:\r
+2228: 3D            dec  a\r
+2229: F5            push af\r
+222A: CD FE 21      call $21FE\r
+222D: F1            pop  af\r
+222E: C8            ret  z\r
+222F: 18 F7         jr   $2228\r
+\r
+\r
\r
+;\r
+; Value in register A       What it represents\r
+; =====================================================\r
+; 0                         Display Player one's score \r
+; 1                         Display Player two's score \r
+; 2                         Display high score.\r
+; 3                         Do all of the above.\r
+;\r
+\r
+DISPLAY_SCORE_COMMAND:\r
+2231: FE 03         cp   $03\r
+2233: 30 18         jr   nc,$224D            ; if A>= #$03, goto $224D, DISPLAY_ALL_SCORES\r
+2235: A7            and  a                   ; test if A is 0\r
+2236: 11 A4 40      ld   de,$40A4            ; pointer to last 2 BCD digits of PLAYER_ONE_SCORE\r
+2239: 28 1B         jr   z,$2256             ; if A is 0, goto $2256 (DECIDE_TO_DISPLAY_PLAYER_ONE_OR_PLAYER_TWO_SCORE)\r
+223B: 3D            dec  a                   ; if A was 1 on entry, then Z flag is now set\r
+223C: 20 0A         jr   nz,$2248             \r
+223E: 3A 0E 40      ld   a,($400E)           ; read IS_TWO_PLAYER_GAME\r
+2241: A7            and  a                   ; test if zero\r
+2242: C8            ret  z                   ; if it's zero, then we're just in a single player game, return\r
+2243: 11 A7 40      ld   de,$40A7            ; pointer to last 2 BCD digits of PLAYER_TWO_SCORE\r
+2246: 18 0E         jr   $2256               ; goto DECIDE_TO_DISPLAY_PLAYER_ONE_OR_PLAYER_TWO_SCORE\r
+\r
+DISPLAY_HIGH_SCORE:\r
+2248: 11 AA 40      ld   de,$40AA            ; pointer to last 2 BCD digits of HI_SCORE\r
+224B: 18 AB         jr   $21F8\r
+\r
+; This displays player scores and high scores\r
+DISPLAY_ALL_SCORES:\r
+224D: 3D            dec  a\r
+224E: F5            push af\r
+224F: CD 31 22      call $2231\r
+2252: F1            pop  af\r
+2253: C8            ret  z\r
+2254: 18 F7         jr   $224D\r
+\r
+; \r
+; Expects:\r
+;\r
+; Value in register A       Action taken                     \r
+; ====================================================\r
+; 0                         Display player one's score       \r
+; 1                         Display player two's score\r
+;      \r
+; DE = pointer to *last* byte of 3 BCD bytes representing a score (ie: player 1 score, player 2 score)\r
+\r
+DECIDE_TO_DISPLAY_PLAYER_ONE_OR_PLAYER_TWO_SCORE:\r
+2256: DD 21 81 53   ld   ix,$5381            ; pointer to character RAM location for player one's score\r
+225A: A7            and  a                   ; test if A is 0.   \r
+225B: 28 04         jr   z,$2261             ; if A is 0 then we want to draw player one's score, goto $2261\r
+225D: DD 21 21 51   ld   ix,$5121            ; pointer to character RAM location for player two's score\r
+\r
+\r
+;\r
+; Plot score to the screen. \r
+;\r
+; Expects:\r
+;\r
+; DE = pointer to *last* byte of 3 BCD bytes representing a score (ie: player 1 score, player 2 score, or high score)\r
+;      See docs for PLAYER_ONE_SCORE to understand how scores are packed as BCD \r
+;\r
+; IX = pointer to character RAM to begin plotting characters from\r
+;\r
+;\r
+\r
+PLOT_SCORE_CHARACTERS:\r
+2261: 21 E0 FF      ld   hl,$FFE0            ; load HL with $FFE0 (-32 decimal) \r
+2264: EB            ex   de,hl               ; now HL = pointer to score bytes, DE = offset to add to screen address after plot     \r
+2265: 06 03         ld   b,$03               ; a score is 3 bytes in size..                \r
+2267: 0E 04         ld   c,$04               ; max number of leading zeros that can be skipped. For example,\r
+                                             ; when you start the game you have a score of zero. It renders as "00". \r
+                                             ; So this says "skip the first 4 zeros in the score, but display the rest"\r
+2269: 7E            ld   a,(hl)              ; read BCD digits from score byte\r
+226A: 0F            rrca                     ; move high nibble (first digit of BCD number)...\r
+226B: 0F            rrca\r
+226C: 0F            rrca\r
+226D: 0F            rrca                     ; into lower nibble (second digit). \r
+226E: CD 79 22      call $2279               ; call PLOT_LOWER_NIB_AS_DIGIT to plot the first digit                 \r
+2271: 7E            ld   a,(hl)\r
+2272: CD 79 22      call $2279               ; call PLOT_LOWER_NIB_AS_DIGIT to plot the second digit \r
+2275: 2B            dec  hl                  ; bump to *previous* BCD byte\r
+2276: 10 F1         djnz $2269               ; do until all BCD digits in score have been drawn\r
+2278: C9            ret\r
+\r
+\r
+; Pokes a digit of the score to character RAM.\r
+;\r
+; Expects:\r
+;\r
+; Lower nibble of A: BCD digit to be plotted as a character on screen \r
+; C = max number of leading zero digits in the score that can be skipped. \r
+; If C is 0, zero digits will always be drawn\r
+; IX = pointer to character RAM where digit will be plotted.\r
+\r
+PLOT_LOWER_NIB_AS_DIGIT:\r
+2279: E6 0F         and  $0F                 ; mask in lower nibble\r
+227B: 28 04         jr   z,$2281             ; if the lower nibble is zero, goto $2281\r
+\r
+; OK, we have a nonzero digit. \r
+227D: 0E 00         ld   c,$00               ; tell the plot routine to draw all digits, even if they are zero, from now on\r
+227F: 18 07         jr   $2288               ; go plot the character\r
+\r
+; we have a zero digit. Do we print it, or print a space instead?\r
+2281: 79            ld   a,c                 ; how many zero digits can we skip over?   \r
+2282: A7            and  a                   ; test if A is zero\r
+2283: 28 03         jr   z,$2288             ; if we can't skip over any more leading zero digits, then goto $2288 to draw "0". \r
+\r
+; Otherwise, we are skipping a leading "0" digit and will print an empty space in its stead..\r
+2285: 3E 80         ld   a,$80               ; when added to $90 this will produce $10 (16 decimal) - ordinal for empty character\r
+2287: 0D            dec  c                   ; decrement count of leading zeros we are allowed to ignore\r
+\r
+2288: C6 90         add  a,$90               ; transform A into ordinal of character to be plotted\r
+228A: DD 77 00      ld   (ix+$00),a          ; plot character for score to screen\r
+228D: DD 19         add  ix,de               ; now IX points to character directly above one just plotted\r
+228F: C9            ret\r
+\r
+\r
+;\r
+; Load DE with the [effective] address of the current player's score.\r
+; \r
+\r
+LEA_DE_OF_CURRENT_PLAYER_SCORE:\r
+2290: 11 A2 40      ld   de,$40A2            ; address of PLAYER_ONE_SCORE\r
+2293: 3A 0D 40      ld   a,($400D)           ; read CURRENT_PLAYER           \r
+2296: A7            and  a                   ; test if zero\r
+2297: C8            ret  z                   ; if it is zero, then current player is player one. Return.\r
+2298: 11 A5 40      ld   de,$40A5            ; address of PLAYER_TWO_SCORE\r
+229B: C9            ret\r
+\r
+\r
+\r
+\r
+\r
+AWARD_EXTRA_LIFE:\r
+229C: 3A 0D 40      ld   a,($400D)           ; load CURRENT_PLAYER into A\r
+229F: 21 AD 40      ld   hl,$40AD            ; load HL with address of PLAYER_ONE_AWARDED_EXTRA_LIFE flag.\r
+22A2: 85            add  a,l\r
+22A3: 6F            ld   l,a                 ; Now HL points to either PLAYER_ONE_AWARDED_EXTRA_LIFE or PLAYER_TWO_AWARDED_EXTRA_LIFE \r
+22A4: CB 46         bit  0,(hl)              ; Test if current player has already had a bonus live given to them.\r
+22A6: C0            ret  nz                  ; if flag is set, then return. Player gets no more extra lives.\r
+\r
+; player awarded extra life\r
+22A7: 36 01         ld   (hl),$01            ; Set "Player has had his extra life" flag\r
+22A9: 3E 01         ld   a,$01\r
+22AB: 32 C7 41      ld   ($41C7),a           ; set PLAY_EXTRA_LIFE_SOUND flag.\r
+22AE: 21 1D 42      ld   hl,$421D            ; pointer to PLAYER_LIVES\r
+22B1: 34            inc  (hl)                ; increment number of lives\r
+22B2: 46            ld   b,(hl)              ; read number of player lives into B\r
+\r
+\r
+\r
+DISPLAY_PLAYER_SHIPS_REMAINING:\r
+22B3: 21 9E 53      ld   hl,$539E            ; address in character RAM\r
+22B6: 0E 05         ld   c,$05\r
+22B8: 3A 00 42      ld   a,($4200)           ; read HAS_PLAYER_SPAWNED\r
+22BB: A7            and  a                   ; test if flag is set\r
+22BC: 28 03         jr   z,$22C1                       \r
+22BE: 05            dec  b\r
+22BF: 28 08         jr   z,$22C9\r
+22C1: 3E 66         ld   a,$66\r
+22C3: CD 93 25      call $2593               ; plot 2X2 characters\r
+22C6: 0D            dec  c\r
+22C7: 10 F8         djnz $22C1\r
+22C9: 0D            dec  c\r
+22CA: F8            ret  m\r
+22CB: CD 91 25      call $2591               ; plot spaces to screen\r
+22CE: 18 F9         jr   $22C9\r
+\r
+\r
+\r
+; Score values for aliens here....  see $21B1\r
+\r
+ALIEN_SCORE_TABLE:\r
+22D0: \r
+30 00 00            ; 30 PTS\r
+40 00 00            ; 40 PTS\r
+50 00 00            ; 50 PTS\r
+60 00 00            ; 60 PTS\r
+70 00 00            ; 70 PTS\r
+80 00 00            ; 80 PTS\r
+00 01 00            ; 100 PTS\r
+50 01 00            ; 150 PTS\r
+00 02 00            ; 200 PTS\r
+00 03 00            ; 300 PTS\r
+00 08 00            ; 800 PTS\r
+\r
+\r
+;\r
+; A = index of string to print\r
+;\r
+; Bit 6 set: scroll this text onto screen\r
+; Bit 7 set: clear this text\r
+;\r
+; Value in A (ANDed with $3F)       Text printed\r
+; =============================================================\r
+; 0                                 GAME OVER\r
+; 1                                 PUSH START BUTTON  \r
+; 2                                 PLAYER ONE \r
+; 3                                 PLAYER TWO \r
+; 4                                 HIGH SCORE\r
+; 5                                 CREDIT\r
+; 6                                 BONUS GALIXIP FOR   000 PTS\r
+; 7                                 CONVOY CHARGER \r
+; 8                                 - SCORE ADVANCE TABLE -\r
+; 9                                 MISSION: DESTROY ALIENS\r
+; A                                 WE ARE THE GALAXIANS\r
+; B                                 30       60  PTS \r
+; C                                 40       80  PTS\r
+; D                                 50      100  PTS\r
+; E                                 60      300  PTS\r
+; F                                 NAMCO logo\r
+; 10                                FREE PLAY\r
+\r
+PRINT_TEXT:\r
+22F1: 21 5C 23      ld   hl,$235C            ; HL = address of TEXTPTRS  \r
+22F4: 87            add  a,a                 ; A = A * 2.   This may affect C and PO flags. \r
+22F5: F5            push af      \r
+22F6: E6 3F         and  $3F                 ; mask in bits 0..5. Now A = a value in range of 0..63\r
+22F8: 5F            ld   e,a     \r
+22F9: 16 00         ld   d,$00               ; Extend A into DE \r
+22FB: 19            add  hl,de               ; HL now points to an entry in the TEXTPTRS lookup table.\r
+22FC: 5E            ld   e,(hl) \r
+22FD: 23            inc  hl\r
+22FE: 56            ld   d,(hl)              ; DE now holds a pointer to a character string to print. See docs @$235C  \r
+22FF: EB            ex   de,hl               ; HL = pointer to character string. DE we don't care about, it will be overwritten.                \r
+2300: 5E            ld   e,(hl)              \r
+2301: 23            inc  hl\r
+2302: 56            ld   d,(hl)              ; DE = *HL. Now DE holds character RAM address to print text at\r
+2303: 23            inc  hl\r
+2304: EB            ex   de,hl               ; Now HL = pointer to character RAM, DE = pointer to text to print\r
+2305: 01 E0 FF      ld   bc,$FFE0            ; offset to add to HL after every character write. (-32 in decimal)\r
+2308: F1            pop  af\r
+2309: 38 0E         jr   c,$2319\r
+230B: FA 23 23      jp   m,$2323             ; if minus flag is set, then we want to scroll text onto screen - goto $2323\r
+\r
+; HL = pointer to character RAM\r
+; DE = pointer to character to write\r
+230E: 1A            ld   a,(de)              ; read character to be drawn   \r
+230F: D6 30         sub  $30                 \r
+2311: FE 0F         cp   $0F                 ; is this the string terminator, #$3F?\r
+2313: C8            ret  z                   ; yes, so exit routine\r
+2314: 77            ld   (hl),a              ; write character to character RAM\r
+2315: 13            inc  de                  ; bump DE to point to next character\r
+2316: 09            add  hl,bc               ; Add offset to screen address so that next character is drawn at correct location.   \r
+2317: 18 F5         jr   $230E               ; and continue\r
+\r
+; I'll stick my neck out and guess this code is to erase text that was drawn previously.\r
+2319: 1A            ld   a,(de)\r
+231A: FE 3F         cp   $3F\r
+231C: C8            ret  z\r
+231D: 36 40         ld   (hl),$40\r
+231F: 13            inc  de\r
+2320: 09            add  hl,bc\r
+2321: 18 F6         jr   $2319\r
+\r
+\r
+\r
+;\r
+; Set text up for scrolling. Invoked by $230B within PRINT_TEXT\r
+;\r
+; HL = pointer to character RAM\r
+; DE = pointer to text string to render\r
+;\r
+\r
+2323: 22 B5 40      ld   ($40B5),hl          ; store pointer to character RAM in COLUMN_SCROLL_CHAR_RAM_PTR\r
+2326: EB            ex   de,hl               ; now HL = pointer to text string, DE = pointer to character RAM \r
+2327: 22 B3 40      ld   ($40B3),hl          ; store pointer to next char to scroll on in COLUMN_SCROLL_NEXT_CHAR_PTR\r
+232A: 7B            ld   a,e                 ; get low byte of character RAM address into A\r
+232B: E6 1F         and  $1F                 ; mask in bits 0..4. Effectively A = A mod #$20 (32 decimal). A now represents a column index from 0-31.\r
+232D: 47            ld   b,a                 ; save column index in B.\r
+; compute offset into OBJRAM_BACK_BUF\r
+232E: 87            add  a,a                 ; A=A*2. This is because attribute RAM requires 2 bytes per column. \r
+232F: C6 20         add  a,$20               ; add $20 (32 decimal) as OBJRAM_BACK_BUF starts at $4020\r
+2331: 6F            ld   l,a                 ; \r
+2332: 26 40         ld   h,$40               ; now HL = a pointer to scroll attribute value in OBJRAM_BACK_BUF \r
+2334: 22 B1 40      ld   ($40B1),hl          ; set COLUMN_SCROLL_ATTR_BACKBUF_PTR\r
+2337: E5            push hl                  ; save pointer to scroll offset attribute on the stack\r
+\r
+2338: CB 3B         srl  e\r
+233A: CB 3B         srl  e\r
+233C: 7A            ld   a,d                \r
+233D: E6 03         and  $03\r
+233F: 0F            rrca\r
+2340: 0F            rrca\r
+2341: B3            or   e\r
+2342: E6 F8         and  $F8\r
+2344: 4F            ld   c,a                 ; C = scroll offset to write to OBJRAM_BACK_BUF \r
+\r
+; we're going to clear this line ready for scrolling text on.\r
+2345: 21 00 50      ld   hl,$5000            ; HL = start of character RAM\r
+2348: 78            ld   a,b                 ; restore column index from B (see @$232D)\r
+2349: 85            add  a,l                 \r
+234A: 6F            ld   l,a                 ; Add column index to L. Now HL = pointer to column to clear\r
+234B: 11 20 00      ld   de,$0020            ; offset to add to HL. $20 (32 decimal) characters per row\r
+234E: 43            ld   b,e                 ; B = count of how many characters need to be cleared by DJNZ loop\r
+234F: 36 10         ld   (hl),$10            ; write empty space character\r
+2351: 19            add  hl,de               ; add offset to HL. Now HL points to same column next row down\r
+2352: 10 FB         djnz $234F\r
+2354: E1            pop  hl                  ; restore attribute pointer from the stack\r
+2355: 71            ld   (hl),c              ; write initial scroll offset to OBJRAM_BACK_BUF\r
+2356: 3E 01         ld   a,$01\r
+2358: 32 B0 40      ld   ($40B0),a           ; set IS_COLUMN_SCROLLING flag\r
+235B: C9            ret\r
+\r
+\r
+\r
+;\r
+; The TEXTPTRS table is a lookup table comprised of pointers to text strings.\r
+;\r
+; The text strings are always organised thus:\r
+;\r
+; First 2 bytes: pointer to character RAM to print text at.\r
+; Subsequent bytes: characters to print, terminated by #$3F (63 decimal)\r
+;\r
+; For example, lets take the first entry in the table, 7E 23.\r
+;\r
+; 7E 23 forms memory address $237E. \r
+\r
+; Note: I suggest you open a memory window in the MAME debugger and view 237E, it'll make this a lot easier to follow.\r
+;\r
+; The first 2 bytes stored at $237E are 96 and 52. This forms a character RAM address of $5296, where the first character will be drawn.\r
+; The subsequent bytes represent the string "GAME OVER" in (mostly) ASCII. The $3F after the "R" terminates the string.\r
+;\r
+\r
+\r
+TEXTPTRS:                                     \r
+235C: 7E 23                                  ; GAME OVER\r
+      8B 23                                  ; PUSH START BUTTON  \r
+      9F 23                                  ; PLAYER ONE \r
+      AC 23                                  ; PLAYER TWO \r
+      B9 23                                  ; HIGH SCORE\r
+      C6 23                                  ; CREDIT\r
+      D1 23                                  ; BONUS GALIXIP FOR   000 PTS  \r
+      EF 23                                  ; CONVOY CHARGER \r
+      01 24                                  ; - SCORE ADVANCE TABLE -\r
+      1B 24                                  ; MISSION: DESTROY ALIENS\r
+      35 24                                  ; WE ARE THE GALAXIANS\r
+      4C 24                                  ; 30       60  PTS \r
+      61 24                                  ; 40       80  PTS\r
+      76 24                                  ; 50      100  PTS\r
+      8B 24                                  ; 60      300  PTS\r
+      A0 24                                  ; NAMCO logo\r
+      AB 24                                  ; FREE PLAY\r
+\r
+237E: 96            \r
+237F: 52            \r
+\r
+2380:  47 41 4D 45 40 40 4F 56 45 52 3F F1 52 50 55 53  GAME@@OVER?.RPUS\r
+2390:  48 40 53 54 41 52 54 40 42 55 54 54 4F 4E 3F 94  H@START@BUTTON?.\r
+23A0:  52 50 4C 41 59 45 52 40 30 4E 45 3F 94 52 50 4C  RPLAYER@0NE?.RPL\r
+23B0:  41 59 45 52 40 54 57 4F 3F 80 52 48 49 47 48 40  AYER@TWO?.RHIGH@\r
+23C0:  53 43 4F 52 45 3F 7F 53 43 52 45 44 49 54 40 40  SCORE?.SCREDIT@@\r
+23D0:  3F 98 53 42 4F 4E 55 53 40 47 41 4C 41 58 49 50  ?.SBONUS@GALAXIP\r
+23E0:  40 46 4F 52 40 40 40 30 30 30 40 D0 D1 D2 3F D1  @FOR@@@000@...?.\r
+23F0:  52 43 4F 4E 56 4F 59 40 40 43 48 41 52 47 45 52  RCONVOY@@CHARGER\r
+2400:  3F 4F 53 5B 40 53 43 4F 52 45 40 41 44 56 41 4E  ?OS[@SCORE@ADVAN\r
+2410:  43 45 40 54 41 42 4C 45 40 5B 3F 69 53 4D 49 53  CE@TABLE@[?iSMIS\r
+2420:  53 49 4F 4E D3 40 44 45 53 54 52 4F 59 40 41 4C  SION.@DESTROY@AL\r
+2430:  49 45 4E 53 3F 27 53 57 45 40 41 52 45 40 54 48  IENS?'SWE@ARE@TH\r
+2440:  45 40 47 41 4C 41 58 49 41 4E 53 3F D9 52 40 40  E@GALAXIANS?.R@@\r
+2450:  33 30 40 40 40 40 40 40 40 36 30 40 40 D0 D1 D2  30@@@@@@@60@@...\r
+2460:  3F D7 52 40 40 34 30 40 40 40 40 40 40 40 38 30  ?.R@@40@@@@@@@80\r
+2470:  40 40 D0 D1 D2 3F D5 52 40 40 35 30 40 40 40 40  @@...?.R@@50@@@@\r
+2480:  40 40 31 30 30 40 40 D0 D1 D2 3F D3 52 40 40 36  @@100@@...?.R@@6\r
+2490:  30 40 40 40 40 40 40 33 30 30 40 40 D0 D1 D2 3F  0@@@@@@300@@...?\r
+24A0:  7C 52 CA CB CC CD CE CF 9E 9F 3F 7F 53 46 52 45  |R........?.SFRE\r
+24B0:  45 40 50 4C 41 59 3F A7 28 66 3D 28 2E 3D 28 08  E@PLAY?.(f=(.=(.\r
+24B6:  3F            \r
+\r
+\r
+\r
+;\r
+; Selects information to be displayed at the bottom of the screen.\r
+;\r
+; On entry:\r
+; A identifies what to be displayed.\r
+;\r
+; Value in A                Action taken                                                                 See also \r
+; ===============================================================================================================================================\r
+; 0                         The player advances to the next level and the red level flags are redrawn.   See: $2520 (DISPLAY_LEVEL_FLAGS)\r
+; 1                         Display FREE PLAY or CREDIT n at bottom left of screen.                      See: $24EB (DISPLAY_AVAILABLE_CREDIT)\r
+; 2                         Display BONUS GALAXIP FOR (nnnnn) PTS on screen.                             See: $24C8 (DISPLAY_BONUS_GALAXIP_FOR)\r
+; Any other value           Display player ships remaining at bottom left of screen.                     See: $22B3 (DISPLAY_PLAYER_SHIPS_REMAINING)\r
+;\r
+\r
+DISPLAY_BOTTOM_OF_SCREEN:\r
+24B7: A7            and  a                   ; test if parameter is zero\r
+24B8: 28 66         jr   z,$2520             ; if parameter is 0, goto $2520 (DISPLAY_LEVEL_FLAGS)\r
+24BA: 3D            dec  a\r
+24BB: 28 2E         jr   z,$24EB             ; if parameter was 1 then goto $24EB (DISPLAY_AVAILABLE_CREDIT)\r
+24BD: 3D            dec  a\r
+24BE: 28 08         jr   z,$24C8             ; if parameter was 2 then goto $24C8 (DISPLAY_BONUS_GALAXIP_FOR) \r
+24C0: 3A 1D 42      ld   a,($421D)           ; read number of lives for current player\r
+24C3: 47            ld   b,a\r
+24C4: CF            rst  $08                 ; assert that it's not GAME OVER (TODO: verify this)\r
+24C5: C3 B3 22      jp   $22B3               ; goto DISPLAY_PLAYER_SHIPS_REMAINING\r
+\r
+\r
+;\r
+; Displays the text string BONUS GALAXIP FOR (nnnnn) on screen.\r
+;\r
+\r
+DISPLAY_BONUS_GALIXIP_FOR:\r
+24C8: 3A AC 40      ld   a,($40AC)           ; read BONUS GALIXIP FOR value\r
+24CB: FE FF         cp   $FF                 ; check if there is any bonus. I think this code is redundant.\r
+24CD: C8            ret  z                   ; if no bonus, then return\r
+\r
+24CE: 3E 06         ld   a,$06               ; index of BONUS GALIXIP FOR 0000 PTS text string\r
+24D0: CD F1 22      call $22F1               ; display text on screen\r
+24D3: 3A AC 40      ld   a,($40AC)           ; read BONUS GALIXIP value\r
+24D6: E6 0F         and  $0F                 ; mask in low nibble\r
+24D8: 32 38 51      ld   ($5138),a           ; write value to character RAM\r
+24DB: 3A AC 40      ld   a,($40AC)           ; read BONUS GALIXIP value\r
+24DE: E6 F0         and  $F0                 ; mask in high nibble\r
+24E0: 20 01         jr   nz,$24E3            ; if it's !=0, goto $24E3\r
+24E2: 3C            inc  a\r
+24E3: 0F            rrca                     ; move high nibble...\r
+24E4: 0F            rrca\r
+24E5: 0F            rrca\r
+24E6: 0F            rrca                     ; ... into lower nibble, so that A is now a number from 0..9\r
+24E7: 32 58 51      ld   ($5158),a           ; and POKE number to screen RAM, displaying single digit in correct place\r
+24EA: C9            ret                      ; we're out\r
+\r
+\r
+;\r
+; Displays either FREE PLAY or CREDIT (n) at bottom left of screen\r
+;\r
+\r
+DISPLAY_AVAILABLE_CREDIT:\r
+24EB: 3A 06 40      ld   a,($4006)           ; read IS_GAME_IN_PLAY flag           \r
+24EE: 0F            rrca                     ; move flag into carry\r
+24EF: D8            ret  c                   ; if the game is in play, return.\r
+24F0: 3A 11 40      ld   a,($4011)           ; read PORT_STATE_6800\r
+24F3: E6 C0         and  $C0                 ; mask in dip switch 1 & 2 state\r
+24F5: FE C0         cp   $C0                 ; are both dip switches on?\r
+24F7: 3E 10         ld   a,$10               ; index of text string "FREE PLAY"\r
+24F9: CA F1 22      jp   z,$22F1             ; call PRINT_TEXT\r
+\r
+; if we get here, then we're not in FREE PLAY mode. We will display number of credits on screen.\r
+24FC: 3E 05         ld   a,$05               ; index of text string "CREDIT"\r
+24FE: CD F1 22      call $22F1               ; call PRINT_TEXT\r
+2501: 3A 02 40      ld   a,($4002)           ; read number of credits\r
+2504: FE 63         cp   $63                 ; compare to 99 decimal\r
+2506: 38 02         jr   c,$250A             ; if A <99 then goto $250A\r
+2508: 3E 63         ld   a,$63               ; clamp number of credits to 99\r
+250A: CD 69 25      call $2569               ; call CONVERT_A_TO_BCD. Now A = BCD equivalent of what it was\r
+250D: 47            ld   b,a                 ; save credits as BCD in B\r
+250E: E6 F0         and  $F0                 ; mask in high nibble, which is first digit of BCD\r
+2510: 28 07         jr   z,$2519             ; if the first digit is 0, goto $2519. We don't display it.\r
+2512: 0F            rrca                     ; shift high nibble...\r
+2513: 0F            rrca\r
+2514: 0F            rrca\r
+2515: 0F            rrca                     ; to low nibble.. converting first BCD digit to decimal.\r
+2516: 32 9F 52      ld   ($529F),a           ; Write first digit of credits to character RAM\r
+2519: 78            ld   a,b                 ; get credits as BCD into A again. We preserved it in B @$250D\r
+251A: E6 0F         and  $0F                 ; mask in low nibble, which is second digit of BCD. Converts second BCD digit to decimal.\r
+251C: 32 7F 52      ld   ($527F),a           ; Write second digit of credits to character RAM \r
+251F: C9            ret                      \r
+\r
+\r
+;\r
+; Called when the player has completed the level.\r
+;\r
+; This routine:\r
+;    Resets the swarm tempo;\r
+;    increments the player level (48 levels maximum);\r
+;    Draws level flags. \r
+;\r
+\r
+DISPLAY_LEVEL_FLAGS:\r
+2520: CF            rst  $08                 ; assert that it's not GAME OVER (TODO: verify this)\r
+2521: 3A 20 42      ld   a,($4220)           ; read HAVE_NO_ALIENS_IN_SWARM\r
+2524: A7            and  a                   ; test if flag is set\r
+2525: 28 05         jr   z,$252C             ; if flag is not set, goto $252C\r
+2527: 3E 01         ld   a,$01\r
+2529: 32 D0 41      ld   ($41D0),a           ; set RESET_SWARM_SOUND_TEMPO flag to 1. The swarm tempo will be slow again. \r
+\r
+252C: 3A 1C 42      ld   a,($421C)           ; read PLAYER_LEVEL.\r
+252F: 3C            inc  a                   ; increment it.\r
+2530: FE 30         cp   $30                 ; Compare to #$30 (48 decimal)\r
+2532: 38 02         jr   c,$2536             ; if A < 48, goto $2536\r
+2534: 3E 30         ld   a,$30               ; Level 48 is the limit.\r
+; A = level number (0-48)\r
+2536: CD 69 25      call $2569               ; convert A to BCD. Now A = BCD equivalent \r
+\r
+; A = level number in BCD            \r
+2539: F5            push af\r
+253A: 21 7E 50      ld   hl,$507E            ; address in character RAM to start drawing flags at\r
+253D: E6 F0         and  $F0                 ; mask in high nibble\r
+253F: 28 10         jr   z,$2551             ; if the high nibble is zero, then goto $2551\r
+\r
+; Calculate how many "10" flags we are going to draw\r
+2541: 0F            rrca                     ; shift bits in high nibble of BCD number....\r
+2542: 0F            rrca\r
+2543: 0F            rrca\r
+2544: 0F            rrca                     ; to lower nibble.\r
+2545: 47            ld   b,a                 ; B now holds the number of red "10" flags to draw at the bottom right of the screen.\r
+2546: 0E 10         ld   c,$10               ; C is a count of how much space, in characters, we have to plot flags. We start with #$10 (16 decimal) \r
+2548: 3E 68         ld   a,$68               ; ordinal of first character of "10" flag to plot\r
+254A: CD 85 25      call $2585               ; plot the flag with "10" on it\r
+254D: 0D            dec  c                   ; A "10" flag takes up 2 spaces..\r
+254E: 0D            dec  c                   ; ..so reduce C by 2.\r
+254F: 10 F7         djnz $2548               ; repeat until B==0\r
+2551: F1            pop  af\r
+\r
+; Calculate how many normal red flags we are going to draw\r
+; A=level number in BCD\r
+2552: E6 0F         and  $0F                 ; mask in lower nibble of BCD number. Now A represents how many flags we are going to draw.\r
+2554: 47            ld   b,a                 ; B = number of flags to draw\r
+2555: 11 1F 00      ld   de,$001F            ; offset to add to HL after every flag drawn.\r
+2558: 28 08         jr   z,$2562             ; if we don't have any flags to draw then goto $2562\r
+255A: 3E 6C         ld   a,$6C               ; ordinal of first character to plot\r
+255C: CD A0 25      call $25A0               ; draw the normal flag on character map\r
+255F: 0D            dec  c                   ; A normal flag takes up just 1 character space, so reduce C by 1\r
+2560: 10 F8         djnz $255A               ; repeat until B == 0\r
+\r
+; if we get here, we want to erase any flags left from the previous level\r
+2562: 0D            dec  c                   ; decrement "space for characters remaining" count in C\r
+2563: F8            ret  m                   ; return if c has become a negative value. \r
+2564: CD 9E 25      call $259E               ; plot spaces to overwrite any existing flags\r
+2567: 18 F9         jr   $2562\r
+\r
+\r
+;\r
+; Convert value in register A to BCD equivalent \r
+; \r
+; For example, if you pass in $63 (99 decimal) in A, this function will return 99 BCD\r
+; \r
+; Expects:\r
+; A = non BCD value, from 0..99\r
+;\r
+; Returns:\r
+; A = BCD equivalent\r
+; \r
+; Thanks to Slavo Labsky for his help deciphering the strange "add a,$00" instruction, which resets the half-carry flag.  \r
+\r
+CONVERT_A_TO_BCD:\r
+2569: 47            ld   b,a                 ; preserve A in B register\r
+256A: E6 0F         and  $0F                 ; mask in low nibble\r
+256C: C6 00         add  a,$00               ; clears the half carry flag which might affect DAA\r
+256E: 27            daa\r
+256F: 4F            ld   c,a                 ; store result in C\r
+2570: 78            ld   a,b                 ; restore A to its original value\r
+2571: E6 F0         and  $F0                 ; mask in high nibble\r
+2573: 28 0B         jr   z,$2580             ; if high nibble is zero we don't care, goto $2580\r
+2575: 0F            rrca                     ; shift high nibble...\r
+2576: 0F            rrca\r
+2577: 0F            rrca\r
+2578: 0F            rrca                     ; ... into lower nibble\r
+2579: 47            ld   b,a                 ; and store in B.\r
+; Final product is (B * 16 ) + C\r
+257A: AF            xor  a                   ; clear A\r
+257B: C6 16         add  a,$16               ; Add 16 hex (which in BCD terms is 16 decimal) to A  (so A will progress in BCD from 0->16->32->48... )\r
+257D: 27            daa\r
+257E: 10 FB         djnz $257B               ; and repeat until B is 0.\r
+2580: 81            add  a,c                 ; add in value of lower nibble preserved @$256f  \r
+2581: 27            daa                      ; ensure A is a valid BCD number\r
+2582: C9            ret                      ; and we're out\r
+\r
+\r
+\r
+2583: 3E 2C         ld   a,$2C               ; space character\r
+\r
+;\r
+; Draw 4 characters in a 2 x 2 layout. \r
+;\r
+; register A is the ordinal of the first character to draw. \r
+; The next 3 characters are derived automatically by incrementing A after each character drawn.\r
+;\r
+; Expects:\r
+; A = ordinal of first character to poke to character RAM. \r
+; HL = pointer to character RAM address\r
+; \r
+; Resulting layout is:\r
+;\r
+; A   |  A+1\r
+; ----------\r
+; A+2 |  A+3\r
+;\r
+\r
+PLOT_CHARACTERS_2_BY_2_ASCENDING:\r
+2585: D5            push de\r
+2586: 11 1F 00      ld   de,$001F            ; load de with 31 decimal. This is the width of a row, in characters, minus 1.\r
+2589: CD A0 25      call $25A0               ; plot 2 characters on same row... \r
+258C: CD A0 25      call $25A0               ; and 2 characters on the next row \r
+258F: D1            pop  de\r
+2590: C9            ret\r
+\r
+\r
+;\r
+; Draw 4 characters in a 2 x 2 layout. \r
+;\r
+; register A is the ordinal of the first character to draw. \r
+;\r
+; Expects:\r
+; A = ordinal of first character to poke to character RAM. \r
+; HL = pointer to character RAM address\r
+; \r
+; Resulting layout is:\r
+;\r
+; A-2 |  A-1\r
+; ----------\r
+; A   |  A+1\r
+\r
+PLOT_CHARACTERS_2_BY_2_DESCENDING:\r
+2591: 3E 2E         ld   a,$2E\r
+2593: D5            push de\r
+2594: 11 DF FF      ld   de,$FFDF            ; load de with -33 decimal as signed word\r
+2597: CD A0 25      call $25A0               ; plot 2 characters on one row..\r
+259A: C6 FC         add  a,$FC               ; subtract 4 from A\r
+259C: 18 EE         jr   $258C               ; plot 2 characters on row above\r
+\r
+\r
+259E: 3E 2C         ld   a,$2C               ; space character\r
+\r
+\r
+;\r
+; Plots 2 contiguous characters on same row\r
+; register A is the ordinal of the first character to draw. A+1 is drawn in the next column.\r
+;\r
+; Expects:\r
+; A = ordinal of first character to plot\r
+; HL = pointer to character RAM where first character will be plotted\r
+; DE = offset to add to HL after both characters have been plotted\r
+;\r
+; Returns:\r
+; HL = updated pointer to character RAM\r
+;\r
+\r
+PLOT_TWO_CHARS_ON_SAME_ROW:\r
+25A0: 77            ld   (hl),a              ; plot first character\r
+25A1: 3C            inc  a                   ; increment A\r
+25A2: 23            inc  hl                  ; bump HL to next address in RAM\r
+25A3: 77            ld   (hl),a              ; plot second character\r
+25A4: 3C            inc  a\r
+25A5: 19            add  hl,de               ; add offset in DE to HL\r
+25A6: C9            ret\r
+\r
+\r
+25A7: 3E 2C         ld   a,$2C               ; space character\r
+\r
+;\r
+; Plots 2 characters in the same column, one beneath the other.\r
+;\r
+; register A is the ordinal of the first character to draw. A+2 is drawn in the same column of the row beneath.\r
+;\r
+; Expects:\r
+; A = ordinal of first character to plot\r
+; HL = pointer to character RAM where first character will be plotted\r
+;\r
+\r
+PLOT_TWO_CHARACTERS_IN_SAME_COLUMN:\r
+25A9: D5            push de\r
+25AA: 11 20 00      ld   de,$0020            ; each row is comprised of $20 (32 decimal) characters...\r
+25AD: 77            ld   (hl),a              ; plot first character        \r
+25AE: C6 02         add  a,$02\r
+25B0: 19            add  hl,de               ; bump HL to point to the character at the row beneath\r
+25B1: 77            ld   (hl),a              ; plot second character\r
+25B2: D1            pop  de\r
+25B3: C9            ret\r
+\r
+; From 25B4 - 3fff, it's just NOPs\r
index a78a946..b936d44 100644 (file)
 #include "stty_sane.h"
 #include "z80/z80.h"
 
-#define PACMAN 1
+#define GALAXIAN 1
+#define PACMAN 0
 #define APPLE 0
 
-#if PACMAN
+#if GALAXIAN
+#define GALAXIAN_WIDTH 224
+#define GALAXIAN_HEIGHT 256
+
+#define PADDED_WIDTH GALAXIAN_WIDTH
+#define PADDED_HEIGHT GALAXIAN_HEIGHT
+
+#define WINDOW_X_SCALE 3
+#define WINDOW_Y_SCALE 3
+#elif PACMAN
 #define PACMAN_WIDTH 224
 #define PACMAN_HEIGHT 288
 
 #define CYCLES_PER_SAMPLE 34 // 44100 Hz @ 1.5 MHz
 #define SAMPLES_PER_UPDATE 500
 
-#if PACMAN
+#if GALAXIAN
 #define INT_VECTOR 0
 
+#define RAM_START 0x4000
+#define TILE_RAM 0x5000
+#define PALETTE_RAM 0x5400
+#define SPRITE_RAM 0x5800
+
+#define IO_PAGE 0x6000
+#elif PACMAN
+#define INT_VECTOR 0
+
+#define RAM_START 0x4000
 #define TILE_RAM 0x4000
 #define PALETTE_RAM 0x4400
 #define SPRITE_CONFIG 0x4ff0
 
 #define TRACE 0
 #define IO_TRACE 0
-#define MEM_TRACE 0
+#define MEM_TRACE 1
 
 extern char **environ;
 
@@ -161,7 +181,7 @@ uint32_t frame[WINDOW_HEIGHT][WINDOW_WIDTH];
 
 // see palette.py
 uint32_t palette[0x10] = {
-#if PACMAN
+#if GALAXIAN || PACMAN
   // see https://www.lomont.org/software/games/pacman/PacmanEmulation.pdf
   0xff000000,
   0xffff0000,
@@ -204,7 +224,17 @@ uint32_t mono_palette[2] = {0xff000000, 0xffffffff};
 
 z80 cpu;
 
-#if PACMAN
+#if GALAXIAN
+#define ROM_GALMIDW_U_ADDR 0
+#define ROM_GALMIDW_V_ADDR 0x800
+#define ROM_GALMIDW_W_ADDR 0x1000
+#define ROM_GALMIDW_Y_ADDR 0x1800
+#define ROM_7L_ADDR 0x2000
+#define ROM_GALAXIAN_CLR_ADDR 0x6f00
+#define ROM_1H_ADDR 0x7000
+#define ROM_1K_ADDR 0x7800
+#define MEM_SIZE 0x8000
+#elif PACMAN
 #define ROM_PACMAN_6E_ADDR 0
 #define ROM_PACMAN_6F_ADDR 0x1000
 #define ROM_PACMAN_6H_ADDR 0x2000
@@ -269,15 +299,21 @@ uint8_t usleep_lo;
 uint8_t dos_lo;
 int usleep_count, exit_flag;
 
-#if PACMAN
+#if GALAXIAN
 uint8_t int_vector;
 
-// Sprite #, x and y flip bits (write only). 8 pairs of 2 bytes:
-// First byte is sprite number 0-31, (bits 2-7), X flip (bit1), Y flip(bit 0)
-// Second byte is sprite palette
-uint8_t sprite_config[0x10];
+// just for now until we have galaxian audio
+#define C0X0_SOFT_SWITCH_TAPEOUT 4
+#define C0X0_SOFT_SWITCH_SPKR 8
+uint8_t c0x0_soft_switches = 0;
 
-// Sprite x, y coordinates
+// every n cycles, sample the tape and speaker outputs to here
+#define C0X0_SOFT_SWITCHES_BUF_SIZE 0x400 // next power of 2 >= 441 * 2
+int c0x0_soft_switches_buf_head;
+int c0x0_soft_switches_buf_count;
+uint8_t c0x0_soft_switches_buf[C0X0_SOFT_SWITCHES_BUF_SIZE];
+#elif PACMAN
+uint8_t int_vector;
 uint8_t sprite_coords[0x10];
 
 // just for now until we have pacman audio
@@ -626,7 +662,7 @@ void dos(char *line) {
 }
 
 uint8_t rb(void *userdata, uint16_t addr0) {
-#if PACMAN
+#if GALAXIAN || PACMAN
   int addr = addr0;
 
   if (addr < IO_PAGE) {
@@ -996,10 +1032,10 @@ uint8_t rb(void *userdata, uint16_t addr0) {
 }
 
 void wb(void *userdata, uint16_t addr0, uint8_t val) {
-#if PACMAN
+#if GALAXIAN || PACMAN
   int addr = addr0;
 
-  if (addr < TILE_RAM)
+  if (addr < RAM_START)
     ;
   else if (addr < IO_PAGE) {
 #if MEM_TRACE
@@ -1007,16 +1043,13 @@ void wb(void *userdata, uint16_t addr0, uint8_t val) {
     fprintf(stderr, "pc=%04x addr=%04x wr=%02x\n", pc, addr, val);
 #endif
     mem[addr] = val;
-
-    // RAM needs to be overlaid on sprite config for memory test to work
-    if (addr >= SPRITE_CONFIG)
-      sprite_config[addr & 0xf] = val;
   }
   else if (addr < IO_PAGE + 0x100) {
 #if MEM_TRACE
     int pc = cpu.pc;
     fprintf(stderr, "pc=%04x addr=%04x wr=%02x (io)\n", pc, addr, val);
 #endif
+#if PACMAN
     switch (addr) {
     case SPRITE_COORDS: // 0x5060
     case SPRITE_COORDS | 1: // 0x5061
@@ -1037,10 +1070,13 @@ void wb(void *userdata, uint16_t addr0, uint8_t val) {
       sprite_coords[addr & 0xf] = val;
       break;
     }
+#endif
   }
 #if MEM_TRACE
-  int pc = cpu.pc;
-  fprintf(stderr, "pc=%04x addr=%04x wr=%02x (nop)\n", pc, addr, val);
+  else {
+    int pc = cpu.pc;
+    fprintf(stderr, "pc=%04x addr=%04x wr=%02x (nop)\n", pc, addr, val);
+  }
 #endif
 #else
   // for joystick, for now count memory accesses as proxy for cycles
@@ -1303,7 +1339,7 @@ void out(z80 *const z, uint8_t port, uint8_t val) {
 #endif
 
   switch (port) {
-#if PACMAN
+#if GALAXIAN || PACMAN
   case INT_VECTOR:
     int_vector = val;
     break;
@@ -1387,7 +1423,7 @@ int main(int argc, char **argv) {
   bool timing = false;
   while (argn < argc) {
     if (strcmp(argv[argn], "--help") == 0) {
-#if PACMAN
+#if GALAXIAN || PACMAN
       printf("usage: %s [--timing] [program.obj[,aNNNN] ...] [-- child-executable [child-argument ...]]\n", argv[0]);
 #else // APPLE
       printf("usage: %s [--cg-rom=file.bin] [--video-rom=file.bin] [--NN-rom=file.bin ...] [--timing] [program.obj[,aNNNN] ...] [-- child-executable [child-argument ...]]\n", argv[0]);
@@ -1938,7 +1974,114 @@ int main(int argc, char **argv) {
       SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0xff);
       SDL_RenderClear(renderer);
 
-#if PACMAN
+#if GALAXIAN
+      // send z80 a vertical refresh interrupt
+      cpu.int_pending = true;
+      cpu.int_data = int_vector;
+
+      // draw tiles
+      for (int i = 0; i < 32; ++i)
+        for (int j = 0; j < 28; ++j) {
+          int offset = i + ((27 - j) << 5);
+          int tile_base = 7 | (mem[TILE_RAM | offset] << 3);
+          int tile_base0 = tile_base | ROM_1H_ADDR;
+          int tile_base1 = tile_base | ROM_1K_ADDR;
+          int palette_base =
+            ((mem[PALETTE_RAM | offset] & 0x3f) << 2) | ROM_GALAXIAN_CLR_ADDR;
+          for (int k = 0; k < 8; ++k) {
+            int y = ((i << 3) | k) * WINDOW_Y_SCALE;
+            for (int l = 0; l < 8; ++l) {
+              int data0 = mem[l ^ tile_base0] << (k & 7);
+              int data1 = mem[l ^ tile_base1] << (k & 7);
+              uint32_t rgb = palette[
+                mem[
+                  ((data0 & 0x80) >> 7) | ((data1 & 0x80) >> 6) | palette_base
+                ] & 0xf
+              ];
+              int x = ((j << 3) | l) * WINDOW_X_SCALE;
+              for (int m = 0; m < WINDOW_Y_SCALE; ++m)
+                for (int n = 0; n < WINDOW_X_SCALE; ++n)
+                  frame[y + m][x + n] = rgb;
+            }
+          }
+        }
+
+      // draw sprites
+      // from MAME /src/mame/video/galaxian.cpp:
+      //   spriteram[0x40] = vertical position of sprite 0
+      //   spriteram[0x41] = picture number and H/V flip of sprite 0
+      //   spriteram[0x42] = color of sprite 0
+      //   spriteram[0x43] = horizontal position of sprite 0
+      //   ...
+      //   spriteram[0x61] = vertical position of shell 0
+      //   spriteram[0x63] = horizontal count until shell 0 starts rendering
+      //   ...
+      for (int i = 0; i < 8; ++i) {
+        int data = mem[0x41 | (i << 2) | SPRITE_RAM];
+        bool x_flip = (data >> 6) & 1;
+        bool y_flip = (data >> 7) & 1;
+        int sprite_base =
+          (!x_flip * 0x17) |
+          (y_flip * 0x28) |
+          ((data & 0x3f) << 5);
+        int sprite_base0 = sprite_base | ROM_1H_ADDR;
+        int sprite_base1 = sprite_base | ROM_1K_ADDR;
+        int palette_base =
+          ((mem[0x42 | (i << 2) | SPRITE_RAM] & 7) << 2) |
+          ROM_GALAXIAN_CLR_ADDR;
+        int x = GALAXIAN_WIDTH + 15 - mem[0x43 | (i << 2) | SPRITE_RAM];
+        int y = GALAXIAN_HEIGHT - mem[0x40 | (i << 2) | SPRITE_RAM];
+        for (int j = 0; j < 16; ++j) {
+          int y1 = y + j;
+          if (y1 >= 0 && y1 < GALAXIAN_HEIGHT) {
+            y1 *= WINDOW_Y_SCALE;
+            for (int k = 0; k < 16; ++k) {
+              int x1 = x + k;
+              if (x1 >= 0 && x1 < GALAXIAN_WIDTH) {
+                if (!y_flip) {
+                  int addr = (k & 7) ^ (j & 8) ^ ((k & 8) << 1);
+                  int shift = j & 7;
+                  int data0 = mem[addr ^ sprite_base0] << shift;
+                  int data1 = mem[addr ^ sprite_base1] << shift;
+                  if ((data0 | data1) & 0x80) {
+                    uint32_t rgb = palette[
+                      mem[
+                        ((data0 & 0x80) >> 7) |
+                        ((data1 & 0x80) >> 6) |
+                        palette_base
+                      ] & 0xf
+                    ];
+                    x1 *= WINDOW_X_SCALE;
+                    for (int l = 0; l < WINDOW_Y_SCALE; ++l)
+                      for (int m = 0; m < WINDOW_X_SCALE; ++m)
+                        frame[y1 + l][x1 + m] = rgb;
+                  }
+                }
+                else {
+                  int addr = (k & 7) ^ (j & 8) ^ ((k & 8) << 1);
+                  int shift = j & 7;
+                  int data0 = mem[addr ^ sprite_base0] >> shift;
+                  int data1 = mem[addr ^ sprite_base1] >> shift;
+                  if ((data0 | data1) & 1) {
+                    uint32_t rgb = palette[
+                      mem[
+                        ((data0 & 0x80) >> 7) |
+                        ((data1 & 0x80) >> 6) |
+                        palette_base
+                      ] & 0xf
+                    ];
+                    x1 *= WINDOW_X_SCALE;
+                    for (int l = 0; l < WINDOW_Y_SCALE; ++l)
+                      for (int m = 0; m < WINDOW_X_SCALE; ++m)
+                        frame[y1 + l][x1 + m] = rgb;
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+#elif PACMAN
       // send z80 a vertical refresh interrupt
       cpu.int_pending = true;
       cpu.int_data = int_vector;
@@ -1953,16 +2096,16 @@ int main(int argc, char **argv) {
                 0x3a0 - 2 + i - (j << 5) :
                 0x1d - (34 << 5) + (i << 5) - j;
           int tile_base =
-            ROM_PACMAN_5E_ADDR | (mem[TILE_RAM | offset] << 4) | 0xf;
+            0xf | (mem[TILE_RAM | offset] << 4) | ROM_PACMAN_5E_ADDR;
           int palette_base =
-            ROM_82S126_4A_ADDR | ((mem[PALETTE_RAM | offset] & 0x3f) << 2);
+            ((mem[PALETTE_RAM | offset] & 0x3f) << 2) | ROM_82S126_4A_ADDR;
           for (int k = 0; k < 8; ++k) {
             int y = ((i << 3) | k) * WINDOW_Y_SCALE;
             for (int l = 0; l < 8; ++l) {
-              int data = mem[tile_base ^ ((k & 4) << 1) ^ l] << (k & 3);
+              int data = mem[l ^ ((k & 4) << 1) ^ tile_base] << (k & 3);
               uint32_t rgb = palette[
                 mem[
-                  palette_base | ((data & 8) >> 3) | ((data & 0x80) >> 6)
+                  ((data & 8) >> 3) | ((data & 0x80) >> 6) | palette_base
                 ] & 0xf
               ];
               int x = ((j << 3) | l) * WINDOW_X_SCALE;
@@ -1975,18 +2118,19 @@ int main(int argc, char **argv) {
 
       // draw sprites
       for (int i = 0; i < 8; ++i) {
-        int data = sprite_config[i << 1];
+        int data = mem[(i << 1) | SPRITE_CONFIG];
         bool y_flip = data & 1;
         bool x_flip = (data >> 1) & 1;
-        int sprite_base = 
-          ROM_PACMAN_5F_ADDR |
-          ((data & 0xfc) << 4) |
+        int sprite_base =
           (!x_flip * 0x27) |
-          (y_flip * 0x18);
+          (y_flip * 0x18) |
+          ((data & 0xfc) << 4) |
+          ROM_PACMAN_5F_ADDR;
         int palette_base =
-          ROM_82S126_4A_ADDR | ((sprite_config[(i << 1) | 1] & 0x3f) << 2);
-        int x = PACMAN_WIDTH + 16 - sprite_coords[i << 1];
-        int y = PACMAN_HEIGHT - 16 - sprite_coords[(i << 1) | 1];
+          ((mem[1 | (i << 1) | SPRITE_CONFIG] & 0x3f) << 2) |
+          ROM_82S126_4A_ADDR;
+        int x = PACMAN_WIDTH + 15 - sprite_coords[i << 1];
+        int y = PACMAN_HEIGHT - 16 - sprite_coords[1 | (i << 1)];
         for (int j = 0; j < 16; ++j) {
           int y1 = y + j;
           if (y1 >= 0 && y1 < PACMAN_HEIGHT) {
@@ -1996,15 +2140,17 @@ int main(int argc, char **argv) {
               if (x1 >= 0 && x1 < PACMAN_WIDTH) {
                 if (!y_flip) {
                   int data = mem[
-                    sprite_base ^
-                    (((j + 4) & 0xc) << 1) ^
                     (k & 7) ^
-                    ((k & 8) << 2)
+                    (((j + 4) & 0xc) << 1) ^
+                    ((k & 8) << 2) ^
+                    sprite_base
                   ] << (j & 3);
                   if (data & 0x88) {
                     uint32_t rgb = palette[
                       mem[
-                        palette_base | ((data & 8) >> 3) | ((data & 0x80) >> 6)
+                        ((data & 8) >> 3) |
+                        ((data & 0x80) >> 6) |
+                        palette_base
                       ] & 0xf
                     ];
                     x1 *= WINDOW_X_SCALE;
@@ -2015,15 +2161,17 @@ int main(int argc, char **argv) {
                 }
                 else {
                   int data = mem[
-                    sprite_base ^
-                    (((j - 4) & 0xc) << 1) ^
                     (k & 7) ^
-                    ((k & 8) << 2)
+                    (((j - 4) & 0xc) << 1) ^
+                    ((k & 8) << 2) ^
+                    sprite_base
                   ] >> (j & 3);
                   if (data & 0x11) {
                     uint32_t rgb = palette[
                       mem[
-                        palette_base | (data & 1) | ((data & 0x10) >> 3)
+                        (data & 1) |
+                        ((data & 0x10) >> 3) |
+                        palette_base
                       ] & 0xf
                     ];
                     x1 *= WINDOW_X_SCALE;
index e11c8c0..9c5d053 100644 (file)
@@ -1,4 +1,5 @@
-PACMAN = 1
+GALAXIAN = 1
+PACMAN = 0
 APPLE = 0
 
 STDIN_DATA = 0xf0
@@ -11,6 +12,11 @@ USLEEP_LO = 0xf6
 USLEEP_HI = 0xf7
 SYS_EXIT = 0xf8
 
+.if GALAXIAN
+TILE_RAM = 0x5000
+PALETTE_RAM = 0x5400
+SPRITE_RAM = 0x5800
+.else
 .if PACMAN
 TILE_RAM = 0x4000
 PALETTE_RAM = 0x4400
@@ -37,15 +43,33 @@ HW_SETHIRES = 0xe057
 HW_SETDHIRES = 0xe05e
 HW_SETIOUDIS = 0xe07e
 HW_CLRIOUDIS = 0xe07f
+.endif
 .endif
 
        .area   text
 
-.if PACMAN
-       sub     a
-       ld      (TILE_RAM | 0x3dd),a
-       inc     a
-       ld      (PALETTE_RAM | 0x3dd),a
+.if GALAXIAN | PACMAN
+       ld      hl,TILE_RAM
+       ld      de,PALETTE_RAM
+       ld      bc,-0x400
+0$:    ld      (hl),0x10 ;l
+       ld      a,h
+       sub     >TILE_RAM
+       ld      (de),a
+       inc     hl
+       inc     de
+       inc     c
+       jr      nz,0$
+       inc     b
+       jr      nz,0$
+
+       ld      hl,0x40 | SPRITE_RAM
+       ld      a,7
+       ld      b,0x20
+1$:    ld      (hl),a
+       add     a,5
+       inc     hl
+       djnz    1$
 .else ; APPLE
        ld      (HW_CLR80COL),a
        ld      a,(HW_CLRIOUDIS)
diff --git a/galaxian/Makefile b/galaxian/Makefile
new file mode 100644 (file)
index 0000000..346aafa
--- /dev/null
@@ -0,0 +1,62 @@
+# need to install intelhex package in Python first:
+#   pip3 install --user intelhex
+BIN2HEX=bin2hex.py
+HEX2BIN=hex2bin.py
+
+# some ROMs are loaded into emulator addresses not accessible by software,
+# this is for convenience so we can build entire game from one source file
+ROM_GALMIDW_U_ADDR=0
+ROM_GALMIDW_V_ADDR=0x800
+ROM_GALMIDW_W_ADDR=0x1000
+ROM_GALMIDW_Y_ADDR=0x1800
+ROM_7L_ADDR=0x2000
+ROM_GALAXIAN_CLR_ADDR=0x6f00
+ROM_1H_ADDR=0x7000
+ROM_1K_ADDR=0x7800
+
+ROMS= \
+galmidw.u \
+galmidw.v \
+galmidw.w \
+galmidw.y \
+7l \
+galaxian.clr \
+1h \
+1k
+
+all: galaxian0.ihx
+
+galaxian0.ihx: ${ROMS:%=%.ihx}
+       hexmerge.py -o $@ $^
+
+galmidw.u.ihx: galmidw.u
+       ${BIN2HEX} --offset=${ROM_GALMIDW_U_ADDR} $< $@
+
+galmidw.v.ihx: galmidw.v
+       ${BIN2HEX} --offset=${ROM_GALMIDW_V_ADDR} $< $@
+
+galmidw.w.ihx: galmidw.w
+       ${BIN2HEX} --offset=${ROM_GALMIDW_W_ADDR} $< $@
+
+galmidw.y.ihx: galmidw.y
+       ${BIN2HEX} --offset=${ROM_GALMIDW_Y_ADDR} $< $@
+
+7l.ihx: 7l
+       ${BIN2HEX} --offset=${ROM_7L_ADDR} $< $@
+
+galaxian.clr.ihx: galaxian.clr
+       ${BIN2HEX} --offset=${ROM_GALAXIAN_CLR_ADDR} $< $@
+
+1h.ihx: 1h
+       ${BIN2HEX} --offset=${ROM_1H_ADDR} $< $@
+
+1k.ihx: 1k
+       ${BIN2HEX} --offset=${ROM_1K_ADDR} $< $@
+
+${ROMS}: ../orig/galaxian.zip
+       rm -f ${ROMS}
+       unzip $<
+       touch ${ROMS}
+
+clean:
+       rm -f *.ihx ${ROMS}
index a718401..757c7a3 100644 (file)
@@ -1,5 +1,16 @@
 .PHONY: all
-all: pacman.zip
+all: \
+galaxian.zip \
+mspacman.zip \
+pacman.zip
+
+galaxian.zip:
+       rm -f $@
+       wget https://archive.org/download/Namco-Classics-MAME-Roms/$@
+
+mspacman.zip:
+       rm -f $@
+       wget https://archive.org/download/Namco-Classics-MAME-Roms/$@
 
 pacman.zip:
        rm -f $@
@@ -10,4 +21,6 @@ clean:
 
 realclean:
        rm -f \
+galaxian.zip \
+mspacman.zip \
 pacman.zip