Back to the JOYCE activities
The following document is the translation from a German magazine I wrote in those days.
Soon after working with CP/M on the JOYCE I bought me the interface expansion CPS 8256 and an 300 Baud acoustic coupler. Because I enjoyed programming myself I dealed with the serial interface extensively. The following article referring to that topic was published in the double issue 4+5/1987 of the German magazine "JOYCE-NEWS".
Unfortunately the publishing of the JOYCE-NEWS was stopped very soon.
Later I found a compilation of technical data of the CPS8256: www.systemed.net/pcw/cps8256.html.
In 2002 John Elliot who is very committed published a document called PCW Hardware.
Still later (in 2004!) I found a link referring to the CPS8256 a circuit diagram inclusive. The original website does not exist anymore but a description may be found here and the diagram here.
For more information find a document describing also Intel's Z80 UART and a description of the Intel timer 8253 (Here as 82C24, which is 8253 compatible).

INTERFACE Programming

Werner Cirsovius well known by his firmware articles in the NEWS 2+3/87 describes the serial interface and its programming here. As distinguished from other listings – mainly written in Pascal and not usable for everyone – neither the source assembler code nor the basic loader will be released on disk but the result of this article: the public domain tool KERMIT especially customized for the JOYCE by Werner!

PREAMBLE

This compilation may be interested for those users whose JOYCE is upgraded with the interface expansion und in connecting to the whole wide world through a serial connection. Find hints and references for programming focused on assembler which allows to optimize programs in the telecommunications environment, e.g. accessing mailboxes.

In a CP/M environment programs should use the BDOS system interface basically. Sometimes it may be useful using the BIOS interface to bypass character control, e.g. using console I/O. Running CP/M PLUS calling the BIOS for disk I/O must be avoided because it may crash the machine. The banked version of CP/M PLUS has BIOS routines in memory banks which are not switched in normally.

An exeption from this rule may be programming in a telecommunications environment. Here it is very important to get close information about the state of the telecommunications device. Also important is the selective setting or resetting of control bits. Neither the BDOS nor the BIOS does support such a function.

The following report gives a short overview of the construction of the JOYCE machine as well as a description of the extended line interface.

After this the programming of the serial line will be discussed.

Everybody who did unscrew the JOYCE for doubling the 256kbyte RAM or just for curiosity will be surprised of the few chips built into the machine (see block diagram in picture 1).

Compared to the JOYCE the interface expansion seems to be equipped with more chips.
Although the floppy controller may be programmed using the so called 'BIOS extended jump block' I will not discuss this. CP/M PLUS is able to administrate drives as well as the remaining I/O devices much better using BDOS calls. Furthermore you will find the circuit 74LS373, an eight bit latch.

The serial interface (SIO) is built upon: The parallel interface (CEN) is built upon: Not the usage of the second UART for the parallel interface!

In the next step we will discuss the programming of the serial interface (SIO) only because the parallel interface (CEN) will be better served by CP/M.

The following hex addressess are assigned to the I/O ports: We start programming the timer because it it easier than programming the UART.

Before sending data to a chanal a control word must be sent to port E7. This word has the following format:

BIT76543210
 SC1SC0RL1RL0M2M1M0BCD
Recognize that only bit SC0 has to be changed. This results into the two possible control words:
Timer 0:0011 0110 or 36hex
Timer 1:0111 0110 or 76hex
After sending this word to the timer 8253 at port E7 the 16 bit counter value must be sent to the chanal. On chanal 0 this corresponds to port E4, on chanal 1 it is port E5.
Calculate this value for a known Baud rate as follows:
The required frequency supplying the UART must be 16 times the Baud rate (this factor must sent to the UART or is the default respectively):

fUART = 16*BAUD

The frequency supplying the timer is the half of the CPU frequency:

fTIMER = fCPU/2

Therefore the factor F is calculated as:

fTIMER = F*fUART

Enough maths, the final equation is:

F = fCPU / (32*BAUD)

Selecting a rate of 300 Baud using a CPU fequency of 4Mhz results in:

F = 417 or hex 01A1

Find here some possible examples programming the transmitter:

... running MAC or RMAC:


	timer   equ     0e7h
        baud0   equ     0e4h
        word    equ     0011$0110b
        value   equ     417

doit:   mvi     a,wrd
        out     timer
        mvi     a,low value
        out     baud0
        mvi     a,high value
        out     baud0
        ret

... running BASIC


1000 baud=300
1010 value=4000000!/(32*baud)
1020 OUT &HE7,&H36
1030 OUT &HE4,value MOD 256
1040 OUT &HE4,value\256
1050 RETURN

... running TURBO PASCAL


program BaudRate;
const
  Word   = $36;
  Baud0  = $E4;
  Value  = 417;

procedure bd_set(Word, Baud0,
                 Value : integer);
const
  Timer  = $E7;
begin
  port[Timer] := Word;
  port[Baud0] := lo(Value);
  port[Baud0] := hi(Value);
end;

begin
  bd_set(Word, Baud0, Value);
end.

To program the Baud rate is interesting only if it will be changed during run time of course. But most often the rate will be set once so the Amstrad utility SETSIO may be used doing this. For the example above to set the transmitter rate to 300 Baud type the command

SETSIO TX 300

Although the normal BIOS supports setting the Baud rate (BIOS call 21, DEVINI = DEVice INItialization) there are still more parameters available to be changed calling the "BIOS extended JUMP block" (XBIOS).

What does XBIOS mean?

Running CP/M Plus the BIOS function 30 does exist, the so called USERF (= USER Function). Requesting it on the JOYCE means, that the call to this function must follow a 16 bit address selecting the required XBIOS function.

In general:

        CALL    USERF
        DW      XBIOS_FUN

The XBIOS functions may be divided into:

  1. Disk driver, e.g. execute UPD765 controller commands directly
  2. SIO driver
  3. Terminal emulator, informations about screen settings
  4. Keyboard, key definitions as known by SETKEYS
  5. Miscellaneous, e.g. accessing the JOYCE video RAM

Interesting for a telecommunications environment is the second group, SIO driver. Three functions are implemented as follows: The above example setting the transmitter Baud rate may be programmed using the XBIOS as follows:

        CALL USERF      ;Read UART setting
        DW   00BCH
        MOV  H,B        ;Let receiver as is
        MVI  L,6        ;Transmitter 300 Baud
        CALL USERF      ;Set Baud rate
        DW   00B9H
One final question has to be solved − how to program the subroutine "USERF". Concerning this the BIOS vector of function 30 must be initalized at the beginning of a program
INIT:   LHLD 1          ;BIOS base address
        LXI  B,3*(30-1) ;Load offset
        DAD  B          ;Calculate vector
        SHLD USERF+1    ;Save as jump address
        RET
USERF:  JMP  $-$        ;Jump to BIOS function 30

Z80 UART

While the programming of the 8253 is quite simple, programming of the Z80 UART is not. Because may functions may be set by the XBIOS function 00B6H not all of the capabilities will be discussed here. For more information please study the datasheet of the Z80 DART (by the way DART means that two UARTs are implemented on one chip, Dual UART).
But find a review here how to program data transfer as well as handling control states. The latter will not be supported by the XBIOS.
As described above the DART occupies two Z80 I/O ports, E0H for data transfer and E1H for control. Port E1H is equipped with some sub functions. There are three read registers (RR0 - RR2) as well as eight write registers (WR0 - WR7). For example WR5 handles the DTR and RTS bits. But also the number of data bits will be written into this register. Because this may run into conflicts handling of these functions should be performed by the XBIOS.
The remaining job is to test if data transfer has taken place or to look for external signals. Furthermore error detection may be performed.

Function 1 : Find out if data transfer is possible.
Format:

76543210Bit
TxBERxCARR0

A routine for sending a character in the Accu may look like this:
OUTPUT: PUSH PSW        ; Save character
WAIT:   IN   0E1H       ; Wait for send ok
        ANI  0000$0100B
        JZ   WAIT
        POP  PSW
        OUT  0E0H       ; Output
        RET
Function 2 : Find out if an error occured
Format:

76543210Bit
FEOEPERR1

Because these bits will be found in register 1, RR1 must be addressed before.

A routine requesting the error state may look like this (if no error occured the zero flag of the CPU will be set):
ERROR:  MVI  A,1
        OUT  0E1H       ; Address RR1
        IN   0E1H       ; Read error state
        PUSH PSW
        MVI  A,0
        OUT  0E1H       ; Address RR0 as default
        POP  PSW
        ANI  0111$0000B ; Generate state of the zero flag
        RET
Function 3 : Find out external line states
Format:

76543210Bit
CTSDSRDCDRR0

Although these bits will be found in regsister 0 (RR0, see function 1) they must be handled in a different way. Namely a reset must be given twice to WR0 before reading the bits. I did not found this a the data sheet but without doing so I was not able to detect a state change on any line.
A routine for reading and masking the external signals may look like this:
STATUS: MVI  A,0001$0000B
        OUT  0E1H       ; Reset once
        OUT  0E1H       ; .. twice
        IN   0E1H       ; Read external signals
        ANI  0011$1000B ; Mask bits
        RET

Addendum to the printed article:

Some applictions use to send a BREAK signal. This is a signal activating the transmitter line for about 300 milliseconds (msecs) causing a break on the receiver side. Write register WR5 performs this on the Z80 UART.
[ In either case the BREAK signal should be at least as long as the longest duration sending a byte. A careful consideration results into the length of the BREAK signal as follows: Using the slowest possible rate of 50 Baud leads to 1/50 seconds, i.e. 20 msecs, to transfer one bit. With the longest bit length of 12 bits (= 1 x start + 8 x data + 1 x parity + 2 x stop) we get a transfer time of 12x20 msecs = 240 msecs. So 300 msecs is a good choice. ]
A routine for sending a BREAK may look as follows (routine as used by KERMIT):
SENDBR:	MVI	D,1001$1010B	; BREAK mask
	MVI	E,30		; Length of the BREAK signal is
				; 300 milliseconds
SNDBR1:	MVI	A,1		; Address RR1
	OUT	0E1H
	IN	0E1H		; Read
	ANI	0000$0001B	; Test "ALL DONE" bit
	JZ	SNDBR1		; Wait until set
;
; Now the BREAK signal will be transmitted
;
SETBIT:	MVI	A,5		; Address WR5
	OUT	0E1H
	LDA	TXBITS		; Load bit length for transmitter
				; They are defined as folows:
				; x00x$xxxx  5 bits
				; x01x$xxxx  7 bits
				; x10x$xxxx  6 bits
				; x11x$xxxx  8 bits
	ORA	D		; Output BREAK,
	OUT	0E1H		; TXENABLE, RTS
;
; Now delay for 300 milliseconds
;
	MOV	A,E		; Delay value
	CALL	DELAY
;
; Time is expired.
; Set tansmitter to normal state now.
;
	MVI	A,5		; Address WR5
	OUT	0E1H
	LDA	TXBITS		; Reload bit length for transmitter
	ORI	1000$1010B	; No BREAK
	OUT	0E1H		; But TXENABLE and RTS
	RET			; Done
;
; Example for a delay routine
; Here for 10 milliseconds
;
DELAY:	LD	C,40		; Relating 4 MHz
DELAY2:	LD	B,70
DELAY3:	DEC	B
	JP	NZ,DELAY3
	DEC	C
	JP	NZ,DELAY2
	DEC	A
	JP	NZ,DELAY
	RET

Closure

The present report should introduce basics into programming the serial line used for telecommunications. Due to the poor informations coming from the JOYCE selling company SCHNEIDER not all facilities are stated here.
Consider that programming on a direct hardware level must be performed very careful independent of the programming language used. Remember that bank switching on the JOYCE is done by setting appropriate I/O ports. An incorrect address used for setting a port may crash the system. A crash may be bad but a complete erasure of a disk due to erroneous programming is worse.

Postscript of the editors: We are interested in reports on one's experiences relating telecommunications, mailboxes, serial interfaces......