DSM/ECU/Pretty disassembly notes
Pretty disassembly notes
<- DSM/ECU
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 clearbrset
: 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 they
register. It seems thaty
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
- i.e. cylinder #1 injection starts at
- 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 degC |
IAT | ===== | ADC | ECT degC |
IAT | ===== | ADC | ECT degC |
IAT | ===== | ADC | ECT degC |
IAT |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
$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 |