20

The most common conventions for indicating newline nowadays are CR+LF (most famously Windows, but also the Internet standard) and bare LF (Unix and Unix-like systems). Historically there have been other conventions – for example some systems used bare CR instead of bare LF (Commodore 64, Apple II, classic Mac OS, among others).

But one particular convention I wanted to ask about is the unusual LF+CR, which according to Wikipedia's Newline article was used by "Acorn BBC and RISC OS spooled text output". Somewhat confusingly, it also lists "Acorn BBC" (by which I assume they really mean Acorn MOS, since not all 8-bit Acorn machines were branded "BBC") as using bare CR, implying Acorn MOS used bare CR for some purposes and LF+CR for others.

Why LF+CR when almost everyone else used CR+LF? Was it just a random decision or were there reasons behind it?

And is it true Acorn used CR in some cases and LF+CR in others? If so, why?

11
  • 4
    Maybe the important part is "spooled output": It's supposed to go to a printer, and early printers don't really care if you do CR LF or LF CR. So it looks like it's less a general line-ending convention (that seems to be just CR for Acorn), but more of a conversion.
    – dirkt
    Commented Apr 27 at 6:20
  • 2
    @dirkt Actually, it's not only printers that wouldn't care - most terminals as well. So, maybe the answer is "because it didn't matter"?
    – tofro
    Commented Apr 27 at 7:04
  • 2
    @dirkt I found out the hard way that it does matter for some printers - I had a 110 baud teletype; a CR would send the carriage back left but it would take a little time and the LF would take up enough time for the action to complete. LF/CR would appear to work but the first character of the next line would get printed while the carriage was still in motion.
    – Frog
    Commented Apr 27 at 8:31
  • 1
    IMO LF first is better otherwise the varied crud that has built up around the print head of the ubiquitous Epson DMP smears the freshly printed line. See also retrocomputing.stackexchange.com/questions/14169/…
    – Lou Knee
    Commented Apr 27 at 12:53
  • 5
    LF+CR is actually what typewriters used to do. When using the lever to return the carriage to the left (which is, after all, where CR came from), the first action of the lever was to rotate the roller so that the paper "scrolled up". Then, continued pressure on the lever pushed the carriage to the left. So Acorn is the only one that got it right :-)
    – paxdiablo
    Commented Apr 27 at 23:43

1 Answer 1

26

It's a consequence of author Sophie Wilson's neat work in squashing the Machine Operating System (MOS) code to save its precious ROM space and get it as fast as possible. The CR/LF sequence would need more ROM space and be slightly slower.

To write a character to the current stream, programs call the 'OS write character' function OSWRCH at address &FFEE. This just writes the byte in A while preserving all 6502 registers (A, X, Y). At &FFEE there's just an indirect jump through the WRCHV vector to the actual routine.

To write an ASCII character, there's a separate function called OSASCI at address FFE3. This writes the byte in A while preserving all 6502 registers, like OSWRCH, but it also expands an ASCII Carriage Return (CR) into the LF/CR sequence.

By using the LF/CR sequence, the OSASCI routine can flow into the OSWRCH routine. The actual software in the MOS is as follows:

FFE3    OSASCI    CMP     #&0D       ;Do an OSWRCH unless this is a CR.
FFE5              BNE     &FFEE
FFE7              LDA     #&0A       ;Got a CR. Use OSWRCH to write LF first.
FFE9              JSR     FFEE
FFEC              LDA     #&0D       ;Now fall into OSWRCH to write CR and exit.
FFEE    OSWRCH    JMP     (&020E)    ;Jump through vector to routine.

(This routine had already been used in their preceding Acorn Atom computer, at different ROM call addresses.)

Because A must be preserved, doing the LF first allows the fall into OSWRCH to occur with the A holding the original called value of &0D (CR).

If the routine tried to do CR/LF, it would need to be larger to call OSWRCH twice then restore A to the original call value. It'd be something like this:

FFDF    OSASCI    CMP     #&0D       ;Do an OSWRCH unless this is a CR.
FFE1              BNE     &FFEE
FFE3              JSR     FFEE       ;Got a CR. Use OSWRCH to it first.   
FFE6              LDA     #&0A       ;Now use OSWRCH to write the LF second.
FFE8              JSR     FFEE
FFEB              LDA     #&0D       ;Restore A and exit.
FFED              RTS
FFEE    OSWRCH    JMP     (&020E)    ;Jump through vector to routine.

Because a JSR to OSWRCH is needed then an RTS, the routine is 4 bytes longer and is slower.

With a CPU that was already slow, though relatively swift for its market at that time, its important to shave off wasted cycles wherever possible on routines that get called repetitively. Here it's a small part of the overall routine. But all the little slow bits add up to a slow system. A goal of Sophie and of the Acorn team was to get their software speed up, that's apparent in the work put into their BASIC and OS software to make it fast.

In the days when they took this decision, it didn't cause anyone problems. The bytes would only be going to their screen routines, to a printer or staying inside BBC Micro/Master/Atom files used at most by other BBC Micros/Masters/Atoms.

I first noticed this in the mid-80s, when reading the BBC Micro Advanced User Guide then disassembling the MOS. It struck me then as a neat way of trimming the routine down and getting the speed up a little.

3
  • An interesting thing to note about this code is that if line breaks were indicating using a bare LF, this approach would output the CR first. I find it interesting in retrospect that the idea that CR should advance the paper seems to have predated the idea that LF should reset the carriage, even though being able to just reset the carriage is probably more often useful than being able to just advance the paper.
    – supercat
    Commented Apr 29 at 17:25
  • Actaully, looking at the code again, I think I misspoke: while swapping all occurrences of CR and LF would yield code that would output CR+LF in response to the Unix-style bare LF, the code could be two bytes shorter if the character which was received in the accumulator were the one that was output first.
    – supercat
    Commented Apr 29 at 19:50
  • 3
    Great analysis. Like it.
    – Raffzahn
    Commented Apr 29 at 22:29

You must log in to answer this question.

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