Prof. Dr. rer. nat. Peer Johannsen

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

  • Ongoing research

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

  • Peer Johannsen

Latest modification on 04/06/2024, 10:30
  • Page updated