Model 100 Serial Interface: Difference between revisions

From Bitchin100 DocGarden
Jump to navigationJump to search
No edit summary
(→‎Detect Clear to Send (CTS): Corrected bit number of CTS line of I/O address 0xBB.)
 
(35 intermediate revisions by 4 users not shown)
Line 1: Line 1:
== Overview ==
== Overview ==


The BASIC ROM provides access to the serial port. BASIC ROM support is well documented elsewhere. This article covers direct use of the IM6402 UART on the Model 100 and Tandy 102.
The BASIC ROM provides access to the serial port. BASIC ROM support is well documented elsewhere. This article covers direct use of the [[Media:6402.pdf‎|IM6402 UART]] (datasheet) on the Model 100 and Tandy 102.


== Configuration ==
== Configuration ==
Line 11: Line 11:
At any given time, either the internal modem or the external RS232 connector may be connected to the UART. This is controllable through software.
At any given time, either the internal modem or the external RS232 connector may be connected to the UART. This is controllable through software.


For RS232 access, port <code>$B8</code>, bit #3 must be set to 0.
For RS232 access, port <code>$BA</code>, bit #3 must be set to 0.


For Modem access, port <code>$B8</code>, bit #3 must be set to 1.
For Modem access, port <code>$BA</code>, bit #3 must be set to 1.


Note that <code>$B8</code> is shared with other functions, including power control.
Note that <code>$BA</code> is shared with other functions, including power control.


=== Serial Word Format ===
=== Serial Word Format ===
Line 41: Line 41:
CPU selection of baud rate is accomplished by loading a divisor into the PIO register through output ports <code>$BC</code> or <code>$B4</code> and <code>$BD</code> or <code>$B5</code>.
CPU selection of baud rate is accomplished by loading a divisor into the PIO register through output ports <code>$BC</code> or <code>$B4</code> and <code>$BD</code> or <code>$B5</code>.


The baud rate selection must be committed by writing <code>$C3</code> to register <code>$B8</code>
The master clock is 4.9152MHz; the CPU divides this /2 and the PIO /16; thus the divisor is 4,915,200/2/16/baud, i.e. 153,600/baud.
 
Port $BD should contain the integer value of the divisor/256+64.
 
Port $BC should contain the remainder (MOD) from the divisor/256.
 
The baud rate selection must be committed by writing <code>$C3</code> to register <code>$B8</code>.
 
Here is a table of some baud rates that may be of interest:


{|  border="1" cellpadding="5" cellspacing="0" style="border-width: thin; border-style: solid; border-color: blue; border-collapse: collapse;"
{|  border="1" cellpadding="5" cellspacing="0" style="border-width: thin; border-style: solid; border-color: blue; border-collapse: collapse;"
Line 65: Line 73:
|-
|-
| 38400 || 4 || 64 || 4
| 38400 || 4 || 64 || 4
|-
| 76800 || 4 || 64 || Either 2 or 1
|}
|}
I include 76800 since it is are the highest baud rate that the M100 is capable of. Certainly it is usable for communicating with another Model 100. However since is not a "known" baud rate, it is less useful for communicating with PCs or other devices.
That said, you may be able to [[Serial Devices Supporting 76800bps|find a device]] or bitbang GPIO lines to communicate at these rates. I have read that some USB->Serial adapters are capable of communicating at many more baud rates than the typical built-in serial ports, so there is definitely room for further exploration.
Also, it turns out that NADSBox hardware is able to support the 76800bps rate! Ken may enable features that leverage the higher rate in future software updates.


Here is a practical example of using 38400bps using only BASIC code. This program dumps all RAM contents to the serial port.
Here is a practical example of using 38400bps using only BASIC code. This program dumps all RAM contents to the serial port.
Line 90: Line 106:


So, here's an assembly language version of the same program. It transfers your Model 100's 32K RAM to a serial connection in about 8.5 seconds!
So, here's an assembly language version of the same program. It transfers your Model 100's 32K RAM to a serial connection in about 8.5 seconds!
Note that this code obeys the "clear to send" signal from the PC, so given hardware flow control enabled and properly configured on the PC, we should not overrun the remote UART.
Further note that if the PC is busy doing something else (like printing to the screen) it might flow control us, and you may not achieve the maximum speed.


<pre>
<pre>
Line 116: Line 136:
JZ WAITEMPTY
JZ WAITEMPTY


WAITCTS: IN $BB
CMA
ANI $10
JZ WAITCTS
MOV A,M
MOV A,M
OUT $C8
OUT $C8
Line 141: Line 166:
  OUT 200,ASC("A")
  OUT 200,ASC("A")


In the UART, there is room for two characters: the character currently be transmitted, and the next one. This permits you to ensure there is always once character waiting in the wings.
In the UART, there is room for two characters: the character currently being transmitted, and the next one. This permits you to ensure there is always one character waiting in the wings.


You should never overflow the transmission buffers. Either do some other work during the "downtime" and/or wait until there is room by polling bit 4 (0x10) of register $D8. The code the follows just does a "busy wait."
You should never overflow the transmission buffers. Either do some other work during the "downtime" and/or wait until there is room by polling bit 4 (0x10) of register $D8. The code that follows just does a "busy wait."


  WAITEMPTY: IN $D8
  WAITEMPTY: IN $D8
Line 165: Line 190:
For maximum efficiency you may wish to poll Interrupt 6.5 pin rather than accept the overhead of an interrupt. This is made possible by the 8085's SIM and RIM instructions.
For maximum efficiency you may wish to poll Interrupt 6.5 pin rather than accept the overhead of an interrupt. This is made possible by the 8085's SIM and RIM instructions.


Using the SIM instruction, you can set the interrupt mask such that UART DR (data ready), bit 1, will not trigger an interrupt. Then, you can poll this bit using the RIM instruction ANDing with 0x02 to see if DR is set.
Using the SIM instruction, you can set the interrupt mask such that UART DR (data ready), bit 1, will not trigger an interrupt. Then, you can poll this bit using the RIM instruction ANDing with 0x20 to see if DR is set.


Alternatively, you could disable interrupt processing altogether during your polled routine, but still use RIM to poll. This has the disadvantage of disabling the background ISR.
Alternatively, you could disable interrupt processing altogether during your polled routine, but still use RIM to poll. This has the disadvantage of disabling the background ISR. But to achieve higher baud rates, you may not want the background ISR running anyway.


== Flow Control ==
== Flow Control ==


The Model T has full support in hardware for the CTS and RTS flow control lines. In the BASIC ROM, however, it is not implemented. Instead the BASIC ROM relys on slow, kludgy XON/XOFF character escapes. This makes it difficult to transmit of receive binary files since the XON/XOFF some characters are reserved for flow control.
The Model T has full support in hardware for the CTS and RTS flow control lines. In the BASIC ROM, however, it is not implemented. Instead the BASIC ROM relies on slow, kludgy XON/XOFF character escapes. This makes it difficult to transmit or receive binary files since the XON/XOFF characters are reserved for flow control.


Since we are discussing direct control of the UART, we can do better and implement full flow control access.
Since we are discussing direct control of the UART, we can do better and implement full flow control.


=== Detect Clear to Transmit (CTS) ===
=== Detect Clear to Send (CTS) ===


The CTS line is an input to the Model 100. It indicates whether the device attached to the serial port (or the remote equipment behind it) has room to accept new characters. The device can "flow off" the Model T when its receive buffers are full.
The CTS line is an input to the Model 100. It indicates whether the device attached to the serial port (or the remote equipment behind it) has room to accept new characters. The device can "flow off" the Model T when its receive buffers are full.


(IN PROGRESS)
Implementing CTS is easy. The following code performs a busy wait on CTS:
 
<pre>
WAITCTS: IN $BB
CMA
ANI $10
JZ WAITCTS
</pre>
 
CTS line is read from bit #4 of I/O address 0xBB. Note that it is inverted; if it is 0, the device is signalling "clear to send." If 1, the device is attempting to flow us off from transmitting.


=== Request Peer to Send (RTS) ===
=== Request Peer to Send (RTS) ===


The RTS line is an output from the Model 100 to the device connected on the serial port. It is a signal  "requesting" the device to send (or not to send). From the other point of view, the device should transmit if and only if RTS is high.
The RTS line is an output from the Model 100 to the device connected on the serial port. It is a signal  "requesting" the device to send (or not to send). From the other point of view, the device should transmit if and only if RTS is high. Note that from the device perspective, it sees our RTS as its CTS.
 
If the device implements hardware flow control, it will not send if our RTS (and probably DTR) signals are low. So, at least we will want to set these two bits.
 
The RTS and DTR lines are located on I/O register 0xBA (186). To assert RTS, clear the bit #7 of 0xBA (set to 0). To deassert RTS, set bit #7 of 0xBA to 1. Similarly for DTR, except that it is Bit #6.
 
The following code sample sets RTS corresponding to high and low watermarks of the serial buffer:
 
<code>
<pre>
; Assert RTS if the low water mark is passed (pressure relieved)
CHECK_LOW_WATER: CPI 10
JNC EXIT_LOW_WATER
ASSERT_RTS: IN $BA
ANI $7F
OUT $BA
EXIT_LOW_WATER: POP PSW
RET
 
 
; Deassert RTS if high water mark is exceeded (high pressure)
CHECK_HIGH_WATER: CPI 40
JC EXIT_HIGH_WATER
DEASSERT_RTS: IN $BA
ORI $80
OUT $BA
EXIT_HIGH_WATER: POP PSW
RET
</pre>
</code>
 
== Handling Communication Errors ==


(IN PROGRESS)
(IN PROGRESS)


== I/O Map ==
== I/O Map ==
Here, for reference, is the serial I/O map:


<table border="1" cellpadding="5" cellspacing="0" style="border-width: thin; border-style: solid; border-color: blue; border-collapse: collapse;">
<table border="1" cellpadding="5" cellspacing="0" style="border-width: thin; border-style: solid; border-color: blue; border-collapse: collapse;">
<tr><td>Name</td><td>Direction</td><td>Port</td></tr>
<tr><td>Name</td><td>Direction</td><td>Port</td></tr>
<tr><td>TX</td>  <td>Output</td>  <td>$C8</td></tr>
<tr><td>TX</td>  <td>Output</td>  <td>$C8 (200)</td></tr>
<tr><td>RX</td>  <td>Input</td>    <td>$C8</td></tr>
<tr><td>RX</td>  <td>Input</td>    <td>$C8 (200)</td></tr>
<tr><td>RTS</td> <td>Output</td>  <td>$BA, bit 7</td></tr>
<tr><td>RTS</td> <td>Output</td>  <td>$BA (186), bit 7</td></tr>
<tr><td>CTS</td> <td>Input</td>    <td>$BB, bit 4</td></tr>
<tr><td>CTS</td> <td>Input</td>    <td>$BB (187), bit 4</td></tr>
<tr><td>DSR</td> <td>Input</td>    <td>$BB, bit 5</td></tr>
<tr><td>DSR</td> <td>Input</td>    <td>$BB (187), bit 5</td></tr>
<tr><td>DTR</td> <td>Output</td>  <td>$BA, bit 6</td></tr>
<tr><td>DTR</td> <td>Output</td>  <td>$BA (186), bit 6</td></tr>
</table>
</table>


nb: sense on DSR, CTS seem to be inverted. So CTS ==0 means that it is OK to transmit. A '1' means the device is flowing the Model T off.
nb: sense on DSR, CTS seem to be inverted. So CTS ==0 means that it is OK to transmit. A '1' means the device is flowing the Model T off.
nb: Port <code>$BA</code> is called "Port B" in the Model 100 Technical Reference. It has other functions than UART control, including the critical Power On/Off line. See [[Model 100 Port B]] for the breakdown.


Direction indicates both data flow, and whether to use an <code>IN</code> or <code>OUT</code> instruction to read/write to the given pin.
Direction indicates both data flow, and whether to use an <code>IN</code> or <code>OUT</code> instruction to read/write to the given pin.


[[IO map]] details
[[Category:Model T Developer Reference]]

Latest revision as of 11:26, 6 August 2012

Overview

The BASIC ROM provides access to the serial port. BASIC ROM support is well documented elsewhere. This article covers direct use of the IM6402 UART (datasheet) on the Model 100 and Tandy 102.

Configuration

Before the serial port can be used, it must be configured both for serial word format (data bits, stop bits, and parity) and baud rate.

Select RS232 Port

At any given time, either the internal modem or the external RS232 connector may be connected to the UART. This is controllable through software.

For RS232 access, port $BA, bit #3 must be set to 0.

For Modem access, port $BA, bit #3 must be set to 1.

Note that $BA is shared with other functions, including power control.

Serial Word Format

Port $D8 controls the serial word format.

Actually, any of $D0 → $DF can be used.

Bit Function Settings
0 Stop Bit Select 0 → 1 stop bit
1 → 2 or 1.5 stop bits (1.5 if character length is 5, 2 otherwise)
1 Even Parity Enable 0 → Odd parity
1 → Even
2 Parity Inhibit 0 → Parity
1 → No parity
4-3 Character Length Select Bits:
00 → 5
01 → 6
10 → 7
11 → 8
7-5 Unused

Baud Rate

CPU selection of baud rate is accomplished by loading a divisor into the PIO register through output ports $BC or $B4 and $BD or $B5.

The master clock is 4.9152MHz; the CPU divides this /2 and the PIO /16; thus the divisor is 4,915,200/2/16/baud, i.e. 153,600/baud.

Port $BD should contain the integer value of the divisor/256+64.

Port $BC should contain the remainder (MOD) from the divisor/256.

The baud rate selection must be committed by writing $C3 to register $B8.

Here is a table of some baud rates that may be of interest:

Baud Rate PIO
Divisor
Port $BD Port $BC
75 2048 72 0
110 1396 69 116
300 512 66 0
600 256 65 0
1200 128 64 128
2400 64 64 64
4800 32 64 32
9600 16 64 16
19200 8 64 8
38400 4 64 4
76800 4 64 Either 2 or 1

I include 76800 since it is are the highest baud rate that the M100 is capable of. Certainly it is usable for communicating with another Model 100. However since is not a "known" baud rate, it is less useful for communicating with PCs or other devices.

That said, you may be able to find a device or bitbang GPIO lines to communicate at these rates. I have read that some USB->Serial adapters are capable of communicating at many more baud rates than the typical built-in serial ports, so there is definitely room for further exploration.

Also, it turns out that NADSBox hardware is able to support the 76800bps rate! Ken may enable features that leverage the higher rate in future software updates.

Here is a practical example of using 38400bps using only BASIC code. This program dumps all RAM contents to the serial port.

 1	DEFINTA-Z:
	OPEN"COM:98N1D"FOROUTPUTAS1:
	D$="":
	A=FRE("0"):
	L=VARPTR(D$)+1:
	M=L+1:
	POKEL-1,128:
	OUT180,4:
	OUT181,64:
	OUT184,195:
	FORI=0TO255:
		POKEM,128+I/2:
		POKEL,(IMOD2)*128:
		PRINT#1,D$;:
	NEXT

Unfortunately this BASIC code does not transmit the 32K of data any faster than the 19200bps code. Both take about 33 seconds to transmit the image.

So, here's an assembly language version of the same program. It transfers your Model 100's 32K RAM to a serial connection in about 8.5 seconds!

Note that this code obeys the "clear to send" signal from the PC, so given hardware flow control enabled and properly configured on the PC, we should not overrun the remote UART.

Further note that if the PC is busy doing something else (like printing to the screen) it might flow control us, and you may not achieve the maximum speed.

		.org	64704

		; select RS232 port
		MVI	A, $25
		OUT	$BA

		; set up 8N1
		MVI	A, 28
		OUT	$D8

		; set up 38400 bps
		MVI	A, 64
		OUT	$BD
		MVI	A, 4
		OUT	$BC
		MVI	A, $C3
		OUT	$B8

		LXI	H,32768

WAITEMPTY:	IN	$D8
		ANI	$10
		JZ	WAITEMPTY

WAITCTS:	IN	$BB
		CMA
		ANI	$10
		JZ	WAITCTS
	
		MOV	A,M
		OUT	$C8

		INX	H
		MOV	A,H
		ORA	L

		JNZ	WAITEMPTY

		RET

		.END

Data Transmission

Once configured, sending a character is simply

		MVI	'A'
		OUT	$C8

or, in BASIC,

OUT 200,ASC("A")

In the UART, there is room for two characters: the character currently being transmitted, and the next one. This permits you to ensure there is always one character waiting in the wings.

You should never overflow the transmission buffers. Either do some other work during the "downtime" and/or wait until there is room by polling bit 4 (0x10) of register $D8. The code that follows just does a "busy wait."

WAITEMPTY:	IN	$D8
		ANI	$10
		JZ	WAITEMPTY

When deciding whether the other device is ready to receive you should also consider the Clear To Send (CTS) flow control line described later in this document.

Data Reception

BASIC ROM Interrupt 6.5 Handling

Whenever the UART receives another character, Interrupt 6.5 is signalled. Then, if interrupts are enabled, the CPU stops what it is doing and CALLs location 0x0034 in the BASIC ROM. This code disables further interrupts and jumps to the location 0x6DAC.

The ISR then jumps to vector 0xFE5C (0xF5FC?) in RAM. Normally this is just a return instruction, but you could hook the interrupt here. [ stack manipulation to avoid/ or include default processing + re-enable interrupts? - TBD ]

Polling Interrupt 6.5

Accepting the BASIC ROM's handling imposes significant overhead. With each interrupt, the instruction pointer must be placed on the stack and a jump performed at minimum. If the BASIC ROM (as opposed to an Option ROM) is switched in, then you will have the overhead of disabling interrupts, a jump to the DR vector, a return instruction, and then the default processing of reading and enqueing the new character. All that, and at this point no useful work has been performed other than to relieve the UART.

For maximum efficiency you may wish to poll Interrupt 6.5 pin rather than accept the overhead of an interrupt. This is made possible by the 8085's SIM and RIM instructions.

Using the SIM instruction, you can set the interrupt mask such that UART DR (data ready), bit 1, will not trigger an interrupt. Then, you can poll this bit using the RIM instruction ANDing with 0x20 to see if DR is set.

Alternatively, you could disable interrupt processing altogether during your polled routine, but still use RIM to poll. This has the disadvantage of disabling the background ISR. But to achieve higher baud rates, you may not want the background ISR running anyway.

Flow Control

The Model T has full support in hardware for the CTS and RTS flow control lines. In the BASIC ROM, however, it is not implemented. Instead the BASIC ROM relies on slow, kludgy XON/XOFF character escapes. This makes it difficult to transmit or receive binary files since the XON/XOFF characters are reserved for flow control.

Since we are discussing direct control of the UART, we can do better and implement full flow control.

Detect Clear to Send (CTS)

The CTS line is an input to the Model 100. It indicates whether the device attached to the serial port (or the remote equipment behind it) has room to accept new characters. The device can "flow off" the Model T when its receive buffers are full.

Implementing CTS is easy. The following code performs a busy wait on CTS:

WAITCTS:	IN	$BB
		CMA
		ANI	$10
		JZ	WAITCTS

CTS line is read from bit #4 of I/O address 0xBB. Note that it is inverted; if it is 0, the device is signalling "clear to send." If 1, the device is attempting to flow us off from transmitting.

Request Peer to Send (RTS)

The RTS line is an output from the Model 100 to the device connected on the serial port. It is a signal "requesting" the device to send (or not to send). From the other point of view, the device should transmit if and only if RTS is high. Note that from the device perspective, it sees our RTS as its CTS.

If the device implements hardware flow control, it will not send if our RTS (and probably DTR) signals are low. So, at least we will want to set these two bits.

The RTS and DTR lines are located on I/O register 0xBA (186). To assert RTS, clear the bit #7 of 0xBA (set to 0). To deassert RTS, set bit #7 of 0xBA to 1. Similarly for DTR, except that it is Bit #6.

The following code sample sets RTS corresponding to high and low watermarks of the serial buffer:

; Assert RTS if the low water mark is passed (pressure relieved)
CHECK_LOW_WATER:	CPI		10
			JNC		EXIT_LOW_WATER
ASSERT_RTS:		IN		$BA
			ANI		$7F
			OUT		$BA
EXIT_LOW_WATER:		POP		PSW
			RET


; Deassert RTS if high water mark is exceeded (high pressure)
CHECK_HIGH_WATER:	CPI		40
			JC		EXIT_HIGH_WATER
DEASSERT_RTS:		IN		$BA
			ORI		$80
			OUT		$BA
EXIT_HIGH_WATER:	POP		PSW
			RET

Handling Communication Errors

(IN PROGRESS)

I/O Map

Here, for reference, is the serial I/O map:

NameDirectionPort
TX Output $C8 (200)
RX Input $C8 (200)
RTS Output $BA (186), bit 7
CTS Input $BB (187), bit 4
DSR Input $BB (187), bit 5
DTR Output $BA (186), bit 6

nb: sense on DSR, CTS seem to be inverted. So CTS ==0 means that it is OK to transmit. A '1' means the device is flowing the Model T off.

nb: Port $BA is called "Port B" in the Model 100 Technical Reference. It has other functions than UART control, including the critical Power On/Off line. See Model 100 Port B for the breakdown.

Direction indicates both data flow, and whether to use an IN or OUT instruction to read/write to the given pin.