I believe I have deciphered how the "3D" tables in SBECs work. The cal I was working with was a 91 T3, which should be in the files section. I'm not positive this is the *only* new table type, or that this is the only look up function, but it's at least one new one. I have not yet looked at any other cals.
I will share what I've learned.
The table processing function seems to be at D418, and it is called in about 5 places in the code that I can see so far. It usually seems to be used as a "modifier" on the output of other tables. In other words a call is made to an 8-bit table lookup, and the output of that table in register A is passed straight through to the 3D lookup. The B register is usually loaded with a value from the RAM area, and is presumably the "control".
The table handles both signed and unsigned data by indicating the type in the third header byte. It really only makes a small difference to the math.
Input to table address are in A and B and are values in the range 0-FF. These values are scaled into the width and height address range of the table and then used to look up the first byte. The fractional value of the table column address is retained and used to interpolate between this first byte and the next byte in the row. The last byte in a row will never be addressed directly, it's there strictly for the interpolation, which is why you see the width being DECremented in places. The last row in the table serves a similar purpose. The process is done a second time at the same column position, but in the next row down. The two column values derived are then interpolated between against the fractional Y address, and this value is returned in A.
The data format is pretty simple:
BYTE 0: Height, number of rows (Y dimension)
BYTE 1: Width, number of columns (X dimension)
BYTE 2: 0 = unsigned data, 80 (Bit 7 set) = signed data
BYTE 3: Width bytes data
BYTE 3+ (Width *1): Width bytes data
BYTE 3+ (Width *2): Width bytes data
...
BYTE (3 + Width * Height - 1): Last byte of data
What follows is the commented disassembly of the lookup function:
; 3D Table Lookup
; Inputs:
; A = 0-FF Y-Axis/Row address
; B = 0-FF X-Axis/Column address
; Output:
; A = Interpolated value from table
; Default is unsigned data
D418 18 CE D4 9B LD418: ldY #$D49B
D41C 1F 02 FF 04 brclr 2, X, #%11111111, LD424
; Signed data interpolation
D420 18 CE D4 97 ldY #$D497
; Making room on the stack for local variables
D424 18 3C LD424 pushY
D426 36 pushA
D427 36 pushA
; Get the number of columns in a row
D428 A6 01 ldaA 1, X
; Decrement because last byte is for interpolation
D42A 4A decA
; Scale input X value into table X-axis (A), save the fractional part (B)
for later
D42B 3D mul
; A = column number, B = fractional column value for interpolation
D42C 36 pushA
D42D 37 pushB
; Setup Y to be our frame for local variables on the stack
D42E 18 30 tSY
; Get input row index A value
D430 18 A6 02 ldaA 2, Y
; First table byte is number rows in table
D433 E6 00 ldaB 0, X
; Decrement because last row is for interolation only
D435 5A decB
D436 3D mul
; A = row number, B = fractional row value for interpolation
; Store the fraction for later
D437 18 E7 03 staB 3, Y
; Get number of columns in table, store for later
D43A E6 01 ldaB 1, X
D43C 18 E7 02 staB 2, Y
; Multiply row number by the width of a row to get byte-offset into table
D43F 3D mul
; Bump up X pointer past header to start of data
D440 08 incX
D441 08 incX
D442 08 incX
; Add offset into table to X
D443 3A aBX
; If offset is less than 0x100 (IE high byte = 0) then skip next bit
D444 4D tstA
D445 27 04 beq LD44B
; Add 100H to table offset pointer
; Note that this effectively limits the table to 0x1ff entries
D447 08 incX
D448 C6 FF ldaB #$FF
D44A 3A aBX
; Get interpolated column position
D44B 18 E6 01 LD44B ldaB 1, Y
; Add to table offset
D44E 3A aBX
; Get fractional portion of column address
D44F 18 E6 00 ldaB 0, Y
; We need Y for some calcs ahead, save it's value on the stack
D452 18 3C pushY
; Load the pointer to the interpolation entry point chosen at the beginning
D454 18 EE 04 ldY 4, Y
; Get the first data byte indexed from the table
D457 A6 00 ldaA 0, X
; Subtract the next byte in the row
D459 A0 01 subA 1, X
; Call the interpolator
D45B 18 AD 00 call 0, Y
; A contains the return value
; Restore local variable frame
D45E 18 38 popY
; Store interpolated table X value
D460 18 A7 01 staA 1, Y
; Get number of columns per row
D463 18 E6 02 ldaB 2, Y
; Advance table offset pointer to next row, same column
D466 3A aBX
; Get fractional column address from stack
D467 33 popB
; Save local variable frame pointer
D468 18 3C pushY
; Get interpolation function pointer
D46A 18 EE 04 ldY 4, Y
; Interpolate the value between the two points
D46D A6 00 ldaA 0, X
D46F A0 01 subA 1, X
D471 18 AD 00 call 0, Y
; A contains the interpolated value
; Restore local variable frame
D474 18 38 popY
; X now contains the local variable frame pointer
; This is done because the interpolation function expects
; X to point to the two point values, which for this last
; operation are stored on the stack in our local variable area
D476 30 tSX
; Store the row+1 interpolated value
D477 A7 01 staA 1, X
; Get the row address fraction
D479 E6 02 ldaB 2, X
; Get the interpolation function pointer
D47B 1A EE 03 ldY 3, X
; Perform a final interpolation of the values from the two rows
D47E A6 00 ldaA 0, X
D480 A0 01 subA 1, X
D482 18 AD 00 call 0, Y
; Clean up stack frame
D485 33 popB
D486 38 popX
D487 38 popX
; Return value is A
D488 39 ret
; Appears to be dead code? No references to it I can find
D489 E6 00 LD489: ldaB 0, X
D48B 5A decB
D48C 3D mul
D48D 37 pushB
D48E 16 tAB
D48F 5C incB
D490 3A aBX
D491 EC 00 ldD 0, X
D493 10 sBA
D494 33 popB
D495 20 04 jr LD49B
; This function interpolates the absolute value of the
; difference between two points
;
; Inputs:
; A = Delta between two points
; B = Fractional value 0-FF
; X = Pointer to first point
; Carry Flag = Result of Point1 - Point2
;
; Outputs:
; A = interpolated value
; Interpolation function, signed math entry point
; Branch if first byte was less than second
D497 2D 0A LD497: blt LD4A3
D499 20 02 jr LD49D
; Interpolation function, unsigned math entry point
; Branch if first byte was less than second
D49B 25 06 LD49B bcs LD4A3
; First byte was bigger than second
; Multiply delta x fraction
D49D 3D LD49D mul
; Round up
D49E 89 00 adcA #$00
; Negate result so that value subtracts from first byte
D4A0 40 negA
D4A1 20 04 jr LD4A7
; Second byte was bigger than first
; Get abs value of the delta
D4A3 40 LD4A3 negA
; Interpolate
D4A4 3D mul
; Round up
D4A5 89 00 adcA #$00
; Add the base value to the interpolated delta value
D4A7 AB 00 LD4A7 addA 0, X
; A returns the interpolated value
D4A9 39 ret