Here’s a log of it missing a character:
Code
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Write C0n1=20
[:sl1:grappler] Output data 20
[:sl1:grappler] Read C0n2
[:sl1:grappler] Write C0n2=20
[:sl1:grappler] Output /STROBE=0
[:sl1:grappler] Clearing acknowledge latch
[:sl1:grappler] BUSY=1
[:sl1:grappler] Output /STROBE=0
[:sl1:grappler] BUSY=0
[:sl1:grappler] Read C0n4
[:sl1:grappler] Write C0n4=20
[:sl1:grappler] Output /STROBE=1
[:sl1:grappler] Output /STROBE=1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Write C0n1=20
[:sl1:grappler] Output data 20
[:sl1:grappler] Read C0n2
[:sl1:grappler] Write C0n2=20
[:sl1:grappler] Output /STROBE=0
[:sl1:grappler] Previous data not acknowledged
[:sl1:grappler] Output /STROBE=0
[:sl1:grappler] Read C0n4
[:sl1:grappler] Write C0n4=20
[:sl1:grappler] Output /STROBE=1
[:sl1:grappler] Output /STROBE=1
[:sl1:grappler] /ACK=0
[:sl1:grappler] Set acknowledge latch
[:sl1:grappler] BUSY=1
[:sl1:grappler] BUSY=0
[:sl1:grappler] /ACK=1
It writes C0n2 to assert /STROBE, and the acknowledge latch is cleared in a callback from the scheduler. There are no intervening reads of C0n1 where it could mistakenly still see the acknowledge latch still set from the previous byte (this could happen if the timeslice wasn’t aborted fast enough). It doesn’t wait for the acknowledge latch to be set before sending the next byte. The acknowledge after the second byte comes very quickly because it’s actually the acknowledge for the first byte, and the second byte is lost.

Now look at a more normal pair of bytes:
Code
[:sl1:grappler] Write C0n1=20
[:sl1:grappler] Output data 20
[:sl1:grappler] Read C0n2
[:sl1:grappler] Write C0n2=20
[:sl1:grappler] Output /STROBE=0
[:sl1:grappler] Clearing acknowledge latch
[:sl1:grappler] BUSY=1
[:sl1:grappler] Output /STROBE=0
[:sl1:grappler] Read C0n4
[:sl1:grappler] Write C0n4=20
[:sl1:grappler] Output /STROBE=1
[:sl1:grappler] Output /STROBE=1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] /ACK=0
[:sl1:grappler] Set acknowledge latch
[:sl1:grappler] BUSY=0
[:sl1:grappler] /ACK=1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Write C0n1=20
[:sl1:grappler] Output data 20
[:sl1:grappler] Read C0n2
[:sl1:grappler] Write C0n2=20
[:sl1:grappler] Output /STROBE=0
[:sl1:grappler] Clearing acknowledge latch
[:sl1:grappler] BUSY=1
[:sl1:grappler] Output /STROBE=0
[:sl1:grappler] Read C0n4
[:sl1:grappler] Write C0n4=20
[:sl1:grappler] Output /STROBE=1
[:sl1:grappler] Output /STROBE=1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] Read C0n1
[:sl1:grappler] /ACK=0
[:sl1:grappler] Set acknowledge latch
[:sl1:grappler] BUSY=0
[:sl1:grappler] /ACK=1
See how it sits in a polling loop waiting for the acknowledge latch to be set before it continues with the next byte?