DottyMatrix: ghost of the typewriter and the curse of the carriage return

In designing my DottyMatrix serial-to-parallel printer interface, I had to decide how it would handle the incoming data stream.

The purpose of the DottyMatrix is to connect my venerable Epson MX-80 F/T III dot matrix printer to (probably) a Raspberry Pi so that I can print out text files, such as program listings. Obviously, it would be good if it had additional applications, but that’s the main reason for doing it.

The way these interfaces would work, back in the day, is that the host computer would send data from its parallel port with the rate of data flow being governed by handshaking signals – principally, the /ACK and BUSY signals managed by the printer. Even an early 1980s microcomputer, such as my beloved BBC Micro, could spit out characters faster than the chuntering MX-80 could print them.

The manual for the Epson says that its interface is capable of handling 1,000 characters per second, but the maximum speed for the print head is 80 characters a second. The printer probably has some kind of buffer (I could find no reference to one in the manual, though), but it would be small.

In my setup, the Raspberry Pi will send data to the DottyMatrix over a 19,200 baud serial connection. I’m planning to use very basic handshaking in the form of a CTS (clear to send) line managed by the DottyMatrix. When this is high, it’s okay to send data; when it’s low, the RPi should wait and check again in a moment.

But how is that data to be packaged? It could just be in discrete lumps. The DottyMatrix code currently has a 255-character buffer, of which the final byte must always be a null (0).

I’ve opted for a line-oriented approach in which it’s assumed that each packet of data is one line of text. The MX-80 is an 80-character printer, or 132 characters in condensed mode. That means 254 characters should easily accommodate all the printable characters plus a healthy dose of non-printing control characters, such as those use to select underlining, bold etc.

The issue, though, is how to treat carriage returns and linefeeds.

We can trace the origins of these commands – and the fact that there’s two of them – to the typewriter. When you get to the end of the line when typing you press the return bar which does two things – it rolls the platen to advance the paper one line (linefeed, or LF); and if you keep the pressure on you push the carriage back over to the right, returning it to its starting position (carriage return, or CR).

ASR-33 Teletype. On display at the Musée Bolo, EPFL, Lausanne.

This mechanism – suitably electrified – was carried over into one of the earliest computer peripherals, the teletype or teleprinter. This was basically an electric typewriter (often a modified IBM Selectric) that also interacted with the computer as well as printing the I/O session on paper.

Windows still shows this legacy, generally using both a carriage return (ASCII 13, or 0x0D) and a linefeed (10, or 0x0A) to designate the end of a line. But I started this project thinking purely in terms of a Raspberry Pi running Linux. And in the *nix world it’s common to use just a linefeed as the end-of-line designator.

I’ve coded serial comms routines before and have always used linefeeds as terminators because, in the context of what I was doing, it was convenient and effective. So this time I did the same without thinking.

But it needn’t be this way. I could just get the RPi to send the data in 254-byte lumps, with any carriage returns or linefeeds falling where they may. So one packet of data might be the end of one line and the beginning of the next. And this would continue until all the data is sent.

I went back and forth on this quite a bit but, in the end, came down in favour of the line-based approach. Partly this is because of some other applications I have in mind for this device.

In effect, the host is expected to send one line’s worth of text at a time. This string should be terminated with a null. If a carriage return and linefeed are wanted at the end of the line – which they mostly will be – the host should ensure the next-to-last byte is either a carriage return or a linefeed.

Why one or the other? Because of the way I decided to code this. Here’s a snippet.

switch(byte) {
    case 0: // string termination. Buffer is now ready.
        printBuf[buf_index] = byte; // add null termination to string
        buf_ready = true;
        break;
    case 10: // newline 0x0A - also a string termination
        printBuf[buf_index] = byte; // keep newline in string
        if(buf_index < PRINT_BUF_LEN - 2) {
            // there are still at least 2 bytes left in buffer
            printBuf[buf_index + 1] = 13; // add a carriage return
            printBuf[buf_index + 2] = 0; // also add null termination
        } else {
            printBuf[buf_index + 1] = 0; // we're at penultimate byte - last one has to be null
        }
        buf_ready = true;
        break;
    case 13: // carriage return 0x0D - also a string termination
        printBuf[buf_index] = byte; // keep CR in string
        if(buf_index < PRINT_BUF_LEN - 2) {
            // there are still at least 2 bytes left in buffer
            if(autoLF) {
                // the printer will add the LF
                printBuf[buf_index + 1] = 0; 
            } else {
                printBuf[buf_index + 1] = 10; // add a linefeed
                printBuf[buf_index + 2] = 0;  // also add null termination
            }
        } else {
            printBuf[buf_index + 1] = 0; // at penultimate byte - next one must be null
        }
        buf_ready = true;
        break;
    default:
        printBuf[buf_index] = byte;
        // check on buffer length
        if(buf_index == PRINT_BUF_LEN - 2) {
            // We're at the penultimate element in the array.
            // The next one must be a terminator.
            printBuf[buf_index + 1] = 0;
            buf_ready = true;
        } else {
            buf_index++;
        }
        break;
}

We’re reading data from the serial port and putting each byte into a buffer. The buf_ready boolean is used to check whether we need to keep iterating. When it gets set to true, we stop reading bytes and the buffer is considered ready to print.

As soon as we encounter a null, a LF or a CR, we stop. But in the case of LF or CR, the other one is also added to the buffer.

The reason I’ve done it this way is that there’s no predicting whether the CR or the LF will come first, even assuming they’ve both been sent. This way I don’t have to keep track. Each LF is treated as a LF+CR and each CR is treated as a CR+LF. And both are treated as terminators.

Once the buffer is ready, any remaining data on the serial line is flushed. If the host has sent both a CR and LF (in that order), then the LF will still be in the serial buffer – and vice versa. Also, if the data has been sent programmatically, it’s likely to still have a null byte in the buffer.

This approach means that the null byte isn’t necessary. I can send data to the DottyMatrix from a terminal, turning it into a somewhat slow typewriter.

Anyway, it’s working, and I can always change the firmware later, so I’m happy with this approach for now.

4 thoughts on “DottyMatrix: ghost of the typewriter and the curse of the carriage return

  1. Lenore Underwood

    Hi,

    I’m working on a parallel port emulator/printer buffer for the Atari 8bit machines. I was doing google searches for any projects dealing with parallel ports and ATMega chips when I came across your blog posts on the Dotty Matrix project. So far I’ve kinda settled on the ATMEGA 128 mcu with XMEM expansion so I can offer a printer buffer of upto 64k. The connection will be serial in from the Atari SIO port and then of course data buffering and eventual parallel output through the parallel port to a dot matrix printer. I hope to add two basic features to clear the buffer and to repeat printing of the buffer.

    I’m still in the early phases of gathering information and parts. I have a basic ATMEGA 128 Development board coming in and am designing a plugin board for the external ram, Atari SIO connection and printer interface.

    Do you plan to offer a comprehensive schematic/source files download when you finish the project? Obviously my project would be different, different hardware and interfacing, but it would help me to have a guide to extrapolate from for at least the parallel port printer interfacing basics.

    Thank you,

    Reply
    1. Machina Post author

      I may offer schematics etc – if it works! But it would be strictly on the basis that I’m a self-taught amateur, so use any of my stuff at your own risk…

      Reply
      1. Lenore Underwood

        I’ve blown up a few things in my time…. er eh I’ve had several learning experiences in my time. 🙂

        Have you completed the PI connection yet? Or is it still in emulation mode through the Mac?

        Reply
        1. Machina Post author

          I’ve started on the Pi part – some Python test code. Alas, I have many projects and am easily distracted by shiny things…

          Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.