-
Notifications
You must be signed in to change notification settings - Fork 2
/
standard_E932_E931_source.asm
14304 lines (12908 loc) · 962 KB
/
standard_E932_E931_source.asm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
;****************************************************************************************************
;* GENERAL NOTES
;* =============
;* Project started with the help of dsm-ecu Yahoo group, thanks for the great info.
;* Most disassembly comments in this file by Christian, [email protected].
;*
;* CPU
;* ----
;* The microcomputer chip used in the 1G DSM ECU seems to be a custom application
;* built around the 6801 architecture, Check the 6801, 6803, 6301, 68HC11 at web sites
;* such as alldatasheet.com, etc.
;*
;* CPU clock frequency is assumed to be 2MHz, i.e. the instructions cycle time is 0.5us.
;*
;* Assembly binary verifications:
;* ------------------------------
;*
;* The 2 binaries produced without any customization ("enableCustom" definition is
;* commented-out) have been verified to be identical to the E931 and E932 eprom
;* images at hand.
;*
;* To check the validity of symbolic substitution, the entire code section and tables
;* was offset by $0200 using "codeOffset" and the corresponding binary was tested on
;* my car (E932) without any problems for weeks. Additional tests were conducted by
;* writing inline code in several part of the code and no adverse effect was ever noted.
;*
;* To check the validity of symbolic substitution for ram addresses, every ram location
;* starting at $0057 was offset by 1 (i.e. temp1 was at memory address $58 instead of
;* $57, etc) and the corresponding binary was tested on my car (E932) without any problems
;* during car startup and engine revving. No additional test performed.
;*
;* This means that the code can be modified inline and in most cases, ram memories can
;* be moved around by changing the label addresses. Note however that some groups of
;* ram memories have to be moved in blocks because the code assumes they are contiguous.
;* e.g. the temp1 to temp9 variables, the inj1_offT, inj3_offT, inj4_offT and inj2_offT
;* variables, etc.
;*
;* Ram memory:
;* -----------
;* Memory from $0040 to $01bf is backed-up by battery, meaning it is preserved when the
;* ECU is powered-off as long as battery power is supplied. However, memory from $0057 to
;* $0190 is cleared to 0 by the code every time the ECU is powered-on. That can be however
;* changed by modifying the code... Battery backup was checked by disabling memory reset using
;* the "noRamReset" and then check ram memory at $018f to see if it gets preserved after power
;* off/on cycle, and it did. During the test, $018f was used as a distance counter using
;* the reed switch.
;*
;* Comments:
;* --------
;* Some comments use variable names quite loosly. For instance, multi-byte variables
;* such as [airCnt0:airCnt1:airCnt2] might be refered to as only airCnt0. airCnt0
;* might therefore refer to the single byte airCnt0, to the 16 bit value
;* [airCnt0:airCnt1] or to the 24 bit complete variable, depending on the context.
;*
;* Comments were added incrementally as my knowledge of code and variables
;* increased. As new knowledge was learned, old comments were updated or corrected
;* as much as possible but not necessarily all of them, so beware... In the end, the
;* code is the only truth... Some small areas of the code were also never completly
;* understood as a general understanding was reached and I did not care to go further
;* e.g. airflow sensor active filter reset
;*
;* Opcodes:
;* --------
;* -cmpd: cmpd1 is used for some addressing modes instead of cmpd since
;* TASM does not support unusual mitsubishi ECU cmpd opcodes..
;*
;* -brclr: branch if ALL the given bits are clear
;*
;* -brset: branch if ANY of the given bits are set (as opposed to usual
;* implementation of ALL bits set...)
;*
;* -The addressing mode using Y indexing also implicitly
;* modifies the y register. It seems that y is increased
;* by 1 or 2 depending whether the instruction is a 8 bit
;* or 16 bits operation... The following cases are confirmed
;*
;* cmpa $00,y -> y = y + 1
;* cmpb $00,y -> y = y + 1
;* ldaa $00,y -> y = y + 1
;* suba $00,y -> y = y + 1
;* ldx $00,y -> y = y + 2
;* std $00,y -> y = y + 2
;*
;*
;* Telemark assembler:
;* --------------------
;* This assembler does not provide warning messages when code assembles to
;* the same memory space, e.g. you insert code in the middle of the file
;* which result in the rest of the code to be offset by N bytes. This
;* results in the interrupt vector table to be overwritten. No warning
;* is given. The only way to know about it is to manually check the listing
;* file produced by the assembler. Check that the buffer space between
;* sections is all "$ff". Check that there is no code spilage over .org
;* statements. Check that the address space does not exceed $ffff. Use the
;* "codeOffset" at the beginnng of the file to correct the problem.
;*
;*
;* Fuel injector and coil power transistor control
;* ------------------------------------------------
;* Although the 4 fuel injectors and the 2 coil power transistors are mapped to
;* regular ports (port1, port2 and port5) which can be read to know the current
;* state of these outputs, they are also mapped in hardware to output compare
;* registers in order to activate or deactivate them at specific time instants.
;* Writing to the ports might therefore not work unless the output compare
;* configuration registers are changed to disable harware control of these
;* outputs. This might not be possible unless an "output enable" bit exists,
;* which I haven't found at this point...
;* Another way to activate or deactivate them would be to use the output
;* compare registers (as currently done by the ECU code) and provoke an
;* immediat output change.
;*
;* Here is my current understanding of how injector scheduling works, not
;* everything is clear to me so don't take this as gospel...:
;* The output compare registers for the fuel injectors seem to be at least double
;* buffered and maybe triple buffered (see schedInjSim routine). That means that
;* up to 3 different output compare values can be written to t1_outCmpWr and t2_outCmpWr
;* to activate or deactivate the injectors at those time instants. Each time a value
;* is written to t1_outCmpWr or t2_outCmpWr, the corresponding injector state
;* is also internally stored. That means that to activate injector #1 at time X,
;* you would first reset bit 0 of t1_csr, corresponding to injector #1 and then
;* write X to t1_outCmpWr. You could then immediately schedule the deactivation
;* of injector #1 by setting bit 0 of t1_csr to 1 and then write the deactivation
;* time to t1_outCmpWr. When one of the output compare register stored value matches
;* the clock at t1t2_clk, the injector is activated/deactivated and the corresponding
;* interrupt routine is called (if the interrupt mask is clear...) at outCompInt1 or
;* outCompInt2.
;*
;* Here is my current understanding of how the coil power transistor scheduling
;* works, not everything is clear to me so don't take this as gospel...: t3_outCmpWr
;* is the output compare register used to activate or deactivate the coil power
;* transistors (energize the coil and provoke ignition at the specified time instants)
;* To energize the coil for cylinder 1 and 4 at time X you would write X to t3_outCmpWr
;* and reset(0) bit 2 of t3_csr0. At time X, t3_csr0.2 would be loaded into port5.1
;* which would energize the coil. t3_csr0.2 should not be changed until that happens.
;* In the code, most of the time 2 successive values (the same one) are written to t3_outCmpWr
;* but there are some instances where only 1 value is written. My impression is that
;* the first value serves to activate/deactivate the coil power transistor at the
;* specified instant while the second one only serves to generate an interrupt
;* in order to call the outCompInt3 routine. Hence when only the coil need
;* to be activated/deactivated without calling outCompInt3, you would only write
;* one value. If in addition you want to have outCompInt3 called when the coil
;* is energized/ignited, you would write two successive values (corresponding to the
;* same time...). This is all speculation of course... As for the 2 clocks at t3_clock1
;* and t3_clock1, I assume they are connected to the same internal clock at 250KHz
;* but might be input capture registers latched when one of the two output compare
;* at t3_outCmpWr is triggered??????? Again speculation, this is the part of the code
;* I understand the least...
;*
;*
;* Timing diagram
;* --------------
;*
;* -4 cylinders = 2 rotations = 2 * 360degrees = 720 degrees
;*
;* -For sequential injection, fuel injection starts on the cas falling edge
;* i.e. cylinder #1 injection starts at -5 BTDC of #3 TDC
;*
;* -Simultaneous injection of all 4 injectors is performed when starting to
;* crank or starting a cold engine or during acceleration, check the tech manual
;* and code for more details. Simultaneous injection starts on the 5deg BTDC
;* cas signal except in the case of acceleration where it starts when an
;* injector is deactivated and no other injector is active (i.e. at the
;* beginning of the time period where no injector is active)
;*
;* -Coil energization is usually scheduled (the energization time is loaded into
;* the output compare register, energization will occur at the specified time)
;* from the cas rising edge. Coil ignition can be scheduled when energization
;* occurs (output compare interrupt) or on the cas falling edge depending on
;* the desired timing. Note however that coil energization can also be scheduled
;* when ignition occurs on the preceeding cylinder. This would correspond to
;* scheduling ignition before the cas rising edge (at high rpm I assume). Coil
;* energization can also be scheduled on the cas falling edge when the desired
;* timing is high (e.g. 10deg ATDC). As this shows, there are several combinations
;* and the complexity of the code to handle the coil reflects that fact.
;*
;*
;* No 1 TDC No 3 TDC No 4 TDC No 2 TDC
;* : : : :
;* ___________ _____
;* TDC sensor | | | |
;* signal | : | : | | : :
;* ____|___________|_______________________|_____|________________________
;* degrees 85 55 85 15
;* (BTDC/ATDC) : : : :
;* ______ ______ ______ ______
;* CAS sensor | | | | | | | |
;* signal | | : | | : | | : | | :
;* _____|______|__________|______|__________|______|__________|______|____
;* degrees 75 5 : 75 5 : 75 5 : 75 5 :
;* (BTDC) : : : :
;*
;* : : : :
;* No 1 cyl. compression : combustion : exhaust : intake : compression
;* No 3 cyl. intake : compression : combustion : exhaust : intake
;* No 4 cyl. exhaust : intake : compression : combustion : exhaust
;* No 2 cyl. combustion : exhaust : intake : compression : combustion
;*
;*
;*
;* Airflow calculations dependencies, more details in code
;* --------------------------------------------------------
;*
;* masProc: airflow sensor interrupt, increases [airCntNew0:airCntNew1]
;* | by airQuantum for every airflow sensor pulse received
;* |
;* |
;* |
;* |--> [airCntNew0:airCntNew1]: Increased by airQuantum for every airflow sensor pulse
;* | Reset and used as input to [airCnt0:airCnt1:airCnt2]
;* | on every cas falling edge, i.e. air is counted twice
;* | per rotation, once for every cylinder cycle... It can
;* | therefore be seen as the air count per cylinder.
;* |
;* |--> [airCnt0:airCnt1:airCnt2]: Filtered version of 256*[airCntNew0:airCntNew1]
;* | exponential averaging is used.
;* |
;* |
;* |
;* |--> mafraw16: 16 bit airflow sensor pulse frequency (mafraw16/10.24)Hz
;* | | mafraw16 = 8205*[airCnt0:airCnt1]/Tcas
;* | |
;* | |
;* | |--> mafraw: 8 bit airflow sensor pulse frequency (6.25*mafraw)Hz
;* | mafraw: = mafraw16/64
;* |
;* |
;* |
;* |--> airVol16: Equals [airCnt0:airCnt1] * masScalar/65536
;* | |
;* | |
;* | |
;* | |--> airVol : Equals airVol16/2
;* | |--> airVolT : Equals airVol16/2 * iatCompFact/128
;* | |--> airVolTB : Equals airVol16/2 * iatCompFact/128 * baroFact/128
;* | |--> airVolB : Equals airVol16/2 * baroFact/128
;* |
;* |
;* |--> injPw: Injector pulse width in "normal" operation,
;* injPw = [airCnt0:airCnt1] * injFactor/256 + other corrections
;*
;*
;*
;* Discussion on MAS compensation factors
;* ---------------------------------------
;*
;* Total airflow sensor compensation is made-up of:
;*
;* totMasComp(freq,iat,baro) = masComp + t_masComp(freq) + t_masLin(freq,iat,baro)
;*
;* where maxComp is a fixed offset ($64 for 1G and $40 for 2G) and t_masComp and t_masLin
;* are table values interpolated from frequency, intake air temperature and barometric
;* pressure. t_masComp(freq) is basically compensation for the airflow sensor charcteristic
;* curve as a function of frequency (to linearize the number of pulse per sec vs. the volume
;* of air passing through the sensor) while t_masLin(freq,iat,baro) is a smaller factor
;* probably compensating for temperature drift (electronic) and airflow characteristic
;* change as a function of air density???
;*
;* Assuming the following:
;*
;* -injComp = 100% (for 260cc injectors at 36psi)
;* -workFtrim = 100%
;* -o2FuelAdj = 100%
;* -iatCompFact = 100% (at 25.6degC)
;* -baroFact = 100% (~1 bar)
;* -openLoopEnr = 100%
;* -coldTempEnr = 100%
;* -enrWarmup = 0%
;*
;*
;* Then the injector pulswidth is calculated by the ECU as (excluding deadtime)
;*
;* injPw(usec/cylinder) = numPulsePerCasInterrupts *$9c * totMasComp * 16/256
;* = numPulsePerCasInterrupts * totMasComp * 9.75
;*
;* If we also assume a 14.7 air to fuel ratio, Dair=1.18 air density (g/litre) at 25degC,
;* Dgas=0.775 fuel density (g/cc) then we would need 23900 usec of injection per
;* litre of air using the same 260cc at 36psi, working that factor into the equation, we
;* get
;*
;* injPw(usec/cylinder) = numPulsePerCasInterrupts * totMasComp * 9.75
;* = numPulsePerCasInterrupts * totMasComp/2452 * 2452 * 9.75
;* = numPulsePerCasInterrupts * totMasComp/2452 * 23900usecOfInjection/litreOfAir
;*
;* This means that under the above assumptions, totMasComp/2452 has units of
;* litreOfAirPerAirflowSensorPulse.
;*
;* The factor 2452 is similar to the one provided by J. Oberholtzer, I think.
;* The exact value must be somewhere in that range...
;*
;* masScalar is also used for maf compensation ($5e86,24198 for 1G, $7A03,31235 for 2g)
;* for controls other than fuel injection. It probably correspond to some metric of
;* the totMasComp curve (average or max under given conditions). From 1G and 2G numbers,
;* It could correspond to the max of the masComp + t_masComp(freq) curve multiplied
;* by 0.808*128? It could also correspond to the masComp + t_masComp(freq) curve
;* sampled at around 69Hz and multiplied by 128.
;*
;* masScalar = maxTotMasComp*0.808*128 = totMasComp(69Hz)*128
;*
;* We then have in the case of masScalar = maxTotMasComp*0.808*128:
;*
;* airVol16 = numPulsePerCasInterrupts * $9c * masScalar / 65536
;* = numPulsePerCasInterrupts * $9c * maxTotMasComp*0.808*128 / 65536
;* = numPulsePerCasInterrupts * maxTotMasComp * 0.2462
;* = numPulsePerCasInterrupts * maxTotMasComp/2452 * 2452*0.2462
;* = numPulsePerCasInterrupts * maxTotMasComp/2452 * 603.68
;*
;* since totMasComp/2452 is litreOfAirPerAirflowSensorPulse, we have
;*
;* airVol16 = numPulsePerCasInterrupts * litreOfAirPerAirflowSensorPulse * 603.68
;*
;* Using again 1.18g/litre air density we get
;*
;* airVol16 = numPulsePerCasInterrupts * litreOfAirPerAirflowSensorPulse *1.18 * 603.68/1.18
;* = numPulsePerCasInterrupts * gramsOfAirPerAirflowSensorPulse * 512
;* = gramsOfAirPerCasInterrupts * 512
;*
;* In that case, airVol16/512 can be seen has having units of gramsOfAirPerCasInterrupts
;* (grams of air entering one cylinder). Note that the factor of 512 is not random, the
;* factor 0.808 is used to get it in that case...
;*
;* The load index values used to interpolate the fuel map is then
;*
;* airVol16/2 <= 96
;*
;* loadIndex = (airVol16/2-32)/16
;* = (gramsOfAirPerCasInterrupts*512/2 -32)/16
;* = gramsOfAirPerCasInterrupts*16-2
;*
;* airVol16/2 >= 96
;*
;* loadIndex = gramsOfAirPerCasInterrupts * 512/2 * 0.668/16
;* = gramsOfAirPerCasInterrupts*10.69
;*
;* Which correspond to (gramsOfAirPerCasInterrupts for each index value)
;*
;* 0 1 2 3 4 5 6 7 8 9 10 11
;* 0.125 0.1875 0.25 0.3125 0.3750 0.4678 0.5614 0.6549 0.7485 0.8421 0.9356 1.0292
;*
;* gramsOfAirPerRevolution would be twice those values. Notice that the max value of 1.0292
;* correspond to about 250HP when BSFC=0.55 which is in the range of the stock 1G 195HP...
;*
;* Also notice that the 8 bit airflow airVol = airVol16/2 will saturate to $ff when
;* airVol16/2 = 255 which correspond to gramsOfAirPerCasInterrupts = 1 gram. airVolT
;* airVolTB and airVolB will also saturate in the same range...
;*
;* We can now compare these results with the stock boost gauge. It has a max range
;* of 1Kg per sq cm which equals 14.2 psi. The boost gauge duty cycle is given by
;*
;* bGaugeODuty = t_bGauge(airVolT/32)/24
;*
;* When maximum airVolT = 255 = iatCompFact*airVol16/2, bGaugeODuty = 20/24 = 0.83.
;* At 25.6 degC, iatCompFact = 1.0 and therefore airVol16=510 which translates to
;* 1g of air. boost gauge duty of 0.83 correspond to approx. 10.9psi (by eye...).
;* Assuming a displacement of 0.5litre per cylinder and charge air density of 1.18
;* (25degC, probably too low for that psi range, unless you have a perfect intercooler..)
;* we would get 1.18*0.5*(10.9+14.5)/14.5 = 1.03g of air per cylinder (cas
;* interrupt). This is quite close to the 1.0g we had earlier.
;*
;* The 0psi point on the gauge correspond to a duty cycle of about 40.5% which
;* correspond to bGaugeODuty=9.75/24 which from t_bGauge correspond to
;* airVolT/32=2.875 which means airVolT = 92. with iatCompFact = 1.0 @25degC,
;* we get airVol16 = 2*airVolT/iatCompFact = 184 which correspond to 0.36grams of air
;* Assuming a displacement of 0.5litre per cylinder and charge air density of 1.18@25degC
;* we would get 1.18*0.5 = 0.59g of air per cylinder (cas interrupt) at 0psi. Compared to
;* 0.36g we had earlier this is a large error but then there are several factor not taken onto
;* account in the calculations, I suppose???.
;*
;*
;* Engine coolant and intake air temperature
;* ------------------------------------------
;*
;* Approximate sensor curves (temperature
;* against ADC value, taken from MMCD). The
;* control points in the service manual are
;* quite close (0 to 2 degC off).
;*
;*
;* ADC ECT IAT ADC ECT IAT ADC ECT IAT ADC ECT IAT
;* degC degC degC degC
;*
;* $00 158.0 184.0 $40 52.0 56.0 $80 21.0 23.0 $c0 -7.0 -7.0
;* $01 154.4 178.1 $41 51.3 55.3 $81 20.6 22.5 $c1 -7.5 -7.6
;* $02 150.9 172.5 $42 50.7 54.6 $82 20.2 22.1 $c2 -8.1 -8.2
;* $03 147.5 167.2 $43 50.1 53.9 $83 19.8 21.7 $c3 -8.6 -8.8
;* $04 144.2 162.0 $44 49.5 53.3 $84 19.4 21.2 $c4 -9.2 -9.4
;* $05 140.9 157.1 $45 48.9 52.6 $85 19.0 20.8 $c5 -9.8 -10.1
;* $06 137.7 152.4 $46 48.3 52.0 $86 18.7 20.4 $c6 -10.4 -10.7
;* $07 134.6 148.0 $47 47.7 51.3 $87 18.3 19.9 $c7 -10.9 -11.3
;* $08 131.6 143.7 $48 47.2 50.7 $88 17.9 19.5 $c8 -11.5 -12.0
;* $09 128.6 139.6 $49 46.6 50.1 $89 17.6 19.0 $c9 -12.1 -12.6
;* $0a 125.7 135.7 $4a 46.1 49.4 $8a 17.2 18.6 $ca -12.7 -13.2
;* $0b 122.9 132.0 $4b 45.6 48.8 $8b 16.9 18.2 $cb -13.2 -13.9
;* $0c 120.2 128.5 $4c 45.0 48.2 $8c 16.5 17.7 $cc -13.8 -14.5
;* $0d 117.5 125.1 $4d 44.5 47.7 $8d 16.1 17.3 $cd -14.3 -15.1
;* $0e 114.9 121.9 $4e 44.0 47.1 $8e 15.7 16.8 $ce -14.9 -15.7
;* $0f 112.4 118.8 $4f 43.5 46.5 $8f 15.3 16.4 $cf -15.4 -16.3
;* $10 110.0 116.0 $50 43.0 46.0 $90 15.0 16.0 $d0 -16.0 -17.0
;* $11 107.6 113.2 $51 42.4 45.4 $91 14.5 15.5 $d1 -16.5 -17.6
;* $12 105.3 110.6 $52 41.9 44.9 $92 14.1 15.1 $d2 -17.0 -18.2
;* $13 103.0 108.1 $53 41.4 44.3 $93 13.7 14.6 $d3 -17.5 -18.8
;* $14 100.8 105.8 $54 40.9 43.8 $94 13.3 14.2 $d4 -18.0 -19.4
;* $15 98.7 103.5 $55 40.4 43.3 $95 12.9 13.7 $d5 -18.6 -20.1
;* $16 96.7 101.4 $56 39.9 42.8 $96 12.4 13.3 $d6 -19.2 -20.8
;* $17 94.7 99.4 $57 39.3 42.3 $97 12.0 12.8 $d7 -19.8 -21.5
;* $18 92.8 97.5 $58 38.8 41.8 $98 11.5 12.4 $d8 -20.5 -22.3
;* $19 91.0 95.7 $59 38.3 41.4 $99 11.1 12.0 $d9 -21.3 -23.1
;* $1a 89.2 93.9 $5a 37.8 40.9 $9a 10.6 11.5 $da -22.1 -24.0
;* $1b 87.5 92.3 $5b 37.3 40.4 $9b 10.2 11.1 $db -23.0 -24.9
;* $1c 85.9 90.7 $5c 36.9 39.9 $9c 9.7 10.7 $dc -24.0 -26.0
;* $1d 84.3 89.2 $5d 36.4 39.4 $9d 9.3 10.2 $dd -25.0 -27.1
;* $1e 82.8 87.7 $5e 35.9 38.9 $9e 8.8 9.8 $de -26.2 -28.3
;* $1f 81.3 86.3 $5f 35.4 38.4 $9f 8.4 9.4 $df -27.5 -29.6
;* $20 80.0 85.0 $60 35.0 38.0 $a0 8.0 9.0 $e0 -29.0 -31.0
;* $21 78.6 83.6 $61 34.5 37.5 $a1 7.5 8.5 $e1 -30.5 -32.5
;* $22 77.4 82.4 $62 34.0 37.0 $a2 7.1 8.1 $e2 -32.2 -34.1
;* $23 76.2 81.1 $63 33.6 36.4 $a3 6.6 7.7 $e3 -33.9 -35.7
;* $24 75.0 79.9 $64 33.1 35.9 $a4 6.2 7.3 $e4 -35.8 -37.5
;* $25 73.9 78.8 $65 32.7 35.4 $a5 5.8 6.9 $e5 -37.7 -39.3
;* $26 72.9 77.7 $66 32.3 34.9 $a6 5.3 6.4 $e6 -39.7 -41.2
;* $27 71.9 76.6 $67 31.8 34.4 $a7 4.9 6.0 $e7 -41.7 -43.0
;* $28 70.9 75.5 $68 31.4 33.9 $a8 4.5 5.6 $e8 -43.7 -44.9
;* $29 69.9 74.5 $69 31.0 33.4 $a9 4.0 5.2 $e9 -45.8 -46.8
;* $2a 69.0 73.5 $6a 30.5 32.9 $aa 3.6 4.7 $ea -47.8 -48.7
;* $2b 68.1 72.5 $6b 30.1 32.4 $ab 3.2 4.3 $eb -49.8 -50.6
;* $2c 67.3 71.5 $6c 29.7 31.9 $ac 2.7 3.8 $ec -51.8 -52.4
;* $2d 66.4 70.6 $6d 29.3 31.4 $ad 2.3 3.4 $ed -53.7 -54.1
;* $2e 65.6 69.7 $6e 28.8 30.9 $ae 1.8 2.9 $ee -55.5 -55.8
;* $2f 64.8 68.8 $6f 28.4 30.4 $af 1.4 2.4 $ef -57.3 -57.4
;* $30 64.0 68.0 $70 28.0 30.0 $b0 1.0 2.0 $f0 -59.0 -59.0
;* $31 63.1 67.1 $71 27.5 29.5 $b1 0.5 1.5 $f1 -59.0 -59.0
;* $32 62.3 66.3 $72 27.1 29.0 $b2 0.0 0.9 $f2 -59.0 -59.0
;* $33 61.5 65.5 $73 26.6 28.6 $b3 -0.3 0.4 $f3 -59.0 -59.0
;* $34 60.7 64.7 $74 26.2 28.1 $b4 -0.8 -0.0 $f4 -59.0 -59.0
;* $35 59.9 63.9 $75 25.7 27.7 $b5 -1.3 -0.5 $f5 -59.0 -59.0
;* $36 59.2 63.1 $76 25.3 27.2 $b6 -1.8 -1.1 $f6 -59.0 -59.0
;* $37 58.4 62.3 $77 24.8 26.8 $b7 -2.3 -1.6 $f7 -59.0 -59.0
;* $38 57.6 61.6 $78 24.4 26.4 $b8 -2.8 -2.2 $f8 -59.0 -59.0
;* $39 56.9 60.9 $79 23.9 25.9 $b9 -3.3 -2.8 $f9 -59.0 -59.0
;* $3a 56.1 60.1 $7a 23.5 25.5 $ba -3.8 -3.3 $fa -59.0 -59.0
;* $3b 55.4 59.4 $7b 23.0 25.1 $bb -4.3 -3.9 $fb -59.0 -59.0
;* $3c 54.7 58.7 $7c 22.6 24.7 $bc -4.8 -4.5 $fc -59.0 -59.0
;* $3d 54.0 58.0 $7d 22.2 24.2 $bd -5.3 -5.1 $fd -59.0 -59.0
;* $3e 53.3 57.3 $7e 21.8 23.8 $be -5.9 -5.7 $fe -59.0 -59.0
;* $3f 52.6 56.6 $7f 21.4 23.4 $bf -6.4 -6.3 $ff -59.0 -59.0
;*
;*
;*
;*
;****************************************************************************************************
;***************************************************************
;*
;*
;* Assembler general settings
;*
;*
;*
;***************************************************************
.msfirst ; Assembler endian setting, do not change
.define E931 ; E931 or E932 depending on desired output
;.define enableCustom ; Define to enable custom features below, comment-out to get the original E931 or E932 binaries
#ifdef enableCustom
;-----------------
; Custom settings
;-----------------
codeOffset .equ $0100 ; Allows to move all the code up in the eprom to make space for new code, Original offset is 0.
.define ftrimMax $b3 ; Maximum fuel trim adjustement (xx/$80)%, $b3=140%
.define fuelMapClip $d0 ; Fuel map max value (will be clippped to this in code)
.define injComp $31 ; Injector size compensation referenced to $80=100% for 260cc at 36psi: 390cc(4E,43psi);450(4A);510(41);550(3D);600(38);650(33);660(32);680(31);700(30);750(2C);800(2A);850(27);
.define idleVal $64 ; Idle speed /8, Normal $60
.define idleDrVal $57 ; Idle speed /8, Normal $53
.define fuelCutVal $ff ; Fuel cut value, Original $a0
.define masComp $40 ; Mas multiplier (1G:$64, 2G:$40)
.define masScalar $7a03 ; Mas scalar (1G:$5e86, 2G:$7a03)
.define baudRate $02 ; BaudRate divider->00(125000baud),01(15625baud),02(1953baud),03(488baud)
.define custDeadTime ; Use custom injector deadtime table
.define custMas ; Use custom MAS table
.define custFuelMap ; Use custom fuel map
.define custTimingMap ; Use custom timing map
.define custOctaneMap ; Use custom octane map
.define octaneReset ; Reset octane on every start
.define extLoadRange ; Extended load range for timing, fuel and octane maps...
.define extLoadRange2 ; Use temperature compensation for load calc when extLoadRange is enabled
.define batteryGauge ; Battery gauge instead of boost gauge
.define masLog2X ; Double the MAS logging range
;.define noFuelCut ; Remove fuel cut altogether
;.define noRamReset ;
;.define noClosedLoop ; Remove closed loop mode, for testing...
#else
#ifdef E931
;--------------------------------------
; Default values for original 931 ECU
;--------------------------------------
codeOffset .equ $0000 ;
.define ftrimMax $b3 ;
.define fuelMapClip $ca ;
.define injComp $4a ; 450cc injectors used at 36psi...
.define idleVal $60 ;
.define fuelCutVal $a0 ;
.define masComp $64 ;
.define masScalar $5e86 ;
.define baudRate $02 ;
#else
;--------------------------------------
; Default values for original 932 ECU
;--------------------------------------
codeOffset .equ $0000 ;
.define ftrimMax $b0 ;
.define fuelMapClip $c0 ;
.define injComp $4e ; 390cc injectors used at 43psi, the value reflects that pressure difference compared to E931
.define idleVal $60 ;
.define idleDrVal $53 ;
.define fuelCutVal $a0 ;
.define masComp $64 ;
.define masScalar $5e86 ;
.define baudRate $02 ;
#endif
#endif
;***************************************************************
;*
;*
;* Microcontroller registers
;*
;*
;***************************************************************
p1_ddr .EQU $0000 ; Port 1 data direction register. Initialized with $7E=01111110 (0=intput, 1=output)
p2_ddr .EQU $0001 ; Port 2 data direction register. Initialized with $16=00010110
port1 .EQU $0002 ; Port 1 Data register
; bit 0 (0x01): in - Unused but varies(seems to have correlation with CAS), by extrapolation, set to out for injector #5 or #6 on other ECUs?
; bit 1 (0x02): out - Set to 0 to activate injector #3?
; bit 2 (0x04): out - Set to 0 to activate injector #2?
; bit 3 (0x08): out - Set to 0 to activate injector #4?
; bit 4 (0x10): out - Fuel pump relay
; bit 5 (0x20): out - Air cond. clutch
; bit 6 (0x40): out - ???, reset to 0 on init and first sub
; bit 7 (0x80): in - Reed switch, 4 square pulse (square wave) per odometer rotation, each of the 4 complete square wave correspond to ~40cm (20cm for each rising or falling edge)
port2 .EQU $0003 ; Port 2 Data register
; bit 0 (0x01): in - Unused but varies (seems to have correlation with CAS), by extrapolation, set to out for injector #5 or #6 on other ECUs?
; bit 1 (0x02): out - Set to 0 to activate injector #1?
; bit 2 (0x04): out - Airflow sensor active filter reset. Set/reset depending on tps,rpm,airVol,idleSwitch?????? (in serial clock)- Connected to serial port clock???
; bit 3 (0x08): in - Connected to serial port input (if serial RE is enabled) and test connector serial interface
; bit 4 (0x10): out - Connected to serial port output (if serial TE is enabled) and test connector serial interface, controlled directly to output heart beat code to test connector
; bit 5 (0x20): in - 0, ECU Operating mode PC0 (latched on ECU reset)
; bit 6 (0x40): in - 1, ECU Operating mode PC1
; bit 7 (0x80): in - 0, ECU Operating mode PC2
p3_ddr .EQU $0004 ; Port 3 data direction register, Initialized to 0 (all input)
p4_ddr .EQU $0005 ; Port 4 data direction register, Initialized to 0 (all input)
port3 .EQU $0006 ; Port 3 Data register
; bit 0 (0x01): in - IG2 related, 0 when IG2 at +12V??? (ABS unit?????) see around Md4d4 and M23db?
; bit 1 (0x02): in - IG1. 0 when IG1 at +12V. Set to 1 when power has been turned off and control relay is about to turn off. i.e. ECU is going to loose power in a short while.
; bit 2 (0x04): in - Top dead center sensor signal (TDC). Set to 0 when TDC signal is active
; bit 3 (0x08): in - Set to 1 if power steering pump is activated
; bit 4 (0x10): in - Air cond. switch (1=off). 0 indicate that AC should be activated, if possible... Connected to the output of the A/C control unit through the the ECT switch (switch cuts signal therefore asking ECU to cut clutch...)
; bit 5 (0x20): in - Inhibitor switch (A/T only) Set to 1 when transmission is in park or neutral
; bit 6 (0x40): in - 0 if key is in start position
; bit 7 (0x80): in - Set to 1 when the idle switch is on
port4 .EQU $0007 ; Port 4 data register
; bit 0 (0x01): in - c0, set when config resistor R129 is installed. used in conjucntion with c1 in #t_strap1 lookup, FEDERAL (0) or CALIFORNIA (1)
; bit 1 (0x02): in - c1, set when config resistor R130 is installed. used in conjucntion with c0 in #t_strap1 lookup, FWD (0) or AWD (1)
; bit 2 (0x04): in - Signal from the ignition sensing circuit. Toggled on every ignition signal sent to the coil (toggled on every cylinder ignition if the power transistor output changed...), stays at the given level from one ignition to the other
; bit 3 (0x08): in - Set to 1 when ECU test mode terminal is grounded
; bit 4 (0x10): in - Set to 1 when the timing terminal is grounded
; bit 5 (0x20): in - Knock sensor feedback? (set to 1 indicates it works...)???
; bit 6 (0x40): in - Fuel pump driven feedback? 0 when FP is driven?
; bit 7 (0x80): in - Injector driven feedback. Set to 0 when injector circuit is working properly??? Bit is tested when an injector to test was just deactivated and no other injector is active??? Bit might be loaded on the falling edge of the injector driving current???
; Service manual says injector is bad if injector is not continuously driven for 4 sec during idle or cranking. 4 sec is implemented by fault code regular code... So this bit would be "injector driven" bit
t1_csr .EQU $0008 ; Dual of $18, Timer1 control and status register, dual of t2_csr
; bit 0 (0x01): Injector 1 activation/deactivation bit. Bit is transfered to port2.1 when a t1 or t2 output compare interrupt is generated???
; bit 1 (0x02): cas edge detection polarity, set to 0 to trigger an interrupt on the CAS rising edge, set to 1 to trigger an interrupt on the CAS falling edge
; bit 2 (0x04): By extrapolation, set to 0 when injector 5/6 is on????
; bit 3 (0x08): Set to 1 to enable outCompInt1 interrupts (injector 1 only or 1 and 4)?
; bit 4 (0x10): Set to 1 to enable inCaptInt1 interrupts (cas)?
; bit 5 (0x20): By extrapolation, set to 0 when injector 5/6 is on????
; bit 6 (0x40): 1 indicate that outCompInt1 interrupt is pending/has been activated (injector #1 or #4 activation/deactivation)
; bit 7 (0x80): 1 indicate that inCaptInt1 interrupt is pending/has been activated (cas)
t1t2_clk .EQU $0009 ;:$000a ; Free running counter at 1MHz for t1 and t2 timer functions
t1_outCmpWr .EQU $000b ;:$000c ; Dual of $001B, Output compare register, value is compared to t1t2_clk and when a match occurs, injector ports are loaded with the values indicated in t1_csr. Seems 2 or 3 successive value can be written (injector activation and deactivation times...)
t1_inCapt .EQU $000d ;:$000e ; Cas sensor input capture register. Contains the value of t1t2_clk when the cas sensor "edge" was detected
L000f .EQU $000f ; Init to 0??????????????
sci_baud .EQU $0010 ; Serial communication rate and mode control register (clock source = 2MHz)
; bit 0 (0x01): SS0, [SS1:SS0] is baud rate divider, 00(16) 01(128) 10(1024) 11(4096), assuming basic clock of 2MHZ, we get 125000baud, 15625baud, 1953baud, 488baud
; bit 1 (0x02): SS1
; bit 2 (0x04): CC0, [CC1:CC0] is the mode control register
; bit 3 (0x08): CC1
; bit 4 (0x10): NU?
; bit 5 (0x20): NU?
; bit 6 (0x40): NU?
; bit 7 (0x80): NU?
sci_scr .EQU $0011 ; Serial communication status and control register?
; bit 0 (0x01): WU - Wake-up on idle line
; bit 1 (0x02): TE - transmit enable, set to 1
; bit 2 (0x04): TIE - Tx interrupt enable, reset to 0
; bit 3 (0x08): RE - Rx enable, checked for set before tx
; bit 4 (0x10): RIE - Rx interrupt enable, Reset/set to 0/1 in real time int
; bit 5 (0x20): TDRE - transmit data register empty
; bit 6 (0x40) ORFE - Overrun and framing error
; bit 7 (0x80): RDRF - Read data register full
sci_rx .EQU $0012 ; SCI data read register
sci_tx .EQU $0013 ; SCI data write register
ramControl .EQU $0014 ; RAM control register/battery saving status register
; bit 0 (0x01): Init to 0?
; bit 1 (0x02): Init to 0?
; bit 2 (0x04): Init to 0?
; bit 3 (0x08): Init to 0?
; bit 4 (0x10): Init to 0?
; bit 5 (0x20): Init to 0?
; bit 6 (0x40): Ram enable bit??? Set to 1 after the fresh reset initialization is done, reset to 0 in failureInt?
; bit 7 (0x80): Power standby bit, Set to 1 after the fresh reset initialization is done, reset to 0 if we loose standby power (i.e. 0 when ram content was not preserved after a power-off)
p5_ddr .EQU $0015 ; Port 5 data direction register, Initialized to $#fe (1111 1110)
port5 .EQU $0016 ; Port 5
; bit 0 (0x01): in - CAS, crank angle sensor signal. Set to 0 when the CAS signal is activated
; bit 1 (0x02): out - Power transistor output for cyl 1 and 4. Set to 0 to energize the coil.
; bit 2 (0x04): out - Power transistor output for cyl 2 and 3. Set to 0 to energize the coil.
; bit 3 (0x08): out - EGR control solenoid output
; bit 4 (0x10): out - Fuel pressure solenoid output (0=activated)
; bit 5 (0x20): out - Boost control solenoid output
; bit 6 (0x40): out - ISC step control, see table t_iscPattern
; bit 7 (0x80): out - ISC step control, see table t_iscPattern
L0017 .EQU $0017 ; Init to 0?????
t2_csr .EQU $0018 ; Timer2 control and status register, uses the same clock as timer 1 (t1t2_clk)
; bit 0 (0x01): Injector 3 activation/deactivation bit. Bit is transfered to port1.1 when a t1 or t2 output compare interrupt is generated
; bit 1 (0x02): Airflow sensor edge detection polarity (0=rising edge, 1=falling edge, or the opposite?). See masProc subroutine header
; bit 2 (0x04): Injector 2 activation/deactivation bit. Bit is transfered to port1.2 when a t1 or t2 output compare interrupt is generated
; bit 3 (0x08): Set to 1 to enable outCompInt2 interrupts (injectors 2, 3 and maybe 4)?
; bit 4 (0x10): Set to 1 to enable inCaptInt2 interrupts (airflow sensor)?
; bit 5 (0x20): Injector 4 activation/deactivation bit. Bit is transfered to port1.3 when a t1 or t2 output compare interrupt is generated
; bit 6 (0x40): 1 indicate that outCompInt2 interrupt is pending/has been activated (injectors #2 or #3 activation/deactivation)
; bit 7 (0x80): 1 indicate that inCaptInt2 interruot is pending/has been activated (airflow sensor pulse)
t3_csr0 .EQU $0019 ; Normally the dual of $0009 but since the ECU didn't need the equivalent of t1t2_clk for timer 2 (timer 1 and timer 2 both use t1t2_clk), it is used for something else...
; timer 3 (coil) control ans status register 0 ???
; bit 0 (0x01): 0 all the time except, set to 1 when no cas interrupt received for 1.275sec???
; bit 1 (0x02): 1 on every loop
; bit 2 (0x04): 1 Set to 0 when the output compare interrupt need to energize coil for cylinder 1 or 4, i.e. bit will be loaded in port5.1 when interrupt occur
; bit 3 (0x08): 1 Set to 0 when the output compare interrupt need to energize coil for cylinder 2 or 3, i.e. bit will be loaded in port5.2 when interrupt occur
; bit 4 (0x10): 1 on init but not on every loop, Used to decide which of t3_clock1 or t3_clock2 should be used upon a CAS interrupt???
; bit 5 (0x20): 0 on every loop
; bit 6 (0x40): 1 on every loop
; bit 7 (0x80): 0 on every loop
t3_csr1 .EQU $001a ; Normally the dual of $000a but since the ECU didn't need the equivalent of t1t2_clk for timer 2 (timer 1 and timer 2 both use t1t2_clk), it is used for something else...
; timer 3 (coil) control ans status register 1???
; bit 0 (0x01): 0 Cylinder 1/4 or 2/3 ?? output compare detection polarity?
; bit 1 (0x02): 1 Cylinder 1/4 or 2/3 ?? output compare detection polarity?
; bit 2 (0x04): 0 Cylinder 1/4 or 2/3 ?? output compare detection polarity?
; bit 3 (0x08): 1 Cylinder 1/4 or 2/3 ?? output compare detection polarity?
; bit 4 (0x10): 0
; bit 5 (0x20): 0
; bit 6 (0x40): 0 1 indicate that the outCompInt3 interrupt is pending/has been activated???
; bit 7 (0x80): 0
t2_outCmpWr .EQU $001b ;:$001c ; Dual of $0b:$0c, Output compare register, value is compared to t1t2_clk and when a match occurs, injector ports are loaded with the values indicated in t2_csr. seems 2 or 3 successive value can be written (injector activation and deactivation times...)
t2_inCapt .EQU $001d ;:$001e ; Dual of $0d:$0e, Airflow sensor input capture register. Contains the value of t1t2_clk when an airflow sensor pulse edge detected
adc_ctl .EQU $001f ; ADC control; [bit 3 = start bit?, bit 2:0 = channel select ]???
; bit 0 (0x01): c0 [c2:c1:c0] is the port number to use aas input to the A/D converter
; bit 1 (0x02): c1
; bit 2 (0x04): c2
; bit 3 (0x08): Start bit, set to 1 to start A/D conversion
; bit 4 (0x10): ?
; bit 5 (0x20): ?
; bit 6 (0x40): ?
; bit 7 (0x80): ?
adc_data .EQU $0020 ; 8 bit A to D converter result data
L0021 .EQU $0021 ; Unused?
L0022 .EQU $0022 ; Unused?
L0023 .EQU $0023 ; Unused?
L0024 .EQU $0024 ; Init to 0?
L0025 .EQU $0025 ; Unused?
rti_ctl .EQU $0026 ; Timer control and status register for real time interrupt? init to $4D = 0100 1101
; bit 0 (0x01): ?
; bit 1 (0x02): ?
; bit 2 (0x04): ?
; bit 3 (0x08): ?
; bit 4 (0x10): ?
; bit 5 (0x20): ?
; bit 6 (0x40): Set to 1 to enable rti interrupts?
; bit 7 (0x80): ?
rti_freq .EQU $0027 ; Real time interrupt frequency setting: Freq = 125000/(256-x) where x is the content of rti_freq
L0028 .EQU $0028 ; Unused?
t3_clock1 .EQU $0029 ;:$002a ; Readable counter. Frequency seems to be 250KHz (2MHz/8).
t3_outCmpWr .EQU $002b ;:$002c ; Writable output compare register for counters at $0029:$002A and $002D:$002E
; Seems to be double buffered...
t3_clock2 .EQU $002d ;:$002e ; Dual of $0029. I think it always has the same value as t3_clock1 but ipon a cas interrupt, the code decides between t3_clock1 and t3_clock2???
port6 .EQU $002f ; Port 6 (all output, no data direction register?)
; bit 0 (0x01): out - Write 1 to reset instant knock count???
; bit 1 (0x02): out - ??? Set to 0 when rpm>4688rpm, set to 1 when rpm<4600, could be some kind of ECU board filter setting for the knock sensor???
; bit 2 (0x04): out - Boost gauge output
; bit 3 (0x08): out - Check engine (CE) light
; bit 4 (0x10): out - Reset to 0 to activate purge solenoid?
; bit 5 (0x20): out - Toggled at F924 if main loop frequency >20Hz, could be tied to ECU reset in case of trouble (COP clock)
; bit 6 (0x40): out - Not used?
; bit 7 (0x80): out - Not used?
;------------------------------
; Block of 16 probably unused
; microcontroller registers???
;------------------------------
L0030 .EQU $0030 ; Unused
L0031 .EQU $0031 ; Unused
L0032 .EQU $0032 ; Unused
L0033 .EQU $0033 ; Unused
L0034 .EQU $0034 ; Unused
L0035 .EQU $0035 ; Unused
L0036 .EQU $0036 ; Unused
L0037 .EQU $0037 ; Unused
L0038 .EQU $0038 ; Unused
L0039 .EQU $0039 ; Unused
L003a .EQU $003a ; Unused
L003b .EQU $003b ; Unused
L003c .EQU $003c ; Unused
L003d .EQU $003d ; Unused
L003e .EQU $003e ; Unused
L003f .EQU $003f ; Unused
;***************************************************************
;*
;*
;* Block of RAM used to preserve settings when the ECU is off
;* (This block is not cleared to 0 when the ECU is powered-on)
;*
;*
;***************************************************************
ftrim_low .EQU $0040 ; Fuel trim low (.78x)%
ftrim_mid .EQU $0041 ; Fuel trim mid (.78x)%
ftrim_hi .EQU $0042 ; Fuel trim high (.78x)%
ftrimCntr .EQU $0043 ; Fuel trim counter. This counter is increased/decreased by 5 (+/-5 at 40Hz) whenever a fuel trim is below/above o2Fbk threshold. The fuel trim is increased/decreased by 1 whenever this counter rools over, giving an effective update rate of 40Hz/(256/5)=0.78125Hz for the fuel trims update
isc0 .EQU $0044 ;:$0045 ; iscm (isc0 or isc1) are 16 bit long term correction factors/feedback for the isc step adjustment. It is centered at $8000 (100%, no correction). Init to $8c00, A value higher than $8000 indicate that we need to increase the isc step since the current rpm is lower than the desired one
; The isc step used is increased/decreased by iscm/256 - $80. iscm is updated from the short term iscYn variable.The isc step used is increased/decreased by iscm/256 - $80
; isc0 is the long term learning variable when A/C is off, 16 bits, see iscPointers function
isc1 .EQU $0046 ;:$0047 ; isc1 is the long term learning variable when A/C is on, 16 bits, see iscPointers function
iscStepCom .EQU $0048 ; isc step complement, shlould be equal to ~iscStepCurr & $7f. Not sure of its utility???
iscStepCurr .EQU $0049 ; Current isc step (x) range of 0 to 120 (or 13x???)
iscPatrnIdx .EQU $004a ; Current ISC pattern index, two lower bits are used as index into t_iscPattern to update port5.6.7 in order to move the ISC spindle...
iscFlags0 .EQU $004b ; Flag register for ISC updating
; bit 0 (0x01): Set to 1 once the isc calibration is started. This means we initialized iscStepCurr to 135 and set the iscStepTarg to 0. The spindle will therefore be moved to the minimum position irrespective of the starting position, which will allow us to know its real position... Reset to 0 once calibration is finished and ISC is back to iscStepCurr=6
; bit 1 (0x02): Set to 1 once the isc calibration is finished. i.e. once iscStepCurr reached 0. See bit 0. Reset to 0 once calibration is finished and ISC is back to iscStepCurr=6
; bit 2 (0x04): Set when basic idle speed adjustment mode is active
; bit 3 (0x08): Set to 1 when a fixed isc step is used because the engine is running but we are not receiving airflow sensor interrupts.
; bit 4 (0x10): Set to 1 when a fixed isc step is used because the ECU is about to loose power
; bit 5 (0x20): Set to 1 when ISC min calibration need to be performed, i.e. move the spindle 135 steps toward 0, that ensures the spindle is positionned at the minimum position, wherever we started from... Reset to 0 once calibration is finished and ISC is back to iscStepCurr=6
; bit 6 (0x40): Set to 1 when the ISC max calibration has been performed, see bit 7
; bit 7 (0x80): Set to 1 when ISC max calibration need to be performed. Max calibration is achieved by setting iscStepTarg to 135, wait for iscStepCurr to reach 135 (higher than max usable valu of 120) and then set iscStepCurr to 120 since this is the max usable value
stFaultHi .EQU $004c ; Stored faults, High byte. Notice we say its high byte because it is the ECU convention to store high byte before low byte and it is also used that way in the code
stFaultLo .EQU $004d ; Stored faults, Low byte.
faultHi .EQU $004e ; Faults, high byte. Notice we say its high byte because it is the ECU convention to store high byte before low byte and it is also used that way in the code
faultLo .EQU $004f ; Faults, low byte
o2BadCnt .EQU $0050 ; Used to test the o2 sensor, 0 when 02 sensor not in fault or not tested, 1 or greater when o2 sensor is bad. Can only increase by 1 each time the ECU is turned on and sensor is tested
egrtBadCnt .EQU $0051 ; Used to test the egrt sensor, 0 when egrt sensor not in fault or not tested, 1 or greater when egrt sensor is bad. Can only increase by 1 each time the ECU is turned on and sensor is tested
octane .EQU $0052 ; Octane value used in timing advance calculation with min 0(bad fuel...), max 255 (no knock). Updated at 2.5Hz from knockSum under specific circumstances (decremented by 1 if knocksum>5, incremented by 1 if knocksum<3)
knockFlags .EQU $0053 ; Flags related to knock sensor
; bit 0 (0x01):
; bit 1 (0x02):
; bit 2 (0x04):
; bit 3 (0x08):
; bit 4 (0x10):
; bit 5 (0x20):
; bit 6 (0x40): Set to 1 when engine has been runnning for more than 1 sec
; bit 7 (0x80): Set to 1 when airVol>$49, used to know whether engine is under high or loaw load for knockSum and knockSum decay calculations
L0054 .EQU $0054 ; UNUSED?
config1 .EQU $0055 ; Configuration flags depending on config resistors, Loaded with t_strap1[port4& (#$03 << 1)]
; bit 0 (0x01):
; bit 1 (0x02):
; bit 2 (0x04):
; bit 3 (0x08):
; bit 4 (0x10):
; bit 5 (0x20):
; bit 6 (0x40):
; bit 7 (0x80):
config2 .EQU $0056 ; Configuration flags depending on config resistors, Loaded with t_strap1[port4& (#$03 << 1)+1]
; bit 0 (0x01):
; bit 1 (0x02):
; bit 2 (0x04):
; bit 3 (0x08):
; bit 4 (0x10):
; bit 5 (0x20):
; bit 6 (0x40):
; bit 7 (0x80):
;***************************************************************
;*
;*
;* RAM, cleared to 0 when the ECU is powered-on
;*
;*
;***************************************************************
ramClearStart .EQU $0057
temp1 .EQU $0057 ;
temp2 .EQU $0058 ;
temp3 .EQU $0059 ;
temp4 .EQU $005a ;
temp5 .EQU $005b ;
temp6 .EQU $005c ;
temp7 .EQU $005d ;
temp8 .EQU $005e ;
temp9 .EQU $005f ;
L0060 .EQU $0060 ; Unused
casFlags0 .EQU $0061 ; Flag register
; bit 0 (0x01): Bit is set to 1 when rpm(Tcas) >= 505, reset when rpm(Tcas) < 401 (hysteresis)
; bit 1 (0x02): Old value of bit 0
; bit 2 (0x04): 1 if rpm(Tcas) > 1775rpm
; bit 3 (0x08): Old value of bit 2
; bit 4 (0x10): 1 if rpm(Tcas) > 1540rpm
; bit 5 (0x20): 1 if rpm(Tcas) > 4801rpm
; bit 6 (0x40): Set to 1 if timing adjustment mode is active
; bit 7 (0x80): Unused?
ignFallFlags .EQU $0062 ; Coil ignition scheduling on the cas falling edge
; bit 0 (0x01): Set to 1 when coil ignition was not scheduled on the CAS
; rising edge and therefore need to be scheduled on the CAS falling edge?
; bit 1 (0x02): not used
; bit 2 (0x04): not used
; bit 3 (0x08): not used
; bit 4 (0x10): not used
; bit 5 (0x20): not used
; bit 6 (0x40): not used
; bit 7 (0x80): not used
enerFlags .EQU $0063 ; Coil energization state, bit 0 and 1 are mutually exclusive, they are never set at the same time...
; Note that when rpm is low, these flags might not be set as indicated (during cranking?)
; bit 0 (0x01): Set to 1 when coil is currently energized?
; bit 1 (0x02): Set to 1 when coil energization has been scheduled?
; bit 2 (0x04): not used
; bit 3 (0x08): not used
; bit 4 (0x10): not used
; bit 5 (0x20): not used
; bit 6 (0x40): not used
; bit 7 (0x80): not used
TcasLast0 .EQU $0064 ; TcasLast0:TcasLast1 (250KHz clock) is identical to TcasNew0:TcasNew1 but it has been validated for range. Basically it is the last Tcas value that was valid
TcasLast1 .EQU $0065 ; See TcasLast0
TcasNew0 .EQU $0066 ; TcasNew0:TcasNew1 (250KHz clock) is the new value of Tcas calculated during the CAS interrupt
TcasNew1 .EQU $0067 ; See TcasNew0
casRiseTime0 .EQU $0068 ; casRiseTime0:casRiseTime1 (250KHz clock) is the clock value when the last CAS rising edge interrupt occured
casRiseTime1 .EQU $0069 ; See casRiseTime0
casFallTime0 .EQU $006a ; casFallTime0:casFallTime1 (250KHz clock) is the clock value when the last CAS falling edge interrupt occured
casFallTime1 .EQU $006b ; See casFallTime0
timCas0 .EQU $006c ; The current ignition timing (xx/256*90)degrees referenced to the CAS pulse rising edge (75deg BTDC), [timCas0:timCas1] = 256 * (75.77 - degAdv)/90, calculated from tim61 + $002a
timCas1 .EQU $006d ; See timCas0
ignRelTime0 .EQU $006e ; [ignRelTime0:ignRelTime1] is the current ignition time minus 72us measured in 1/250000 sec (timer clock) and referenced to the CAS rising edge (75deg BTDC). Calculated from timCas0: [ignRelTime0:ignRelTime1] = [TcasNew0:TcasNew1]/2 * [timCas0:timCas1]/256 - $0012
ignRelTime1 .EQU $006f ; See ignRelTime0
ignFallRelTime0 .EQU $0070 ; Similar to ignRelTime0 but measured from te cas falling edge, used to schedule ignition when the timing is high (past the cas falling edge...)
ignFallRelTime1 .EQU $0071 ; See ignFallRelTime0
enerLenX0 .EQU $0072 ; One of the coil energization durations, the one used depends on current conditions...
enerLenX1 .EQU $0073 ; See enerLenX0
enerAbsTime0 .EQU $0074 ; Coil energization absolute time (t3_clock1)
enerAbsTime1 .EQU $0075 ; See enerAbsTime0
ignTime0 .EQU $0076 ; Coil ignition absolute time (t3_clock1)
ignTime1 .EQU $0077 ; See ignTime1
enerAbsTimeNext0 .EQU $0078 ; Coil absolute energization time (ignTime1) but only used when energization of the "next cylinder" is scheduled from the preceeding cylinder coil ignition time...
enerAbsTimeNext1 .EQU $0079 ; See enerAbsTimeNext0
TcasLast128 .EQU $007a ; Set to TcasLast0/128
tdcMask0 .EQU $007b ; tdcMask0:tdcMask1 contains $0204 when TDC signal is active (cylinder 1 or 4) on the CAS rising edge, $0402 otherwise. Toggled on every CAS rising edge
tdcMask1 .EQU $007c ; See tdcMask0
tim61 .EQU $007d ; Current timing (xx/256*90)degrees referenced to 61deg BTDC, tim61 = 256 * (61 - degAdv) / 90, where degAdv is the timing advance in degrees. Calculated from tim61Tot0
temp20 .EQU $007e ;
temp21 .EQU $007f ;
temp22 .EQU $0080 ;
temp23 .EQU $0081 ;
temp24 .EQU $0082 ;
tdcCasCount .EQU $0083 ; CAS rising edge counter when key is not in start, incremented on every CAS rising edge up to a maximum value of 6, used in TDC synch. operation
T40s_casInt .EQU $0084 ; Initialized to 1.275sec on every CAS rising edge interrupt and decremented in first subroutine at ~40Hz. Will reach 0 (expire) only when no CAS interrupt was received for over 1.275sec, i.e. engine is really not rotating or something is wrong?
coilChkFlags .EQU $0085 ; Flag register used to validate the ignition signal using the ignition coil sensing circuit
; bit 0 (0x01): Injector 1, set to 1 to indicate that the injector can be used, 0 indicate injector is disabled because ignition is not happening on the corresponding cylinder
; bit 1 (0x02): Injector 3, set to 1 to indicate that the injector can be used, 0 indicate injector is disabled because ignition is not happening on the corresponding cylinder
; bit 2 (0x04): Injector 4, set to 1 to indicate that the injector can be used, 0 indicate injector is disabled because ignition is not happening on the corresponding cylinder
; bit 3 (0x08): Injector 2, set to 1 to indicate that the injector can be used, 0 indicate injector is disabled because ignition is not happening on the corresponding cylinder
; bit 4 (0x10):
; bit 5 (0x20): Set to 1 when engine is running and rpm<5000 and 8V<=battRaw<=18V, meaning we can proceed with checking the ignition
; bit 6 (0x40):
; bit 7 (0x80): Set to 1 when we detected that several ignition signals were missing, ignition is not working properly.
p4Latched .EQU $0086 ; Loaded with port4 and checked for bit #$04 in CAS interrupt
timAdjFlags .EQU $0087 ; Timing adjustment mode flags
; bit 0 (0x01): Set when rpm31>2000rpm, reset when rpm31 goes lower than 1813rpm (hysteresis)
; bit 1 (0x02):
; bit 2 (0x04):
; bit 3 (0x08):
; bit 4 (0x10):
; bit 5 (0x20):
; bit 6 (0x40):
; bit 7 (0x80): Set to 1 when timing adjustment mode is active (timing terminal is grounded but the ECU test mode terminal is not grounded
tim61Tot0 .EQU $0088 ; New target timing (xx/256*90)degrees referenced to 61deg BTDC. knockSum is added to this value in order to retard timing further and then a maximum rate of change of 22.5deg/iteration is applied. The result becomes the new timing to apply (tim61 and timCas0:timCas1). Calculated from advTotal
enerLen .EQU $0089 ; Coil energization time as loaded from the t_enerLen(battRaw) table. Actual energization time used might be different, longer...
timingAdv .EQU $008a ; Current timing advance, (x-10)degrees, timingAdv = degAdv+10, Calculated from tim61
knockSum .EQU $008b ; Current knock sum value
T200s_knock .EQU $008c ; Knock attenuation timer decremented at 200Hz and looping at 1.67Hz or 100Hz depending on airVol, knockSum is decremented by 1 every time this timer expires
airCnt0 .EQU $008d ; [airCnt0:airCnt1:airCnt2] is the exponentially averaged 24 bit air count (input is 16 bit [airCntNew0:airCntNew1]*256)
airCnt1 .EQU $008e ; See airCnt0
airCnt2 .EQU $008f ; See airCnt0
airCntNew0 .EQU $0090 ; airCntNew0:airCntNew1 is the 16 bits air count used as input to [airCnt0:airCnt1:airCnt2]. It is equal (N+r) * $9c where N is the number of airflow sensor pulse counted by the mas interrupt between each cas interrupt (1 cas interrupt for every cylinder cycle, 4 per every 2 engine rotations) r<=1 is a "remainder" proportional to the time elapsed since the last interrupt...
airCntNew1 .EQU $0091 ; See airCntNew0
oldAirCnt0 .EQU $0092 ;:$0093 ; This is the old value of airCnt0:airCnt1 used to compute some kind of air count derivative
airDiffPos .EQU $0094 ; Contains airCnt0-oldAirCnt0 when the difference is positive, This is kind of the derivative of air count which is positive when air count is increasing (acceleration)
airDiffNeg .EQU $0095 ; Contains abs(airCnt0-oldAirCnt0) when the difference is negative (contains oldAirCnt0-airCnt0...). This is kind of the derivative of air count which is negative when air count is decreasing (decceleration)
t1_lastCas .EQU $0096 ;:$0097 ; Latest value of t1_inCapt when CAS interrupt was called
t2_lastMas .EQU $0098 ;:$0099 ; Latest value of t2_inCapt when MAS interrupt was called
t2_diff8 .EQU $009a ;:$009b ; Time between 2 edges (2 edges per pulse...) of the airflow sensor with timer based rounding (see code), calculated on each mas interrupts from t2_inCapt/8
airQuantum .EQU $009c ; This value ($9c) is the the "amount of air" corresponding to 1 airflow sensor pulse. Using a non unitary value allows the ECU to interpolate the airflow between pulses, i.e. if at the time we calculate airflow we are at 2/3 in between two pulses then we add 2/3 of airQuantum...
; It is added to [airCntNew0:airCntNew1] on each mas interrupt call (accumulates N times $9C...). A ratio is also applied to this value when it is added to [airCntNew0:airCntNew1] for the last time (partial count in between pulses) before airCnt0 is calculated.
L009d .EQU $009d ; Not used?
masCasFlags .EQU $009e ; Flag register
; bit 0 (0x01): Bit is set when the CAS rising edge interrupt code is executed to flag the event to the main loop. Flag is read from main loop to update rpmX4Filt and then reset
; bit 1 (0x02):
; bit 2 (0x04):
; bit 3 (0x08):
; bit 4 (0x10):
; bit 5 (0x20):
; bit 6 (0x40):
; bit 7 (0x80): Scaling for the airflow sensor pulse counting. Set to 0 when we count both the rising and falling edge of the airflow sensor pulse. Set to 0 in case we count only the rising edges (or only the falling ones)
airFiltFact .EQU $009f ; airCnt0 exponential averaging factor with alpha = airFiltFact/256, 0<=alpha<=1, basically used to filter the air count: new airCnt0 = alpha * old airCnt0 + (1-alpha)*newAirCntValue, possible values in the code are $b3(70%), $d1(82%) or $e4(89%)
airCntMax .EQU $00a0 ; Air count based on rpm, ect and iat, 8*airCntMax is used as a maximum on airCnt0 or when engine not rotating/starting to rotate
accEnr .EQU $00a1 ; Acceleration enrichment (100x/255)%. This value is actually updated with min(airCnt0-oldAirCnt0,$48) under acceleration, see code. Max value is $48 from code
state3 .EQU $00a2 ; Flag register
; bit 0 (0x01): Copied from same bit in state1 (1=startingToCrank)
; bit 1 (0x02): Copied from same bit in state1 (1=no pulse accumulator interrupts)
; bit 2 (0x04): Set when RPM exceeds threshold (rev limiter)
; bit 3 (0x08): Copied from same bit in state1 (1=rotatingStopInj)
; bit 4 (0x10): Copied from same bit in state1 (1=notRotating)
; bit 5 (0x20): Set if rotatingStopInj and not runningFast
; bit 6 (0x40):
; bit 7 (0x80): Set to injFlags0.7 (1 when startingToCrankColdEngine)
injFactor .EQU $00a3 ;:$00a4 ; Global injector factor used to calculate injPw from [airCnt0:airCnt1],
; injFactor = 16*totMasComp * injComp/128 * [workFtrim + o2FuelAdj + 2*$80]/512 * iatCompFact/128 * baroFact/128 * openLoopEnr/128 * coldTempEnr/128 * (2*enrWarmup + $80)/128
oldReedVal .EQU $00a5 ; Old value of the reed switch sensor
deadTime .EQU $00a6 ; Injector deadtime in increment of 24uS (depends on current batteryVoltage)
injPw .EQU $00a7 ;:$00a8 ; 16 bit injector pulse width in microseconds. Logger reports high and low bytes: (.256 highByte)mS
inj1_offT .EQU $00a9 ;:$00aa ; Injector #1? deactivation time (relative to timer t1t2_clk)
inj3_offT .EQU $00ab ;:$00ac ; Injector #3? deactivation time (relative to timer t1t2_clk)
inj4_offT .EQU $00ad ;:$00ae ; Injector #4? deactivation time (relative to timer t1t2_clk)
inj2_offT .EQU $00af ;:$00b0 ; Injector #2? deactivation time (relative to timer t1t2_clk)
last_t1t2_clk .EQU $00b1 ; Initialized to t1t2_clk/256 on CAS falling edge, every one of them???
injToAct .EQU $00b2 ; Indicate which injectors are currently active or should be activated, Set to 1 for an active injector
; bit 0 (0x01): Inj 1?
; bit 1 (0x02): Inj 3?
; bit 2 (0x04): Inj 4?
; bit 3 (0x08): Inj 2?
; bit 4 (0x10):
; bit 5 (0x20):
; bit 6 (0x40):
; bit 7 (0x80):
tdcCasFlags .EQU $00b3 ; Init to 5
; bit 0 (0x01): c0, c2:c1:c0 used as a down counter (on every CAS pulse falling edge) initialized with 5. Reset to 0 when the CAS pulse falling edge correpond to the cylinder #1 TDC pulse (see bit 7)
; bit 1 (0x02): c1
; bit 2 (0x04): c2
; bit 3 (0x08): Set to the last value of TDC bit on port3. 2
; bit 4 (0x10):
; bit 5 (0x20):
; bit 6 (0x40):
; bit 7 (0x80): Set to 1 when cylinder #1 TDC is detected on the CAS falling edge. Set to 1 when we detect that TDC bit on port3.2 has changed from 1 to 0 (falling edge) from one CAS falling edge to the other. That basically indicate cylinder #1 TDC
casCylIndex .EQU $00b4 ; Cas current cylinder index (0,1,2,3 -> cyl #2,#1,#3,#4). Counter looping from 0 to 3 and increased on every CAS falling edge. re-init to 0 when TDC of cylinder #1 is detected (tdcCasFlags.7 set).
; bit 0 (0x01):
; bit 1 (0x02):
; bit 2 (0x04):
; bit 3 (0x08):
; bit 4 (0x10):
; bit 5 (0x20):
; bit 6 (0x40):
; bit 7 (0x80):
newInjToAct .EQU $00b5 ; Indicate which injector should be activated (also, bit 7 is set when doing simultaneous injection). Mostly updated on the CAS falling edge
; bit 0 (0x01): Inj 1?
; bit 1 (0x02): Inj 3?
; bit 2 (0x04): Inj 4?
; bit 3 (0x08): Inj 2?
; bit 4 (0x10): Inj 5/6?
; bit 5 (0x20): Inj 5/6?
; bit 6 (0x40):
; bit 7 (0x80): Set to 1 when when we should be doing simultaneous injection on all 4 cylinders, 0 indicate sequential injection
tdcCheck .EQU $00b6 ; Init to 8 on the cas falling edge of the cylinder #1 TDC, decremented by 1 on every cas falling edge. Used to check that TDC sensor is working correctly, it should never reach 0...
oldInjToAct .EQU $00b7 ; Old value of injToAct (before it was updated)
injToTest .EQU $00b8 ; The current injector to test for proper operation (set to 1 to test), 1 bit per injector. Testing proceed from bit 0 to bit 3. We stay on the same injector if it is found to be bad, see around L1884
; bit 0 (0x01): Inj 1?
; bit 1 (0x02): Inj 3?
; bit 2 (0x04): Inj 4?
; bit 3 (0x08): Inj 2?
; bit 4 (0x10):
; bit 5 (0x20):
; bit 6 (0x40):
; bit 7 (0x80):
injBad .EQU $00b9 ; Injector testing flags
; bit 0 (0x01): Set to 1 when one of the injector is not working correctly based on injector feedback bit, see injToTest
; bit 1 (0x02): Not used
; bit 2 (0x04): Not used
; bit 3 (0x08): Not used
; bit 4 (0x10): Not used
; bit 5 (0x20): Not used
; bit 6 (0x40): Not used
; bit 7 (0x80): Not used
obdInjCmd .EQU $00ba ; processing of OBD code bit 0 to 5 correspond to injectors being turned on/off
; bit 0 (0x01): Inj. 1, Set to 0 if injector is currently turned off by obd command, 1 in normal operation
; bit 1 (0x02): Inj. 3, See bit 0
; bit 2 (0x04): Inj. 4, See bit 0
; bit 3 (0x08): Inj. 2, See bit 0
; bit 4 (0x10): Inj 5/6 See bit 0
; bit 5 (0x20): Inj 5/6 See bit 0
; bit 6 (0x40): Not used
; bit 7 (0x80): Not used
rtiCnt .EQU $00bb ; free counter increased on every real time interrupt (~800Hz), used to execute some functions only 1 out of N times (check count value)