Code Annotation
Annotating the Code
Once the code is disassembled into a giant list of instructions, you need to work your way through it and start figuring out what it does. Conceptually, it is easy. In truth, it can be really hard. Easy, because at least it starts off easy. Hard, because we are working at the computer machine language level, which is as deep down as it gets. It's almost as if you wanted to figure out how a car worked, but your only tool was a microscope. Some times, the microscope would be the exact tool you needed. But most of the time, it would be providing needless detail when all you wanted was some high level understanding of the car's characteristics like how many doors it had. Nonetheless, you work with what you got, so the only thing to do is to start tracing code and see where it gets you.
To show you where you are headed, after a lot of detective and brain work the snippet shown above can be decoded into the form shown below. For non-programmers, anything that starts with a semicolon character is called a "comment". Comments are used to document what is happening in more descriptive terms that the strict sequence of machine instructions. The comments are added by humans, and are strictly for use by humans to make it easier to understand what the instruction sequences are trying to accomplish. To a computer, the comments are meaningless. In fact, an assembler program that would be used to turn this sample code back into the machine code simply strips out the comments and throws them away.
;*****************************************************************
;Test for the most recent crankshaft 1/2 period for overrev
;situations.
;The different maps have different spark and fuel cutoffs. As
;RPMs rise, the spark cuts off first, then the fuel.
;There are a pair of limits for each test (#1A,#1B) and (#2A,#2B).
;Maybe this has something to do with per-cylinder maps? Might be
;the case, but per-cylinder table entries based on RPM are fairly
;pointless since the most recent engine 1/2 period engine will be
;the same RPM regardless of which cyl you are looking at.
LC2D9_overRev:
ldaA L0049_PORTG_debounced
andA #%00101000 ;check both map selects: MS1, MS2
cmpA #derestricted ;are we derestricted?
bne @4 ;branch if this is NOT the case
;Otherwise, the wire is cut and we are derestricted:
ldD L00D8 ;get the most recent crank 1/2 period
cmpD L9A77 ;compare it to stored limit #1A: 10482 rpm
bcc @0 ;branch if the period is >= stored period
;(i.e. RPM is less than limit )
cmpD L9A79 ;compare it to stored limit #1B: 10482 rpm
bcc @1 ;...this will never branch since it
;is the same as the first test.
;If we get here, the current period is < limit #1A (or #1B). For
;the 549US EPROM, this means the engine is turning faster than
;10482 RPM. This is an overspeed condition!
bset L0007, #%10000000 ;disable the fuel injectors
jr @1
If the comments weren't clear enough for non-programmers out there, we see some code that is used to test if the Aprilia rider is trying to run the engine past the redline. The processor checks to see if the period of the last half a rotation of the crankshaft occurred faster than the limit imposed by the fuel cutoff redline. Remember this is a period measurement, not an RPM measurement. This means that the shorter the period of time it took the crank so spin its last 1/2 a rotation, the faster the engine must be rotating. This is why we skip the fuel cutoff if the measured period is greater than the redline cutoff period. If the measured period is less than the cutoff period, we are overreving. The processor indicates the overrev problem by storing a 1-bit flag in the left-most bit of the byte of RAM stored at memory location $0007. Elsewhere, the code that starts the fuel injectors squirting knows to check for this error flag before starting to squirt. If it sees the error flag, it skips the "start squirt" operation. This means that no fuel is injected to prepare for the next combustion event. The engine has run into the rev limiter.
One last important point is to notice that the comments make it clear that while we have a good idea about what is going on, we may not be perfectly sure about all the aspects.
There are lots of ways to proceed at this point. The simplest way is to pretend to be the processor and start digging into the source starting at the RESET vector. You will see the processor initialize its I/O ports and then fall into the main loop. From there, you can attack things by starting from the other interrupt vectors. For example, the circuit diagrams tell you that the front ignition coils are controlled by CPU pin PA7/OC1. This means that the front ignition circuitry must be controlled by the OC1 (Output Compare #1) interrupt. If you start working through the code starting at the OC1 interrupt vector, you will immediately find the code that has to do with firing sparks on the front cylinder.
The process can be tedious, but it can also be lots of fun to finally figure certain things out. Or maybe it helps to be a nerd. To me, it's just like a giant puzzle that I still haven't figured out yet. Some day though.
So that's the start of it all. There is a buttload of software in that EPROM, and it takes a 10x buttload of time to try and figure it out. The next goal is to develop a picture of the ECU's basic software design by staring at all the pieces and seeing how they fit together.