SX RS232 Serial IO

UART with Parity

Peter Verkaik says:

Unable to find an existing uart for 8 databits + parity I wrote one myself. I saw a few posts on the basic stamp forum about this so I share this. It is an assembly source.

The program itself contains more than just the uart as it is written as a template to be extended for applications.

;*****************************************************************************************
; ELX1 board, co-procesor template
; ----------------------------------------------------------------------------------------
;
; version 0.9 beta
; december 9, 2004
; compile with SASM
;
; Author: Peter Verkaik
; Email: peterverkaik@boselectro.nl
;
; This program is a SX28 template providing a 2-wire host interface (halfduplex serial line +
; write enable line used for synchronization), and a 2-wire serial interface for terminal or
; other serial device. This terminal interface can be configured at runtime for:
;	TX/RX with or without xon/xoff handshake, TX only with CTS handshake, RX only with RTS handshake
;	four selectable baudrates (default 1200,2400,9600,19200)
;	7 or 8 databits
;	parity odd, even or none
;	powerup default is TX/RX without handshake, 1200 baud, 8 databits, no parity
; Port A is dedicated to the host and terminal.
; Ports B and C are not used and are available for the application.
; Code pages 2 and 3 are empty
; Ram banks 4, 5, 6 and 7 are empty
; 5 free global ram bank bytes
; four 8-byte buffers for terminal and host uarts
; Host command interface is expandable for up to 128 commands
;
;*****************************************************************************************
; Connection diagram. Both wires should have 4.7k-10k pullup resistor.
;
; MCU            co-processor (this device)
; -------+      +------------------------+
;        |      |                        |
;  pinWE o------o RA.1 = /WE             |
;  pinDQ o------o RA.0 = DQ              |
;        |      |              RC.0-RC.7 o-- application
; -------+      |              RB.0-RB.7 o-- application
;               |                        |
;               |                   RA.2 o-- terminal TX (or RTS in case of RX/RTS)
;               |                   RA.3 o-- terminal RX (or CTS in case of TX/CTS)
;               +------------------------+
;
;*****************************************************************************************
; Communication protocol between this device and the host MCU.
;
;      ------+                         +-+                             +------+-----
; pinWE  S0  |  S1 |       S2          | | S3  |       S4              |  S5  |  S0
;            +-----+-------------------+ +-----+-----------------------+
;            <--- pulled low by MCU ---> <- pulled low by this device ->
;
;      ------+                         +-+                             +------+-----
; pinDQ       <cmd> <param1>...<paramN>   <rsp>    <data1>...<dataN>
;      - hiZ +- - - - - input - - - - -+-+ - - - - output - - - - - - -+ - - hiZ - -
;
; State S0: idle
; State S1: receive command byte
; State S2: receive parameter bytes
; State S3: transmit response byte
; State S4: transmit data bytes
; State S5: finalize
;
; Each command sequence starts at the H->L transition on pinWE.
; This may be follwed by <cmd> and <param> bytes.
; A L->H transition on pinWE (from MCU) is ALWAYS follewed by
; a H->L transition and <rsp>.
; <data> bytes are optional and determined by command or status.
; The command sequence stops after this device releases pinWE.
;
;-----------------------------------------------------------------------------------------
; program modules
;-----------------------------------------------------------------------------------------
device				SX28,oschs1,turbo,stackx,optionx	;sx device options
irc_cal				IRC_SLOW
id				'ELX1TMPL'				;Elx1 board template
reset				reset_entry				;set reset vector
;select one of these freq
;freq				4_000_000
;freq				10_000_000
freq				20_000_000
;select according to freq
;RESONATOR		equ	4
;RESONATOR		equ	10
RESONATOR		equ	20


ERROR p2w 'Assembled assuming a ' RESONATOR ' MHz oscillator'
; The host interface (pins ra.0 and ra.1) is used to accept commands and data
; from a host mcu and delivers status and data to that mcu.


; The terminal interface (pins ra.2 and ra.3) is used to accept key input from a terminal(program)
; and delivers display/control characters to that terminal(program).


;-----------------------------------------------------------------------------------------
; uart constants
;-----------------------------------------------------------------------------------------


IF RESONATOR = 20
;128*1200=64*2400=32*4800=16*9600=8*19200
;uartfs=128*1200=153600
;int_period=20000000/153600=130
uartfs			equ	153600		; uart's basic frequency 128*1200 = 153600
int_period		equ	130		; 6.5uS used as seed for uart baud rate.
THREADS			equ	1
ENDIF


IF RESONATOR = 10
;64*1200=32*2400=16*4800=8*9600=4*19200
;uartfs=64*1200=76800
;int_period=10000000/76800=130
uartfs			equ	76800		; uart's basic frequency 64*1200 = 76800
int_period		equ	130		; 13uS used as seed for uart baud rate.
THREADS			equ	1
ENDIF


IF RESONATOR = 4
;32*1200=16*2400=8*4800=4*9600=2*19200
;uartfs=32*1200=38400
;int_period=4000000/38400=104
uartfs			equ	38400		; uart's basic frequency 32*1200 = 38400
int_period		equ	104		; 26uS used as seed for uart baud rate.
THREADS			equ	1
ENDIF


mscount			equ	((2000*RESONATOR/int_period)+1)/2	;number of int cycles for 1 msec
ERROR p2w '# of cycles per interrupt = ' mscount




XON			equ	17		;XON character (ctrl-Q)
XOFF			equ	19		;XOFF character (ctrl-S)


;The following baudrates are available


baud0			equ	1200
baud1			equ	2400
baud2			equ	4800
baud3			equ	9600
baud4			equ	19200
								; 20MHz  10MHz  4MHz
divide0			equ	uartfs / (baud0 * THREADS)	; 128    64     32
StDelay0 		equ	divide0 + (divide0 / 2)		; 192    96     48


divide1			equ	uartfs / (baud1 * THREADS)	;  64    32     16
StDelay1 		equ	divide1 + (divide1 / 2)		;  96    48     24


divide2			equ	uartfs / (baud2 * THREADS)	;  32    16      8
StDelay2 		equ	divide2 + (divide2 / 2)		;  48    24     12


divide3			equ	uartfs / (baud3 * THREADS)	;  16     8      4
StDelay3 		equ	divide3 + (divide3 / 2)		;  24    12      6


divide4			equ	uartfs / (baud4 * THREADS)	;   8     4      2
StDelay4 		equ	divide4 + (divide4 / 2)		;  12     6      3


hostDivide		equ	divide3		;baud used by host
hostStDelay		equ	StDelay3


;-----------------------------------------------------------------------------------------
; Pin definitions & Port assignments
;-----------------------------------------------------------------------------------------


;host and term pin definitions


hostDQ			equ	0
hostWE			equ	1
pinDQ			equ	ra.hostDQ	;host uart transmit/receive
pinWE			equ	ra.hostWE	;host write enable input


termTX			equ	2
termRX			equ	3
pinTX			equ	ra.termTX	;term uart transmit
pinRX			equ	ra.termRX	;term uart receive


; ra changes output level by changing direction register!! Never drive output high!!
RA_latch		equ	%00000000	;port A output latch init	1=high	0=low
RA_tris			equ	%11111111	;port A direction register	1=input	0=output
RA_plp			equ	%00000000	;port A pull up register	1=off	0=on
RA_lvl			equ	%11111111	;port A level register		1=ttl	0=cmos


; rb changes output level by changing direction register!! Never drive output high!!
RB_latch		equ	%00000000	;port B output latch			1=high	0=low
RB_tris			equ	%11111111	;port B direction register		1=input	0=output
RB_plp			equ	%00000000	;port B pull up register		1=off	0=on
RB_lvl			equ	%00000000	;port B level register			1=ttl	0=cmos
RB_st			equ	%11111111	;port B schmitt trigger register	1=off	0=on
RB_wken			equ	%11111111	;port B wake up enable register		1=off	0=on	** KEEP OFF
RB_wked			equ	%11111111	;port B wake up edge register		1=neg	0=pos


; rc changes output level by changing direction register!! Never drive output high!!
RC_latch		equ	%00000000	;port C output latch			1=high	0=low
RC_tris			equ	%11111111	;port C direction register		1=input	0=output
RC_plp			equ	%00000000	;port C pull up register		1=off	0=on
RC_lvl			equ	%11111111	;port C level register			1=ttl	0=cmos
RC_st			equ	%11111111	;port C schmitt trigger register	1=off	0=on


;-----------------------------------------------------------------------------------------
; Code Locations
;-----------------------------------------------------------------------------------------
CODEPAGE_0		equ	$0
CODEPAGE_1		equ	$200
CODEPAGE_2		equ	$400
CODEPAGE_3		equ	$600


;-----------------------------------------------------------------------------------------
; Data Memory address definitions
;-----------------------------------------------------------------------------------------
global_org		equ	$08
bank0_org		equ	$10
bank1_org		equ	$30
bank2_org		equ	$50
bank3_org		equ	$70
bank4_org		equ	$90
bank5_org		equ	$B0
bank6_org		equ	$D0
bank7_org		equ	$F0


;-----------------------------------------------------------------------------------------
; Global Register definitions
;-----------------------------------------------------------------------------------------
			org     global_org


temp			ds	1		;for temporary use (buffers, calculation)
ra_dir_buf		ds	1		;holds value for ra direction register


status_flags		ds	1
  cmd_invalid		equ	status_flags.0	;set if command invalid
  cmd_parameter_missing	equ	status_flags.1	;set if missing parameter
  buffer_overrun	equ	status_flags.2	;set if any buffer overrun
  term_parity_error	equ	status_flags.3	;set if parity error


free_global		ds	5


;-----------------------------------------------------------------------------------------
; RAM Bank Register definitions
;-----------------------------------------------------------------------------------------


	; Bank 0 - host uart bank, timer bank
	;---------------------------------------------------------------------------------
			org     bank0_org


hu_bank			=	$		;host uart bank
hu_rx_count		ds	1		;number of bits to receive
hu_rx_divide		ds	1		;receive timing counter
hu_rx_byte		ds	1		;received byte
hu_tx_count		ds	1		;number of bits to transmit
hu_tx_divide		ds	1		;transmit timing counter
hu_tx_low		ds	1		;low byte to transmit
hu_tx_high		ds	1		;high byte to transmit
hu_head			ds	1		;hinib=tx, lonib=rx: index of next free entry in buf
hu_tail			ds	1		;hinib=tx, lonib=rx: index of next byte to read from buf
hu_num			ds	1		;hinib=tx, lonib=rx: number of bytes in buf
  hu_rx_full		equ	hu_num.3	;  set if hu_rx full (num=8)
  hu_tx_full		equ	hu_num.7	;  set if hu_tx full (num=8)
hu_flags		ds	1
  hu_rx_data		equ	hu_flags.0	;set if hu_rx not empty
  hu_rx_overrun		equ	hu_flags.1	;set if hu_rx overruns
  hu_rx_enable		equ	hu_flags.2	;set to enable host uart receive
  hu_rx_flag		equ	hu_flags.3	;set if byte in hu_rx_byte
  hu_tx_data		equ	hu_flags.4	;set if hu_tx not empty
  hu_tx_overrun		equ	hu_flags.5	;set if hu_tx overruns
  hu_tx_enable		equ	hu_flags.6	;set to enable host uart transmit
  hu_tx_flag		equ	hu_flags.7	;not used
hu_state		ds	1		;host state
						;  b2-b0: state of host_command_task routine
  pinWE_present		equ	hu_state.6	;  current state of pinWE
  pinWE_past		equ	hu_state.7	;  past state of pinWE
hu_command		ds	1		;received host command
  cmd_done		equ	hu_command.7	;  set if command finished


timer_bank		=	$
timer_int		ds	1		;increments every isr
timer_1m		ds	1		;decrements every msec


free_bank0		ds	1


	; Bank 1 - term uart bank
	;---------------------------------------------------------------------------------
			org	bank1_org


tu_bank			=	$		;term uart bank
tu_rx_count		ds	1		;number of bits to receive
tu_rx_divide		ds	1		;receive timing counter
tu_rx_byte		ds	1		;received byte
tu_tx_count		ds	1		;number of bits to transmit
tu_tx_divide		ds	1		;transmit timing counter
tu_tx_low		ds	1		;low byte to transmit
tu_tx_high		ds	1		;high byte to transmit
tu_head			ds	1		;hinib=tx, lonib=rx: index of next free entry in buf
tu_tail			ds	1		;hinib=tx, lonib=rx: index of next byte to read from buf
tu_num			ds	1		;hinib=tx, lonib=rx: number of bytes in buf
  tu_rx_full		equ	tu_num.3	;  set if tu_rx full (num=8)
  tu_tx_full		equ	tu_num.7	;  set if tu_tx full (num=8)
tu_flags		ds	1
  tu_rx_data		equ	tu_flags.0	;set if tu_rx not empty
  tu_rx_overrun		equ	tu_flags.1	;set if tu_rx overruns
  tu_rx_enable		equ	tu_flags.2	;set if transmitted XON, cleared if transmitted XOFF
  tu_rx_flag		equ	tu_flags.3	;set if byte in tu_rx_byte
  tu_tx_data		equ	tu_flags.4	;set if tu_tx not empty
  tu_tx_overrun		equ	tu_flags.5	;set if tu_tx overruns
  tu_tx_enable		equ	hu_flags.6	;set if received XON, cleared if received XOFF
  tu_rx_parity		equ	tu_flags.7	;received parity bit
tu_state		ds	1		;term state (not implemented)
tu_command		ds	1		;received term command (not implemented)
tu_config		ds	1		;terminal configuration
						;  b1-b0 = baud (1200,2400,9600,19200)
  tu_rts		equ	tu_config.2	;  1 for rts handshake (TX used as RTS)
  tu_cts		equ	tu_config.3	;  1 for cts handshake (RX used as CTS)
  tu_xonxoff		equ	tu_config.4	;  1 for xon/xoff handshake (inter-character delay must be 1 msec at 9600 baud)
  tu_7bits		equ	tu_config.5	;  1 for 7 databits (0 = 8 databits)
  tu_parity_even	equ	tu_config.6	;  1 for even parity
  tu_parity_odd		equ	tu_config.7	;  1 for odd parity
tu_char			ds	1		;save received byte here
tu_bits			ds	1		;number of bits (startbit+databits+paritybit+stopbit)
						;  set by command TM_SET_CONFIG (bits is 9, 10 or 11)


	; Bank 2 - host uart buffers
	;---------------------------------------------------------------------------------
			org     bank2_org


hu_buf_bank		=	$
hu_rx_buf		ds	8
hu_tx_buf		ds	8


	; Bank 3 - term uart buffers
	;---------------------------------------------------------------------------------
			org	bank3_org


tu_buf_bank		=	$
tu_rx_buf		ds	8
tu_tx_buf		ds	8


	; Bank 4 - 
	;---------------------------------------------------------------------------------
			org	bank4_org


free_bank4		ds	16


	; Bank 5 - 
	;---------------------------------------------------------------------------------
			org	bank5_org


free_bank5		ds	16


	; Bank 6 - 
	;---------------------------------------------------------------------------------
			org     bank6_org


free_bank6		ds	16


	; Bank 7 - 
	;---------------------------------------------------------------------------------
			org     bank7_org


free_bank7		ds	16


;-------------- end of ram variables -----------------------------------------------------




;*****************************************************************************************
			org	CODEPAGE_0
;*****************************************************************************************




;-----------------------------------------------------------------------------------------
; Interrupt Service Routine
;-----------------------------------------------------------------------------------------


interrupt		jmp	_interrupt		;1


	; hooks for application
	;---------------------------------------------------------------------------------
app_isr			retp				;change to jmp @myapp_isr if any
app_main		retp				;change to jmp @myapp_main if any


	; jump table for template
	;---------------------------------------------------------------------------------
enqueue_hu_tx		jmp	_enqueue_hu_tx		;store byte for output to host
enqueue_hu_rx		jmp	_enqueue_hu_rx		;store byte received from host
dequeue_hu_tx		jmp	_dequeue_hu_tx		;retrieve byte for output to host
dequeue_hu_rx		jmp	_dequeue_hu_rx		;retrieve byte received from host
enqueue_tu_tx		jmp	_enqueue_tu_tx		;store byte for output to terminal
enqueue_tu_rx		jmp	_enqueue_tu_rx		;store byte received from terminal
dequeue_tu_tx		jmp	_dequeue_tu_tx		;retrieve byte for output to terminal
dequeue_tu_rx		jmp	_dequeue_tu_rx		;retrieve byte received from terminal
host_input_task		jmp	_host_input_task	;receives host bytes into hu_rx_buf
host_output_task	jmp	_host_output_task	;transmit host bytes from hu_tx_buf


	; get term baud parameters as determined by tu_config.0 and tu_config.1
	;---------------------------------------------------------------------------------
tu_divide
			mov	w,tu_config
			and	w,#$03
			add	pc,w
			retw	divide0			;1200
			retw	divide1			;2400
			retw	divide3			;9600
			retw	divide4			;19200


tu_StDelay
			mov	w,tu_config
			and	w,#$03
			add	pc,w
			retw	StDelay0		;1200
			retw	StDelay1		;2400
			retw	StDelay3		;9600
			retw	StDelay4		;19200


	; host command task
	; Wait for a command received from host MCU,
	; execute command and transmit results to host MCU.
	;---------------------------------------------------------------------------------


STATE_IDLE		equ	0
STATE_RECEIVE_COMMAND	equ	1
STATE_RECEIVE_PARAMETER	equ	2
STATE_TRANSMIT_RESPONSE	equ	3
STATE_TRANSMIT_DATA	equ	4
STATE_FINALIZE		equ	5


host_command_task
			call	@checkPinWE		;check for edges
			bank	hu_bank
			mov	w,hu_state
			and	w,#$07
			add	pc,w
			jmp	hostIdle		;0
			jmp	hostReceiveCommand	;1
			jmp	hostReceiveParameter	;2
			jmp	hostTransmitResponse	;3
			jmp	hostTransmitData	;4
			jmp	hostFinalize		;5
			jmp	hostIdle		;6 failsafe
			jmp	hostIdle		;7 failsafe


hostIdle
			clrb	hu_rx_enable
			clrb	hu_tx_enable
			setb	ra_dir_buf.hostDQ
			setb	ra_dir_buf.hostWE
			retp


hostReceiveCommand
			sb	hu_rx_data		;any received data?
			retp				;no
			call	dequeue_hu_rx
			mov	hu_command,w		;this is command
			mov	w,#$7F
			snb	cmd_done
			mov	hu_command,w		;make command $7F if b7 is set
			clrb	cmd_invalid
			clrb	term_parity_error
			setb	cmd_parameter_missing	;cleared by function
			call	@host_command_handler	;execute command
			bank	hu_bank
			inc	hu_state		;move to next state
			retp


hostReceiveParameter
			sb	hu_rx_data		;any received data?
			retp				;no
			jmp	@host_command_handler	;execute command


hostTransmitResponse
			clrb	ra_dir_buf.hostWE	;this device pulls pinWE low
			mov	w,hu_flags
			and	w,#$22			;extract overrun flags
			sz
			setb	buffer_overrun
			bank	tu_bank
			mov	w,tu_flags
			and	w,#$22			;extract overrun flags
			sz
			setb	buffer_overrun
			mov	w,status_flags		;transmit status flags
			call	enqueue_hu_tx
			clrb	hu_rx_overrun
			clrb	hu_tx_overrun
			inc	hu_state		;move to next state
			bank	tu_bank
			clrb	tu_rx_overrun
			clrb	tu_tx_overrun
			clrb	buffer_overrun
			retp


hostTransmitData
			snb	hu_tx_data		;output buffer empty?
			retp				;no
			call	@host_command_handler	;get next data
			bank	hu_bank
			snb	cmd_done		;all done?
			inc	hu_state		;yes, move to next state
			retp


hostFinalize
			snb	hu_tx_data		;output buffer empty
			retp				;no
			test	hu_tx_count		;transmitter ready?
			sz
			retp				;no
			clrb	hu_tx_enable		;disable transmitter
			setb	ra_dir_buf.hostWE	;release pinWE
			and	hu_state,#$C0		;wait for new command
			retp


;-------------- start of the Interrupt Service Routines ----------------------------------
_interrupt


	; Update timers
	;---------------------------------------------------------------------------------
inc_timer
			bank	timer_bank
			inc	timer_int		;increments every isr
			cjne	timer_int,#mscount,:it_done	; isr cycles for 1 msec
			clr	timer_int
			dec	timer_1m		;decrements every msec
:it_done
inc_timer_done


			call	app_isr			;call application isr routines


	; Update ra with buffered data
	;---------------------------------------------------------------------------------
			mov	w,ra_dir_buf
			mov	!ra,w


	; Run uarts
	;---------------------------------------------------------------------------------


hostVP
			bank	hu_bank


	;host uart receive				;receive only when enabled


			jnb	hu_rx_enable,:hu_rx_done
:receive
	        	sb	pinDQ			; get current rx bit
	        	clc 
	        	snb	pinDQ
	        	stc
			test	hu_rx_count		; currently receiving byte?
			sz 
			jmp	:hur1               	; if so, jump ahead
			mov	w,#9			; in case start, ready 9 bits
			sc				; skip ahead if not start bit
			mov	hu_rx_count,w		; it is, so renew bit count
			mov	w,#hostStDElay
			mov	hu_rx_divide,w 
:hur1
			decsz	hu_rx_divide		; middle of next bit?
          		jmp	:hu_rx_done
			dec	hu_rx_count		; last bit?
			sz				; if not
			rr      hu_rx_byte		;  then save bit
			jnz	:hur2			;  and exit
			setb	hu_rx_flag
:hur2
			mov	w,#hostDivide
			mov	hu_rx_divide,w
:hu_rx_done


	;host uart transmit				;transmit only when enabled


			jnb	hu_tx_enable,:hu_tx_done
:transmit
			decsz	hu_tx_divide		; only execute the transmit routine
			jmp	:hu_tx_done 
			mov	w,#hostDivide
			mov	hu_tx_divide,w
			test    hu_tx_count		; are we sending?
			snz
			jmp	:hu_tx_done
			clc				; yes, ready stop bit
			rr	hu_tx_high		;  and shift to next bit
			rr	hu_tx_low
			dec	hu_tx_count		; decrement bit counter
			snb	hu_tx_low.6		; output next bit
			clrb	ra_dir_buf.hostDQ	; update term_port_buf state
			sb	hu_tx_low.6		; (pin change state occurs next interrupt)
			setb	ra_dir_buf.hostDQ
:hu_tx_done


termVP
			bank	tu_bank


	;term uart receive


			jb	tu_cts,:term_rxdone	;only receive if rx is not cts
:receive
	        	sb	pinRX			; get current rx bit
	        	clc
	        	snb	pinRX
	        	stc
			test	tu_rx_count		; currently receiving byte?
			sz
			jmp	:term_rx1		; ;if so, jump ahead
			mov	w,--tu_bits		; in case start, ready bits
			sc				; skip ahead if not start bit
			mov     tu_rx_count,w		; it is, so renew bit count
			call	tu_StDelay
			mov     tu_rx_divide,w
:term_rx1
			decsz	tu_rx_divide		; middle of next bit?
          		jmp	:term_rxdone
			dec     tu_rx_count		; last bit?
			jz	:term_rx2		;   if not
			rr      tu_rx_byte		;   then save bit
			movb	tu_rx_parity,C		;   and save b0
			jmp	:term_rx3		;   and exit
:term_rx2
			movb	C,tu_rx_parity		;get last saved b0
			movb	tu_rx_parity,tu_rx_byte.7	;save possible parity bit
			sb	tu_bits.1
			rr	tu_rx_byte		;adjust for 7 databits + no parity (tu_bits=9)
			sb	tu_bits.1
			rr	tu_rx_byte
			snb	tu_bits.0
			rl	tu_rx_byte		;adjust for 8 databits + parity (tu_bits=11)
			snb	tu_7bits
			clrb	tu_rx_byte.7		;clear b7 for 7 databits
			mov	w,tu_rx_byte		;save byte to allow receive another byte
			mov	tu_char,w		;  while processing this one
			setb	tu_rx_flag		;mark byte received
:term_rx3
			call	tu_divide
			mov	tu_rx_divide,w
:term_rxdone


	;term uart transmit


			jb	tu_rts,:term_txdone	;only transmit if tx is not rts
:transmit
			decsz	tu_tx_divide		; only execute the transmit routine
			jmp	:term_txdone
			call	tu_divide
			mov	tu_tx_divide,w
			test    tu_tx_count		; are we sending?
			snz
			jmp	:term_txdone
			clc				; yes, ready stop bit
			rr	tu_tx_high		;  and shift to next bit
			rr	tu_tx_low
			dec	tu_tx_count		; decrement bit counter
			snb	tu_tx_low.5		; output next bit
			clrb	ra_dir_buf.termTX	; update term_port_buf state
			sb	tu_tx_low.5		; (pin change state occurs next interrupt)
			setb	ra_dir_buf.termTX
:term_txdone


	; Set Interrupt Rate
	;---------------------------------------------------------------------------------
isr_end
			mov	w,#(-int_period)&$FF	; refresh RTCC on return
			retiw				; return from the interrupt


;-------------- end of the Interrupt Service Routines ------------------------------------


	;RESET VECTOR	Program execution begins here on power-up or after a reset
	;---------------------------------------------------------------------------------
reset_entry


	; Initialise all port configuration
	;---------------------------------------------------------------------------------
M_WKED			equ	$0A
M_WKEN			equ	$0B
M_ST			equ	$0C
M_LVL			equ	$0D
M_PLP			equ	$0E
M_TRIS			equ	$0F


			mov	m,#M_WKED		; point mode to WKED
			mov	!rb,#RB_wked
			mov	m,#M_WKEN		; point mode to WKEN
			mov	!rb,#RB_wken
			mov	m,#M_ST			; point mode to ST
			mov	!rc,#RC_st
			mov	!rb,#RB_st
			mov	m,#M_LVL		; point mode to LVL
			mov	!rc,#RC_lvl
			mov	!rb,#RB_lvl
			mov	!ra,#RA_lvl
			mov	m,#M_PLP		; point mode to PLP
			mov	!rc,#RC_plp
			mov	!rb,#RB_plp
			mov	!ra,#RA_plp
			mov	rc,#RC_latch		; init latches
			mov	rb,#RB_latch
			mov	ra,#RA_latch
			mov	m,#M_TRIS		; point mode to TRIS
			mov	!rc,#RC_tris
			mov	!rb,#RB_tris
			mov	!ra,#RA_tris


	; Clear all Data RAM locations
	;---------------------------------------------------------------------------------
			mov	fsr,#global_org
:zero_global
			clr	indf
			inc	fsr
			cjne	fsr,#$10,:zero_global
:zero_ram
			clr	indf
			inc	fsr
			jz	:zero_end
			setb	fsr.4
			jmp	:zero_ram
:zero_end


	; Initialize registers
	;---------------------------------------------------------------------------------
			mov	ra_dir_buf,#$FF
			bank	hu_bank
			mov	hu_state,#$C0	;pin levels high
			bank	tu_bank
			setb	tu_tx_enable	;enable transmit
			setb	tu_rx_enable	;enable receive
			mov	tu_bits,#10	;set correct bits for startup (8N1,1200)


	; Setup option register. 
	;---------------------------------------------------------------------------------
RTCC_ON			equ	%10000000	;Enables RTCC at address $01 (RTW hi)
						;*WREG at address $01 (RTW lo) by default
RTCC_ID			equ	%01000000	;Disables RTCC edge interrupt (RTE_IE hi)
						;*RTCC edge interrupt (RTE_IE lo) enabled by default
RTCC_INC_EXT		equ	%00100000	;Sets RTCC increment on RTCC pin transition (RTS hi)
						;*RTCC increment on internal instruction (RTS lo) is defalut
RTCC_FE			equ	%00010000	;Sets RTCC to increment on falling edge (RTE_ES hi)
						;*RTCC to increment on rising edge (RTE_ES lo) is default
RTCC_PS_ON		equ	%00000000	;Assigns prescaler to RTCC (PSA lo)
RTCC_PS_OFF		equ	%00001000	;Assigns prescaler to WDT (PSA hi)
PS_000			equ	%00000000	;RTCC = 1:2, WDT = 1:1
PS_001			equ	%00000001	;RTCC = 1:4, WDT = 1:2
PS_010			equ	%00000010	;RTCC = 1:8, WDT = 1:4
PS_011			equ	%00000011	;RTCC = 1:16, WDT = 1:8
PS_100			equ	%00000100	;RTCC = 1:32, WDT = 1:16
PS_101			equ	%00000101	;RTCC = 1:64, WDT = 1:32
PS_110			equ	%00000110	;RTCC = 1:128, WDT = 1:64
PS_111			equ	%00000111	;RTCC = 1:256, WDT = 1:128


			mov	w,#RTCC_PS_OFF	;setup option register
			mov	!option,w


	; Mainloop
	;---------------------------------------------------------------------------------
mainloop
			call	host_input_task		;handle host data input
			call	host_output_task	;handle host data output
			call	host_command_task	;handle host command
			call	@term_input_task	;handle keypad input
			call	@term_output_task	;handle display output
			call	app_main		;call application mainloop
			jmp	mainloop


	;put w into hu_tx_buf
	;---------------------------------------------------------------------------------


_enqueue_hu_tx
			bank	hu_bank
			snb	hu_tx_full
			setb	hu_tx_overrun
			snb	hu_tx_full
			retp
			mov	temp,w
			mov	w,<>hu_head
			and	w,#$0F
			mov	fsr,w			;calculate buffer address
			mov	w,#hu_tx_buf
			add	fsr,w
			mov	w,temp			;store received byte
			mov	indf,w
			bank	hu_bank
			add	hu_num,#16		;adjust byte count
			add	hu_head,#16
			clrb	hu_head.7		;wrap around head
			setb	hu_tx_data		;mark holding data
			retp


	;put w into hu_rx_buf
	;---------------------------------------------------------------------------------


_enqueue_hu_rx
			bank	hu_bank
			snb	hu_rx_full
			setb	hu_rx_overrun
			snb	hu_rx_full
			retp
			mov	temp,w
			mov	w,hu_head
			and	w,#$0F
			mov	fsr,w			;calculate buffer address
			mov	w,#hu_rx_buf
			add	fsr,w
			mov	w,temp			;store received byte
			mov	indf,w
			bank	hu_bank
			add	hu_num,#1		;adjust byte count
			add	hu_head,#1
			clrb	hu_head.3		;wrap around head
			setb	hu_rx_data		;mark holding data
			retp


	;get w from hu_tx_buf
	;---------------------------------------------------------------------------------


_dequeue_hu_tx
			bank	hu_bank
			sb	hu_tx_data
			retp
			mov	w,<>hu_tail		;get next byte from buffer
			and	w,#$0F
			mov	fsr,w
			mov	w,#hu_tx_buf
			add	fsr,w
			mov	w,indf
			mov	temp,w
			bank	hu_bank
			sub	hu_num,#16		;adjust byte count
			mov	w,<>hu_num
			and	w,#$0F
			snz
			clrb	hu_tx_data
			add	hu_tail,#16
			clrb	hu_tail.7		;wrap around tail
			mov	w,temp
			retp


	;get w from hu_rx_buf
	;---------------------------------------------------------------------------------


_dequeue_hu_rx
			bank	hu_bank
			sb	hu_rx_data		;test if any byte in buffer
			retp				;if not then exit
			mov	w,hu_tail		;get next byte from buffer
			and	w,#$0F
			mov	fsr,w
			mov	w,#hu_rx_buf
			add	fsr,w
			mov	w,indf
			mov	temp,w
			bank	hu_bank
			sub	hu_num,#1		;adjust byte count
			mov	w,hu_num
			and	w,#$0F
			snz
			clrb	hu_rx_data		;mark buffer empty
			add	hu_tail,#1
			clrb	hu_tail.3		;wrap around tail
			mov	w,temp
			retp


	;put w into tu_tx_buf
	;---------------------------------------------------------------------------------


_enqueue_tu_tx
			bank	tu_bank
			snb	tu_tx_full
			setb	tu_tx_overrun
			snb	tu_tx_full
			retp
			mov	temp,w
			mov	w,<>tu_head
			and	w,#$0F
			mov	fsr,w			;calculate buffer address
			mov	w,#tu_tx_buf
			add	fsr,w
			mov	w,temp			;store received byte
			mov	indf,w
			bank	tu_bank
			add	tu_num,#16		;adjust byte count
			add	tu_head,#16
			clrb	tu_head.7		;wrap around head
			setb	tu_tx_data		;mark holding data
			retp


	;put w into tu_rx_buf
	;---------------------------------------------------------------------------------


_enqueue_tu_rx
			bank	tu_bank
			snb	tu_rx_full
			setb	tu_rx_overrun
			snb	tu_rx_full
			retp
			mov	temp,w
			mov	w,tu_head
			and	w,#$0F
			mov	fsr,w			;calculate buffer address
			mov	w,#tu_rx_buf
			add	fsr,w
			mov	w,temp			;store received byte
			mov	indf,w
			bank	tu_bank
			add	tu_num,#1		;adjust byte count
			add	tu_head,#1
			clrb	tu_head.3		;wrap around head
			setb	tu_rx_data		;mark holding data
			retp


	;get w from tu_tx_buf
	;---------------------------------------------------------------------------------


_dequeue_tu_tx
			bank	tu_bank
			sb	tu_tx_data
			retp
			mov	w,<>tu_tail		;get next byte from buffer
			and	w,#$0F
			mov	fsr,w
			mov	w,#tu_tx_buf
			add	fsr,w
			mov	w,indf
			mov	temp,w
			bank	tu_bank
			sub	tu_num,#16		;adjust byte count
			mov	w,<>tu_num
			and	w,#$0F
			snz
			clrb	tu_tx_data
			add	tu_tail,#16
			clrb	tu_tail.7		;wrap around tail
			mov	w,temp
			retp


	;get w from tu_rx_buf
	;---------------------------------------------------------------------------------


_dequeue_tu_rx
			bank	tu_bank
			sb	tu_rx_data		;test if any byte in buffer
			retp				;if not then exit
			mov	w,tu_tail		;get next byte from buffer
			and	w,#$0F
			mov	fsr,w
			mov	w,#tu_rx_buf
			add	fsr,w
			mov	w,indf
			mov	temp,w
			bank	tu_bank
			sub	tu_num,#1		;adjust byte count
			mov	w,tu_num
			and	w,#$0F
			snz
			clrb	tu_rx_data		;mark buffer empty
			add	tu_tail,#1
			clrb	tu_tail.3		;wrap around tail
			mov	w,temp
			retp


	; host input task
	;---------------------------------------------------------------------------------
_host_input_task
			bank	hu_bank
			sb	hu_rx_enable		;may we receive?
			retp				;no
			sb	hu_rx_flag		;byte received?
			retp				;no
			mov	w,hu_rx_byte
			clrb	hu_rx_flag
			jmp	enqueue_hu_rx		;store byte in buffer


	; host output task
	;---------------------------------------------------------------------------------
_host_output_task
			bank	hu_bank
			sb	hu_tx_enable		;may we transmit?
			retp				;no
			sb	hu_tx_data		;byte to transmit?
			retp				;no
			test	hu_tx_count		;transmitter ready?
			sz
			retp				;no
			call	dequeue_hu_tx		;get byte to transmit
			not     w			;ready bits (inverse logic)
			mov     hu_tx_high,w         	;store data byte
			setb    hu_tx_low.7          	;set up start bit
			mov     w,#10			;1 start + 8 data + 1 stop bit
			mov     hu_tx_count,w		;VP starts transmitting
			retp


;-------------- codepage 0 end -----------------------------------------------------------




;*****************************************************************************************
			org	CODEPAGE_1
;*****************************************************************************************
			jmp	$	;generates error if code above crosses page boundary


term_input_task		jmp	_term_input_task	;receives term bytes into tu_rx_buf
term_output_task	jmp	_term_output_task	;transmit term bytes from tu_tx_buf


	; set parity and stopbits for byte in tu_tx_high (just about to transmit)
	;---------------------------------------------------------------------------------
tu_set_parity
			snb	tu_7bits		;8 databits?
			clrb	tu_tx_high.7		;no, ready stop bit
			mov	w,tu_tx_high
			call	even_parity
:odd
			jnb	tu_parity_odd,:even
			movb	C,/temp.0
			jmp	:parity
:even
			jnb	tu_parity_even,:exit
			movb	C,temp.0
:parity
			sb	tu_7bits		;7 databits?
			retp				;no
			movb	tu_tx_high.7,C		;move parity into b7
:exit
			clc				;ready stop bit
			retp


	; calculate even parity for w
	; result in temp.0
	;---------------------------------------------------------------------------------
even_parity
			mov	temp,w


	;calculation code by John Payson, as found on sxlist.com
	;This routine will leave the parity of temp in temp.0
	;while blenderizing most of the rest of temp


			mov	w,<>temp	; temp = abcdefgh
			xor	temp,w
			mov	w,>>temp
			xor	temp,w


	; at this point, the parity for half the bits
	; (a, b, e, and f) is in bit 2 of temp, and the
	; parity for the other half (bits c, d, g, and h)
	; is in bit 0 of temp.


			snb	temp.2		; if the parity of (a,b,e,f) is 0,
                        			; then the parity of (a,b,c,d,e,f,g,h)
                        			; is equal to the parity of (c,d,g,h)...
                        			; which is already in bit 0, so skip ahead.


			inc	temp		; otherwise, the parity of (a,b,e,f) is 1,
						; so the parity of (a,b,c,d,e,f,g,h) is
						; NOT equal to the parity of (c,d,g,h).
						; invert bit 0.


	; at this point, bit 0 contains the parity of
	; (a,b,c,d,e,f,g,h).


			retp


	; term output handshake off (inform remote deivce to stop transmitting)
	; either RTS (on TX pin) or XON/XOFF
	;---------------------------------------------------------------------------------
tu_hs_off
			sb	tu_rx_enable
			retp				;hs already off
			mov	w,tu_config
			and	w,#$14			;test if tu_xonxoff or tu_rts set
			snz
			retp				;no handshake
			mov	w,tu_num
			and	w,#$0F			;extract bytes in tu_rx_buf
			jb	tu_xonxoff,:tu_xoff	;xonxoff handshake
			xor	w,#$05			;disable receive if 5 bytes in tu_rx_buf
			sz
			retp
			clrb	tu_rx_enable
			setb	ra_dir_buf.termTX	;turn off handshake
			retp
:tu_xoff
			xor	w,#$02			;disable receive if 2 byte in tu_rx_buf
			sz
			retp
:tu_xoff1
			test	tu_tx_count
			jnz	:tu_xoff1		;wait for not busy
			clrb	tu_rx_enable
			mov	w,#XOFF			;send XOFF
			jmp	tu_output_w


	; term output handshake on (inform remote deivce to start transmitting)
	; either RTS (on TX pin) or XON/XOFF
	;---------------------------------------------------------------------------------
tu_hs_on
			snb	tu_rx_enable
			retp				;hs already on
			mov	w,tu_config
			and	w,#$14			;test if tu_xonxoff or tu_rts set
			snz
			retp				;no handshake
			mov	w,tu_num
			and	w,#$0F			;extract bytes in tu_rx_buf
			jb	tu_xonxoff,:tu_xon	;xonxoff handshake
			xor	w,#$01			;enable receive if only 1 byte in tu_rx_buf
			sz
			retp
			setb	tu_rx_enable
			clrb	ra_dir_buf.termTX	;turn on handshake
			retp
:tu_xon
			xor	w,#$01			;enable receive if only 1 byte in tu_rx_buf
			sz
			retp
			setb	tu_rx_enable
:tu_xon1
			test	tu_tx_count
			jnz	:tu_xon1		;wait for not busy
			mov	w,#XON			;send XOFF
			jmp	tu_output_w


	; term input task
	;---------------------------------------------------------------------------------
_term_input_task
			bank	tu_bank
			snb	tu_cts			;test if rx is used as cts
			retp				;yes, so do not receive
			sb	tu_rx_flag		;byte received?
			retp				;no
			clrb	tu_rx_flag		;clear receive flag (byte in tu_char)
			jnb	tu_xonxoff,:tu_input	;jump ahead if no xon/xoff
			mov	w,tu_char
			mov	temp,w			;test for xon/xoff
			xor	w,#XON			;test if byte received is XON
			snz
			setb	tu_tx_enable		;yes, so enable tx
			snz
			retp				;and exit (XON is not stored)
			mov	w,tu_char
			xor	w,#XOFF			;test if byte received is XOFF
			snz
			clrb	tu_tx_enable		;yes, so disable tx
			snz
			retp				;and exit (XOFF is not stored)
:tu_input
			jb	tu_parity_odd,:parity
			jb	tu_parity_even,:parity
			jmp	:exit
:parity
			mov	w,tu_char
			call	even_parity
			snb	tu_parity_odd
			not	temp
			and	temp,#$01		;extract calculated parity bit
			addb	temp,tu_rx_parity	;add received parity bit
			snb	temp.0			;temp=0 or temp=2 if parity bits match
			setb	term_parity_error
:exit
			mov	w,tu_char
			call	@enqueue_tu_rx		;put byte in queue
			jmp	@tu_hs_off		;turn off handshake if required


	; term output task
	;---------------------------------------------------------------------------------
_term_output_task
			bank	tu_bank
			snb	tu_rts			;test if tx is used as rts
			retp				;tx is handshake so do not send
			sb	tu_tx_data		;data to send?
			retp				;no
			test	tu_tx_count		;transmitter busy?
			sz
			retp				;yes
			jb	tu_xonxoff,tu_hs_soft
			jnb	tu_cts,tu_output	;rx not used as handshake
tu_hs_cts						;test if handshake input on
			jnb	pinRX,tu_output		;if so then send
			retp
tu_hs_soft
			sb	tu_tx_enable		;send only if enabled
			retp
tu_output
			call	@dequeue_tu_tx
tu_output_w
			not	w			;ready bits (inverse logic)
			mov	tu_tx_high,w         	;store data byte
			setb	tu_tx_low.7          	;set up start bit
			call	tu_set_parity
			rr	tu_tx_high		;shiftin parity bit or stop bit
			rr	tu_tx_low
			mov	w,tu_bits		;start + data + parity + stop
			mov	tu_tx_count,w
			retp


	; detect transition on pinWE
	; actions when neg edge:
	;   - disable transmitter
	;   - clear buffers
	;   - initialize receive registers
	;   - enable receiver
	;   - move to state STATE_RECEIVE_COMMAND
	; actions when pos edge:
	;   - disable receiver
	;   - initialize transmit registers
	;   - enable transmitter
	;   - move to state STATE_TRANSMIT_RESPONSE
	;---------------------------------------------------------------------------------
checkPinWE
			bank	hu_bank
			snb	hu_tx_enable
			retp
			movb	pinWE_past,pinWE_present
			movb	pinWE_present,pinWE
			mov	w,hu_state
			and	w,#$C0			;extract pinWE states
			xor	w,#$40			;L->H transition?
			jz	:init_transmit		; yes
			xor	w,#$C0			;H->L transition
			sz
			retp				; no
:init_receive
			clrb	hu_tx_enable		;disable transmitter
			clr	hu_head			;clear buffers parameters (tx and rx)
			clr	hu_tail
			clr	hu_num
			clrb	hu_rx_data
			clrb	hu_rx_overrun
			clrb	hu_tx_data
			clrb	hu_tx_overrun
			clr	hu_rx_count		;initialize receive registers
			clrb	hu_rx_flag
			setb	hu_rx_enable		;enable receiver
			and	hu_state,#$C0		;keep pin states
			or	hu_state,#STATE_RECEIVE_COMMAND	;move to state RECEIVE_COMMAND
			retp
:init_transmit
			clrb	hu_rx_enable		;disable receiver
			clr	hu_tx_count		;initialize transmit registers
			setb	hu_tx_enable		;enable transmitter
			and	hu_state,#$C0		;keep pin states
			or	hu_state,#STATE_TRANSMIT_RESPONSE	;move to state TRANSMIT_RESPONSE
			retp


	; Delay approx. 1 second
	;--------------------------------------------------------------------
delay_1s00
			mov	w,#250
			call	delay_w
delay_0s75
			mov	w,#250
			call	delay_w


	; Delay approx. 0.5 second
	;--------------------------------------------------------------------
delay_0s50
			mov	w,#250
			call	delay_w
delay_0s25
			mov	w,#250


	; Delay w milliseconds
	;--------------------------------------------------------------------
delay_w
			bank	timer_bank
			mov	timer_1m,w
:wait
			test	timer_1m
			jnz	:wait
			retp


	; Host command handler.
	; This is the host command dispatcher entry.
	; It is always called when command or parameters are received.
	;---------------------------------------------------------------------------------


HOST_COMMANDS		equ	$08			;number of host commands


host_command_handler
			bank	hu_bank
			snb	cmd_done
			jmp	clear_input		;command already finished
			csb	hu_command,#HOST_COMMANDS
			jmp	invalid_host_command
			mov	w,hu_command
			add	pc,w
			jmp	_DV_GET_VERSION		;00 device get version
			jmp	_TM_SET_CONFIG		;01 terminal set configuration
			jmp	_TM_READ_DATA		;02 terminal read data
			jmp	_TM_WRITE_DATA		;03 terminal write data
			jmp	_IO_READ_PORTB		;04 i/o read port B
			jmp	_IO_WRITE_PORTB_DIR	;05 i/o write port B direction
			jmp	_IO_READ_PORTC		;06 i/o read port C
			jmp	_IO_WRITE_PORTC_DIR	;07 i/o write port C direction


			;new commands here


	; Invalid commands must remove input data to prevent hu_rx_buf overrun
	;---------------------------------------------------------------------------------
invalid_host_command
			setb	cmd_invalid
clear_input
			call	@dequeue_hu_rx		;get rid of any input


	; Mark command finished
	;---------------------------------------------------------------------------------
host_command_exit
			bank	hu_bank
			setb	cmd_done
			retp


	; Command $00	-	Get device version.
	; Parameters	-	none
	; Description	-	This command returns 8 bytes to the host
	;---------------------------------------------------------------------------------
_DV_GET_VERSION
			clrb	cmd_parameter_missing
			sb	hu_tx_enable		;may we transmit?
			retp				;no
			and	hu_head,#$0F		;reset hu_tx_buf parameters
			and	hu_tail,#$0F
			and	hu_num,#$0F
			or	hu_num,#$80
			bank	hu_buf_bank
			mov	hu_tx_buf+$0,#'T'	;co-processor identification
			mov	hu_tx_buf+$1,#'E'
			mov	hu_tx_buf+$2,#'M'
			mov	hu_tx_buf+$3,#'P'
			mov	hu_tx_buf+$4,#'L'
			mov	hu_tx_buf+$5,#'A'
			mov	hu_tx_buf+$6,#'T'
			mov	hu_tx_buf+$7,#'E'
			bank	hu_bank
			setb	hu_tx_data
			jmp	host_command_exit


	; Command $01	-	terminal set configuration
	; Parameters	-	config (default value = 0)
	;			b7  b6  b5  b4  b3  b2  b1  b0
	;			|   |   |   |   |   |   +---+-- baud (0 to 3 -> see baud table)
	;			|   |   |   |   |   +---------- 1 if TX used as RTS handshake
	;			|   |   |   |   +-------------- 1 if RX used as CTS handshake
	;			|   |   |   +------------------ 1 if xon/xoff handshake
	;			|   |   +---------------------- 1 for 7 databits (default = 8 databits)
	;			|   +-------------------------- 1 for even parity (default = no parity)
	;			+------------------------------ 1 for odd parity
	; Description	-	set terminal baud, parity, handshake
	;---------------------------------------------------------------------------------
_TM_SET_CONFIG
			jb	hu_tx_enable,:output	;prevent deadlock if not received byte
			sb	hu_rx_data
			retp
			clrb	cmd_parameter_missing
			call	@dequeue_hu_rx
			bank	tu_bank
			mov	tu_config,w
			mov	tu_bits,#10		;assume 8 databits and no parity
			snb	tu_parity_even
			setb	tu_bits.0		;if parity set bits to 11
			snb	tu_parity_odd
			setb	tu_bits.0
			snb	tu_7bits
			dec	tu_bits			;if 7 databits decrement bits
:output
			jmp	host_command_exit


	; Command $02	-	read terminal data (one byte)
	; Parameters	-	none
	; Description	-	Return current terminal character, if any, else 0
	;---------------------------------------------------------------------------------
_TM_READ_DATA
			clrb	cmd_parameter_missing
			sb	hu_tx_enable
			retp
			clr	w
			bank	tu_bank
			jnb	tu_rx_data,:tm_rd
			call	@dequeue_tu_rx
:tm_rd
			call	@enqueue_hu_tx
			bank	tu_bank
			call	@tu_hs_on		;turn on handshake if required
			jmp	host_command_exit


	; Command $03	-	write terminal data
	; Parameters	-	bytes to write
	; Description	-	write one or more characters to term
	;---------------------------------------------------------------------------------
_TM_WRITE_DATA
			clrb	cmd_parameter_missing
			jb	hu_tx_enable,:output
			sb	hu_rx_data
			retp
			call	@dequeue_hu_rx
			call	@enqueue_tu_tx
			retp
:output
			jmp	host_command_exit


	; Command $04	-	read port B
	; Parameters	-	none
	; Description	-	Return current port B input value to host
	;---------------------------------------------------------------------------------
_IO_READ_PORTB
			clrb	cmd_parameter_missing
			sb	hu_tx_enable		;may we transmit
			retp				;no
			mov	w,rb
			call	@enqueue_hu_tx
			jmp	host_command_exit


	; Command $05	-	write value to port B direction register
	; Parameters	-	dirmask
	; Description	-	Make port B pins low output (bit=0) or input (bit=1)
	;---------------------------------------------------------------------------------
_IO_WRITE_PORTB_DIR
			jb	hu_tx_enable,:output	;prevent deadlock if not received byte
			sb	hu_rx_data
			retp
			clrb	cmd_parameter_missing
			call	@dequeue_hu_rx
			mov	!rb,w
:output
			jmp	host_command_exit


	; Command $06	-	read port C
	; Parameters	-	none
	; Description	-	Return current port C input value to host
	;---------------------------------------------------------------------------------
_IO_READ_PORTC
			clrb	cmd_parameter_missing
			sb	hu_tx_enable		;may we transmit
			retp				;no
			mov	w,rc
			call	@enqueue_hu_tx
			jmp	host_command_exit


	; Command $07	-	write value to port C direction register
	; Parameters	-	dirmask
	; Description	-	Make port C pins low output (bit=0) or input (bit=1)
	;---------------------------------------------------------------------------------
_IO_WRITE_PORTC_DIR
			jb	hu_tx_enable,:output	;prevent deadlock if not received byte
			sb	hu_rx_data
			retp
			clrb	cmd_parameter_missing
			call	@dequeue_hu_rx
			mov	!rc,w
:output
			jmp	host_command_exit


;-------------- codepage 1 end -----------------------------------------------------------




;*****************************************************************************************
			org	CODEPAGE_2
;*****************************************************************************************
			jmp	$	;generates error if code above crosses page boundary


;-------------- codepage 2 end -----------------------------------------------------------




;*****************************************************************************************
			org	CODEPAGE_3
;*****************************************************************************************
			jmp	$	;generates error if code above crosses page boundary


;-------------- codepage 3 end ------------------------------------------------------