Z80: help identifying an algorithm

Discussion in 'Technical Discussion' started by Andlabs, Oct 27, 2013.

  1. Andlabs

    Andlabs

    「いっきまーす」 Wiki Sysop
    2,175
    0
    0
    Writing my own MD/Genesis sound driver :D
    Code:

    Code (Text):
    1. ROM:198D ; =============== S U B R O U T I N E =======================================
    2. ROM:198D
    3. ROM:198D
    4. ROM:198D sub_198D:                               ; CODE XREF: ROM:1061p
    5. ROM:198D                                         ; sub_1343+17p ...
    6. ROM:198D                 ld      h, 1            ; 100h
    7. ROM:198F                 ld      a, b            ; a = b - c
    8. ROM:1990                 sub     c
    9. ROM:1991                 jr      nc, loc_199E    ; if a xxx 0 goto loc
    10. ROM:1993                 neg                     ; a = -a
    11. ROM:1995                 srl     a               ; a >>= 1 (a /= 2)
    12. ROM:1997                 ld      c, a            ; c = a
    13. ROM:1998                 add     a, b            ; a += b
    14. ROM:1999                 ld      l, a            ; a = (100h)[a]
    15. ROM:199A                 ld      a, (hl)
    16. ROM:199B                 ld      l, c            ; a -= (100h)[c]
    17. ROM:199C                 sub     (hl)            ; alternatively, swap b and c, then do the below
    18. ROM:199D                 ret
    19. ROM:199E ; ---------------------------------------------------------------------------
    20. ROM:199E
    21. ROM:199E loc_199E:                               ; CODE XREF: sub_198D+4j
    22. ROM:199E                 srl     a               ; a >>= 1 (a /= 2)
    23. ROM:19A0                 ld      b, a            ; b = a
    24. ROM:19A1                 add     a, c            ; a += c
    25. ROM:19A2                 ld      l, a            ; a = (100h)[a]
    26. ROM:19A3                 ld      a, (hl)
    27. ROM:19A4                 ld      l, b            ; a -= (100h)[b]
    28. ROM:19A5                 sub     (hl)
    29. ROM:19A6                 ret
    30. ROM:19A6 ; End of function sub_198D
    Data at 100h:

    Code (Text):
    1. ROM:0100                 db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1
    2. ROM:0100                 db 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4
    3. ROM:0100                 db 5, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 9, 9, 9, 0Ah, 0Ah
    4. ROM:0100                 db 0Ah, 0Bh, 0Bh, 0Ch, 0Ch, 0Dh, 0Dh, 0Eh, 0Eh, 0Fh, 0Fh
    5. ROM:0100                 db 10h, 10h, 11h, 11h, 12h, 12h, 13h, 13h, 14h, 14h, 15h
    6. ROM:0100                 db 15h, 16h, 17h, 17h, 18h, 19h, 19h, 1Ah, 1Ah, 1Bh, 1Ch
    7. ROM:0100                 db 1Ch, 1Dh, 1Eh, 1Eh, 1Fh, 20h, 21h, 21h, 22h, 23h, 24h
    8. ROM:0100                 db 24h, 25h, 26h, 27h, 27h, 28h, 29h, 2Ah, 2Bh, 2Bh, 2Ch
    9. ROM:0100                 db 2Dh, 2Eh, 2Fh, 30h, 31h, 31h, 32h, 33h, 34h, 35h, 36h
    10. ROM:0100                 db 37h, 38h, 39h, 3Ah, 3Bh, 3Ch, 3Dh, 3Eh, 3Fh, 40h, 41h
    11. ROM:0100                 db 42h, 43h, 44h, 45h, 46h, 47h, 48h, 49h, 4Ah, 4Bh, 4Ch
    12. ROM:0100                 db 4Dh, 4Eh, 4Fh, 51h, 52h, 53h, 54h, 55h, 56h, 57h, 59h
    13. ROM:0100                 db 5Ah, 5Bh, 5Ch, 5Dh, 5Fh, 60h, 61h, 62h, 64h, 65h, 66h
    14. ROM:0100                 db 67h, 69h, 6Ah, 6Bh, 6Ch, 6Eh, 6Fh, 70h, 72h, 73h, 74h
    15. ROM:0100                 db 76h, 77h, 79h, 7Ah, 7Bh, 7Dh, 7Eh, 7Fh, 81h, 82h, 84h
    16. ROM:0100                 db 85h, 87h, 88h, 8Ah, 8Bh, 8Dh, 8Eh, 90h, 91h, 93h, 94h
    17. ROM:0100                 db 96h, 97h, 99h, 9Ah, 9Ch, 9Dh, 9Fh, 0A0h, 0A2h, 0A4h
    18. ROM:0100                 db 0A5h, 0A7h, 0A9h, 0AAh, 0ACh, 0ADh, 0AFh, 0B1h, 0B2h
    19. ROM:0100                 db 0B4h, 0B6h, 0B7h, 0B9h, 0BBh, 0BDh, 0BEh, 0C0h, 0C2h
    20. ROM:0100                 db 0C4h, 0C5h, 0C7h, 0C9h, 0CBh, 0CCh, 0CEh, 0D0h, 0D2h
    21. ROM:0100                 db 0D4h, 0D5h, 0D7h, 0D9h, 0DBh, 0DDh, 0DFh, 0E1h, 0E2h
    22. ROM:0100                 db 0E4h, 0E6h, 0E8h, 0EAh, 0ECh, 0EEh, 0F0h, 0F2h, 0F4h
    23. ROM:0100                 db 0F6h, 0F8h, 0FAh, 0FCh, 0FEh
    The given function takes b and c as input and produces a as output.

    This is a sound driver, and at this point in the program I come across a structure like

    Code (Text):
    1.     db command_byte
    2.     db unknown_parameter
    3.     dw jump_table_target_1
    4.     dw jump_table_target_2
    5.     dw jump_table_target_3
    6.     ...
    7. jump_table_target_1:
    8.     db command_bytes...
    unknown_parameter is loaded into b and some other value is loaded into c; the result is the index into the jump table.

    I am trying to determine one of the following two things:
    1) Is unknown_parameter the size of the jump table? Or in other words, is b the upper bound of a?
    2) What does this function do? I have no idea...
    Thanks.
     
  2. ValleyBell

    ValleyBell

    Tech Member
    237
    10
    18
    researching SMPS sound drivers
    Something from a sound driver ... interesting.

    Let's begin with the table at 100h: It's a simple lookup table for 256 squared values between 0 and 1.
    You can recreate the table with this code:

    Code (Text):
    1. for (idx = 0x00; idx < 0x100; idx ++)
    2. {
    3.     value = (idx / 256.0) * (idx / 256.0);
    4.     LUT[idx] = floor(value * 256.0);
    5. }
    I'm not sure what prupose the actual function has, but it does this:

    Code (Text):
    1. float sub_198D(float param_b, float param_c)
    2. {
    3.     val_low = abs(param_b - param_c) / 2;
    4.     val_high = (param_b + param_c) / 2;
    5.     return (val_high * val_high) - (val_low * val_low);
    6. }
    (float = register / 256.0 and vice versa.)

    If I would need to guess, I'd say it has something to do with modulation. (Just because squares don't make much sense anywhere except for frequencies.)
     
  3. Andlabs

    Andlabs

    「いっきまーす」 Wiki Sysop
    2,175
    0
    0
    Writing my own MD/Genesis sound driver :D
    All right; trial and error on your float code shows me that yes b is the upper bound, which gives me the answer to 1)... except Ican't reproduce it with the original Z80 code, unless I transcribed the code wrong...?

    I'm also unsure what's going on here, since the c parameter doesn't seem to be set anywhere; there's another function elsewhere that divides it by 2 (it's a 16-bit value and the low byte is used as c here), so I'm assuming there's still a block of code I haven't come across (or it's set subtly).
     
  4. ValleyBell

    ValleyBell

    Tech Member
    237
    10
    18
    researching SMPS sound drivers
    You have indeed a tiny error in your code.
    Code (Text):
    1. if int8(a) >= 0 { goto loc_199E }
    If you set b = 224 and c = 64 you get 224-64 = 160 or 0x90. Treat this as int8 and you get -96.
    The Z80 calculates 224 - 64 and gets 160, but since the calculation doesn't underflow, the carry flag stays at 0. (Your line equals "jr p, loc_199E")
    It checks, if B-C >= 0, that's equal to (C < B).

    For reference, here is the non-float version of my code:
    Code (Text):
    1. Private Function DoFunc(ByVal ParamB As Byte, ByVal ParamC As Byte) As Byte
    2.  
    3.     Dim IdxLow As Byte
    4.     Dim IdxHigh As Byte
    5.    
    6.     If ParamB >= ParamC Then
    7.         ' loc_199E
    8.         IdxLow = (ParamB - ParamC) \ 2
    9.         IdxHigh = IdxLow + ParamC
    10.         'IdxHigh = ParamB/2 + ParamC/2
    11.         'IdxLow  = ParamB/2 - ParamC/2
    12.         DoFunc = ValArr(IdxHigh) - ValArr(IdxLow)
    13.     Else
    14.         IdxLow = (ParamC - ParamB) \ 2
    15.         IdxHigh = IdxLow + ParamB
    16.         'IdxHigh = ParamB/2 + ParamC/2
    17.         'IdxLow  = ParamC/2 - ParamB/2
    18.         DoFunc = ValArr(IdxHigh) - ValArr(IdxLow)
    19.     End If
    20.  
    21. End Function
    (Yes, I often test things in Visual Basic 6. "\" means "integer divide")
     
  5. Andlabs

    Andlabs

    「いっきまーす」 Wiki Sysop
    2,175
    0
    0
    Writing my own MD/Genesis sound driver :D
    Ah okay; carry flag is something I can never remember :argh: Thanks

    I'll leave this question open if anyone wants to try figuring out what mathematical thing this is doing; when I figure out where else this function is used I will update. Thanks again!
     
  6. Andlabs

    Andlabs

    「いっきまーす」 Wiki Sysop
    2,175
    0
    0
    Writing my own MD/Genesis sound driver :D
    I can now tell you the function is mainly used for volume calculations. Here's what seems to happen:

    Code (Text):
    1. For each operator
    2.     x = current operator TL ^ 0x7F
    3.     If the current operator IS an output operator/slot
    4.         x = this function(b = x, c = an external parameter loaded by the 68000; I think it's the global volume and it's 0xFF by default and 0xFF or 0x00 otherwise)
    5.         x = this function(b = x, c = current channel volume (set by an effect command))
    6.     LoadTL(x ^ 0x7F) (to set back to TL)
    My best guess is that this is a linear->log (linear->exponential?) conversion function with built-in max()...