(back to project list)
Starship Commander Disassembly
Starship Commander
is a space combat simulator written for the Apple II by
Gilman Louie. It
was published in 1981 by Voyager Software.
The game is written primarily in Applesoft BASIC, implemented in two
dozen separate programs. A few assembly-language routines are used to
draw characters on the hi-res screen and make sounds.
The user interface is implemented entirely with hi-res graphics, with
user input exclusively from a joystick or game paddle. The game is an
excellent example of providing a clean, consistent user interface for
a complex simulation.
The disassemblies of the BASIC programs are essentially LIST output
(from CiderPress) with comments added. The assembly-language subroutines
and font files have been disassembled with SourceGen. According to
a comment in HELLO, the disk image is Starship Commander v1.20, written
Dec 1981.
General:
Starship Commander has quite a lot of game state, most of it held in
Applesoft variables and arrays. Normally, when a new program is run, all
variables are cleared, but the game uses a CHAIN program to preserve them.
Because all Applesoft variables have global scope, and use two-character
identifiers, it gets a bit crowded. When reviewing the source code it's
useful to have this chart on hand:
Initialization and setup:
- HELLO - changes the Applesoft
load address from $0801 to $4001, and runs START.
- START - loads various binary
components: subroutines, shape tables, HRCG. Runs INTRO.
- SUBROUTINES @$0802 - sound generation
and screen clear/invert routines
- Sounds:
POKE 2048,pitch : POKE 2049,duration : CALL 2050
- Clear screen:
POKE 2072,color1 : POKE 2073,color2 : CALL 2074
- Screen invert:
CALL 2115
- H @$88FE - hi-res character generator;
includes default font (#0 - standard Apple II font with upper and lower case)
- SMALL.SET @$1268 - HRCG font #1 -
various symbols (upper case) and small upper-case letters (lower case)
- WORD.SET @$1568 - HRCG font #2 -
words spelled out compactly with multiple letters.
For example, "ABC" is "SHIELD", "DEFG" is "TORPEDO".
- SHADOW.SET @$1868 - HRCG font #3 -
a square "computer" font, with upper and lower case (this appears
to be the BYTE font from DOS Toolkit, not SHADOW.)
- COLOSSAL.SET @$1B68 - HRCG font #4 -
bold text, with upper and lower case (font matches COLOSSAL
in the DOS Toolkit).
- SHIP1 SHAPE @$9100 - SHIP1 shape table,
used in various displays
- SHIP2 SHAPE @$0866 - SHIP2 shape table,
used in WEAPONS
- CHAIN @$0208 - utility to load a
new BASIC program without losing all variables. Loaded when needed
(it's not explicitly discarded, but it does sit on the keyboard input
buffer).
- INTRO - draws the title screen. Runs DIM.
- DIM - initializes Applesoft variables
and arrays used throughout the game. Initializes a chunk of memory
from $1E6E-1F5D to hold crew efficiency data. Chains ENTER.
- ENTER - offers choice between starting
a new game and continuing a saved game. For a new game, the game
parameters (number of enemies, placement, etc) are selected, and
it chains to INFO1. For an existing game, it chains to
GET DATA.
- GET DATA - loads a saved game from
the file STARSHIP DATA, then chains to INFO1. If loading fails,
it runs DIM.
Main Loop:
- INFO1 - updates ship energy and damage.
Shows animation of enemy ship movement. If all enemy ships are dead or
out of range, chain BYE; otherwise, chain INFO2.
- INFO2 - update auto-shield assignments,
crew efficiency, calculate max speed and turn, etc. Chain MAIN MENU.
- MAIN MENU - allows player to
select different simulated stations, or end their turn.
- COMMUNICATION - send a
message to the alien fleet commander. If one side surrenders,
chain BYE. Otherwise, chain MAIN MENU.
- DEFENSE - manage shields.
Chain MAIN MENU.
- ENGINEER - draw engineering
console and chain ENG, which
manages energy distribution and system repair.
Chain MAIN MENU.
- LIFESC - draw life-support
console and chain LIFE, which
manages crew assignments.
Chain MAIN MENU.
- NAV - draw navigation
console and chain N, which
handles ship navigation.
Chain MAIN MENU.
- SCIENCE - displays information
about enemy ships.
Chain MAIN MENU.
- WEAPONSC - draw weapons
console and chain WEAPONS to
target positron beams and torpedoes.
Chain MAIN MENU.
- TARGET - ends turn by exchanging
fire with enemy ships. If the player is destroyed, chain BYE.
Otherwise, chain DAMAGE.
- DAMAGE - computes damage from
incoming weapons fire. Applies damage to individual systems and
crew, and shows a damage report. If the player asked to save
their game, chain STORE DATA. Otherwise, chain INFO1.
Misc:
- STORE DATA - saves game to
STARSHIP DATA. Chains to INFO1 or BYE.
- BYE - game over. Shows score. To
start a new game, runs INTRO.
- CREW - text file with crew names and ranks
You can download the full set of
SourceGen projects for the binary files,
as well as a disk image of the game.
Playing the Game
I haven't found a copy of the manual online, but most of the interface
is easy to figure out.
Quite a bit can be gleaned from the "Designer's Notes" in
Computer Gaming World issue 2.5 (PDF),
published in September 1982, starting on page 33. The game's author
provides advice on how best to play, explaining how several of the
ship systems work.
Nothing in the game requires reflexes, so most of it plays a bit better
in an emulator at 2x speed. You do have to be a little quicker when
entering a number with the paddle though.
Bugs & Quirks
A game of this size and complexity is bound to have some bugs. Most
of the issues can be blamed on the lack of scoping for variables in
Applesoft, and the ability to use them without first initializing them.
Cosmetic/Trivial
- "Repair Driods" mis-spelling in "DEFENSE".
- "Engergy" mis-spelling in "LIFE".
- Display in life support always shows cost=20; should be 22/12/2. (This is just a display issue.) See "LIFE" line 26100.
- Torpedo lock code checks for power in light engines, but draws 20pts from battery, which can therefore go negative. See "WEAPONS" line 12300.
- Ships are out of range past 6500Mm, but the test in "INFO1" line 10000 relies on DK(n), which isn't updated until "INFO2". So we will play one turn with the ship out of range, then next turn it'll be dropped even if it flies back into range.
Minor/Moderate
- Firing some torpedos with one spread, and some with a different spread (e.g. 6 torps with spreads of 2 and 4), will cause the damage from the first set to be ignored. See "TARGET" line 27000.
- Calculations in "DAMAGE" use DM(n) rather than D(n) in two places, but DM() is never set to anything. One use causes all torpedo tubes to be unloaded if any tube is damaged (line 19545). The other fails to set D(2) when shield #2 is damaged (line 19675), which will cause the status to be incorrectly displayed in "DEFENSE", and allow power redistribution in "ENG".
- If you attempt to surrender, but are refused, F1 (the initial number of enemy ships) is set to 1 in "COMMUNICATION". This is used by "BYE" when computing your score, and by "INFO1" when giving power to drained enemy ships (which doesn't actually work anyway), and when counting ships on line 41281.
- The code in "LIFE" that reshuffles the crew sets DT(1), (2), and (3) to zero. DT identifies the on-duty team, and is normally 1-3; after the change, it points to a non-existent crew with an efficiency of zero. This has various effects; for example, reassigning the crew of any station will make it impossible to change the Evasive Action setting on the current turn. The DT array is refreshed in "INFO1", but after evasive action (EV) is updated, so the Evasive Action value EV will be reduced to zero on the following turn. Fortunately DT(4), the defense team, is not affected, so rearranging crew does not increase the damage sustained during an attack.
- "INFO1" line 56600 tests KP rather than KP(SH,0), but KP is never set. This skips power reassignment on a Vegan ship when the total power is between 1000 and 2000.
- Bluffing is very broken. If the bluff fails, the enemy ships will still jump to max speed and reverse course. There is no penalty for bluffing every turn. You can get a minor victory (6100 points or so) by parking the Ranger and simply bluffing repeatedly. In addition, the computation for whether bluffing is accepted is derailed by "INFO2" line 14000, which makes it based purely on chance if you happen to have used the "ENG" features that touch temporary variable C1.
- Auto-nav based on "projected" position is calculated by "INFO2" line 15200, but fails to set the speed S for each ship from KW(SH). The calculation will use the value that happens to be in S, which is likely the speed of the highest-numbered Vegan ship (left over from a previous calculation).
Major
- Damage done to enemy ships is deducted twice, once in "TARGET" (line 27150 for torps, 27550 for positrons), and once in "INFO1" (line 58060). This doubles the effectiveness of the Ranger's weapons, though the second deduction is purely an energy drain. The second deduction of VH(SH) is done after the energy reallocation is done, and looks like an attempt to ensure that damage is reflected on the shield after the reallocation, but the subtracted energy isn't added back to the battery. The total energy in KP(SH,0) is now inaccurate, but won't be recomputed until next turn, so looking at the ship from the Science station doesn't show the energy drain (which is probably how it was missed in testing). The implementation should have capped the shield alloc, not subtracted power.
Unclear
- Not exactly a bug: "DIM" attempts to seed the random number generator from $4e/$4f, but those locations are only updated when waiting for keyboard input -- which Starship Commander never does. So, when booting directly into the game from power-on in an emulator, the initial "random" ship setup is always the same. (Work around it by resetting or booting into something else first, then PR#6.) Also, RND() only seeds the RNG when the argument is negative, and "DIM" does nothing to ensure that, so the attempt at seeding may do nothing.
- Possibly deliberate: the forward shield, shield 1, is more likely to break than the others. Shields 2/3/4 must be hit by at least ~300 damage, and only have a 30% chance of breaking. Shield 1 has a 30% chance of breaking for 100-300 damage, and a 100% chance of breaking at 300. (The calculation is more complex, and the above is only true if the shield energy just barely absorbed the incoming fire, but it's still odd that the toughest shield is the most brittle.)
- In enemy ship torpedo computation ("TARGET" line 28000), X8 is used without initialization. The value is bracketed so it can't really do harm, but it's unclear what the intent was.
Additional Notes on Bluffing
- COMMUNICATION line 20110 prints success/failure message depending on CB (1-5 = fail, 6 = success), and sets B1 to 1 regardless of outcome.
- TARGET line 1550 correctly tests (B1 AND CB = 6) to determine whether or not the enemy will fire.
- INFO1 line 41280 acts as if bluff succeeded if CB is nonzero, which it always is after “INFO2” runs for the first time. The course/speed change is performed here. It looks like the course is reversed, not directed away, so each turn it would ping-pong if the ability to turn weren’t restricted by high speeds.
- INFO2 line 325 attempts to set CB according to your bluff chances. It tests for “B = 0”, which it always is since nothing ever sets B. If CB is 6, the update calculation is skipped (so once gullible, they remain gullible). It tests EN, which is supposed to hold total energy, but that isn’t set to current values until later (though it’s also set by some “ENG” displays, so there’s a reasonable chance it’s set).
- INFO2 line 14000 sets CB to a totally random value if C1 > 0. C1 is only ever used as a temporary variable in ENG.
Copyright 2020 by Andy McFadden