The Basics of creating an ECU file (or any definition file) from a hack

By Mark Mansur
http://tunerpro.markmansur.com/

Introduction

There seems to be a growing number of questions asking how to create bin definition files provided access to a hack for a particular application. The purpose of this document is to give the reader enough information to create a definition file with which he can modify his bin, without being able to read assembly.

This document uses the ECU file format and TunerPro as the working environment and the $6E ARAP disassembly (hack) as the vehicle application. The concepts within can be applied to potentially any definition file format.

The ECU File Format

Since this document uses the ECU file format as an example, lets take a quick look at the format. The ECU file format was created for a program called PROM Edit by Wayne Blair. The format is in the public domain and is used by a number of different applications today. ECU files contain the basic information needed to convert information in a .bin file to "real world" engineering values. ECU files are defined by a 'header" which describes the global bin file properties and the ECU file itself, followed by each of the "items".

The ECU header describes the global properties of the bin file including the location of the checksum, the address range within which the checksum is calculated, and information about the ECU file itself such as the number of items contained in the file. The header must be filled out correctly in order for the checksum to be calculated correctly. Below is a list of each field in the header and a description of its' use.

ECU ID A brief description of the ECU file's intended application. Can be something like "1989 Trans Am $6E" or "GM P4 ECM"
File Size The size of the bin that this ECU describes (in hex)
Checksum Address The location within the bin at which the checksum is stored
Checksum Size The size of the checksum (usually 2 bytes)
Checksum Start Address The address at wihch to start inclusion in the checksum calculation
Checksum End Address The address at which to end inclusion in the checksum calculation
Number of Items to follow The number of items the ECU file defines. TunerPro handles this field automatically.

There are 13 fields (or "pieces of information") that are stored in each ECU item. They are listed immediately below with a brief description of what they mean.

Start Address The location of the item within the bin file measured in bytes from the beginning of the bin.
Columns The number of columns in the item.
Rows The number of rows in the item. Constants are 1 row and 1 column.
Element Size The size of the item in bytes
Bitmask If set, the item is a bitmask (or a collection of "flags", explained later).
Offset The number to be added to the result of the mathematical operation performed on the item
Operation

The mathematical operation to be performed on the data found at this locaiton. Can be
0) multiply: ( data * factor ) + Offset
1) divide: ( factor / data ) + Offset
2) "Show in hex" - Show the value as plain hex. Perform no math

Factor The number by which the data found at this location should be multiplied by or divided by. See Operation.
Map Name The name to be displayed for the item
Y Label The unit label for the y (vertical) axis of the table
Y Axis The labels for each of the rows in the table (each label must be exactly 5 characters)
X Label The unit label for the x (horizontal) axis of the table
X Axis The labels for each of the columns in the table (each label must be exactly 3 characters)

Below is a screenshot of the TunerPro ECU Item editor.

What is a hack?

A hack (or hac) is a commented disassembly of the program image (the bin file) the ECM uses to operate. A well "written" hack has clear comments for each line or range of lines that outlines what the line or group of lines of code does. These lines of code include both the actual program code (that is, the "work" thats done to operate the engine) and the parameter information (the static information, such as the spark table or max spark advance) that is used by the program code. A really good hack can be compiled again using a compiler.

An example of the information found in a hack is found immediately below. This example will be used through this paper. This particular hack is for the $6E code mask (ARAP - 1227165, '89 vette) and the author is unknown. Ellipsus (...) represent code that has been removed for clarity.

LC000: FDB $1023 ; EPROM ID code
...
LC014: FCB $90 ; %1001 0000 Air Fuel Opt Word, W/VATS
;
; Bit 0 = MANUAL XMISSION
; 1 = SINGLE FIRE MODE
; 2 = ANALOG MAF METER IN USE, (HLM)
; 3 =
; 4 = VATS ENABLE
; 5 = REQ CLSED LP FOR CAN PURGE
; 6 = USE TCC FOR SHFT LAMP CNT'L
; 7 = USE FILTER FOR AIR FLOW

...
LC01C: FCB 17 ; Advance Ref, (5.9 Deg), (Val/2.844)

...

; LV8 AE FACTOR vs DELTA LV8
;
; DELTA LV8 ABOVE MINIMUM (LC356)
;
; TBL = Mult * 128
;
; MULT Diff ld VAL\
;----------------------------------
LC358 FCB 32 ; 0.25 0
LC359 FCB 32 ; 0.25 64
LC35A FCB 40 ; 0.31 128
LC35B FCB 52 ; 0.40 192
LC35C FCB 52 ; 0.40 256

Reading the hack file

First, lets start off with two of the most basic "keywords" in reading the assembly: FCB and FDB. FCB means "a single byte is used to store this value." FDB means "a double byte is used to store this value." Second, in assembly, a dollar sign ($) means the value displayed is in hexidecimal (base-16).

With those two pieces of information in mind, lets get started interpretting the hack, starting with the first line in the example:

LC000: FDB $1023 ; EPROM ID code

LC000 in English, means "Location 000." As you know from the paragraph above, FDB means the value at this location is 2 bytes (16 bits) in size. The number found at this location is $1023 (hex 1023, or 4131 decimal). The semicolon (;) means the text to follow on the line is a comment. In this case, the comment is "EPROM ID Code". Most of us know this as the PROM ID.

Adding values to the definition file

So now the goal is to add an item to the definition file that allows us to view and edit the PROM ID. How would we do so? In TunerPro, which uses the ECU file format, you would select "Add Item" in the ECU menu which will bring up the ECU Item Editor.

Constants:

Consider the example we read in the previous section:

LC000: FDB $1023 ; EPROM ID code

We know that the item is at location 000, so the Start Address should be "0". We know the item is a single constant (that is, it is not a flag and it is not a cell from a table), so we should enter 1 for both the rows and columns. The item in the hack is an FDB, or a double byte, so the element size should be set to 2. The item is not a set of flags, so do not check "Bit Mask." PROM IDs are usually displayed in hex, so you can set the operation to "Display in Hex." Since we aren't doing any calculations on this item, we will specify 0 for both the offset and factor (they aren't used when displaying the value in raw hex). If you wish to display the PROM ID as a decimal number, set the operation to "Multiply" and set the factor to 1. Enter the title for the item, i.e. "PROM ID" (without the quotes). This item has no unit or axis labels, so we can leave these blank. You may wish to add comments to the item for other users or for yourself. You can do so in the "Additional Info / Help Field.'


Lets try creating another constant from the hack example above:

LC01C: FCB 17 ; Advance Ref, (5.9 Deg), (Val/2.844)

This item is at LC01C, or "location 01C." So you should enter 1C (getting rid of the leading zero) as the start address. Now would be a good time to confirm that you're entering/viewing address in hex within TunerPro by right-clicking in the editor window. If you're viewing addresses as integers, you should convert $01C to dec (28). FCB tells us the item is a single byte in size, so we can set "Element Size" to 1. This item is not a bit mask, so we can keep that unchecked.

Something new with this item is that we must do some math on it to convert it to a real world value (in this case, degrees of spark advance). Luckily, our hack is commented with the information we need to do this. The bin that was disassembled had a value of 17 (decimal, since there is no "$" in front of it), and the comment tells use that 17 means 5.9 degrees of spark advance. Additionally (and more importantly) the comment says that the spark advance degrees is calculated by taking the value and dividing it by 2.844. Applying this, 17/2.844 = 5.977 degrees (or 5.97 as it will be displayed in TunerPro).

With this information, you might think we need to set the operation to "Divide," however, take a closer look at what the divide operation does. It takes the factor and divides it by the value (factor/value). In this case, we don't want to take 2.844 (the factor) and divide it by 17 (the value)! What we want to do is take 17 and divide it by 2.844. We can accomplish this by multiplying 17 by 1/2.844 (or 0.351617). 17 * 0.351617 = 5.97. So we now know that we should use multiply as our operation, with a factor of 0.351617 (and an offset of zero, since we're not adding anything to the value).

In the case of a constant, the unit value is set in the X Label field. Our units for this item are "Degrees" and should be set as such in the "X Label" field. The map name in this case could be something like "Spark Advance Reference Angle" or "Base Spark Advance."

Flags (or Bit Masks)

Flags, also called "bit masks," are single bits within a byte that the code uses as switches to determine whether or not to use a particular sensor or feature (or even another line of code). Consider the following bit mask from the hack example above:

LC014: FCB $90 ; %1001 0000 Air Fuel Opt Word, W/VATS
;
; Bit 0 = MANUAL XMISSION
; 1 = SINGLE FIRE MODE
; 2 = ANALOG MAF METER IN USE, (HLM)
; 3 =
; 4 = VATS ENABLE
; 5 = REQ CLSED LP FOR CAN PURGE
; 6 = USE TCC FOR SHFT LAMP CNT'L
; 7 = USE FILTER FOR AIR FLOW

From the previous examples, we know that this item is located at offset $14 and is a single byte in size. The value in the bin that is disassembled at this offset is $90. You'll notice in the comment for the line that $90 in binary (%)is 10010000. Each 1 and 0 represent a bit, where 1 is a "set" bit and 0 is a "clear" bit. Bits are usually identified in "zero-based" fashion, which means the first bit is Bit 0. Bit zero in the binary string 10010000 is the bit furthest right (0). From the comments, we can see that bit 0 is "Manual Transmission" (as vague as that is).

Adding this item to the ECU file, we'd enter 14 (hex) as the start address, "Air Fuel Opt Word" as a map name (from the comment in the hack), we'd check "bitmask" since we know its a bitmask. We're not doing math on this item, so the offset, factor, and operation don't matter. X Unit and Y Unit don't matter, and X Axis Label doesn't matter.

To specify a label for each bit, we use Y Axis Label. Bit 0 is Row 8, Bit 7 is Row 1 (which, I know, is a little counter-intuitive). Keep in mind that each label can only be 5 characters in length (per the ECU file format specification).

Tables

Tables consist of multiple cells, displayed in multiple rows and/or columns. They are found in the bin (and therefore the hack) in subsequent, sequential locations. Consider the following example:


; LV8 AE FACTOR vs DELTA LV8
;
; DELTA LV8 ABOVE MINIMUM (LC356)
;
; TBL = Mult * 128
;
; MULT Diff ld VAL\
;----------------------------------
LC358 FCB 32 ; 0.25 0
LC359 FCB 32 ; 0.25 64
LC35A FCB 40 ; 0.31 128
LC35B FCB 52 ; 0.40 192
LC35C FCB 52 ; 0.40 256

Per the first comment for the item, this table represents AE (Acceleration Enrichment) Factor vs. Delta ("change in") LV8 (Load Variable), or, abbreviated, "AE Factor vs. Delta LV8."

We can gather that there are 5 cells in this table (by the fact that there are 5 lines after the commented declaration of the table), with each cell being a single byte (FCB). I prefer to define single dimensioned tables in a single-column, multi-row (though some people prefer single-row, multi-column). To do so, set the columns to 1 and the rows to 5. Set the element size to 1. The first cell in the table is at location 358, so we'll set the start address to 358. Per the comment in the hack, the value in the bin equals the multiplier (the real world value we're interested in) times 128. Using this information, the multiplier is the bin value divided by 128 (or the bin value times 1/128). So we should set the operation to multiply, the factor to 1/128 (or 0.390625), and the offset to 0.

The Y Label (units) in this case is LV8 (enter "LV8"), and the X Label (units) is the multiplier (enter "Multiplier"). For the Y Axis labels (LV8), per the comments for each cell, row 1 is "0" (you may enter 2 spaces, since the label has to be 3 characters, or tunerpro will enter these for you if you don't), 2 is "64", 3 is "128" and so on. That should complete your table. Check your work by opening the table in the editor and validating that the cells match the data in the hack (if using the same bin that the hack represents) or by validating that the data in your bin looks realistic.

In Conclusion

The ECU file format is the basis for your bin editing. If the ECU file is inaccurate, you could be changing values incorrectly or "corrupting" values that you're unaware of, both of which could result in engine damage (if you run on the bad bin for too long), especially if you're using an emulator. As a result, its extremely important that you take your time when creating a bin definition file and double and triple check your work. Consult multiple hacks if you are able.

Similarly, its extremely important that you use an ECU file that is trusted if you didn't create it yourself. You might even want to verify the ECU with a hack as you edit your bin.

That sums up the basics for creating an ECU, or any bin defintion format, from a hack.

Appendix A - Common factors and offset for converting 8 bit (1 byte) binary values to engineering units

These are guidelines and may not always be the case. Consult your hack for verification.

Engineering Unit
Factor
Offset
Degrees F
1.35
-40.0
Degrees C
.75
-40.0
Degrees Spark Advance
0.351567
0.0
MPH
1.0
0.0
LV8
1.0
0.0