Post by gtoal on Oct 20, 2019 1:38:07 GMT -5
Am I right in thinking that the protocol used with the veclink cable is fixed baud rate synchronous serial?
If that's the case, then would using an asynchronous serial protocol instead be faster? (possibly even
running at the fastest rate the hardware could support?)
When I'm talking about synchronous vs asynchronous here I mean in the style of PDP16 Register Transfer Modules,
which nowadays (in the context of CPU design) is referred to as 'clocked' vs 'unclocked' (or 'self-timed'), eg as in the unclocked ARM designs that Steve Furber designed at Manchester University.
I'm not talking about what is usually meant by synchronous vs asynchronous in the context of serial communications, which refers to a clocked serial bit stream.
In a synchronous serial system the clocks at each end are synchronised (remember Bisync, SDLC?), whereas in an asynchronous serial system the clocks are free running and adjusted on the edge of each bit with a PLL, hence the need for schemes such as Manchester encoding that require sense changes in order to force edge transitions and keep the clocks aligned, so that bits can either be reliably sampled in the middle of the bit (depending on the specific protocol), or determined by the presence or absence of a transition between bits.
So below is my idea for a protocol to do serial I/O the unclocked way - no bit timing needed at all, other than an arbitrarily small delay to ensure that the data bit is already valid whenever the corresponding strobe bit is asserted.
Clearly this is a rough sketch, so please don't get into the weeds of criticising the details - for the purposes of discussing the overall method the details are not critical. I did write some code below to give myself a more concrete framework for discussing this concept, but I'm looking for feedback for the method in general, not for the code which I haven't tried to assemble and I know will need tweaking.
Is this way of communicating between a Vectrex and another Vectrex (or the GPIO pins on a Raspberry Pi etc) worth putting some effort into, to see if it is more robust and/or faster?
(My gut feeling is that a synchronous handshaked protocol is going to be significantly more reliable than an asynchronous one that relies on exact timing and speed. And I'm not sure but I'm hoping it will also be faster.)
G
--
Veclink cable type 2: pins 1, 2, 3 connected at each end, pin 8 is common ground.
Pin 1: data bit
Pin 2: strobe (when toggle detected)
Pin 3: handshake acknowledgement (when toggle detected)
We use toggling to make assertion of signals twice as fast - you do not need to wait until a signal is de-asserted before asserting it again.
(strobe and handshake could be looked on as similar to 'request to send' and 'clear to send' if you prefer)
One wire (button) is used for a data bit, a second wire is used for a strobe bit. Data is latched
at the receiving end when the strobe bit toggles. However we can't expect to change the data bit and strobe bits simultaneously,
because of race conditions in the hardware, so we set the data bit first then update the output pins with the toggled strobe bit later.
Thus strobing occurs on either edge of that pin, whether rising or falling.
When the receiver loads the data bit on the detection of the strobe, it then handshakes back to the
sender by toggling the third wire.
Note that because the cable has only 3 wires, the interface has to be half-duplex with an explicit reversal of direction controlled by higher-level software. I.e. no unexpected asynchronous input can allowed by the basic protocol.
For most master/slave applications this will be fine, but for a symmetric application (ie 2-player game) we need to initially
assign only one of the two participants as a master and doing that arbitrarily may involve a handshake using random numbers and random exponential backoff. But since this will only happen once at startup it may not be a major issue if it takes a second or two.
I'm assuming the existence of a 'write_A_to_Joy2' and 'read_A_from_Joy2', with code similar to that which Malban posted in vectorgaming.proboards.com/thread/1856/accessing-joystick-port-devices#ixzz62rU78rFf where he also notes that Port 1 buttons 1-4 are connected to the four upper bits of Port A of the PSG chip (button 1 to bit 4, button 2 to bit 5, button 3 to bit 6), hence these constants:
G
If that's the case, then would using an asynchronous serial protocol instead be faster? (possibly even
running at the fastest rate the hardware could support?)
When I'm talking about synchronous vs asynchronous here I mean in the style of PDP16 Register Transfer Modules,
which nowadays (in the context of CPU design) is referred to as 'clocked' vs 'unclocked' (or 'self-timed'), eg as in the unclocked ARM designs that Steve Furber designed at Manchester University.
I'm not talking about what is usually meant by synchronous vs asynchronous in the context of serial communications, which refers to a clocked serial bit stream.
In a synchronous serial system the clocks at each end are synchronised (remember Bisync, SDLC?), whereas in an asynchronous serial system the clocks are free running and adjusted on the edge of each bit with a PLL, hence the need for schemes such as Manchester encoding that require sense changes in order to force edge transitions and keep the clocks aligned, so that bits can either be reliably sampled in the middle of the bit (depending on the specific protocol), or determined by the presence or absence of a transition between bits.
So below is my idea for a protocol to do serial I/O the unclocked way - no bit timing needed at all, other than an arbitrarily small delay to ensure that the data bit is already valid whenever the corresponding strobe bit is asserted.
Clearly this is a rough sketch, so please don't get into the weeds of criticising the details - for the purposes of discussing the overall method the details are not critical. I did write some code below to give myself a more concrete framework for discussing this concept, but I'm looking for feedback for the method in general, not for the code which I haven't tried to assemble and I know will need tweaking.
Is this way of communicating between a Vectrex and another Vectrex (or the GPIO pins on a Raspberry Pi etc) worth putting some effort into, to see if it is more robust and/or faster?
(My gut feeling is that a synchronous handshaked protocol is going to be significantly more reliable than an asynchronous one that relies on exact timing and speed. And I'm not sure but I'm hoping it will also be faster.)
G
--
Veclink cable type 2: pins 1, 2, 3 connected at each end, pin 8 is common ground.
Pin 1: data bit
Pin 2: strobe (when toggle detected)
Pin 3: handshake acknowledgement (when toggle detected)
We use toggling to make assertion of signals twice as fast - you do not need to wait until a signal is de-asserted before asserting it again.
(strobe and handshake could be looked on as similar to 'request to send' and 'clear to send' if you prefer)
One wire (button) is used for a data bit, a second wire is used for a strobe bit. Data is latched
at the receiving end when the strobe bit toggles. However we can't expect to change the data bit and strobe bits simultaneously,
because of race conditions in the hardware, so we set the data bit first then update the output pins with the toggled strobe bit later.
Thus strobing occurs on either edge of that pin, whether rising or falling.
When the receiver loads the data bit on the detection of the strobe, it then handshakes back to the
sender by toggling the third wire.
Note that because the cable has only 3 wires, the interface has to be half-duplex with an explicit reversal of direction controlled by higher-level software. I.e. no unexpected asynchronous input can allowed by the basic protocol.
For most master/slave applications this will be fine, but for a symmetric application (ie 2-player game) we need to initially
assign only one of the two participants as a master and doing that arbitrarily may involve a handshake using random numbers and random exponential backoff. But since this will only happen once at startup it may not be a major issue if it takes a second or two.
I'm assuming the existence of a 'write_A_to_Joy2' and 'read_A_from_Joy2', with code similar to that which Malban posted in vectorgaming.proboards.com/thread/1856/accessing-joystick-port-devices#ixzz62rU78rFf where he also notes that Port 1 buttons 1-4 are connected to the four upper bits of Port A of the PSG chip (button 1 to bit 4, button 2 to bit 5, button 3 to bit 6), hence these constants:
#define BUTTON1 16
#define BUTTON2 32
#define BUTTON3 64
#define DATABIT BUTTON1
#define STROBE BUTTON2
#define HANDSHAKE BUTTON3
Init:
Lda #0
Sta TxStrobe ; first strobe sent will be low to high transition
; TxStrobe is either 0 or STROBE
Sta RxStrobe ; first strobe received will be from low to high transition.
; RxStrobe is either 0 or STROBE
Sta Handshake ; first handshake received will be from low to high transition.
; Handshake is either 0 or HANDSHAKE
Jsr write_A_toJoy2 ; ensure our strobe
Jsr read_A_fromJoy2 ; and handshake pins initially 0
Rts
Transmit_byte: ; byte to be sent is in A. Other system must be expecting to read data.
Sta TxData
Bsr t4 ; execute "Transmit_bit(txdata&1); txdata >>= 1;" 8 times
t4: Bsr t2
t2: Bsr transmit_bit
transmit_bit: ; sends lowest bit of txdata and shifts txdata down ready for the next bit
Clr A
Ror TxData ; first bit sent is lowest bit.
; move lowest bit into carry and line up the next bit
Ror A : Ror A ; Carry -> $80 -> $40
(could save two Asl's if data were Button3 instead)
Ror A : Ror A ; -> $20 -> $10 ... A is now either 0 or 16 (DATABIT)
PshS A ; Save data bit. (can optimise later)
Ora TxStrobe ; last (current, unchanged) value of strobe pin must be included
; when we update the buttons bits
Jsr write_A_toJoy2 ; SET PINS FOR (DATABIT OR (previous strobe bit value)
Lda TxStrobe
EorA #STROBE
Sta TxStrobe ; Update toggled strobe so receiver can detect next edge
; and know that data bit is definitely valid
PulS A ; restore the data bit ($00 or $10)
Ora TxStrobe ; once data bit is asserted, assert strobe
; (which was toggled immediately above). ($20 or $30)
Jsr write_A_toJoy2 ; SET PINS FOR (DATABIT OR (toggled strobe bit value))
; i.e. only strobe bit is changing.
wait_for_handshake: ; NOW WAIT FOR THE HANDSHAKE BIT TO BE TOGGLED!
; (when receiver acknowleges that data bit was received)
Jsr read_A_fromJoy2 ; look for change in handshake input pin.
; If it was 0 we wait for a 1, if it was 1 we wait for a 0
EorA RxHandshake ; Bit will be 1 if it changed.
AndA #HANDSHAKE ; Only test that bit.
Beq wait_for_handshake ; 0, so no change. (1 denotes a transition)
Sta RxHandshake ; update current value of handshake pin
; so that we can detect the next transition after sending the next bit
Rts
; ( both systems may run the same code but don't confuse the variables stored on opposite ends of the connection! )
Receive_byte: ; return fetched byte in 'A'.
Bsr r4 ; call 8 times: "rxdata >>= 1; rxdata |= (Receive_bit()<<7)"
r4: Bsr r2 ; 4
r2: Bsr Receive_bit ; 2
receive_bit:
Jsr read_A_fromJoy2
EorA RxStrobe ; detect change in strobe input pin.
; If it was 0, has it changed to 1?, if it was 1, has it changed to 0?
BitA #STROBE ; (non-destructive 'AndA') If the strobe bit has changed,
; that position in A will now be 1.
Beq receive_bit ; 0, so no change. (1 denotes a transition)
; otherwise strobe detected, so we know the data bit loaded
; at the same time was valid
; Extract the data bit from A:
; AndA #DATABIT ; (other bits will be lost, we don't need to mask them off)
Rol A : Rol A ; 16 -> 32 -> 64
Rol A : Rol A ; 64 -> 128 -> Carry. Move data bit into carry
Ror RxData ; Carry -> 128. first bit received will eventually be
; shifted right 8 times into lowest bit pos
Lda RxStrobe
EorA #STROBE
Sta RxStrobe ; toggle and update strobe bit
Lda TxHandshake
EorA #HANDSHAKE ; NOW TOGGLE THE HANDSHAKE WIRE!
Sta TxHandshake
Jsr write_A_toJoy2
; Acknowledge that we received the bit.
Lda RxData ; return result (only on 8th iteration do we care)
Rts
G