Code Analysis

Phew, quite some time since the last update! I had to make some development pauses due to other priorities (my second hobby music gained some more traction for a while, with me taking singing lessons and revising my equipment, also my job took more of my time and energy than it should). But nevertheless there was some progress: I have completely rehauled the combat system and am nearly done with that. It turned out to be more challenging than I thought and involved a lot of cycle counting to get it right. This for sure deserves a blog entry on its own when it is complete. Here is a little preview:

Combat with eight enemies

Another thing that I did is that after needing to ramp up several times after development pauses I decided to create a tool to help me understanding my own code structure. An assembly project as large as this game is hard to keep under control. There is a lot of knowledge in my head about which code is used where and which variables are shared between procedures while I am working on it. But all of this gets lost already after some weeks not looking at the code.

I am currently chasing a bug in my combat code and have the suspicion that it has to do with a temporary variable that is accidentally shared between the IRQ and the main loop. In order to help me finding that, I created a tool that will analyse my assembly source code and create a call graph. It is using GraphViz for this, a general purpose tool for drawing graphs. I first tried this on my complete code, but the resulting graph was utterly useless, because it was far too complex. But then I changed it to create graphs for each module (and treating calls to other modules as a single node), which made it actually useful! Here is a graph of a rather complex module, my main engine for the game.

Automatically generated call graph of charengine.asm

Yes, it looks complex, but with some understanding what the procedures actually do, it is really useful. Blue blocks are the interface of the module (the exported procedures that can be called from outside), grey blocks are internal procedures that are only called from inside of the module, and white blocks are procedures from other modules.

The tool will also identify all registers and global temporary variables that are used in a procedure, which are visible in the graph as A, X, Y and R1 – R14. These will also be patched into the procedure header in the source code accordingly. I was documenting this by hand in the past, but this is very error prone, as every time this changes in a procedure, the header of all calling procedures and the procedures calling those etc. need to be updated. It is also a lot of work and I frankly often did not care to do the manual update, so the headers were now either empty or wrong (which is even worse). Here is an example of procedure header:

;********************************************************************
; Purpose : Adds environment dependent commands to ingame menu
; Input   :
; Output  : A = selected menu entry
; Modifies: A,X,Y,R1,R3,R4,R16,RP1,RP3,RP5
;********************************************************************
.proc show_command_menu

    ; local variables
    commands_ptr    = R16
    num_commands    = R3
    command_counter = R4

    jsr ingamemenu_init ; add cancel menu entry
...

The actual analysis of the used registers and variables is rather primitive, but takes a couple of specialities of my source code into account. I sometimes define procedure specific names for the temporary variables, e.g. in the example above. I also make use of unconditional branches and of fall throughs at the end of procedures. All of this is considered during the analysis.

I am confident that this tool will help me finding bugs and ramping up after development pauses. Let’s see if it will be useful for the bug I am chasing! By the way, the tool is written in Java, which makes parsing text really easy. So it was implemented in a single day! Sometimes it is great to use modern languages after a while of assembly hacking.

Leave a Reply

Your email address will not be published. Required fields are marked *