6502-OS/system/spi.s65

257 lines
6.7 KiB
Plaintext

.include "spi.h65"
.include "system.h65"
Export spi_p, init, irq_handler, status, buffer_size, recv_size
ExportZp spi_p, buffer_ptr
Export spi_p, begin_read, end_read, irq_read_byte, recv_bytes
Export spi_p, begin_write, end_write, irq_write_byte, sent_bytes
.zeropage
buffer_ptr: .res 2
.bss
recv_bytes:
sent_bytes: .res 2
status: .res 1
buffer_size: .res 2
recv_size:
send_size: .res 2
irq_handler: .res 2
SPI_IO := spi_p::SPI_IO
; spi-transferred code will be placed here
; SPI_P = as peripheral
.code
;;********************************************************************************
;; @function Begin listening for SPI transfers
;;********************************************************************************
.proc init
lda #spi_p::STATUS::DONE
sta status
stz recv_bytes
stz recv_bytes+1
stz buffer_size
stz buffer_size+1
stz recv_size
stz recv_size+1
rts
.endproc
;;********************************************************************************
;; @function Begin listening for SPI transfers
;; @details
;; The transfer must be: <SIZE, >SIZE, data (SIZE bytes)
;;
;; - initialize variables
;; - configure shift register to shift in under external clock
;; - enable shift register interrupts
;; @param ARG0-1: Start address of the buffer
;; @param ARG2-3: Size of the buffer
;; @todo: Use irq register handler
;; @note: The buffer must be large enough for the received data.
;;********************************************************************************
.proc begin_read
stz recv_bytes
stz recv_bytes+1
stz recv_size
stz recv_size+1
lda #spi_p::STATUS::XFER_SIZEL
sta status
; store address in zp
lda ARG0
sta buffer_ptr
lda ARG1
sta buffer_ptr+1
; load irq handler
lda #<irq_read_byte
sta irq_handler
lda #>irq_read_byte
sta irq_handler+1
; temporarily store the low byte in buffer, will be checked later
lda ARG2
sta buffer_size
lda ARG3
sta buffer_size+1
; set Shift register to shift in under external clock on CB1
MaskedWrite SPI_IO+IO::ACR, #IO::ACR::SR_SIN_PHIE, #IO::ACR_MASK::SR
; enable SR interrupts
lda #(IO::IRQ::IRQ | IO::IRQ::SR)
sta SPI_IO + IO::IER
; load SR to reset
lda SPI_IO + IO::SR
rts
.endproc
;;********************************************************************************
;; @function Stop reading/writing from/to SPI
;; @details
;; Disables shift register interrupts
;; @modifies A
;;********************************************************************************
end_write:
.proc end_read
; disable SR interrupts
lda #IO::IRQ::SR
sta SPI_IO + IO::IER
lda #spi_p::STATUS::DONE
sta status
rts
.endproc
;;********************************************************************************
;; @function Read a byte from SPI
;; @details
;; Reads a byte from the shift register and stores it in the SPI code buffer
;; The first two bytes must be the size of the incumong data (LE).
;; The spi_p:status is updated according to the current stage of the transfer.
;; If the buffer size given in begin_read is too small too fit the incoming data,
;; the status is set to ERROR and spi (shift register) interrupts are disabled before
;; the first real byte is read.
;; @modifies A,Y
;;********************************************************************************
.proc irq_read_byte
lda SPI_IO + IO::SR
; Printf "%x",
bit status
bmi @read_size1 ; bit7 set => XFER_SIZEL
bvs @read_size2 ; bit6 set => XFER_SIZEH
ldy recv_bytes
sta (buffer_ptr),y
inc recv_bytes
beq @new_page
rts
@new_page: ; increment high bytes
inc recv_bytes+1
inc buffer_ptr+1
rts
@read_size1:
sta recv_size
lda #spi_p::STATUS::XFER_SIZEH
sta status
rts
@read_size2:
sta recv_size+1
; check if the buffer is large enough
cmp buffer_size+1
beq @hieq
bcc @enough ; recv_size+1 < buffer_size+1
bra @not_enough
@hieq: ; high bytes are equal, check lo
lda buffer_size
cmp recv_size
bcs @enough
@not_enough:
lda #spi_p::STATUS::ERROR
sta status
; disable SR interrupts
lda #IO::IRQ::SR
sta SPI_IO + IO::IER
rts
@enough:
lda #spi_p::STATUS::XFER
sta status
rts
.endproc
;;********************************************************************************
;; @function Begin writing to spi
;; @details
;; - initialize variables
;; - configure shift register to shift in under external clock
;; - enable shift register interrupts
;; @param ARG0-1: Start address of the buffer
;; @param ARG2-3: Size of the buffer
;; @todo: Use irq register handler
;;********************************************************************************
.proc begin_write
stz recv_bytes
stz recv_bytes+1
; store address in zp
lda #<ARG0
sta buffer_ptr
lda #>ARG1
sta buffer_ptr+1
; store size
lda #<ARG2
sta buffer_size
lda #>ARG3
sta buffer_size+1
; load irq handler
lda #<irq_read_byte
sta irq_handler
lda #>irq_read_byte
sta irq_handler+1
; set Shift register to shift out under external clock on CB1
MaskedWrite SPI_IO+IO::ACR, #IO::ACR::SR_SOUT_PHIE, #IO::ACR_MASK::SR
; enable SR interrupts
lda #(IO::IRQ::IRQ | IO::IRQ::SR)
sta SPI_IO + IO::IER
; write the first byte
@write_size1:
lda buffer_size
sta SPI_IO+IO::SR
lda #spi_p::STATUS::XFER_SIZEH
sta status
rts
.endproc
;********************************************************************************
; @function Write a byte
; @details
; Write a byte to the spi shift reister
; @modifies A,Y
;********************************************************************************
;.proc irq_write_byte
; ldy sent_bytes
; lda (buffer_ptr),y
; sta SPI_IO + IO::SR
; inc sent_bytes
; beq @new_page
; rts
;@new_page:
; inc sent_bytes+1
; inc buffer_ptr+1
; rts
;.endproc
;;********************************************************************************
;; @function Write a byte to SPI
;; @details
;; Write a byte from the buffer to the (spi) shift register.
;; The first two bytes are the size of the data (LE).
;; The spi_p:status is updated according to the current stage of the transfer.
;; @modifies A,Y
;;********************************************************************************
.proc irq_write_byte
bit status
bvs @write_size2 ; bit 6 set -> XFER_SIZEH
ldy sent_bytes
lda (buffer_ptr),y
sta SPI_IO + IO::SR
inc sent_bytes
beq @new_page
rts
@new_page:
inc sent_bytes+1
inc buffer_ptr+1
rts
@write_size2:
lda buffer_size+1
sta SPI_IO+IO::SR
lda #spi_p::STATUS::XFER
sta status
rts
.endproc