1

I'm interested in the limiting speed of I/O on 8-bit computers.

Setting aside the case where parallel cables are used, like the IEEE 488 bus on the Commodore PET; such cables are expensive, which is why they were not used on subsequent Commodore machines. Assuming serial I/O.

Clearly there is some upper limit on the physically possible bandwidth that a single copper wire can handle, but I think 8-bit systems typically did not come close to that, but were limited by what the electronics at each end could handle.

It would be helpful to have a chip like the 6526 CIA, that could take a byte from the CPU and send it over a serial port one bit at a time. Say you don't have such a chip, and the CPU must supply one bit at a time.

It would be tempting to estimate speed with a calculation like, count the number of cycles for LSR; STA 1234 on a 6502, or the equivalent on Z80, and divide into the clock speed. But I'm sure it's not that simple, and there are complications that make the real speed lower. For example, how do you tell the exact length of a long run of zeroes? Is that what stop bits are for?

An example is the Spectrum tape I/O. The ULA has just one pin marked TAPE, and it doesn't have any dedicated I/O chips like the CIA, so it is presumably a case of the CPU having to drive one bit at a time. How fast can it go? That question was asked here: What is the maximum BAUD rate for ZX Spectrum tapes?

It seems the answer is disappointingly fuzzy, being limited in practice by the mechanical properties of the tapes, but the electronics can handle up to 27kpbs.

Is that representative? Does that mean a serial cable connecting two 3.5 MHz Z80 computers could transfer up to 27kpbs? Just what is the limiting factor in that scenario?

8
  • 4
    If you want high speed (by the definition of the 8-bit era) you wouldn't bit-bang you would use a UART. Direct bit-by-bit output was certainly possible but the problem is you would use up so much of your CPU bandwidth doing the serial I/O that you would have little left for other stuff. 19.2 kbps was quite common and a quick search indicates that upwards of 100 kbps was possible depending on the UART and the CPU clock speed. Commented May 26 at 15:57
  • 1
    I would wager that the bandwidth of copper cables was never the limiting factor for the bit-rate of short-distance serial links used by home computers in the 8-bit era. Commented May 26 at 16:09
  • 1
    That question is basically asking for the maximum speed of an 8-bit CPU. Such a limit is in no way different from any CPU, so there could "easily" exist GHz-8-bit-CPUs - As you're asking for bit-banging, the limit is how fast the CPU can drive that single pin.
    – tofro
    Commented May 26 at 16:12
  • 1
    For example, how do you tell the exact length of a long run of zeroes? Is that what stop bits are for? Ordinary serial I/O is just data in, data out. The only changes are to add start bit(s), parity bit (if used) and stop bit(s). The only calculation or variance from the actual data or standard things is possibly parity (odd or even; mark and space are static). That is different from some more sophisticated signal types. Commented May 26 at 16:40
  • 4
    I'm not so sure that parallel interfaces fell out of favour (not only on 8-bit machines, but across the board) because cables were 'expensive'. Rather, at some point deskewing N signal paths limits the achievable speed.
    – dave
    Commented May 26 at 20:19

5 Answers 5

9

Well there is no simple answer.

If you assume that the CPU runs undisturbed, you can just count the cycles and that's how many systems already handle their tape storage, you just seem to want higher speeds than used for tape.

The problem is, on many systems, the CPU does not run undisturbed. Many systems have things that take away time from your code, such as timer interrupts, DRAM memory refresh, any DMA that puts the CPU on hold while some other chip does something - like on C64, there is a thing called "bad video lines", where every 8th video line the VIC-II video chip tranfers a lot of data into it's internal buffers.

Which makes cycle-counting rather useless, as you might not know how much time passed elsewhere to compensate for the loop length. Which is why data rates are very low and maybe synchronized to these system features so that they are not a problem, or a timer chip of some sort is used for sending the data pulses or at least keeping track of clock cycles so the CPU just asks the clock cycle count from the timer to keep the generated data stream in sync.

Also you seem to be talking about asynchronous serial transmission, like what UARTs do. They frame the data bytea with start and stop bits, so you are in sync with each byte. So in reality, you would not just send out an endless stream of data bits with no info how to make sense of it.

Another mechanism is to of course send data synchronously. For example if you use clock and data wires, it does not matter how irregular the clock is, as long as the receiver can detect each clock pulse and capture the data.

Another mechanism for synchronous data is already used by the tape interfaces, which use pulse width to define data bit, and so each pulse is one bit and lenth defines if it was a zero bit or a one bit. There are also other line codes such as sending the data using Manchester line coding, and other simple codes of the era.

The tape interfaces are also different between machines. A C64 has digital interface to tape drive, with the analog interface in the Datassette. It should be possible to connect two C64s from their cassette ports and communicate serially. Some other computers had their analog interface inside the computer so you only get e.g Mic and Speaker interfacea to a standard audio tape drive, which offers less bandwidth, and the analog interface cannot be bypassed likr on C64. Yet connecting two such computers through their Mic and Speaker ports should be doable, as long as their voltage levels are compatible - often tape drives used much lower voltages for mic input than for speaker output so it may not be possible if voltage levels don't match.

So anyway, whatever method you use between devices, the tolerances of all the "cycle eaters" need to be taken into account, so that you transmit slowy enough that any lost cycles have only small effect and the receiving device can capture the data even if it has it's own "cycle eaters".

2
  • 2
    "It should be possible to connect two C64s from their cassette ports and communicate serially." Not only should be possible, but was sometimes done. But there was never much software to do it very well at all. Commented May 26 at 18:56
  • This and Jerry Coffin's answers are both good, but I'm marking this as the accepted one, because I think it hits the nail on the head: There are few practical systems that don't have some cycle eaters, and if you don't have a dedicated I/O chip, that's going to be the limiting factor on the reliably achievable speed.
    – rwallace
    Commented May 27 at 2:25
6

Hmm...at least offhand, it seems like you need around 4 instructions per bit and a Z80 needs at least 4 T cycles per instruction. Along with that you need to load the next byte from memory once every 8 bits (and that load needs to access memory, so you're looking at 8 T cycles, if memory serves).

Let's assume something like a normal serial protocol, with one start bit and one stop bit per byte, so a total of 10 bits transmitted for each byte of data.

So that's something like 168 clocks to send one byte of data. At 3.5 MHz, that would be 3.5M/168 = 20,833 kilobytes per second (note:kilobytes, not kilobits).

On a realistic system, it's going to be somewhat lower than that. If we're presuming a system designed to a low enough price point that it can't use a UART for this task, we can probably figure it's going to insert at least 1 wait state for every memory (and probably I/O) access, so something like 25% slower as a minimum. Maybe more than that.

I do think it's worth keeping in mind, however, that bit-banging wasn't really the norm for serial ports. You seem to be drawing your conclusion primarily from the tape port of the ZX-Spectrum. I think you're taking this as meaning a lot more than it really does though, for at least two reasons.

  1. Cassette tapes were very low bandwidth by nature. Even at 1200 bits per second, reliability was often fairly poor. Adding a shift register wouldn't have improved speed.
  2. The ZX-Spectrum was designed to about the lowest price-point possible at the time. Even the Commodore 64 (itself designed to a low price point) included a couple of CIA chips, each of which included a shift register that could transmit/receive data at 500 Kbps (and even the VIA (predecessor of the CIA) had a similar shift register.
6

There are so* many misconceptions in that question, it's hard to know where to start.

The issue with IEEE-488 was absolutely nothing to do with it being parallel, and everything to do with it being a very complicated interface. It was intended as a shared bus for many devices to communicate. For I/O where the micro is the master and all the devices are slaves, it is massively overkill, especially in an era where processing was expensive.

Parallel cables remained the best solution for high-bandwidth devices until the early 2000s when USB2 became available. Printers were widely available with Centronics parallel ports until then, because USB1 was slower. It was only with USB2 that the computer industry got a serial interface which genuinely could replace parallel.

The reason serial was used, in all cases, was for situations where faster speeds simply weren't necessary. Tape drives were limited by how fast you could get information off a tape, which was always limited by the bandwidth of an audio tape. Longer-distance links over modems were limited by the bandwidth of an audio phone link, which was even worse. Mice only needed to update as fast as you moved them. Keyboards only needed to update as fast as you typed.

Sure you can work out upper limits if you want. But if you want to start talking real scenarios, you need to know more about the electronics and computing infrastructure of the era. And this isn't a quick answer to a SE post.

4

On the 6502, a lot depends upon the placement of the bits in whatever I/O register is being used or--for reception--if one bypasses the use of an I/O register entirely. The fastest reception from a synchronous serial data source could be achieved by having code execute from a ROM with an address bit tied to the serial input, which is the approach used for the Apple I tape interface (despite the fact that it didn't need to run at maximum speed). Using this approach, and a training sequence before each block, would allow a burst transfer speed of two cycles per bit, or an average speed of about three cycles per bit.

Using I/O registers, the best choice on the 6502 is to use bit 7 for reading and either bit 0 or bit 7 for writing, with other written bits being "don't care". To read, assuming transmitter and receiver are pre-synchronized, use eight repetitions of the sequence:

CPX INPORT ; Have X hold 127
ROL ; or ROR

to write, use eight repetitions of:

STA OUTPORT
LSR ; or ASL

Either approach would take 6 cycles per bit, plus turnaround time between bytes. Placing the bits anywhere else on the I/O ports would effectively require conditional branching based upon the bits being exchanged, adding an extra 4 cycles per bit.

Note that in considering the relative performance of serial and parallel interface, it's also important to consider the relative performance of unipolar and differential signaling. In the 8-bit era, most communication was done by having wires switch between two ground-relative voltages; this meant that the signal and ground wires would together act like an antenna. Preventing unwanted RF emissions required the use of filtering that limited switching speeds. Fast serial buses like USB, by contrast, have a pair of twisted wires that always switch (during high speed communication) in opposition to each other, so that whenever one is swinging high the other will be swinging low, and vice versa. The improvement in signal quality and switching speeds more than makes up for the need to send eight bits for each byte. I'm not sure how much extra silicon would have been required to have an I/O chip include a differential transceiver, but even one that was fairly simplistic by 1970s-1980s technological standards could have offered a useful performance boost.

1

Bit-banging by processor would be the exception, rather than the rule; 8 bit shift registers weren't that rare or expensive. (But bit banging the tape port was fairly common, as this could be done faster than the 300-1500 baud of cassette tapes).

For most processors, you would need to:

  1. load byte to accumulator
  2. increment pointer (if there wasn't a load & accumulate)
  3. write accumulator to serial port
  4. see if done
  5. go back to 1

so in cycles, that's going to vary. 6502 was 2-3 cycles/nstruction, and 8080 was 4-17, iirc. most of these (except 4) should be at the lower end. [hmm, I'm assuming that there's a single instruction to use X&Y as 16 bit on the 6502 ]

  1. could be broken into a) decrement a location or counting register b) branch if zero

so call that, at a minimum six instructions to tosss a bite to tart or shift register.

And to keep numbers round, call that 17 and 33 cycles on 6502 and 8080. so at 1 & 2 MHz, 60kbyte/second on either processor.

But that's assuming a dedicated, tight loop with the processor not on call for interrupts or anything else. And probably add a few cycles for needing 16 bit rather than 8 bit counters, among other things.

If you were really going to bit bang, you'd need another short instruction to shift the accumulator, and then decrement, and then branch if zero (to count down the eight bits)--and that's per bit, rather than byte, so divide the above rate by 10 or so, to 6k bytes.

but then again (bit banging), you have about 3 extra bits for timing/pairty, and testing for those conditions, so roughly half it again, so about 3kbytes/s.

The 1802 had direct from memory to IO port, including increment, as a 2 cycle instruction. But it would still have to loop, so three 2 cycle instructions (for the simple case).

But at 2mhz, and 8 clocks/cycle, only 250k cycles/second, so 40k as opposed to 60k bytes/scond.

These seem to line up with real world results, with the Apple ][ serial card having a cap of 19.6 baud.

flash ahead a decade or two into the late 90s, and you'd find that a linux box that tapped out at 56k could do twice that running DOS--as DOS was in a tight loop, and linux was running an operating system while it was at it.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .