THE Z80 SIO SERIAL COMMUNICATIONS CONTROLLER
The programming complexities of large scale integration components are usually directly proportional to the facilities and versatility of the device.In the case of the Z80SIO, it will probably take some time even for even the more experienced programmer to work out how to make it perform even the most rudimentary tasks. To the beginner at microprocessor programming, it is possible that the bewildering array of registers will cause a total confusion unless a logical approach is taken from the start.
Let us look at what is on offer then.
The SIO contains 3x READ ONLY registers for the 'B' channel, 2x READ ONLY registers for the 'A' channel, 8x WRITE registers on the 'B' channel and 7x WRITE registers for the 'A'. Clearly for anyone attempting to make one of these things work correctly, a degree of understanding on which have to be programmed and which may be left alone, will make the job much easier.
First of all, break down the task into more manageable chunks. Hopefully those new to the device will initially be concerned with a single channel. i.e. Receive 'A' and Transmit 'A'. We can therefore ignore any of the dedicated channel 'B' registers. Hopefully (!) Most programmers will be using the device in ASYNC mode, which means that we can ignore any references to SYNC, SDLC and HDLC. That's a few more out the way! Lastly, although it is up to the individual as to whether they use the extensive interrupt facilities on offer, or whether to use the much simpler 'Polling' method of operation.
With INTERRUPTS, the CPU ignores the SIO until it's attention is grabbed by the SIO raising an INTERRUPT. In the case of the much simpler POLLING method, the CPU periodically shuffles over to the SIO to see if it has anything that needs dealing with. Both can be made to work equally well, but from the programmer's point of view, it is often far easier to resort to polling.
Let's take a typical WORKING example that may be applied to ANY circuit using the device. Obviously, it may be necessary to change some of the values if the particular application calls for it.
First and foremost, ensure that you have a proper LIST of the addresses that will be needed to access the SIO. The HARDWARE will need to be organised correctly in order to do this.
Connect up the SIO as an I/O device with it's two control lines taken to Address lines 0 and 1. This allows the CPU to access the two separate channels and the control / data registers therein. In the programming example that follows, we will choose an I/O address of 30h. If we now attach the two register select lines A/B and C/D to address lines 0 and 1, we can select the registers by using the following table:
30 Selects the Data register on channel A
31 Selects the Data register on channel B
32 Selects the Control registers on channel A
33 Selects the Control registers on channel B
Both the READ and WRITE control registers (excepting register 0), need TWO consecutive SIO transfers in order to gain access to them. The FIRST access is a 'pointer' byte in write register 0 of that channel, before the chosen register can be either programmed or read. Look at the WORKING RECEIVER example which follows:
410D 18 Load the accumulator with value 18h
410F 32 Output the 18h to Write control register 0. RESET CHANNEL
4110 00 NOP
4112 04 Load the accumulator with value 04h
4114 32 Output 04h to write ctl reg 0: Request transfer to register 4
4116 44 Load the accumulator with 44h
4118 32 Output the 44 to Register 4: x16 clock, 1 stop bit, no parity
411A 03 Load the accumulator with 03h
411C 32 Output the 03h to write ctl reg 0:Request transfer to register 3
411E C0 Load the accumulator with C0h
4120 32 Output the C0h to register 3: Set receive config to 8 bits
4122 05 Load A with 05h
4124 32 Output 05h to write control reg 0: Request transfer to reg 5
4126 60 Load A with 60h
4128 32 Output to Reg 5: Transmitter configuration set to 8 bits
Channel A can now be considered set up for both receiving and transmitting. The above initializing routines only need setting up at the beginning of the program, unless some factors are to be altered once the program is running.
In order to use the device for RECEIVING, (and by using the same transfer methods we used above) we need to send a C1h to Write register '3' (remember,at I/O address 32h?) - which will enable the receiver. After doing this, all that is necessary, if the POLLING method of usage is being applied is to periodically send a ZERO to read register 0 (code in the above example would be D3 32) then READ in the status register by using the command DB 32. As the'Read character available' bit in this register is actually Bit 0, we can rotate the result, so putting Bit 0 into the 'Carry' flag position by using the command 'RRA'. (there are of course several other alternative ways of testing it.) After which the 'Jump if no carry' command can be used if it is empty; thus:
481B 03 Ld A,03
481D 32 Output to 32
481F C1 Ld A,C1
4821 32 Output to 32
4824 97 Zero A TEST FOR A RECEIVED CHARACTER STARTS HERE
4826 32 Output to 00
4828 32 Read in 32
4829 1F RRA
482C 48 Jump if no carry to 4835h (go to do other tasks elsewhere)
482E 30 Carry bit is set. Read in ASCII character from SIO to accumulator
A few last points to note.
The conditional jump at 482C is there to allow other processing tasks to be performed before the program returns to 4824 to 'poll' the register again. Obviously once the program has found a character and is running beyond 482E,it is up to the programmer to determine just what to do with it!
A simple TRANSMITTER routine to set up and output a character follows here:
47DD 05 LDA,05
47DF 32 Output 05 to request register 5
47E1 68 LDA,68
47E3 32 Enable transmitter at register 5
47E4 97 Zero A
47E6 32 Access read register 0
47E8 32 Read in register 0
47EA 57 Test bit 2 of byte read in for transmitter already busy
47ED 47 Jump if zero (i.e. not empty and in use) to 47E4 to retest
47EE 7A LDA,D Load A with the byte to transmit
- in this program we've put it in the D register
47F0 30 Write the SIO data register with the byte to be sent
If a return were to be added to the bottom of this routine (at 47F1h) then the whole block of code may be considered a SUBROUTINE in it's own right - to use it, one simply has to place the character to be transmitted in the 'D'register then use the GOSUB command with the start address:
Strictly speaking (as I'm in a picky mood this evening!) It is not necessary to enable the transmitter each and every time one sends a character, so one could theoretically jump into the subroutine at address 47E4h.