BSPIO   TITLE   "Z80 BCPL Run Time System  -  Section BSPIO"
        GET     "HEADER"
        GET     "SCBHDR"
        LAYOUT
;  This section contains all the routines to handle byte stream I/O.  It
;  requires the ring BSPLIB to do the I/O to the ring hardware.
        LAYOUT
        REF     BSPRDCH
        REF     BSPWRCH
        REF     BSPRESET
        REF     BSPCLOSE
        REF     FORCETX
        REF     RQCLOSE
        REF     NEXTCO

        DEF     B.FNDI
        DEF     B.FNDO
        LAYOUT
        RELOCATABLE

        ORG     0

BSPIO   DEFB    'BCPL'                  ;  Entry flag for BCPL module
        DEFW    BSPIOE-BSPIO            ;  Length of module in bytes
        LAYOUT
;  FINDINPUT, FINDOUTPUT
;  ---------------------
;
;    Byte stream "findinput" and "findoutput" routines.  Both return the same
;    format of SCB.


B.FNDI
B.FNDO  LD      DE,BSCB                 ;  Load address of SCB
        LD      BC,BSCBL                ;  Length of SCB

        EX      DE,HL                   ;  Swap source and destination pointers
        LDIR                            ;  Copy values across

        PUSH    IX                      ;  Save pointer to SCB
        POP     HL                      ;  Set result from function
        RET                             ;  And return
        LAYOUT
;  Prototype SCB layout
;  --------------------


BSCB    DEFB    B.IN+B.OUT              ;  Flags
        DEFB    0                       ;  POS field
        DEFB    0                       ;  END field
        DEFW    ERR                     ;  Buffer READ routine
        DEFW    ERR                     ;  Buffer WRITE routine
        DEFW    RDCH                    ;  Binary READ routine
        DEFW    WRCH                    ;  Binary WRITE routine
        DEFW    CLOSE                   ;  CLOSE routine
        DEFW    0                       ;  Arg1
        DEFW    0                       ;  Arg2
BSCBL   EQU     $-BSCB                  ;  Length of SCB
        LAYOUT
;  RDCH
;  ----
;
;    Byte stream RDCH routine.  Read a byte from the stream, and return it.
;    If no character is present, then loop until it is.  If the stream is in
;    some special state, then return this as a character as well.


RDCH    LD      C,(IX+S.ARG1+0)         ;  Low byte of BSCB
        LD      B,(IX+S.ARG1+1)         ;  High byte of BSCB
        PUSH    BC                      ;  Save on the stack
        EX      (SP),IY                 ;  And load into IY

RDCH0   CALL    BSPRDCH                 ;  Attempt to read a character
        JP      Z,RDCH1                 ;  Z flag set, so valid data character

;  If we drop through here, then there was no valid data character from the
;  stream.  We should therefore look at the return code, and decide whether
;  the state should be passed to the user.

        CP      20                      ;  RC < 20
        JP      C,RDCH2                 ;  Yes, so retry

        CP      30                      ;  RC = 30
        JP      Z,RDCH3                 ;  Yes, so reset

;  If we drop through here, then the stream is in a hopeless state, and we
;  should return "endstreamch".

        POP     IY                      ;  Restore IY
        LD      HL,ENDCH                ;  Load "endstreamch"
        RET                             ;  And return

;  If we come here, then the byte stream has been reset, and so we should
;  return "ch.reset" back to the user.

RDCH3   POP     IY                      ;  Restore IY
        LD      HL,CH.RST               ;  Load "ch.reset"
        RET                             ;  And return

;  If we come here, then we must retry.  Call "nextco", and then read a
;  character again.

RDCH2   CALL    NXTCO                   ;  Go to next coroutine
        JP      RDCH0                   ;  Loop again

;  If we come here, then we have found a valid data character.  The should be
;  returned immediately to yhe user.

RDCH1   LD      L,A                     ;  Low byte of character
        LD      H,0                     ;  High byte of character
        POP     IY                      ;  Restore IY
        RET                             ;  And return
        LAYOUT
;  WRCH
;  ----
;
;    Write the character given down the byte stream.  Check for one of the
;    special control characters, and handle it if necessary.  Otherwise, just
;    wait until we can write the character, and then write it.

WRCH    LD      C,(IX+S.ARG1+0)         ;  Low byte of BSCB
        LD      B,(IX+S.ARG1+1)         ;  High byte of BSCB
        PUSH    BC                      ;  Save on the stack
        EX      (SP),IY                 ;  And load into IY

        LD      A,H                     ;  High byte of character
        OR      A                       ;  Is it zero ?
        JP      Z,WRCH0                 ;  Yes, so write normal character

;  If we drop through here, then we have been given a special control
;  character to handle.  The ones we know about are:
;
;      ch.reset          cause a byte stream reset
;      ch.push           cause a byte stream push
;      ch.closerq        cause a byte stream close request

        EX      DE,HL                   ;  Get character in DE

        LD      HL,CH.RST               ;  Load "ch.reset"
        OR      A                       ;  Clear carry flag
        SBC     HL,DE                   ;  Characters equal ?
        JP      Z,WRCH3                 ;  Reset handler

        LD      HL,CH.PSH               ;  Load "ch.push"
        OR      A                       ;  Clear carry flag
        SBC     HL,DE                   ;  Characters equal ?
        JP      Z,WRCH4                 ;  Push handler

        LD      HL,CH.CRQ               ;  Load "ch.closerq"
        OR      A                       ;  Clear carry flag
        SBC     HL,DE                   ;  Characters equal ?
        JP      Z,WRCH5                 ;  Close request handler

;  If we drop through there, we don't understand this character, and so we
;  should sink it.

        POP     IY                      ;  Restore IY
        RET                             ;  And return

;  If we come here, then we have been asked to reset the byte stream.  Call
;  the routine to do this, and return.

WRCH3   CALL    BSPRESET                ;  Reset the byte stream
        POP     IY                      ;  Restore IY
        RET                             ;  And return

;  If we come here, then we have been asked to push the data down the byte
;  stream.  We must wait until this has been done properly.

WRCH4   CALL    FORCETX                 ;  Force the data down the byte stream
        JP      Z,WRCH6                 ;  Success, so return

        CP      20                      ;  RC < 20
        JP      NC,WRCH6                ;  Fatal error, so return

        CALL    NXTCO                   ;  Go to next coroutine
        JP      WRCH4                   ;  And continue trying

WRCH6   POP     IY                      ;  Restore IY
        RET                             ;  And return

;  If we come here, then we have been asked to send a "close request" down
;  the byte stream.  We must wait until this has been done properly.

WRCH5   CALL    RQCLOSE                 ;  Send a close request
        JP      Z,WRCH7                 ;  Success, so return

        CP      20                      ;  RC < 20
        JP      NC,WRCH7                ;  Fatal error, so return

        CALL    NXTCO                   ;  Go to next coroutine
        JP      WRCH5                   ;  And continue trying

WRCH7   POP     IY                      ;  Restore IY
        RET                             ;  And return

;  If we come here, then this is a normal character which we must attempt to
;  write out.

WRCH0   LD      A,L                     ;  Load character to be written
        PUSH    HL                      ;  Save HL
        CALL    BSPWRCH                 ;  Attempt to write the character
        POP     HL                      ;  Restore HL
        JP      Z,WRCH1                 ;  Z flag set implies success

;  If we drop through here, then the write failed.  Examine the return code
;  to decide on what to do next.

        CP      20                      ;  RC < 20 ?
        JP      C,WRCH2                 ;  Yes, so retry the write

;  If we drop through here, then the stream is in a bad way, and we should
;  just sink the character.

        POP     IY                      ;  Restore IY
        RET                             ;  And return

;  If we come here, then we should call "nextco", and attempt to write the
;  character again.

WRCH2   PUSH    HL                      ;  Save HL
        CALL    NXTCO                   ;  Go to next coroutine
        POP     HL                      ;  Restore HL
        JP      WRCH0                   ;  And loop

;  If we come here, then we have successfully written the character out.
;  We should therefore return to the caller.

WRCH1   POP     IY                      ;  Restore IY
        RET                             ;  And return
        LAYOUT
;  CLOSE
;  -----
;
;    Close routine, called from "endread" or "endwrite" when a stream
;    is closed.


CLOSE   LD      C,(IX+S.ARG1+0)         ;  Low byte of BSCB
        LD      B,(IX+S.ARG1+1)         ;  High byte of BSCB
        PUSH    BC                      ;  Save on the stack
        EX      (SP),IY                 ;  And load into IY

        CALL    BSPCLOSE                ;  Close the byte stream

        POP     IY                      ;  Restore IY
        RET                             ;  And return
        LAYOUT
;  ERROR
;  -----
;
;    Enter here if the user attempts to muck around with the routines in the
;    stream control block.


ERR     ERRTRAP                         ;  Enter the debugger
        RET                             ;  And return
        LAYOUT
;  NXTCO
;  -----
;
;    Go to next coroutine, preserving IX, IY and the current I/O streams.


NXTCO   PUSH    IX                      ;  Save IX
        PUSH    IY                      ;  Save IY

        CALL    NEXTCO                  ;  Go to next coroutine

        POP     IY                      ;  Restore IY
        POP     IX                      ;  Restore IX
        RET                             ;  And return
        LAYOUT
        ALIGN
        DEFW    0                       ;  End of global list
        DEFW    0                       ;  HRG
        LAYOUT
BSPIOE  END


