Category Archives: Development

DPF Light Patch

This is meant for programmers or at least folks who understand coding in general. Here I am going to show how I implemented the “DPF light patch”, part of Diesel ECU Patch v1, on my former (Euro 4) car.
DPF Light Patch - Active Regeneration In Progress

Actual source code, updated to C++14:

// Copyright SubaruDieselCrew (2011-2016)
// c++14

#include <array>
#include <chrono>
#include "sh.h"
#include "JZ2F401A.h"

using namespace std::chrono;

 * @brief DPF light flashing modes (stock)
enum class DPFLightMode {
    off = 0,
     * @brief soot-high warning aka vehicle speed request
    on_steady = 1,
     * @brief error
     * (multiple causes: compulsory regeneration required, oil dilution critical, ash overfill, DPF limp-home mode;
     * see )	 *
    flashing = 2,

 * @brief time resolution (= CAN frame ID 0x600 interval)
constexpr auto interval {50ms};
 * @brief stock period for flashing mode is 800 ms,
 *        does not have to match stock here
constexpr auto dpfLightPeriod {800ms};
 * @brief defines DPF light output over time when active regeneration is on
constexpr std::array<bool, dpfLightPeriod / interval> dpfLightCustomPattern
{   {   1, 1, 0, 0,  1, 1, 0, 0,
        0, 0, 0, 0,  0, 0, 0, 0

 * @brief Implement custom flashing mode.
 * Called every 50 ms (CAN-ID 0x600 interval)
 * from patched stock function "calcDpfLight".
 * Standard error flashing mode already handled by untouched
 * stock subroutine portion and this case this function won't get called.
void calc_DPFLight_continue()
    // needed as original functionality has been overwritten for hook instructions
    if (DPFLightMode(*DPFLightModeEnum_b) == DPFLightMode::on_steady)
        *DPFLight_bool = true;

    // at this point DPFLightMode == DPFLightMode::off
    if (!*DPF_Regeneration_bool_SSM)
        *DPFLight_bool = false;

    // at this point active DPF regeneration is ON, do custom flashing
    // reusing DPF light counter var is safe
    auto counter = *DPFLightCounter_b;
    if (++counter >= dpfLightCustomPattern.size())
        counter = 0;
    *DPFLightCounter_b = counter;
    *DPFLight_bool =;

// Copyright (c) 2011 SubaruDieselCrew

	For stock ROM:
	Model	2009/2010 Impreza Turbo Diesel 2.0 6MT EDM 110 kW / 150 PS
	ROMID	6644D87207
	CID		JZ2F401A
	CVN		F5AD7142 FB841734
	PAK		22611AP283

#ifndef JZ2F401A_H
#define JZ2F401A_H

#include "diesel_rom.h"

// RAM vars
static auto const DPFLight_bool = reinterpret_cast<volatile bool*>(0xFFFF9C1E);
static auto const DPFLightModeEnum_b = reinterpret_cast<volatile int8_t*>(0xFFFF9C1F);
static auto const DPFLightCounter_b = reinterpret_cast<volatile uint8_t*>(0xFFFF9C53);
static auto const DPF_Regeneration_bool_SSM = reinterpret_cast<volatile bool*>(0xFFFFB222);

Disassembly using objdump which is part of GNU binutils. Binary had been generated by GCC. I added quite verbose comments for those who don’t know SH (SuperH) disassembly well enough.

void calc_DPFLight_continue()
  c: 91 1b  mov.w   0x46,r1 ! 9c1f   // r1 = &DPFLightModeEnum_b = 0xFFFF9C1F (sign extension of value 0x9c1f)
  e: 60 10  mov.b   @r1,r0           // r0 = DPFLightModeEnum_b
 10: 88 01  cmp/eq  #1,r0            // DPFLightModeEnum_b == ONsteady ?
 12: 8f 02  bf.s    0x1a             // if not --> jump
 14: 71 ff  add     #-1,r1           // r1 = 0xFFFF9C1E = &DPFLight_bool
 16: 00 0b  rts                      // return (after following instruction)
 18: 21 00  mov.b   r0,@r1           // DPFLight_bool = true

 1a: 92 15  mov.w   0x48,r2 ! b222   // r2 = &DPF_Regeneration_bool_SSM = 0xFFFFB222
 1c: 62 20  mov.b   @r2,r2           // r2 = DPF_Regeneration_bool_SSM
 1e: 63 2c  extu.b  r2,r3            // r3 = (uint8)DPF_Regeneration_bool_SSM
 20: 23 38  tst     r3,r3            // DPF_Regeneration_bool_SSM == false ?
 22: 8d 0e  bt.s    0x42             // is false --> DPFLight_bool = false; return
 24: 00 09  nop
 26: 91 10  mov.w   0x4a,r1 ! 9c53   // r1 = &DPFLightCounter_b = 0xFFFF9C53
 28: e2 0e  mov     #14,r2           // r2 = CounterMax - 2 = 14
 2a: 60 10  mov.b   @r1,r0           // r0 = DPFLightCounter_b
 2c: 60 0c  extu.b  r0,r0            // r0 = counter = (uint8)DPFLightCounter_b
 2e: 30 26  cmp/hi  r2,r0            // counter > 14 ?
 30: 8d 02  bt.s    0x38             // if yes --> DPFLightCounter_b = 0
 32: 70 01  add     #1,r0            // ++counter
 34: a0 01  bra     0x3a             // --> DPFLightCounter_b = counter
 36: 60 0c  extu.b  r0,r0
 38: e0 00  mov     #0,r0            // r0 = 0
 3a: 21 00  mov.b   r0,@r1           // DPFLightCounter_b = r0
 3c: d1 04  mov.l   0x50,r1 ! 943c0  // r1 = &table[0]
 3e: 02 1c  mov.b   @(r0,r1),r2      // r2 = table[counter]
 40: 91 04  mov.w   0x4c,r1 ! 9c1e
 42: 00 0b  rts
 44: 21 20  mov.b   r2,@r1           // DPFLight_bool = r2; return
 46: 9c 1f                  // 0x9c1f
 48: b2 22                  // 0xb222
 4a: 9c 53                  // 0x9c53
 4c: 9c 1e                  // 0x9c1e
 4e: 00 09  nop
 50: 00 09                  // 0x943c0 --> blob position 3c0
 52: 43 c0

3c0: 01 01  .word 0x0101    // table[16] = { 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
3c2: 00 00  .word 0x0000
3c4: 01 01  .word 0x0101
3c6: 00 00  .word 0x0000
3c8: 00 00  .word 0x0000
3ca: 00 00  .word 0x0000
3cc: 00 00  .word 0x0000
3ce: 00 00  .word 0x0000

As you can tell there is not much code required. Much more work, orders of magnitude (!), is necessary to reverse-engineer the related stock ROM portions in the first place, defining functions, disassembling machine instructions, naming local and global variables etc.

Usually, ROM and RAM addresses depend on the actual ROM version used. Above definitions work for outdated CID JZ2F401A (dated 2009-Sep). Same code should also work for all current Euro 4, 5 and 6 models.

Compiled binary generated from above source code is to be inserted into a free unused ROM region. On Renesas SH microprocessors (i.e. SH7058S) free ROM space is rather easy to find – just look for big chunks of continuous FF-bytes. This is because those chips erase bytes to value 0xFF. Others, e.g. Infineon TriCore series, erase their internal flash ROM to zeroes instead.

To actually make use of the added logic, I had to modify (patch) a few bytes in the original calc_DPF_light subroutine, so that after doing some of its work it will call my own function, knowing its start address (0x9400C). Usually there is no free space in between stock functions, therefore we have to apply clever patching tricks to make room for a few new instructions and/or divert execution flow.

Finally, after carefully verifying the changes applied to the stock ROM, you have to correct checksums. Flashing software usually does this anyway, perhaps asking first. Checksum correction and verification is actually very easy to do for such Denso firmware.

Providing SDC-modified ROMs is possible, however will not be free due to amount of labour involved. Contact us if you’re interested.


  • 2016-11: updated source to C++14
  • 2016-10: updated source to C++11 with Doxygen documentation
  • 2016-01: added disassembly

ScoobyRom v0.8.2 Released

What’s new in v0.8.2:
  • Export as TunerPro XDF format.
  • Support for ROM type SH72531 (1.25 MiB = 1280 KiB size)
  • Display Reflash Count if known/available (Properties-window).

Project homepage: ScoobyRom Software


Already implemented in previous version is the ability to parse and display ROM Date (year-month-day), diesel as well as petrol type encodings. May not work for all ROM types (yet), though. AFAIK, ScoobyRom is the only software parsing this Denso specific date.

ScoobyRom 0.8.2 - Properties window, CID JZ4A211B

ScoobyRom 0.8.2 – Properties window, CID JZ4A211B

TunerPro (XDF)

Due to my intense work on BMW ECUs in recent years, I got familiar with TunerPro which is a generic ROM editor. It is quite popular in the BMW community, it can handle lots of data format options. Its definition format “XDF” is XML-based as usual. As an exercise to verify XDF knowledge I implemented XDF output in ScoobyRom.

Compared to RomRaider, reading an XDF containing hundreds of tables is very fast, almost instant. TunerPro is Windows-only and closed-source however.

TunerPro 5.00.8853 screenshot, Windows 10 x64, XDF generated by ScoobyRom

TunerPro 5.00.8853 screenshot, Windows 10 x64, XDF generated by ScoobyRom


Just published my new little C# project:

ParsePID is a console application to analyse Extended/Enhanced OBD-II mode 22 capabilities, specifically for Subaru diesel and petrol control units supporting this protocol.

Go to

  • README document includes demo output, currently for a diesel and petrol model.
  • The project uses published definitions from page Extended OBD-II, saved as CSV (delimiter: tab) format.
  • There is no need to compile the code for yourself if you haven’t got own data.

As always, do not hesitate to provide feedback…

ScoobyRom v0.8.0 Released

New in v0.8.0:
  • Navigation bar visualisation.
  • Additional columns (conversion: Multiplier & Offset or NaN if not used; Axes locations: XPos, YPos)
  • Select all/none
  • RomRaider definitions export: choose whether to export all/selected/annotated tables.
  • Roughly 3 times faster when scanning whole ROM.
  • Lots of misc improvements in code at least…

Go to homepage: ScoobyRom Software
Post software specific feedback there, please!
Also, if you find this software useful consider to “like” above page as minimum feedback and motivation for future work!

ScoobyRom v0.7.1 Released

New in v0.7.1:
  • Dynamically adjust icon size (Ctrl-+, Ctrl--, Ctrl-0)
  • Edit -> Copy Table: Can paste values into existing RomRaider table, spreadsheet (LibreOffice Calc, Microsoft Excel), text editor etc.
  • Miscellaneous improvements as always.
  • Also tested on Windows 10, no changes were necessary.

Go to page: ScoobyRom Software
Enjoy and provide software specific feedback there, please!

ScoobyRom v0.7.0 Released

Go to page: ScoobyRom Software

Enjoy and provide software specific feedback there, please!

Oil Dilution Reset (K-Line, Euro4)

Applies to

Old Euro 4 models, model years (MY) 2009/2010, fitted with closed-type DPF, (therefore Impreza and Forester only?). This also means using SSM2 via Serial (K-Line) protocol, as SSM2 via CAN will not work for most maintenance operations on such old ECUs due to ROM software limitation.


After changing the engine oil, the ECU needs to be told in order to reset its oil dilution amount and ratio values – the ECU cannot detect the change by itself. By the way, Euro 4 engines do not even have an oil level switch/sensor. Also look at post Oil Dilution Graph describing ECU calculations.

By applying a little communication, dilution will be zeroed, also saved into the ECU’s EEPROM after ignition off. Otherwise this (estimated!) value might rise over time and trigger DPF light flashing having reached 10% ratio.

It seems there are still folks out there interested in this, changing oil by themselves, not at the dealership where mechanics use Subaru Select Monitor (SSM-III or newer SSM-IV) software.
I searched through some of my old notes and found the rather short and easy procedure.


Working K-Line connection to ECU. See page SSM2 via Serial.

C-style pseudo code

// only one single SSM2 address is needed
const int address = 0x27D;
byte b = Read(address);

// set bit 6
Write(address, b | 0x40);

// Wait for some time (500 ms or so is more than enough for the ECU to do its work)

// clear bit 6
b = Read(address);
Write(address, b & ~0x40);

// verify, make sure bit 6 is zero again...
b = Read(address);

Almost done! Now turn ignition off so that new content gets saved into EEPROM! I also recommend waiting for about 20 seconds, then turn ignition back on in order to query oil dilution ratio [%] and/or oil dilution amount [g] parameter(s) to be sure. These values should be zero now. See post Why and when cycling ignition matters for explanation.

In RomRaider logger definitions (version 310) these parameters are called:

P193 Oil Dilution Ratio % Öl Verdünnung Verhältniss
P236 Cumulative oil diesel entry g Öl kum Dieseleintrag

SSM2 via K-Line communication example

SND = message from tester to ECU
RCV = received response from ECU

SND: 80 10 F0 05 A8 00 00 02 7D AC
RCV: 80 F0 10 02 E8 00 6A

SND: 80 10 F0 05 B8 00 02 7D 40 FC
RCV: 80 F0 10 00 F8 40 BA

SND: 80 10 F0 05 A8 00 00 02 7D AC
RCV: 80 F0 10 02 E8 40 AA

SND: 80 10 F0 05 B8 00 02 7D 00 BC
RCV: 80 F0 10 00 F8 00 7A

SND: 80 10 F0 05 A8 00 00 02 7D AC
RCV: 80 F0 10 02 E8 00 6A


It has been confirmed to be working, using RomRaider‘s built-in Test tool (menu: Tools → Launch Test App…). Since the sequence of individual commands is not time critical, it can be done by sending SSM2 messages manually one after the other. See this RomRaider Forums thread.