
; -----------------------------------------------------------------------------
;				  asm120.s03
; -----------------------------------------------------------------------------
; DESCRIPTION:	This is a skeleton program for the C8051F120 evaluation board
;		from Silicon Labs.  It demonstrates Jan's line editor
;		subroutine.
;
; REVISIONS:	14 May 07 - RAC - Adapted from Jan's initial implementation
; -----------------------------------------------------------------------------

	LSTOUT-				; Don't list header file
$f120.inc				; Hardware register and bit definitions
	LSTOUT+				; Listing back on

;------------------------------------------------------------------------------
;			       Names for Numbers
;------------------------------------------------------------------------------

LED		EQU	P1.6		; LED on 'F120 evaluation board

BEEP_CHR	EQU	07h		; Names for ASCII characters
BS_CHR		EQU	08h
CR_CHR		EQU	0Dh
LF_CHR		EQU	0Ah
SPACE_CHR	EQU	20h

RX_BUFFER_SIZE	EQU	10		; Test buffer size

;------------------------------------------------------------------------------
;				 Data Segment
;------------------------------------------------------------------------------

;-----	The data segment loads at address 8, immediately above the registers.

	RSEG	DATA
RxBuffer:
	DS	RX_BUFFER_SIZE

;------------------------------------------------------------------------------
;				 Stack Segment
;------------------------------------------------------------------------------

;-----  The stack starts immediately after all the other RAM variables, and
;       expands upward from there.

	RSEG	CSTACK
StackBegin:
	DS	1

;------------------------------------------------------------------------------
;				  Boot Vector
;------------------------------------------------------------------------------

	COMMON	INTVEC			; Loads at address zero
Startup:
	JMP	Main			; Jump to main routine

;-----	Timer 0 interrupt vector

	ORG	Startup+0Bh

;------------------------------------------------------------------------------
;				     Main
;------------------------------------------------------------------------------
; DESCRIPTION:	Shuts off the watchdog timer, initializes the hardware, then
;		spins a loop forever spitting characters out the serial port.
;
; REVISIONS:	14 May 07 - RAC - Adapted from a similar program written in C.
;------------------------------------------------------------------------------

	RSEG	RCODE			; Loads after INTVEC

Main:
	MOV	SP,#StackBegin-1	; Set up the stack pointer
	CALL	InitializeHardware	; Go set up the chip

MainLoop:

;-----	Get a line of input from the human user

	call	ReceiveString

;-----	Print the length of the received string

	call	TxCRLF			; Print CR/LF
	mov	a,r0			; Get pointer to end of data
	clr	c			; Prepare to subtract
	subb	a,#RxBuffer		; Calculate string length
	call	TxHexa			; Print it
	call	TxCRLF			; Print CR/LF

;-----	Print the string itself

	mov	r0,#RxBuffer		; Start at the beginning of the buffer
Loop1:
	mov	a,@r0			; Get next character
	jz	Loop2			; It's zero - we're done
	call	TxChar			; Not zero - go print it
	inc	r0			; Advance to next character
	sjmp	Loop1			; Repeat for entire string
Loop2:
	call	TxCRLF			; Print CR/LF

	JMP	MainLoop		; Go do it again

; -----------------------------------------------------------------------------
;				    TxChar
; -----------------------------------------------------------------------------
; DESCRIPTION:	Sends the byte in the accumulator out the UART.
;
; REVISIONS:	15 May 07 - RAC - Adapted from an old putchar() routine
; -----------------------------------------------------------------------------

TxChar:
	MOV	SBUF,A			; Send the character
	JNB	TI0,$			; Spin until it's sent
	CLR	TI0			; Reset done flag
	RET				; Done

; -----------------------------------------------------------------------------
;				    RxChar
; -----------------------------------------------------------------------------
; DESCRIPTION:	Waits for a byte from the UART and returns it in the
;		accumulator
;
; REVISIONS:	15 May 07 - RAC - Adapted from an old putchar() routine
; -----------------------------------------------------------------------------

RxChar:
	JNB	RI0,$			; Wait for incoming character
	CLR	RI0			; Clear flag
	MOV	A,SBUF			; Got it
	RET

; -----------------------------------------------------------------------------
;			      InitializeHardware
; -----------------------------------------------------------------------------
; DESCRIPTION:	Sets up the C8051F120 hardware
;
; REVISIONS:	15 May 07 - RAC - Extracted from Main
; -----------------------------------------------------------------------------

InitializeHardware:

;-----	Start with general initialization

	MOV	WDTCN,#0DEh		; Disable the watchdog timer
	MOV	WDTCN,#0ADh

	MOV	SFRPAGE,#CONFIG_PAGE	; Switch to config page
	MOV	XBR2,#40h		; Enable Port I/O crossbar
	ORL	P0MDOUT,#1		; Set UART Tx for push/pull out
	ORL	P1MDOUT,#40h		; Set P1.6 for push/pull out

;-----	Set up Timer 1 as the on-board serial port baud rate generator

	MOV	SFRPAGE,#TIMER01_PAGE	; Timer 1 config page
	MOV	TH1,#0F4h		; Reload value for 115.2 Kbps
	ANL	TMOD,#0Fh		; Clear upper nibble of TMOD
	ORL	TMOD,#20h		; Put Timer 1 into Mode 2
	ORL	CKCON,#10h		; SYSCLK clocks Timer 1
	SETB	TR1			; Start Timer 1

;-----	Set up the onboard UART

	MOV	SFRPAGE,#CONFIG_PAGE	; Switch to config page
	MOV	XBR0,#04h		; Enable UART to I/O pins
	MOV	SFRPAGE,#TIMER01_PAGE	; Timer 1 config page
	MOV	SCON0,#50h		; Mode 1, Rx enabled
	ORL	SSTA0,#10h		; Disable UART divide by 2

;-----	Switch from the internal oscillator to the external crystal oscillator

	MOV	SFRPAGE,#CONFIG_PAGE	; Switch to config page
	MOV	OSCXCN,#67h		; Enable the crystal oscillator

	MOV	R0,#5			; Delay for about 1.2 msec
TimingLoop:
	MOV	R1,#255
	DJNZ	R1,$
	DJNZ	R0,TimingLoop

NotStableYet:				; Wait for oscillator to
	MOV	A,OSCXCN		;  stabilize
	ANL	A,#80h
	JZ	NotStableYet

	MOV	CLKSEL,#1		; Switch to external oscillator

	MOV	SFRPAGE,#LEGACY_PAGE	; Return to default SFR page
	RET				; Done

; -----------------------------------------------------------------------------
;			        Print Utilities
; -----------------------------------------------------------------------------

;-----	Prints CR/LF

TxCRLF:
	mov	a,#CR_CHR		; Get a carriage return
	call	TxChar			; Go print it
	mov	a,#LF_CHR		; Get a line feed
	call	TxChar			; Go print it
	ret				; Done

;-----	Prints accumulator contents as a 2-digit hex number

TxHexa:
	push	acc			; Save for second digit

	swap	a			; Isolate most-significant nibble
	anl	a,#0Fh
	add	a,#TxHexaT-TxHexaX1	; Index into table of hex digits
	movc	a,@a+pc			; Get hex digit
TxHexaX1:
	call	TxChar			; Go print it

	pop	acc			; Recall and isolate least significant
	anl	a,#0Fh			;  nibble
	add	a,#TxHexaT-TxHexaX2	; Index into table of hex digits
	movc	a,@a+pc			; Get hex digit
TxHexaX2:
	call	TxChar			; Go print it
	ret				; Done

TxHexaT:
	db   '0123456789ABCDEF'		; Table of hex digits

;------------------------------------------------------------------------------
;				 ReceiveString
;------------------------------------------------------------------------------
; DESCRIPTION:  This subroutine is called from many places to receive a line of
;               input from a human user via a terminal connected to the onboard
;               UART.  Each line of input starts with the first character
;               entered after this subroutine is called, and ends with a
;               carriage return.
;
;               This subroutine accumulates the data in a buffer called
;               'RxBuffer', which is located in the internal data RAM and is
;               accessed only via indirect addressing (i.e.  can be located in
;               the upper 128 bytes).  The symbol RX_BUFFER_SIZE gives the size
;               of the buffer, in bytes.  Note that to leave room for the
;               trailing zero, the actual number of characters received is
;               limited to RX_BUFFER_SIZE-1.
;
;               On entry, this function does minor internal housekeeping to
;               prepare for a new line of input (i.e.  resets the pointer :-))
;               It then receives characters one by one from the user.  It
;               handles each received character as follows:
;
;                 - Backspace (08h): If the buffer is not empty, removes the
;                   newest character from the buffer and echoes the three-
;                   character sequence 08h 20h 08h (backspace space backspace)
;                   to the terminal to erase the most recently typed character
;                   from the user's screen.  If the buffer is empty when the
;                   backspace character is received, echoes a single 07h (beep)
;                   to the terminal.
;
;                 - Carriage Return (0Dh): Appends zero character to the buffer
;                   to mark the end of the line and returns.
;
;                 - Printable characters (20h through 7Fh, inclusive): If the
;                   buffer is full, echoes a single 07h (beep) to the terminal.
;                   Otherwise appends the character to the buffer.
;
;                 - All other characters: Does nothing.
;
;               As noted above, this function returns when it receives a
;               carriage return.  Nothing specific is returned in registers.
;
;               The function won't guarantee to keep the value of registers
;               (this is the standard behaviour of C routines both in Keil and
;               SDCC), although resources usage will be determined and noted
;               below.
;
;               A test program is written to try the routine, which is not part
;               of the routine.  Test with Hyperterminal or equivalent, 9600
;               baud, 8N1, no handshake, auto terminal mode.  Should beep when
;               10th character is attempted to be entered.  Deleting should
;               "eat" characters until the last one, after it deleting should
;               beep.  After enter, should write number of characters and the
;               string again.
;
; RESOURCE USE:	Uses and destroys ACC and R0
;		A few stack bytes
;		No external memory
;		No interrutps disabled
;
; REVISIONS:	13 May 07 - RAC - Initial specification
;               13 May 07 - JW  - Modified specification and initial
;				   implementation.
; -----------------------------------------------------------------------------

ReceiveString:
	mov	r0,#RxBuffer        	; Initialize receiver pointer

;-----	Loop here until a line of input has been received

LoopReceive:
	call	RxChar			; Go get a character
	jb      acc.7,LoopReceive	; It's unprintable (>= 80h) - ignore it

	cjne    a,#20h,$+3		; Is it a control character (<20h)?
	jc      ControlChars		; Yes - process separately
	cjne    r0,#RxBuffer+RX_BUFFER_SIZE-1,StoreChar    ; No - store if no overflow
EchoBeep:				; Overflow or underflow - beep
	mov     a,#BEEP_CHR
	sjmp    SendChar
StoreChar:
	mov     @r0,a			; Put it into buffer
	inc     r0			; Bump pointer
SendChar:
	call	TxChar			; Go print the character
	sjmp    LoopReceive		; Go get next character

ControlChars:
	cjne    a,#BS_CHR,NotBackSpace
	cjne    r0,#RxBuffer,EatCharacter    ;if BackSpace, eat last character
	sjmp    EchoBeep		; if empty buffer, just beep
EatCharacter:
	mov	a,#BS_CHR		; Do what's needed to eat the character
	call	TxChar			;  on the terminal
	mov	a,#SPACE_CHR
	call	TxChar
	mov	a,#BS_CHR
	call	TxChar

	dec     r0			; Eat the character in the buffer
	sjmp    LoopReceive		; Go get next character

NotBackSpace:
	cjne    a,#CR_CHR,LoopReceive	; If not CR, go get next character
	mov     @r0,#0			; Got CR - terminate the string
	ret				; Done

	END
