Weird Science - Tales from the
Vectrex Academy Lab
Vectrex
Project Title
- An analysis of the original HEADS-UP
(SOCCER-FOOTBALL) cartridge and source code
Project
Status
Synopsis
- Digital Archeology - An investigation of the
Heads-Up (Soccer-Football) cartridge
- Some time ago, I asked Malban for
suggestions of which game to analyze next, and
he voted for Polar Rescue. When I was about to
copy the binary file to my project folder, by
chance I noticed that its size was 8K instead of
the regular 4K of most of the early GCE games. I
looked what other games also had 8K and saw that
Heads-Up was among them. And out of pure
curiosity why specifically Heads-Up was granted
8K I then started this analysis.
- Here is a summary of some interesting,
yet probably utterly useless findings...
An
analysis of the HEADS UP source code
- These findings are deductions (and some
speculations) based on a full disassembly of the
original Heads-Up cartridge code.
- Standard Disclaimer: I hold the highest respect
for all the designers and programmers who worked on
the original Vectrex games back in the Eighties, and
who were using development environments which are
completely different than those of our modern times.
So, if some of what I write here might sound
negative or presumptuous, it is not meant like that
at all.
- The cartridge
comes with 8K ROM, which is kind of
interesting, as ROM was still rather
expensive back on those days. I wonder why
management granted 8K for this game? I do
not have any numbers, but the original price
must have been higher than those of the
other 4K cartridges. Soccer was and still is
not one of the most popular sports in the
US, so why did they expect good enough sales
to justify this investment? Paul Allen
Newell had an (as he said excellent) 6K
version of Scramble and was forced to cut
the code down to 4K. So why 8K for Soccer?
- The cartridge code uses almost the complete 8K.
Only the last 156 bytes are not used. In the binary
which I used to disassemble the code, those last 156
bytes were not filled with zeros, but with some
(trash?) data which I was not able to fully decode.
The last 55 bytes contain the ascii string
"Drive
Track
Sector DMA
address
\x00Read ". My guess is that those last 156
bytes are leftover garbage from either the tool that
was used to burn the original ROMs at Western
Technologies, or leftover garbage from the
modern-day tool that was used to extract the binary
from the cartridge ROM. It would be great if someone
who has the means could read a fresh binary from a
hardware cartridge and send it to me, so that we can
compare this.
- The implementation can only be characterized as
"spaghetti code". There is one huuuuuge main loop
with tons of conditional jumps, and only very few
function calls to sub-procedures. Modern software
engineering advocates a well-structured hierarchical
and modular design, consisting of lots of
comparatively small functions, which call
sub-functions, which then call sub-sub-functions.
The Heads-Up code is the complete opposite of this.
Both methodologies have their advantages and
disadvantages. High-level programming languages
usually enforce a modular design, and spaghetti code
is typical for hand written assembly language code.
Here, I think, this design was chosen for
performance reasons.
- Personal note: I think Heads-Up is
highly underrated. The scrolling engine is
excellent. Very smooth, and with accurate
clipping. The game provides a 2.5-dimensional
view with the players becoming smaller when
running towards to far side of the field, and
becoming larger when running towards to front
side. There are 3 different AIs provided when
playing against the computer. Yes, they have
their weaknesses, but considering that this was
done in 1982, I find it amazing.
- Heads-Up does not alter the refresh
timer value. It uses the preset value of 30.000
cycles, which technically corresponds to 50Hz.
However, the average cycle count per frame
during a game is around ~40.000 cycles, so it
runs at ~37.5Hz, and we have a more or less
noticeable flicker on a real console.
- There are no obvious and easy major
performance improvements possible (like in
Bedlam or Hyperchase), at least I do not see
any. The graphics engine, the player control,
and the opponent AI simply use lots of
computation time, so no easy way to bring this
game to 50Hz.
- There are many LBRA instructions in the code. A
JMP is shorter and faster. So there is potential for
small performance improvements.
- In several places, an LDA and a subsequent LBD
with immediate values can be combined to an LDD,
which again is shorter and faster. There are in fact
almost as many places where exactly this is done, so
I wonder why this type of optimization was not used
all over the code.
- The BIOS routine Abs_a_b() is called a lot. In
several places though, either, only one of the two
computed values is effectively used, or, in other
places, one of the two register values is already
absolute before the routine is called. More room for
optimization.
- One nice and tiny little detail, which is maybe
not known to everyone: There is a cross drawn inside
the ball when it is held by the dark team, and no
cross when it is held by the light team.
- There are eight different animations lists for the
graphics of the players, one for each direction of
the compass. Each list has entries pointing to two
separate sublists, one for head and body vector
lists, and the other pointing again to sublists with
arms and legs animations. So the drawing of each
individual player is quite complex, but in total
this allows for some really cool perspective drawing
and animations of the players.
- The code also
contains some dead data. There is a short
melody which is (as far as I can see) not
used throughout the whole game. It can be
started from the sound handler, but I could
not find any code triggering this. The
melody is in the style of the title melody
and sounds like an alternate tune for
scoring a goal, but this is just a guess of
mine. Maybe at some point it was planned
that there were different sounds for each
team when scoring a goal?
- The game is
programmed to always react to the input of
the two joysticks 1 and 2. If the computer
AI plays, then the AI code writes its own
values to the Joy_2_Y and Joy_2_X RAM
locations (overriding the input from the
controllers) in order to trigger the moves
and actions of its players. This is quite
clever.
- The BIOS
Random() function is used for sound effects
only (the cheering of the spectators). Any
randomness in the actions of the computer AI
is achieved by inspecting the LSB (bit 0) of
the frame cycle counter. This came as a bit
of a surprise to me, as in general this is
not a good way to generate randomness. But
it is of course way faster than using the
BIOS routine, so it was likely chosen for
this reason.
- The counting
of the score and the time is done by
in-string score-computations. Meaning, there
are no integer values stored in RAM to keep
track of the score and the time, but only
their ascii character string
representations. These are directly used in
the Print_Str() routines, and the strings
are directly modified, whenever a goal is
scored, or a second of time has gone by.
This is a neat little trick to save the CPU
cycles of converting binary integer values
to decimal string representations, which is
time-consuming.
- The code makes
heavy use of the BIOS mem copy routines. In
each frame, there is lots of ROM data copied
to RAM locations and then modified and
processed. This of course takes time.
- In two player
mode, if player 2 does not kickoff for 2
minutes after a goal, then the AI takes over
and continues playing. I wonder if this was
intended.
- If the final
score is a draw, then the game continues for
another 3 min in sudden death mode. This
goes on until a goal is scored by either
side. After 10 of such rounds, the round
counter starts showing garbage, "0:", as the
value of the ascii character is simply
increased. I guess, no one has ever played
the sudden death this far ;-)
- There are some
unused ram locations, which are only
written, but never read. Maybe those were
used in early versions of the game, or
especially in early versions of the
scrolling engine? The values which are
computed and stored (and then never used)
suggest that maybe there once was also a
vertical movement implemented in the
scrolling engine.
- The graphics
engine computes a perfect clipping. That is
really amazing, but also computationally
expensive. When the viewport scrolls to the
very left or the very right side of the
playfield, then the diagonal lines and the
goal scroll in with perfect clipping.
Meaning: for all vectors the engine computes
which part of the vector is visible, and
which is not, and only the visible part is
effectively drawn. Other games, like e.g.
Scramble, simply draw the complete vector
regardless if some part of it goes out of
the physical screen. There, we have a
natural hardware-clipping, which is fast.
Heads-Up defines its own viewport within the
physical screen, and then computes and draws
vectors only within this viewport. This
explains where many of the cycles go.
- There are still some 30% (?) of the code which I
have not yet analyzed in full detail. I still would
love to fully understand the behaviors of the
computer AIs and how they are programmed. So far,
the active AI player never passes the ball to one of
its teammates. If this somehow could be added, I
think playing against the computer would become much
more attractive and challenging. Unfortunately,
right now I do not have the time to pursue this.
- To be continued. Comments are welcome!
Source
Code
- There are
still some parts of the code which I have
not yet fully analyzed, this is still
ongoing work. I did not make the code
disassembly beautiful in any way. I have not
fully documented it, and some comments might
be outdated. If you spot any mistakes, or if
you have additional insight, please let me
know.
Credits
- Again, this
analysis would not have been possible
without Vide.
- Many thanks to
Malban for once more adding tons of new
features I suggested or asked for.
Patched
Heads Up Version:
- Please note, these are just experimental patches,
not meant as alternate game versions.
- heads_up_patched.bin,
containing the following modifications:
- Title
text changed
to "Heads Up -
Patched"
- When
scoring a
goal, the
hidden melody
is now used
instead of the
original tune.
- (not
yet done: lots
of minor
performance
improvements
in the code,
likely
unnoticeable)
- Downloads are free and for
non-commercial use only. Use at your own risk.
- Please respect the
copyright and credit the author and the
origin of this game.
Online
Playing
- Link to Dr. Snuggles' online
emulator to directly play the game in your
browser: Heads Up_Patched
- For comparison (though comparing this on real
consoles is much more telling): original Heads Up
Author
Latest
modification on 04/06/2023, 10:30
|