Tutorial for “reverse-engineering” the Weber Marelli IAW ECUs

(as found in the Lancia Delta Integrale)

The grale-ECU (GECU) tools are provided free of charge but without warranty or support. Here I describe how I use them.

Since buying my Lancia Delta Integrale (non-Evo 16V with ABS) I find this car already has a very advanced engine management ECU controlling spark, fuel and boost. My ECU is labeled WH4W.08/90K-9B. I have archived more information about the basic hardware of the system on the tools page GECU HARDWARE INFORMATION section..

Being in a country where these cars were not sold from new, local support is non-existent so I need to do everything myself. Since the ECU appears to be at least as advanced as systems like megasquirt I figured it would be worthwhile to figure it out rather than replacing it.

1. Create a working directory and get the EPROM binary

Create a directory (eg. c:\gecu) on your PC and download the EPROM binary there (right click on the link and save in new directory).
The ECU operating instructions (code) and configuration tables (maps) are contained in the same EPROM (27128 in the case of my ECU). This is what we are trying to reverse-engineer. Although both the code and maps can be changed to affect the engine performance the ECU is not designed for in-service upgrade by FLASH re-programming (more on this in the tuning tutorial). In general the binary file can be extracted from the stock EPROM by plugging it into an EPROM reader or you might find it in the PROGRALE download area.

2. Download the GECU tools

Download these tools as a single zip file into the gecu directory you created in step 1 and extract them there.
These are the software tools necessary to interpret what’s in the EPROM. If for some reason you don’t have the ability to unzip then download them one-by-one from the tools page GECU REVERSE ENGINEERING section.

You should now have a folder with the following files in it:
1. dasm.bat
2. dhc11.exe
3. diss.bat
4. gecu.bin
4: gecuadd.exe
5. gecudctl.txt
6. gecudiss.txt
7 gecuparse.exe

3. Run the 1st pass of the disassembler

Open a file explorer window and navigate to the working directory (eg. c:\gecu). Run the 1st pass of the disassembler (double click on diss.bat).
A DOS window will temporarily open then close. I have redirected all output to log files so we can just do stuff from the windows environment rather than having to go to DOS. This should have created 4 new files:
1. diss.log (this file tells you the results of running the diss batch file.)
2. gecu.cmt (this is an empty comment file needed for later)
3. GECU.DIS (this is the raw disassembled ECU file - but we won't be using this)
4. gecu.txt (this will eventually be the commented disassembly file - but right now its empty)

4. Now the real work starts !

Run the disassembler using dasm.bat (double click dasm.bat). A DOS window temporarily opens and closes with the results in the log file dasm.log.

Now the process of converting from a binary to something that is human readable begins. The process of disassembling the code can be aided in 2 ways;
1. Whenever we learn where things are in the memory map (internal or external RAM or internal or external device) we create an entry in the disassembler control file so that the next time we run the dasm batch file we'll have an easier to understand english language name rather than a number.
2. When we figure out how things are working in the code we can add comments to the commented disassembly file so we'll remember what was going on the next time we look at this section of code (or when it happens to be used again - say as a subroutine)

This will be an iterative process until we've figured out the whole EPROM binary. That's all there is to it !

5. Background - lets look at each tool in detail

gecuparse

gecuparse usage: gecuparse input_commented_file output_comment_only_file
This tool strips comments from the commented disassembly file and saves them in a comment file. In general what it does is:

gecuadd

useage: gecuadd input_comment_file input_disassembler_file output_merged file
This tool merges comments from the comment file with the same line in the disassembler file to create the merged commented disassembler file. In general what it does is:
 Note the following restrictions:

dhc11

useage: dhc11 disassembler_control_file -a -op –ov
The best description of this tool is the tutorial on the Tech Edge web site. I will explain how I use it.
I use the following command line options:

gecudiss.txt   & gecudctl.txt

The disassembler control file(s) deserves a little extra detail. The most important thing that happens in the control file is the creation of the memory map for the disassembler. In the 1st pass of the disassembler (diss.bat) we don’t know anything about the system so all that we have in there is the input and output file names. Have a look at gecudiss.txt and here’s what you’ll find:
input  gecu.bin
output  gecu.dis

Note that the disassembler uses “;” for its comment character. If you want to add comment information to the control file you have to use the ; character. Everything after the “;” is ignored.

Control file commands

The following commands, all within the control file, tell the disassembler information about the binary image it will process. The more information that can be determined and supplied here, the better the resulting disassembly will be. Optional parameters are enclosed in square [ ] brackets.

Label    <addr> <name> Assign a label <name> to address <addr>.
Entry    <addr> [<name>] Provide a code entry point <addr> with optional label <name>. The code seeking algorithm scans memory for these locations, and automatically adds new entry points as branch and call instructions are encountered. When no new entry points are added in a single pass, then the disassembler has completed the code seek phase
Indirect <addr> [<name> [ <here>]] Define a pointer to to an (indirect) address. An indirect address is a 16 bit quantity (ie. word, or two bytes) that is used by the processor to form a target (jump or call) address.  The word at memory address <addr> points to an address that is tagged as an entry point, and the optional <name> is the label for this address. And finally, <here> will be tagged as a data word. The ordering of labels was chosen to maintain compatibility with existing disassemblers.
Vectors <addr> <count>  [<name> [<here>]] Define a range of indirect addresses, as would be produced by a jump table, or list of procedure addresses. The number of data words (or vectors) is defined by <count>. The optional <name>, if supplied, is used to create a label, for each indirect address, of the form <name>_NN where NN starts from 00. The optional <here> is the address (ie. <addr>) of the word table. Note that NN is one less than <count>.
Bytes    <addr> <count>  [<name>] Define a byte table at <addr> of <count> length. 
Words  <addr> <count>  [<name>] Define a word table at <addr> of <count> length

Mnemonics

And finally on the disassembler topic please note the following differences about the way the Tech Edge disassembler presents the mnemonics compared to those specified by Motorola for the processor.
DHC11's Mnemonics Motorola's Function Performed
call JSR Call
callr BSR Call Relative (short call)
cmpD, cmpX cmpY CP? Compare (16 bit register)
decX, decY, decS DE? Decrement (16 bit register)
di SEI Disable Interrupts
ei CLI Enable Interrupts
incX, incY, incS IN? Increment (16 bit register)
jr BRA Jump Relative (short jump)
push, pushB, pushX, pushY PSH? Push on to stack
popA, popB, popX, popY PUL? Pop off stack
ret RTS Return (from subroutine)
reti RTI Return From Interrupt
xorA, xorB EOR? eXclusive Or

As you can see, DHC11's mnemonics use, at most, one extra character, but this makes their meaning much clearer, and is closer to a majority of other assembler syntaxes. In addition, the mnemonics are displayed in a mixed case that is designed to highlight the registers use by the instruction. For example, LDA, the Load A instruction is displayed as ldA to emphasise that the A register is used in this ld instruction. The tAB and xgDY are examples of instructions that use two registers in the one mnemonic.

6. Overview of disassembly process

Now that we know the details of each tool lets have a look at each element in the process

dasm.bat - the disassembler batch file

This batch file does the following:

dasm.log - the disassembly log file

The log file records the messages from the comment parser and adder and the disassembler. An initial log file might look like this:

GECUparse - Comment Parser v1.1 (c) Copyright 2008 www.forzavx.com

Input file: gecu.txt
Output file: gecu.cmt
Total of 0 comments parsed.

DHC11 - 68HC11 Disassembler v1.1 (c) Copyright 2000 Tech Edge Pty. Ltd.

Input file: gecu.bin
Input file gecu.bin has 16384 bytes ($4000).
Output file: gecu.dis
Code resides from $C000 to $FFFF ($4000 bytes).
** Symbol "RESET_00" is already Install at $C000 ("MIR_irq_00" requested)
** Symbol "RESET_00" is already Install at $C000 ("SIR_irq_00" requested)
Indexed Call/Jump at $C3DF, may require a vector table.
Pass 1 found 1336 new entry points.
Pass 2 found 0 new entry points.
Total of 2 iteration(s) to find all code.

GECUadd - Comment Adder v1.1 (c) Copyright 2008 www.forzavx.com

Input comment file: gecu.cmt
Input disassembly file: GECU.DIS
Output file: gecu.txt

Total of 0 comments added.

And now for the meat of the disassembly process

gecudctl.txt - the disassembler control file

This file tells the disassembler to use an understandable name for various parts of the memory map. Lets look at it a piece at a time.

The input / output file spec:
; This control file is called gecudctl.txt
;
input   gecu.bin   ;this is the binary input file
output  gecu.dis   ;this is the disassembly source/listing file

The next section is the internal memory mapped devices of the MC6803U4 processor
; MEMORY MAP

; --- INTERNAL DEVICES ---

; 0x0000 to 0x001F internal registers (see MC6803U4 datasheet)
label $0000 PORT1_DDR
label $0001 PORT2_DDR
label $0002 PORT1_DATA
label $0003 PORT2_DATA

label $0008 TMR_CSR
label $0009 TMR_CNTd     ; 16 bit
label $000B TMR_OCR1d    ; 16 bit
label $000D TMR_ICR1d    ; 16 bit

label $0010 SCI_RMCR
label $0011 SCI_TRCSR
label $0012 SCI_RX
label $0013 SCI_TX

label $0015 TMR_CNTAd    ; 16 bit - alternate to TMR_CNT (TOF safe)
label $0017 TMR_CR1
label $0018 TMR_CR2
label $0019 TMR_SR
label $001A TMR_OCR2d    ; 16 bit
label $001C TMR_OCR3d    ; 16 bit
label $001E TMR_ICR2d    ; 16 bit

The next section is the internal RAM variable declarations. I've named a few that were easy to find since they are part of the serial command interface.
; 0x40 to 0xFF internal RAM
; insert symbol names here
;
label $0048 cmd_throttle_valve_angle
label $0049 cmd_air_temp
label $004A cmd_water_temp
label $004B cmd_injection_duration_msb
label $004C cmd_injection_duration_lsb
label $004D cmd_ignition_advance

label $0054 cmd_injection_timing_angle
label $0055 cmd_co_trimmer

label $005F cmd_battery_voltage
label $0093 cmd_vae_duty_cycle_msb
label $0094 cmd_vae_duty_cycle_lsb

label $00A7 cmd_param_12
label $00A8 cmd_param_13

label $00AF cmd_param_14

label $00B5 cmd_engine_period_msb
label $00B6 cmd_engine_period_lsb

label $00CE cmd_uniram_1
label $00CF cmd_uniram_2

label $00D7 cmd_intake_pressure
label $00D8 cmd_uniram_3

The next section is the external RAM variable declarations. Coincidentally a few are also part of the serial command interface.

; --- EXTERNAL DEVICES ---

; 0x1800 to 0x1FFF external RAM (6116 2KX8 SRAM)
; insert symbol names here
label $1819 cmd_intake_pres2
label $1821 cmd_uniram_s

label $1826 int_error_1
label $1827 int_error_2
label $182F cmd_param_17msb
label $1830 cmd_param_17lsb

The next section identifies the external memory mapped devices
;0x2000 to 0x2006 (MC6840 timer)
label $2000 ETMR_WCR3_1
label $2001 ETMR_WCR2_RSR
label $2002 ETMR_TCR1
label $2004 ETMR_TCR2
label $2006 ETMR_TCR3

;0x4000 to 0x4002 (MC14442 A to D converter 8 bit with input mux)
; configured as 16 bit registers
label $4000 ADC_DATA
label $4002 ADC_CTRL

This next section shows some examples of labeling code addresses as a jump table, parameter list and vector table. The first table was found as part of the self-test commands, the next table is the list of memory addresses that are dumped as the diagnostic command responses and finally the vector table at the top of memory again based on the MC6803U4 CPU.

; External EPROM (27128 - 16Kx8)
; 0xC000 to 0xFFFF

; jump table
; vectors $C3F9 5 jump jumpvectors ; works but doesn't give desired result
indirect $C3F9 DIAG_test_fuel_pump DIAG_jump_vectors
indirect $C3FB DIAG_test_injectors
indirect $C3FD DIAG_test_coil
indirect $C3FF DIAG_test_vae
indirect $C401 DIAG_test_overboost

; ECU parameter table
words $ED65 24 CMD_REPLY_TABLE
;
; vector table (see MC6803U4 datasheet)
vectors $FFFE 1 RESET RESETvector
vectors $FFF0 1 SCI_irq SCIvector
vectors $FFF2 1 TOF_irq TimerOverflowFlagvector
vectors $FFF4 1 OCF_irq OutputCompareFlagvector
vectors $FFF6 1 ICF_irq InputCompareFlagvector
vectors $FFF8 1 MIR_irq MaskIntReqVector
vectors $FFFA 1 SIR_irq SwIntReqvector
vectors $FFFC 1 NMI_irq NMIvector

gecu.txt - the commented disassembler file

This file is where we look to see what the ECU is doing to our car. We haven't inserted any comments yet so if we look at the log file (dasm.log) we will see that it found 0 comments. The file is too large to list here but lets have a look at a few key sections and add a couple comments. To look at the file I find it easiest to use notepad. Unless you've changed the default behavior of windows notepad will automatically open the file when you double-click on gecu.txt.

;
; DHC11 - 68HC11 Disassembler v1.1 (c) Copyright 2000 Tech Edge Pty. Ltd.
;
;        http://www.techedge.com.au/utils/dhc11.htm
;
;
; Disassembly of gecu.bin - from $C000 to $FFFF
;
*** Internal Registers as we defined in the disassembler contol file
PORT1_DDR =    $0000

PORT2_DDR =    $0001
PORT1_DATA =    $0002
PORT2_DATA =    $0003
TMR_CSR    =    $0008
TMR_CNTd =    $0009
TMR_OCR1d =    $000B
TMR_ICR1d =    $000D
SCI_RMCR =    $0010
SCI_TRCSR =    $0011
SCI_RX    =    $0012
SCI_TX    =    $0013
TMR_CNTAd =    $0015
TMR_CR1    =    $0017
TMR_CR2    =    $0018
TMR_SR    =    $0019
TMR_OCR2d =    $001A
TMR_OCR3d =    $001C
TMR_ICR2d =    $001E
L0040    =    $0040
L0041    =    $0041
L0042    =    $0042
L0043    =    $0043
L0044    =    $0044
L0045    =    $0045
L0046    =    $0046
L0047    =    $0047
*** Internal RAM variables as we defined in the disassembler control file
cmd_throttle_valve_angle =    $0048
cmd_air_temp =    $0049
cmd_water_temp =    $004A
cmd_injection_duration_msb =    $004B
cmd_ignition_advance =    $004D
<snip>
*** Start of code execution (notice code label)
C000                    RESET_00:
C000    8E 00 FF            ldS    #$00FF
C003    4F                  clrA   
C004    B7 18 12            staA    L1812
C007    86 D6               ldaA    #$D6
*** Instruction with a reference to a variable as defined in the disassembler control file
C009    97 02               staA    PORT1_DATA
<snip>
*** This section is where the internal self test is activated - Lets add some comments !
C3C9    C4 7F           LC3C9    andB    #%01111111
C3CB    C1 05               cmpB    #$05 
C3CD    24 AC               bcc    LC37B
C3CF    37                  pushB   
C3D0    BD ED F6            call    LEDF6
C3D3    33                  popB   
C3D4    86 1A               ldaA    #$1A
C3D6    97 11               staA    SCI_TRCSR
*** Go to this section in the listing file and type in the * and comment
C3D8    CE C3 F9            ldX    #$C3F9   * load the base address of the diagnostic jump table
C3DB    58                  lslB            * double diagnostic test code to account for double word offset in table
C3DC    3A                  aBX             * add offset to jump table base
C3DD    EE 00               ldX    0, X     * get jump address from table
C3DF    AD 00               call    0, X    * start execution from jump address
C3E1    86 0A           LC3E1    ldaA    #$0A
C3E3    97 11               staA    SCI_TRCSR
C3E5    4F                  clrA   
C3E6    5F                  clrB   
C3E7    DD D3               stD    L00D3
C3E9    96 D5               ldaA    L00D5
C3EB    84 7C               andA    #%01111100
C3ED    97 D5               staA    L00D5
C3EF    C6 FF               ldaB    #$FF
C3F1    D7 D0               staB    L00D0
C3F3    BD ED F6            call    LEDF6
C3F6    7E C3 7B            jmp    LC37B
;
;
C3F9                    DIAG_jump_vectors:
C3F9                        dw    DIAG_test_fuel_pump
C3FB                    LC3FB:    dw    DIAG_test_injectors
C3FD                    LC3FD:    dw    DIAG_test_coil
C3FF                    LC3FF:    dw    DIAG_test_vae
C401                    LC401:    dw    DIAG_test_overboost
;

Once you have entered the 5 comments, save and close the commented disassembly file and try running the dasm.bat file again.
Now when you look at the log file you should see that 5 comments were parsed and subsequently added.

This process continues until you understand the sections of code you are interested in (or all of it).

Have fun !