Without changing the hardware (via rewiring the IEC connector in the
C64 or using a different connector instead of or in addition to the
IEC connector) you have three basic options.
Kernal Routines
The KERNAL routines for communicating across the IEC bus (TALK
,
TKSA
, ACPTR
, etc.) are relatively slow for several
reasons. First, the routines themselves are not the most efficient.
This is not just code in the routines themselves that's not as
optimized as it could be, but also that you usually have to call
several different routines to do a full bus transaction for an
outgoing request and incoming data and they also do their bus
transactions in a way that's not always the most optimal way it could
be done. And on top of all this there are also the limitations of the
IEC protocol itself.
Custom IEC Protocol Routines
If you write your own routines to talk the IEC protocol, you can
optimize a few things. This will require a deep understanding of the
IEC protocol and probably the IEC communications portion of Commodore
DOS as well. My IEC bus notes provide a brief overview and
lots of references; the best one to start with is probably IEC
disected [sic], particularly the reprint it contains of Jim
Butterfield's article from the July 1983 Compute!, "How the VIC/64
Serial Bus Works." Inside Commodore DOS is a good
reference for the DOS in the 1541 and similar drives, including their
internals.
You need to allocate some sort of "endpoint address" for your code in
the drive so that you can talk to it. I can see three places this
could be done:
- Try and piggyback on the DOS command channel (15) protocol, the one
to which you send commands like
SCRATCH:MYFILE
. This seems overly
difficult and adds the most overhead.
- Use the drive's device number (typically 8 for the first drive),
but repurpose one of the DOS channel numbers for your own
use. Re-using 0, 1 or 15 would break DOS, but you could grab one of
the data communications channels between 2 and 14. That would make
it incompatible with programs trying to use that channel for I/O,
and also might add some overhead for the channel management.
- Allocate a new device number for your code. Adding, say, 12 to the
disk drive's device number would put your device number in the
generally-unused 16-30 range and you can then use any protocol you
like (within IEC limits) to talk to that device without worrying
about interference with other devices.
If you follow this last plan, you can optimize your IEC protocol speed
by making your system a pure command-response system: assert A̅T̅N̅
,
send a talk command to your device followed by the request data,
reverse the transfer direction on the bus, and immediately read the
response from your device. That's about as fast as bidirectional IEC
communications can be done. There are still a couple of issues that
will slow your communications here, though.
The first is that all devices on the bus must read and acknowledge
each byte of the command you send. The timing also dictates that you
must hold each data bit (individual bits are not ack'd, just bytes)
for 20 μs, and you can't reduce this this without potentially losing
other devices on the bus that need to keep up, so you end up being
limited to a transfer rate of a few kilobytes per second for your
outgoing command and data. Even worse, message setup and
acknowledgements have very lose timeouts, up to a second, so a very
slow device on the bus can delay messages drastically, even if you've
programmed the C64 and your device to be able to handle this quickly.
The second is that, without blanking the screen, you are unable to
communicate during the badline interval, a 40 cycle period
occurring every eight raster lines (or even more often if you're using
tricks to get more colors) when the CPU is paused to allow the VIC-II
to read color memory. Data being read from the IEC bus by the C64 is
clocked by the device sending the data and acknowledged only every
eight bits, so if you're not available to read bits between the
acknowledgements they get lost. The standard C64 DOS works around this
by increasing the intra-bit delay to 60 μs, but that slows you down
even further.
The only way around this that I can think of is to make your messages
short enough to be able to complete the transfer between badlines and
time them so that they start with enough time left before the badline
to complete. But even here, you still have the potential issue of
other devices on the bus slowing you down, as described above.
Custom Non-IEC Protocol
If you can guarantee that all the devices on the IEC bus are running
your code (say, by making the user unplug any devices you're not
programming) you've got the flexibility to change to a protocol more
efficient than IEC and without its timing limitations.
Your main set of constraints is the connections of the pins on the IEC
serial port itself. These are described (with a schematic) on page 13
of the C64/C64C Service Manual:
S̅R̅Q̅I̅N̅
: Input only. Connected to the F̅L̅A̅G̅
pin on CIA1 and thus
requires a separate read from all other lines (which are on CIA2),
though it can be programmed to generate an interrupt on the C64.
GND
: Ground. Obviously not usable for I/O.
A̅T̅N̅
: Output only, via CIA2 PA3.
C̅L̅K̅
: I/O via CIA2 PA6 and PA4.
D̅A̅T̅A̅
: I/O via CIA2 PA7 and PA5.
R̅E̅S̅E̅T̅
: Connected to the C64 reset line, and in practice not
usable for communications since asserting it resets the C64.
Supercat came up with the brilliant suggestion of having
the C64 clock the data transfers in to the C64 as well as out from
it; this solves a lot of timing problems, particularly the badline
one. Basically, so long as the device can keep up (which a 1571 ought
to be able to, since it has little it must do but communicate if you
program it appropriately) the C64 can clock data in and out at its
maximum rate, ignoring pauses for badline, interrupts or anything
else; the remote device will simply pause at the same time until the
C64 is ready again.
Doing this, since you have three lines available and need only one
input for the data, you can also do bidirectional data transfers in a
SPI-like way: the other two lines become your clock and data
outputs. (A̅T̅N̅
, being output-only, must be used for either your clock
or your data output; the other line and data input can be assigned to
C̅L̅K̅
and D̅A̅T̅A̅
as you see fit.) The 1 kΩ pull-ups on all those lines
are fast enough that your speed here will be limited only by how fast
you can program the CPU to toggle those lines.
I'd suggest using D̅A̅T̅A̅
for input from the device to the C64; because
it's read via bit 7 of the CIA2 PA register, there are some tricks
you can use to maximize the speed at which you can do
CPU-driven I/O. Not all these tricks apply exactly; for example you
can't use an absolute-addressed INC
of memory to toggle the clock
because you don't have a serial port pin connected to bit 0 of a CIA
parallel register¹, so you'd need to substitute something like an ADC
followed by an STO
. But there's still a lot you can do within the
limitations of your hardware arrangement. The details of this would
probably best be discussed in the 6502.org programming forum,
followed by a question and answer posted here once more of the details
have been hashed out.
I'm not sure how fast you could actually get this going, but with the
badline and similar issues gone, bidirectional data transfers and some
clever programming I'd think you could burst your forty-byte transfers
at around 20 cycles per bit, allowing you to do a transfer in less
than a millisecond, giving you plenty of time during the 16.66 ms
frame to do other work.
¹You could work around this by connecting the drive to the user
port on the C64, but that brings in its own host of issues. Still, it
would probably be possible, with a lot of care, to build a cable that
connects the drive to both and write your software in a way that
maintains compatibility with normal drive usage.