--- /dev/null
+;\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