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

Edit: Of course it's 50 GOTO 30, as pointed out in the comments :)

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()
{
}


Friday, 17 January 2014

QL Networking

Happy 30th Sinclair QL!

But what's better than a Sinclair QL? Two Sinclair QLs, of course! Here's my noob's experience of connecting two Sinclair QLs with the NET connectors.


The service and user manuals are a bit silent about what kind of cable is used for connecting the computers. Even the internet was a bit unhelpful. It almost started to seem like the best kept non-secret on the topic of Sinclair machines. In the end I just looked at an auction photograph and decided that the Sinclair net cable appears to be identical to a 3,5mm mono audio cable of a "phone plug" variety. Pretty much what was used for loading tapes on a Speccy.

A short cable might be preferable. Only one is needed for connecting two QL's for two-directional transfer. It doesn't seem to matter how the cable is connected on the two available connectors. But, at least in case of ZX Spectrum/Interface 1, you are not supposed to "loop" the network if you have multiple computers. This might hold true for QL too.

In SuperBASIC, each of the computers are given an ID via the NET command (1-64). After this, the neti_ and neto_ can be used to indicate input and output for the appropriate identity, such as neti_1 and neti_2 in this case.

Example:

On computer A, type
NET 1
and press enter.

On computer B, type
NET 2
and press enter.

On computer A, type
LBYTES neti_2,131072
and press enter

On computer B, type
SBYTES neto_1,131072,32768
and press enter.

The screen memory (32k) of the computer A will be filled with the contents sent from the other QL in about 11 seconds. LOAD, SAVE and LRUN work too.

Edit: There was a horrible mistake in the above (both loading and saving using neto_2) but it is now correct.

Incidentally, here's the way to draw filled triangles on QL Basic.
neto_0 and neti_0 can apparently be used for broadcasting and receiving on every ID. The manual says broadcasting more than 256 bytes is unreliable, possibly because there's no two-way protocol when broadcasting. So things like sending screen memory the way described above, is not likely to work with "broadcasting".

It should be possible to OPEN channels (OPEN #10,neti_) for printing and inputting text, but this is also not so straightforward. Perhaps with machine code.

Whether it is possible to hijack the signal with a PC (or, say, Arduino) I don't know.