.include "spi.h65" Export spi_p, begin_read, irq_read_byte, begin_write, irq_write_byte, end, recv_bytes, sent_bytes, status, spi_irq .zeropage buffer_start: .res 2 .bss recv_bytes: sent_bytes: .res 2 status: .res 1 buffer_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 ; .struct SPI_P_Pins ; ; VIA addresses ; DDR_a .word ; address of the data direction register ; R_a .word ; address of the register ; ; pin mask ; SCLK_p .byte ; Serial Clock ; POCI_p .byte ; Peripheral Out / Controller In ; PICO_p .byte ; Peripheral In / Controller Out ; CSB_p .byte ; Chip Select ; ; settings ; CPOL .byte ; Clock Polarity ; CPHA .byte ; Clock Phase ; .endstruct ;;******************************************************************************** ;; @function Begin listening for SPI transfers ;; @details ;; The transfer must be: 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 lda #%spi_p::STATUS::XFER_SIZEL sta status ; store address in zp lda #ARG1 sta buffer_start+1 ; load irq handler lda #irq_read_byte sta irq_handler+1 ; temporarily store the low byte in buffer, will be checked later 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 ;;******************************************************************************** .proc end ; disable SR interrupts lda #IO::IRQ::SR sta SPI_IO + IO::IER 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 bit status bmi @read_size1 bvs @read_size2 ldy recv_bytes sta (buffer_start),y inc recv_bytes beq @new_page rts @new_page: inc recv_bytes+1 inc buffer_start+1 rts @read_size1: sta buffer_size lda #spi_p::STATUS::XFER_SIZEH sta status rts @read_size2: ; todo: check if the buffer is large enough: ; low byte of buffer size in (buffer_start), high byte in buffer_size+1 ; low byte of recv buffer size in buffer_size, high byte in A cmp buffer_size+1 beq @hieq sta buffer_size+1 bcs @enough ; buffer larger bra @not_enough @hieq: ; high bytes are equal, check lo lda (buffer_start) cmp buffer_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 #ARG1 sta buffer_start+1 ; store size lda #ARG3 sta buffer_size+1 ; load 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 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_start),y ; sta SPI_IO + IO::SR ; inc sent_bytes ; beq @new_page ; rts ;@new_page: ; inc sent_bytes+1 ; inc buffer_start+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_start),y sta SPI_IO + IO::SR inc sent_bytes beq @new_page rts @new_page: inc sent_bytes+1 inc buffer_start+1 rts @write_size2: lda buffer_size+1 sta SPI_IO+IO_SR lda #spi_p::STATUS::XFER sta status rts .endproc .endproc