.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, 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+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 #ARG1 sta buffer_ptr+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 .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