Pages: [1]
Author Topic: Tricore Custom Code  (Read 6344 times)
superglitch
Jr. Member
**

Karma: +4/-0
Offline Offline

Posts: 45


« on: July 28, 2021, 10:32:59 AM »

So I'm fairly familiar with IDA Pro and writing assembly, and it's a pain.  I'm looking to start utilizing a compiler to help aid in creating more complex functions and reduce effort required to create patching programs to translate to similar ECU software.

I found Hightech's free compiler here: https://free-entry-toolchain.hightec-rt.com/

I do have that working and generating assembly without issue, my issue comes from properly writing code in order to read and write variables correctly.  I'll keep it very basic in order so we can create knowledge on how to adapt this to any problem/situation that someone is working on.

Here's the C code:
Code:
#include <tc1767.h>

void main()
{
volatile float * xa = (volatile float *)0xD000D000;
volatile float * ya = (volatile float *)0xD000D004;
volatile float * za = (volatile float *)0xD000D008;

float x = *xa;
float y = *ya;
float z = x * y;

*za = z;
}

Which does compile and generates this assembly:
Code:
main:
mov16.aa        a14, sp
sub16.a         sp, #0x18
movh            d15, #0xD001
addi            d15, d15, #-0x3000
st32.w          [a14]-4, d15
movh            d15, #0xD001
addi            d15, d15, #-0x2FFC
st32.w          [a14]-8, d15
movh            d15, #0xD001
addi            d15, d15, #-0x2FF8
st32.w          [a14]-0xC, d15
ld32.w          d15, [a14]-4
mov16.a         a15, d15
ld16.w          d15, [a15]0
st32.w          [a14]-0x10, d15
ld32.w          d15, [a14]-8
mov16.a         a15, d15
ld16.w          d15, [a15]0
st32.w          [a14]-0x14, d15
ld32.w          d2, [a14]-0x10
ld32.w          d15, [a14]-0x14
mul.f           d15, d2, d15
st32.w          [a14]-0x18, d15
ld32.w          d2, [a14]-0xC
ld32.w          d15, [a14]-0x18
mov16.a         a15, d2
st16.w          [a15]0, d15
ret16

Inserting this code causes the ECU to not respond, requiring boot to restore.  Almost as if it's stuck in a loop in the ASW.   Cry


I have manually written assembly to accomplish the same desired code and can be called without issue:
Code:
movh.a          a9, #@HIS(unk_D000C000)
lea             a9, [a9]@LOS(unk_D000C000)
ld32.bu         d15, [a9](unk_D000D000 - unk_D000C000)
ld32.bu         d14, [a9](unk_D000D004 - unk_D000C000)
mul16           d14, d15
st32.b          unk_D000D008, d14
ret16

Obviously this is an over simplification of what I'm actually trying to accomplish, but I figure it's best to keep everything simple until I can get the compiler trick to operate correctly so I can actually perform some real functions and math.

Any help with the method of writing my C or understanding what is going wrong would be greatly appreciated.
Logged
daniel2345
Full Member
***

Karma: +11/-7
Offline Offline

Posts: 197


« Reply #1 on: July 28, 2021, 11:21:46 AM »

Some Registers are used by generic functions in ASW and some are dedicated to BSW.

It changes from build to build. Try to use other Registers in generated assembler code. Then modify toolchain according correct register usage.

Maybe modifications of stack pointer (sp) are also not a good idea, depending on main loop functions or BSW.
Logged
nihalot
Full Member
***

Karma: +41/-3
Offline Offline

Posts: 117


« Reply #2 on: July 28, 2021, 12:38:19 PM »

compile the C/C++ code with -mcpu=TC1767 and -O2 or -O3 flags
That way stack ptr is usually avoided by compiler unless absolutely necessary.
Logged

www.tangentmotorsport.com

multimap/LC/rolling antilag for MG1/MED17/EDC17/MED9/EDC15

contact for reverse engineering services of any ECU/TCU
superglitch
Jr. Member
**

Karma: +4/-0
Offline Offline

Posts: 45


« Reply #3 on: July 28, 2021, 01:01:38 PM »

@nihalot that resolved the stack pointer stuff, I am now generating much simpler code

Now we are sitting at
Code:
movh.a          a15, #0xD001
lea             a15, [a15]-0x3000
ld16.w          d2, [a15]0
movh.a          a15, #0xD001
lea             a15, [a15]-0x2FFC
ld16.w          d15, [a15]0
movh.a          a15, #0xD001
mul.f           d15, d2, d15
lea             a15, [a15]-0x2FF8
st16.w          [a15]0, d15
ret16

I have not had a chance to test, but what @daniel2345 brings up. it would be best if possible to somehow inform the compiler of existing registers to use, for example if I know the mappings of a0, a1, or a9 throughout the file without the need to modify a15.  Though I do think that in this particular binary I'm testing with a15 is safe to be modified in the position I'm placing the code.

Is there a better manual out there on the compiler than -v --help?  The verbose help output is definitely something everyone should review.
Logged
d3irb
Full Member
***

Karma: +134/-1
Offline Offline

Posts: 195


« Reply #4 on: July 28, 2021, 01:39:11 PM »

https://gcc.gnu.org/onlinedocs/gcc-4.6.1/gcc/Global-Reg-Vars.html#Global-Reg-Vars

Yes, the HighTec compiler is just Tricore backend patches to GCC (GNU C Compiler) so there is a wealth of documentation dating back many years.
Logged
prj
Hero Member
*****

Karma: +1072/-480
Offline Offline

Posts: 6035


« Reply #5 on: July 29, 2021, 04:06:26 AM »

Read TriCore manual and EABI.

You're not gonna get anywhere without.
Logged

PM's will not be answered, so don't even try.
Log your car properly - WinOLS database - Tools/patches
jcsbanks
Full Member
***

Karma: +19/-3
Offline Offline

Posts: 146


« Reply #6 on: July 29, 2021, 08:17:38 AM »

Apart from the good advice already given, I never tried floats since the OS in my targets doesn't use floats, it might need some initialization that you are not having. You can use the stack though, but it is rarely needed or used when using high -O levels. Don't use a0, a1, a8, a9, your compiler isn't and will generally follow the EABI and work with existing code. Read up on upper and lower contexts, check out the calling conventions using the first variable in d4, first pointer in a4, return variable in d2 and if you are returning a pointer, a2. IIRC you can pass up to 4 variables and 4 pointers before the EABI and the compiler will use the stack.
Logged
Herleybob
Newbie
*

Karma: +5/-0
Offline Offline

Posts: 11


« Reply #7 on: August 30, 2021, 11:02:52 PM »

I use these guidelines when compiling code. 99% of the time I will be patching a function, or jumping out of the end of a call tree into my own code so I can either use the registers that hightec assigns or I can change them out to not modify existing ones. I like to just use a new function per code segment i'm writing because ghidra makes it easy to find it.

You will need to disassemble (obviously) and then inspect the asm before patching.

Code:
#define CUSTOM_VARIABLE_ONE (*((volatile unsigned char *)  0xD0003F11))
#define CUSTOM_VARIABLE_TWO (*((volatile unsigned char *)  0xD0003F12))
#define CUSTOM_VARIABLE_THREE (*((volatile unsigned char *)  0xD0003F13))

void PROCESS_EXAMPLE() {

unsigned char adc = CUSTOM_VARIABLE_ONE ;

if(adc > CUSTOM_VARIABLE_TWO ) {
CUSTOM_VARIABLE_THREE = 2;
return;
} else {
CUSTOM_VARIABLE_THREE = 1;
return;
}
}

If you don't need to repeatedly call the address, just make a pointer to the address.

Code:
void COPY_PARAM_TO_RAM() {
volatile short *delay_ms =   (int *)0xD0003F24;
volatile short *const_delay_ms =   (int *)0x800A02F8;

*delay_ms = *const_delay_ms;
}

Also, ive found multiple functions that process 2D and 3D tables, returning varying variables ( chars, shorts, longs, signed, unsigned, etc). I created a faux function that calls to the map interpolation function that i want to use, and give it the same arguments so that i can call it and then just replace the call address when i decompile and patch it in. (hopefully that made sense, im just trying to interpolate my own 2d/3d tables so i use the built in functions).

Once I compile it and then disassemble it in Ghidra, i know the call address to the actual interpolation functions, i will replace those in my code asm and verify it is putting the correct variables in the registers before it calls.

Code:
unsigned char FUNC_PROCESS_2D_TABLE_8BIT(unsigned char AXIS_LENGTH, unsigned char MONITOR ,volatile unsigned char *AXIS_VALUES, volatile unsigned char *TABLE_VALUES) 
{
    return MONITOR; // Ignore this, it isnt actually used when i disassemble (only here as a "placeholder")
}

unsigned char FUNC_PROCESS_2D_TABLE(unsigned char MONITOR, volatile unsigned char *TABLE)
{
    return MONITOR; // Ignore this, it isnt actually used when i disassemble (only here as a "placeholder")
}

unsigned char INTERPOLATE_ALL_TABLE_ETHANOL(volatile unsigned char * TABLE_ONE, volatile unsigned char * TABLE_TWO) {
FLEX_SWITCH_T1V = FUNC_PROCESS_2D_TABLE(ECT, TABLE_ONE);
FLEX_SWITCH_T2V = FUNC_PROCESS_2D_TABLE(ECT, TABLE_TWO);

return FUNC_PROCESS_2D_TABLE_8BIT(0x2, FLEX_ETH_PERC, &FLEX_SWITCH_AXIS, &FLEX_SWITCH_T1V);
}

As a final example, i'm working on adding turn signals to a SxS, something that didnt originally come with the function. See below for my code. It might not be 100% correct but it works and give me the least amount of asm.

Code:
void PROCESS_TURN_SIGNALS() {
volatile char *tmp_status =       (int *)0xD0003F20;
volatile char *flash_pos =        (int *)0xD0003F21;
volatile char *flash_count =      (int *)0xD0003F22;
volatile short *delay_ms =        (int *)0xD0003F24;
volatile short *timer =           (int *)0xD0003F26;
volatile short *num_flashes =     (int *)0xD0003F28;
volatile short *triple_timer =    (int *)0xD0003F2C;

switch(TURN_ADC_IN >> 2)  {
case 257 ... 512: // Left turn
*flash_pos = 1;
*flash_count = 0;
*tmp_status = (*tmp_status & ~0b00000011) | (*flash_pos & 0b00000011);
goto enable;
case 513 ... 768: // Right turn
*flash_pos = 2;
*flash_count = 0;
*tmp_status = (*tmp_status & ~0b00000011) | (*flash_pos & 0b00000011);
goto enable;
case 769 ... 1024: // Hazards
*flash_pos = 3;
*flash_count = 0;
*tmp_status = (*tmp_status & ~0b00000011) | (*flash_pos & 0b00000011);
goto enable;
default:
if(*flash_count > 0) {
goto enable;
}

if(*num_flashes == 0 && *triple_timer >= *timer) {
*flash_count = 5;
goto enable;
}

*tmp_status = 12; // sets outputs to high at the start of flashing.
*timer = 0;
*num_flashes = 0;
goto end;
}

enable:
if(*delay_ms >= *timer) {
*timer += 1;
goto end;
} else {
*timer = 0;
if(*flash_count != 0) *flash_count -= 1;
*tmp_status ^= (*flash_pos << 2);
*num_flashes++;
goto end;
}

end:
return;
}

In the end I will decompile my code, verify the flow looks correct and then patch it in, Make sure it isn't overwriting any registers that are needed, and then try it out. The above code needs 0 modifying before patching in and trying.


EDIT:

See below for some macros to work with bits. I have needed these quite a bit and just got the storebit working.

These will translate into:

JZ.T/JNZ.T - For getting a bit
ST.T - For storing a bit

Code:
#define GETBIT(var, bit)	(((char)(var) >> (bit)) & 1) 
#define STOREBIT(addr,bpos,b)   __asm("st.t %0,%1,%2"::"i"(addr),"i"(bpos),"i"(b))

Examples:

#define NOTAREALADDRESS   (*((volatile unsigned char *) 0xD00017AA))

/Getting a bit
char brakeStatus = GETBIT(NOTAREALADDRESS, 3);

//Setting a bit
STOREBIT(&NOTAREALADDRESS, 0x3, 0x1);
« Last Edit: September 16, 2021, 11:50:00 PM by Herleybob » Logged
Pages: [1]
  Print  
 
Jump to:  

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines Page created in 0.021 seconds with 17 queries. (Pretty URLs adds 0s, 0q)