by Richard Lancaster
I get the feeling that people hate the serial port and don't want to
touch it with a barge pole. Acorn didn't make it any more friendly by
spreading the relevant information over three chapters of the PRM, nor did
we get a C library to ease the pain. What follows is my own concise guide
to accessing the serial port via kernel.h.
There are several things that must be done before you can actually use the serial port:
You can then either read or write to the serial input and output buffers (the serial port is buffered). Finally you can flush the buffers if necessary.
The serial input buffer is not by default turned on. It therefore will not accept data from the outside world, and so you will not be able to read in serial data. The buffer can be set to one of three states :
| 0 | Off (No data ever read from the serial cable into the buffer; computer functions as normal) [This is the default]. |
| 1 | Keyboard input from serial port (The keyboard is disabled and keyboard input is taken from the serial port. Warning, if you don't have a serial keyboard or another computer connected then this will effectively lock you out of your computer until you perform a hard reset!). |
| 2 | Background (Data read into the serial input buffer and can be accessed by a program when necessary, computer functions as normal). |
The state can be set using the following piece of code (where
r is of type _kernel_swi_regs):
r.r[0] = 2; r.r[1] = level; _kernel_swi(OS_Byte, &r, &r);
Where level is 0, 1 or 2 as above. If you want normal
serial port operation then set it to 2. I have left out any error checking
etc. for reasons of space.
The transmission baud rate can be set with the following piece of code :
r.r[0] = 6; r.r[1] = rate; _kernel_swi(OS_SerialOp, &r, &r);
Where rate is the baud rate code, which can be found from
this table:
| Code | Baud | Code | Baud | Code | Baud | Code | Baud |
|---|---|---|---|---|---|---|---|
| 0 | 9600 | 4 | 1200 | 8 | 19200 | 12 | 600 |
| 1 | 75 | 5 | 2400 | 9 | 50 | 13 | 1800 |
| 2 | 150 | 6 | 4800 | 10 | 110 | 14 | 3600 |
| 3 | 300 | 7 | 9600 | 11 | 134.5 | 15 | 7200 |
The reception rate can be set in the same way but with R0
set to 5 instead of 6.
I haven't got access to the rate codes for the faster baud rates on the RISC PC so perhaps someone would like to fill me in.
The following piece of code allows you to set how many data bits you want, how many stop bits you want and whether you want parity etc. :
r.r[0] = 1; r.r[1] = format; _kernel_swi(OS_SerialOp, &r, &r);
Where format is a 32 bit word that describes the data
format you want to use. Bits 0 and 1 describe the number of data bits, bit
2 specifies the number of stop bits, bit 3 specifies whether you want
parity bits added, bits 4 and 5 set the type of parity and, finally bits 6
to 31 must be set to 0, presumably to allow for future expansion. This
table sets out the meaning of different bit values:
| 5 | 4 | Parity type | 3 | Parity bits | 2 | Stop bits | 1 | 0 | Data bits |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | Odd parity | 0 | No | 0 | 1 | 0 | 0 | 8 |
| 0 | 1 | Even parity | 1 | Yes | 1 | 21 | 0 | 1 | 7 |
| 1 | 0 | Transmit 1; ignore received | 1 | 0 | 6 | ||||
| 1 | 1 | Transmit 0; ignore received | 1 | 1 | 5 |
Some example settings would be : format = 0x0;
would give 8 data bits, 1 stop bit and no parity, format =
0xb; would give 5 data bits, 1 stop bit and odd parity.
The handshaking type can be set with the following code :
r.r[0] = 0; r.r[1] = XORMask; r.r[2] = ANDMask; _kernel_swi(OS_SerialOp, &r, &r);
This call sets a register that specifies the handshaking type. The call allows you to alter only the bits of the register you want to while leaving the others intact. You should therefore ensure that you don't alter any of the bits that you don't explicitly intend to. The SWI decides whether you want a bit set, unset or left alone by looking as the corresponding bits in the AND and XOR masks. The following table tells you what will happen to a bit depending on the state of that bit in the masks:
XORMask bit |
ANDmask bit |
Effect on register bit |
|---|---|---|
| 0 | 0 | Bit becomes 0 |
| 0 | 1 | Bit is unchanged |
| 1 | 0 | Bit becomes 1 |
| 1 | 1 | Bit is toggled |
The three register bits that concern us are bits 0, 1 and 2 the rest can be left alone. The purpose of each bit is detailed below :
0 If set then XON/XOFF handshaking is employed.
1 If bit is unset then use DCD handshaking.
2 If bit is unset then use DSR handshaking.
Therefore if we wish to turn XON/XOFF handshaking on then we use:
ANDMask = 0xfffffffe; XORMask =
0x00000001;
This is the most confusing of the serial port calls and you might like
to read the description of OS_SerialOp 0 in the PRMs for more
details.
Bytes can be read from the input buffer using the following code :
r.r[0] = 4; r.r[1] = 300; _kernel_swi(OS_SerialOp, &r, &r);
If there is a byte in the input buffer then it will be placed in
R1. If there isn't any data in the buffer then R1
will be preserved. You can therefore test whether you have actually read
any data out of the buffer by placing a number greater then 255 (i.e.
larger than can be stored in a byte) into R1 before you call
the SWI (in the example I have used 300). If after the SWI R1
is still the number you put in then the buffer was empty, otherwise
R1 will hold the byte read from the buffer.
This stumped me for a very long time. The reason is that the
OS_SerialOp 3 command that sends bytes to the buffer sets the
processor carry flag if the buffer was full and the byte wasn't sent. You
might wonder what the problem is. Well, you obviously have to detect that
the byte was not sent otherwise you will lose data. The problem is how do
you read the carry flag from C? I almost resorted to writing an assembler
routine but I then found the following function :
_kernel_oserror *_kernel_swi_c(int no, _kernel_swi_regs *in,
_kernel_swi_regs *out, int *carry);
As well as calling the SWI as normal this sets the integer
carry to the status of the carry flag on exit. The following
piece of code will therefore transmit bytes :
int notsent; /* read from carry flag */ r.r[0] = 3; r.r[1] = (int) byte; _kernel_swi_c(OS_SerialOp, &r, &r, ¬sent);
Where on exit you can test notsent to see whether the byte
was transmitted or not.
If you simply wish to see whether there is any data in the input buffer without extracting a byte then you can use the following piece of code :
int bufferempty; r.r[0] = 152; r.r[1] = 1; _kernel_swi_c(OS_Byte, &r, &r, &bufferempty);
Here bufferempty is set to 1 if the buffer is empty and 0
if it has data in.
Finally you can flush the buffers using :
r.r[0] = 21; r.r[1] = buffer; _kernel_swi(OS_Byte, &r, &r);
Where buffer is set to 1 for the input buffer and to 2 for
the output buffer.
| 1 | but 1 stop bit if you use an 8 bit word with parity and 1.5 stop bits if you use a 5 bit word without parity. |
From CAUGers volume 2 issue 2
Comments to caug@accu.org