Okay, then the two blocks are obviously for the negative and positive polarity. The first block would be used if J501 is low (positive polarity), and the second block for negative polarity. The use second or third word depends on the polarity of vset - word1. If this is negative, then the second word is used. Otherwise the second word is skipped and the third word is used. The result is fed in this function that I can not completely figure out:
ROM:08A1 ; de = intermediate dac value (= word1 - vset)
ROM:08A1 ; hl = calib word 2 or 3
ROM:08A1
ROM:08A1 some_calc_hl_de: ; CODE XREF: check_limits+69p
ROM:08A1 ; sub_A39+3Bp ...
ROM:08A1 ld c, l
ROM:08A2 ld b, h
ROM:08A3 ld hl, 0
ROM:08A6 ld a, 10h
ROM:08A8
ROM:08A8 loc_8A8: ; CODE XREF: some_calc_hl_de+16j
ROM:08A8 sla l
ROM:08AA rl h
ROM:08AC rl e
ROM:08AE rl d
ROM:08B0 jr nc, loc_8B6
ROM:08B2 add hl, bc
ROM:08B3 jr nc, loc_8B6
ROM:08B5 inc de
ROM:08B6
ROM:08B6 loc_8B6: ; CODE XREF: some_calc_hl_de+Fj
ROM:08B6 ; some_calc_hl_de+12j
ROM:08B6 dec a
ROM:08B7 jr nz, loc_8A8
ROM:08B9 ret
ROM:08B9 ; End of function some_calc_hl_de
Clearly the intermediate calibration value (word1 - vset) is added to an accumulator up to 0x10 times, depending on the value of word 2 / 3. Plus a bunch of bit shifting. Does anyone recognize this as a common mathematical function? Anyway, word 2 / 3 does seem some kind of multiplier/exponent. It presumably translates the signed 16-bit value to an unsigned 12-bit value.
This is the context of that function call. It comes immediately after the block I posted in my previous post:
ROM:0942 loc_942: ; CODE XREF: check_limits+5Ej
ROM:0942 pop hl
ROM:0943 push de
ROM:0944 call some_calc_hl_de ; in:
ROM:0944 ; de = intermediate dac value
ROM:0944 ; hl = calib word 2 or 3
ROM:0944 ; out:
ROM:0944 ; hl = result
ROM:0944 ; de = ???
ROM:0947 bit 7, h
ROM:0949 jr z, loc_94C
ROM:094B inc de
ROM:094C
ROM:094C loc_94C: ; CODE XREF: check_limits+6Ej
ROM:094C pop hl
ROM:094D add hl, de
ROM:094E ld a, (byte_40E6)
ROM:0951 bit 0, a
ROM:0953 jr z, loc_957
ROM:0955 set 7, h
ROM:0957
ROM:0957 loc_957: ; CODE XREF: check_limits+78j
ROM:0957 pop af
ROM:0958 ret
So after the operation the result hl is ignored, and the result de is added to (word1 - vset).
There may be some other data in there: by my math the four blocks of six bytes should only take 0x18 bytes, yet the second block starts at +0x2a (at 0x1fca). There does appear data between 0x1fb8 and 0x1fca. Is my understanding wrong? Or is this data really not used? If the second block is also 0x2a bytes long, it should stop at 0x1ff4, yet it copies until 0x1ffc.
This code appears to check an additional parameter block at 0x099F. This is a four byte block of zeroes in Sandra's ROM dump. This appears to indicate the number of iterations of the loop at 0x093c. So this could be an additional linearity correction or something (maybe used for different ranges, e.g. 5 kV vs 10 kV models). Though the different location in the firmware and the fact that it is not copied to RAM seems odd for calibration, and seems to argue for different models.
ROM:092E ld hl, unk_99F
ROM:0931 add a, l
ROM:0932 ld l, a
ROM:0933 jr nc, loc_936
ROM:0935 inc h
ROM:0936
ROM:0936 loc_936: ; CODE XREF: check_limits+58j
ROM:0936 ld a, (hl)
ROM:0937 cp 0
ROM:0939 jr z, loc_942
ROM:093B ld b, a
ROM:093C
ROM:093C loc_93C: ; CODE XREF: check_limits+65j
ROM:093C sla e
ROM:093E rl d
ROM:0940 djnz loc_93C
ROM:0942
ROM:0942 loc_942:
Actually, on second thought, it looks like WORD? can query words from the calibration RAM:
ROM:1DE5 parse_word?: ; CODE XREF: ROM:1D7Aj
ROM:1DE5 ld hl, 2Dh ; '-'
ROM:1DE8 call get_gpib_num3
ROM:1DEB jp nz, end_of_string
ROM:1DEE push hl
ROM:1DEF call gpib_check_end_command
ROM:1DF2 pop hl
ROM:1DF3 jp nz, syntax_error
ROM:1DF6 ld de, byte_4026
ROM:1DF9 ex de, hl
ROM:1DFA add hl, de
ROM:1DFB add hl, de
ROM:1DFC ld e, (hl)
ROM:1DFD inc hl
ROM:1DFE ld d, (hl)
ROM:1DFF ex de, hl
ROM:1E00 call sub_1878
ROM:1E03 jp end_of_string
The initial hl is the max input it accepts. So it expects a decimal integer up to 0x2d (45). And it adds this address twice to 0x4026. By my math that puts it two bytes short from the 0x5c bytes copied at 0x94. Either that or I made an off-by-one error. sub_1878 looks GPIB related, so may very well send a response back. I was also wrong about the WORD write. It appears to write the calibration RAM, and then changes the DAC setting to match the new calibration value. That was I was confused about it settings a value: it does call set_control value, but only to update the DAC with the new calibration constants. This is the complete code:
ROM:1D77 parse_word:
ROM:1D77 call check_question_mark
ROM:1D7A jp z, parse_word?
ROM:1D7D ld hl, 2Dh ; '-'
ROM:1D80 call get_gpib_num3
ROM:1D83 jp nz, end_of_string
ROM:1D86 ld (var_temp_word_address), hl
ROM:1D89 call check_comma
ROM:1D8C jp nz, syntax_error
ROM:1D8F ld hl, 0FFFFh
ROM:1D92 call get_gpib_num3
ROM:1D95 jp nz, end_of_string
ROM:1D98 push hl
ROM:1D99 call gpib_check_end_command
ROM:1D9C pop hl
ROM:1D9D jp nz, syntax_error
ROM:1DA0 ld de, calibration_ram
ROM:1DA3 ld bc, (var_temp_word_address)
ROM:1DA7 ex de, hl
ROM:1DA8 add hl, bc
ROM:1DA9 add hl, bc
ROM:1DAA ld (hl), e
ROM:1DAB inc hl
ROM:1DAC ld (hl), d
ROM:1DAD ld a, c
ROM:1DAE cp 0Ch
ROM:1DB0 jr c, loc_1DBA
ROM:1DB2 sub 15h
ROM:1DB4 jr c, loc_1DE0
ROM:1DB6 cp 0Ch
ROM:1DB8 jr nc, loc_1DE0
ROM:1DBA
ROM:1DBA loc_1DBA: ; CODE XREF: ROM:1DB0j
ROM:1DBA cp 3
ROM:1DBC jr nc, loc_1DC5
ROM:1DBE ld a, 0
ROM:1DC0 call set_control_value ; a = { 0: vset, 1: vlim, 2: ilim, 3: itrp }
ROM:1DC3 jr loc_1DE0
ROM:1DC5 ; ---------------------------------------------------------------------------
ROM:1DC5
ROM:1DC5 loc_1DC5: ; CODE XREF: ROM:1DBCj
ROM:1DC5 cp 6
ROM:1DC7 jr nc, loc_1DD0
ROM:1DC9 ld a, 1
ROM:1DCB call set_control_value ; a = { 0: vset, 1: vlim, 2: ilim, 3: itrp }
ROM:1DCE jr loc_1DE0
ROM:1DD0 ; ---------------------------------------------------------------------------
ROM:1DD0
ROM:1DD0 loc_1DD0: ; CODE XREF: ROM:1DC7j
ROM:1DD0 cp 9
ROM:1DD2 jr nc, loc_1DDB
ROM:1DD4 ld a, 2
ROM:1DD6 call set_control_value ; a = { 0: vset, 1: vlim, 2: ilim, 3: itrp }
ROM:1DD9 jr loc_1DE0
ROM:1DDB ; ---------------------------------------------------------------------------
ROM:1DDB
ROM:1DDB loc_1DDB: ; CODE XREF: ROM:1DD2j
ROM:1DDB ld a, 3
ROM:1DDD call set_control_value ; a = { 0: vset, 1: vlim, 2: ilim, 3: itrp }
ROM:1DE0
ROM:1DE0 loc_1DE0: ; CODE XREF: ROM:1DB4j
ROM:1DE0 ; ROM:1DB8j ...
ROM:1DE0 ld c, 0
ROM:1DE2 jp end_of_string
So if my theory is correct, WORD? 0 should return 0x09AC (probably as decimal and possibly in different endianess). WORD 0,256 should change the value at 0x4026 and WORD? 0 after that should return 256. Basically a peek and poke, but only for the calibration memory. If this works, then it would make reverse-engineering the calibration parameters a whole lot faster and save a lot of wear and tear on the EEPROM burner.