Hi guys,
Here is my Map Switching project for MED9.1
Since there are numerous versions of the MED9.1 binary, each with its own map locations, ram variables and free space, it’s never going to be as easy as just copying and pasting the examples. Rather, the best way is to work through the example and then apply that to your own bin
This post is best read in conjunction with these other posts on
storing data in the eeprom and
interacting with the cluster.
Pulling it all together, we need a few things:
1) A way for the ecu to ‘remember’ what maps are currently selected. It would be no use if we had to re-select our maps every time we switched the car off and on again! Fortunately we can store a variable representing our selection in the e2p.
2) A way of interacting with the driver so you can select a map and get feedback about what you have chosen. I have a manual car without cruise control, so I decided to use clutch and brake pedals for choosing my maps and the rev counter to show the selection.
3) Some maps to select between. In this example, I have just used maps that already have multiple copies. For example in my bin I have three copies of LDRXN of which only the first seems to be in use (at least, only the first has a reference to it in IDA).
The code to select which map is current is in fact the most straightforward of this whole example. All the hard work is already done and described in my other posts about the
ram mirror and
cluster interaction. My work on the cluster interaction meant that I had a handy variable that I could check against and jump to a specific location based thereupon. You’ll need the information in those posts to make sense of this post.
Just like in the other posts, in order to patch in my changes I needed an entry point into the code. In this case, just before the code reads the location of LDRXN, I insert a jump to my new routine. The new routine then compares what my map count variable is set to and jumps back again with a suitable location for LDRXN. This is all there is to it – depending on the value of byte_7FABF7 I jump to one of three places. In each of those places I load up an address and then jump back to the main program. The code below corresponds to LDRXN. If you wanted additional maps (e.g. LAMFA), then simply repeat the code below for the extra maps you require.
ROM:0047C850 loc_47C850: # CODE XREF: ROM:00519790j
ROM:0047C850 lbz r10, byte_7FABF7 (my non-volatile map count variable. Check it against 0, 1 & 2)
ROM:0047C854 cmpwi r10, 0
ROM:0047C858 beq loc_47C86C
ROM:0047C85C cmpwi r10, 1
ROM:0047C860 beq loc_47C878
ROM:0047C864 cmpwi r10, 2
ROM:0047C868 bge loc_47C884
ROM:0047C86C
ROM:0047C86C loc_47C86C: # CODE XREF: ROM:0047C858j
ROM:0047C86C lis r10, ((off_5CEF48+0x10000)@h) (load an appropriate address pointing at the address of LDRXN 0, 1 or 2)
ROM:0047C870 addi r10, r10, -0x10B8 # off_5CEF48
ROM:0047C874 b loc_519798 (jump back to the main program)
ROM:0047C878 # ---------------------------------------------------------------------------
ROM:0047C878
ROM:0047C878 loc_47C878: # CODE XREF: ROM:0047C860j
ROM:0047C878 lis r10, ((off_5CEF4C+0x10000)@h)
ROM:0047C87C addi r10, r10, -0x10B4 # off_5CEF4C
ROM:0047C880 b loc_519798
ROM:0047C884 # ---------------------------------------------------------------------------
ROM:0047C884
ROM:0047C884 loc_47C884: # CODE XREF: ROM:0047C868j
ROM:0047C884 lis r10, ((off_5CEF50+0x10000)@h)
ROM:0047C888 addi r10, r10, -0x10B0 # off_5CEF50
ROM:0047C88C b loc_519798
Worth noting here is that the address I load up (e.g. off_5CEF48) is not the direct address of the LDRXN map. In fact it’s an address that in turn contains the address of the LDRXN map. So 5CEF48 actually points at 5CED28 which is the first LDRXN map. Why it’s like that, I don’t know. Fortunately IDA sorts it all out.
ROM:005CEF48 off_5CEF48: .long unk_5CED28 # DATA XREF: ROM:loc_47C86Co
ROM:005CEF4C off_5CEF4C: .long word_5CED6A # DATA XREF: ROM:loc_47C878o
ROM:005CEF50 off_5CEF50: .long dword_5CEDAC # DATA XREF: ROM:loc_47C884o
Q&AWhat bin is all this based upon?MED9.1
Audi S3 8P MY2007 UK spec
8P0907115H
0261S02342
387951
How do I change maps?The maps are changed like this. Without starting the car, switch on the ignition. Press the brake pedal and keep it pressed. Now press and release the clutch pedal. Every time you press the clutch pedal, the rev counter needle will flick 1000 rpm -> 2000 rpm -> 3000 rpm -> 4000 rpm -> 1000 rpm. That represents what you’ve chosen (1 to 4).
What map am I currently on? I was running out of pedals here, so you can’t really just view your active selection. View and change are combined. Every time you press that clutch pedal, you move to the next map and the rpm needle shows you what’s active. Pressing the pedal will change you to the next selection, but since there are only four selections at the moment you’ll be fine
I get an oil pressure alarm? Don’t be alarmed if you get an oil pressure alarm whilst the engine is off. The cluster thinks the engine is running since it sees rpm, but it isn’t getting a corresponding oil pressure signal. Once you’ve chosen your map, you can switch the ignition off and back on again to clear any false alarms that popped up. Any genuine alarms will be persistent and should be investigated!
My variable counts from 1 to 4 but there are only three LDRXN maps? That’s right. Right now 3 and 4 both select the third LDRXN map. Code it how you want e.g. 4 might choose the same LDRXN as 3 but have a different LAMFA map instead. It’s your map switching routine
How do I get the RAM dumps to figure out the RAM locations? Hmm, that’s a good one. I used a ram logger based on an Arduino, but you need some hardware and software for that (which I made myself). The best suggestion I’ve got is disassemble my bin that I’ve posted plus the internal and external RAM dumps I’ve posted and line up the cross references against your own bin. Also, if there is a popular bin that a lot of people use, I could load that into my bench ecu and dump the ram contents on your behalf.
How do you know what general purpose registers are free? Read a bit ahead in the code. If you see something like this
ROM:0047C878 lis r10, ((off_5CEF4C+0x10000)@h)
It means that the contents of r10 are being loaded with a new value. Therefore it’s safe enough to use r10 since the original code is going to load a new value into r10 anyway. Just make sure the original code doesn’t check r10 between you using it and the original code loading a new value in it!
What ram is free? Check in IDA for cross references. I picked a value towards the end of the external ram that had no references to it in IDA.
Could you do this with the CCS buttons instead? Of course! To find the variables that contain the CCS buttons, try this. CCS buttons are Measuring Block 67-2. Work through
this post on Measuring Blocks and you’ll end up with the memory and ram locations.
What should I look for/at in IDA? I’ve attached my IDA project containing all these patches. I’ve also included my bin (both stock and patched). This is where I’ve patched:
004F 7B60 - jump from original code to new cluster interaction code at 0047 C750
0047 C750 - new cluster interaction code
0051 9790 - jump from original code to new LDRXN address code at 0047 C850
0047 C850 - new LDRXN address code
What else is in the bin? There are a few other patches in there as well at 0047 C650. They relate to measuring blocks though and aren’t necessary here. Some of the maps have been changed as well in my attempts at a mild stage 1 tune. I really suggest you don’t copy them though, since I’m better at the software than I am at the tuning!!
I hope it helps and motivates you to create your own map switching routine
Post up how you get on and I’ll help as best I can.