include "UART_HOST.inc"
include "memory.inc"
include "m8c.inc"

export _ED_HOST_init
export _ED_HOST_isData
export _ED_HOST_getData
export _ED_HOST_RX_ISR
export _ED_HOST_TX_ISR
export _ED_HOST_putData

ED_HOST_TXBUFFERSIZE: equ 100h ; must be power of 2
ED_HOST_TXBUFFERMASK: equ (ED_HOST_TXBUFFERSIZE-1)

ED_HOST_RXBUFFERSIZE: equ 100h ; must be power of 2
ED_HOST_RXBUFFERMASK: equ (ED_HOST_RXBUFFERSIZE-1)

;-----------------------------------------------
; Variable Allocation
;-----------------------------------------------
AREA InterruptRAM (RAM, REL, CON)

 ED_HOST_RX_outpos:		BLK  1
 ED_HOST_RX_inpos:      BLK  1
 ED_HOST_TX_outpos:     BLK  1
 ED_HOST_TX_inpos:      BLK  1
 ED_HOST_TX_inprogress: BLK  1
 ;ED_HOST_RX_bufavail:   BLK  1
 ED_HOST_TX_bufavail:   BLK  1
 
_ED_HOST_fStatus:
 ED_HOST_fStatus:       BLK  1

AREA UART_HOST_RAM_RX (RAM, REL, CON)
  ED_HOST_RxBuffer:     BLK ED_HOST_RXBUFFERSIZE
AREA UART_HOST_RAM_TX (RAM, REL, CON)
  ED_HOST_TxBuffer:     BLK ED_HOST_TXBUFFERSIZE

;-----------------------------------------------
;  Global Symbols
;-----------------------------------------------

AREA UserModules (ROM, REL, CON)

;-----------------------------------------------
;  init
;-----------------------------------------------

_ED_HOST_init:
	RAM_PROLOGUE RAM_USE_CLASS_3
	RAM_SETPAGE_CUR >ED_HOST_RX_inpos
	mov [ED_HOST_RX_inpos], 0
	mov [ED_HOST_RX_outpos], 0
	mov [ED_HOST_TX_inpos], 0
	mov [ED_HOST_TX_outpos], 0
	mov [ED_HOST_TX_inprogress], 0
	mov [ED_HOST_TX_bufavail], ED_HOST_TXBUFFERSIZE-1
	RAM_EPILOGUE RAM_USE_CLASS_3
	ret

;-----------------------------------------------
;  isData
;-----------------------------------------------

_ED_HOST_isData:
	RAM_PROLOGUE RAM_USE_CLASS_3
	
	RAM_SETPAGE_CUR >ED_HOST_RX_inpos
	
	M8C_DisableGInt
	
	mov A, [ED_HOST_RX_outpos]
	sub A, [ED_HOST_RX_inpos]
	
	M8C_EnableGInt

	RAM_EPILOGUE RAM_USE_CLASS_3
	ret

;-----------------------------------------------
;  getData
;-----------------------------------------------
_ED_HOST_getData:
	RAM_PROLOGUE RAM_USE_CLASS_3
	
	RAM_SETPAGE_CUR >ED_HOST_RX_inpos
    RAM_SETPAGE_IDX >ED_HOST_RxBuffer

	mov  X,[ED_HOST_RX_outpos]                               ; Load X with byte counter
 	mov  A, [X+ED_HOST_RxBuffer]
 	inc  [ED_HOST_RX_outpos]
 	and  [ED_HOST_RX_outpos], ED_HOST_RXBUFFERMASK
 	
	RAM_EPILOGUE RAM_USE_CLASS_3
	ret



;-----------------------------------------------
;  RX ISR
;-----------------------------------------------
_ED_HOST_RX_ISR:
   push A
   push X
   
   IF SYSTEM_LARGE_MEMORY_MODEL
      REG_PRESERVE IDX_PP
    ENDIF
   
   mov  X,[ED_HOST_RX_inpos]                              ; Load X with byte counter
   mov  A,REG[UART_HOST_RX_CONTROL_REG]                    ; Read the control register
   push A                                                  ; Store copy for later test
                                                           ; IF real RX interrupt
   and  A,UART_HOST_RX_REG_FULL                            ; Did really really get an IRQ
   jnz  .UARTRX_ReadRx                                     ; Data ready, go get it
   pop  A                                                  ; Restore stack
   jmp  .RESTORE_IDX_PP

.UARTRX_ReadRx:
   pop  A                                                  ; Restore status flags
                                                           ; IF there is no error, get data
                                                           ; Check for parity or framing error
   and  A,UART_HOST_RX_ERROR
   jz   .UARTRX_NO_ERROR                                   ; If there is not an Error go read data

   or   [ED_HOST_fStatus],A                                ; Set error flags (parity,framing,overrun) bits
   mov  A,REG[UART_HOST_RX_BUFFER_REG ]                    ; Read the data buffer to clear it.
   and  A,UART_HOST_RX_FRAMING_ERROR                       ; Check for framing error special case
   jz   .RESTORE_IDX_PP                                    ; Not framing error, all done

                                                           ; Disable and re-enable RX to reset after
                                                           ; framing error.
   and   REG[UART_HOST_RX_CONTROL_REG], ~UART_HOST_RX_ENABLE   ; Disable RX
   or    REG[UART_HOST_RX_CONTROL_REG],  UART_HOST_RX_ENABLE   ; Enable RX
   jmp  .RESTORE_IDX_PP                                    ; Done with framing error, leave.


.UARTRX_NO_ERROR:
   mov  A,REG[UART_HOST_RX_BUFFER_REG ]                    ; Read the data buffer

; we have a good input character now
   RAM_SETPAGE_IDX >ED_HOST_RxBuffer
   RAM_CHANGE_PAGE_MODE FLAG_PGMODE_10b  
   mov  [X+ED_HOST_RxBuffer],A                            ; store data in array
   RAM_CHANGE_PAGE_MODE FLAG_PGMODE_00b 
   
   inc  [ED_HOST_RX_inpos]                                  ; Inc the pointer
   and  [ED_HOST_RX_inpos], ED_HOST_RXBUFFERMASK
   
   jmp  .RESTORE_IDX_PP

.RESTORE_IDX_PP:
   IF SYSTEM_LARGE_MEMORY_MODEL
      REG_RESTORE IDX_PP
   ENDIF

.END_UARTRX_ISR:
   pop  X
   pop  A


UART_HOST_RX_ISR_END:
   reti


;-----------------------------------------------
;  putData
;-----------------------------------------------
_ED_HOST_putData:
	RAM_PROLOGUE RAM_USE_CLASS_3
	RAM_SETPAGE_CUR >ED_HOST_TX_inprogress
	
	; if we're about to overrun our buffer space, loop!
.waitloop:
	cmp  [ED_HOST_TX_bufavail], 0x02
	jnc   nooverflow
	jmp  .waitloop

nooverflow:
	M8C_DisableGInt 		; avoid race conditions
	; our main concern is us deciding to enqueue the byte since
	; the ISR is inprogress, but the ISR simultaneously exiting.
	; so we protect our decision and enqueuing by disabling 
	; interrupts.

	; if the ISR is still expecting another interrupt, just enqueue this one	
	tst [ED_HOST_TX_inprogress], 0x01
	jnz ED_HOST_enqueue

	; inprogress will only be cleared in the ISR when the buffer is empty
	; and we didn't put anything else in. So, we don't need to test the value
	; of the control register.
	
	; send immediately
	or [ED_HOST_TX_inprogress], 0x1
	
	M8C_EnableGInt

bufwait:
	tst reg[UART_HOST_TX_CONTROL_REG], UART_HOST_TX_BUFFER_EMPTY
	jnz  bufwaitb  ; The buffer isn't empty; enqueue the byte!
	jmp  bufwait
	
bufwaitb:
    mov reg[UART_HOST_TX_BUFFER_REG], A     ; Write data to Tx Port

	RAM_EPILOGUE RAM_USE_CLASS_3
	ret
	
ED_HOST_enqueue:
    RAM_SETPAGE_IDX >ED_HOST_TxBuffer

	mov  X,[ED_HOST_TX_inpos]                               ; Load X with byte counter
	mov  [X+ED_HOST_TxBuffer], A

 	inc  [ED_HOST_TX_inpos]
 	and  [ED_HOST_TX_inpos], ED_HOST_TXBUFFERMASK

	dec  [ED_HOST_TX_bufavail]
	M8C_EnableGInt
 	
	RAM_EPILOGUE RAM_USE_CLASS_3
	ret
	
;-----------------------------------------------
;  TX_IRQ
;-----------------------------------------------
_ED_HOST_TX_ISR:
   push A
   push X
   
   IF SYSTEM_LARGE_MEMORY_MODEL
      REG_PRESERVE IDX_PP
   ENDIF
   
   mov  A, [ED_HOST_TX_outpos]                              ; Load X with byte counter
   cmp  A, [ED_HOST_TX_inpos]	; are we done?
   jz   ED_HOST_TX_ISR_COMPLETE

   ; nope, send another character. A still contains outpos
   RAM_SETPAGE_IDX >ED_HOST_TxBuffer

   mov  X, A
   RAM_CHANGE_PAGE_MODE FLAG_PGMODE_10b 
   mov  A, [X+ED_HOST_TxBuffer]
   RAM_CHANGE_PAGE_MODE FLAG_PGMODE_00b 
   
bufwait2:
   tst reg[UART_HOST_TX_CONTROL_REG], UART_HOST_TX_BUFFER_EMPTY
   jnz  bufwait2b  ; The buffer isn't empty; enqueue the byte!
	jmp bufwait2
	
bufwait2b:
   mov REG[UART_HOST_TX_BUFFER_REG], A    ; Write data to Tx Port

   inc [ED_HOST_TX_outpos]
   and [ED_HOST_TX_outpos], ED_HOST_TXBUFFERMASK

   inc  [ED_HOST_TX_bufavail]

   IF SYSTEM_LARGE_MEMORY_MODEL
      REG_RESTORE IDX_PP
   ENDIF
   
   pop X
   pop A
   reti
   
ED_HOST_TX_ISR_COMPLETE:
   mov [ED_HOST_TX_inprogress], 0

   IF SYSTEM_LARGE_MEMORY_MODEL
      REG_RESTORE IDX_PP
   ENDIF

   pop X
   pop A
   reti
	
;-----------------------------------------------
;  getTxBufData
;-----------------------------------------------
_ED_HOST_getTxBufAvail:
	RAM_PROLOGUE RAM_USE_CLASS_3
	
	RAM_SETPAGE_CUR >ED_HOST_TX_bufavail
 
 	mov  A, [ED_HOST_TX_bufavail]

	RAM_EPILOGUE RAM_USE_CLASS_3
	ret

; end of file UART_HOSTINT.asm

