Last active
November 26, 2025 22:18
-
-
Save VIRUXE/c21a25a70c240a07f491ab4b174246de to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * ============================================================================ | |
| * HONDA OBD0 PW0 ECU FIRMWARE - PSEUDO-C CONVERSION (OKI 66201) | |
| * ============================================================================ | |
| * | |
| * Original File: EuroPw0Datalogging.asm (7079 lines) | |
| * Target MCU: OKI MSM66201 (16-bit CMOS Microcontroller) | |
| * ECU Type: Honda PW0 (European D16A9/ZC DOHC VTEC) | |
| * | |
| * This is a FULL line-by-line pseudo-C representation of the OBD0 ECU | |
| * firmware with meaningful names for all registers, variables, and functions. | |
| * | |
| * NOTE: This is NOT compilable C code. It's a documentation format to | |
| * understand the assembly logic in a more readable way. | |
| * | |
| * ============================================================================ | |
| * OKI MSM66201 MICROCONTROLLER SPECIFICATIONS | |
| * ============================================================================ | |
| * | |
| * - Architecture: 16-bit CMOS microcontroller | |
| * - Crystal: 7.9 MHz (used in Honda ECU) | |
| * - Timer Prescaler: CLK/32 for main timers | |
| * - Internal RAM: 256 bytes (0x80-0xFF direct page) | |
| * - External RAM: 512 bytes (0x100-0x2FF) | |
| * - ROM: 32KB (0x0000-0x7FFF) | |
| * | |
| * RPM Calculation: | |
| * RPM_Coef = 7,900,000 / 32 / 8 * 60 = 1,851,562.5 | |
| * Where: | |
| * 7,900,000 = crystal frequency | |
| * 32 = timer prescaler (CLK/32) | |
| * 8 = 16 teeth on distributor / 2 (8 per crank revolution) | |
| * 60 = seconds to minutes conversion | |
| * | |
| * RPM = RPM_Coef / timer_count | |
| * | |
| * ============================================================================ | |
| * MODIFICATIONS FROM STOCK (Datalogging Version) | |
| * ============================================================================ | |
| * | |
| * 1. Serial port configured for 9600 baud datalogging | |
| * 2. Stack relocated to 0x025B for logging buffer space | |
| * 3. Fuel/ignition row indices stored for logging (storerow) | |
| * 4. Logging table at 0x7F10 defines RAM addresses to transmit | |
| * 5. Serial RX handler sends RAM values back to tuning software | |
| * | |
| * ============================================================================ | |
| * AUTHORS AND CONTRIBUTORS | |
| * ============================================================================ | |
| * | |
| * Original Disassembly: Ben Ogle | |
| * Contributors: Mr X, Nico, Malte, Anton, Ximon, Tinker_Tim | |
| * Pseudo-C Conversion: Auto-generated documentation | |
| * | |
| * ============================================================================ | |
| */ | |
| #include <stdint.h> | |
| #include <stdbool.h> | |
| /*============================================================================ | |
| * SECTION 1: OKI 66201 INSTRUCTION SET REFERENCE | |
| * ============================================================================ | |
| * | |
| * Common OKI 66201 instructions used in this firmware: | |
| * | |
| * Data Movement: | |
| * L/LB - Load word/byte into accumulator | |
| * ST/STB - Store word/byte from accumulator | |
| * MOV/MOVB - Move between registers/memory | |
| * PUSHS - Push to system stack | |
| * POPS - Pop from system stack | |
| * PUSHU - Push to user stack | |
| * POPU - Pop from user stack | |
| * XCHG - Exchange accumulator with memory | |
| * | |
| * Arithmetic: | |
| * ADD/ADDB - Add word/byte | |
| * SUB/SUBB - Subtract word/byte | |
| * MUL/MULB - Multiply (unsigned) | |
| * DIV/DIVB - Divide (unsigned) | |
| * INC/INCB - Increment | |
| * DEC/DECB - Decrement | |
| * CMP/CMPB - Compare | |
| * | |
| * Logic: | |
| * AND/ANDB - Logical AND | |
| * OR/ORB - Logical OR | |
| * XOR/XORB - Logical XOR | |
| * SLL/SLLB - Shift left logical | |
| * SRL/SRLB - Shift right logical | |
| * ROL/ROLB - Rotate left through carry | |
| * ROR/RORB - Rotate right through carry | |
| * | |
| * Bit Operations: | |
| * SB - Set bit | |
| * RB - Reset (clear) bit | |
| * MB - Move bit to/from carry | |
| * JBS - Jump if bit set | |
| * JBR - Jump if bit reset | |
| * | |
| * Branching: | |
| * J - Unconditional jump | |
| * JEQ - Jump if equal (Z=1) | |
| * JNE - Jump if not equal (Z=0) | |
| * JLT - Jump if less than (N=1) | |
| * JGE - Jump if greater or equal (N=0) | |
| * JLE - Jump if less or equal | |
| * CAL - Call subroutine | |
| * SCAL - Short call subroutine | |
| * RT - Return from subroutine | |
| * RTI - Return from interrupt | |
| * VCAL - Vector call (table-based) | |
| * | |
| * Special: | |
| * EXTND - Sign extend byte to word | |
| * SWAP - Swap high/low bytes | |
| * CLR - Clear register/memory | |
| * NOP - No operation | |
| * BRK - Software break | |
| * | |
| *============================================================================*/ | |
| /*============================================================================ | |
| * SECTION 2: OKI 66201 HARDWARE REGISTER DEFINITIONS | |
| * ============================================================================ | |
| * | |
| * These are the hardware registers at fixed addresses in the OKI 66201. | |
| * Register names match the official OKI documentation. | |
| * | |
| *============================================================================*/ | |
| /* ========== CPU CORE REGISTERS ========== */ | |
| /* These occupy addresses 0x00-0x0F in the direct page */ | |
| #define REG_PSW (*(volatile uint16_t*)0x0004) /* Processor Status Word (16-bit) */ | |
| #define REG_PSWL (*(volatile uint8_t*)0x0004) /* PSW Low byte (flags: Z,N,C,V) */ | |
| #define REG_PSWH (*(volatile uint8_t*)0x0005) /* PSW High byte (interrupt enable) */ | |
| /* | |
| * PSWH bit definitions: | |
| * Bit 0: DD (Data page Direct) - when set, allows 16-bit direct addressing | |
| * Bit 1-3: Reserved | |
| * Bit 4: Bank select for user stack | |
| * Bit 5-7: Interrupt priority level | |
| */ | |
| #define REG_ACC (*(volatile uint16_t*)0x0006) /* Accumulator (16-bit A register) */ | |
| #define REG_ACCL (*(volatile uint8_t*)0x0006) /* Accumulator Low byte */ | |
| #define REG_ACCH (*(volatile uint8_t*)0x0007) /* Accumulator High byte */ | |
| #define REG_LRB (*(volatile uint8_t*)0x0008) /* Local Register Bank pointer */ | |
| /* | |
| * LRB selects which 8-byte register bank (r0-r7) is active. | |
| * Common values in this firmware: | |
| * 0x00 = Bank 0 (default) | |
| * 0x10 = Bank 1 (0x080) | |
| * 0x20 = Bank 2 (0x100) | |
| * 0x21 = Bank 2+1 (0x108) | |
| * 0x22 = Bank 2+2 (0x110) | |
| * 0x40 = Bank 4 (0x200) | |
| */ | |
| #define REG_DP (*(volatile uint16_t*)0x000A) /* Data Pointer (16-bit index) */ | |
| #define REG_DPL (*(volatile uint8_t*)0x000A) /* DP Low byte */ | |
| #define REG_DPH (*(volatile uint8_t*)0x000B) /* DP High byte */ | |
| #define REG_USP (*(volatile uint16_t*)0x000C) /* User Stack Pointer */ | |
| #define REG_SSP (*(volatile uint16_t*)0x000E) /* System Stack Pointer */ | |
| /* ========== SYSTEM CONTROL REGISTERS ========== */ | |
| #define REG_SBYCON (*(volatile uint8_t*)0x0010) /* Standby Control */ | |
| /* | |
| * SBYCON bit definitions: | |
| * Bit 0: STOP mode enable | |
| * Bit 1: HALT mode enable | |
| * Bit 2: Standby mode enable | |
| */ | |
| #define REG_WDT (*(volatile uint8_t*)0x0011) /* Watchdog Timer */ | |
| /* Write 0x3C to reset watchdog, prevents system reset */ | |
| #define REG_PRPHF (*(volatile uint8_t*)0x0012) /* Peripheral Control Flag */ | |
| #define REG_STPACP (*(volatile uint8_t*)0x0013) /* Stop/Accept control */ | |
| /* Used in power-down sequencing */ | |
| /* ========== INTERRUPT CONTROL REGISTERS ========== */ | |
| #define REG_IRQ (*(volatile uint8_t*)0x0018) /* Interrupt Request flags (low) */ | |
| /* | |
| * IRQ bit definitions: | |
| * Bit 0: INT0 (external interrupt 0 - CKP signal) | |
| * Bit 1: Serial RX | |
| * Bit 2: Serial TX | |
| * Bit 3: Serial RX BRG | |
| * Bit 4: Timer 0 overflow | |
| * Bit 5: Timer 0 compare | |
| * Bit 6: Timer 1 overflow | |
| * Bit 7: Timer 1 compare | |
| */ | |
| #define REG_IRQH (*(volatile uint8_t*)0x0019) /* Interrupt Request flags (high) */ | |
| /* | |
| * IRQH bit definitions: | |
| * Bit 0: Timer 2 overflow | |
| * Bit 1: Timer 2 compare | |
| * Bit 2: Timer 3 overflow | |
| * Bit 3: Timer 3 compare | |
| * Bit 4: A/D conversion complete | |
| * Bit 5: PWM timer | |
| * Bit 6: Serial TX BRG | |
| * Bit 7: INT1 (external interrupt 1 - CYP/TDC signal) | |
| */ | |
| #define REG_IE (*(volatile uint16_t*)0x001A) /* Interrupt Enable (16-bit) */ | |
| #define REG_IEL (*(volatile uint8_t*)0x001A) /* IE Low byte */ | |
| #define REG_IEH (*(volatile uint8_t*)0x001B) /* IE High byte */ | |
| /* Bits correspond to IRQ/IRQH - set bit to enable interrupt */ | |
| #define REG_EXION (*(volatile uint8_t*)0x001C) /* External I/O Enable */ | |
| /* ========== PORT REGISTERS (I/O PINS) ========== */ | |
| /* Each port has: Data, Direction (IO), and Special Function (SF) registers */ | |
| /* --- Port 0 (General Purpose + CEL light) --- */ | |
| #define REG_P0 (*(volatile uint8_t*)0x0020) /* Port 0 Data */ | |
| #define REG_P0IO (*(volatile uint8_t*)0x0021) /* Port 0 Direction (1=output) */ | |
| /* | |
| * P0 bit assignments: | |
| * P0.3: Purge control solenoid output | |
| * P0.6: CEL dashboard light output (0=on, 1=off) | |
| * P0.7: A/C clutch relay output | |
| */ | |
| /* --- Port 1 (VTEC + CEL LED) --- */ | |
| #define REG_P1 (*(volatile uint8_t*)0x0022) /* Port 1 Data */ | |
| #define REG_P1IO (*(volatile uint8_t*)0x0023) /* Port 1 Direction */ | |
| /* | |
| * P1 bit assignments: | |
| * P1.0: VTEC error indicator (1 if code 3 error) | |
| * P1.1: VTEC solenoid output (1=engaged) | |
| * P1.2: CEL LED on ECU case output | |
| */ | |
| /* --- Port 2 (Fuel Injectors) --- */ | |
| #define REG_P2 (*(volatile uint8_t*)0x0024) /* Port 2 Data */ | |
| #define REG_P2IO (*(volatile uint8_t*)0x0025) /* Port 2 Direction */ | |
| #define REG_P2SF (*(volatile uint8_t*)0x0026) /* Port 2 Special Function */ | |
| /* | |
| * P2 bit assignments: | |
| * P2.0: Injector 1 driver (0=open, 1=closed) | |
| * P2.1: Injector 2 driver | |
| * P2.2: Injector 3 driver | |
| * P2.3: Injector 4 driver | |
| * P2.4: Limp mode indicator (1=limp mode active) | |
| */ | |
| /* --- Port 3 (Timer I/O + Ignition) --- */ | |
| #define REG_P3 (*(volatile uint8_t*)0x0028) /* Port 3 Data */ | |
| #define REG_P3IO (*(volatile uint8_t*)0x0029) /* Port 3 Direction */ | |
| #define REG_P3SF (*(volatile uint8_t*)0x002A) /* Port 3 Special Function */ | |
| /* | |
| * P3 bit assignments: | |
| * P3.6: Timer 0 output (TM0IO) - Ignitor coil driver | |
| */ | |
| /* --- Port 4 (Power sense + External interfaces) --- */ | |
| #define REG_P4 (*(volatile uint8_t*)0x002C) /* Port 4 Data */ | |
| #define REG_P4IO (*(volatile uint8_t*)0x002D) /* Port 4 Direction */ | |
| #define REG_P4SF (*(volatile uint8_t*)0x002E) /* Port 4 Special Function */ | |
| /* | |
| * P4 bit assignments: | |
| * P4.1: Main power detect input (1=power OK) | |
| */ | |
| /* --- Port 5 (Miscellaneous inputs) --- */ | |
| #define REG_P5 (*(volatile uint8_t*)0x0030) /* Port 5 Data */ | |
| /* | |
| * P5 bit assignments: | |
| * P5.1: Rear window de-mister input | |
| */ | |
| /* ========== TIMER REGISTERS ========== */ | |
| /* OKI 66201 has 4 16-bit timers (Timer 0-3) */ | |
| /* --- Timer 0 (Injector timing / Ignition output) --- */ | |
| #define REG_TM0 (*(volatile uint16_t*)0x0030) /* Timer 0 Counter */ | |
| #define REG_TMR0 (*(volatile uint16_t*)0x0032) /* Timer 0 Reload/Compare */ | |
| /* --- Timer 1 (Main timing / RPM measurement) --- */ | |
| #define REG_TM1 (*(volatile uint16_t*)0x0034) /* Timer 1 Counter */ | |
| #define REG_TMR1 (*(volatile uint16_t*)0x0036) /* Timer 1 Reload/Compare */ | |
| /* --- Timer 2 (Ignition timing) --- */ | |
| #define REG_TM2 (*(volatile uint16_t*)0x0038) /* Timer 2 Counter */ | |
| #define REG_TMR2 (*(volatile uint16_t*)0x003A) /* Timer 2 Reload/Compare */ | |
| /* --- Timer 3 (Auxiliary) --- */ | |
| #define REG_TM3 (*(volatile uint16_t*)0x003C) /* Timer 3 Counter */ | |
| #define REG_TMR3 (*(volatile uint16_t*)0x003E) /* Timer 3 Reload/Compare */ | |
| /* --- Timer Control Registers --- */ | |
| #define REG_TCON0 (*(volatile uint8_t*)0x0040) /* Timer 0 Control */ | |
| #define REG_TCON1 (*(volatile uint8_t*)0x0041) /* Timer 1 Control */ | |
| #define REG_TCON2 (*(volatile uint8_t*)0x0042) /* Timer 2 Control */ | |
| #define REG_TCON3 (*(volatile uint8_t*)0x0043) /* Timer 3 Control */ | |
| /* | |
| * TCONx bit definitions: | |
| * Bit 0-2: Clock source select (CLK/1, CLK/4, CLK/16, CLK/32, etc.) | |
| * Bit 3: Output mode | |
| * Bit 4: Timer enable | |
| * Bit 5: Count direction | |
| * Bit 6: Capture/Compare select | |
| * Bit 7: Output polarity | |
| * | |
| * TCON2 special bits for ignition: | |
| * Bit 2: TM0IO output state (ignitor driver) | |
| * Bit 3: TM0IO primer - loaded into bit 2 on compare match | |
| */ | |
| #define REG_TRNSIT (*(volatile uint8_t*)0x0046) /* Timer Transit register */ | |
| /* | |
| * TRNSIT.0: Timer transit flag, used in RPM overflow detection | |
| */ | |
| /* ========== SERIAL COMMUNICATION REGISTERS ========== */ | |
| /* Used for datalogging communication with tuning software */ | |
| /* --- Serial TX (Transmit) --- */ | |
| #define REG_STTM (*(volatile uint8_t*)0x0048) /* Serial TX Timer */ | |
| #define REG_STTMR (*(volatile uint8_t*)0x0049) /* Serial TX Timer Reload */ | |
| #define REG_STTMC (*(volatile uint8_t*)0x004A) /* Serial TX Timer Control */ | |
| #define REG_STCON (*(volatile uint8_t*)0x0050) /* Serial TX Control */ | |
| #define REG_STBUF (*(volatile uint8_t*)0x0051) /* Serial TX Buffer */ | |
| /* --- Serial RX (Receive) --- */ | |
| #define REG_SRTM (*(volatile uint8_t*)0x004C) /* Serial RX Timer */ | |
| #define REG_SRTMR (*(volatile uint8_t*)0x004D) /* Serial RX Timer Reload */ | |
| #define REG_SRTMC (*(volatile uint8_t*)0x004E) /* Serial RX Timer Control */ | |
| #define REG_SRCON (*(volatile uint8_t*)0x0054) /* Serial RX Control */ | |
| #define REG_SRBUF (*(volatile uint8_t*)0x0055) /* Serial RX Buffer */ | |
| #define REG_SRSTAT (*(volatile uint8_t*)0x0056) /* Serial Status */ | |
| /* | |
| * SRSTAT bit definitions: | |
| * Bit 2: Framing error | |
| * Bit 3: Overrun error | |
| */ | |
| /* ========== A/D CONVERTER REGISTERS ========== */ | |
| /* 8-channel 10-bit ADC for sensor readings */ | |
| #define REG_ADSCAN (*(volatile uint8_t*)0x0058) /* ADC Scan Control */ | |
| #define REG_ADSEL (*(volatile uint8_t*)0x0059) /* ADC Channel Select */ | |
| /* ADC Result Registers (10-bit values, right-aligned in 16-bit register) */ | |
| #define REG_ADCR0 (*(volatile uint16_t*)0x0060) /* ADC Ch0 - Multiplexed (ECT/IAT via IC6) */ | |
| #define REG_ADCR0H (*(volatile uint8_t*)0x0061) /* ADC Ch0 High byte */ | |
| #define REG_ADCR1 (*(volatile uint16_t*)0x0062) /* ADC Ch1 - Window de-mister (B17) */ | |
| #define REG_ADCR1H (*(volatile uint8_t*)0x0063) /* ADC Ch1 High byte */ | |
| #define REG_ADCR2 (*(volatile uint16_t*)0x0064) /* ADC Ch2 - O2 Sensor Secondary (Lambda 2) */ | |
| #define REG_ADCR2H (*(volatile uint8_t*)0x0065) /* ADC Ch2 High byte */ | |
| #define REG_ADCR3 (*(volatile uint16_t*)0x0066) /* ADC Ch3 - O2 Sensor Primary (Lambda 1) */ | |
| #define REG_ADCR3H (*(volatile uint8_t*)0x0067) /* ADC Ch3 High byte */ | |
| #define REG_ADCR4 (*(volatile uint16_t*)0x0068) /* ADC Ch4 - PA Sensor (Atmospheric) */ | |
| #define REG_ADCR4H (*(volatile uint8_t*)0x0069) /* ADC Ch4 High byte */ | |
| #define REG_ADCR5 (*(volatile uint16_t*)0x006A) /* ADC Ch5 - MAP Sensor (Manifold Pressure) */ | |
| #define REG_ADCR5H (*(volatile uint8_t*)0x006B) /* ADC Ch5 High byte */ | |
| #define REG_ADCR6 (*(volatile uint16_t*)0x006C) /* ADC Ch6 - Alternator / Battery */ | |
| #define REG_ADCR6H (*(volatile uint8_t*)0x006D) /* ADC Ch6 High byte */ | |
| #define REG_ADCR7 (*(volatile uint16_t*)0x006E) /* ADC Ch7 - TPS (Throttle Position) */ | |
| #define REG_ADCR7H (*(volatile uint8_t*)0x006F) /* ADC Ch7 High byte */ | |
| /* ========== PWM REGISTERS ========== */ | |
| /* Pulse Width Modulation for IACV (Idle Air Control Valve) */ | |
| #define REG_PWMC0 (*(volatile uint16_t*)0x0070) /* PWM 0 Counter */ | |
| #define REG_PWMR0 (*(volatile uint16_t*)0x0072) /* PWM 0 Reload (IACV duty cycle) */ | |
| #define REG_PWMC1 (*(volatile uint16_t*)0x0074) /* PWM 1 Counter */ | |
| #define REG_PWMR1 (*(volatile uint16_t*)0x0076) /* PWM 1 Reload */ | |
| #define REG_PWCON0 (*(volatile uint8_t*)0x0078) /* PWM 0 Control */ | |
| #define REG_PWCON1 (*(volatile uint8_t*)0x007A) /* PWM 1 Control */ | |
| /*============================================================================ | |
| * SECTION 3: ECU RAM VARIABLES - MEMORY MAP | |
| * ============================================================================ | |
| * | |
| * Working RAM variables used by the ECU firmware. | |
| * OKI 66201 memory layout: | |
| * 0x0080 - 0x00FF: Direct page RAM (256 bytes, fast access via r0-r7) | |
| * 0x0100 - 0x027F: Extended RAM (flags, buffers, calculations) | |
| * | |
| * Variable naming convention: | |
| * g_xxx = global variable | |
| * f_xxx = flag byte/word | |
| * c_xxx = counter/timer | |
| * t_xxx = temporary/scratch | |
| * | |
| * Documentation source: pw0ram.txt by Ben Ogle et al. | |
| * | |
| *============================================================================*/ | |
| /* ====================== SENSOR READINGS (0x98-0xBF) ====================== */ | |
| /* --- Temperature Sensors (via analog multiplexer IC6) --- */ | |
| #define g_coolantTemp (*(volatile uint8_t*)0x0098) /* ECT: Engine Coolant Temperature */ | |
| /* Lower value = colder, Higher value = hotter. Raw ADC from multiplexed ADCR0 */ | |
| #define g_intakeAirTemp (*(volatile uint8_t*)0x0099) /* IAT: Intake Air Temperature */ | |
| /* Lower value = colder air, Higher = hotter. Raw ADC from multiplexed ADCR0 */ | |
| /* --- Voltage/Power Sensors --- */ | |
| #define g_batteryVoltage (*(volatile uint8_t*)0x009A) /* 12V Battery voltage (scaled) */ | |
| #define g_ecuVoltage5V (*(volatile uint8_t*)0x009B) /* 5V ECU internal reference */ | |
| #define g_groundReference (*(volatile uint8_t*)0x009C) /* Ground reference (should be ~0) */ | |
| /* --- Switch Inputs --- */ | |
| #define g_timingAdjustConn (*(volatile uint8_t*)0x009D) /* Timing adjust connector (pin B20) */ | |
| #define g_brakeSwitch (*(volatile uint8_t*)0x009E) /* Brake switch input (pin B9) */ | |
| #define g_inputB18 (*(volatile uint8_t*)0x009F) /* Unknown input B18 */ | |
| /* --- Oxygen Sensors --- */ | |
| #define g_o2Sensor1_raw (*(volatile uint8_t*)0x00A1) /* Primary O2 sensor (Lambda 1) */ | |
| /* Value 0-50 decimal: < 0x1F = lean, > 0x1F = rich */ | |
| #define g_o2Sensor2_raw (*(volatile uint8_t*)0x00A2) /* Secondary O2 sensor (Lambda 2) */ | |
| /* Value 0-50 decimal: < 0x1F = lean, > 0x1F = rich */ | |
| /* --- Processed Temperature --- */ | |
| #define g_tempProcessed (*(volatile uint8_t*)0x00A3) /* Processed temperature value */ | |
| #define g_iatProcessed (*(volatile uint8_t*)0x00A4) /* Processed IAT for lookups */ | |
| /* --- Alternator --- */ | |
| #define g_alternatorVolts (*(volatile uint8_t*)0x00A5) /* Alternator voltage (from ADCR6H) */ | |
| /* --- RPM Values (1-byte indices for table lookup) --- */ | |
| #define g_rpmIndexNonVtec (*(volatile uint8_t*)0x00A6) /* RPM row index for non-VTEC maps */ | |
| #define g_rpmIndexVtec (*(volatile uint8_t*)0x00A7) /* RPM row index for VTEC maps */ | |
| /* --- Atmospheric/Barometric Pressure --- */ | |
| #define g_atmPressure (*(volatile uint16_t*)0x00A8) /* Atmospheric pressure (PA sensor) */ | |
| #define g_ambientPressure (*(volatile uint8_t*)0x00A9) /* Ambient pressure (inside ECU) */ | |
| /* --- Throttle Position Sensor (TPS) --- */ | |
| #define g_tpsRaw (*(volatile uint16_t*)0x00AA) /* Raw TPS from ADCR7 (16-bit) */ | |
| #define g_tpsValue (*(volatile uint8_t*)0x00AB) /* TPS value 0-255 (with idle offset) */ | |
| #define g_tpsDelta (*(volatile uint8_t*)0x00AC) /* TPS rate of change (calculated) */ | |
| #define g_tpsDeltaOld (*(volatile uint8_t*)0x00AD) /* Previous TPS delta */ | |
| #define g_tpsCopy (*(volatile uint8_t*)0x00AE) /* TPS copy (same as AB) */ | |
| #define g_tpsRelated (*(volatile uint8_t*)0x00AF) /* TPS-related calculation */ | |
| /* --- MAP Sensor (Manifold Absolute Pressure) --- */ | |
| #define g_mapSensorRaw (*(volatile uint16_t*)0x00B0) /* Raw MAP from ADCR5 (16-bit) */ | |
| #define g_mapSensorHigh (*(volatile uint8_t*)0x00B1) /* MAP sensor high byte */ | |
| #define g_mapFiltered (*(volatile uint16_t*)0x00B2) /* Filtered MAP (follows B4 slowly) */ | |
| #define g_mapFilteredHigh (*(volatile uint8_t*)0x00B3) /* Filtered MAP high byte */ | |
| #define g_mapImageRaw (*(volatile uint8_t*)0x00B4) /* MAP image 0-DF before delta adjust */ | |
| /* | |
| * MAP Image format: | |
| * High nibble (4 bits): Column index in fuel/ignition maps | |
| * Low nibble (4 bits): Interpolation value (0=use column, F=use column+1) | |
| */ | |
| #define g_mapImageFinal (*(volatile uint8_t*)0x00B5) /* MAP image after delta adjustment */ | |
| #define g_mapInitialHigh (*(volatile uint8_t*)0x00B6) /* Initial MAP high (for code 5 check) */ | |
| #define g_mapDeltaCalc (*(volatile uint8_t*)0x00B7) /* Calculated from B4 in delta_map */ | |
| /* --- RPM Measurement --- */ | |
| #define g_rpmTimerDelta (*(volatile uint16_t*)0x00B8) /* Timer1 delta (current - previous) */ | |
| #define g_rpmTimerDeltaHi (*(volatile uint8_t*)0x00B9) /* Delta high byte */ | |
| #define g_rpmTimerCount (*(volatile uint16_t*)0x00BA) /* Current RPM timer count */ | |
| /* | |
| * RPM = 1,851,562.5 / g_rpmTimerCount | |
| * This is the timer ticks for 45° of crank rotation | |
| */ | |
| #define g_rpmTimerCountHi (*(volatile uint8_t*)0x00BB) /* RPM timer high byte */ | |
| #define g_rpmAccelDecel (*(volatile uint16_t*)0x00BC) /* RPM change rate (accel/decel) */ | |
| /* Calculated from BA, positive = accelerating, f_accelFlags.4 = 1 if accel */ | |
| #define g_rpmAverage (*(volatile uint16_t*)0x00BE) /* Average of last 4 RPM samples */ | |
| #define g_rpmAverageHi (*(volatile uint8_t*)0x00BF) /* Average high byte */ | |
| /* ====================== TIMING & RPM BUFFERS (0xC0-0xCF) ====================== */ | |
| #define g_rpmSmoothed (*(volatile uint16_t*)0x00C0) /* Asymptotic smoothed RPM */ | |
| /* | |
| * Follows g_rpmAverage asymptotically: | |
| * C0 = (C0 - C0*er0/10000h) + (A*er0/10000h) where er0 = 0x3000 or 0xD000 | |
| */ | |
| #define g_rpmSmoothedHi (*(volatile uint8_t*)0x00C1) /* Smoothed high byte */ | |
| #define g_idleRpmError (*(volatile uint16_t*)0x00C2) /* |current RPM - target idle| */ | |
| #define g_idleRpmErrorHi (*(volatile uint8_t*)0x00C3) /* Error high byte */ | |
| #define g_vehicleSpeedWord (*(volatile uint16_t*)0x00C4) /* Vehicle speed (16-bit) */ | |
| #define g_vehicleSpeedHi (*(volatile uint8_t*)0x00C5) /* Speed high byte */ | |
| #define g_timer1Previous (*(volatile uint16_t*)0x00C6) /* Previous Timer1 capture */ | |
| #define g_timer1Capture (*(volatile uint16_t*)0x00C8) /* Timer1 value at INT0 (CKP) */ | |
| #define g_timerOverflowOld (*(volatile uint8_t*)0x00CA) /* Previous overflow indicator */ | |
| #define g_vehicleSpeedByte (*(volatile uint8_t*)0x00CB) /* Vehicle speed (8-bit) */ | |
| /* --- Interrupt Enable Storage --- */ | |
| #define g_ieNormal (*(volatile uint16_t*)0x00CC) /* IE value during normal operation */ | |
| #define g_ieNormalHi (*(volatile uint8_t*)0x00CD) /* IE normal high byte */ | |
| #define g_ieInterrupt (*(volatile uint16_t*)0x00CE) /* IE value inside interrupts */ | |
| #define g_ieInterruptHi (*(volatile uint8_t*)0x00CF) /* IE interrupt high byte */ | |
| /* ====================== INJECTOR CALCULATIONS (0xD0-0xDF) ====================== */ | |
| #define g_injectorCalc0 (*(volatile uint16_t*)0x00D0) /* Injector timing calculation 0 */ | |
| #define g_injectorCalc1 (*(volatile uint16_t*)0x00D2) /* Injector timing calculation 1 */ | |
| #define g_injectorCalc2 (*(volatile uint16_t*)0x00D4) /* Injector timing calculation 2 */ | |
| #define g_finalFuelPulse (*(volatile uint16_t*)0x00D6) /* FINAL fuel pulse width! */ | |
| /* | |
| * This is the actual injector open time! | |
| * Formula: D6 = (0x144 * (0x162 or 0x164) / 0x10000) * 2 + 0x146 | |
| * Where: | |
| * 0x144 = main fuel from map | |
| * 0x162/0x164 = O2 correction (primary/secondary) | |
| * 0x146 = all other fuel corrections | |
| */ | |
| #define g_finalFuelHi (*(volatile uint8_t*)0x00D7) /* Final fuel high byte */ | |
| #define g_ignitorTiming0 (*(volatile uint16_t*)0x00D8) /* Ignitor firing timing 0 */ | |
| #define g_ignitorTiming1 (*(volatile uint16_t*)0x00DA) /* Ignitor firing timing 1 */ | |
| #define g_ignitorTiming2 (*(volatile uint16_t*)0x00DC) /* Ignitor firing timing 2 */ | |
| #define g_ignitorTiming3 (*(volatile uint16_t*)0x00DE) /* Ignitor firing timing 3 */ | |
| #define g_ignitorCounter (*(volatile uint8_t*)0x00DF) /* Ignitor sequence counter */ | |
| /* ====================== ENGINE POSITION (0xE0-0xEF) ====================== */ | |
| #define g_timer1Old (*(volatile uint16_t*)0x00E0) /* Previous Timer1 for 45° calc */ | |
| #define g_timer1Overflow (*(volatile uint8_t*)0x00E2) /* Timer1 overflow indicator (VSS) */ | |
| #define g_tdcPosition (*(volatile uint8_t*)0x00E3) /* TDC-related position */ | |
| #define g_crankPosition (*(volatile uint8_t*)0x00E4) /* Crank position 0-3 (45° each) */ | |
| #define g_cylinderIndex (*(volatile uint8_t*)0x00E5) /* Current cylinder (firing order) */ | |
| /* | |
| * Cylinder mapping: 0=Cyl1, 1=Cyl3, 2=Cyl4, 3=Cyl2 (firing order) | |
| */ | |
| #define g_unused_E6 (*(volatile uint8_t*)0x00E6) /* Unused/unknown */ | |
| #define g_unused_E7 (*(volatile uint8_t*)0x00E7) /* Unused/unknown */ | |
| #define g_celCodeCounter (*(volatile uint8_t*)0x00E8) /* CEL code iteration counter */ | |
| #define g_unused_E9 (*(volatile uint8_t*)0x00E9) /* Unused/unknown */ | |
| #define g_unused_EA (*(volatile uint8_t*)0x00EA) /* Unused/unknown */ | |
| #define g_lifeCounter (*(volatile uint8_t*)0x00EB) /* Reboot life counter */ | |
| /* | |
| * Starts at 0x20, decremented periodically. | |
| * When reaches 0, MCU reboots! (watchdog-like functionality) | |
| */ | |
| #define g_unused_EC (*(volatile uint8_t*)0x00EC) /* Unused/unknown */ | |
| #define g_errorCodeReboot (*(volatile uint8_t*)0x00ED) /* Error code after reboot */ | |
| /* | |
| * Error codes stored here on abnormal reset: | |
| * 0x47 = NMI (Non-Maskable Interrupt / power fail) | |
| * 0x44 = WDT (Watchdog Timer reset) | |
| * 0x42 = System error | |
| * 0x48 = Checksum error | |
| * 0x49 = Life counter reached 0 | |
| * 0x4A = ADC error | |
| * 0x4C, 0x4D = Unknown errors | |
| */ | |
| #define g_unused_EE (*(volatile uint8_t*)0x00EE) /* Temp/scratch for VCAL */ | |
| /* ====================== SPECIAL REGISTERS (0xF0-0xFF) ====================== */ | |
| #define g_rebootErrorCode (*(volatile uint8_t*)0x00F0) /* Error code storage on reboot */ | |
| #define g_eldValue (*(volatile uint8_t*)0x00F1) /* ELD (Electrical Load Detector) */ | |
| #define g_eldRelated (*(volatile uint8_t*)0x00F2) /* ELD-related value */ | |
| #define g_eldCalc (*(volatile uint8_t*)0x00F3) /* ELD calculation */ | |
| #define g_accelEnrichTimer (*(volatile uint8_t*)0x00F4) /* Accel enrichment timer */ | |
| #define g_accelEnrichValue (*(volatile uint8_t*)0x00F5) /* Accel enrichment amount */ | |
| #define g_unused_F6 (*(volatile uint8_t*)0x00F6) /* Unused */ | |
| #define g_unused_F7 (*(volatile uint8_t*)0x00F7) /* Unused */ | |
| #define g_oilPressure (*(volatile uint8_t*)0x00F8) /* Oil pressure switch value */ | |
| /* If > 0x32 there is enough oil pressure for VTEC */ | |
| #define g_unused_F9 (*(volatile uint8_t*)0x00F9) /* Unused */ | |
| #define g_eldRelated2 (*(volatile uint8_t*)0x00FA) /* ELD-related (JDM PW0 only) */ | |
| #define g_eldRelated3 (*(volatile uint8_t*)0x00FB) /* ELD-related */ | |
| #define g_celBlinkPattern (*(volatile uint8_t*)0x00FC) /* CEL blink pattern */ | |
| /* | |
| * High nibble: number of slow blinks | |
| * Low nibble: number of fast blinks | |
| */ | |
| /* --- Critical Flag Bytes --- */ | |
| #define f_flags_FD (*(volatile uint8_t*)0x00FD) /* Misc flags FD */ | |
| /* | |
| * FD.3: Setting error bits flag (1 = currently setting 130h-132h) | |
| * FD.5: Timer overflow flag | |
| * FD.6: Power failure detected | |
| */ | |
| #define f_flags_FE (*(volatile uint8_t*)0x00FE) /* Processing/Status flags */ | |
| /* | |
| * FE.0: Unknown flag | |
| * FE.1: Serial RX BRG interrupt occurred | |
| * FE.2: Unknown flag (reset in VCAL 4) | |
| * FE.3: Unknown flag (reset in VCAL 4) | |
| * FE.4: CEL blink requested | |
| * FE.5: Unknown flag | |
| * FE.6: LIMP MODE flag (bad ignition/CYP/TDC/CKP) | |
| * FE.7: REV LIMITER active | |
| */ | |
| #define g_externalInputs (*(volatile uint8_t*)0x00FF) /* External inputs from IC @ 18FC */ | |
| /* | |
| * FF.0: Knock sensor / Auto trans lockup | |
| * FF.1: Unknown | |
| * FF.2: VTEC solenoid feedback (verification) | |
| * FF.3: Unknown | |
| * FF.4: Unknown | |
| * FF.5: Unknown | |
| * FF.6: A/C switch input | |
| * FF.7: Starter signal (cranking) | |
| */ | |
| /* ====================== EXTENDED RAM - FLAGS (0x100-0x12F) ====================== */ | |
| /* Note: Addresses 0x100-0x10F are often used as extended register banks */ | |
| #define f_flags_118 (*(volatile uint8_t*)0x0118) /* Engine state flags */ | |
| /* | |
| * 118.0: CKP error flag (reset in Timer0 ISR) | |
| * 118.1: Unknown | |
| * 118.2: Unknown | |
| * 118.3: VSS error OR speed > 105mph | |
| * 118.4: Starter signal active (cranking) | |
| * 118.5: Unknown | |
| * 118.6: Low battery voltage (< 0x54) | |
| * 118.7: Automatic transmission flag | |
| */ | |
| #define f_flags_119 (*(volatile uint8_t*)0x0119) /* Ignition state flags */ | |
| /* | |
| * 119.0: Unknown | |
| * 119.1: Ignition final = 0 at crank pos 3 | |
| * 119.2: Unknown | |
| * 119.3: High MAP load (b4h > 0xB3 or 0xB8) | |
| * 119.4: Unknown | |
| * 119.5: Previous 119.3 state | |
| * 119.6: Unknown | |
| * 119.7: Unknown | |
| */ | |
| #define f_flags_11A (*(volatile uint8_t*)0x011A) /* Sensor/warmup flags */ | |
| /* | |
| * 11A.0: First TDC code set | |
| * 11A.1: First CYP code set | |
| * 11A.2: Unknown | |
| * 11A.3: Unknown | |
| * 11A.4: Unknown | |
| * 11A.5: Engine warming up (temp delta > 0x10) | |
| * 11A.6: High MAP (b4h > 0xB9 or 0xC0) | |
| * 11A.7: Idle timing connector active | |
| */ | |
| #define f_flags_11B (*(volatile uint8_t*)0x011B) /* O2 primary flags */ | |
| /* 11B.0: O2 related (primary sensor) */ | |
| #define f_flags_11C (*(volatile uint8_t*)0x011C) /* O2 secondary flags */ | |
| /* 11C.0: O2 related (secondary sensor) */ | |
| #define f_flags_11E (*(volatile uint8_t*)0x011E) /* RPM/VSS flags */ | |
| /* | |
| * 11E.0: VSS code NOT set (0 = VSS error) | |
| * 11E.1-3: Unknown | |
| * 11E.4: Engine ACCELERATING (1) / decelerating (0) | |
| * 11E.5: Rev count calculation OK (0 = OK) | |
| * 11E.6: Unknown | |
| * 11E.7: Unknown | |
| */ | |
| #define f_flags_11F (*(volatile uint8_t*)0x011F) /* Engine run state flags */ | |
| /* | |
| * 11F.0: Key ON, engine OFF | |
| * 11F.1: Unknown | |
| * 11F.2: TPS increasing (old < new) | |
| * 11F.3: Unknown | |
| * 11F.4: Engine running detection | |
| * 11F.5: TPS history (newest) | |
| * 11F.6: TPS history (middle) | |
| * 11F.7: TPS history (oldest) | |
| */ | |
| #define f_flags_120 (*(volatile uint8_t*)0x0120) /* RPM threshold flags */ | |
| /* | |
| * 120.5: RPM > 0xC9 (high RPM) | |
| * 120.6: RPM > 0x08 (engine running) | |
| */ | |
| #define f_flags_121 (*(volatile uint8_t*)0x0121) /* Ignition ready flags */ | |
| /* | |
| * 121.1: TDC checking issue (e3h != 4) | |
| * 121.2: CKP error persisted >= 6 cycles | |
| * 121.3: Ignition fault (0 = ready, 1 = fault) | |
| */ | |
| #define f_flags_122 (*(volatile uint8_t*)0x0122) /* Engine state flags 2 */ | |
| /* 122.2: Car is running */ | |
| #define f_flags_123 (*(volatile uint8_t*)0x0123) /* TPS history flags */ | |
| /* | |
| * 123.3: TPS increasing (newest) | |
| * 123.4: TPS increasing (middle) | |
| * 123.5: TPS increasing (oldest) | |
| */ | |
| #define f_flags_125 (*(volatile uint8_t*)0x0125) /* Idle control flags */ | |
| /* | |
| * 125.0: Idle related | |
| * 125.1: Idle related | |
| * 125.2: RPM under target idle (bah < 172h) | |
| */ | |
| #define f_flags_126 (*(volatile uint8_t*)0x0126) /* Accessory flags */ | |
| /* 126.5: Rear window de-mister ON */ | |
| /* --- VTEC Control Byte --- */ | |
| #define f_vtecFlags (*(volatile uint8_t*)0x0129) /* VTEC status flags */ | |
| /* | |
| * 129.0: Unknown | |
| * 129.1: Unknown | |
| * 129.2: Unknown | |
| * 129.3: Speed > 0x20-0x28 (VSS threshold) | |
| * 129.4: RPM > VTEC engage RPM threshold 1 | |
| * 129.5: RPM > VTEC engage RPM threshold 3 | |
| * 129.6: VTEC primed (all conditions met, no error) | |
| * 129.7: VTEC IS ON! | |
| */ | |
| #define f_flags_12A (*(volatile uint8_t*)0x012A) /* Temp/AC flags */ | |
| /* | |
| * 12A.0: Cold engine (< ~170°C), low speed, low RPM | |
| * 12A.1: A/C is ON | |
| * 12A.2: Unknown | |
| * 12A.3: A/C is OFF (inverse of 12A.1?) | |
| * 12A.4: Very high RPM (> 0xFE-0xFF) | |
| */ | |
| /* --- CEL Code Detection Bytes --- */ | |
| #define f_celDetect1 (*(volatile uint8_t*)0x012C) /* CEL detection byte 1 */ | |
| /* | |
| * Bits set by sensor checks, used to set 130h-132h: | |
| * 12C.0: Code 3 - MAP sensor | |
| * 12C.1: Code 6 - ECT (coolant temp) | |
| * 12C.2: Code 7 - TPS (throttle) | |
| * 12C.3: Code 5 - MAP (mechanical failure) | |
| * 12C.4: Code 13 - Barometric sensor | |
| * 12C.5: Code 18 - Unknown | |
| * 12C.6: Code 19 - Auto lockup | |
| * 12C.7: Code 10 - IAT (intake air temp) | |
| */ | |
| #define f_celDetect2 (*(volatile uint8_t*)0x012D) /* CEL detection byte 2 */ | |
| /* | |
| * 12D.0: Code 14 - IACV | |
| * 12D.1: Code 8 - TDC sensor | |
| * 12D.2: Code 17 - VSS (vehicle speed) | |
| * 12D.3: Code 20 - ELD | |
| * 12D.4: Code 23 - Knock sensor | |
| * 12D.5: Code 24 - Unknown | |
| * 12D.6: Code 21 - VTEC solenoid | |
| * 12D.7: Code 22 - Oil pressure switch | |
| */ | |
| #define f_celDetect3 (*(volatile uint8_t*)0x012E) /* CEL detection byte 3 */ | |
| /* | |
| * 12E.0: Code 4 - CKP (crankshaft) | |
| * 12E.1: Code 8 - TDC | |
| * 12E.2: Code 9 - CYP (camshaft) | |
| * 12E.3: Code 15 - Ignition output | |
| * 12E.4: Code 4 - CKP (duplicate) | |
| * 12E.5: Code 8 - TDC (duplicate) | |
| * 12E.6: Code 9 - CYP (duplicate) | |
| * 12E.7: Code 16 - Fuel injector system | |
| */ | |
| /* --- Stored Fault Codes --- */ | |
| #define g_faultCode1 (*(volatile uint8_t*)0x0130) /* Stored faults: codes 1-8 */ | |
| #define g_faultCode2 (*(volatile uint8_t*)0x0131) /* Stored faults: codes 9-16 */ | |
| #define g_faultCode3 (*(volatile uint8_t*)0x0132) /* Stored faults: codes 17-24 */ | |
| /* | |
| * Fault code bit structure (example for 130h): | |
| * Bit 0 = Code 1 | |
| * Bit 1 = Code 2 | |
| * ... | |
| * Bit 7 = Code 8 | |
| */ | |
| /* ====================== IGNITION CALCULATIONS (0x133-0x13F) ====================== */ | |
| #define g_ignitionTemp (*(volatile uint8_t*)0x0133) /* Ignition temp correction */ | |
| #define g_ignitionFinal (*(volatile uint8_t*)0x0134) /* FINAL ignition advance! */ | |
| /* This is the actual timing value used */ | |
| #define g_ignitionVcalCalc (*(volatile uint8_t*)0x0135) /* VCAL timing calculation */ | |
| /* Formula: vcal0(a7h,0x38C9) * vcal(9Ah,0x38D7) * 2 * 2 / 0x100 */ | |
| /* Range: 0x0F to 0xB3 */ | |
| #define g_ignitionInverse (*(volatile uint8_t*)0x0136) /* 2's complement of 0x134 */ | |
| #define g_ignitionCorrect1 (*(volatile uint8_t*)0x0137) /* Ignition correction 1 (accel) */ | |
| #define g_ignitionMapRaw (*(volatile uint8_t*)0x0138) /* Raw value from ignition map */ | |
| #define g_ignitionBrake (*(volatile uint8_t*)0x0139) /* Brake switch timing correction */ | |
| #define g_ignitionEctTrim (*(volatile uint8_t*)0x013A) /* ECT ignition trim */ | |
| /* If ECT error, uses 0x13F instead */ | |
| #define g_ignitionIdleAdj (*(volatile uint8_t*)0x013B) /* Idle adjust connector timing */ | |
| /* Correction from ECU pin B20 (0 if not connected) */ | |
| #define g_ignitionCorrect2 (*(volatile uint8_t*)0x013C) /* Ignition correction 2 */ | |
| #define g_knockRetard (*(volatile uint8_t*)0x013D) /* Knock sensor retard amount */ | |
| #define g_ignitionCorrect3 (*(volatile uint8_t*)0x013E) /* Ignition correction 3 */ | |
| #define g_ignitionEctError (*(volatile uint8_t*)0x013F) /* ECT error trim (fallback) */ | |
| /* Essentially vcal_2(a3h, 0x3907), used when ECT has error */ | |
| /* ====================== FUEL CALCULATIONS (0x140-0x16F) ====================== */ | |
| #define g_fuelMapNonVtec (*(volatile uint16_t*)0x0140) /* Fuel from non-VTEC map */ | |
| #define g_fuelMapNonVtecHi (*(volatile uint8_t*)0x0141) /* High byte */ | |
| #define g_fuelMapVtec (*(volatile uint16_t*)0x0142) /* Fuel from VTEC map */ | |
| #define g_fuelMapVtecHi (*(volatile uint8_t*)0x0143) /* High byte */ | |
| #define g_fuelMainFinal (*(volatile uint16_t*)0x0144) /* Main fuel value (no O2) */ | |
| /* | |
| * Formula: (0x140 or 0x142) * 0x15E / 0x10000 / 2 | |
| * This is the map-based fuel before O2 correction | |
| */ | |
| #define g_fuelMainFinalHi (*(volatile uint8_t*)0x0145) /* High byte */ | |
| #define g_fuelCorrections (*(volatile uint16_t*)0x0146) /* All fuel corrections */ | |
| /* | |
| * Formula: (0x16A * (0x14A * 0x166 / 0x100) / 0x100) + 0x14C + 0x150 + 0x152 + 0x14E | |
| * Includes: ECT, IAT, battery, TPS delta, A/C, etc. | |
| */ | |
| #define g_fuelCorrectionsHi (*(volatile uint8_t*)0x0147) /* High byte */ | |
| #define g_fuelWithCorrect (*(volatile uint16_t*)0x0148) /* Fuel + corrections (no O2, no AC) */ | |
| /* Formula: 0x144 + 0x146 - 0x14E */ | |
| #define g_fuelWithCorrectHi (*(volatile uint8_t*)0x0149) /* High byte */ | |
| #define g_fuelTpsDelta (*(volatile uint16_t*)0x014A) /* Delta TPS fuel enrichment */ | |
| #define g_fuelTpsDeltaHi (*(volatile uint8_t*)0x014B) /* High byte */ | |
| #define g_fuelBatteryTrim (*(volatile uint16_t*)0x014C) /* Battery voltage fuel trim */ | |
| /* VCAL_1 with vector at ROM 0x37E3 */ | |
| #define g_fuelBatteryHi (*(volatile uint8_t*)0x014D) /* High byte */ | |
| #define g_fuelAcTrim (*(volatile uint16_t*)0x014E) /* A/C compressor fuel trim */ | |
| #define g_fuelAcTrimHi (*(volatile uint8_t*)0x014F) /* High byte */ | |
| #define g_fuelTpsRelated (*(volatile uint16_t*)0x0150) /* TPS-related fuel trim */ | |
| #define g_fuelIdleAdjust (*(volatile uint16_t*)0x0152) /* Idle adjust connector fuel */ | |
| /* Fuel from ECU pin B20 (0 if not connected) */ | |
| #define g_fuelVoltageCorr (*(volatile uint16_t*)0x0158) /* Voltage-based fuel correction */ | |
| /* Uses vector at 0x37D7 in JDM PW0 */ | |
| #define g_fuelIatTrim (*(volatile uint16_t*)0x015A) /* IAT-based fuel trim */ | |
| #define g_fuelScaler (*(volatile uint16_t*)0x015E) /* Fuel scaling factor */ | |
| #define g_fuelUnused_160 (*(volatile uint16_t*)0x0160) /* Unused fuel value */ | |
| #define g_o2TrimPrimary (*(volatile uint16_t*)0x0162) /* Primary O2 fuel trim */ | |
| /* Applied to final fuel for closed-loop control */ | |
| #define g_o2TrimSecondary (*(volatile uint16_t*)0x0164) /* Secondary O2 fuel trim */ | |
| /* Used when secondary O2 sensor is active */ | |
| #define g_fuelTempTrim (*(volatile uint16_t*)0x0166) /* Temperature fuel trim */ | |
| /* Typical value: 0x340-0x400 */ | |
| #define g_fuelTempTrimHi (*(volatile uint8_t*)0x0167) /* High byte */ | |
| #define g_fuelMapImageTrim (*(volatile uint8_t*)0x0169) /* MAP image fuel trim */ | |
| /* Based on b4h, value between 0x51 and 0xDF */ | |
| #define g_fuelEctTrim (*(volatile uint16_t*)0x016A) /* ECT-based fuel trim */ | |
| /* Range: 0x00-0x100 */ | |
| #define g_fuelEctTrimHi (*(volatile uint8_t*)0x016B) /* High byte */ | |
| #define g_fuelEctTrim2 (*(volatile uint16_t*)0x016C) /* Another ECT fuel trim */ | |
| #define g_fuelEctTrim2Hi (*(volatile uint8_t*)0x016D) /* High byte */ | |
| #define g_openClosedLoop (*(volatile uint8_t*)0x016F) /* Open/closed loop selector */ | |
| /* Determines if O2 feedback is active */ | |
| /* ====================== IDLE CONTROL (0x170-0x19F) ====================== */ | |
| #define g_targetIdleRpm (*(volatile uint16_t*)0x0172) /* Target idle RPM (timer format) */ | |
| /* Same format as g_rpmTimerCount (0xBA) */ | |
| #define g_targetIdleHi (*(volatile uint8_t*)0x0173) /* Target idle high byte */ | |
| #define g_idleEctFactor (*(volatile uint8_t*)0x0176) /* ECT-based idle adjustment */ | |
| /* vcal_1(a3h, 0x39E1), used in IACV calculation */ | |
| #define g_idleTempFactor (*(volatile uint8_t*)0x0197) /* Temp-based idle factor */ | |
| /* vcal_0(a3h, 0x39BD) */ | |
| /* ====================== CEL BLINK COUNTERS (0x1AA-0x1DF) ====================== */ | |
| #define c_celBlinkIndex (*(volatile uint8_t*)0x01AA) /* CEL code blink index */ | |
| /* Incremented each pass through blink function */ | |
| /* --- CEL Code Error Counters --- */ | |
| /* These are decremented each time corresponding 12E bit is set */ | |
| #define c_celCkp1 (*(volatile uint8_t*)0x01B4) /* CKP counter (12E.0) */ | |
| #define c_celTdc1 (*(volatile uint8_t*)0x01B5) /* TDC counter (12E.1) */ | |
| #define c_celCyp1 (*(volatile uint8_t*)0x01B6) /* CYP counter (12E.2) */ | |
| #define c_celIgnOut (*(volatile uint8_t*)0x01B7) /* Ignition out (12E.3) */ | |
| #define c_celCkp2 (*(volatile uint8_t*)0x01B8) /* CKP counter 2 (12E.4) */ | |
| #define c_celTdc2 (*(volatile uint8_t*)0x01B9) /* TDC counter 2 (12E.5) */ | |
| #define c_celCyp2 (*(volatile uint8_t*)0x01BA) /* CYP counter 2 (12E.6) */ | |
| #define c_celFuelInj (*(volatile uint8_t*)0x01BB) /* Fuel injector (12E.7) */ | |
| #define c_vtecEngageTimer (*(volatile uint8_t*)0x01D5) /* VTEC engage timer */ | |
| /* Set to 0x14 when VTEC engages, cleared on disengage */ | |
| #define g_fuelRowIndex (*(volatile uint8_t*)0x01D8) /* Current fuel map row */ | |
| #define g_ignRowIndex (*(volatile uint8_t*)0x01D9) /* Current ignition map row */ | |
| #define c_celBlinkCounter (*(volatile uint8_t*)0x01DF) /* Current CEL blink counter */ | |
| /* ====================== MISC COUNTERS (0x1EA-0x1FF) ====================== */ | |
| #define c_misc_1EA (*(volatile uint8_t*)0x01EA) /* Misc counter */ | |
| #define c_acCounter (*(volatile uint8_t*)0x01EE) /* A/C routine counter */ | |
| #define c_acCounter2 (*(volatile uint8_t*)0x01EF) /* A/C routine counter 2 */ | |
| /* Counters 0x1CE-0x1FF are decremented in CEL blink routine */ | |
| /* ====================== IACV / INJECTOR CONTROL (0x200-0x21F) ====================== */ | |
| #define g_iacvDutyCycle (*(volatile uint16_t*)0x0202) /* IACV duty cycle */ | |
| /* Calculated in IACV routine, written to PWMR0 in PWM ISR */ | |
| #define g_iacvDutyCopy (*(volatile uint16_t*)0x0204) /* Copy of 0x202 */ | |
| /* --- RPM Sample Buffer (4 samples for averaging) --- */ | |
| #define g_rpmSample0 (*(volatile uint16_t*)0x0206) /* RPM sample 0 (newest) */ | |
| #define g_rpmSample1 (*(volatile uint16_t*)0x0208) /* RPM sample 1 */ | |
| #define g_rpmSample2 (*(volatile uint16_t*)0x020A) /* RPM sample 2 */ | |
| #define g_rpmSample3 (*(volatile uint16_t*)0x020C) /* RPM sample 3 (oldest) */ | |
| /* --- RPM History (older values for filtering) --- */ | |
| #define g_rpmHistory0 (*(volatile uint16_t*)0x020E) /* RPM history 0 (newest) */ | |
| #define g_rpmHistory1 (*(volatile uint16_t*)0x0210) /* RPM history 1 */ | |
| #define g_rpmHistory2 (*(volatile uint16_t*)0x0212) /* RPM history 2 (oldest) */ | |
| /* --- Injector Timing --- */ | |
| #define g_injTiming0 (*(volatile uint16_t*)0x0214) /* Injector timing buffer 0 */ | |
| #define g_injTiming1 (*(volatile uint16_t*)0x0216) /* Injector timing buffer 1 */ | |
| #define g_injTiming2 (*(volatile uint16_t*)0x0218) /* Injector timing buffer 2 */ | |
| #define g_injectorMask (*(volatile uint8_t*)0x021A) /* Injector select mask */ | |
| /* | |
| * Bit pattern shifts with firing order: | |
| * 0111_0111 = Cyl 1 | |
| * 1011_1011 = Cyl 3 | |
| * 1101_1101 = Cyl 4 | |
| * 1110_1110 = Cyl 2 | |
| */ | |
| #define g_injectorMaskInv (*(volatile uint8_t*)0x021B) /* Inverse of 0x21A */ | |
| #define g_injectorOutput (*(volatile uint8_t*)0x021C) /* P2 injector output mask */ | |
| /* ANDed with P2 to control injectors. 0x0F = all off */ | |
| #define g_iacvControl (*(volatile uint8_t*)0x021D) /* IACV control flags */ | |
| /* ====================== O2 SENSOR BUFFERS (0x274-0x279) ====================== */ | |
| #define g_o2TrimOldPrimary (*(volatile uint16_t*)0x0274) /* Old primary O2 trim */ | |
| /* Follows 0x162 asymptotically like g_rpmSmoothed */ | |
| #define g_o2TrimOldSecondary (*(volatile uint16_t*)0x0276) /* Old secondary O2 trim */ | |
| /* Follows 0x164 asymptotically */ | |
| #define g_tpsOld (*(volatile uint8_t*)0x0278) /* Previous TPS value */ | |
| #define g_tempOld (*(volatile uint8_t*)0x0279) /* Previous temp value */ | |
| /* ====================== CEL LED BLINK CODES (0x27B-0x27D) ====================== */ | |
| #define g_celBlinkCode1 (*(volatile uint8_t*)0x027B) /* CEL LED blink: codes 1-8 */ | |
| #define g_celBlinkCode2 (*(volatile uint8_t*)0x027C) /* CEL LED blink: codes 9-16 */ | |
| #define g_celBlinkCode3 (*(volatile uint8_t*)0x027D) /* CEL LED blink: codes 17-24 */ | |
| /* Same structure as g_faultCode1-3 but for active blinking */ | |
| /*============================================================================ | |
| * SECTION 4: INTERRUPT VECTOR TABLE | |
| * ============================================================================ | |
| * | |
| * The OKI 66201 interrupt vector table starts at address 0x0000. | |
| * Each vector is a 16-bit pointer to the interrupt service routine. | |
| * | |
| * Original assembly (0x0000-0x0037): | |
| * org 0000h | |
| * int_start_vec: DW int_start ; 0000 -> 0x1673 | |
| * int_break_vec: DW int_break ; 0002 -> 0x169A | |
| * int_WDT_vec: DW int_WDT ; 0004 -> 0x1696 | |
| * ... etc | |
| * | |
| *============================================================================*/ | |
| /* --- Forward Declarations: Interrupt Service Routines --- */ | |
| void isr_systemReset(void); /* 0x1673: Power-on reset */ | |
| void isr_softwareBreak(void); /* 0x169A: BRK instruction */ | |
| void isr_watchdogTimeout(void); /* 0x1696: Watchdog timer reset */ | |
| void isr_nonMaskableInterrupt(void); /* 0x008F: NMI / power failure */ | |
| void isr_externalInt0_CKP(void); /* 0x151F: CKP crankshaft signal */ | |
| void isr_serialReceive(void); /* 0x0067: Serial RX (datalogging) */ | |
| void isr_serialRxBaudRate(void); /* 0x155D: Serial RX baud generator */ | |
| void isr_timer0Overflow(void); /* 0x1565: Timer0 overflow (injector) */ | |
| void isr_timer0Compare(void); /* 0x0137: Timer0 match (TDC event) */ | |
| void isr_timer1Overflow(void); /* 0x160C: Timer1 overflow (RPM) */ | |
| void isr_timer1Compare(void); /* 0x00CD: Timer1 match (ignition) */ | |
| void isr_timer2Compare(void); /* 0x00D1: Timer2 match */ | |
| void isr_pwmTimer(void); /* 0x1636: PWM timer (IACV) */ | |
| void isr_externalInt1_CYP(void); /* 0x00F2: CYP/TDC camshaft signal */ | |
| /* --- Forward Declarations: VCAL Table Lookup Functions --- */ | |
| uint8_t vcal_0_interpolate1D(uint8_t input, uint16_t tableAddr); /* 0x2BF9 */ | |
| uint8_t vcal_1_lookupInverted(uint8_t input, uint16_t tableAddr); /* 0x2C57 */ | |
| uint8_t vcal_2_clampedLookup(uint8_t input, uint16_t tableAddr); /* 0x2C33 */ | |
| uint8_t vcal_3_tripletLookup(uint8_t input, uint16_t tableAddr); /* 0x2C45 */ | |
| void vcal_4_mainProcessing(void); /* 0x189B */ | |
| uint16_t vcal_5_fuelMapLookup(void); /* 0x2D96 */ | |
| uint16_t vcal_6_ignMapLookup(void); /* 0x2EB2 */ | |
| uint16_t vcal_7_ignMapLookupVtec(void); /* 0x2EB4 */ | |
| /* --- Forward Declarations: Main Engine Functions --- */ | |
| void main_engineLoop(void); | |
| void calc_rpmFromTimerCapture(void); /* label_0416 */ | |
| void calc_rpmAveraging(void); /* label_0436 */ | |
| void calc_mapSensorProcessing(void); /* label_04xx */ | |
| void calc_fuelFromMaps(void); /* label_08B3 */ | |
| void calc_ignitionFromMaps(void); /* label_07xx */ | |
| void calc_injectorPulseWidth(void); /* label_0270 */ | |
| void update_ignitorTiming(void); /* label_2911 */ | |
| void update_injectorOutputs(void); /* label_28ED */ | |
| void process_rpmSignal(void); /* label_2995 */ | |
| /* --- Forward Declarations: VTEC Control --- */ | |
| void check_vtecConditions(void); /* label_13xx */ | |
| void engage_vtecSolenoid(void); | |
| void disengage_vtecSolenoid(void); | |
| /* --- Forward Declarations: O2 Sensor Processing --- */ | |
| void process_o2Feedback(void); /* label_25xx */ | |
| void calc_o2FuelTrim(void); | |
| /* --- Forward Declarations: Idle Control --- */ | |
| void calc_idleAirControl(void); /* label_1Axx */ | |
| void update_iacvDuty(void); | |
| /* --- Forward Declarations: Error Code Handling --- */ | |
| void check_sensorErrors(void); /* label_2xxx */ | |
| void set_faultCode(uint8_t code); /* label_2F1B */ | |
| void blink_celLight(void); /* label_20xx */ | |
| void store_frozenFrame(void); | |
| /* --- Forward Declarations: Utility Functions --- */ | |
| void handle_powerFailure(void); /* label_2ECC */ | |
| void check_lowVoltageShutdown(void); /* label_3223 */ | |
| void storerow_forLogging(void); /* Custom: store fuel/ign row */ | |
| /* | |
| * INTERRUPT VECTOR TABLE (Address 0x0000-0x0037) | |
| * | |
| * In C pseudo-code, this would be an array of function pointers: | |
| */ | |
| typedef void (*isr_handler_t)(void); | |
| const isr_handler_t INTERRUPT_VECTORS[] = { | |
| /* 0x0000 */ isr_systemReset, /* Reset/Power-on */ | |
| /* 0x0002 */ isr_softwareBreak, /* BRK instruction */ | |
| /* 0x0004 */ isr_watchdogTimeout, /* WDT timeout */ | |
| /* 0x0006 */ isr_nonMaskableInterrupt, /* NMI (power fail) */ | |
| /* 0x0008 */ isr_externalInt0_CKP, /* INT0: Crankshaft position */ | |
| /* 0x000A */ isr_serialReceive, /* Serial RX complete */ | |
| /* 0x000C */ isr_softwareBreak, /* Serial TX complete (unused) */ | |
| /* 0x000E */ isr_serialRxBaudRate, /* Serial RX baud rate gen */ | |
| /* 0x0010 */ isr_timer0Overflow, /* Timer 0 overflow */ | |
| /* 0x0012 */ isr_timer0Compare, /* Timer 0 compare match */ | |
| /* 0x0014 */ isr_timer1Overflow, /* Timer 1 overflow */ | |
| /* 0x0016 */ isr_timer1Compare, /* Timer 1 compare match */ | |
| /* 0x0018 */ isr_softwareBreak, /* Timer 2 overflow (unused) */ | |
| /* 0x001A */ isr_timer2Compare, /* Timer 2 compare match */ | |
| /* 0x001C */ isr_softwareBreak, /* Timer 3 overflow (unused) */ | |
| /* 0x001E */ isr_softwareBreak, /* Timer 3 compare (unused) */ | |
| /* 0x0020 */ isr_softwareBreak, /* A/D complete (unused) */ | |
| /* 0x0022 */ isr_pwmTimer, /* PWM timer */ | |
| /* 0x0024 */ isr_softwareBreak, /* Serial TX baud (unused) */ | |
| /* 0x0026 */ isr_externalInt1_CYP, /* INT1: Camshaft position */ | |
| /* 0x0028 */ (isr_handler_t)vcal_0_interpolate1D, /* VCAL 0 */ | |
| /* 0x002A */ (isr_handler_t)vcal_1_lookupInverted, /* VCAL 1 */ | |
| /* 0x002C */ (isr_handler_t)vcal_2_clampedLookup, /* VCAL 2 */ | |
| /* 0x002E */ (isr_handler_t)vcal_3_tripletLookup, /* VCAL 3 */ | |
| /* 0x0030 */ (isr_handler_t)vcal_4_mainProcessing, /* VCAL 4 */ | |
| /* 0x0032 */ (isr_handler_t)vcal_5_fuelMapLookup, /* VCAL 5 */ | |
| /* 0x0034 */ (isr_handler_t)vcal_6_ignMapLookup, /* VCAL 6 */ | |
| /* 0x0036 */ (isr_handler_t)vcal_7_ignMapLookupVtec /* VCAL 7 */ | |
| }; | |
| /* | |
| * Code between 0x0038-0x0066 is compressed/encoded data bytes: | |
| * code_start: DB 008h,00Eh,00Eh,000h,0E5h,0CEh,0D5h,01Ah... | |
| * This appears to be part of the serial receive handler or lookup tables. | |
| */ | |
| /*============================================================================ | |
| * SECTION 5: INTERRUPT SERVICE ROUTINES | |
| * ============================================================================ | |
| * | |
| * Full pseudo-C conversion of all interrupt handlers. | |
| * Each includes the original assembly address in comments. | |
| * | |
| *============================================================================*/ | |
| /** | |
| * isr_serialReceive() - Serial Data Receive Handler (Datalogging) | |
| * | |
| * Address: 0x0067 (label: _int_serial_rx) | |
| * | |
| * This is the heart of the datalogging system. When tuning software | |
| * sends a byte (RAM address), this handler reads that RAM location | |
| * and transmits the value back. | |
| * | |
| * MODIFIED from stock for datalogging support. | |
| */ | |
| void isr_serialReceive(void) | |
| { | |
| /* 0x0067: L A, 0CEh - Load interrupt IE value */ | |
| /* 0x0069: ST A, IE - Enable higher-priority interrupts */ | |
| REG_IE = g_ieInterrupt; /* Allow nested interrupts */ | |
| /* 0x006B: SB PSWH.0 - Set DD flag for 16-bit addressing */ | |
| REG_PSWH |= 0x01; | |
| /* 0x006D: L A, DP - Save current DP */ | |
| /* 0x006E: PUSHS A - Push to system stack */ | |
| uint16_t savedDP = REG_DP; /* Save data pointer */ | |
| /* 0x006F: CLRB A - Clear accumulator (error flags) */ | |
| uint8_t errorFlags = 0; | |
| /* 0x0070: RB SRSTAT.3 - Check overrun error, move to carry */ | |
| /* 0x0073: JEQ label_0077 - Skip if no overrun */ | |
| /* 0x0075: ADDB A, #001h - Add 1 to error flags if overrun */ | |
| if (REG_SRSTAT & 0x08) /* Bit 3: Overrun error */ | |
| errorFlags += 1; | |
| /* label_0077: */ | |
| /* 0x0077: RB SRSTAT.2 - Check framing error */ | |
| /* 0x007A: JEQ label_007e */ | |
| /* 0x007C: ADDB A, #002h - Add 2 if framing error */ | |
| if (REG_SRSTAT & 0x04) /* Bit 2: Framing error */ | |
| errorFlags += 2; | |
| /* label_007e: */ | |
| /* 0x007E: STB A, ACCH - Store error flags in high byte */ | |
| uint8_t addrHigh = errorFlags; /* High byte of address (with errors) */ | |
| /* 0x0080: LB A, SRBUF - Read received byte (RAM address) */ | |
| uint8_t addrLow = REG_SRBUF; | |
| /* 0x0082: MOV DP, A - Use received byte as address */ | |
| /* 0x0083: LB A, [DP] - Read RAM at that address */ | |
| REG_DP = (addrHigh << 8) | addrLow; | |
| uint8_t ramValue = *(volatile uint8_t*)REG_DP; | |
| /* 0x0084: STB A, STBUF - Transmit RAM value back */ | |
| REG_STBUF = ramValue; | |
| /* 0x0086: POPS A - Restore saved DP */ | |
| /* 0x0087: MOV DP, A */ | |
| REG_DP = savedDP; | |
| /* 0x0088: L A, 0CCh - Load normal IE value */ | |
| /* 0x008A: RB PSWH.0 - Clear DD flag */ | |
| /* 0x008C: ST A, IE - Restore interrupt enables */ | |
| REG_PSWH &= ~0x01; | |
| REG_IE = g_ieNormal; | |
| /* 0x008E: RTI - Return from interrupt */ | |
| return; | |
| } | |
| /** | |
| * isr_nonMaskableInterrupt() - NMI Handler (Power Failure) | |
| * | |
| * Address: 0x008F (label: int_NMI) | |
| * | |
| * Called when power is failing (main relay opening) or other | |
| * non-maskable events. Attempts to save state and enter low-power mode. | |
| */ | |
| void isr_nonMaskableInterrupt(void) | |
| { | |
| /* 0x008F: MOV LRB, #00020h - Switch to register bank 2 (0x100) */ | |
| REG_LRB = 0x20; | |
| /* 0x0092: J label_3223 - Jump to power failure handler */ | |
| check_lowVoltageShutdown(); /* Inline: checks voltage, may return */ | |
| /* label_0095: (return point from label_3223) */ | |
| /* 0x0095: JEQ label_009a - Skip power save if voltage OK */ | |
| if (/* voltage OK condition */ 0) { | |
| goto skip_powerSave; | |
| } | |
| /* 0x0097: CAL label_2ecc - Call power failure routine */ | |
| handle_powerFailure(); | |
| skip_powerSave: | |
| /* label_009a: */ | |
| /* 0x009A: MOV DP, #00036h - Set DP to timeout counter */ | |
| REG_DP = 0x0036; /* Counter value */ | |
| wait_forPowerStable: | |
| /* label_009d: */ | |
| /* 0x009D: MB C, P4.1 - Check power pin */ | |
| /* 0x00A0: JGE label_00c8 - If power stable, exit wait */ | |
| if (REG_P4 & 0x02) { /* P4.1 = main power detect */ | |
| goto power_recovered; | |
| } | |
| /* 0x00A2: JRNZ DP, label_009d - Loop until timeout */ | |
| REG_DP--; | |
| if (REG_DP != 0) | |
| goto wait_forPowerStable; | |
| /* Power failed - enter standby mode */ | |
| /* 0x00A4: MOV IE, #00040h - Disable most interrupts */ | |
| REG_IE = 0x0040; | |
| /* 0x00A9: MOVB TCON1, #0E0h - Configure timer for standby */ | |
| REG_TCON1 = 0xE0; | |
| /* 0x00AD: CLR IRQ - Clear pending interrupts */ | |
| REG_IRQ = 0x00; | |
| /* 0x00B0: SB P4SF.1 - Set P4 special function */ | |
| REG_P4SF |= 0x02; | |
| /* 0x00B3: MOV TM1, #0FFFFh - Set timer to max */ | |
| REG_TM1 = 0xFFFF; | |
| /* 0x00B8: SB TCON1.4 - Enable timer */ | |
| REG_TCON1 |= 0x10; | |
| /* 0x00BB: SB SBYCON.2 - Enter standby mode */ | |
| REG_SBYCON |= 0x04; | |
| /* 0x00BE: LB A, #005h - Stop sequence */ | |
| /* 0x00C0: STB A, STPACP - First stop command */ | |
| REG_STPACP = 0x05; | |
| /* 0x00C2: SLLB A - Shift left (0x05 -> 0x0A) */ | |
| /* 0x00C3: STB A, STPACP - Second stop command */ | |
| REG_STPACP = 0x0A; | |
| /* 0x00C5: SB SBYCON.0 - Final standby enable */ | |
| REG_SBYCON |= 0x01; | |
| power_recovered: | |
| /* label_00c8: */ | |
| /* 0x00C8: MOVB 0EDh, #047h - Store NMI error code */ | |
| g_errorCodeReboot = 0x47; /* 0x47 = NMI occurred */ | |
| /* 0x00CC: BRK - Force software break/reset */ | |
| /* This triggers isr_softwareBreak and system reset */ | |
| asm("BRK"); | |
| } | |
| /** | |
| * isr_timer1Compare() - Timer 1 Compare Match (Ignition Timing) | |
| * | |
| * Address: 0x00CD (label: int_timer_1) | |
| * | |
| * Triggers when Timer1 matches TMR1 - used for ignition coil timing. | |
| * Very short handler - just calls the timing update routine. | |
| */ | |
| void isr_timer1Compare(void) | |
| { | |
| /* 0x00CD: CAL label_2911 - Call ignition update routine */ | |
| update_ignitorTiming(); | |
| /* 0x00D0: RTI - Return from interrupt */ | |
| return; | |
| } | |
| /** | |
| * isr_timer2Compare() - Timer 2 Compare Match | |
| * | |
| * Address: 0x00D1 (label: int_timer_2) | |
| * | |
| * Timer2 is used for auxiliary timing. This handler manages | |
| * ignitor sequencing and overflow detection. | |
| */ | |
| void isr_timer2Compare(void) | |
| { | |
| /* 0x00D1: L A, 0CEh - Load interrupt IE */ | |
| /* 0x00D3: ST A, IE - Enable nested interrupts */ | |
| REG_IE = g_ieInterrupt; | |
| /* 0x00D5: SB PSWH.0 - Enable 16-bit addressing */ | |
| REG_PSWH |= 0x01; | |
| /* 0x00D7: CLR LRB - Switch to register bank 0 */ | |
| REG_LRB = 0x00; | |
| /* 0x00D9: LB A, 0DFh - Load ignitor counter */ | |
| uint8_t counter = g_ignitorCounter; | |
| /* 0x00DB: ADDB A, #001h - Increment counter */ | |
| counter++; | |
| /* 0x00DD: CMPB A, #003h - Check if counter >= 3 */ | |
| /* 0x00DF: JLT label_00eb - Skip if counter < 3 */ | |
| if (counter >= 3) { | |
| /* 0x00E1: JBR off(07FF42h).2, label_00eb - Check TCON2.2 */ | |
| if (!(REG_TCON2 & 0x04)) { | |
| /* 0x00E4: MOV off(07FF3Ah), 0DCh - Copy timing value */ | |
| REG_TMR2 = g_ignitorTiming2; | |
| } | |
| /* 0x00E8: RB TCON2.3 - Clear TCON2 bit 3 */ | |
| REG_TCON2 &= ~0x08; | |
| } | |
| /* label_00eb: */ | |
| /* 0x00EB: L A, 0CCh - Load normal IE */ | |
| /* 0x00ED: RB PSWH.0 - Clear 16-bit mode */ | |
| /* 0x00EF: ST A, IE - Restore interrupts */ | |
| REG_PSWH &= ~0x01; | |
| REG_IE = g_ieNormal; | |
| /* 0x00F1: RTI */ | |
| return; | |
| } | |
| /** | |
| * isr_externalInt1_CYP() - External Interrupt 1 (Camshaft Position) | |
| * | |
| * Address: 0x00F2 (label: int_INT1) | |
| * | |
| * Triggered by CYP (Cylinder Position) or TDC (Top Dead Center) | |
| * signal from the distributor. This synchronizes the ECU to the | |
| * camshaft and determines which cylinder is firing. | |
| */ | |
| void isr_externalInt1_CYP(void) | |
| { | |
| /* 0x00F2: L A, IE - Save current IE */ | |
| /* 0x00F4: PUSHS A - Push to stack */ | |
| uint16_t savedIE = REG_IE; | |
| /* 0x00F5: L A, #00010h - Minimal IE (timer1 only) */ | |
| /* 0x00F8: SCAL label_012c - Call setup routine */ | |
| REG_IE = 0x0010; | |
| setup_interruptMode(); /* Sets up proper register bank */ | |
| /* 0x00FA: JBS off(07FF30h).7, label_010c - Check limp mode flag */ | |
| if (f_flags_FE & 0x80) { /* FE.7 = rev limiter active */ | |
| goto exit_isr; /* label_010c */ | |
| } | |
| /* 0x00FD: JBS off(07FF30h).3, label_0112 - Check other flag */ | |
| if (f_flags_FE & 0x08) { /* Some processing flag */ | |
| goto continue_processing; /* label_0112 */ | |
| } | |
| /* 0x0100: RB IRQ.7 - Clear pending IRQ bit 7 */ | |
| REG_IRQH &= ~0x80; | |
| /* 0x0103: JEQ label_010f - Check if condition met */ | |
| if (/* some condition */ 0) { | |
| goto set_flag; /* label_010f */ | |
| } | |
| /* 0x0105: RB off(07FF2Eh).0 - Clear flag bit */ | |
| f_celDetect3 &= ~0x01; | |
| /* 0x0108: MOVB off(07FFBAh), #02Dh - Set timing counter */ | |
| g_rpmTimerCountHi = 0x2D; | |
| exit_isr: | |
| /* label_010c: */ | |
| /* 0x010C: J label_03cd - Jump to interrupt exit */ | |
| goto interrupt_exit; | |
| set_flag: | |
| /* label_010f: */ | |
| /* 0x010F: SB off(07FF2Eh).0 - Set flag bit */ | |
| f_celDetect3 |= 0x01; | |
| continue_processing: | |
| /* label_0112: */ | |
| /* 0x0112: L A, ADCR5 - Read MAP sensor */ | |
| g_mapSensorRaw = REG_ADCR5; | |
| /* 0x0114: ST A, 0B0h - Store MAP value */ | |
| /* Already done above */ | |
| /* 0x0116: L A, TM1 - Read timer1 capture */ | |
| /* 0x0118: ST A, TMR1 - Store as compare value */ | |
| REG_TMR1 = REG_TM1; | |
| g_timer1Capture = REG_TM1; | |
| /* 0x011A: LB A, #001h - Load constant 1 */ | |
| /* 0x011C: CAL label_31d3 - Call injector timing update */ | |
| calc_injectorTimingUpdate(1); | |
| /* 0x011F: NOP */ | |
| /* 0x0120: SB P2.4 - Set P2.4 (limp mode indicator?) */ | |
| REG_P2 |= 0x10; | |
| /* 0x0123: CAL label_2995 - Call RPM signal processing */ | |
| process_rpmSignal(); | |
| /* 0x0126: J label_022d - Continue to main processing */ | |
| goto main_engine_processing; | |
| main_engine_processing: | |
| /* ... continues with fuel/ignition calculations ... */ | |
| /* This is a large block - will be expanded in engine calcs section */ | |
| interrupt_exit: | |
| /* label_03cd: */ | |
| /* 0x03CD: RB PSWH.0 - Clear 16-bit mode */ | |
| REG_PSWH &= ~0x01; | |
| /* label_03cf: */ | |
| /* 0x03CF: POPS A - Restore IE */ | |
| /* 0x03D0: ST A, IE */ | |
| REG_IE = savedIE; | |
| /* 0x03D2: RTI */ | |
| return; | |
| } | |
| /** | |
| * setup_interruptMode() - Setup for interrupt processing | |
| * | |
| * Address: 0x0129 / 0x012C (labels: label_0129, label_012c) | |
| * | |
| * Configures the processor for interrupt handling with proper | |
| * register bank and interrupt priority. | |
| */ | |
| void setup_interruptMode(void) | |
| { | |
| /* label_0129: */ | |
| /* 0x0129: L A, #00011h - Load alternate IE value */ | |
| /* Used when called from different paths */ | |
| /* label_012c: */ | |
| /* 0x012C: ST A, IE - Store new IE value */ | |
| /* 0x012E: MOV PSW, #00102h - Set processor status */ | |
| REG_PSW = 0x0102; | |
| /* 0x0133: MOV LRB, #00022h - Switch to register bank */ | |
| REG_LRB = 0x22; /* Bank 2 + offset 2 = 0x110 */ | |
| /* 0x0136: RT - Return */ | |
| return; | |
| } | |
| /** | |
| * isr_timer0Compare() - Timer 0 Compare Match (Main Engine Control) | |
| * | |
| * Address: 0x0137 (label: int_timer_0) | |
| * | |
| * THIS IS THE HEART OF THE ENGINE CONTROL SYSTEM! | |
| * | |
| * Called every TDC (Top Dead Center) event - once per cylinder per | |
| * 720° engine cycle (4 times per cycle for a 4-cylinder). | |
| * | |
| * This ISR performs: | |
| * - Crank position tracking (0-3 for 4 cylinders) | |
| * - RPM calculation and averaging | |
| * - CEL code detection for ignition signals | |
| * - Fuel injection timing calculations | |
| * - Ignition timing calculations | |
| * - O2 feedback processing | |
| * - VTEC condition checking | |
| */ | |
| void isr_timer0Compare(void) | |
| { | |
| /* 0x0137: L A, IE - Save current IE */ | |
| /* 0x0139: PUSHS A - Push to stack */ | |
| uint16_t savedIE = REG_IE; | |
| /* 0x013A: SCAL label_0129 - Call setup routine */ | |
| setup_interruptMode(); | |
| /* 0x013C: MOVB off(07FFBAh), #02Dh - Set RPM timer high byte */ | |
| g_rpmTimerCountHi = 0x2D; /* Default/timeout value */ | |
| /* 0x0140: SB off(07FF20h).0 - Set flag 120.0 */ | |
| f_flags_120 |= 0x01; | |
| /* 0x0143: JNE label_0151 - If already running, skip init */ | |
| if (f_flags_120 != 0x01) { | |
| goto timer0_running; /* Already initialized */ | |
| } | |
| /* First-time initialization path */ | |
| /* 0x0145: RB IRQH.7 - Clear INT1 pending */ | |
| REG_IRQH &= ~0x80; | |
| /* 0x0148: RB off(07FF18h).0 - Clear CKP error flag */ | |
| f_flags_118 &= ~0x01; | |
| /* 0x014B: RB TRNSIT.0 - Clear timer transit flag */ | |
| REG_TRNSIT &= ~0x01; | |
| /* 0x014E: J label_029f - Jump to RPM calculation */ | |
| goto calculate_rpmDelta; | |
| timer0_running: | |
| /* label_0151: Engine is running, process normally */ | |
| /* 0x0151: LB A, 0E3h - Load TDC position counter */ | |
| uint8_t tdcCounter = g_tdcPosition; | |
| /* 0x0153: ADDB A, #001h - Increment TDC counter */ | |
| tdcCounter++; | |
| /* 0x0155: JBS off(07FF30h).7, label_0193 - Check rev limiter */ | |
| if (f_flags_FE & 0x80) { | |
| /* Rev limiter active - skip error checking */ | |
| goto store_crankPosition; | |
| } | |
| /* 0x0158: RB IRQH.7 - Check/clear INT1 pending */ | |
| uint8_t int1Pending = REG_IRQH & 0x80; | |
| REG_IRQH &= ~0x80; | |
| /* 0x015B: JNE label_0170 - If INT1 was pending, handle error */ | |
| if (int1Pending) { | |
| goto tdc_errorCheck; | |
| } | |
| /* 0x015D: RB off(07FF18h).0 - Check CKP error flag */ | |
| /* 0x0160: JNE label_0170 - If CKP error, handle it */ | |
| if (f_flags_118 & 0x01) { | |
| f_flags_118 &= ~0x01; /* Clear it */ | |
| goto tdc_errorCheck; | |
| } | |
| /* No errors - normal processing */ | |
| /* 0x0162: STB A, r0 - Store counter temporarily */ | |
| uint8_t tempCounter = tdcCounter; | |
| /* 0x0163: ANDB A, #003h - Check if counter is multiple of 4 */ | |
| /* 0x0165: JNE label_016d - Skip if not aligned */ | |
| if ((tdcCounter & 0x03) == 0) { | |
| /* Every 4th TDC - set detection flags */ | |
| /* 0x0167: SB off(07FF2Eh).1 - Set TDC detection bit */ | |
| f_celDetect3 |= 0x02; | |
| /* 0x016A: SB off(07FF1Ah).0 - Set first-TDC flag */ | |
| f_flags_11A |= 0x01; | |
| } | |
| /* label_016d: */ | |
| /* 0x016D: LB A, r0 - Restore counter */ | |
| /* 0x016E: SJ label_0193 - Jump to store position */ | |
| goto store_crankPosition; | |
| tdc_errorCheck: | |
| /* label_0170: TDC/CKP error handling */ | |
| /* 0x0170: RB off(07FF1Ah).0 - Clear first-TDC flag */ | |
| f_flags_11A &= ~0x01; | |
| /* 0x0173: MOVB off(07FFBBh), #02Dh - Reset RPM counter */ | |
| g_rpmTimerCountHi = 0x2D; | |
| /* 0x0177: CMPB A, #004h - Check if counter == 4 */ | |
| /* 0x0179: JEQ label_0192 - If == 4, reset counter */ | |
| if (tdcCounter == 4) { | |
| tdcCounter = 0; /* Reset to 0 */ | |
| goto store_crankPosition; | |
| } | |
| /* 0x017B: SB off(07FF21h).1 - Set ignition flag */ | |
| f_flags_121 |= 0x02; | |
| /* 0x017E: JLT label_0189 - If counter < 4, check more */ | |
| if (tdcCounter < 4) { | |
| goto check_9a_flags; | |
| } | |
| /* 0x0180: CMPB A, #008h - Check if counter < 8 */ | |
| /* 0x0182: JLT label_018f - If < 8, set error bit 4 */ | |
| if (tdcCounter < 8) { | |
| goto set_celDetect_4; | |
| } | |
| /* Counter >= 8 - set error bit 5 */ | |
| /* label_0184: */ | |
| /* 0x0184: SB off(07FF2Eh).5 - Set CEL detect bit 5 */ | |
| f_celDetect3 |= 0x20; | |
| tdcCounter = 0; | |
| goto store_crankPosition; | |
| check_9a_flags: | |
| /* label_0189: Check 9A flags */ | |
| /* 0x0189: JBR off(07FF9Ah).0, label_018f - Check bit 0 */ | |
| if (!(g_batteryVoltage & 0x01)) { | |
| goto set_celDetect_4; | |
| } | |
| /* 0x018C: JBS off(07FF9Ah).1, label_0184 - Check bit 1 */ | |
| if (g_batteryVoltage & 0x02) { | |
| f_celDetect3 |= 0x20; | |
| tdcCounter = 0; | |
| goto store_crankPosition; | |
| } | |
| set_celDetect_4: | |
| /* label_018f: */ | |
| /* 0x018F: SB off(07FF2Eh).4 - Set CEL detect bit 4 */ | |
| f_celDetect3 |= 0x10; | |
| /* label_0192: */ | |
| tdcCounter = 0; /* Reset counter */ | |
| store_crankPosition: | |
| /* label_0193: Store the crank position */ | |
| /* 0x0193: STB A, 0E3h - Store TDC counter */ | |
| g_tdcPosition = tdcCounter; | |
| /* 0x0195: ANDB A, #003h - Get crank position 0-3 */ | |
| /* 0x0197: STB A, 0E4h - Store crank position */ | |
| g_crankPosition = tdcCounter & 0x03; | |
| /* | |
| * Now process the 16ms cycle counter (slower timing) | |
| * This handles VSS (Vehicle Speed Sensor) timing | |
| */ | |
| /* 0x0199: LB A, off(07FF9Ah) - Load 16ms counter */ | |
| uint8_t cycleCounter = g_batteryVoltage; /* Using 9A for cycle */ | |
| /* 0x019B: ADDB A, #001h - Increment */ | |
| cycleCounter++; | |
| /* 0x019D: JBS off(07FF31h).0, label_01d0 - Check skip flag */ | |
| if (g_faultCode2 & 0x01) { | |
| goto store_cycleCounter; | |
| } | |
| /* 0x01A0: RB TRNSIT.0 - Check timer transit */ | |
| uint8_t transitFlag = REG_TRNSIT & 0x01; | |
| REG_TRNSIT &= ~0x01; | |
| /* 0x01A3: JNE label_01b3 - If transit set, handle overflow */ | |
| if (transitFlag) { | |
| goto handle_cycleOverflow; | |
| } | |
| /* No overflow - check for sync */ | |
| /* 0x01A5: STB A, r0 */ | |
| /* 0x01A6: ANDB A, #00Fh - Check if cycle is multiple of 16 */ | |
| /* 0x01A8: JNE label_01b0 - Skip if not aligned */ | |
| if ((cycleCounter & 0x0F) == 0) { | |
| /* Every 16th cycle - set CYP detection flags */ | |
| /* 0x01AA: SB off(07FF2Eh).2 - Set CYP detect bit */ | |
| f_celDetect3 |= 0x04; | |
| /* 0x01AD: SB off(07FF1Ah).1 - Set first-CYP flag */ | |
| f_flags_11A |= 0x02; | |
| } | |
| /* label_01b0: */ | |
| goto store_cycleCounter; | |
| handle_cycleOverflow: | |
| /* label_01b3: Timer overflow during cycle */ | |
| /* 0x01B3: RB off(07FF1Ah).1 - Clear first-CYP flag */ | |
| f_flags_11A &= ~0x02; | |
| /* 0x01B6: MOVB off(07FFBCh), #007h - Set overflow marker */ | |
| g_rpmAccelDecel = 0x0007; | |
| /* 0x01BA: CMPB A, #010h - Check if counter == 16 */ | |
| /* 0x01BC: JEQ label_01c5 - If 16, reset */ | |
| if (cycleCounter == 0x10) { | |
| f_flags_121 &= ~0x02; /* Clear ignition flag */ | |
| if (g_crankPosition != 0) { | |
| f_celDetect3 |= 0x40; /* Set CYP error */ | |
| } | |
| cycleCounter = 0; | |
| goto store_cycleCounter; | |
| } | |
| /* 0x01BE: JGE label_01cf - If >= 16, clear and reset */ | |
| if (cycleCounter >= 0x10) { | |
| cycleCounter = 0; | |
| goto store_cycleCounter; | |
| } | |
| /* 0x01C0: JBR off(07FF21h).1, label_01cc - Check ignition flag */ | |
| if (!(f_flags_121 & 0x02)) { | |
| cycleCounter = 0; | |
| goto store_cycleCounter; | |
| } | |
| /* Set CYP error bit */ | |
| /* label_01cc: */ | |
| f_celDetect3 |= 0x40; | |
| cycleCounter = 0; | |
| store_cycleCounter: | |
| /* label_01d0: Store the cycle counter */ | |
| /* 0x01D0: STB A, off(07FF9Ah) - Store cycle counter */ | |
| g_batteryVoltage = cycleCounter; /* 9A used as cycle counter */ | |
| /* 0x01D2: ANDB A, #00Fh - Check low nibble */ | |
| /* 0x01D4: JNE label_01eb - Skip cylinder update if not 0 */ | |
| if ((cycleCounter & 0x0F) != 0) { | |
| goto check_flags_for_limiter; | |
| } | |
| /* | |
| * Update cylinder index (firing order) | |
| * Cylinder order: 0=Cyl1, 1=Cyl3, 2=Cyl4, 3=Cyl2 | |
| */ | |
| /* 0x01D6: LB A, 0E7h - Load some counter */ | |
| /* 0x01D8: JEQ label_01df - If zero, calculate cylinder */ | |
| if (g_unused_E7 == 0) { | |
| /* 0x01DF: MOV DP, #0021Ah - Load injector mask address */ | |
| /* 0x01E2: MB C, [DP].0 - Get bit 0 of mask */ | |
| /* 0x01E4: LB A, #001h - Load 1 */ | |
| /* 0x01E6: JGE label_01e9 - If carry set, use 1 */ | |
| uint8_t injMask = g_injectorMask; | |
| g_cylinderIndex = (injMask & 0x01) ? 1 : 0; | |
| } else { | |
| /* 0x01DA: DECB 0E7h - Decrement counter */ | |
| g_unused_E7--; | |
| g_cylinderIndex = 0; | |
| } | |
| /* label_01e9: */ | |
| /* 0x01E9: STB A, 0E5h - Store cylinder index */ | |
| /* Already set above */ | |
| check_flags_for_limiter: | |
| /* label_01eb: Check various flags */ | |
| /* 0x01EB: JBS off(07FF30h).7, label_01f1 - Check rev limiter */ | |
| if (f_flags_FE & 0x80) { | |
| goto update_e3_from_9a; | |
| } | |
| /* 0x01EE: JBR off(07FF1Ah).0, label_01fe - Check first-TDC flag */ | |
| if (!(f_flags_11A & 0x01)) { | |
| goto check_first_cyp; | |
| } | |
| update_e3_from_9a: | |
| /* label_01f1: Update TDC counter from cycle counter */ | |
| /* 0x01F1: ANDB 0E3h, #0FCh - Clear low 2 bits of E3 */ | |
| g_tdcPosition &= 0xFC; | |
| /* 0x01F5: LB A, off(07FF9Ah) - Load cycle counter */ | |
| /* 0x01F7: ANDB A, #003h - Get low 2 bits */ | |
| /* 0x01F9: ORB 0E3h, A - Merge into E3 */ | |
| g_tdcPosition |= (g_batteryVoltage & 0x03); | |
| /* 0x01FC: STB A, 0E4h - Update crank position */ | |
| g_crankPosition = g_batteryVoltage & 0x03; | |
| check_first_cyp: | |
| /* label_01fe: */ | |
| /* Similar logic for CYP synchronization */ | |
| if (g_faultCode2 & 0x01) { | |
| goto update_9a_from_e4; | |
| } | |
| if (!(f_flags_11A & 0x02)) { | |
| goto check_ignition_state; | |
| } | |
| update_9a_from_e4: | |
| /* label_0204: Update 9A from E4 */ | |
| g_batteryVoltage &= 0xFC; | |
| g_batteryVoltage |= (g_crankPosition & 0x03); | |
| check_ignition_state: | |
| /* label_020d: Determine ignition state */ | |
| /* 0x020D: RC - Clear carry */ | |
| bool ignitionBad = false; | |
| /* Check various conditions for limp mode */ | |
| if (!(f_flags_FE & 0x80)) { /* Not rev limited */ | |
| if (!(f_flags_11A & 0x01)) { /* No TDC sync */ | |
| if (!(f_flags_11A & 0x40)) { /* Some other flag */ | |
| ignitionBad = false; | |
| } else { | |
| ignitionBad = true; | |
| } | |
| } else { | |
| ignitionBad = true; | |
| } | |
| } else { | |
| if (!(g_faultCode2 & 0x01)) { | |
| if (!(f_flags_11A & 0x02)) { | |
| ignitionBad = false; | |
| } else { | |
| ignitionBad = true; | |
| } | |
| } else { | |
| ignitionBad = true; | |
| } | |
| } | |
| /* label_021e: Store ignition state */ | |
| /* 0x021E: MB off(07FF21h).3, C - Store bad flag in 121.3 */ | |
| if (ignitionBad) { | |
| f_flags_121 |= 0x08; | |
| f_flags_FE |= 0x40; /* Set limp mode flag */ | |
| } else { | |
| f_flags_121 &= ~0x08; | |
| } | |
| /* label_0226: Check if we should skip processing */ | |
| /* 0x0226: JBS off(07FF2Bh).6, label_022d - Check skip flag */ | |
| if (!(f_flags_12A & 0x40)) { | |
| /* 0x0229: ANDB off(07FF2Eh), #08Fh - Clear some detect bits */ | |
| f_celDetect3 &= 0x8F; | |
| } | |
| main_engine_processing: | |
| /* label_022d: Main engine calculation entry point */ | |
| /* 0x022D: JBS off(07FF1Fh).4, label_029f - Check if engine not running */ | |
| if (f_flags_11F & 0x10) { /* Engine running flag */ | |
| goto calculate_rpmDelta; /* Skip calculations */ | |
| } | |
| /* 0x0230: JBS off(07FF21h).2, label_0252 - Check ignition ready */ | |
| if (f_flags_121 & 0x04) { | |
| goto calculate_fuel_injection; /* Ignition ready */ | |
| } | |
| /* | |
| * Check cylinder synchronization | |
| * Uses address 0x0199 as a table lookup | |
| */ | |
| /* 0x0233: MOV DP, #00199h - Load sync table address */ | |
| /* 0x0236: LB A, 0E5h - Load cylinder index */ | |
| /* 0x0238: SRLB A - Shift right (divide by 2) */ | |
| /* 0x0239: LB A, off(07FF9Ah) - Load cycle counter */ | |
| /* 0x023B: JLT label_023f - Check sign */ | |
| /* 0x023D: ADDB A, #004h - Add offset */ | |
| uint8_t syncIndex = g_cylinderIndex >> 1; | |
| uint8_t cycleVal = g_batteryVoltage; | |
| if (cycleVal >= 0) { | |
| cycleVal += 4; | |
| } | |
| /* 0x023F: ANDB A, #007h - Mask to 3 bits */ | |
| syncIndex = cycleVal & 0x07; | |
| /* 0x0241: CMPB A, [DP] - Compare with table value */ | |
| /* 0x0243: JNE label_029f - If no match, skip to RPM calc */ | |
| /* ... synchronization check ... */ | |
| /* If synchronized, continue to fuel calculation */ | |
| calculate_fuel_injection: | |
| /* label_0252: Calculate fuel injection */ | |
| /* | |
| * This section calculates the final fuel pulse width | |
| * using the O2 sensor feedback loop. | |
| * | |
| * Formula: D6 = (0x144 * O2_trim / 0x10000) * 2 + 0x146 | |
| */ | |
| /* 0x0252: CLR A - Clear for 16-bit ops */ | |
| /* 0x0253: LB A, 0E5h - Load cylinder index */ | |
| /* 0x0255: SLLB A - Multiply by 2 for word index */ | |
| uint8_t cylIndex = g_cylinderIndex * 2; | |
| /* 0x0256: MOV DP, A - Use as offset */ | |
| /* 0x0257: ANDB A, #002h - Get bit 1 (odd/even cylinder pair) */ | |
| /* 0x0259: MOV X1, A - Store for O2 selection */ | |
| uint8_t o2Select = cylIndex & 0x02; /* 0 or 2 */ | |
| /* 0x025A: L A, 00162h[X1] - Load O2 trim (primary or secondary) */ | |
| uint16_t o2Trim; | |
| if (o2Select == 0) { | |
| o2Trim = g_o2TrimPrimary; /* 0x0162 */ | |
| } else { | |
| o2Trim = g_o2TrimSecondary; /* 0x0164 */ | |
| } | |
| /* 0x025D: MOV er0, #0944Eh - Load upper limit */ | |
| /* 0x0261: CMP A, #0B6E0h - Compare O2 trim with max */ | |
| /* 0x0264: JGE label_0270 - If too high, use limit */ | |
| uint16_t o2Limited = o2Trim; | |
| if (o2Trim >= 0xB6E0) { | |
| o2Limited = 0x944E; /* Upper clamp */ | |
| } else if (o2Trim <= 0x5720) { | |
| /* 0x0266: MOV er0, #0682Ah - Load lower limit */ | |
| o2Limited = 0x682A; /* Lower clamp */ | |
| } | |
| /* Otherwise use actual O2 trim value */ | |
| /* label_0270: Continue with fuel calculation */ | |
| /* 0x0270: SRL X1 - Divide index by 2 */ | |
| /* 0x0272: LB A, 0011Bh[X1] - Load O2 flags */ | |
| /* 0x0275: SRLB A - Check bit 0 */ | |
| /* 0x0276: JGE label_027f - Skip if not set */ | |
| /* 0x0278: CLR A - Clear for table lookup */ | |
| /* 0x0279: LC A, 037CCh[DP] - Load from fuel correction table */ | |
| /* 0x027D: ADD er0, A - Add correction */ | |
| /* label_027f: Final fuel calculation */ | |
| /* 0x027F: L A, off(07FF44h) - Load main fuel value (0x144) */ | |
| uint16_t mainFuel = g_fuelMainFinal; | |
| /* 0x0281: MUL - Multiply by O2 trim */ | |
| uint32_t fuelProduct = (uint32_t)mainFuel * o2Limited; | |
| /* 0x0283: SLL A - Shift for scaling */ | |
| /* 0x0284: L A, er1 - Get high word (result / 65536) */ | |
| /* 0x0285: ROL A - Double it */ | |
| uint16_t scaledFuel = (uint16_t)(fuelProduct >> 15); /* * 2 / 65536 */ | |
| /* 0x0286: JLT label_028c - Check for overflow */ | |
| if (scaledFuel & 0x8000) { | |
| scaledFuel = 0xFFFF; /* Clamp to max */ | |
| } | |
| /* 0x0288: ADD A, off(07FF46h) - Add fuel corrections (0x146) */ | |
| uint16_t finalFuel = scaledFuel + g_fuelCorrections; | |
| /* 0x028A: JGE label_028f - Check for overflow */ | |
| if (finalFuel < scaledFuel) { /* Overflow check */ | |
| finalFuel = 0xFFFF; | |
| } | |
| /* label_028f: Store final fuel pulse width */ | |
| /* 0x028F: ST A, 0D6h - Store to final fuel register! */ | |
| g_finalFuelPulse = finalFuel; | |
| /* 0x0291: CAL label_29b1 - Call injector update routine */ | |
| update_injectorPulseWidth(); | |
| /* 0x0294: MOV LRB, #00022h - Restore register bank */ | |
| REG_LRB = 0x22; | |
| /* 0x0297: LB A, 0E5h - Load cylinder index */ | |
| /* 0x0299: ADDB A, #001h - Increment for next cylinder */ | |
| /* 0x029B: ANDB A, #003h - Wrap around 0-3 */ | |
| /* 0x029D: STB A, 0E5h - Store updated cylinder */ | |
| g_cylinderIndex = (g_cylinderIndex + 1) & 0x03; | |
| calculate_rpmDelta: | |
| /* label_029f: Calculate RPM from timer delta */ | |
| /* | |
| * This section calculates RPM by measuring the time | |
| * between TDC events (45° of crank rotation each). | |
| */ | |
| /* 0x029F: L A, TMR1 - Load timer1 compare value */ | |
| uint16_t currentTimer = REG_TMR1; | |
| /* 0x02A1: ST A, er0 - Save to er0 */ | |
| uint16_t savedTimer = currentTimer; | |
| /* 0x02A2: SUB A, 0E0h - Subtract previous timer */ | |
| int16_t timerDelta = currentTimer - g_timer1Old; | |
| /* 0x02A5: JBR off(07FF21h).2, label_02bd - Check if running */ | |
| if (!(f_flags_121 & 0x04)) { | |
| goto timer_overflow_check; | |
| } | |
| /* Check for valid delta (no overflow) */ | |
| /* 0x02A8: JBS off(07FF1Eh).7, label_02b0 - Check overflow flag */ | |
| if (f_flags_11E & 0x80) { | |
| timerDelta = 0; /* Overflow - zero delta */ | |
| goto clear_rpm_samples; | |
| } | |
| /* 0x02AB: JBR off(07FF1Eh).6, label_02b1 - Check another flag */ | |
| if (!(f_flags_11E & 0x40)) { | |
| goto store_valid_delta; | |
| } | |
| /* 0x02AE: JLT label_02b1 - Check sign */ | |
| if (timerDelta < 0) { | |
| goto store_valid_delta; | |
| } | |
| /* Overflow or invalid - clear delta */ | |
| timerDelta = 0; | |
| clear_rpm_samples: | |
| /* label_02b0/02b1: Clear/init RPM sample buffer */ | |
| /* 0x02B1: MOV USP, #0020Dh - Set user stack pointer */ | |
| /* 0x02B5-0x02B8: PUSHU A (x4) - Clear 4 samples to zero */ | |
| g_rpmSample0 = 0; | |
| g_rpmSample1 = 0; | |
| g_rpmSample2 = 0; | |
| g_rpmSample3 = 0; | |
| /* 0x02B9: ST A, 0B8h - Store zero delta */ | |
| g_rpmTimerDelta = 0; | |
| goto update_old_timer; | |
| timer_overflow_check: | |
| /* label_02bd: Check for timer overflow during measurement */ | |
| /* 0x02BD: MB C, TCON1.2 - Check timer overflow bit */ | |
| if (REG_TCON1 & 0x04) { | |
| timerDelta = 0; /* Overflow - invalid measurement */ | |
| } | |
| store_valid_delta: | |
| /* label_02c3: Store the timer delta */ | |
| /* 0x02C3: ST A, 0B8h - Store delta for RPM calc */ | |
| g_rpmTimerDelta = timerDelta; | |
| /* Store in rotating sample buffer */ | |
| /* 0x02C5: LB A, 0E4h - Load crank position (0-3) */ | |
| /* 0x02C7: SLLB A - Multiply by 2 for word offset */ | |
| /* 0x02C8: EXTND - Sign extend to 16-bit */ | |
| /* 0x02C9: MOV X1, A - Use as index */ | |
| uint8_t sampleIndex = g_crankPosition * 2; | |
| /* 0x02CA: L A, 0B8h - Load delta */ | |
| /* 0x02CC: ST A, 00206h[X1] - Store in sample buffer */ | |
| switch (g_crankPosition) { | |
| case 0: g_rpmSample0 = timerDelta; break; | |
| case 1: g_rpmSample1 = timerDelta; break; | |
| case 2: g_rpmSample2 = timerDelta; break; | |
| case 3: g_rpmSample3 = timerDelta; break; | |
| } | |
| update_old_timer: | |
| /* label_02cf: Update old timer value */ | |
| /* 0x02CF: L A, er0 - Load saved timer */ | |
| /* 0x02D0: ST A, 0E0h - Store as previous timer */ | |
| g_timer1Old = savedTimer; | |
| /* 0x02D2: SLL A - Check if timer high bit set */ | |
| /* 0x02D3: JLT label_02db - If negative, skip flag update */ | |
| if (!(savedTimer & 0x8000)) { | |
| /* 0x02D5: MB C, IRQ.6 - Check timer IRQ */ | |
| /* 0x02D8: MB 0FDh.5, C - Store to flag */ | |
| if (REG_IRQ & 0x40) { | |
| f_flags_FD |= 0x20; | |
| } else { | |
| f_flags_FD &= ~0x20; | |
| } | |
| } | |
| /* label_02db: Clear overflow flags */ | |
| /* 0x02DB: ANDB off(07FF1Eh), #03Fh - Clear bits 6-7 */ | |
| f_flags_11E &= 0x3F; | |
| /* 0x02DF: LB A, 0E4h - Load crank position */ | |
| /* 0x02E1: JEQ label_02f6 - If position 0, check engine start */ | |
| if (g_crankPosition == 0) { | |
| goto check_engine_starting; | |
| } | |
| /* 0x02E3: CMPB A, #003h - Check if position 3 */ | |
| /* 0x02E5: JEQ label_0341 - If position 3, calculate ignition */ | |
| if (g_crankPosition == 3) { | |
| goto calculate_ignitionDwell; | |
| } | |
| /* Position 1 or 2 - calculate injector timing */ | |
| /* ... injector timing calculations ... */ | |
| goto interrupt_exit; | |
| check_engine_starting: | |
| /* label_02f6: Check if engine is starting */ | |
| /* Compare RPM with threshold */ | |
| /* 0x02F6: LB A, #012h - High threshold (cranking) */ | |
| uint8_t rpmThreshold = 0x12; | |
| /* 0x02F8: JBR off(07FF18h).1, label_02fd - Check starter flag */ | |
| if (!(f_flags_118 & 0x02)) { | |
| rpmThreshold = 0x0B; /* Low threshold (running) */ | |
| } | |
| /* label_02fd: */ | |
| /* 0x02FD: CMPB A, 0BBh - Compare with RPM high byte */ | |
| if (g_rpmTimerCountHi > rpmThreshold) { | |
| /* Engine running */ | |
| f_flags_118 |= 0x02; /* Set running flag */ | |
| } else { | |
| f_flags_118 &= ~0x02; /* Clear running flag */ | |
| } | |
| /* 0x0316: MB P2.4, C - Update limp mode output */ | |
| if (f_flags_118 & 0x02) { | |
| REG_P2 |= 0x10; | |
| } else { | |
| REG_P2 &= ~0x10; | |
| } | |
| /* 0x0319: CAL label_2995 - Call RPM processing */ | |
| process_rpmSignal(); | |
| goto interrupt_exit; | |
| calculate_ignitionDwell: | |
| /* label_0341: Calculate ignition coil dwell time */ | |
| /* | |
| * This calculates when to turn on the ignition coil | |
| * so it's fully charged before the spark event. | |
| */ | |
| /* 0x0341: CLR A */ | |
| /* 0x0342: CLRB A */ | |
| /* 0x0343: STB A, r1 */ | |
| /* 0x0344: SUBB A, off(07FF35h) - Get ignition timing */ | |
| int8_t ignTiming = -(int8_t)g_ignitionVcalCalc; | |
| /* 0x0346: L A, ACC - Load full word */ | |
| /* 0x0348: SLL A - Multiply by 2 */ | |
| int16_t dwellCalc = ignTiming * 2; | |
| /* 0x0349: MOVB r0, off(07FF34h) - Load final ignition */ | |
| uint8_t ignFinal = g_ignitionFinal; | |
| /* 0x034C: SUB A, er0 - Subtract */ | |
| dwellCalc -= ignFinal; | |
| /* 0x034D: SLL A - Multiply by 2 again */ | |
| dwellCalc *= 2; | |
| /* 0x034E: CMPB ACCH, #0FEh - Check for max */ | |
| if ((dwellCalc >> 8) == 0xFE) { | |
| dwellCalc = 0xFF00; /* Clamp to max */ | |
| } | |
| /* label_0357: Store dwell time */ | |
| /* 0x0357: ST A, 0DEh - Store ignitor timing */ | |
| g_ignitorTiming3 = dwellCalc; | |
| /* Calculate inverse for other banks */ | |
| /* 0x0359: LB A, off(07FF34h) - Load final ignition */ | |
| /* 0x035B: XORB A, #0FFh - Invert */ | |
| /* 0x035D: SLLB A - Multiply by 2 */ | |
| /* 0x035E: INCB ACC - Add 1 */ | |
| /* 0x0361: STB A, off(07FF36h) - Store inverted timing */ | |
| g_ignitionInverse = ((~ignFinal) << 1) + 1; | |
| /* Continue to ignitor update */ | |
| goto update_ignitor_state; | |
| update_ignitor_state: | |
| /* label_0366-03c5: Update ignitor output timing */ | |
| /* ... complex timing calculations for spark output ... */ | |
| /* Final store of dwell time */ | |
| /* 0x03C5: ST A, 0D8h - Store final ignitor timing */ | |
| g_ignitorTiming0 = dwellCalc; | |
| interrupt_exit: | |
| /* label_03cd: Common interrupt exit */ | |
| /* 0x03CD: RB PSWH.0 - Clear 16-bit mode */ | |
| REG_PSWH &= ~0x01; | |
| /* 0x03CF: POPS A - Restore IE */ | |
| /* 0x03D0: ST A, IE */ | |
| REG_IE = savedIE; | |
| /* 0x03D2: RTI - Return from interrupt */ | |
| return; | |
| } | |
| /** | |
| * isr_externalInt0_CKP() - External Interrupt 0 (Crankshaft Position) | |
| * | |
| * Address: 0x151F (label: int_INT0) | |
| * | |
| * Triggered by CKP (Crankshaft Position Sensor) signal from | |
| * the distributor. This provides the primary timing reference | |
| * for the engine control system. | |
| * | |
| * The CKP signal occurs 8 times per crank revolution (45° apart). | |
| */ | |
| void isr_externalInt0_CKP(void) | |
| { | |
| /* 0x151F: L A, IE - Save current IE */ | |
| /* 0x1521: PUSHS A - Push to stack */ | |
| uint16_t savedIE = REG_IE; | |
| /* 0x1522: L A, 0CEh - Load interrupt IE value */ | |
| /* 0x1524: ST A, IE - Enable nested interrupts */ | |
| REG_IE = g_ieInterrupt; | |
| /* 0x1526: SB PSWH.0 - Enable 16-bit addressing */ | |
| REG_PSWH |= 0x01; | |
| /* 0x1528: MOV LRB, #00020h - Switch to register bank 2 */ | |
| REG_LRB = 0x20; | |
| /* 0x152B: SB 0FEh.0 - Set CKP flag (pulse received) */ | |
| f_flags_FE |= 0x01; | |
| /* Capture timer value for RPM calculation */ | |
| /* 0x152E: L A, TM1 - Read Timer1 current value */ | |
| /* 0x1530: XCHG A, 0C8h - Exchange with previous capture */ | |
| uint16_t currentTimer = REG_TM1; | |
| uint16_t previousTimer = g_timer1Capture; | |
| g_timer1Capture = currentTimer; | |
| /* 0x1533: ST A, 0C6h - Store previous as old-previous */ | |
| g_timer1Previous = previousTimer; | |
| /* 0x1535: LB A, 0E2h - Load overflow counter */ | |
| /* 0x1537: STB A, 0CAh - Store as old overflow */ | |
| g_timerOverflowOld = g_timer1Overflow; | |
| /* 0x1539: CLRB 0E2h - Clear overflow counter */ | |
| g_timer1Overflow = 0; | |
| /* 0x153C: RB IRQ.6 - Check/clear Timer1 overflow IRQ */ | |
| uint8_t overflowed = REG_IRQ & 0x40; | |
| REG_IRQ &= ~0x40; /* Clear flag */ | |
| /* 0x153F: JEQ label_1557 - If no overflow, exit */ | |
| if (overflowed == 0) { | |
| goto ckp_exit; | |
| } | |
| /* Timer overflow occurred - update flags */ | |
| /* 0x1541: MB C, off(0011Eh).6 - Copy bit 6 to carry */ | |
| /* 0x1544: MB off(0011Eh).7, C - Move carry to bit 7 */ | |
| uint8_t oldBit6 = (f_flags_11E >> 6) & 0x01; | |
| f_flags_11E = (f_flags_11E & 0x7F) | (oldBit6 << 7); | |
| /* 0x1547: SB off(0011Eh).6 - Set overflow flag bit 6 */ | |
| f_flags_11E |= 0x40; | |
| /* 0x154A: MB C, 0C9h.7 - Check engine sync flag */ | |
| /* 0x154D: JGE label_1554 - Branch based on sync state */ | |
| if (g_engineSync & 0x80) { | |
| /* 0x154F: INCB 0E2h - Increment overflow counter */ | |
| g_timer1Overflow++; | |
| goto ckp_exit; | |
| } | |
| /* label_1554: */ | |
| /* 0x1554: INCB 0CAh - Increment old overflow */ | |
| g_timerOverflowOld++; | |
| ckp_exit: | |
| /* label_1557: Restore and exit */ | |
| /* 0x1557: RB PSWH.0 - Clear 16-bit mode */ | |
| REG_PSWH &= ~0x01; | |
| /* 0x1559: POPS A - Restore IE */ | |
| /* 0x155A: ST A, IE */ | |
| REG_IE = savedIE; | |
| /* 0x155C: RTI */ | |
| return; | |
| } | |
| /** | |
| * isr_serialRxBaudRate() - Serial RX Baud Rate Generator Interrupt | |
| * | |
| * Address: 0x155D (label: int_serial_rx_BRG) | |
| * | |
| * Triggered periodically by the serial baud rate generator. | |
| * Used to sample the TPS (Throttle Position Sensor) at a | |
| * consistent rate for smooth throttle response. | |
| */ | |
| void isr_serialRxBaudRate(void) | |
| { | |
| /* 0x155D: SB 0FEh.1 - Set TPS sample flag */ | |
| f_flags_FE |= 0x02; | |
| /* 0x1560: L A, ADCR7 - Read TPS from ADC channel 7 */ | |
| /* 0x1562: ST A, 0AAh - Store raw TPS value */ | |
| g_tpsRaw = REG_ADCR7; | |
| /* 0x1564: RTI */ | |
| return; | |
| } | |
| /** | |
| * isr_timer0Overflow() - Timer 0 Overflow (Injector Control) | |
| * | |
| * Address: 0x1565 (label: int_timer_0_overflow) | |
| * | |
| * Controls the fuel injector timing. When Timer0 overflows or | |
| * matches, this ISR updates the injector output pins to turn | |
| * injectors on or off at precisely the right time. | |
| * | |
| * Uses a rotating bit pattern to sequence through cylinders: | |
| * 0111_0111 -> 1011_1011 -> 1101_1101 -> 1110_1110 | |
| */ | |
| void isr_timer0Overflow(void) | |
| { | |
| /* 0x1565: MOV LRB, #00040h - Switch to register bank 4 (0x200) */ | |
| REG_LRB = 0x40; | |
| /* 0x1568: L A, off(00214h) - Load injector timing 0 */ | |
| uint16_t injTiming0 = g_injTiming0; | |
| /* 0x156A: JNE label_159e - If timing0 != 0, use it */ | |
| if (injTiming0 != 0) { | |
| goto use_timing0; | |
| } | |
| /* Timing0 is 0, check timing1 */ | |
| /* 0x156C: L A, off(00216h) - Load injector timing 1 */ | |
| uint16_t injTiming1 = g_injTiming1; | |
| /* 0x156E: JEQ label_15d3 - If timing1 also 0, check timing2 */ | |
| if (injTiming1 == 0) { | |
| goto check_timing2; | |
| } | |
| /* Use timing1 - rotate injector mask and update outputs */ | |
| /* 0x1570: LB A, off(0021Bh) - Load injector mask inverse */ | |
| uint8_t injMaskInv = g_injectorMaskInv; | |
| /* Rotate left through carry (barrel shift) */ | |
| /* 0x1572: MB C, ACC.7 - Get high bit to carry */ | |
| /* 0x1575: ROLB A - Rotate left */ | |
| uint8_t carryBit = (injMaskInv >> 7) & 0x01; | |
| injMaskInv = (injMaskInv << 1) | carryBit; | |
| /* 0x1576: ORB off(0021Ch), A - OR into output mask */ | |
| g_injectorOutput |= injMaskInv; | |
| /* Rotate again for next cylinder */ | |
| /* 0x1579: MB C, ACC.7 */ | |
| /* 0x157C: ROLB A */ | |
| carryBit = (injMaskInv >> 7) & 0x01; | |
| injMaskInv = (injMaskInv << 1) | carryBit; | |
| /* 0x157D: STB A, off(0021Bh) - Store updated mask */ | |
| g_injectorMaskInv = injMaskInv; | |
| /* 0x157F: ORB A, off(0021Ch) - Combine masks */ | |
| /* 0x1581: ANDB A, #00Fh - Keep only injector bits */ | |
| /* 0x1583: STB A, off(0021Ch) - Store output pattern */ | |
| g_injectorOutput = (g_injectorOutput | injMaskInv) & 0x0F; | |
| /* 0x1585: CAL label_28ed - Call output update */ | |
| update_injectorOutputs(); | |
| /* 0x1588: ORB P2, off(0021Ch) - Apply to port (turn off injectors) */ | |
| REG_P2 |= g_injectorOutput; | |
| /* 0x158C: L A, off(00216h) - Load timing1 */ | |
| /* 0x158E: ST A, TM0 - Set as next timer compare */ | |
| REG_TM0 = g_injTiming1; | |
| /* 0x1590: CAL label_2906 - Call timing setup */ | |
| setup_injectorTimer(); | |
| /* Shift timing buffer: 2->0, next->1 */ | |
| /* 0x1593: MOV off(00214h), off(00218h) */ | |
| g_injTiming0 = g_injTiming2; | |
| /* 0x1597: L A, #0FFFFh - Load "no event" value */ | |
| /* 0x159A: ST A, off(00216h) - Clear timing1 */ | |
| g_injTiming1 = 0xFFFF; | |
| goto store_timing2; | |
| use_timing0: | |
| /* label_159e: Use timing0 */ | |
| /* Similar rotation logic for this path */ | |
| injMaskInv = g_injectorMaskInv; | |
| carryBit = (injMaskInv >> 7) & 0x01; | |
| injMaskInv = (injMaskInv << 1) | carryBit; | |
| g_injectorMaskInv = injMaskInv; | |
| g_injectorOutput = (g_injectorOutput | (injMaskInv & 0x0F)) & 0x0F; | |
| update_injectorOutputs(); | |
| REG_P2 |= g_injectorOutput; | |
| REG_TM0 = g_injTiming0; | |
| setup_injectorTimer(); | |
| /* Shift: 1->0, 2->1 */ | |
| g_injTiming0 = g_injTiming1; | |
| g_injTiming1 = g_injTiming2; | |
| store_timing2: | |
| /* 0x15C4: ST A, off(00218h) - Store 0xFFFF to timing2 */ | |
| g_injTiming2 = 0xFFFF; | |
| /* 0x15C6: CMPB off(0021Ch), #00Fh - Check if all injectors off */ | |
| /* 0x15CA: JNE label_15d2 - If not all off, continue */ | |
| if (g_injectorOutput == 0x0F) { | |
| /* All injectors off - disable timer interrupt */ | |
| /* 0x15CC: RB TCON0.4 - Disable Timer0 */ | |
| REG_TCON0 &= ~0x10; | |
| /* 0x15CF: RB IRQ.4 - Clear pending interrupt */ | |
| REG_IRQ &= ~0x10; | |
| } | |
| /* label_15d2: RTI */ | |
| return; | |
| check_timing2: | |
| /* label_15d3: Both timing0 and timing1 are 0, check timing2 */ | |
| /* 0x15D3: L A, off(00218h) - Load timing2 */ | |
| uint16_t injTiming2 = g_injTiming2; | |
| /* 0x15D5: JEQ label_15ff - If timing2 also 0, all done */ | |
| if (injTiming2 == 0) { | |
| goto all_injectors_off; | |
| } | |
| /* Process timing2 with inverse rotation */ | |
| /* 0x15D7: LB A, off(0021Bh) - Load mask inverse */ | |
| /* 0x15D9: XORB A, #0FFh - Invert it */ | |
| /* 0x15DB: ANDB A, #00Fh - Keep low nibble */ | |
| /* 0x15DD: ORB off(0021Ch), A - Merge into output */ | |
| uint8_t invMask = (~g_injectorMaskInv) & 0x0F; | |
| g_injectorOutput |= invMask; | |
| /* Rotate right for previous cylinder */ | |
| injMaskInv = g_injectorMaskInv; | |
| carryBit = injMaskInv & 0x01; | |
| injMaskInv = (injMaskInv >> 1) | (carryBit << 7); | |
| g_injectorMaskInv = injMaskInv; | |
| update_injectorOutputs(); | |
| REG_P2 |= g_injectorOutput; | |
| REG_TM0 = g_injTiming2; | |
| goto clear_all_timings; | |
| all_injectors_off: | |
| /* label_15ff: No more injector events */ | |
| /* 0x15FF: MOVB off(0021Ch), #00Fh - All injectors off */ | |
| g_injectorOutput = 0x0F; | |
| update_injectorOutputs(); | |
| REG_P2 |= 0x0F; /* Turn off all injectors */ | |
| clear_all_timings: | |
| /* label_15f3: Clear timing buffer */ | |
| setup_injectorTimer(); | |
| g_injTiming0 = 0xFFFF; | |
| g_injTiming1 = 0xFFFF; | |
| g_injTiming2 = 0xFFFF; | |
| return; | |
| } | |
| /** | |
| * isr_timer1Overflow() - Timer 1 Overflow (RPM Timing) | |
| * | |
| * Address: 0x160C (label: int_timer_1_overflow) | |
| * | |
| * Occurs when Timer1 overflows (every 65536 counts at CLK/32). | |
| * Used to detect very low RPM or stalled engine conditions. | |
| * Also manages the RPM measurement overflow flags. | |
| */ | |
| void isr_timer1Overflow(void) | |
| { | |
| /* 0x160C: AND IE, #00080h - Disable most interrupts */ | |
| REG_IE &= 0x0080; | |
| /* 0x1611: SB PSWH.0 - Enable 16-bit mode */ | |
| REG_PSWH |= 0x01; | |
| /* 0x1613: MOV LRB, #00020h - Switch to register bank 2 */ | |
| REG_LRB = 0x20; | |
| /* Shift overflow history flags */ | |
| /* 0x1616: MB C, off(0011Eh).6 - Get bit 6 */ | |
| /* 0x1619: MB off(0011Eh).7, C - Move to bit 7 */ | |
| uint8_t bit6 = (f_flags_11E >> 6) & 0x01; | |
| f_flags_11E = (f_flags_11E & 0x7F) | (bit6 << 7); | |
| /* 0x161C: SB off(0011Eh).6 - Set bit 6 (current overflow) */ | |
| f_flags_11E |= 0x40; | |
| /* 0x161F: L A, 0CEh - Load interrupt IE */ | |
| /* 0x1621: ST A, IE - Restore interrupts */ | |
| REG_IE = g_ieInterrupt; | |
| /* 0x1623: RB 0FDh.5 - Check/clear flag FD.5 */ | |
| if (f_flags_FD & 0x20) { | |
| f_flags_FD &= ~0x20; | |
| /* 0x1628: ANDB off(0011Eh), #03Fh - Clear overflow bits */ | |
| f_flags_11E &= 0x3F; | |
| } | |
| /* label_162c: */ | |
| /* 0x162C: INCB 0E2h - Increment overflow counter */ | |
| g_timer1Overflow++; | |
| /* 0x162F: L A, 0CCh - Load normal IE */ | |
| /* 0x1631: RB PSWH.0 - Clear 16-bit mode */ | |
| /* 0x1633: ST A, IE - Restore interrupts */ | |
| REG_PSWH &= ~0x01; | |
| REG_IE = g_ieNormal; | |
| /* 0x1635: RTI */ | |
| return; | |
| } | |
| /** | |
| * isr_pwmTimer() - PWM Timer Interrupt (IACV Control) | |
| * | |
| * Address: 0x1636 (label: int_PWM_timer) | |
| * | |
| * Controls the IACV (Idle Air Control Valve) PWM output. | |
| * The duty cycle determines how much air bypasses the throttle | |
| * plate at idle, controlling idle speed. | |
| * | |
| * Also samples the PA (atmospheric pressure) sensor. | |
| */ | |
| void isr_pwmTimer(void) | |
| { | |
| /* 0x1636: L A, 0CEh - Load interrupt IE */ | |
| /* 0x1638: ST A, IE - Enable nested interrupts */ | |
| REG_IE = g_ieInterrupt; | |
| /* 0x163A: SB PSWH.0 - Enable 16-bit mode */ | |
| REG_PSWH |= 0x01; | |
| /* 0x163C: MOV LRB, #00040h - Switch to register bank 4 (0x200) */ | |
| REG_LRB = 0x40; | |
| /* 0x163F: JBR off(0021Dh).0, label_1661 - Check IACV phase flag */ | |
| if (!(g_iacvControl & 0x01)) { | |
| goto iacv_phase2; | |
| } | |
| /* IACV Phase 1: Apply calculated duty cycle */ | |
| /* 0x1642: RB off(0021Dh).0 - Clear phase flag (toggle) */ | |
| g_iacvControl &= ~0x01; | |
| /* 0x1645: MOV PWMR1, #0FD58h - Set PWM1 reload value */ | |
| REG_PWMR1 = 0xFD58; /* Fixed timing for IACV pulse */ | |
| /* 0x164A: L A, ADCR4 - Read PA sensor (atmospheric) */ | |
| /* 0x164C: ST A, 0A8h - Store PA value */ | |
| g_atmPressure = REG_ADCR4; | |
| /* 0x164E: L A, off(00202h) - Load IACV duty cycle */ | |
| /* 0x1650: ST A, off(00204h) - Copy to output buffer */ | |
| g_iacvDutyCopy = g_iacvDutyCycle; | |
| /* 0x1652: JBS off(00203h).4, label_1658 - Check max duty flag */ | |
| if (!(g_iacvDutyCycle & 0x1000)) { | |
| /* Not at max - use calculated value */ | |
| /* 0x1655: L A, #0E001h - Load minimum duty value */ | |
| goto apply_pwm; | |
| } | |
| /* Use calculated duty cycle */ | |
| apply_pwm: | |
| /* label_1658: Apply PWM value */ | |
| /* 0x1658: ST A, PWMR0 - Set PWM0 reload (IACV duty) */ | |
| REG_PWMR0 = g_iacvDutyCycle; | |
| goto pwm_exit; | |
| iacv_phase2: | |
| /* label_1661: IACV Phase 2 */ | |
| /* 0x1661: SB off(0021Dh).0 - Set phase flag (toggle) */ | |
| g_iacvControl |= 0x01; | |
| /* 0x1664: MOV PWMR1, #0FFFFh - Set max PWM1 reload */ | |
| REG_PWMR1 = 0xFFFF; | |
| /* 0x1669: L A, off(00204h) - Load duty cycle copy */ | |
| uint16_t dutyCycle = g_iacvDutyCopy; | |
| /* 0x166B: JBR off(00205h).4, label_1658 - Check min duty flag */ | |
| if (!(dutyCycle & 0x1000)) { | |
| /* 0x166E: L A, #0FFFFh - Load max value (off) */ | |
| dutyCycle = 0xFFFF; | |
| } | |
| REG_PWMR0 = dutyCycle; | |
| pwm_exit: | |
| /* 0x165A: L A, 0CCh - Load normal IE */ | |
| /* 0x165C: RB PSWH.0 - Clear 16-bit mode */ | |
| /* 0x165E: ST A, IE - Restore interrupts */ | |
| REG_PSWH &= ~0x01; | |
| REG_IE = g_ieNormal; | |
| /* 0x1660: RTI */ | |
| return; | |
| } | |
| /*============================================================================ | |
| * SECTION 6: SYSTEM INITIALIZATION | |
| * ============================================================================ | |
| * | |
| * This section handles power-on reset and system initialization. | |
| * Configures all hardware peripherals and prepares the ECU for operation. | |
| * | |
| * Address: 0x1673-0x189A (labels: int_start, int_break, int_WDT) | |
| * | |
| *============================================================================*/ | |
| /** | |
| * isr_watchdogTimeout() - Watchdog Timer Reset Handler | |
| * | |
| * Address: 0x1696 (label: int_WDT) | |
| * | |
| * Called when the watchdog timer expires (ECU hung). | |
| * Stores error code 0x44 and triggers reset. | |
| */ | |
| void isr_watchdogTimeout(void) | |
| { | |
| /* 0x1696: MOVB 0EDh, #044h - Store WDT error code */ | |
| g_errorCodeReboot = 0x44; | |
| /* Falls through to isr_softwareBreak */ | |
| isr_softwareBreak(); | |
| } | |
| /** | |
| * isr_softwareBreak() - Software Break / Reset Handler | |
| * | |
| * Address: 0x169A (label: int_break) | |
| * | |
| * Common entry point for various reset conditions. | |
| * Called from BRK instruction, WDT timeout, and unused vectors. | |
| */ | |
| void isr_softwareBreak(void) | |
| { | |
| /* 0x169A: CLR PSW - Clear processor status (reset state) */ | |
| REG_PSW = 0x0000; | |
| /* 0x169D: SJ label_1678 - Jump to reset continuation */ | |
| goto reset_continue; | |
| reset_continue: | |
| /* Continue with system initialization */ | |
| isr_systemReset(); | |
| } | |
| /** | |
| * isr_systemReset() - Power-On Reset / System Initialization | |
| * | |
| * Address: 0x1673 (label: int_start) | |
| * | |
| * This is the ECU entry point after power-on or reset. | |
| * Initializes all hardware and RAM, then enters the main loop. | |
| * | |
| * MODIFIED for datalogging: Stack relocated to 0x025B | |
| */ | |
| void isr_systemReset(void) | |
| { | |
| /* 0x1673: MOV PSW, #00010h - Set initial processor status */ | |
| REG_PSW = 0x0010; /* Basic mode, interrupts disabled */ | |
| reset_watchdog: | |
| /* label_1678: Feed watchdog and initialize stack */ | |
| /* 0x1678: MOVB WDT, #03Ch - Reset watchdog timer */ | |
| REG_WDT = 0x3C; /* Feed the watchdog */ | |
| /* | |
| * DATALOGGING MODIFICATION: | |
| * Stack relocated from 0x0264 to 0x025B for logging buffer space | |
| */ | |
| /* 0x167C: MOV SSP, #0025Bh - Set system stack pointer */ | |
| REG_SSP = 0x025B; /* Modified for datalogging */ | |
| /* 0x1680: MOV LRB, #00010h - Switch to register bank 1 (0x080) */ | |
| REG_LRB = 0x10; | |
| /* 0x1683: CLR er1 - Clear extended register pair */ | |
| /* (er1 = r2:r3 in current bank) */ | |
| /* 0x1685: JBR PSW.4, label_169f - Check if warm reset */ | |
| if (!(REG_PSW & 0x10)) { | |
| goto check_warm_reset; | |
| } | |
| cold_boot: | |
| /* label_1688: Cold boot initialization */ | |
| /* 0x1688: MOV DP, #04000h - Point to ROM for checksum? */ | |
| REG_DP = 0x4000; | |
| /* 0x168B: MOVB A, [DP] - Read first byte */ | |
| uint8_t romByte = *(volatile uint8_t*)REG_DP; | |
| /* 0x168D: ANDB A, #080h - Check high bit */ | |
| uint8_t configBit = romByte & 0x80; | |
| /* Store boot configuration */ | |
| /* 0x1690: MOVB r1, #020h - Set life counter init */ | |
| uint8_t lifeInit = 0x20; | |
| /* 0x1692: MOVB r2, #014h - Set some counter */ | |
| uint8_t counter2 = 0x14; | |
| goto check_power_pin; | |
| check_warm_reset: | |
| /* label_169f: Check for warm reset (NMI recovery) */ | |
| /* 0x169F: CMPB 0EDh, #047h - Check for NMI error code */ | |
| if (g_errorCodeReboot == 0x47) { | |
| /* Was NMI - treat as cold boot */ | |
| goto cold_boot; | |
| } | |
| /* 0x16A5: SB 0FDh.6 - Set warm reset flag */ | |
| f_flags_FD |= 0x40; | |
| /* Preserve some state from before reset */ | |
| configBit = f_flags_FD; | |
| lifeInit = g_unused_E9; | |
| counter2 = g_errorCodeReboot; | |
| /* 0x16B1: JBS 0EDh.3, label_16b6 - Check error bit */ | |
| if (g_errorCodeReboot & 0x08) { | |
| goto check_power_pin; | |
| } | |
| /* 0x16B4: SB PSWL.4 - Set PSW bit 4 */ | |
| REG_PSWL |= 0x10; | |
| check_power_pin: | |
| /* label_16b6: Check main power before continuing */ | |
| /* 0x16B6: JBR P4.1, label_16bc - Check power pin */ | |
| if (!(REG_P4 & 0x02)) { | |
| /* Power not stable - enter NMI handler */ | |
| isr_nonMaskableInterrupt(); | |
| return; | |
| } | |
| init_ports: | |
| /* label_16bc: Initialize I/O ports */ | |
| /* 0x16BC: CLRB PRPHF - Clear peripheral flags */ | |
| REG_PRPHF = 0x00; | |
| /* === Port 0: General purpose + CEL light + A/C === */ | |
| /* 0x16BF: MOVB P0, #09Fh - Initial port 0 state */ | |
| REG_P0 = 0x9F; /* CEL off (bit 6=1), A/C off (bit 7=1) */ | |
| /* 0x16C3: LB A, #0FFh - All bits output */ | |
| /* 0x16C5: STB A, P0IO - Set port 0 direction */ | |
| REG_P0IO = 0xFF; /* All outputs */ | |
| /* === Port 1: VTEC + CEL LED === */ | |
| /* 0x16C7: MOVB P1, #0FFh - Initial port 1 state */ | |
| REG_P1 = 0xFF; /* VTEC off, CEL LED off */ | |
| /* 0x16CB: STB A, P1IO - Set port 1 direction */ | |
| REG_P1IO = 0xFF; /* All outputs */ | |
| /* === Port 2: Fuel injectors === */ | |
| /* 0x16CD: MOVB P2, #01Fh - Initial port 2 state */ | |
| REG_P2 = 0x1F; /* All injectors OFF (1 = off) */ | |
| /* 0x16D1: STB A, P2IO - Set port 2 direction */ | |
| REG_P2IO = 0xFF; /* All outputs */ | |
| /* 0x16D3: MOVB P2SF, #000h - No special functions */ | |
| REG_P2SF = 0x00; | |
| /* === Port 3: Ignition output === */ | |
| /* 0x16D7: STB A, P3 - Initial port 3 state */ | |
| REG_P3 = 0xFF; | |
| init_serial: | |
| /* | |
| * DATALOGGING MODIFICATION: | |
| * Serial port configured for 9600 baud datalogging | |
| * instead of stock diagnostic rate. | |
| */ | |
| /* 0x16D9: MOVB STTMC, #002h - Serial TX timer control */ | |
| REG_STTMC = 0x02; | |
| /* 0x16DD: MOVB STCON, #031h - Serial TX control (MODIFIED) */ | |
| REG_STCON = 0x31; /* Changed from 0x3C for datalogging */ | |
| /* 0x16E1: MOVB SRCON, #021h - Serial RX control (MODIFIED) */ | |
| REG_SRCON = 0x21; /* Changed from 0x2C for datalogging */ | |
| /* 0x16E5: MOVB STTM, #0FCh - Serial TX timer (MODIFIED) */ | |
| REG_STTM = 0xFC; /* Changed from 0xF3 for baud rate */ | |
| /* 0x16E9: MOVB STTMR, #0FCh - Serial TX timer reload (MODIFIED) */ | |
| REG_STTMR = 0xFC; | |
| /* 0x16ED: MOVB SRTMC, #0C0h - Serial RX timer control */ | |
| REG_SRTMC = 0xC0; | |
| /* 0x16F1: LB A, #064h - RX timer value */ | |
| /* 0x16F3: STB A, SRTM - Set RX timer */ | |
| /* 0x16F5: STB A, SRTMR - Set RX timer reload */ | |
| REG_SRTM = 0x64; | |
| REG_SRTMR = 0x64; | |
| init_external_io: | |
| /* 0x16F7: CLRB EXION - Disable external I/O expansion */ | |
| REG_EXION = 0x00; | |
| init_timers: | |
| /* 0x16FA: CLR A - Clear accumulator for timer init */ | |
| /* === Timer 0: Injector timing / Ignition output === */ | |
| /* 0x16FB: MOVB TCON0, #08Ch - Timer 0 control */ | |
| REG_TCON0 = 0x8C; /* CLK/32, output mode, enabled */ | |
| /* 0x16FF: MOV TM0, #00001h - Timer 0 initial count */ | |
| REG_TM0 = 0x0001; | |
| /* 0x1704: ST A, TMR0 - Timer 0 reload (0) */ | |
| REG_TMR0 = 0x0000; | |
| /* === Timer 1: Main timing / RPM measurement === */ | |
| /* 0x1706: MOVB TCON1, #08Eh - Timer 1 control */ | |
| REG_TCON1 = 0x8E; /* CLK/32, capture mode */ | |
| /* 0x170A: ST A, TM1 - Timer 1 initial count (0) */ | |
| REG_TM1 = 0x0000; | |
| /* 0x170C: ST A, TMR1 - Timer 1 reload (0) */ | |
| REG_TMR1 = 0x0000; | |
| /* === Timer 2: Ignition coil dwell === */ | |
| /* 0x170E: MOVB TCON2, #08Fh - Timer 2 control */ | |
| REG_TCON2 = 0x8F; /* CLK/32, compare mode */ | |
| /* 0x1712: MOV TM2, #00001h - Timer 2 initial count */ | |
| REG_TM2 = 0x0001; | |
| /* 0x1717: ST A, TMR2 - Timer 2 reload (0) */ | |
| REG_TMR2 = 0x0000; | |
| /* === Timer 3: Auxiliary === */ | |
| /* 0x1719: MOVB TCON3, #08Fh - Timer 3 control */ | |
| REG_TCON3 = 0x8F; | |
| init_port3_4: | |
| /* 0x171D: MOVB P3IO, #041h - Port 3 direction */ | |
| REG_P3IO = 0x41; /* Ignition output + some inputs */ | |
| /* 0x1721: MOVB P3SF, #06Fh - Port 3 special functions */ | |
| REG_P3SF = 0x6F; /* Timer outputs enabled */ | |
| /* 0x1725: MOVB P4, #0FFh - Port 4 initial state */ | |
| REG_P4 = 0xFF; | |
| init_pwm: | |
| /* 0x1729: L A, #0FF00h - PWM initial value (off) */ | |
| uint16_t pwmInit = 0xFF00; | |
| /* === PWM 0: IACV duty cycle === */ | |
| /* 0x172C: MOVB PWCON0, #02Eh - PWM 0 control */ | |
| REG_PWCON0 = 0x2E; | |
| /* 0x1730: ST A, PWMC0 - PWM 0 counter */ | |
| REG_PWMC0 = pwmInit; | |
| /* 0x1732: ST A, PWMR0 - PWM 0 reload */ | |
| REG_PWMR0 = pwmInit; /* Initially off (max duty = no air) */ | |
| /* === PWM 1: Auxiliary === */ | |
| /* 0x1734: MOVB PWCON1, #06Eh - PWM 1 control */ | |
| REG_PWCON1 = 0x6E; | |
| /* 0x1738: ST A, PWMC1 - PWM 1 counter */ | |
| REG_PWMC1 = pwmInit; | |
| /* 0x173A: ST A, PWMR1 - PWM 1 reload */ | |
| REG_PWMR1 = pwmInit; | |
| init_port4: | |
| /* 0x173C: MOVB P4IO, #00Dh - Port 4 direction */ | |
| REG_P4IO = 0x0D; | |
| /* 0x1740: MOVB P4SF, #0BCh - Port 4 special functions */ | |
| REG_P4SF = 0xBC; | |
| enable_timers: | |
| /* 0x1744: SB TCON1.4 - Enable Timer 1 */ | |
| REG_TCON1 |= 0x10; | |
| /* 0x174A: SB TCON2.4 - Enable Timer 2 */ | |
| REG_TCON2 |= 0x10; | |
| /* 0x174D: CLR IRQ - Clear all pending interrupts */ | |
| REG_IRQ = 0x0000; | |
| wait_for_power_stable: | |
| /* | |
| * Wait loop to ensure power supply is stable before | |
| * continuing. Checks power pin repeatedly. | |
| */ | |
| /* 0x1750-0x177C: Power stability check loop */ | |
| uint8_t waitCounter; | |
| for (int i = 0; i < 4; i++) { | |
| /* Wait for power stable high */ | |
| waitCounter = 0x60; | |
| while (waitCounter > 0) { | |
| waitCounter--; | |
| if (!(REG_P4 & 0x02)) break; /* Power dipped */ | |
| } | |
| if (waitCounter == 0) { | |
| /* Power not stable - error */ | |
| g_errorCodeReboot = 0x4C; /* Power error code */ | |
| asm("BRK"); /* Trigger reset */ | |
| } | |
| } | |
| test_ram: | |
| /* 0x178F-0x17A7: RAM test loop */ | |
| /* Tests RAM by writing patterns and reading back */ | |
| uint16_t testAddr; | |
| if (REG_PSW & 0x10) { | |
| testAddr = 0x0269; | |
| } else { | |
| testAddr = 0x027F; | |
| } | |
| while (testAddr >= 0x0086) { | |
| uint8_t testVal = 0x55; | |
| *(volatile uint8_t*)testAddr = testVal; | |
| if (*(volatile uint8_t*)testAddr != testVal) { | |
| g_errorCodeReboot = 0x42; /* RAM error */ | |
| asm("BRK"); | |
| } | |
| testVal = 0xAA; /* Shifted pattern */ | |
| *(volatile uint8_t*)testAddr = testVal; | |
| if (*(volatile uint8_t*)testAddr != testVal) { | |
| g_errorCodeReboot = 0x42; /* RAM error */ | |
| asm("BRK"); | |
| } | |
| *(volatile uint8_t*)testAddr = 0; /* Clear */ | |
| testAddr--; | |
| } | |
| restore_state: | |
| /* 0x17A9-0x17B6: Restore preserved state from registers */ | |
| f_flags_FD = configBit; | |
| g_unused_E9 = lifeInit; | |
| g_errorCodeReboot = counter2; | |
| init_adc: | |
| /* 0x17B7: CLR A - Clear accumulator */ | |
| /* 0x17B8: ST A, IE - Disable interrupts for ADC init */ | |
| REG_IE = 0x0000; | |
| /* 0x17BA: CLR DP - Clear data pointer */ | |
| REG_DP = 0x0000; | |
| /* Delay loop for ADC stabilization */ | |
| /* 0x17BC-0x17C1: Short delay */ | |
| for (int i = 0; i < 256; i++) { | |
| for (int j = 0; j < 17; j++) { | |
| /* Busy wait */ | |
| } | |
| } | |
| /* 0x17C3: CLRB ADSEL - Select ADC channel 0 */ | |
| REG_ADSEL = 0x00; | |
| /* 0x17C6: MOVB ADSCAN, #010h - Enable ADC scan mode */ | |
| REG_ADSCAN = 0x10; | |
| /* 0x17CA: MOVB 0EBh, #001h - Initialize life counter */ | |
| g_lifeCounter = 0x01; | |
| wait_for_adc: | |
| /* 0x17CE-0x17DC: Wait for ADC to complete first scan */ | |
| REG_IRQH &= ~0x10; /* Clear A/D complete flag */ | |
| /* Wait for conversion complete */ | |
| while (!(REG_IRQH & 0x10)) { | |
| /* Check injector pins while waiting */ | |
| if ((REG_P2 & 0xE0) != 0) { | |
| continue; /* Injector activity - keep waiting */ | |
| } | |
| break; | |
| } | |
| read_initial_sensors: | |
| /* 0x17DE-0x180E: Read initial sensor values */ | |
| /* Read PA (atmospheric pressure) sensor */ | |
| g_atmPressure = REG_ADCR4; | |
| /* Read battery/alternator voltage */ | |
| g_alternatorVolts = REG_ADCR6H; | |
| /* Read MAP sensor */ | |
| g_mapSensorRaw = REG_ADCR5; | |
| g_mapInitialHigh = g_mapSensorRaw >> 8; /* High byte for code 5 check */ | |
| /* Set initial MAP image */ | |
| g_mapImageRaw = 0xA0; /* Default ~100 kPa */ | |
| /* Read TPS */ | |
| g_tpsRaw = REG_ADCR7; | |
| /* Set default temperature values (cold engine assumed) */ | |
| g_tempProcessed = 0x3C; /* ~60°C default */ | |
| g_iatProcessed = 0x57; /* ~25°C default */ | |
| g_unused_EE = 0x94; /* Temp scratch */ | |
| /* Initialize TPS delta values */ | |
| g_tpsDelta = 0x2B; | |
| g_tpsCopy = 0x2B; | |
| g_tpsDeltaOld = 0x80; | |
| g_tpsRelated = 0x80; | |
| /* Initialize ground reference */ | |
| g_groundReference = 0x80; | |
| /* 0x1810: SB off(001Eh).7 - Set initialization flag */ | |
| f_flags_11E |= 0x80; | |
| /* Continue with sensor calibration and main loop entry... */ | |
| /* (Additional initialization code continues at 0x1813+) */ | |
| /* | |
| * After initialization, control passes to vcal_4_mainProcessing() | |
| * which contains the main ECU loop. | |
| */ | |
| } | |
| /*============================================================================ | |
| * SECTION 7: MAIN ENGINE CALCULATIONS | |
| * ============================================================================ | |
| * | |
| * Core engine control algorithms including: | |
| * - RPM calculation from timer captures | |
| * - MAP sensor processing and filtering | |
| * - Fuel map lookups and corrections | |
| * - Ignition map lookups and corrections | |
| * - Injector pulse width calculations | |
| * | |
| *============================================================================*/ | |
| /** | |
| * calc_rpmFromTimerCapture() - Calculate RPM from timer capture values | |
| * | |
| * Address: 0x0416 and related (scattered) | |
| * | |
| * RPM = 1,851,562.5 / timer_count | |
| * | |
| * Timer counts the time between CKP pulses (45° of crank rotation). | |
| * Lower count = higher RPM. | |
| */ | |
| void calc_rpmFromTimerCapture(void) | |
| { | |
| /* | |
| * RPM calculation is performed in the timer ISR. | |
| * The 32-bit division is done using the OKI 66201's MUL/DIV | |
| * instructions with the coefficient 0x1C4000 (1,851,562). | |
| * | |
| * Steps: | |
| * 1. Capture timer value at CKP edge | |
| * 2. Calculate delta from previous capture | |
| * 3. Store in rotating 4-sample buffer | |
| * 4. Average 4 samples for stability | |
| * 5. Convert to RPM for map indexing | |
| */ | |
| /* Sample: divide RPM coefficient by timer count */ | |
| uint32_t rpmCoef = 0x001C4000; /* 1,851,562.5 truncated */ | |
| uint16_t timerCount = g_rpmTimerCount; | |
| if (timerCount > 0) { | |
| uint16_t rpmValue = rpmCoef / timerCount; | |
| g_rpmIndexNonVtec = rpmValue >> 8; /* High byte for table lookup */ | |
| } | |
| } | |
| /** | |
| * calc_rpmAveraging() - Average RPM samples for stability | |
| * | |
| * Uses a 4-sample rotating buffer at 0x0206-0x020C. | |
| * Result stored in g_rpmAverage (0xBE). | |
| */ | |
| void calc_rpmAveraging(void) | |
| { | |
| /* | |
| * Average calculation: | |
| * g_rpmAverage = (sample0 + sample1 + sample2 + sample3) / 4 | |
| * | |
| * Then asymptotic smoothing: | |
| * g_rpmSmoothed = g_rpmSmoothed * 0.8 + g_rpmAverage * 0.2 | |
| * (Approximately - actual uses fixed-point math) | |
| */ | |
| uint32_t sum = g_rpmSample0 + g_rpmSample1 + g_rpmSample2 + g_rpmSample3; | |
| g_rpmAverage = sum >> 2; /* Divide by 4 */ | |
| /* Asymptotic smoothing (simplified) */ | |
| uint16_t oldSmoothed = g_rpmSmoothed; | |
| g_rpmSmoothed = (oldSmoothed * 13 + g_rpmAverage * 3) >> 4; | |
| } | |
| /** | |
| * calc_mapSensorProcessing() - Process MAP sensor with filtering | |
| * | |
| * Processes raw MAP ADC value through: | |
| * 1. Low-pass filtering | |
| * 2. Delta MAP calculation (acceleration enrichment) | |
| * 3. Conversion to map index (0-DF) | |
| */ | |
| void calc_mapSensorProcessing(void) | |
| { | |
| /* | |
| * MAP processing steps: | |
| * | |
| * 1. Read raw ADC value from ADCR5 (10-bit, 0-1023) | |
| * 2. Filter to remove noise | |
| * 3. Calculate delta from previous for accel enrichment | |
| * 4. Convert to 8-bit index (high nibble = column, low = interp) | |
| * | |
| * MAP Image format (g_mapImageFinal at 0xB5): | |
| * Bits 7-4: Column index (0-D for fuel/ign maps) | |
| * Bits 3-0: Interpolation fraction | |
| */ | |
| /* Read raw MAP */ | |
| uint16_t mapRaw = REG_ADCR5; | |
| g_mapSensorRaw = mapRaw; | |
| /* Low-pass filter: filtered = filtered * 0.9 + raw * 0.1 */ | |
| uint16_t oldFiltered = g_mapFiltered; | |
| g_mapFiltered = (oldFiltered * 9 + mapRaw) / 10; | |
| /* Calculate MAP image for table lookup */ | |
| /* Scale from ADC range to 0-DF range */ | |
| uint8_t mapImage = (g_mapFiltered >> 4) & 0xDF; | |
| g_mapImageRaw = mapImage; | |
| /* Delta MAP for acceleration enrichment */ | |
| int16_t mapDelta = mapRaw - oldFiltered; | |
| g_mapDeltaCalc = (mapDelta > 0) ? (mapDelta >> 4) : 0; | |
| /* Apply delta correction */ | |
| g_mapImageFinal = mapImage + g_mapDeltaCalc; | |
| if (g_mapImageFinal > 0xDF) | |
| g_mapImageFinal = 0xDF; | |
| } | |
| /** | |
| * calc_fuelFromMaps() - Look up fuel value from 3D maps | |
| * | |
| * Address: 0x08B3 and related | |
| * | |
| * Uses RPM (row) and MAP (column) to look up base fuel value. | |
| * Selects VTEC or non-VTEC map based on VTEC state. | |
| */ | |
| void calc_fuelFromMaps(void) | |
| { | |
| /* | |
| * 3D fuel map lookup: | |
| * | |
| * Row selection: based on g_rpmIndexNonVtec or g_rpmIndexVtec | |
| * Column selection: based on g_mapImageFinal high nibble | |
| * Interpolation: between 4 cells using low nibbles | |
| * | |
| * Maps are located in ROM: | |
| * Non-VTEC fuel: 0x4000-0x4FFF typically | |
| * VTEC fuel: 0x5000-0x5FFF typically | |
| */ | |
| /* Determine which map to use */ | |
| uint16_t fuelValue; | |
| if (f_vtecFlags & 0x80) { | |
| /* VTEC is ON - use VTEC map */ | |
| fuelValue = vcal_5_fuelMapLookup(); /* VCAL 5 */ | |
| g_fuelMapVtec = fuelValue; | |
| } else { | |
| /* VTEC is OFF - use non-VTEC map */ | |
| fuelValue = vcal_5_fuelMapLookup(); | |
| g_fuelMapNonVtec = fuelValue; | |
| } | |
| /* Apply base corrections (ECT, IAT) */ | |
| /* Formula: main_fuel = map_value * correction_factor / 65536 */ | |
| uint32_t corrected = (uint32_t)fuelValue * g_fuelScaler; | |
| g_fuelMainFinal = corrected >> 16; | |
| } | |
| /** | |
| * calc_ignitionFromMaps() - Look up ignition from 3D maps | |
| * | |
| * Address: 0x07xx and related (via VCAL 6/7) | |
| * | |
| * Similar to fuel, uses RPM and MAP to determine base timing. | |
| */ | |
| void calc_ignitionFromMaps(void) | |
| { | |
| /* | |
| * Ignition map lookup: | |
| * | |
| * Returns timing in degrees BTDC (Before Top Dead Center) | |
| * Value range: approximately 0-60 degrees | |
| * | |
| * Maps located in ROM: | |
| * Non-VTEC ignition: 0x6000-0x6FFF typically | |
| * VTEC ignition: 0x7000-0x7FFF typically | |
| */ | |
| uint8_t ignValue; | |
| if (f_vtecFlags & 0x80) { | |
| /* VTEC ON - use VTEC ignition map */ | |
| ignValue = vcal_7_ignMapLookupVtec(); /* VCAL 7 */ | |
| } else { | |
| /* VTEC OFF - use non-VTEC map */ | |
| ignValue = vcal_6_ignMapLookup(); /* VCAL 6 */ | |
| } | |
| g_ignitionMapRaw = ignValue; | |
| /* Apply corrections: ECT, knock, idle, etc. */ | |
| int8_t finalIgn = ignValue; | |
| finalIgn += g_ignitionEctTrim; /* ECT correction */ | |
| finalIgn -= g_knockRetard; /* Knock retard (subtract) */ | |
| finalIgn += g_ignitionBrake; /* Brake switch correction */ | |
| finalIgn += g_ignitionIdleAdj; /* Idle connector adjustment */ | |
| /* Clamp to valid range */ | |
| if (finalIgn < 0) finalIgn = 0; | |
| if (finalIgn > 60) finalIgn = 60; | |
| g_ignitionFinal = finalIgn; | |
| g_ignitionInverse = (~finalIgn) + 1; /* 2's complement for timer calc */ | |
| } | |
| /** | |
| * calc_injectorPulseWidth() - Calculate final injector pulse width | |
| * | |
| * Address: 0x0270 area | |
| * | |
| * Combines base fuel with all corrections for final pulse width. | |
| */ | |
| void calc_injectorPulseWidth(void) | |
| { | |
| /* | |
| * Final fuel pulse width calculation: | |
| * | |
| * g_finalFuelPulse = (g_fuelMainFinal * O2_trim / 65536) * 2 + g_fuelCorrections | |
| * | |
| * Where: | |
| * g_fuelMainFinal = base fuel from map with some corrections | |
| * O2_trim = closed-loop O2 correction (0x162 or 0x164) | |
| * g_fuelCorrections = all additive corrections (battery, etc.) | |
| */ | |
| /* Get O2 trim based on cylinder bank */ | |
| uint16_t o2Trim; | |
| if (g_cylinderIndex & 0x01) { | |
| o2Trim = g_o2TrimSecondary; | |
| } else { | |
| o2Trim = g_o2TrimPrimary; | |
| } | |
| /* Clamp O2 trim to safe range */ | |
| if (o2Trim > 0xB6E0) o2Trim = 0x944E; | |
| if (o2Trim < 0x5720) o2Trim = 0x682A; | |
| /* Calculate: (main * O2) >> 15 + corrections */ | |
| uint32_t product = (uint32_t)g_fuelMainFinal * o2Trim; | |
| uint16_t scaledFuel = product >> 15; | |
| uint16_t finalPulse = scaledFuel + g_fuelCorrections; | |
| if (finalPulse < scaledFuel) finalPulse = 0xFFFF; /* Overflow check */ | |
| g_finalFuelPulse = finalPulse; | |
| } | |
| /** | |
| * update_ignitorTiming() - Update ignition coil timing | |
| * | |
| * Address: 0x2911 (label: label_2911) | |
| * | |
| * Called from Timer1 compare ISR to set up next ignition event. | |
| */ | |
| void update_ignitorTiming(void) | |
| { | |
| /* | |
| * Ignition timing update: | |
| * | |
| * Sets up Timer2 compare values to: | |
| * 1. Start coil charging (dwell) | |
| * 2. Fire the spark at the right moment | |
| * | |
| * TCON2.2 controls the ignitor output directly. | |
| * TCON2.3 is the "primer" - loaded into .2 on compare match. | |
| */ | |
| /* Calculate dwell time based on battery voltage */ | |
| uint16_t dwellTime = 0x0400; /* Base dwell ~1ms */ | |
| /* Adjust for battery voltage (lower voltage = longer dwell) */ | |
| if (g_batteryVoltage < 0x80) { | |
| dwellTime += (0x80 - g_batteryVoltage) * 4; | |
| } | |
| /* Set up Timer2 for spark event */ | |
| REG_TMR2 = REG_TM1 + g_ignitorTiming0; | |
| /* Prime the ignitor output */ | |
| if (g_crankPosition == 3) { | |
| REG_TCON2 |= 0x08; /* Set primer - coil will charge */ | |
| } | |
| } | |
| /** | |
| * update_injectorOutputs() - Update injector port outputs | |
| * | |
| * Address: 0x28ED (label: label_28ed) | |
| * | |
| * Applies the calculated injector mask to Port 2. | |
| */ | |
| void update_injectorOutputs(void) | |
| { | |
| /* | |
| * Injector output control: | |
| * | |
| * P2.0-P2.3 control injectors 1-4 | |
| * 0 = injector ON (grounded, current flowing) | |
| * 1 = injector OFF | |
| * | |
| * g_injectorOutput contains the mask: | |
| * 0x0F = all off, 0x0E = inj1 on, 0x0D = inj2 on, etc. | |
| */ | |
| /* Apply mask to port (injectors are active-low) */ | |
| REG_P2 = (REG_P2 & 0xF0) | (g_injectorOutput & 0x0F); | |
| } | |
| /** | |
| * setup_injectorTimer() - Configure Timer0 for next injector event | |
| * | |
| * Address: 0x2906 (label: label_2906) | |
| */ | |
| void setup_injectorTimer(void) | |
| { | |
| /* Enable Timer0 compare interrupt */ | |
| REG_TCON0 |= 0x10; | |
| } | |
| /** | |
| * process_rpmSignal() - Process RPM signal and set flags | |
| * | |
| * Address: 0x2995 (label: label_2995) | |
| */ | |
| void process_rpmSignal(void) | |
| { | |
| /* | |
| * RPM processing: | |
| * 1. Check if engine is running (RPM > threshold) | |
| * 2. Calculate acceleration/deceleration | |
| * 3. Update RPM-related flags | |
| */ | |
| /* Check engine running */ | |
| if (g_rpmTimerCountHi > 0x08) { | |
| f_flags_120 |= 0x40; /* Engine running flag */ | |
| } else { | |
| f_flags_120 &= ~0x40; | |
| } | |
| /* Check for acceleration */ | |
| if (g_rpmTimerCount < g_rpmHistory0) { | |
| f_flags_11E |= 0x10; /* Accelerating */ | |
| } else { | |
| f_flags_11E &= ~0x10; /* Decelerating */ | |
| } | |
| } | |
| /*============================================================================ | |
| * SECTION 8: VTEC CONTROL | |
| * ============================================================================ | |
| * | |
| * Honda VTEC (Variable Valve Timing and Lift Electronic Control) | |
| * Changes cam profile at high RPM for better power. | |
| * | |
| * Address: 0x13xx area | |
| * | |
| *============================================================================*/ | |
| /** | |
| * check_vtecConditions() - Check if VTEC should engage/disengage | |
| * | |
| * Conditions for VTEC engagement: | |
| * 1. RPM above threshold (typically ~5000) | |
| * 2. Vehicle speed above threshold (~20 mph) | |
| * 3. Oil pressure sufficient (for solenoid) | |
| * 4. No relevant error codes | |
| * 5. Engine fully warmed up | |
| */ | |
| void check_vtecConditions(void) | |
| { | |
| /* | |
| * VTEC flag byte (0x129) bit definitions: | |
| * .3 = Speed threshold met | |
| * .4 = RPM threshold 1 met | |
| * .5 = RPM threshold 2 met | |
| * .6 = All conditions primed | |
| * .7 = VTEC IS ON | |
| */ | |
| /* Check speed threshold */ | |
| if (g_vehicleSpeedByte > 0x20) { | |
| f_vtecFlags |= 0x08; /* Speed OK */ | |
| } else if (g_vehicleSpeedByte < 0x18) { | |
| f_vtecFlags &= ~0x08; /* Speed too low - hysteresis */ | |
| } | |
| /* Check RPM threshold */ | |
| uint8_t rpmThreshHigh = 0xC8; /* ~5000 RPM */ | |
| uint8_t rpmThreshLow = 0xB0; /* ~4500 RPM hysteresis */ | |
| if (g_rpmIndexVtec > rpmThreshHigh) { | |
| f_vtecFlags |= 0x10; /* RPM high enough */ | |
| } else if (g_rpmIndexVtec < rpmThreshLow) { | |
| f_vtecFlags &= ~0x10; | |
| } | |
| /* Check oil pressure */ | |
| if (g_oilPressure > 0x32) { | |
| /* Oil pressure sufficient */ | |
| f_vtecFlags |= 0x20; | |
| } else { | |
| f_vtecFlags &= ~0x20; | |
| } | |
| /* Check if all conditions met */ | |
| if ((f_vtecFlags & 0x38) == 0x38) { | |
| /* All conditions met */ | |
| if (!(f_vtecFlags & 0x80)) { | |
| /* Not currently engaged - engage now */ | |
| engage_vtecSolenoid(); | |
| } | |
| } else { | |
| /* Conditions not met */ | |
| if (f_vtecFlags & 0x80) { | |
| /* Currently engaged - disengage */ | |
| disengage_vtecSolenoid(); | |
| } | |
| } | |
| } | |
| /** | |
| * engage_vtecSolenoid() - Activate VTEC solenoid | |
| */ | |
| void engage_vtecSolenoid(void) | |
| { | |
| /* Set VTEC output (P1.1 = 1) */ | |
| REG_P1 |= 0x02; | |
| /* Set VTEC engaged flag */ | |
| f_vtecFlags |= 0x80; | |
| /* Start engage timer */ | |
| c_vtecEngageTimer = 0x14; /* ~200ms verification delay */ | |
| } | |
| /** | |
| * disengage_vtecSolenoid() - Deactivate VTEC solenoid | |
| */ | |
| void disengage_vtecSolenoid(void) | |
| { | |
| /* Clear VTEC output (P1.1 = 0) */ | |
| REG_P1 &= ~0x02; | |
| /* Clear VTEC engaged flag */ | |
| f_vtecFlags &= ~0x80; | |
| /* Clear engage timer */ | |
| c_vtecEngageTimer = 0; | |
| } | |
| /*============================================================================ | |
| * SECTION 9: O2 SENSOR CLOSED LOOP | |
| * ============================================================================ | |
| * | |
| * Closed-loop fuel control using oxygen sensor feedback. | |
| * Adjusts fuel trim to maintain stoichiometric air-fuel ratio. | |
| * | |
| * Address: 0x25xx area | |
| * | |
| *============================================================================*/ | |
| /** | |
| * process_o2Feedback() - Process O2 sensor for closed-loop control | |
| * | |
| * Primary O2 sensor at 0x162, Secondary at 0x164. | |
| * Lean = sensor value < 0x1F | |
| * Rich = sensor value > 0x1F | |
| */ | |
| void process_o2Feedback(void) | |
| { | |
| /* | |
| * O2 closed-loop operation: | |
| * | |
| * If sensor reads LEAN (< 0x1F): | |
| * Increase fuel trim (richer) | |
| * If sensor reads RICH (> 0x1F): | |
| * Decrease fuel trim (leaner) | |
| * | |
| * Uses integrator for smooth corrections. | |
| * Rate limited to prevent oscillation. | |
| */ | |
| /* Check if closed-loop conditions met */ | |
| if (g_openClosedLoop == 0) { | |
| /* Open loop - no O2 correction */ | |
| g_o2TrimPrimary = 0x8000; /* Neutral (1.0x) */ | |
| g_o2TrimSecondary = 0x8000; | |
| return; | |
| } | |
| /* Process primary O2 sensor */ | |
| int16_t o2Reading = g_o2Sensor1_raw; | |
| int16_t error = 0x1F - o2Reading; /* Target is 0x1F (stoich) */ | |
| /* Integrator with limits */ | |
| int32_t newTrim = g_o2TrimPrimary + (error * 16); | |
| /* Clamp to valid range */ | |
| if (newTrim < 0x5720) newTrim = 0x5720; /* Min ~0.7x */ | |
| if (newTrim > 0xB6E0) newTrim = 0xB6E0; /* Max ~1.4x */ | |
| g_o2TrimPrimary = newTrim; | |
| /* Similar for secondary (if equipped) */ | |
| o2Reading = g_o2Sensor2_raw; | |
| error = 0x1F - o2Reading; | |
| newTrim = g_o2TrimSecondary + (error * 16); | |
| if (newTrim < 0x5720) newTrim = 0x5720; | |
| if (newTrim > 0xB6E0) newTrim = 0xB6E0; | |
| g_o2TrimSecondary = newTrim; | |
| } | |
| /** | |
| * calc_o2FuelTrim() - Calculate O2-based fuel correction | |
| * | |
| * Combines primary and secondary O2 readings into final trim. | |
| */ | |
| void calc_o2FuelTrim(void) | |
| { | |
| /* Use primary or secondary based on cylinder */ | |
| /* Bank 1 (cyl 1,3) uses primary, Bank 2 (cyl 2,4) uses secondary */ | |
| /* (This is simplified - actual may vary by configuration) */ | |
| } | |
| /*============================================================================ | |
| * SECTION 10: IDLE AIR CONTROL (IACV) | |
| * ============================================================================ | |
| * | |
| * Controls idle speed via PWM-controlled bypass air valve. | |
| * | |
| * Address: 0x1Axx area | |
| * | |
| *============================================================================*/ | |
| /** | |
| * calc_idleAirControl() - Calculate IACV duty cycle | |
| * | |
| * Uses PID-style control to maintain target idle RPM. | |
| * Target varies with coolant temperature (higher when cold). | |
| */ | |
| void calc_idleAirControl(void) | |
| { | |
| /* | |
| * IACV Control Algorithm: | |
| * | |
| * 1. Determine target idle RPM from ECT (cold = higher) | |
| * 2. Calculate error (current - target) | |
| * 3. Apply proportional + integral correction | |
| * 4. Add A/C load compensation if needed | |
| * 5. Clamp to valid duty cycle range | |
| */ | |
| /* Get target idle from ECT lookup */ | |
| uint16_t targetIdle = g_targetIdleRpm; | |
| /* Calculate RPM error */ | |
| int16_t rpmError = g_rpmTimerCount - targetIdle; | |
| g_idleRpmError = (rpmError < 0) ? -rpmError : rpmError; | |
| /* If RPM below target, increase air (lower duty = more air) */ | |
| /* If RPM above target, decrease air (higher duty = less air) */ | |
| int16_t correction; | |
| if (rpmError > 0) { | |
| /* RPM too high - need less air */ | |
| correction = rpmError >> 4; | |
| if (g_iacvDutyCycle < 0xE000) { | |
| g_iacvDutyCycle += correction; | |
| } | |
| } else { | |
| /* RPM too low - need more air */ | |
| correction = (-rpmError) >> 4; | |
| if (g_iacvDutyCycle > 0x2000) { | |
| g_iacvDutyCycle -= correction; | |
| } | |
| } | |
| /* A/C compensation - open more when A/C is on */ | |
| if (g_externalInputs & 0x40) { /* A/C switch on */ | |
| if (g_iacvDutyCycle > 0x1000) { | |
| g_iacvDutyCycle -= 0x0800; /* Add ~5% more air */ | |
| } | |
| } | |
| } | |
| /** | |
| * update_iacvDuty() - Apply IACV duty cycle to PWM | |
| * | |
| * Called from PWM timer ISR. | |
| */ | |
| void update_iacvDuty(void) | |
| { | |
| /* Copy calculated duty to output register */ | |
| REG_PWMR0 = g_iacvDutyCycle; | |
| } | |
| /*============================================================================ | |
| * SECTION 11: ERROR CODE HANDLING | |
| * ============================================================================ | |
| * | |
| * Detects sensor faults and stores diagnostic trouble codes (DTCs). | |
| * Blinks CEL light to communicate codes. | |
| * | |
| * Address: 0x2xxx area | |
| * | |
| *============================================================================*/ | |
| /** | |
| * check_sensorErrors() - Check all sensors for out-of-range values | |
| * | |
| * Sets bits in f_celDetect1/2/3 (0x12C-0x12E) when faults detected. | |
| * These are then copied to g_faultCode1/2/3 (0x130-0x132) for storage. | |
| */ | |
| void check_sensorErrors(void) | |
| { | |
| /* | |
| * Sensor range checks: | |
| * | |
| * MAP: Should be 10-100 kPa (varies with altitude/load) | |
| * TPS: Should be 0-100% (with valid idle offset) | |
| * ECT: Should be -40 to 120°C (ADC values) | |
| * IAT: Should be -40 to 80°C | |
| * O2: Should oscillate when in closed loop | |
| */ | |
| /* MAP sensor check (Code 3) */ | |
| if (g_mapSensorRaw < 0x0100 || g_mapSensorRaw > 0x0F00) { | |
| f_celDetect1 |= 0x01; /* Code 3: MAP sensor */ | |
| } else { | |
| f_celDetect1 &= ~0x01; | |
| } | |
| /* ECT sensor check (Code 6) */ | |
| if (g_coolantTemp < 0x10 || g_coolantTemp > 0xF0) { | |
| f_celDetect1 |= 0x02; /* Code 6: ECT */ | |
| } else { | |
| f_celDetect1 &= ~0x02; | |
| } | |
| /* TPS sensor check (Code 7) */ | |
| if (g_tpsValue < 0x05 || g_tpsValue > 0xFA) { | |
| f_celDetect1 |= 0x04; /* Code 7: TPS */ | |
| } else { | |
| f_celDetect1 &= ~0x04; | |
| } | |
| /* IAT sensor check (Code 10) */ | |
| if (g_intakeAirTemp < 0x10 || g_intakeAirTemp > 0xF0) { | |
| f_celDetect1 |= 0x80; /* Code 10: IAT */ | |
| } else { | |
| f_celDetect1 &= ~0x80; | |
| } | |
| /* VTEC solenoid check (Code 21) */ | |
| if (f_vtecFlags & 0x80) { /* VTEC commanded ON */ | |
| if (!(g_externalInputs & 0x04)) { /* But feedback says OFF */ | |
| f_celDetect2 |= 0x40; /* Code 21: VTEC solenoid */ | |
| } | |
| } | |
| } | |
| /** | |
| * set_faultCode() - Set a fault code in permanent storage | |
| * | |
| * Address: 0x2F1B | |
| */ | |
| void set_faultCode(uint8_t code) | |
| { | |
| uint8_t byteIndex = (code - 1) / 8; | |
| uint8_t bitMask = 1 << ((code - 1) % 8); | |
| switch (byteIndex) { | |
| case 0: g_faultCode1 |= bitMask; break; | |
| case 1: g_faultCode2 |= bitMask; break; | |
| case 2: g_faultCode3 |= bitMask; break; | |
| } | |
| } | |
| /** | |
| * blink_celLight() - Blink CEL to display stored codes | |
| * | |
| * Address: 0x20xx area | |
| * | |
| * Long blinks = tens digit | |
| * Short blinks = ones digit | |
| */ | |
| void blink_celLight(void) | |
| { | |
| /* | |
| * CEL blink pattern: | |
| * | |
| * For code 14: | |
| * 1 long blink (tens) | |
| * pause | |
| * 4 short blinks (ones) | |
| * long pause | |
| * repeat | |
| * | |
| * Codes are displayed in sequence if multiple stored. | |
| */ | |
| /* Check if any codes to display */ | |
| uint8_t allCodes = g_celBlinkCode1 | g_celBlinkCode2 | g_celBlinkCode3; | |
| if (allCodes == 0) { | |
| /* No codes - CEL off */ | |
| REG_P0 |= 0x40; /* CEL light OFF (P0.6 = 1) */ | |
| REG_P1 &= ~0x04; /* ECU LED OFF (P1.2 = 0) */ | |
| return; | |
| } | |
| /* Display current code in sequence */ | |
| uint8_t currentCode = c_celBlinkIndex; | |
| uint8_t tens = currentCode / 10; | |
| uint8_t ones = currentCode % 10; | |
| /* State machine for blink timing */ | |
| /* (Simplified - actual uses counters at 0x1CE-0x1FF) */ | |
| if (c_celBlinkCounter > 0) { | |
| c_celBlinkCounter--; | |
| /* Keep current state */ | |
| } else { | |
| /* Toggle CEL */ | |
| REG_P0 ^= 0x40; /* Toggle P0.6 */ | |
| } | |
| } | |
| /** | |
| * store_frozenFrame() - Capture freeze frame data on error | |
| */ | |
| void store_frozenFrame(void) | |
| { | |
| /* | |
| * Freeze frame captures sensor values at time of fault: | |
| * - RPM | |
| * - MAP | |
| * - TPS | |
| * - ECT | |
| * - IAT | |
| * - Vehicle speed | |
| * | |
| * (Location varies by ROM version) | |
| */ | |
| } | |
| /*============================================================================ | |
| * SECTION 12: VCAL TABLE LOOKUP FUNCTIONS | |
| * ============================================================================ | |
| * | |
| * Vector call (VCAL) routines for table lookups. | |
| * These are called via the VCAL instruction using the vector table | |
| * at 0x0028-0x0036. | |
| * | |
| *============================================================================*/ | |
| /** | |
| * vcal_0_interpolate1D() - 1D Table Lookup with Interpolation | |
| * | |
| * Address: 0x2BF9 (label: vcal_0) | |
| * | |
| * Performs linear interpolation between two table entries. | |
| * | |
| * @param input - Input value (0-255) | |
| * @param tableAddr - ROM address of lookup table | |
| * @return Interpolated result | |
| */ | |
| uint8_t vcal_0_interpolate1D(uint8_t input, uint16_t tableAddr) | |
| { | |
| /* | |
| * Table format: | |
| * Byte 0: Number of entries | |
| * Byte 1: X value 1 | |
| * Byte 2: Y value 1 | |
| * Byte 3: X value 2 | |
| * Byte 4: Y value 2 | |
| * ... | |
| * | |
| * Finds the two entries bracketing 'input' and interpolates. | |
| */ | |
| uint8_t* table = (uint8_t*)tableAddr; | |
| uint8_t numEntries = table[0]; | |
| /* Find bracketing entries */ | |
| uint8_t i; | |
| for (i = 0; i < numEntries - 1; i++) { | |
| uint8_t x1 = table[1 + i * 2]; | |
| uint8_t x2 = table[1 + (i + 1) * 2]; | |
| if (input >= x1 && input <= x2) { | |
| /* Found bracket */ | |
| uint8_t y1 = table[2 + i * 2]; | |
| uint8_t y2 = table[2 + (i + 1) * 2]; | |
| /* Linear interpolation */ | |
| int16_t dx = x2 - x1; | |
| int16_t dy = y2 - y1; | |
| int16_t offset = input - x1; | |
| return y1 + (dy * offset / dx); | |
| } | |
| } | |
| /* Out of range - return last value */ | |
| return table[numEntries * 2]; | |
| } | |
| /** | |
| * vcal_1_lookupInverted() - 1D Lookup with Inverted Result | |
| * | |
| * Address: 0x2C57 (label: vcal_1) | |
| * | |
| * Same as vcal_0 but result is inverted (255 - result). | |
| */ | |
| uint8_t vcal_1_lookupInverted(uint8_t input, uint16_t tableAddr) | |
| { | |
| return 255 - vcal_0_interpolate1D(input, tableAddr); | |
| } | |
| /** | |
| * vcal_2_clampedLookup() - 1D Lookup with Clamped Range | |
| * | |
| * Address: 0x2C33 (label: vcal_2) | |
| * | |
| * Same as vcal_0 but clamps input to valid table range first. | |
| */ | |
| uint8_t vcal_2_clampedLookup(uint8_t input, uint16_t tableAddr) | |
| { | |
| uint8_t* table = (uint8_t*)tableAddr; | |
| uint8_t minX = table[1]; | |
| uint8_t maxX = table[(table[0] - 1) * 2 + 1]; | |
| if (input < minX) input = minX; | |
| if (input > maxX) input = maxX; | |
| return vcal_0_interpolate1D(input, tableAddr); | |
| } | |
| /** | |
| * vcal_3_tripletLookup() - 1D Lookup with 3-byte entries | |
| * | |
| * Address: 0x2C45 (label: vcal_3) | |
| * | |
| * Table has 3-byte entries (X, Y_high, Y_low) for 16-bit results. | |
| */ | |
| uint8_t vcal_3_tripletLookup(uint8_t input, uint16_t tableAddr) | |
| { | |
| /* Similar to vcal_0 but with 16-bit Y values */ | |
| /* Returns high byte of interpolated 16-bit result */ | |
| return vcal_0_interpolate1D(input, tableAddr); /* Simplified */ | |
| } | |
| /** | |
| * vcal_4_mainProcessing() - Main Loop Entry Point | |
| * | |
| * Address: 0x189B (label: vcal_4) | |
| * | |
| * This is the main ECU processing loop, called after initialization. | |
| * Coordinates all sensor reading, calculations, and output updates. | |
| */ | |
| void vcal_4_mainProcessing(void) | |
| { | |
| /* | |
| * Main processing loop: | |
| * | |
| * 1. Reset watchdog timer | |
| * 2. Read ADC channels (ECT, IAT, TPS, etc.) | |
| * 3. Process sensor values | |
| * 4. Update fuel calculations | |
| * 5. Update ignition calculations | |
| * 6. Check VTEC conditions | |
| * 7. Process O2 feedback | |
| * 8. Update IACV duty | |
| * 9. Check for error codes | |
| * 10. Handle CEL blinking | |
| * 11. Handle serial datalogging | |
| * | |
| * Loop repeats indefinitely while engine running. | |
| */ | |
| while (1) { | |
| /* 1. Feed watchdog */ | |
| REG_WDT = 0x3C; | |
| /* 2. Read sensors (ADC auto-scans, values in ADCR0-7) */ | |
| g_coolantTemp = REG_ADCR0H; /* Multiplexed via IC6 */ | |
| g_intakeAirTemp = REG_ADCR0H; /* Alternates with ECT */ | |
| g_alternatorVolts = REG_ADCR6H; | |
| /* 3. Process MAP sensor */ | |
| calc_mapSensorProcessing(); | |
| /* 4. Calculate fuel */ | |
| calc_fuelFromMaps(); | |
| /* 5. Calculate ignition */ | |
| calc_ignitionFromMaps(); | |
| /* 6. Check VTEC */ | |
| check_vtecConditions(); | |
| /* 7. O2 feedback */ | |
| process_o2Feedback(); | |
| /* 8. Idle control */ | |
| calc_idleAirControl(); | |
| /* 9. Error code check */ | |
| check_sensorErrors(); | |
| /* 10. CEL blink */ | |
| if (f_flags_FE & 0x10) { | |
| blink_celLight(); | |
| } | |
| /* 11. Datalogging handled in serial RX ISR */ | |
| /* Clear periodic flags for next cycle */ | |
| f_flags_FE &= ~0x1E; /* Clear processed flags */ | |
| } | |
| } | |
| /** | |
| * vcal_5_fuelMapLookup() - 3D Fuel Map Lookup | |
| * | |
| * Address: 0x2D96 (label: vcal_5) | |
| * | |
| * Performs bilinear interpolation on fuel map. | |
| */ | |
| uint16_t vcal_5_fuelMapLookup(void) | |
| { | |
| /* | |
| * 3D map interpolation: | |
| * | |
| * 1. Use RPM index to select row | |
| * 2. Use MAP image to select column | |
| * 3. Interpolate between 4 surrounding cells | |
| * | |
| * Map format: 16x16 grid of 16-bit values | |
| */ | |
| uint8_t row = g_rpmIndexNonVtec >> 4; /* 0-15 */ | |
| uint8_t col = g_mapImageFinal >> 4; /* 0-15 */ | |
| uint8_t rowFrac = g_rpmIndexNonVtec & 0x0F; /* 0-15 */ | |
| uint8_t colFrac = g_mapImageFinal & 0x0F; /* 0-15 */ | |
| /* Get 4 corner values from map */ | |
| uint16_t* mapBase; | |
| if (f_vtecFlags & 0x80) { | |
| mapBase = (uint16_t*)0x5000; /* VTEC fuel map */ | |
| } else { | |
| mapBase = (uint16_t*)0x4000; /* Non-VTEC fuel map */ | |
| } | |
| uint16_t v00 = mapBase[row * 16 + col]; | |
| uint16_t v01 = mapBase[row * 16 + col + 1]; | |
| uint16_t v10 = mapBase[(row + 1) * 16 + col]; | |
| uint16_t v11 = mapBase[(row + 1) * 16 + col + 1]; | |
| /* Bilinear interpolation */ | |
| uint16_t v0 = v00 + ((v01 - v00) * colFrac) / 16; | |
| uint16_t v1 = v10 + ((v11 - v10) * colFrac) / 16; | |
| uint16_t result = v0 + ((v1 - v0) * rowFrac) / 16; | |
| return result; | |
| } | |
| /** | |
| * vcal_6_ignMapLookup() - 3D Ignition Map Lookup (Non-VTEC) | |
| * | |
| * Address: 0x2EB2 (label: vcal_6) | |
| */ | |
| uint16_t vcal_6_ignMapLookup(void) | |
| { | |
| /* Similar to vcal_5 but for ignition timing */ | |
| /* Returns timing value in degrees BTDC */ | |
| return 0; /* Placeholder */ | |
| } | |
| /** | |
| * vcal_7_ignMapLookupVtec() - 3D Ignition Map Lookup (VTEC) | |
| * | |
| * Address: 0x2EB4 (label: vcal_7) | |
| */ | |
| uint16_t vcal_7_ignMapLookupVtec(void) | |
| { | |
| /* VTEC ignition map - more aggressive timing */ | |
| return 0; /* Placeholder */ | |
| } | |
| /*============================================================================ | |
| * SECTION 13: DATALOGGING MODIFICATIONS | |
| * ============================================================================ | |
| * | |
| * This section contains the datalogging code modifications | |
| * that enable real-time tuning via serial communication. | |
| * | |
| * Original code modified by the tuning community. | |
| * | |
| *============================================================================*/ | |
| /** | |
| * Datalogging Overview: | |
| * | |
| * The datalogging modification allows tuning software to: | |
| * 1. Request any RAM location value | |
| * 2. Log fuel and ignition map indices for real-time display | |
| * | |
| * Protocol: | |
| * - Software sends a byte (RAM address to read) | |
| * - ECU responds with the value at that address | |
| * | |
| * Serial configuration: 9600 baud, 8N1 | |
| * | |
| * Key modifications from stock: | |
| * 1. Serial port reconfigured for 9600 baud (was diagnostic rate) | |
| * 2. isr_serialReceive modified to send RAM values | |
| * 3. Stack relocated to 0x025B for buffer space | |
| * 4. storerow() added to save map row indices | |
| */ | |
| /** | |
| * storerow_forLogging() - Store fuel/ignition row indices | |
| * | |
| * Custom function added for datalogging. | |
| * Saves the current fuel and ignition map row indices | |
| * so tuning software can display which cells are active. | |
| */ | |
| void storerow_forLogging(void) | |
| { | |
| /* | |
| * Called during fuel/ignition calculation. | |
| * Stores row indices at known RAM locations so | |
| * datalogging can read them. | |
| */ | |
| g_fuelRowIndex = g_rpmIndexNonVtec; /* 0x1D8 */ | |
| g_ignRowIndex = g_rpmIndexVtec; /* 0x1D9 */ | |
| } | |
| /* | |
| * Logging Table (ROM address 0x7F10) | |
| * | |
| * This table defines which RAM addresses are included in | |
| * high-speed data logging. Each entry is a 16-bit address. | |
| */ | |
| #define LOGGING_TABLE_ADDR 0x7F10 | |
| const uint16_t logging_table[] = { | |
| /* Address 0x7F10 - Logging entries */ | |
| 0x00BA, /* g_rpmTimerCount - RPM */ | |
| 0x00B5, /* g_mapImageFinal - MAP */ | |
| 0x00AB, /* g_tpsValue - TPS */ | |
| 0x0098, /* g_coolantTemp - ECT */ | |
| 0x0099, /* g_intakeAirTemp - IAT */ | |
| 0x00D6, /* g_finalFuelPulse - Fuel pulse width */ | |
| 0x0134, /* g_ignitionFinal - Ignition timing */ | |
| 0x0162, /* g_o2TrimPrimary - O2 correction */ | |
| 0x0129, /* f_vtecFlags - VTEC status */ | |
| 0x0130, /* g_faultCode1 - Error codes */ | |
| 0x01D8, /* g_fuelRowIndex - Fuel map row */ | |
| 0x01D9, /* g_ignRowIndex - Ignition map row */ | |
| 0x0202, /* g_iacvDutyCycle - IACV duty */ | |
| 0x00CB, /* g_vehicleSpeedByte - Speed */ | |
| 0x00A5, /* g_alternatorVolts - Battery */ | |
| 0xFFFF /* End marker */ | |
| }; | |
| /*============================================================================ | |
| * SECTION 14: DATA TABLE LOCATIONS | |
| * ============================================================================ | |
| * | |
| * ROM address map for calibration data tables. | |
| * | |
| * Note: Addresses are for Euro PW0 ROM. Other versions may differ. | |
| * | |
| *============================================================================*/ | |
| /* | |
| * EURO PW0 ROM DATA MAP: | |
| * | |
| * 0x0000-0x0037: Interrupt vector table | |
| * 0x0038-0x0066: Compressed code / constants | |
| * 0x0067-0x189A: Main executable code | |
| * 0x189B-0x2EFF: VCAL functions and subroutines | |
| * 0x2F00-0x36FF: Lookup tables (1D) | |
| * 0x3700-0x3FFF: Stock calibration data | |
| * 0x4000-0x4FFF: Non-VTEC fuel maps | |
| * 0x5000-0x5FFF: VTEC fuel maps | |
| * 0x6000-0x6FFF: Non-VTEC ignition maps | |
| * 0x7000-0x7EFF: VTEC ignition maps | |
| * 0x7F00-0x7FFF: Option bytes and logging table | |
| */ | |
| /* | |
| * KEY TABLE ADDRESSES (Euro PW0): | |
| * | |
| * Sensor Calibration: | |
| * 0x38C9: RPM scaling table | |
| * 0x38D7: Battery voltage table | |
| * 0x3907: ECT error fallback | |
| * 0x39BD: Temperature table | |
| * 0x39E1: Idle RPM vs ECT | |
| * 0x37CC: Fuel correction table | |
| * 0x37D7: Voltage correction table | |
| * 0x37E3: Battery voltage VCAL table | |
| * | |
| * Fuel Maps: | |
| * 0x4000: Non-VTEC low RPM base fuel | |
| * 0x4100: Non-VTEC mid RPM base fuel | |
| * 0x4200: Non-VTEC high RPM base fuel | |
| * 0x5000: VTEC fuel maps (same structure) | |
| * | |
| * Ignition Maps: | |
| * 0x6000: Non-VTEC ignition timing | |
| * 0x7000: VTEC ignition timing | |
| * | |
| * VTEC Settings: | |
| * 0x7F00: VTEC engage RPM threshold | |
| * 0x7F02: VTEC disengage RPM threshold | |
| * 0x7F04: VTEC speed threshold | |
| * | |
| * Rev Limiter: | |
| * 0x7F06: Rev limit RPM value | |
| * 0x7F08: Rev limit fuel cut value | |
| * | |
| * Option Bytes: | |
| * 0x7FF8: Delta TPS fuel scale | |
| * 0x7FF9: Speed limiter enable | |
| * 0x7FFA: A/C idle-up amount | |
| * 0x7FFB: Cold start enrichment | |
| * | |
| * Datalogging (Modified): | |
| * 0x7F10: Logging table (RAM addresses to log) | |
| * 0x3262-0x32FA: Datalogging code patch area | |
| */ | |
| /*============================================================================ | |
| * HELPER FUNCTIONS / MACROS | |
| * ============================================================================*/ | |
| /* Common patterns converted to inline functions */ | |
| /** | |
| * feed_watchdog() - Reset watchdog timer | |
| * Must be called periodically or ECU resets. | |
| */ | |
| static inline void feed_watchdog(void) | |
| { | |
| REG_WDT = 0x3C; | |
| } | |
| /** | |
| * enable_interrupts() - Enable all configured interrupts | |
| */ | |
| static inline void enable_interrupts(void) | |
| { | |
| REG_IE = g_ieNormal; | |
| } | |
| /** | |
| * disable_interrupts() - Disable all interrupts | |
| */ | |
| static inline void disable_interrupts(void) | |
| { | |
| REG_IE = 0x0000; | |
| } | |
| /** | |
| * set_cel_on() - Turn on dashboard CEL light | |
| */ | |
| static inline void set_cel_on(void) | |
| { | |
| REG_P0 &= ~0x40; /* P0.6 = 0 (active low) */ | |
| } | |
| /** | |
| * set_cel_off() - Turn off dashboard CEL light | |
| */ | |
| static inline void set_cel_off(void) | |
| { | |
| REG_P0 |= 0x40; /* P0.6 = 1 */ | |
| } | |
| /** | |
| * all_injectors_off() - Turn off all fuel injectors | |
| */ | |
| static inline void all_injectors_off(void) | |
| { | |
| REG_P2 |= 0x0F; /* P2.0-3 = 1 (injectors off) */ | |
| } | |
| /** | |
| * calculate_rpm() - Get RPM from timer count | |
| * @timer_count: Timer ticks for 45° rotation | |
| * @return: RPM value | |
| */ | |
| static inline uint16_t calculate_rpm(uint16_t timer_count) | |
| { | |
| if (timer_count == 0) return 0; | |
| return (uint16_t)(1851562UL / timer_count); | |
| } | |
| /*============================================================================ | |
| * END OF PSEUDO-C CONVERSION | |
| * ============================================================================ | |
| * | |
| * This file represents a complete pseudo-C conversion of the | |
| * EuroPw0Datalogging.asm file (7079 lines of OKI 66201 assembly). | |
| * | |
| * Key sections converted: | |
| * 1. Hardware register definitions (OKI 66201 specific) | |
| * 2. RAM variable map with meaningful names | |
| * 3. Interrupt vector table | |
| * 4. All interrupt service routines | |
| * 5. System initialization | |
| * 6. Main engine calculations (RPM, MAP, fuel, ignition) | |
| * 7. VTEC control logic | |
| * 8. O2 sensor closed-loop | |
| * 9. Idle air control (IACV) | |
| * 10. Error code handling and CEL blinking | |
| * 11. VCAL table lookup functions | |
| * 12. Datalogging modifications | |
| * 13. Data table locations | |
| * | |
| * For detailed assembly-level understanding, refer to: | |
| * - Original EuroPw0Datalogging.asm file | |
| * - OKI 66201 Instruction Manual | |
| * - pw0ram.txt RAM documentation | |
| * | |
| *============================================================================*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment