Decode my I2C Stream!
GNU sed, 427 378 364 + 1 = 428 379 365 bytes

+1 byte for -r flag. Takes colon-separated input. Output format is e.g. a0x16oRd0xB5, where a precedes the address, o precedes the operation (R or W), and d precedes the data.

Why do I do this to myself?



s/^/:/
:Q
s/:.+:$|::./E/
s/:(.).(.*):_-/\1:x\2:x/
s/:./:/g
tQ
s/(.{7})(.)(.)(.*)(.):/#\3\1%\2#\5\4:/
/#-|E/{z;iinvalid
q}
s/#./#/g
:R
s/[:%]/!u0;%0123456789ABCDEF,/
:A
s/((-)|_)!/!\2/
s/!-(u+)(.+);/!\1\2;\1/
:
/u%/!bZ
s/F;/;0/
t
s/(.);(.*%.*\1)(.)/\3\2\3/
s/u;/u1/
s/u(u*)%/;\1%/
b
:Z
s/!(u+)/!\1\1/
/#!/!bA
s/#!u+(.+);.+,/\1/
/:/bR
s/(..)(.)/a0x\1o\2d0x/
y/-_/RW/


Try it online!

Explanation

The code works in two phases. In phase 1 we turn the two signals into a single binary string. This is the easy part.

# Prepend a `:`
s/^/:/
:Q
  # Check for length mismatch (one string exhausted before the other)
  s/:.+:$|::./E/

  # If the first two characters of SCK are `_-`, record first character of SDA
  # and discard second; replace each pair with `x` so we can unconditionally...
  s/:(.).(.*):_-/\1:x\2:x/

  # ...consume a character from each signal
  s/:./:/g

  # If we did a substitution, branch to :Q
  tQ


If this was our input:


--_____--___-----___--___--___-----__---____--____:___--_-_--_--_-_--_-_--_-_--_-_--_-_--_-___-_--__-



...we now have this:


__-_--_-_-_--_-_-_:



Before phase 2, we reformat this to make it a little easier to work with:

# Reformat `.......ma........b:` to `#a.......%m#b........:`
s/(.{7})(.)(.)(.*)(.):/#\3\1%\2#\5\4:/

# Check if ACK high or length mismatch; if either, print error and quit
/#-|E/{z;iinvalid
q}

# Delete ACK bits
s/#./#/g


Now we have:


#__-_--_%-#-_--_-_-:



In phase 2, we convert each run of _ and - (0 and 1) to a hexadecimal number. Because sed doesn't do math, this is almost half of the code. It actually takes each binary digit, converts it to unary, and then increments the hexadecimal number once for each unary digit.

:R
# Initialize: u = magnitude (unary 1); 0 = running sum (decimal); lookup table
s/[:%]/!u0;%0123456789ABCDEF,/

:A
  # Take the least-significant bit; if it's 0 drop it
  s/((-)|_)!/!\2/

  # If 1 (-), remove it and copy current magnitude (`u+`) after `;`
  s/!-(u+)(.+);/!\1\2;\1/

  # Increment number for each `u` after `;`
  # e.g. 12;uu -&gt; 13uu -&gt; 13;u -&gt; 14u -&gt; 14;
  :
    # No more `u`s, branch to :Z
    /u%/!bZ

    # Carry: 1F;u -&gt; 1;0u -&gt; 20u -&gt; 20;
    s/F;/;0/
    t

    s/(.);(.*%.*\1)(.)/\3\2\3/
    s/u;/u1/

    # Drop a `u`; branch to `:`
    s/u(u*)%/;\1%/
    b

  :Z
    # Double magnitude: !uu3 -&gt; !uuuu3
    s/!(u+)/!\1\1/
    # If there are binary digits left, branch to :A
    /#!/!bA

# Clean up
s/#!u+(.+);.+,/\1/

# Branch to :R for the second number
/:/bR

# Format output
s/(..)(.)/a0x\1o\2d0x/
y/-_/RW/


Before formatting, we have:


16-B5



All that remains is to add a prefix to each hexadecimal number change the operation (- or _) to read (R) or write (W), giving us:


a0x16oRd0xB5