J151 SC88PIO

D. Example Programs

OCR from original document may have introduced errors in the text above, but you should be able to get a good idea of what is going on and generate working code with minor error corrections.

	TITLE	'SC88PIO EXAMPLE PROGRAM'
;
; This is an example program for the SC88PIO board.
;
; A real time clock is set up and the time
; is sent to a terminal continuously.

; Users are free to copy and adapt this code for programs
; that run on the SC88PIO. Most applications will not need all the code here.
;
; The code performs the following operations:
;
;	Initialises the SCC and includes input and output routines
;
;	Initialises timer 2 of the 80188 as a interrupt source and
;	sets up its interrupt vector.
;
;	The interrupt handler increments a real time clock.
;
;	Sets up the chip select for this EPROM for an 8K, 16K or 32K EPROM
;
; This program was assembled using Digital Research's
; ASM86 as supplied with Concurrent DOS.
;
; This assembler is an 8086 assembler and so does not include the
; extra instructions available on the 80188 so the following
; library (also supplied with Concurrent) must be included:

	INCLUDE 186.LIB

; ***********************************************
; * Definitions
; ***********************************************
; Set baud rate. The allowed values are
;	 1 =   50 baud
;	 2 =   75 baud
;	 3 =  110 baud
;	 4 =  134.5 baud
;	 5 =  150 baud
;	 6 =  300 baud
;	 7 =  600 baud
;	 8 = 1200 baud
;	 9 = 1800 baud
;	10 = 2400 baud
;	11 = 3600 baud
;	12 = 4800 baud
;	13 = 7200 baud
;	14 = 9600 baud
;
BDEF	EQU	14			; Console baud rate
BDEF	EQU	8			; Printer baud rate
; Set to real-time clock ticks in ms
TICK	EQU	20			; 50 Hz
WAITI	EQU	2			; 8 MHz 2 wait states for peripherals 
WAITE	EQU	2			; 8 MHz 2 wait states for EPROM 
WAITR	EQU	0			; 8 MHz 0 wait states for RAM
;	
INTREG	EQU	0FF00H			; internal register address (I/O)
EOIREG	EQU	INTREG+022H		; end-of-interrupt register timer
TINTCR	EQU	INTREG+032H		; interrupt control register
TCNT2	EQU	INTREG+060H		; timer 2	count register
TMAXA2	EQU	INTREG+062H		;		maximum count A register
TMCW2	EQU	INTREG+066H		;		mode-control word
UMCS	EQU	INTREG+0A0H		; upper memory chip select register
LMCS	EQU	INTREG+0A2H		; lower memory chip select register
MPCS	EQU	INTREG+0A8H		; peripheral chip select register
RELREG	EQU	INTREG+0A4H		; relocation register
;
PBA	EQU	0F800H			; peripheral base address (I/O)
SCC	EQU	PBA+0H			; SCC
SCCBD	EQU	SCC			; SCC channel B control register
SCCAC	EQU	SCC+1			; SCC channel A control register
SCCBD	EQU	SCC+2			; SCC channel B data register
SCCAC	EQU	SCC+3			; SCC channel A data register
LF	EQU	10
CR	EQU	13

; ***********************************************
; Storage declarations
; ***********************************************
; Define stack segment and stack usage
	SSEG	0030H			; initialisation stack top is 03FFH
	ORG	0100H
STACK	EQU	$
; Define data segment and usage
	DSEG	0040H			; data area is 400H upwards
DATA	EQU	$
; A real-time clock is set up on initialisation
TIMEF		RB	1		; time fraction in 20ms (binary) 
TIMES		RB	1		; time seconds (BCD)
TIMEM		RB	1		; time minutes (BCD) 
TIMEH		RB	1		; time hours   (BCD)
;

; ***********************************************
;		Start of Code
;
;		Serial port drivers
;
; ***********************************************
; Users code will probably fit into the top 2K of the EPROM.
; In this case use CSEG OFF80H and change the ORG statement
; for the top 16 bytes of the EPROM
;
CSEG	0FE00H				; 8K PROGRAM
ORG	0				; SCC initialisation data table
;
; All data consists of two bytes - the SCC register number
; followed by the value to be written
;
SCCTA	DB	009H, 080H		; reset channel A
	DB	004H, 04CH		; clock x16, 2 stop bits
	DB	001H, 000H		; disable external status interrupts
	DB	003H, 0C0H		; Rx 8 bits
	DB	005H, 062H		; Tx 8 bits, RTS
	DB	009H, 000H		; clear reset
	DB	00АH, 000H
	DB	00BH, 056H		; clocks use baud rate generator 

SCCLA1	EQU	OFFSET - OFFSET SCCTA

	DB	00EH, 002H		; BRG source
	DB	00EH, 003H		; BRG enable
	DB	003H, 0C1H		; Rx enable
	DB	005H, 0EAH		; Tx enable, DTR, RTS
	DB	00FH, 000H		; disable external status interrupts
	DB	000H, 010H		; Reset external status interrupts
	DB	000H, 010H		; twice!
	
SCCLA2	EQU	OFFSET - OFFSET SCCTA - OFFSET SCCLA1 SCCTB
	
	DB	009H, 040H 		; reset channel B
	DB	004H, 04CH		; clock x16, 2 stop bits
	DB	003H,			; Rx 8 bits, auto enables
	DB	005H, 062H		; Tx 8 bits, RTS
	DB	009H, 000H		; clear reset
	DB	00AH, 000H
	DB	00BH, 056H		; clocks use baud rate generator

SCCLB1	EQU	OFFSET - OFFSET SCCTB

	DB	00ЕH, 002H		; BRG source
	DB	00EH, 003H		; BRG enable
	DB	003H, 0C14		; Rx enable
	DB	005H, 0EAH		; Tx enable, DTR, RTS
	DB	00FH, 000H		; Disable external status interrupts
	DB	000H, 010H		; Reset external status interrupts
	DB	000H, 010H		; Twice!

SCCLB2	EQU	OFFSET $ - OFFSET SCCTB - OFFSET SCCLB1 

; SCC baud rate divisors for x16 clock, PCLK = 4 MHz

DIVIS	DW	12500, 2500, 1665, 1134, 924, 831, 415, 206 
	DW	102, 67, 50, 33, 24, 15, 11, 5

;
; I/O routines
;
; Initialise channel A
;
CONINT:	PUSHA				; save registers
	MOV	DX, SCCAC
	MOV	BX, BDEF*2
	MOV	SI, OFFSET SCCTA
	PUSH	DS
	PUSHW	SCCLA2			; Length of second part of table
	MOV	CX, SCCLA1		; Length of first part of table
	JMPS	CHINT
;
; Initialise channel B
;
LISINT:	PUSHA
	MOV	DX, SCCBC
	MOV	BX, PDEF*2
	MOV	SI, OFFSET SCCTB
	PUSH	DS
	PUSHW	SCCLB2			; Length of second part of table 
	MOV	CX, SCCLB1		; Length of first part of table 
CHINT:	PUSH	CS			; set DS register DS 
CHIN1:	OUTSB				; access recovery time of SCC prevents 
	LOOP	CHIN1			; use of REP OUTS instruction 
	MOV	AL, 12 
	OUT	DX, AL
	MOV	AX, OFFSET DIVIS[BX] 
	OUT	DX, AL 
	MOV	AL, 13
	OUT	DX, AL 
	MOV	AL, AH
	OUT	DX, AL 
	POP	CX			; Length of second part of table 
CHIN2:	OUTSB
	LOOP	CHIN2
	POP	DS			; restore registers
	ΡΟΡΑ
	RET
;
; Input status of channel A		; Returns AL = 0FFH if char available else 0
;
CONIST:	PUSH	DX
	MOV	DX, SCCAC 
	JUMPS	CHIST

;
; Input status of channel B
;
LISIST:	PUSH	DX
	MOV	DX, SCCBC
CHIST:	IN	AL, DX
	AND	AL, 01
	JZ	CHIS1
	OR	AL, 0FFH
CHIS1:	POP	DX
	RET
;
; Input a character from channel A
; Returns AL = char
;
CONIN:	CALL	CONIST
	JZ	CONIN			; Wait until char available
	PUSH	DX
	MOV	DX, SCCAD
	JUMPS	CHIN
;
; Input a character from channel B
;
LISIN:	CALL	LISIST
	JZ	LISIN
	PUSH	DX
	MOV	DX,SCCBD
CHIN:	IN	AL,DX
	AND	AL,07FH
	POP	DX
	RET
;
; Output status of channel A
; Returns AL = 0FFH if char can be sent
;
CONOST:	PUSH	DX
	MOV	DX, SCCAC
	IN	AL,DX
	AND	AL, 04
	JZ	CONOS1
	OR	AL, 0FFH
CONOS1:	POP	DX
	RET

;
; Output status of channel B
; Returns AL = 0FFH if SCC ready to send char and CTS line asserted
;
LISOST:	PUSH	DX
	MOV	DX, SCCBC
	IN	AL, DX
	AND	AL, 024H
	CMP	AL, 024H
	JNZ	LISOS1			; If NOT (EMPTY AND CTS)
	OR	AL, 0FFH
	POP	DX
	RET
LISOS1:	XOR	AL, AL
	POP	DX
	RET
;
; Output a character to channel A
; Call with AL = char
;
CONOUT:	PUSH	AX
CONOTW:	CALL	CONOST
	JZ	CONOTW			; Wait until ready
	POP	AX
	PUSH	DX
	MOV	DX, SCCAD
	OUT	DX, AL
	POP	DX
	RET
;
; Output a character to channel B
; Call with AL = char
;
LISOUT:	PUSH	AX
LISOTW:	CALL	LISOST
	JZ	LISOTW
	POP	AX
	PUSH	DX
	MOV	DX, SCCBD
	OUT	DX, AL
	POP	DX
	RET

; *********************************************
;
;		Interrupt Handler
;
; *********************************************
; Timer 2 is set up as a real time clock
; On each interrupt ( every 20ms ) the following locations are incremented
; 
;	TIMEF		fractions of a second (in 20ms) in binary 
;	TIMES		seconds in BCD
;	TIMEM		minutes in BCD
;	TIMEH		hours in BCD
;
TIMINT:	PUSH	AX			; save registers]
	PUSH	DX
	PUSH	BX
	PUSH	DS
	MOV	AX, SEG DATA
	MOV	DS, AX
	XOR	AX, AX
	MOV	BX, OFFSET TIMEF
	INC	BYTE PTR [BX]		; increment fraction
	CMP	BYTE PTR [BX], 50	; 1 second?
	JB	TIMIN1
	MOV	[BX], AH
	INC	BX
	MOV	AL, [BX]		; increment seconds
	INC	AL
	DAA
	MOV	[BX], AL
	CMP	AL, 60H			; 1 minute ?
	JB	TIMIN1
	MOV	[BX], AH
	INC	BX
	MOV	AL, [BX]		; increment minutes
	INC	AL
	DAA
	MOV	[BX], AL
	CMP	AL, 60H			; 1 hour?
	JB	TIMIN1
	MOV	[BX], AH
	INC	BX
	MOV	AL, [BX]		; increment hours
	INC	AL
	DAA
	MOV	[BX], AL
TIMIN1:	POP	DS
	POP	BX
	MOV	DX, EOIREG		; issue a non-specific
	MOV	AX, 08000H		; end-of-interrupt command
	OUT	DX, AX
	POP	DX			; restore registers
	POP	AX
	IRET

;**********************************************
;
; Messages and utilities
;
;**********************************************
SIGNON:	DB	CR,LF,LF
	DB	'        _____****<<<< The SC88PIO >>>>****_____'
	DB	CR,LF,LF,LF,0
TIMEIS:	DB	CR, 'The time is ', 0
MINS:	DB	' minutes and '	, 0
SECS	DB	' seconds'	, 0
;
; Utility routines		
; print a string terminated by a zero byte
;
WRITES:	MOV	AL, CS:[BX]
	TEST	AL,AL
	JZ	WRITER
	CALL	CONOUT
	INC	BX
	JMPS	WRITES
WRITER: RET

; print CX hex digits from AX

WRITEH:	DEC	CX			; if CX > 1 WRITE1
	PUSH	AX			; then WRITEH (AX-, -1) DB ;
	SHR	AX, 4
	CALL	WRITEH	
	POP	AX
WRITE1:	AND	AL, 00FH
	DAA				; if AF then add 6 
	ADD	AL, 0F0H		; and set carry else subtract 10 
	ADC	AL, 040H		; convert to ASCII	
	CALL	CONOUT
	RET
	; print CX decimal digits from AX by recursion
PDEC:	MOV	BL, 10 
	DIV	BL 
	DEC	CX			; if CX > 1. 
PDEC1:	PUSH	AX			; then PDEC (AX/10, CX-1)
	XOR	AH, AH
	CALL	PDEC	
	POP	AX
PDEC1:	MOV	AL, AH
	AND	AL, 00FH
	ADD	AL, '0'
	CALL	CONOUT
	RET

;
; print AX in hex
;
PWORDH:
	PUSH	CX
	MOV	CX, 4
	CALL	WRITEH 
	POP	CX 
	RET
;
; print AL in hex
;
PBYTEH:	PUSH	CX
	ΜΟV	CX, 2 
	CALL	WRITEH 
	POP	CX
	RET

; *********************************************
; * 
; * Initialisation code
; *
; *********************************************
START:	CLI				; ensure interrupts are disabled
	MOV	DX, LMCS		; LMCS
	MOV	AX, 03FF8H + WAITR	; 256K RAM 
	OUT	DX, AX
	MOV	DX, MPCS
	MOV	AX, 080B8H		; PCS are in I/O space
	OUT	DX, AX
	MOV	DX, PACS
	MOV	AX, 00FBCH + WAITI	; PBAFF80
	OUT	DX, AX
	OUT	DX, AL
	MOV	AX, SEG STACK		; set up stack pointer
	MOV	SS, AX
	MOV	SP, OFFSET STACK
	MOV	AX, SEG DATA
	MOV	DS, AX
	CLD				; clear direction 
	STI
	CALL	CONINT			; Initialise serial ports
	CALL	LISTNT
	MOV	BX, OFFSET SIGNON	; Print greetings
	CALL	WRITES

;
; Set up interrupt handler for Timer 2
;
	XOR	AX, AX			; Write address of interrupt
	MOV	ES, AX			; handler into interrupt table
	MOV	AX, OFFSET TIMINT
	MOV	BX, 76	
	MOV	ES:[BX], AX		; Offset  into address 0004CH
	MOV	BX, 78			; Segment into address 0004EH
	MOV	AX, SEG TIMINT
	MOV	ES:[BX], AX
	MOV	AX, SEG DATA		; Zero time and date
	MOV	ES, AX
	XOR	AX, AX
	MOV	CX, 3
	MOV	DI, OFFSET TIMEF
	REP STOS AX
	ΜΟV	DX, TCNT2		; Set up timer 2
	XOR	AX, AX			; clear count
	OUT	DX, AX
	MOV	AX, 2000*TICK		; real-time clock constant
	OUT	DX, AX
	MOV	AX, 0E001H		; EN, INT, CONT
	OUT	DX, AX
	MOV	DX, TINTCR		; enable timer interrupts
	XOR	AX, AX			; priority = 0
	OUT	DX, AX

;
; Example Program
;
	PUSHW	SEG DATA		; Set up DS register
	POP	DS
PROG:	MOV	BX, OFFSET TIMEIS	; 'The time is '
	CALL	WRITES
	CLI	
	MOV	AL, TIMEF		; Read time while ints are disabled
	PUSH	AX
	MOV	AL, TIMES
	PUSH	AX
	MOV	AL, TIMEM
	STI	
	CALL	PBYTEH			; Print number of minutes
	MOV	BX, OFFSET MINS		; ' minutes and '
	CALL	WRITES
	POP	AX			; Print number of seconds
	CALL	PBYTEH
	MOV	AL,'.'			; '.'
	CALL	CONOUT
	POP	AX			; Fraction in 20ms
	XOR	AH,AH
	SHL	AL,1			; * 2
	MOV	CX,2
	CALL	PDEC			; Print hundredths of a second
	MOV	BX,OFFSET SECS		; ' seconds'
	CALL	WRITES
	JMP	PROG

;************************************************
; Reset entry point
;************************************************
; The 80188 processor jumps to segment 0FFFFH offset 00000H on reset
; This corresponds to the top 16 bytes of the EPROM in IC18
; Thus these bytes must set up the UMCS register so that the whole
; EPROM is enabled and then jump to the rest of the initialisation
; code
	CSEG	0FFFFH
	ORG	1FF0H			; This positions this code in the
					; top 16 bytes of a 8K EPROM
	MOV	DX,UMCS			; Chip select register for EPROM
;	MOV	AX,0FE3CH + WAITE	; 8K	Enable ONE of these statements
;	MOV	AX,0FC3CH + WAITE	; 16K	depending on your size of
	MOV	AX,0F83CH + WAITE	; 32K	EPPROM
	OUT	DX,AX			; Set up UMCS 
	JMPF	START			; Jump to code in rest of EPROM
	END