* BUFFER.S
* Buffer handling code


* Code to call buffer handlers
* ============================

* Flush buffer - to do
* ====================
BUFFLUSHALL  RTS
BUFFLUSH     RTS

* Attempt to insert byte in buffer, waiting until done or aborted
* ===============================================================
* On entry, X=buffer number
*           A=byte to insert
* On exit,  CC=Successful
*           CS=Aborted with Escape
*
BUFINSERT    PHP
BUFINSERTLP  SEI                  ; Disable interrupts
             JSR   JMPINSV        ; Attempt to insert byte
             BCC   BUFINSERTOK    ; If successful exit
             JSR   ESCPOLL        ; We have a foreground keyboard
                                  ; so we have to poll the hardware
             BMI   BUFINSERTBAD   ; Escape pending, so abort with CS
             CLI                  ; Allow interrupts for a few cycles
             BPL   BUFINSERTLP    ; And loop around and try again
BUFINSERTOK  PLP                  ; Restore IRQs
             CLC                  ; CC=Ok
             RTS
BUFINSERTBAD PLP                  ; Restore IRQs
             SEC                  ; CS=Aborted
             RTS

* OSBYTE &99 - Put byte in buffer checking for Escape
* ===================================================
* On entry, X =buffer number
*           Y =character to be written
* On exit,  If not Escape character
*            X =preserved
*            Y =corrupted
*            CS=buffer full, couldn't insert
*            CC=buffer wasn't full, insertion successful
*           If Escape character
*            X =corrupted
*            Y =corrupted
*
BYTE99       TYA             ; A character to write
             EOR   FXESCCHAR ; Is it current Escape character
             ORA   FXESCON   ; And is it ASCII or Escape
             BNE   BYTE8A    ; If Escape is ASCII or not Escape, insert it
             LDA   FX200VAR  ; Is Escape ignored?
             ROR   A         ; Move bit 0 into Carry to test it
             BCS   BYTE99A   ; Escape completely ignored, exit
             JSR   BYTE7D    ; Set pending Escape
BYTE99A      CLC             ; Exit with CLC
             RTS

* OSBYTE &8E - Insert byte in buffer
* ==================================
* On entry, X =buffer number
*           Y =byte to be written
* On exit,  X =preserved
*           A =preserved
*           Y =corrupted
*           CS=buffer full, couldn't insert
*           CC=buffer wasn't full, insertion successful
*
BYTE8A       TYA             ; A=character to enter
JMPINSV      JMP   (INSV)    ; Pass on to buffer handler

* OSBYTE &91 - Get byte from Buffer
* =================================
* On entry, X =buffer number
* On exit,  X =preserved
*           CS=buffer empty
*              Y corrupted
*           CC=buffer not empty
*              Y=byte from buffer
*
BYTE91       CLV             ; Clear V
             BVC   JMPREMV   ; Pass on to buffer handler

* OSBYTE &9A - Examine Buffer
* ===========================
* On entry, X =buffer number
* On exit,  X =preserved
*           CS=buffer empty
*              Y corrupted
*           CC=buffer not empty
*              Y=byte at top of buffer
*
BYTE9A       BIT   SETV      ; Set V
JMPREMV      JMP   (REMV)    ; Pass on to buffer handler


* Buffer handling code, called by INSV, REMV, CNPV
* ================================================

* Location and sizes of buffers
* -----------------------------
BUF4         EQU   $0340
BUF4SZ       EQU   16
BUF5         EQU   $0350
BUF5SZ       EQU   16
BUF6         EQU   $0360
BUF6SZ       EQU   16
BUF7         EQU   $0370
BUF7SZ       EQU   16
BUF3         EQU   $0380
BUF3SZ       EQU   64
BUFBASE      EQU   $03C0     ; Base of buffer workspace
BUF0         EQU   $03E0
BUF0SZ       EQU   32
BUF2         EQU   $0C00     ; Also transient program area
BUF2SZ       EQU   192
BUF8         EQU   $0CC0     ; Also transient program area
BUF8SZ       EQU   64
BUF1         EQU   $0D00     ; Also transient program area
BUF1SZ       EQU   256


* The buffers are arranged so that an offset of &FF is the last entry, and
* incrementing to &00 passes the end of the buffer. A buffer is empty when
* BUFINP=BUFOUT, and is full when BUFINP+1=BUFOUT.

* BUFFER ADDRESS LOW BYTE LOOKUP TABLE
* ------------------------------------
*                  start+len
BUFLO        DB   (BUF0 + BUF0SZ - 256) AND 255  ; keyboard
             DB   (BUF1 + BUF1SZ - 256) AND 255  ; serial input
             DB   (BUF2 + BUF2SZ - 256) AND 255  ; serial output
             DB   (BUF3 + BUF3SZ - 256) AND 255  ; printer
             DB   (BUF4 + BUF4SZ - 256) AND 255  ; sound 0
             DB   (BUF5 + BUF5SZ - 256) AND 255  ; sound 1
             DB   (BUF6 + BUF6SZ - 256) AND 255  ; sound 2
             DB   (BUF7 + BUF7SZ - 256) AND 255  ; sound 3
             DB   (BUF8 + BUF8SZ - 256) AND 255  ; speech
 
* BUFFER ADDRESS HIGH BYTE LOOKUP TABLE
* -------------------------------------
*                  start+len
BUFHI        DB   (BUF0 + BUF0SZ - 256) DIV 256  ; keyboard
             DB   (BUF1 + BUF1SZ - 256) DIV 256  ; serial input
             DB   (BUF2 + BUF2SZ - 256) DIV 256  ; serial output
             DB   (BUF3 + BUF3SZ - 256) DIV 256  ; printer
             DB   (BUF4 + BUF4SZ - 256) DIV 256  ; sound 0
             DB   (BUF5 + BUF5SZ - 256) DIV 256  ; sound 1
             DB   (BUF6 + BUF6SZ - 256) DIV 256  ; sound 2
             DB   (BUF7 + BUF7SZ - 256) DIV 256  ; sound 3
             DB   (BUF8 + BUF8SZ - 256) DIV 256  ; speech

* BUFFER START ADDRESS OFFSET
* ---------------------------
*                      len
BUFOFF       DB   256-BUF0SZ
             DB   256-BUF1SZ
             DB   256-BUF2SZ
             DB   256-BUF3SZ
             DB   256-BUF4SZ
             DB   256-BUF5SZ
             DB   256-BUF6SZ
             DB   256-BUF7SZ
             DB   256-BUF8SZ

BUFNUM       EQU   BUFHI-BUFLO         ; Number of buffers
BUFFLG       EQU   BUFBASE+0*BUFNUM    ; Buffer flags
BUFINP       EQU   BUFBASE+1*BUFNUM    ; Input pointers
BUFOUT       EQU   BUFBASE+2*BUFNUM    ; Output pointers


* Get buffer base address
* -----------------------
* On entry, X=buffer number (not checked)
* On exit,  (OSINTWS)=>buffer base
BUFADDR      LDA   BUFLO,X
             STA   OSINTWS+0 ; Get buffer base address low
             LDA   BUFHI,X
             STA   OSINTWS+1 ; Get buffer base address high
             RTS


* REMV buffer remove
* ==================
* On entry, X =buffer number
*           VS=examine buffer
*           VC=remove from buffer
* On exit,  X =preserved
*           CS=buffer empty
*              A,Y corrupted
*           CC=buffer not empty
*              A=Y=byte from buffer
*           If called to remove from buffer, pointers updated
*
BUFREM          PHP               ; Save flags
                SEI               ; Disable IRQs
                CPX   #BUFNUM     ; Valid buffer number?
                BCS   BUFFAIL     ; No, return 'empty'
                LDA   BUFOUT,X    ; Get output pointer for buffer X
                CMP   BUFINP,X    ; Compare it to input pointer
                BEQ   BUFFAIL     ; Equal, so buffer is empty
                TAY               ; Y=output pointer
                JSR   BUFADDR     ; Get buffer base address
                LDA   (OSINTWS),Y ; Get byte from buffer
                BVS   BUFREM2     ; If VS, just examine buffer, return
                INY               ; Otherwise, update buffer pointer
                BNE   BUFREM1     ; Not zero, not reached end of buffer
                LDY   BUFOFF,X    ; Get offset to start of buffer
BUFREM1         STY   BUFOUT,X    ; Update the buffer output pointer
BUFREM2         TAY               ; Return A=Y=byte from buffer
BUFREMOK        PLP               ; Restore IRQs
                CLC               ; CLC=success
                RTS


* INSV buffer insert
* ==================
* On entry, X =buffer number
*           A =byte to be inserted
* On exit,  X =preserved
*           A =preserved
*           Y =corrupted
*           CS=buffer full, couldn't insert
*           CC=buffer wasn't full, insertion successful
*
BUFINS       PHP                  ; Save flags
             SEI                  ; Disable IRQs
             CPX   #BUFNUM        ; Valid buffer number?
             BCS   BUFINS2        ; No, sink it and return 'ok'
             PHA                  ; Save A
             LDY   BUFINP,X       ; Get buffer input pointer
             INY                  ; Otherwise, update buffer pointer
             BNE   BUFINS1        ; Not zero, not reached end of buffer
             LDY   BUFOFF,X       ; Get offset to start of buffer
BUFINS1      TYA                  ; A=updated input pointer
             CMP   BUFOUT,X       ; Compare with output pointer
             BEQ   BUFINS4        ; Same, buffer is full, exit with 'failed'
             LDY   BUFINP,X       ; Get unupdated input pointer back
             STA   BUFINP,X       ; Store updated input pointer
             JSR   BUFADDR        ; Get buffer base address
             PLA                  ; Get the byte back
             STA   (OSINTWS),Y    ; And store it in buffer
BUFINS2      PLP                  ; Restore IRQs
             CLC                  ; CLC=success
             RTS
BUFINS4      PLA                  ; Restore A
BUFFAIL      PLP                  ; Restore IRQs
             SEC                  ; SEC=failed
             RTS


* CNPV count/purge buffer
* =======================
* On entry, X =buffer number
*           VC=purge (clear) buffer
*           VS=count buffer
*              CC=count used space
*              CS=count free space
* On exit,  XY=size counted
*           A =corrupted
*
BUFCNP       CPX   #BUFNUM        ; Valid buffer number?
             BCS   BUFCNP1        ; No, ignore it
             BVC   BUFCNT1        ; VS, count buffer
             LDA   BUFOUT,X       ; Set input=output, empty buffer
             STA   BUFINP,X
BUFCNP1      BVS   BUFCNT5        ; Purged, exit
             LDX   #$00
             BEQ   BUFCNT4        ; Count, return zero
  
BUFCNT1      PHP                  ; Save flags
             SEI                  ; Disable IRQs
             SEC                  ; Prepare for SBC
             LDA   BUFOUT,X       ; Get output pointer
             SBC   BUFINP,X       ; Subtract input pointer
             BCS   BUFCNT2        ; No overflow, use it
             SEC                  ; Prepare for SBC
             SBC   BUFOFF,X       ; Subtract buffer start offset
BUFCNT2      PLP                  ; Get flags back, also restore IRQs
             BCC   BUFCNT3        ; CLC, exit with size counted
             CLC                  ; Prepare for ADC
             ADC   BUFOFF,X       ; Add buffer offset to get NEG(bytes free)
             EOR   #$FF           ; Invert it to get free space
BUFCNT3      TAX                  ; YX=count
BUFCNT4      LDY   #$00           ; All our buffers are <256 bytes
BUFCNT5      RTS

