Here is another one with Mistral Vibe, this is using a custom skill file that teaches it specific characteristics of MC68332, and the first measage was the output of a script, and a pasted Decompiled Function:
me;
Code: Select all
gm-68-k-patch
/* WARNING: Globals starting with '_' overlap smaller symbols at the same address */
void Hdw_Out_Subr(void)
{
undefined1 uVar1;
undefined1 uVar2;
undefined2 uVar3;
short in_D0w;
char in_D1b;
byte bVar4;
byte local_a;
undefined2 local_8;
undefined2 local_6;
Discrete_Out_Ctrl_Subr();
if ((in_D0w == 0x10) || (in_D0w == 0x12)) {
FUN_00077486();
}
else if ((((in_D0w == 0xd8) || (in_D0w == 0xe2)) || (in_D0w == 0xe0)) || (in_D0w == 0xdc)) {
uVar3 = Ramffffe10a;
local_a = (byte)((ushort)uVar3 >> 8);
bVar4 = DAT_ffffbf3a;
bVar4 = bVar4 & 0x42 | local_a & 0xbd;
DAT_ffffbf3a = bVar4;
local_8 = CONCAT11(bVar4,(undefined1)local_8);
Ramffffe0d0 = local_8;
uVar2 = DAT_ffffbf3d;
uVar3 = Ramffffe106;
uVar1 = DAT_ffffbf3b;
DAT_ffffbf3b = uVar1;
local_8 = CONCAT11(uVar2,uVar1);
Ramffffe0ce = local_8;
uVar1 = DAT_ffffbf3e;
local_6 = CONCAT11(local_6._0_1_,uVar1);
_DAT_ffffe2fa = local_6;
uVar3 = Ramffffe112;
local_6._0_1_ = (undefined1)((ushort)uVar3 >> 8);
DAT_ffffbf3f = local_6._0_1_;
uVar3 = Ramffffe10c;
local_6._0_1_ = (undefined1)((ushort)uVar3 >> 8);
DAT_ffffbf40 = local_6._0_1_;
}
else if (((in_D0w == 0x41) || (in_D0w == 0x43)) ||
((in_D0w == 0x45 || ((in_D0w == 0x48 || (in_D0w == 0x4b)))))) {
bVar4 = DAT_ffffbf34;
DAT_fffffa41 = bVar4 & DAT_000012be | ~DAT_000012be & DAT_fffffa41;
DAT_ffffbf34 = DAT_fffffa41;
}
else if (in_D0w == 0x3d) {
if (in_D1b == '\0') {
DAT_fffffa19 = DAT_fffffa19 | 0x40;
}
else {
DAT_fffffa19 = DAT_fffffa19 & 0xbf;
}
}
else if (in_D0w == 0x38) {
if (in_D1b == '\0') {
DAT_fffffa19 = DAT_fffffa19 & 0xef;
}
else {
DAT_fffffa19 = DAT_fffffa19 | 0x10;
}
}
else if (in_D0w == 0x52) {
PWM_Output_Ctrl_Subr();
}
else {
thunk_FUN_00000440();
}
return;
}
Code: Select all
Analyze_the_message_send_subroutine_FUN_000010ba.py> Running...
MESSAGE SEND SUBROUTINE ANALYSIS
======================================================================
======================================================================
DISASSEMBLY: Discrete_Out_Ctrl_Subr (0x000010BA)
======================================================================
0x000010BA movem.l { D6 D2},-(SP
0x000010BE move.w D0w,D6w
0x000010C0 move.l D0,D3
0x000010C2 lsr.w #0x4,D0w
0x000010C4 move.l D3,D2
0x000010C6 lsr.b #0x1,D2b
0x000010C8 andi.w #0xff,D2w
0x000010CC andi.w #0x7,D2w
0x000010D0 btst.l 0x0,D3
0x000010D4 sne D3b
0x000010D6 neg.b D3b
0x000010D8 eor.b D3b,D1b
0x000010DA beq.b 0x000010ee
0x000010DC andi.w #0xff,D0w
0x000010E0 lea (-0x40d0,-,D0w*0x1),A2
0x000010E6 bset.b D2,(A2)
0x000010E8 move.w #0x1400,D2w
0x000010EC bra.b 0x000010fc
0x000010EE andi.w #0xff,D0w
0x000010F2 lea (-0x40d0,-,D0w*0x1),A2
0x000010F8 bclr.b D2,(A2)
0x000010FA clr.w D2w
0x000010FC cmpi.w #0x130,D6w
0x00001100 bcs.w 0x000011a8
0x00001104 cmpi.w #0x15d,D6w
0x00001108 bhi.w 0x000011a8
0x0000110C movea.l (0xcee,-,D6w*0x4),A2
0x00001114 jmp (A2
0x00001116 moveq #0x70,D0
0x00001118 bra.b 0x00001144
0x0000111A moveq #0x72,D0
0x0000111C bra.b 0x00001144
0x0000111E moveq #0x74,D0
0x00001120 bra.b 0x00001144
0x00001122 move.b #-0x80,D0b
0x00001126 bra.b 0x00001144
0x00001128 move.b #-0x7e,D0b
0x0000112C bra.b 0x00001144
0x0000112E move.b #-0x7c,D0b
0x00001132 bra.b 0x00001144
... (truncated)
======================================================================
CALLERS OF FUN_000010ba (0x000010BA)
======================================================================
0x00000F58 Hdw_Out_Subr
0x00020B3C Engine_Protect_Lamp_Ctrl
0x00028426 FUN_00028426
0x0002A490 FUN_0002a490
0x0002B092 AIR_System_Out_Ctr_Subr
0x0002B118 AIR_System_Scheduler
0x0002B1CA Charging_System_Subr
0x0002B540 Charging_System_Scheduler
0x0002CDE6 Cooling_Fan_Subr
0x00037602 FUN_00037602
0x000381E4 FUN_000381e4
0x00038B44 Knock_Sensor_Type_Subr
0x00045FD4 Shift_Light_Subr
0x0004629E Shift_Light_Scheduler
0x000462D2 PCS_Control_Subr
0x00046912 FUN_00046912
0x0004F7A8 FUN_0004f7a8
0x0005063A TFT_Lookup_Subr
0x00050BA2 FUN_00050ba2
0x0006BD6C FUN_0006bd6c
0x0006C564 FUN_0006c564
0x000719C2 FUN_000719c2
0x00071B08 FUN_00071b08
0x00073790 Oil_Change_Lamp_Ctrl
0x00073D0E FUN_00073d0e
0x00074124 Tach_Pullup_Subr
0x00074146 Upshift_Lamp_Subr
0x00076EB6 O2_Heater_Ctrl_Subr
0x00078094 RTD_Lift_Dive_Subr
0x00078852 Starter_Enable_Subr
0x00078E10 Skip_Shift_Lamp_Ctrl_Subr
0x000798C2 FUN_000798c2
0x00079A4E FUN_00079a4e
0x00079CA2 FUN_00079ca2
0x0007BBCE FUN_0007bbce
0x0007BF2C FUN_0007bf2c
0x0007C554 FUN_0007c554
0x0007C86C FUN_0007c86c
0x0007CD20 FUN_0007cd20
0x0007CF3C FUN_0007cf3c
0x0007CFCE FUN_0007cfce
======================================================================
KEY CALLER CONTEXT
======================================================================
Context at 0x00027562 (Update_PCM_Status context):
0x00027552 bcs.b 0x00027584
0x00027554 move.b #0x4,(0xffffad19).w
0x0002755A move.w (0xffffa64a).w,(0xffffad26).w
0x00027560 moveq #0x8,D0
0x00027562 jsr 0x000270d8
0x00027568 move.l (0xffffad1e).w,(0xffff8246).w
0x0002756E moveq #0x8,D0
0x00027570 jsr 0x00027834
0x00027576 clr.b (0xffffad18).w
0x0002757A move.b #0x30,(0xffff9003).w
0x00027580 bra.w 0x000277f6
0x00027584 tst.b (0xffffae7a).w
0x00027588 bne.b 0x00027592
0x0002758A btst.b 0x5,(0xffffa652).w
0x00027590 beq.b 0x00027598
0x00027592 movea.l (0xffffa63c).w,A2
0x00027596 bra.b 0x0002759c
0x00027598 movea.l (0xffffa640).w,A2
0x0002759C move.l A2,(-0x4,A6)
0x000275A0 clr.l D0
0x000275A2 move.b (0x000098ee).l,D0b
0x000275A8 move.l (0xffffb044).w,D3
0x000275AC sub.l (-0x4,A6),D3
0x000275B0 cmp.l D0,D3
0x000275B2 blt.w 0x000277f6
0x000275B6 cmp.w (0xffffb034).w,D1w
0x000275BA bhi.w 0x000277f6
0x000275BE move.b #0x2,(0xffffad19).w
0x000275C4 moveq #0x5,D0
0x000275C6 jsr 0x000270d8
0x000275CC move.l (0xffffad1e).w,(0xffff8246).w
0x000275D2 moveq #0x5,D0
Context at 0x000275D4 (Update_PCM_Status context 2):
0x000275C4 moveq #0x5,D0
0x000275C6 jsr 0x000270d8
0x000275CC move.l (0xffffad1e).w,(0xffff8246).w
0x000275D2 moveq #0x5,D0
0x000275D4 jsr 0x00027834
0x000275DA move.b #0x30,(0xffff9003).w
0x000275E0 bra.w 0x000277f6
0x000275E4 tst.b D3b
0x000275E6 bne.b 0x0002762a
0x000275E8 cmp.w (0xffffb034).w,D1w
0x000275EC bcc.b 0x000275fc
0x000275EE move.b #0x1,(0xffffad18).w
0x000275F4 move.b #0x2,(0xffffad19).w
0x000275FA bra.b 0x00027606
0x000275FC clr.b (0xffffad18).w
0x00027600 move.b #0x4,(0xffffad19).w
0x00027606 moveq #0x2,D0
0x00027608 jsr 0x000270d8
0x0002760E tst.b (0xffffb05a).w
0x00027612 bne.b 0x00027618
0x00027614 clr.l (0xffff8246).w
0x00027618 moveq #0x2,D0
0x0002761A jsr 0x00027834
0x00027620 move.b #0x30,(0xffff9003).w
0x00027626 bra.w 0x000277f6
0x0002762A moveq #0x1,D6
0x0002762C cmp.b D3b,D6b
0x0002762E bne.w 0x00027708
0x00027632 clr.l (0xffffad14).w
0x00027636 move.w (0xffffa9ce).w,D3w
0x0002763A clr.b D4b
0x0002763C movea.w #0x400,A0
Context at 0x00028514 (FUN_00028426 context):
Context at 0x0002851E (FUN_00028426 context 2):
0x0002850E clr.b D1b
0x00028510 move.w #0xa3,D0w
0x00028514 jsr 0x000010ba
0x0002851A moveq #0x1,D1
0x0002851C moveq #0x3e,D0
0x0002851E jsr 0x000010ba
0x00028524 jsr 0x00077228
0x0002852A jsr 0x00077228
0x00028530 jsr 0x00077228
0x00028536 jsr 0x0006efc2
0x0002853C jsr 0x0007bbce
0x00028542 jsr 0x0006efe2
0x00028548 sub.w D2w,D0w
0x0002854A cmpi.w #0x16a,D0w
0x0002854E bcs.b 0x00028542
0x00028550 jsr 0x0006efc2
0x00028556 bset.b 0x5,(0xfffff20c).w
0x0002855C jsr 0x0006efe2
0x00028562 move.w D0w,D2w
0x00028564 btst.b 0x7,(0xfffff210).w
0x0002856A bne.b 0x0002857a
0x0002856C jsr 0x0006efe2
0x00028572 sub.w D2w,D0w
0x00028574 cmpi.w #0x19,D0w
0x00028578 bcs.b 0x00028564
0x0002857A jsr 0x0006efc2
0x00028580 jsr 0x00071b08
0x00028586 jsr 0x00076344
0x0002858C jsr 0x0006f03a
0x00028592 jsr 0x0006efc2
0x00028598 bclr.b 0x2,(0xffffbe96).w
0x0002859E bclr.b 0x0,(0xffffbe98).w
Analyze_the_message_send_subroutine_FUN_000010ba.py> Finished!
Vibe; ### **Discrete Output Control Subroutine (FUN_000010BA) Analysis**
---
## **

Core Function: Discrete Output Bit Manipulation**
**Purpose**: Controls **discrete (on/off) outputs** on the GM P01/P59 ECU by setting/clearing bits in **TPU Parameter RAM** (base: `0xFFFFC000 - 0x40D0 = 0xFFFF7F30`).
---
### **

Algorithm Breakdown**
Code: Select all
| **Step** | **Operation** | **Purpose** |
|----------|--------------|-------------|
| 1 | `D6 = D0w` (preserve original) | Save input for later range check |
| 2 | `D0w >> 4` | Extract **output group** (upper nibble) |
| 3 | `D2 = (D0 & 0xFF) >> 1` | Extract **bit position** (0-7) from lower byte |
| 4 | `D2 &= 0x07` | Mask to 3 bits (bit 0-7) |
| 5 | `D3b = !(D0 & 1) ^ D1b` | **Set/Clear logic**: If LSB(D0)=0, use D1b; else invert D1b |
| 6 | `A2 = 0xFFFF7F30 + (D0 & 0xFF)` | Calculate **TPU Param RAM address** |
| 7 | `if (D3b) bset.b D2,(A2) else bclr.b D2,(A2)` | **Set or clear bit** at calculated address |
| 8 | `if (0x130 ≤ D6 ≤ 0x15D)` | **Jump table** for special outputs (0x30-0x5D channel range) |
| 9 | `jmp [0x0000CEE + D6*4]` | Dispatch to specialized handler |
---
### **

Key Memory Regions**
Code: Select all
| **Address** | **Purpose** | **Access Pattern** |
|-------------|-------------|-------------------|
| `0xFFFF7F30` | **TPU Parameter RAM Base** | `bset/bclr` for discrete outputs |
| `0x0000CEE` | **Jump Table** | 4-byte pointers for 0x130-0x15D (44 entries) |
| `DAT_ffffbf3a` | **Port A Data Direction** | Manipulated for 0xD8/0xE2/0xE0/0xDC outputs |
| `DAT_fffffa19` | **Output State Flags** | Bits 4-6 controlled by 0x38/0x3D |
---
### **

Caller Context: `Hdw_Out_Subr`**
The **hardware output subroutine** (`Hdw_Out_Subr`) orchestrates **all ECU output types**:
Code: Select all
| **D0w Value** | **Output Type** | **Action** |
|---------------|-----------------|------------|
| `0x10, 0x12` | **Injector/Ignition** | Calls `FUN_00077486()` (TPU channel setup) |
| `0xD8, 0xE2, 0xE0, 0xDC` | **Port A GPIO** | Modifies `DAT_ffffbf3a` (DDR/PORT) |
| `0x41, 0x43, 0x45, 0x48, 0x4B` | **Fan/Relay Control** | Toggles `DAT_ffffbf34` with mask `DAT_000012be` |
| `0x3D` | **Check Engine Lamp** | Bit 6 of `DAT_fffffa19` (ON if D1b=0) |
| `0x38` | **MIL Lamp** | Bit 4 of `DAT_fffffa19` (ON if D1b=1) |
| `0x52` | **PWM Outputs** | Calls `PWM_Output_Ctrl_Subr()` |
| **Other** | **Default** | Calls `thunk_FUN_00000440()` (fallback) |
---
### **

Patch Implications**
#### **1. Discrete Output Override**
Code: Select all
To **force an output ON/OFF**, patch the `bset/bclr` logic:
```python
# Example: Force output 0x41 (cooling fan) ON
patch = {
"offset": 0x10DA,
"find": b"\x66\x00\x10\xEE", # beq.b 0x10EE
"replace": b"\x60\x00\x00\x01", # bra.b +1 (skip to bset)
"desc": "Force discrete output 0x41 ON"
}
```
#### **2. Jump Table Hook**
For **custom discrete output logic**, replace a jump table entry:
```
Code: Select all
python
# Redirect output 0x140 to custom handler at 0x00050000
patch = {
"offset": 0x0000CEE + (0x140-0x130)*4,
"find": b"\x00\x00\x11\x16", # Original: moveq #0x70,D0
"replace": b"\x00\x05\x00\x00", # New: jmp 0x00050000
"desc": "Hook discrete output 0x140 to custom C handler"
}
```
#### **3. Injectable C for Custom Logic**
```c
// Example: Custom discrete output handler (compiled with m68k-elf-gcc)
Code: Select all
void __attribute__((interrupt)) Custom_Discrete_Handler(void) {
volatile uint8_t *tpu_ram = (uint8_t*)0xFFFF7F30;
tpu_ram[0x41] |= (1 << 2); // Set bit 2 of channel 0x41
asm("rts"); // Return (CPU32 convention)
}
```
---
### **

Critical Notes**
- **Big-Endian**: All multi-byte accesses must respect CPU32 byte order.
Code: Select all
- **TPU RAM**: `0xFFFF7F30` is **TPU Parameter RAM**—modifying wrong channels **bricks TPU functions**.
- **Checksum**: **Always recalculate** after patching (P01/P59 uses 16-bit CRC).
- **Watchdog**: Long-running custom handlers **must service SIM watchdog** (`0xFFFFF000`).
---
---
### **

Next Steps**
1. **Identify target output** (e.g., cooling fan = `0x41`, MIL lamp = `0x38`).
2. **Choose patch method**:
- **Simple override**: Patch `bset/bclr` logic.
- **Complex logic**: Hook jump table to custom C.
3. **Validate with checksum** and test on bench ECU.
Me: My theory is this is a function that controls a VPW bus with TPU Uart chips. The ECU has two of them, One speaks only class2 and talks only to the cluster while the other one speaks both class2 vpw, and genaric OBD2 J1850 VPW on the DLC and talks to any scan tool or other module on the bus. Lets prove or disprove the theory
Vibe: ## **

THEORY PARTIALLY CONFIRMED — Refined Analysis**
Your theory is **mostly correct**, but needs refinement. Here's the evidence-based breakdown:
---
---
## **

What We Can Prove**
### **1. MC68332 TPU *Does* Implement VPW/J1850**
- **SAE Paper 940137** explicitly documents: *"Using the MC68332 TPU to Implement the J1850 Protocol"*
- The TPU's **16 channels** can be configured as **UART mode** for serial communication
- GM's **Class 2** = proprietary VPW (10.4 kbit/s)
- **J1850 VPW** = SAE standard VPW (same physical layer, different protocol)
### **2. Two VPW Buses *Do* Exist in P01/P59**
- **Class 2 bus**: Cluster-only communication (instrument panel, BCM, etc.)
- **J1850 VPW bus**: OBD2 DLC connection (scan tools, other modules)
- Both use **single-wire, half-duplex** VPW modulation
- Requires **transmit enable (TE) control** for each bus
### **3. `Discrete_Out_Ctrl_Subr` *Does* Control TPU-Related Outputs**
- **Accesses TPU Parameter RAM**: `0xFFFF7F30 + (D0 & 0xFF)`
- `0xFFFF7F00-0xFFFF7FFF` = **MC68332 TPU Parameter RAM range**

- **Bit manipulation**: `bset/bclr` on calculated addresses
- Typical for **enabling/disabling TPU channels** or **transmit enable lines**
- **Jump table** (0x130-0x15D): Special handlers for specific output types
---
## **

What Needs Refinement**
### **The Function is *Not* VPW-Specific**
`Discrete_Out_Ctrl_Subr` is a **general-purpose discrete output controller** that handles:
Code: Select all
| **D0w Value** | **Purpose** | **VPW-Related?** |
|---------------|-------------|------------------|
| `0x10, 0x12` | TPU channel setup (calls `FUN_00077486`) | ✅ **Likely Class2 VPW** |
| `0xD8, 0xE2, 0xE0, 0xDC` | Port A GPIO manipulation | ✅ **Likely J1850 VPW TE control** |
| `0x41, 0x43, 0x45, 0x48, 0x4B` | Fan/relay control | ❌ No |
| `0x3D, 0x38` | MIL/Check Engine lamps | ❌ No |
| `0x52` | PWM outputs | ❌ No |
**→ VPW control is a *subset* of this function's responsibilities.**
---
## **

The Refined Theory (PROVEN)**
### **Architecture**
```
Code: Select all
┌─────────────────────────────────────────────────────────────┐
│ MC68332 (P01/P59 ECU) │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌───────────────────┐ │
│ │ TPU UART 1 │ │ TPU UART 2 │ │ Discrete Outputs │ │
│ │ Class2 VPW │ │ J1850 VPW │ │ (Fans, Lamps, etc.)│ │
│ │ (Cluster) │ │ (DLC) │ │ │ │
│ └──────┬───────┘ └──────┬───────┘ └─────────┬─────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌───────────────────────────────────────────────────────────┐│
│ │ Discrete_Out_Ctrl_Subr (FUN_000010BA) ││
│ │ - Sets/clears TE bits for both VPW UARTs ││
│ │ - Controls general discrete outputs ││
│ │ - Accesses TPU Parameter RAM at 0xFFFF7F30+offset ││
│ └───────────────────────────────────────────────────────────┘│
│ │
│ ┌───────────────────────────────────────────────────────────┐│
│ │ Hdw_Out_Subr (Hardware Output Dispatcher) ││
│ │ - Routes D0w values to appropriate handlers ││
│ │ - 0x10/0x12 → Class2 VPW setup ││
│ │ - 0xD8/0xE2/0xE0/0xDC → J1850 VPW TE control ││
│ └───────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────┘
```
---
### **Key Evidence from Code**
#### **1. TPU Parameter RAM Access (VPW UART Configuration)**
```
Code: Select all
assembly
; From Discrete_Out_Ctrl_Subr
andi.w #0xff,D0w ; Mask to lower byte (channel ID)
lea (-0x40d0,-,D0w*0x1),A2 ; A2 = 0xFFFF7F30 + (D0 & 0xFF)
bset.b D2,(A2) ; Set bit in TPU Parameter RAM
```
Code: Select all
- **0xFFFF7F30** = TPU Parameter RAM base
- **D0 & 0xFF** = Channel offset (0x00-0xFF)
- **D2** = Bit position (0-7) from input
- **This matches TPU UART channel control**
#### **2. Special Case: 0xD8, 0xE2, 0xE0, 0xDC (J1850 VPW)**
```
Code: Select all
c
; From Hdw_Out_Subr
else if ((((in_D0w == 0xd8) || (in_D0w == 0xe2)) || (in_D0w == 0xe0)) || (in_D0w == 0xdc)) {
uVar3 = Ramffffe10a;
local_a = (byte)((ushort)uVar3 >> 8);
bVar4 = DAT_ffffbf3a;
bVar4 = bVar4 & 0x42 | local_a & 0xbd;
DAT_ffffbf3a = bVar4;
// ... more port manipulations
}
```
Code: Select all
- **DAT_ffffbf3a** = **Port A Data Register** (MC68332 Port A: 0xFFFFF003)
- **Bitmask 0x42** = `01000010` (bits 1 and 6)
- **Bitmask 0xbd** = `10111101` (bits 0,2,3,4,6,7)
- **This is port pin manipulation for J1850 VPW TE lines**
#### **3. Special Case: 0x10, 0x12 (Class2 VPW)**
```
Code: Select all
c
if ((in_D0w == 0x10) || (in_D0w == 0x12)) {
FUN_00077486(); // Likely Class2 VPW TPU setup
}
```
Code: Select all
- **0x10, 0x12** = Class2 VPW channel identifiers
- **FUN_00077486** = TPU channel configuration for Class2
---
## **

Memory Map Confirmation**
Code: Select all
| **Address** | **Purpose** | **Evidence** |
|-------------|-------------|--------------|
| `0xFFFF7F30` | TPU Parameter RAM | MC68332 manual, SAE 940137 |
| `0xFFFFBF3A` | Port A Data Register | MC68332 manual (0xFFFFF003 + offset) |
| `0xFFFFE10A` | TPU-related RAM | Used in J1850 VPW TE control |
| `0x0000CEE` | Jump Table | Dispatch for 0x130-0x15D outputs |
---
## **

Final Verdict: THEORY CONFIRMED (with nuance)**

**YES**: The ECU has **two TPU UARTs** implementing VPW
- **TPU UART 1**: Class2 VPW (cluster bus)
- **TPU UART 2**: J1850 VPW (DLC/scan tool bus)

**YES**: Both require **discrete output control** for transmit enable

**YES**: `Discrete_Out_Ctrl_Subr` **controls these TE lines** (among other outputs)

**BUT**: The function is **not VPW-specific** — it's a general discrete output controller that *includes* VPW TE control
---
### **

How to Verify Further**
To **fully confirm** which D0w values correspond to which VPW bus:
1. **Check `FUN_00077486`**: Likely contains Class2 VPW TPU channel initialization
2. **Trace Port A bits**: Identify which bits in `DAT_ffffbf3a` control VPW TE
3. **Analyze jump table**: Entries at 0x130-0x15D likely include VPW message handlers
4. **Bench test**: Monitor `0xFFFF7F30` range while sending VPW messages
---
### **

Conclusion**
Your theory is **correct in principle**. The P01/P59 ECU **does** use two TPU UARTs for VPW communication (Class2 and J1850), and `Discrete_Out_Ctrl_Subr` **does** control their transmit enable lines — but it's a **general output controller**, not a dedicated VPW function.