Page 1 of 2

6522 handshaking questions

Posted: Sat Jul 29, 2023 9:45 pm
by Dan Moos
Trying to debug some communication between the 6502, 6522, and a microcontroler.

The 65C22 datasheet is, in my opinion, extremely poorly written. As such, I want to confirm what I THINK is how it works.

If in the PCR register of the 65c22 I set the CA2 control bits to 100 (handshake output),my understanding is that basicallyu I am in control of CA1, and CA2 is controlled by the VIA.

So when reading FROM the VIA, my peripheral sets CA1 to DATA READY, I read the port, and the VIA automatically generates the the DATA TAKEN signal on CA2

And when writing TO my peripheral through the VIA, I write the data to the VIA, I set CA1 for DATA READY, and when I write to the VIA, it automatically sends the DATA TAKEN signal on CA2

Am I reading this correctly?

Re: 6522 handshaking questions

Posted: Sat Jul 29, 2023 10:58 pm
by GARTHWILSON
Dan Moos wrote:
The 65C22 datasheet is, in my opinion, extremely poorly written. As such, I want to confirm what I THINK is how it works.
What may not be clear enough is that CA1 is always an input, regardless of transfer mode.  CA2 will be the output when in the handshake modes.  Beyond that, I think Figures 2-1 and 2-2 in the data sheet are pretty clear.  Otherwise, sometimes it helps to look at the Rockwell data sheets (also on this site) which seem to be the most complete.  I keep the last of the Synertek data books nearby and have all my notes in that one.

Quote:
So when reading FROM the VIA,

It looks like you mean the peripheral is writing TO the VIA, and the 6502 is reading it.  Is that right?

Quote:
my peripheral sets CA1 to DATA READY, I read the port, and the VIA automatically generates the the DATA TAKEN signal on CA2

I've used nearly every part of the VIA, many times, in different ways, but never this handshake mode, so I cannot comment from experience; but I believe you have this correct.

Quote:
And when writing TO my peripheral through the VIA, I write the data to the VIA, I set CA1 for DATA READY, and when I write to the VIA, it automatically sends the DATA TAKEN signal on CA2

Again, CA1 is always an input, never an output.  AIUI, the VIA automatically puts CA2 low to indicate new data are available, and the receiving end puts CA1 low momentarily to say, "Thanks.  Got it," which causes the VIA to raise CA2 until it has the next byte ready to go.

Re: 6522 handshaking questions

Posted: Sun Jul 30, 2023 12:14 am
by Dan Moos
First, from what I can tell, you correctly understand what I'm doing.

Here's my process, including where it seems to fail...
Context: I am running my SD card DOS on an AVR microcontroller. Lets say I want to send a DIR command.

User types DIR on computer. I have assigned a numeric code to each DOS command. For "DIR", it's '0'. Also, PORTD on the AVR is connected to the handshake lines.

Computer triggers and interrupt on the AVR (using the 6522)
MCU responds by putting 0x06 (ack) on PORTA of the VIA. After writing to PORTA, MCU asserts DATA READY on CA2, CPU does NOT check CA2 here. Relevant?
CPU loops until it reads 0x06 from VIA PORTA
MCU loops until it sees DATA TAKEN on CA2 . (scope confirms CA2 toggles)

Ok, here's where things seem to derail. At this point, I'm trying to send the code '0' from the CPU to the MCU to relay that "DIR" has been typed

The MCU loops, waiting for DATA READY to be asserted on CA2. When it gets this, DDRA on MCU is set to all inputs.
CPU sets DDRA on VIA to all outputs. This signal never happens. (per my scope)

I write "0" to VIA PORTA. I never see the CA2 line asserted by the VIA signifying I wrote this data to the VIA. My loop hangs.

Amy thoughts?

Re: 6522 handshaking questions

Posted: Sun Jul 30, 2023 1:44 am
by GARTHWILSON
Dan Moos wrote:
After writing to PORTA, MCU asserts DATA READY on CA2, CPU does NOT check CA2 here. Relevant?
Again, the VIA's control input in this case is CA1, not CA2.

I'll try to check the rest when I get more time.  It's possible there's a small difference between manufacturers' versions like I tell about regarding the shift register at viewtopic.php?p=2323#p2323 in the Tip of the Day column.

Re: 6522 handshaking questions

Posted: Sun Jul 30, 2023 4:08 am
by Dan Moos
That was a mistake in my post. I do indeed send DATA READY on CA1 there.

I believe I only ever write to CA1.

Re: 6522 handshaking questions

Posted: Sun Jul 30, 2023 5:44 am
by gfoot
As Garth said, CA1 is only an input to the computer, while CA2 can be configured as a kind of input or output. You can't assign one to "data ready" and one to "data available" and have it work in both directions - the pins' roles must swap when the data direction changes. It will require some coordination and agreement between the devices if you want to change the direction in the middle of communication.

The basic operation is very simple - when your computer is transmitting data to your peripheral, the VIA will send a pulse or handshake on CA2 after you write data to port A - I believe this happens as soon as PHI2 falls. And you get an interrupt if the peripheral signals you via CA1, so you know you can send the next byte of data. For handshake mode, the VIA keeps CA2 low until it sees that response on CA1; in pulse mode it's just low for one clock cycle I think, so for that mode your peripheral needs to respond to the edge of the signal, e.g. via an interrupt, rather than polling its level. I too have only used pulse mode personally.

When you're reading data, the peripheral will need to set up the data it's sending, then send a signal on CA1 so that your computer will get an interrupt and know data is available, and the CPU will then read the data from the port, and the VIA will automatically send a pulse or handshake on CA2 to let the peripheral know the computer read the data. Again, the handshake is like the pulse but is held low until the VIA sees a response on CA1; and again I've only used pulse modes myself so don't speak first-hand about how handshaking mode works.

So what the VIA is doing is very simple, and is essentially the same in both cases - when data is read or written, it initiates a pulse or handshake; and if it's a handshake, it is cleared when it sees the response on CA1. Either way, you'll also configure CA1 to generate interrupts, and that's how the CPU knows when it is time to read or write the next byte of data.

On specific points from your last post:
Dan Moos wrote:
Computer triggers and interrupt on the AVR (using the 6522)
It's not clear to me what you mean by this, is it happening through some other means that what we're discussing here, e.g. an output pin on port B?
Quote:
MCU responds by putting 0x06 (ack) on PORTA of the VIA. After writing to PORTA, MCU asserts DATA READY on CA2, CPU does NOT check CA2 here. Relevant?
As noted, the MCU needs to send an edge on CA1 if it wants to interrupt the computer - not CA2. The CPU cannot really "check" either CA1 or CA2, there is no facility for that in the VIA - the only way to "read" these pins is to notice interrupts are pending.

I'd also avoid thinking of it in terms of "DATA READY" and "DATA TAKEN", due to the different meanings for reads and writes, but instead think of CA2 as "CPU READY" and edges on CA1 meaning "MCU READY". The VIA sets CA2 low when the CPU is ready for the MCU to read or write its next byte; and the MCU sends an edge on CA1 when it is ready for the CPU to do the same.
Quote:
CPU loops until it reads 0x06 from VIA PORTA
So usually it would just get an interrupt, determine it was from CA1 on the VIA, and read the data. If you do want to poll though then I think you can read the IFR from the VIA in your loop and wait for the CA1 bit to get set. This tells you an edge was seen, not how many edges or what the current level of CA1 is.

You certainly don't want to sit looping reading port A looking for a specific value while in handshaking mode - every read will cause a signal to be sent to the microcontroller. Try to execute only one read to consume the byte, and then the microcontroller will get correctly notified that you read the data.
Quote:
MCU loops until it sees DATA TAKEN on CA2 . (scope confirms CA2 toggles)
Yes this should be sent automatically if you've configured the VIA correctly.
Quote:
Ok, here's where things seem to derail. At this point, I'm trying to send the code '0' from the CPU to the MCU to relay that "DIR" has been typed
You need the MCU and computer to both know that they're switching communication direction and you need to be quite careful. Normally after the CPU reads a byte, the MCU would see the CA2 pulse and send the next byte, including signalling via CA1 that this had happened. But there is no next byte, so there will be no CA1 signal. In handshaking mode, CA2 will stay low. The MCU also can't just try to read a byte straight away as the low state on CA2 is only signifying that the CPU has read the previous byte - not that it has sent the next one.

I think the MCU ought to send an extra pulse on CA1 to clear the CA2 state, and the CPU needs to wait for this extra interrupt before sending its first data byte. Then the MCU can wait for CA2 to fall again, before reading the first byte sent by the CPU, and send CA1 pulses after reading the byte; so if there are more bytes to send the CPU can keep doing so each time it receives an interrupt from the MCU via CA1.

This only covers transitioning from CPU reading to CPU writing - I think you need to also think carefully about how the opposite transition would take place.
Quote:
The MCU loops, waiting for DATA READY to be asserted on CA2. When it gets this, DDRA on MCU is set to all inputs.
CPU sets DDRA on VIA to all outputs. This signal never happens. (per my scope)

I write "0" to VIA PORTA. I never see the CA2 line asserted by the VIA signifying I wrote this data to the VIA. My loop hangs.

Amy thoughts?
I think it would help to see the 6502 code you're using here.

Re: 6522 handshaking questions

Posted: Sun Jul 30, 2023 6:39 am
by GARTHWILSON
What I gather is that in both reading and writing cases, CA1 is the input and CA2 is the output; so there's no need for either them or the AVR's corresponding bits to change directions to go from writing to reading and vice-versa. I haven't tried it, but the following should work if something isn't escaping my attention (which is quite possible).  The 'xxxx' in the third line is for whatever separate behavior you want the CB1 & CB2 lines, if any.  Hopefully your screen won't wrap the longish lines.

Code: Select all

    LDA  #FFH
    STA  VIA_DDRA    ; Start by making PA all outputs.
                     ; We'll just poll the IFR, rather than setting up & handling interrupts.
    LDA  #xxxx1000B  ; CA2 ctrl is for PA handshake output, and CA1 for neg active edge input.
    STA  VIA_PCR     ; What I don't know is if CA2 is init'd as high to start, so the AVR will
                     ; see the falling edge to indicate when the first byte is available.
    STZ  VIA_PA      ; Send your 'DIR' command (0).  1 cycle later, CA2 puts out a low signal.

    LDA  #2          ; Load the mask for BIT, below, to detect only the CA1 active edge.
    BEGIN
       BIT  VIA_IFR  ; Test the IFR, without affecting the data in the accumulator.
    UNTIL_NOT_ZERO   ; When it falls through, it means the AVR took the data.
                     ; The AVR should give only a short pulse on CA1, then return it high.
    BIT  VIA_PA      ; Read PA to clear the CA1 IFR bit, but leave accumulator intact.
    STZ  VIA_DDRA    ; Turn PA from all outputs to all inputs to avoid bus contention
                     ; when the AVR sends back a byte.
    BEGIN            ; (The accumulator still has the 2 in it from above.)
       BIT  VIA_IFR  ; Test the IFR again, this time for "data available" status.
    UNTIL_NOT_ZERO   ; When it falls through, it means there are data available.
    LDA  PA          ; Fetch the data.  This will lower the CA2 output until after
                     ; the AVR brings CA1 high again.

I see gfoot posted while I was writing.  His idea of using the pulse output on CA2 may be a good one.

Re: 6522 handshaking questions

Posted: Sun Jul 30, 2023 10:50 am
by gfoot
I think that's the opposite direction but a good illustration. The only thing I'd be cautious about is "BIT VIA_PA" as in addition to clearing the interrupt this will also send a new pulse/handshake on CA2 which might not be expected by the peripheral, especially if it generates an interrupt at that end - you'd need to decide the protocol for this transition between sending and receiving, and figure out whether that's useful or not. I think I can imagine writing the peripheral code either way around, and if the peripheral is faster than the computer then this extra handshake could be useful if you move it after setting VIA_DDRA, so that it becomes a handshake for the change in bus direction, signalling that it's now OK for the peripheral to output to the bus. This extra handshake should be terminated when the peripheral writes its first response byte and pulses CA1.

Re: 6522 handshaking questions

Posted: Sun Jul 30, 2023 3:57 pm
by GARTHWILSON
gfoot wrote:
The only thing I'd be cautious about is "BIT VIA_PA" as in addition to clearing the interrupt this will also send a new pulse/handshake on CA2 which might not be expected by the peripheral, especially if it generates an interrupt at that end
Ah yes; use the VIA's register 15 rather than 1.  15 avoids the handshake.

Auto handshaking in 6522

Posted: Sat Aug 05, 2023 9:50 pm
by Dan Moos
PCR set to "handshake" for CA2

If the peripheral sends a "data ready" signal on CA1, and the cpu reads ORA causing CA2 to be asserted, what causes CA2 to revert back to its original state? My assumption had been it does this when I reset the CA1 line, but it appears to be happening roughly 11 to 20 cpu clocks after it is asserted by the VIA.

I am not using interrupts, but rather the CPU is polling the flag register of the 6522

Also, I'm a little confused about the normal state of the CA2 line. If I manually set it "LOW" in my initialization code, does that mean it should go "HIGH" when the via signals "DATA TAKEN", and the opposite if its initialized "HIGH"? I'm sure I explained that poorly. Put it another way, if I want CA2 to be active "high", do I initialize at low, and vice versa? I can't find any info on this aspect in either the WDC or the Rockwell datasheets.

Re: Auto handshaking in 6522

Posted: Sat Aug 05, 2023 10:26 pm
by gfoot
Dan Moos wrote:
PCR set to "handshake" for CA2

If the peripheral sends a "data ready" signal on CA1, and the cpu reads ORA causing CA2 to be asserted, what causes CA2 to revert back to its original state? My assumption had been it does this when I reset the CA1 line, but it appears to be happening roughly 11 to 20 cpu clocks after it is asserted by the VIA.
I don't think it's when CA1 is "reset", I think it's when the next active transition happens on CA1, i.e. when the peripheral signals that the next byte is ready. It sounds though like that's also not what's happening in your case, if it's being reset earlier than that. It is what the datasheet shows though, and confirms in the "timing" section near the end - however, I notice that it also shows CA1 being returned to the high state before the read operation takes place - perhaps it is expecting the peripheral to only send a pulse on CA1. The timing section of the datasheet doesn't mention that aspect, and it seems counter-intuitive to me - I would expect it to tolerate CA1 remaining low for a long period, and just respond to its next high-to-low transition.

It might be worth looking at older datasheets from other manufacturers.
Quote:
Also, I'm a little confused about the normal state of the CA2 line. If I manually set it "LOW" in my initialization code, does that mean it should go "HIGH" when the via signals "DATA TAKEN", and the opposite if its initialized "HIGH"? I'm sure I explained that poorly. Put it another way, if I want CA2 to be active "high", do I initialize at low, and vice versa? I can't find any info on this aspect in either the WDC or the Rockwell datasheets.
I don't think you can control CA2's polarity in this regard. You can either set it high, or set it low, set pulse mode, set handshake mode, or set it as an interrupt input a bit like CA1. When you configure it for handshake mode, it doesn't matter how it was previously configured - it's in handshake mode now.

Re: 6522 handshaking questions

Posted: Sat Aug 05, 2023 10:53 pm
by GARTHWILSON
Dan, I merged your two topics since they were about the same thing, to keep it all together.

Re: 6522 handshaking questions

Posted: Sat Aug 05, 2023 10:56 pm
by Dan Moos
Thanks. I usually start a new one because I worry that my new question will get lost in the shuffle if I don't make it a topic. I'll stop doing that :)

Re: 6522 handshaking questions

Posted: Sat Aug 05, 2023 11:17 pm
by GARTHWILSON
Dan Moos wrote:
Thanks. I usually start a new one because I worry that my new question will get lost in the shuffle if I don't make it a topic. I'll stop doing that :)
Any new reply will bring it to the top, no matter how old it is.  Further, if/when email notifications are working, those who have replied earlier will get a notification that there's a new reply.

Re: 6522 handshaking questions

Posted: Wed Aug 09, 2023 10:17 pm
by gfoot
I happened to be playing with a 6522 today so I tried out the handshaking mode and can confirm it works how we thought. With the PCR set to $80 (CA2 = handshake, CA1 = falling edge interrupt), on either a read or a write operation the 6522 brings CA2 low until the next falling edge on CA1, when it goes high again. I verified on a scope that rising edges on CA2 always came at the same time as falling edges on CA1 - I never saw it raise CA2 other than when the peripheral had sent a falling edge on CA1, and holding CA1 low for long periods was also fine even if it overlapped with the next transaction - the 6522 is only looking at the edges, as expected.

That said, I don't really understand the point of the handshake mode - it feels like the idea is to allow the peripheral to respond to the level of the signal rather than its edges, but I can't see a good way to do that without introducing race conditions. In practice I think you still need your peripheral to respond to falling edges of CA2, and so it seems to me the durations of the low periods don't matter at all and you might as well just use pulse mode...

Here's the code in case it's useful for reference:

Code: Select all

    lda #$00 : sta VIA_DDRB
    lda #$08 : sta VIA_PCR
    lda #$02 : sta VIA_IFR
    lda #$82 : sta VIA_IER

startwait:
    bit VIA_PORTB
    bpl startwait

    ; Send a byte to start
    ldx #$ff : stx VIA_DDRA
    ldx #0 : stx VIA_PORTA
    stx DEBUGPORT

    ; Wait for receiver to consume the byte
    jsr waitca1

    ; Send another
    inx : stx VIA_PORTA
    stx DEBUGPORT
    jsr waitca1

    inx : stx DEBUGPORT

    ; Peripheral consumed both bytes - switch to receive mode
    ldx #0 : stx VIA_DDRA
    bit VIA_PORTA ; send a handshake to confirm we're ready to receive

    ; Wait for a byte, read it and display it
    jsr waitca1
    ldx VIA_PORTA : stx DEBUGPORT

    ; And another one
    jsr waitca1
    ldx VIA_PORTA : stx DEBUGPORT

    ; Now we switch back into write mode - wait for another handshake from the peripheral
    ; to tell us it's switched to read mode
    jsr waitca1

    ; Then switch to write mode and send a final byte
    ldx #$ff : stx VIA_DDRA
    ldx #$2a : stx VIA_PORTA
    jsr waitca1

stop:
    bra stop

    ; Wait for peripheral
waitca1:
    lda #2   ; CA1's bit
waitca1loop:
    bit VIA_IFR
    beq waitca1loop
    rts
And the Arduino code it talks to for further reference:

Code: Select all

  Serial.begin(9600);
  
  DDRF = 0;

  pinMode(pin_ca1, OUTPUT);
  pinMode(pin_ca2, INPUT);

  digitalWrite(pin_ca1, HIGH);

  // Wait for byte
  while (digitalRead(pin_ca2) == HIGH);
  int data1 = PINF;
  digitalWrite(pin_ca1, LOW);
  //while (digitalRead(pin_ca2) == LOW);
  digitalWrite(pin_ca1, HIGH);

  Serial.println(data1);
  delay(1000);

  // And another
  while (digitalRead(pin_ca2) == HIGH);
  int data2 = PINF;
  digitalWrite(pin_ca1, LOW);
  //while (digitalRead(pin_ca2) == LOW);
  digitalWrite(pin_ca1, HIGH);

  Serial.println(data2);
  delay(1000);

  if (data1 == 0 && data2 == 1)
  {
    // Wait for another handshake then switch to send mode
    while (digitalRead(pin_ca2) == HIGH);
    DDRF = 0xff;

    // Send a byte
    PORTF = 0x47;
    digitalWrite(pin_ca1, LOW);
    digitalWrite(pin_ca1, HIGH);

    Serial.println("Send 0x47");
    delay(1000);

    // Wait for the handshake
    while (digitalRead(pin_ca2) == HIGH);

    // Send another
    PORTF = 0x72;
    digitalWrite(pin_ca1, LOW);
    digitalWrite(pin_ca1, HIGH);

    Serial.println("Send 0x72");

    // Wait for the handshake
    while (digitalRead(pin_ca2) == HIGH);

    // Now we switch back into read mode, sending a handshake to say we're ready
    DDRF = 0;
    digitalWrite(pin_ca1, LOW);
    digitalWrite(pin_ca1, HIGH);

    Serial.println("Ready to receive again");
    delay(1000);
    
    // Read a final byte
    while (digitalRead(pin_ca2) == HIGH);
    int data3 = PINF;
    digitalWrite(pin_ca1, LOW);
    //while (digitalRead(pin_ca2) == LOW);
    digitalWrite(pin_ca1, HIGH);

    Serial.println(data3);
    delay(1000);
  }