In order to create Commando 2084, I had to disassemble Commando. My original intention was to patch what was only needed and stop there. But I got carried away and I ended up analyzing and commenting the entire Commando code.
The commented code (that can be recompiled to generate the exact original binary) is here:
Findings – Level 2
Apparently, the original idea was to ship Commando with 4 levels instead of 3. There is a lot of code/data that indicates that a “level 2” (the levels that are shipped with Commando are level 0, 1 and 3) was in progress, or even finished.
All the actions, charset-mask, trigger rows are present. What’s missing is the map and a partial charset. The charset used for the main screen is likely the one designed for level 2.
Speculation from my part, “Level 2” was probably removed due to lack of time (?) or due lack of RAM to create a single-load game (?).
For more info about this level, search for “LVL2” in the
Map and actions
- Each level consist of 40×200 map.
- Each level consists of:
- Charset unique for the level: LVL0 at
$C000, LVL1 at
$C800, LVL3 at
- Each char in the charset contains a mask that indicates the background priority. See
- A map: LVL0 is at
$6000, LVL1 at
$8000, LVL3 at
- List of actions: each action represents a sprite that must be created. But an action can perform non-related sprite tasks as well, like opening a door. See
- List of rows: when the master row index (see
V_SCROLL_ROW_IDX) matches this number, the associated action is executed. See
- Actions can receive an optional “sprite X position” argument. See
- Charset unique for the level: LVL0 at
The game supports up to 16 virtual sprites.
- Sprite 0 is used for the hero
- Sprites 1-3 are used for hero’s bullets
- Sprite 4 is used for hero grenades
- Sprites 5-15 are used for enemies, including its bullets/grenades.
Each sprite has:
- X and Y coordinates (including X LSB and X MSB)
- Sprite frame (which sprite to render).
$FFis an empty frame.
- Sprite color
- Background priority (e.g: should it be rendered behind a tree?)
- Animation type (see below). If it is
0, it means it is an empty sprite.
- Extra variables that are used for different things. Varies from anim type to anim type.
Some enemies, like the motorcycle, take two sprites.
An animation represents what the sprite should do during the game. For example, the animation type
TYPE_ANIM_SOLDIER_BULLET, animates the bullet. See
TYPE_ANIM_TBL in main.asm for the complete list of animation types.
An sprite can change its animation in runtime. For example, the
TYPE_ANIM_SOLIDER_BEHIND_SMTH has certain logic. But when the hero is at the same Y position as it is, then it changes its animation to
TYPE_ANIM_SOLDIER. Similar, the
TYPE_ANIM_SOLDIER_JUMPING animates the “jumping” solider. But when the solider lands, it changes its animation to
TYPE_ANIM_SOLDIER. Those are only two examples, but most animations change its animation type in runtime.
Animation type 0
When a sprite goes out of bounds, or an enemy dies, or an animation ends, that sprite is set with animation type
0, which is the
What this animation does, is to create random enemies. And when an enemy is created, it changes in animation type to something different than 0.
That means, that when there map is “full” of enemies (all animation types are different than
TYPE_ANIM_SPAWN_SOLDIER is not called. And when there many empty sprites, it gets called more often.
Collision detection is done in software (no hardware collision detection is used).
There is a routine to check whether the hero is hit (see
CHECK_COLLISION) and another for enemies (see
Each animation type has contains a mask (see
f2544) that indicates whether it can collide with bullet, grenades, both or nothing. For example, when the soldier is in the trench, it can only be killed with a grenade.
From a high-level (architecture) point of view, the code is very well designed. It is pretty easy to add new types of actions, or create new levels, or modify existing ones with little change in the code/data.
From lower-level point of view, it seems that parts of the code could be improved (see
FIXME in main.asm), specially regarding performance and flickers. Seeing many Level-2 traces, plus seeing certain bugs makes me think that the development team was under pressure to release the game ASAP (something fairly common in the gaming industry).
Additionally, it seems that the assembler used didn’t optimize the code to use zero page. For example, calls to:
are assembled to:
STY $00FB,Y ;3 byte variation, instead of the two-one.