Another 6522 SPI question

For discussing the 65xx hardware itself or electronics projects.
User avatar
Dr Jefyll
Posts: 3526
Joined: 11 Dec 2009
Location: Ontario, Canada
Contact:

Re: Another 6522 SPI question

Post by Dr Jefyll »

gfoot wrote:
How about this version - put MOSI on bit 2, with bit 1 being an unconnected output pin, and then:
Whoa -- VERY nice, George! That's about 23% faster than my latest effort (which AFAIK was already in first place compared with anything similar).

And bit1 needn't be wasted and left unconnected -- it's still available for other applications. (This applies to unused bits in my version, too.) If the other application uses the bit as an input then there's no interference from the writes done by the SPI code. And even if the other application does use the bit as an output, it's possible -- depending on the scenario -- that the interference will be tolerable, as long as both applications aren't simultaneously active. (I know Garth gets some extra serious mileage by having umpteen applications on a single VIA port... :) )

So, on the VIA port we have...
  • bit7 is in input mode -- attaches to MISO
  • bit6 is available for other uses, esp. as an input, or it may be chosen as the one to output MOSI
  • [... same ...]
  • bit2 is available for other uses, esp. as an input, or it may be chosen as the one to output MOSI
  • bit1 is available for other uses, esp. as an input
  • bit0 is in output mode -- attaches to Ck
And -- for even more speed :shock: -- your version of the code lends itself very readily to loop unrolling, for a further savings of as much as 3 cycles per SPI bit! For example, here I've unrolled by two:

Code: Select all

SPIBYTEIN:    LDA #$FE        ; for counting, and as a nice supply of set bits
              SEC
INPUTLOOP: 4  STZ VIAPORT_IO  ; Ck=0, mosi=0
           6  ROL VIAPORT_IO  ; set Ck=1, shift MISO into carry
           2  ROL A
           4  STZ VIAPORT_IO  ; Ck=0, mosi=0
           6  ROL VIAPORT_IO  ; set Ck=1, shift MISO into carry
           2  ROL A
           3  BCS INPUTLOOP
That's 13.5 cycles per SPI bit, plus a few cycles for the setup code. And with 8X unrolling it's 12 cycles per SPI bit.

(And knock off 2 more cycles if your I/O is in zero-page, as is mine. I won't dispute that priorities vary -- that's fine -- but for a further 20% increase... )
Quote:
Though I think for SD cards you're meant to be sending set bits - not clear bits - on MOSI when reading data.
You sound a little uncertain about this point (and so am I). Can anyone confirm? In any case, it's true (as you say) that you can avoid the issue. Just load an appropriate value into X or Y and use STX or STY instead of STZ.

-- Jeff
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html
gfoot
Posts: 871
Joined: 09 Jul 2021

Re: Another 6522 SPI question

Post by gfoot »

Dr Jefyll wrote:
And bit1 needn't be wasted and left unconnected -- it's still available for other applications. (This applies to unused bits in my version, too.) If the other application uses the bit as an input then there's no interference from the writes done by the SPI code. And even if the other application does use the bit as an output, it's possible -- depending on the scenario -- that the interference will be tolerable, as long as both applications aren't simultaneously active. (I know Garth gets some extra serious mileage by having umpteen applications on a single VIA port... :) )
The reason I inserted the extra bit there was to ensure that the level on MOSI is held steady while the clock goes high. It's the rising edge that triggers the card to read, I believe, and we need to set up the data level before causing the rising edge. So I reserved that extra bit to hold a dummy copy of MOSI. In your read code this is just zero, but if we were writing data it wouldn't be (but maybe we'd use INC for that instead of ROL).
Quote:
Quote:
Though I think for SD cards you're meant to be sending set bits - not clear bits - on MOSI when reading data.
You sound a little uncertain about this point (and so am I). Can anyone confirm? In any case, it's true (as you say) that you can avoid the issue. Just load an appropriate value into X or Y and use STX or STY instead of STZ.
It's how I've always done it - the main source I learned from was http://elm-chan.org/docs/mmc/mmc_e.html which says (in the "Command and Response" section) "The DI signal must be kept high during read transfer (send a 0xFF and get the received data)."

The official specification is rather redacted in its publicly-available version (https://academy.cba.mit.edu/classes/net ... /SD/SD.pdf) and I had a look but can't find this information in there.

By the way, if you're willing to give up the whole VIA for SD card access, but don't want to use the shift register, then take a look at the implementation I posted in NormalLuser's thread here: viewtopic.php?f=2&t=7840&start=0#p104565 I think it's only 6 cycles per bit to read this way! (with unrolled loops)
User avatar
Dr Jefyll
Posts: 3526
Joined: 11 Dec 2009
Location: Ontario, Canada
Contact:

Re: Another 6522 SPI question

Post by Dr Jefyll »

gfoot wrote:
The reason I inserted the extra bit there was [...] if we were writing data
Hm, okay... My focus was entirely on reading. And yet it'd be nice if there were no conflict with the optimizations for writing. Hm, for writing, it may be best, as you say, to use INC instead of ROL to wiggle the clock (on bit0).
gfoot wrote:
By the way, if you're willing to give up the whole VIA for SD card access, but don't want to use the shift register, then take a look at the implementation I posted in NormalLuser's thread here: viewtopic.php?f=2&t=7840&start=0#p104565 I think it's only 6 cycles per bit to read this way! (with unrolled loops)
Yes, this method and the shift register method are both quite compelling! And both exploit the resources available in a VIA.

The code recently posted in this thread doesn't require any extra resources (handshaking, or a shift register) -- just a bare bones parallel I/O port will suffice. But it's pretty easy to justify including one or more VIA's in one's project, so...

-- Jeff
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html
User avatar
allisonlastname
Posts: 88
Joined: 06 Mar 2023
Location: UK
Contact:

Re: Another 6522 SPI question

Post by allisonlastname »

My design uses the VIA's shift register for data out and a 74HC595 for data in, mainly because I wanted and potentially needed full duplex and didn't want to bother with writing loads of code to bitbang it.

The '595 is accessed through port B, and port A is used for such things as chip selects and other bit IO. I don't even have to use interrupts - I can just watch the shift register bit in the IFR.
probably the youngest person on this forum
BruceRMcF
Posts: 388
Joined: 21 Aug 2019

Re: Another 6522 SPI question

Post by BruceRMcF »

gfoot wrote:
Dr Jefyll wrote:
And bit1 needn't be wasted and left unconnected -- it's still available for other applications. (This applies to unused bits in my version, too.) If the other application uses the bit as an input then there's no interference from the writes done by the SPI code. And even if the other application does use the bit as an output, it's possible -- depending on the scenario -- that the interference will be tolerable, as long as both applications aren't simultaneously active. (I know Garth gets some extra serious mileage by having umpteen applications on a single VIA port... :) )
The reason I inserted the extra bit there was to ensure that the level on MOSI is held steady while the clock goes high. It's the rising edge that triggers the card to read, I believe, and we need to set up the data level before causing the rising edge. ...
Yes, I like that.

If you want to use the shift to output MISO while toggling the clock, and want a SPI_OUT, SPI_IN and SPI_TX, the SPI_TX can have the write byte for the 0 bit in register X and the 1 bit in register Y. If the loop if fully unrolled, a count is not needed, so A can hold the output byte:

Code: Select all

...
    ASL
    BCS +
    STX VIA_PORT ; reset output bit, clock bit
    SEC
    ROL VIA_PORT ; raise clock, read MISO
    ROL
    BCC ++
    BRA +++
+   STY VIA_PORT ; set output bit, reset clock bit
    ROL VIA_PORT ; raise clock, read MISO
    ROL
    BCS +++
++  STX VIA_PORT ; reset output bit, clock bit
    SEC
    ROL VIA_PORT ; raise clock, read MISO
    ROL
    BCC ++
    BRA ++++
+++ ...

If you only have one SPI device to connect to, you can set bit 0 to CLK, bits 1 and 2 to MOSI, with bit 2 connected, so it remains stable through the rotate, bit7 to MISO. Then if the other VIA port is allocated to something, bits 3 and 4 can be allocated to /SPI_SELECT with bit 4 to /SPI_SEL, and bits 5 and 6 are still available for an I2C port or for a second SPI select.

Or if GPIO are in higher demand than handshake lines, CA2 can be set to either low or high output, making a perfectly usable /SPI_SELECT line.
Post Reply