Sunday, 26 January 2014

Sinclair QL Serial


The Sinclair QL serial port can be used for poor man's file transfer between a PC and QL, but as usual the knowledge needed for doing this is not in handy one place. Continuing to celebrate the QL's thirtieth anniversary with some beginner-level material, I'll have a look at practical serial communication between the QL and PC/Mac.

The cable on the QL is end is similar to these puppies, a 6-pin BT.
The pictured plug had to be carved slightly to fit the QL.

First, the cable. The cable is connected to the SER2 on the QL. There are also QL's with 9-pin ports, and the image below depicts the original UK connector. Apparently the 9-pin connectors have a different pin order than a PC RS-232.

A 6-pin BT plug might be difficult to get so the more adventurous hackers might be better off by modifying the connector itself. The plugs appear to be similar to a British telephone standard. Googling for a "British Telecom Plug" ought to reveal sites for buying a cord. It's not a 100% fit, but as long as they have the correct number of pins, the plastic shape may be modified.

A 630W appears to be the exact right model, but I've yet to see anyone sell them new. There are also left-hand and right-hand versions, for example I have a CTL-fitting 9-pin converter which cannot be made to fit the SER port.

I can't imagine how many times I've soldered a cable as a mirror image. The confusing thing is, that here a mirrored cable "sort-of" appears to work, because the transmit pin is in the middle of the 9-pin connector.

This should be correct and the one I use currently. Both connectors are depicted as they are seen from outside the computer.

Connection between the QL and the PC 9-pin serial connectors, as viewed from the outside.
Obviously not to scale! SER2 1:GND,  2:TxD, 3:RxD, 4:DTR, 5:CTS

The next step is to make the computers talk to each other. A terminal program at the PC/Mac end, such as Zterm, may be sufficient for testing the connection. Oh, and your PC/Mac might be one of those new-fangled computers without a 9-pin serial port. So you'll need to have an USB-RS232 adapter.

After all the hassle, on the QL end, you can use a program like this for scanning incoming text:

10 BAUD 2400
20 OPEN #8,ser2
30 I$=INKEY$(#8)
40 PRINT I$;
50 GOTO 20

With a terminal set to 2400 on the PC/Mac end, you can start talking between the computers. Of course it's not very reliable, but some of text typed in the terminal ought to show on the QL screen. You can test the reception of the PC end by writing PRINT #8,"Hello!" on the QL.

File transfer


After testing the connection it's time to move to more interesting topics.

QL BASIC files can be transferred to PC via serial:

SAVE ser2

With a terminal using the same speed setting, you can grab the basic listing from the terminal window, and maybe copy/paste it to a text editor for storage. To send files to QL, your terminal needs to have functionality for sending files in plain ASCII, terminated with character code 26.

LOAD ser2z 

The QL will wait for a BASIC listing to be sent. (The 'z' is there to indicate that the terminating character will be expected.)

But how to send binaries and raw data?

LBYTES ser2,131072

...makes the QL expect raw data, which will be placed from address 131072 onwards (the screen memory). But before the actual data, there has to be a header, otherwise the QL can't know how long the file will be. (The code 26 cannot be used as a terminator, as the data itself might contain the value.)

From the PC end, headers can be examined by viewing the incoming serial data in numeric format. (Not usually possible in a standard terminal.) Data can be sent to the PC by using SBYTES ser2,131072,16 (for example). There are apparently more complicated headers, but this is the simplest way and the QL does not seem to use anything else for the SBYTES.

Header:

[255]
[length highest byte]
[length]
[length]
[length lowest byte]
[0]
[0]
[0]
[0]
[0]
[0]
[0]
[0]
[0]
[0]
[byte x length of actual data...]

So, sending a file with a length of 16 bytes would mean sending this data over the serial:

255,0,0,0,16,0,0,0,0,0,0,0,0,0,0,11,22,43,54,25,64,17,108,19,20,44,22,13,11,10,6

(The 16 bytes here are just bogus numbers)

QL loader


Below is a small Processing source for sending files to the QL. As a simple terminal is handy enough for receiving BASIC files, I did not bother with reception functionality. Copy and paste the source into your Processing editor and run it from there.

It might be used for building some kind of file repository for QL. The program assumes you have the required file stored in the Processing folder for the program. (In this case, QLDATA.BIN and/or QLBASIC.BAS)

Use the b key to send BASIC files and s for sending binary data. The BASIC file should be a plain text ASCII file.

It ought to be obvious from the source how to add or change your own filenames. With binary, I'd recommend loading to screen memory for testing. This way you'll get an immediate visual response as the screen fills with data. Executable code has to be run with CALL command.

Sadly, 2400 baud seems to be the fastest reliable speed for loading directly. At least I could load 32k and 48 k files without any apparent errors.

The source below is very rudimentary, for brevity's sake. The serial port selection is quite crude. Refer to your configuration and Processing reference for the serial library to get best results.

Note: I've found the serial to be sometimes more prone to errors, especially when loading longer BASIC files. I've sometimes used a few millisecond delay between characters. Also, sometimes resetting the QL helps clear problems, and is recommended if the loading fails. Sometimes it seems the computer gets up on the wrong foot.




import processing.serial.*;
Serial myPort;

void setup()
{
  String portName = Serial.list()[0];
  println(Serial.list());
  myPort = new Serial(this, Serial.list()[0], 2400,'N',8,1.0);
  println("At the QL end:");
  println("BAUD 2400");
  println("LBYTES ser2,address [for binary]");
  println("LOAD ser2z [for basic]");
  println("-------");
  println("Here, activate the window and");
  println("Press s to send binary");
  println("Press b to send basic");
}

void sersend(int b)
{
      print(b+".");
      myPort.write(b); 
}

void bin_send(String fname)
{
  byte bas[] = loadBytes(fname);
  int lenni,lea,leb,lec,a,iad;
  lenni=bas.length;
  lenni--;
  println("LENGTH:"+lenni);
  
  lea=lenni/65536;
  leb=(lenni-(lea*65536))/256;
  lec=(lenni-((lea*65536)+(leb*256)));
  sersend(255);
  sersend(0);
  sersend(lea);
  sersend(leb);
  sersend(lec);
  for(int n=1;n<=10;n++){
    sersend(0);
  }
  for(int i=0;i<=lenni;i++){
    a=int(bas[i]);
    sersend(a);
  }
}

void bas_send(String fname)
{
    byte bas[] = loadBytes(fname);
    int lenni=bas.length;
    int a,iad;
    lenni--;
    for(int i=0;i<=lenni;i++){
      a=int(bas[i]);
      sersend(a);
//the delay may help if the QL does not behave
//delay(5) 

    }
  sersend(26);
}

void keyPressed()
{
    if(key=='s'){bin_send("QLDATA.BIN");}
    if(key=='b'){bas_send("QLBASIC.BAS");}
}

void draw()
{
}


3 comments:

  1. That particular 6-pin connector is not exactly a BT plug, but close enough :) After some carving it fits nicely. I have no idea what the cables were in reality, but to find them at Bebek was a veeery lucky coincidence...

    ReplyDelete
    Replies
    1. Indeedy! I altered the text to remind of this.

      Without those cables, I'd probably broken my QL :)

      Delete
  2. I've a problem that my Ser2 on my QL (with Hermes) send every minute 2 bytes on the other side.

    Why? ;)

    ReplyDelete