include "inc/cmdsys.plh"
//
// Bit banged spi over gameport
//
const SPI_SLAVE_READY = '@'
const SPI_SLAVE_ERROR = '!'
const SPI_SLAVE_BUSY  = $FF

word spiReadWriteByte, spiWriteBytes, spiReadBytes

asm spiInc
!SOURCE "vmsrc/plvmzp.inc"
!CPU    65C02
end

asm spiXferByteStd(outbyte)
    PHP             ; DISABLE INTS
    SEI
    STA $C05A       ; ENABLE SLAVE
    LDY #0          ; ASSUME MSB IS ZERO
    LDA ESTKL,X     ; GET ARGUMENT
    BPL +           ; CHECK MSB
    INY             ; IT'S A ONE
+   STA $C058,Y     ; WRITE BIT 7
    STA $C040       ; CLOCK
    LDY #0          ; DOING THIS HERE GIVES TIME FOR OUTPUT TO BECOME STABLE - NOT REALLY NEEDED
    ASL $C061       ; READ BIT 7 INTO CARRY
    ROL             ; ROTATE INTO ACC
    BPL +           ; REPEAT FOR ALL BITS
    INY
+   STA $C058,Y
    STA $C040
    LDY #0
    ASL $C061
    ROL
    BPL +
    INY
+   STA $C058,Y
    STA $C040
    LDY #0
    ASL $C061
    ROL
    BPL +
    INY
+   STA $C058,Y
    STA $C040
    LDY #0
    ASL $C061
    ROL
    BPL +
    INY
+   STA $C058,Y
    STA $C040
    LDY #0
    ASL $C061
    ROL
    BPL +
    INY
+   STA $C058,Y
    STA $C040
    LDY #0
    ASL $C061
    ROL
    BPL +
    INY
+   STA $C058,Y
    STA $C040
    LDY #0
    ASL $C061
    ROL
    BPL +
    INY
+   STA $C058,Y
    STA $C040
    NOP
    ASL $C061
    STA $C05B       ; DISABLE SLAVE
    ROL
    STA ESTKL,X     ; SAVE RETURN PARAMETER
    PLP
    RTS
end

asm spiXferByteGS(outbyte)
    PHP             ; DISABLE INTS
    SEI
    LDA $C036       ; SET 1 MHZ
    PHA
    AND #$7F
    STA $C036
    STA $C05A       ; ENABLE SLAVE
    LDY #0          ; ASSUME MSB IS ZERO
    LDA ESTKL,X     ; GET ARGUMENT
    BPL +           ; CHECK MSB
    INY             ; IT'S A ONE
+   STA $C058,Y     ; WRITE BIT 7
    STA $C05C       ; CLOCK FALLING EDGE
    LDY #0          ; DOING THIS HERE GIVES TIME FOR OUTPUT TO BECOME STABLE - NOT REALLY NEEDED
    STA $C05D       ; CLOCK RISING EDGE
    ASL $C061       ; READ BIT 7 INTO CARRY
    ROL             ; ROTATE INTO ACC
    BPL +           ; REPEAT FOR ALL BITS
    INY
+   STA $C058,Y
    STA $C05C       ; CLOCK FALLING EDGE
    LDY #0
    STA $C05D       ; CLOCK RISING EDGE
    ASL $C061
    ROL
    BPL +
    INY
+   STA $C058,Y
    STA $C05C       ; CLOCK FALLING EDGE
    LDY #0
    STA $C05D       ; CLOCK RISING EDGE
    ASL $C061
    ROL
    BPL +
    INY
+   STA $C058,Y
    STA $C05C       ; CLOCK FALLING EDGE
    LDY #0
    STA $C05D       ; CLOCK RISING EDGE
    ASL $C061
    ROL
    BPL +
    INY
+   STA $C058,Y
    STA $C05C       ; CLOCK FALLING EDGE
    LDY #0
    STA $C05D       ; CLOCK RISING EDGE
    ASL $C061
    ROL
    BPL +
    INY
+   STA $C058,Y
    STA $C05C       ; CLOCK FALLING EDGE
    LDY #0
    STA $C05D       ; CLOCK RISING EDGE
    ASL $C061
    ROL
    BPL +
    INY
+   STA $C058,Y
    STA $C05C       ; CLOCK FALLING EDGE
    LDY #0
    STA $C05D       ; CLOCK RISING EDGE
    ASL $C061
    ROL
    BPL +
    INY
+   STA $C058,Y
    STA $C05C       ; CLOCK FALLING EDGE
    STA $C05D       ; CLOCK RISING EDGE
    ASL $C061
    STA $C05B       ; DISABLE SLAVE
    ROL
    STA ESTKL,X     ; SAVE RETURN PARAMETER
    PLA
    STA $C036
    PLP
    RTS
end
asm spiReadBytesStd(buf, len)
    PHP             ; DISABLE INTS
    SEI
    LDA ESTKL+1,X
    STA DSTL
    LDA ESTKH+1,X
    STA DSTH
    LDA ESTKL,X
    BEQ +
    INC ESTKH,X
+   LDY #$00
-   STA $C05A       ; ENABLE SLAVE
    STA $C040       ; CLOCK
    NOP
    ASL $C061       ; SHIFT IN ALL BITS STARTING WITH MSB
    STA $C040
    ROL
    ASL $C061
    STA $C040
    ROL
    ASL $C061
    STA $C040
    ROL
    ASL $C061
    STA $C040
    ROL
    ASL $C061
    STA $C040
    ROL
    ASL $C061
    STA $C040
    ROL
    ASL $C061
    STA $C040
    ROL
    ASL $C061
    STA $C05B       ; DISABLE SLAVE
    ROL
    STA (DST),Y     ; SAVE TO BUFFER
    INY
    BNE +
    INC DSTH
+   DEC ESTKL,X
    BNE -
    DEC ESTKH,X
    BNE -
    INX             ; REMOVE AN ARGUMENT
    PLP
    RTS
end

asm spiReadBytesGS(buf, len)
    PHP             ; DISABLE INTS
    SEI
    LDA $C036       ; SET 1 MHZ
    PHA
    AND #$7F
    STA $C036
    LDA ESTKL+1,X
    STA DSTL
    LDA ESTKH+1,X
    STA DSTH
    LDA ESTKL,X
    BEQ +
    INC ESTKH,X
+   LDY #$00
-   STA $C05A       ; ENABLE SLAVE
    STA $C05C       ; CLOCK FALLING EDGE
    STA $C05D       ; CLOCK RISING EDGE
    ASL $C061       ; SHIFT IN ALL BITS STARTING WITH MSB
    ROL
    STA $C05C       ; CLOCK FALLING EDGE
    STA $C05D       ; CLOCK RISING EDGE
    ASL $C061
    ROL
    STA $C05C       ; CLOCK FALLING EDGE
    STA $C05D       ; CLOCK RISING EDGE
    ASL $C061
    ROL
    STA $C05C       ; CLOCK FALLING EDGE
    STA $C05D       ; CLOCK RISING EDGE
    ASL $C061
    ROL
    STA $C05C       ; CLOCK FALLING EDGE
    STA $C05D       ; CLOCK RISING EDGE
    ASL $C061
    ROL
    STA $C05C       ; CLOCK FALLING EDGE
    STA $C05D       ; CLOCK RISING EDGE
    ASL $C061
    ROL
    STA $C05C       ; CLOCK FALLING EDGE
    STA $C05D       ; CLOCK RISING EDGE
    ASL $C061
    ROL
    STA $C05C       ; CLOCK FALLING EDGE
    STA $C05D       ; CLOCK RISING EDGE
    ASL $C061
    STA $C05B       ; DISABLE SLAVE
    ROL
    STA (DST),Y     ; SAVE TO BUFFER
    INY
    BNE +
    INC DSTH
+   DEC ESTKL,X
    BNE -
    DEC ESTKH,X
    BNE -
    INX             ; REMOVE AN ARGUMENT
    PLA
    STA $C036
    PLP
    RTS
end

asm spiWriteBytesStd(buf, len)
    PHP             ; DISABLE INTS
    SEI
    LDA ESTKL+1,X
    STA SRCL
    LDA ESTKH+1,X
    STA SRCH
    LDA ESTKL,X
    BEQ +
    INC ESTKH,X
+
-   STA $C05A       ; ENABLE SLAVE
    LDY #0          ; ASSUME MSB IS ZERO
    LDA (SRC),Y     ; GET BYTE
    BPL +           ; CHECK MSB
    INY             ; IT'S A ONE
+   STA $C058,Y     ; WRITE BIT 7
    STA $C040       ; CLOCK
    LDY #0          ; DOING THIS HERE GIVES TIME FOR OUTPUT TO BECOME STABLE
    ASL             ; ROTATE NEXT BIT TO SEND
    BPL +           ; REPEAT FOR ALL BITS
    INY
+   STA $C058,Y
    STA $C040
    LDY #0
    ASL
    BPL +
    INY
+   STA $C058,Y
    STA $C040
    LDY #0
    ASL
    BPL +
    INY
+   STA $C058,Y
    STA $C040
    LDY #0
    ASL
    BPL +
    INY
+   STA $C058,Y
    STA $C040
    LDY #0
    ASL
    BPL +
    INY
+   STA $C058,Y
    STA $C040
    LDY #0
    ASL
    BPL +
    INY
+   STA $C058,Y
    STA $C040
    LDY #0
    ASL
    BPL +
    INY
+   STA $C058,Y
    STA $C040
    STA $C05B       ; DISABLE SLAVE
    INC SRCL
    BNE +
    INC SRCH
+   DEC ESTKL,X
    BNE -
    DEC ESTKH,X
    BNE -
    INX             ; REMOVE AN ARGUMENT
    PLP
    RTS
end

asm spiWriteBytesGS(buf, len)
    PHP             ; DISABLE INTS
    SEI
    LDA $C036       ; SET 1 MHZ
    PHA
    AND #$7F
    STA $C036
    LDA ESTKL+1,X
    STA SRCL
    LDA ESTKH+1,X
    STA SRCH
    LDA ESTKL,X
    BEQ +
    INC ESTKH,X
+
--  STA $C05A       ; ENABLE SLAVE
    PHX
    LDX #8
    LDY #0          ; ASSUME MSB IS ZERO
    LDA (SRC),Y     ; GET BYTE
-   ASL
    BCC +           ; CHECK MSB
    INY             ; IT'S A ONE
+   STA $C058,Y     ; WRITE BIT 7
    STA $C05C       ; CLOCK FALLING EDGE
    STA $C05D       ; CLOCK RISING EDGE
    LDY #0
    DEX
    BNE -
    STA $C05B       ; DISABLE SLAVE
    INC SRCL
    BNE +
    INC SRCH
+   PLX
    DEC ESTKL,X
    BNE --
    DEC ESTKH,X
    BNE --
    INX             ; REMOVE AN ARGUMENT
    PLA
    STA $C036
    PLP
    RTS
end

export def spiXferByte(outbyte)
    return (spiReadWriteByte)(outbyte)
end

export def spiDelay(time)
    return call($FCA8, time, 0, 0, 0) // DELAY
end

export def spiSend(data)
    byte timeout, status

    for timeout = 1 to 100 step 10
        status = (spiReadWriteByte)(data)
        if status <> SPI_SLAVE_BUSY
            return status
        fin
        spiDelay(timeout)
    next
    putc(status);putc('0'+data/10);putc('0'+data%10)
    return status
end

export def spiRecv
    return spiSend(0)
end

export def spiWriteBuf(buf, len)
    spiSend(13)        // CMD_BUF_WRITE
    spiSend(len >> 8); spiSend(len)
   return (spiWriteBytes)(buf, len)
end

export def spiReadBuf(buf, len)
    spiSend(12)        // CMD_BUF_READ
    spiSend(len >> 8); spiSend(len)
    return (spiReadBytes)(buf, len)
end

export def spiReady
    byte timeout

    timeout = 0xFF
    while (spiReadWriteByte)(0) <> SPI_SLAVE_READY and timeout // WAIT FOR READY
        timeout--
        spiDelay(10)
    loop
    return timeout
end

export def spiReset
    ^$C05B // DISABLE SLAVE SELECT
    ^$C05D // CLOCK RAISE (GS ONLY)
    return spiReady
end

if call($FE1F, 0, 0, 0, 1).3 & 1 // GS ID ROUTINE
    spiReadWriteByte = @spiXferByteStd
    spiReadBytes     = @spiReadBytesStd
    spiWriteBytes    = @spiWriteBytesStd
else
    spiReadWriteByte = @spiXferByteGS
    spiReadBytes     = @spiReadBytesGS
    spiWriteBytes    = @spiWriteBytesGS
fin
return spiReset <> 0
done
