include "UART_SLAVE.inc"
include "memory.inc"
include "m8c.inc"

export _ED_SLAVE_init
export _ED_SLAVE_isData
export _ED_SLAVE_getData
export _ED_SLAVE_RX_ISR
export _ED_SLAVE_TX_ISR
export _ED_SLAVE_putData

ED_SLAVE_TXBUFFERSIZE: equ 80h ; must be power of 2
ED_SLAVE_TXBUFFERMASK: equ (ED_SLAVE_TXBUFFERSIZE-1)

ED_SLAVE_RXBUFFERSIZE: equ 80h ; must be power of 2
ED_SLAVE_RXBUFFERMASK: equ (ED_SLAVE_RXBUFFERSIZE-1)

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

 ED_SLAVE_RX_outpos:		BLK  1
 ED_SLAVE_RX_inpos:      BLK  1
 ED_SLAVE_TX_outpos:     BLK  1
 ED_SLAVE_TX_inpos:      BLK  1
 ED_SLAVE_TX_inprogress: BLK  1
 ;ED_SLAVE_RX_bufavail:   BLK  1
 ED_SLAVE_TX_bufavail:   BLK  1
 
_ED_SLAVE_fStatus:
 ED_SLAVE_fStatus:       BLK  1

AREA UART_SLAVE_RAM (RAM, REL, CON)
  ED_SLAVE_RxBuffer:     BLK ED_SLAVE_RXBUFFERSIZE
  ED_SLAVE_TxBuffer:     BLK ED_SLAVE_TXBUFFERSIZE

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

AREA UserModules (ROM, REL, CON)

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

_ED_SLAVE_init:
	RAM_PROLOGUE RAM_USE_CLASS_3
	RAM_SETPAGE_CUR >ED_SLAVE_RX_inpos
	mov [ED_SLAVE_RX_inpos], 0
	mov [ED_SLAVE_RX_outpos], 0
	mov [ED_SLAVE_TX_inpos], 0
	mov [ED_SLAVE_TX_outpos], 0
	mov [ED_SLAVE_TX_inprogress], 0
	mov [ED_SLAVE_TX_bufavail], ED_SLAVE_TXBUFFERSIZE-1
	RAM_EPILOGUE RAM_USE_CLASS_3
	ret

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

_ED_SLAVE_isData:
	RAM_PROLOGUE RAM_USE_CLASS_3
	
	RAM_SETPAGE_CUR >ED_SLAVE_RX_inpos
	mov A, [ED_SLAVE_RX_outpos]
	sub A, [ED_SLAVE_RX_inpos]
	
	RAM_EPILOGUE RAM_USE_CLASS_3
	ret

;-----------------------------------------------
;  getData
;-----------------------------------------------
_ED_SLAVE_getData:
	RAM_PROLOGUE RAM_USE_CLASS_3
	
	RAM_SETPAGE_CUR >ED_SLAVE_RX_inpos
    RAM_SETPAGE_IDX >ED_SLAVE_RxBuffer

	mov  X,[ED_SLAVE_RX_outpos]                               ; Load X with byte counter
 	mov  A, [X+ED_SLAVE_RxBuffer]
 	inc  [ED_SLAVE_RX_outpos]
 	and  [ED_SLAVE_RX_outpos], ED_SLAVE_RXBUFFERMASK
 	
	RAM_EPILOGUE RAM_USE_CLASS_3
	ret



;-----------------------------------------------
;  RX ISR
;-----------------------------------------------
_ED_SLAVE_RX_ISR:
   push A
   push X
   
   IF SYSTEM_LARGE_MEMORY_MODEL
      REG_PRESERVE IDX_PP
    ENDIF
   
   mov  X,[ED_SLAVE_RX_inpos]                              ; Load X with byte counter
   mov  A,REG[UART_SLAVE_RX_CONTROL_REG]                    ; Read the control register
   push A                                                  ; Store copy for later test
                                                           ; IF real RX interrupt
   and  A,UART_SLAVE_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_SLAVE_RX_ERROR
   jz   .UARTRX_NO_ERROR                                   ; If there is not an Error go read data

   or   [ED_SLAVE_fStatus],A                                ; Set error flags (parity,framing,overrun) bits
   mov  A,REG[UART_SLAVE_RX_BUFFER_REG ]                    ; Read the data buffer to clear it.
   and  A,UART_SLAVE_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_SLAVE_RX_CONTROL_REG], ~UART_SLAVE_RX_ENABLE   ; Disable RX
   or    REG[UART_SLAVE_RX_CONTROL_REG],  UART_SLAVE_RX_ENABLE   ; Enable RX
   jmp  .RESTORE_IDX_PP                                    ; Done with framing error, leave.


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

                                                           ; IF buffer not full
   tst  [ED_SLAVE_fStatus],UART_SLAVE_RX_BUF_CMDTERM         ; Check for buffer full
   jnz  .RESTORE_IDX_PP                                    ; All done

; we have a good input character now
   RAM_SETPAGE_IDX >ED_SLAVE_RxBuffer
   RAM_CHANGE_PAGE_MODE FLAG_PGMODE_10b  
   mov  [X+ED_SLAVE_RxBuffer],A                            ; store data in array
   RAM_CHANGE_PAGE_MODE FLAG_PGMODE_00b 
   
   inc  [ED_SLAVE_RX_inpos]                                  ; Inc the pointer
   and  [ED_SLAVE_RX_inpos], ED_SLAVE_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_SLAVE_RX_ISR_END:
   reti


;-----------------------------------------------
;  putData
;-----------------------------------------------
_ED_SLAVE_putData:
	RAM_PROLOGUE RAM_USE_CLASS_3
	RAM_SETPAGE_CUR >ED_SLAVE_TX_inprogress
	
	; if we're about to overrun our buffer space, loop!
.waitloop:
	cmp  [ED_SLAVE_TX_bufavail], 0x01
	jc   .waitloop
	
	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_SLAVE_TX_inprogress], 0x01
	jnz ED_SLAVE_enqueue

	; now it's possible that the ISR thinks it's finished (it just
	; wrote the last byte), but the last byte is still transmitting.
	; We don't want to block in this case, but rather put the byte
	; into the queue (since another interrupt is still pending.)
	tst reg[UART_SLAVE_TX_CONTROL_REG], UART_SLAVE_TX_BUFFER_EMPTY
	jz  ED_SLAVE_enqueue  ; The buffer isn't empty; enqueue the byte!
	
; send immediately
	or [ED_SLAVE_TX_inprogress], 0x1
	
	M8C_EnableGInt

    mov REG[UART_SLAVE_TX_BUFFER_REG], A     ; Write data to Tx Port

	RAM_EPILOGUE RAM_USE_CLASS_3
	ret
	
ED_SLAVE_enqueue:
    RAM_SETPAGE_IDX >ED_SLAVE_TxBuffer

	mov  X,[ED_SLAVE_TX_inpos]                               ; Load X with byte counter
	mov  [X+ED_SLAVE_TxBuffer], A

 	inc  [ED_SLAVE_TX_inpos]
 	and  [ED_SLAVE_TX_inpos], ED_SLAVE_TXBUFFERMASK

	dec  [ED_SLAVE_TX_bufavail]
	M8C_EnableGInt
 	
	RAM_EPILOGUE RAM_USE_CLASS_3
	ret
	
;-----------------------------------------------
;  TX_IRQ
;-----------------------------------------------
_ED_SLAVE_TX_ISR:
   push A
   push X
   
   IF SYSTEM_LARGE_MEMORY_MODEL
      REG_PRESERVE IDX_PP
   ENDIF
   
   mov  A, [ED_SLAVE_TX_outpos]                              ; Load X with byte counter
   cmp  A, [ED_SLAVE_TX_inpos]	; are we done?
   jz   ED_SLAVE_TX_ISR_COMPLETE

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

   mov  X, A
   RAM_CHANGE_PAGE_MODE FLAG_PGMODE_10b 
   mov  A, [X+ED_SLAVE_TxBuffer]
   RAM_CHANGE_PAGE_MODE FLAG_PGMODE_00b 
   mov REG[UART_SLAVE_TX_BUFFER_REG], A    ; Write data to Tx Port

   inc [ED_SLAVE_TX_outpos]
   and [ED_SLAVE_TX_outpos], ED_SLAVE_TXBUFFERMASK

   inc  [ED_SLAVE_TX_bufavail]

   IF SYSTEM_LARGE_MEMORY_MODEL
      REG_RESTORE IDX_PP
   ENDIF
   
   pop X
   pop A
   reti
   
ED_SLAVE_TX_ISR_COMPLETE:
   mov [ED_SLAVE_TX_inprogress], 0

   IF SYSTEM_LARGE_MEMORY_MODEL
      REG_RESTORE IDX_PP
   ENDIF

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

	RAM_EPILOGUE RAM_USE_CLASS_3
	ret

; end of file UART_SLAVEINT.asm

