INITIO  TITLE   "Z80 BCPL Run Time System  -  Section INITIO"
        GET     "HEADER"
        LAYOUT
;  This section contains the basic stand alone I/O routines for the Z80 BCPL
;  system.  The nature of the raw I/O devices will depend on the hardware
;  for which the code is intended.
;
;  **************************************************************************
;  **                                                                      **
;  **   Version for Lab Z80s where the standalone I/O must not halt ring   **
;  **   transactions.  A ring timer is created as a coroutine, and the     **
;  **   main program "start" is also created as a coroutine.  The old      **
;  **   BCPL stack is freed, and the coroutine system entered at STARTCO.  **
;  **                                                                      **
;  **************************************************************************
        LAYOUT
        REF     C.FNDI
        REF     C.FNDO
        REF     B.FNDI
        REF     B.FNDO
        REF     V.FNDI
        REF     V.FNDO

        REF     TRYTX
        REF     TRYRX
        REF     DOSSPTIM
        REF     TIMER

        REF     INITCO
        REF     COCREATE
        REF     STARTCO
        REF     NEXTCO

        REF     FREEVEC

        DEF     C.RDCH
        DEF     C.WRCH


G.SARD  EQU     2*21                    ;  Global number of "sardch"
G.SAWR  EQU     2*22                    ;  Global number of "sawrch"
G.INIO  EQU     2*50                    ;  Global number of "initio"
G.IOLS  EQU     2*51                    ;  Global number of "iolist"
G.MAXG  EQU     2*150                   ;  Highest referenced global

COSLOT  EQU     20                      ;  Number of coroutine slots
COSTCK  EQU     500                     ;  Size of coroutine stack for "start"

U.CONT  EQU     #X10                    ;  UART control register
U.STAT  EQU     #X10                    ;  UART status register
U.TX    EQU     #X11                    ;  UART transmit register
U.RX    EQU     #X11                    ;  UART receive register

U.RESET EQU     #X03                    ;  RESET command
U.INIT  EQU     #X09                    ;  INIT values

U.RDRF  EQU     #X01                    ;  Read register full
U.TDRE  EQU     #X02                    ;  Transmit register empty
        LAYOUT
        RELOCATABLE

        ORG     0

INITIO  DEFB    'BCPL'                  ;  Entry flag for BCPL module
        DEFW    INITIOE-INITIO          ;  Length of module in bytes
        LAYOUT
;  INITIO
;  ------
;
;    initio()


        ALIGN

INIO    SETLINK                         ;  Set up linkage information

        XOR     A                       ;  Clear accumulator
        LD      (IY-24),A               ;  Low byte of CIS
        LD      (IY-23),A               ;  High byte of CIS
        LD      (IY-22),A               ;  Low byte of COS
        LD      (IY-21),A               ;  High byte of COS

;  Now, initialise the UART so that we can use it later.

        LD      A,U.RESET               ;  RESET command
        OUT     (U.CONT),A              ;  Reset the UART
        LD      A,U.INIT                ;  INIT values
        OUT     (U.CONT),A              ;  Initialise the UART

;  We should now create the system coroutines.  These are the ring timer, and
;  the user coroutine "start".  We should call "initco" to initialise the
;  coroutine library, and then get creating.

        LD      HL,COSLOT               ;  Number of coroutine slots
        CALL    INITCO                  ;  Initialise the coroutine library
        JP      NZ,INIO0                ;  Error, so call the debugger

;  Having initialised the coroutine library, create the coroutine associated
;  with ring timing.

        LD      DE,RGTM                 ;  Address of ring timer
        LD      HL,50                   ;  Size of stack

        PUSH    IX                      ;  Save IX
        PUSH    IY                      ;  Save IY
        CALL    COCREATE                ;  Create the ring timer coroutine
        POP     IY                      ;  Restore IY
        POP     IX                      ;  Restore IX

        JP      NZ,INIO0                ;  If error, then error return

;  Now, create the coroutine associated with the user "start" function.  Since
;  it requires a BCPL stack and global vector, we must call BCPL "cocreate".

        LD      L,(IY-126)              ;  Low byte of "start"
        LD      H,(IY-125)              ;  High byte of "start"
        LD      DE,COSTCK               ;  Size of BCPL stack
        EXX                             ;  Save argument register set
        LD      L,(IY+114)              ;  Low byte of "cocreate"
        LD      H,(IY+115)              ;  High byte of "cocreate"
        APPLY                           ;  Apply the function
        DEFB    0                       ;  Increase in stacksize
        LD      A,H                     ;  High byte of result
        OR      L                       ;  Sets "Z" if FALSE
        JP      Z,INIO0                 ;  Create failure, so call debugger

;  We have now finished with the initialisation, and hence we can free the
;  root stack and global vector, and enter the coroutine system.

        PUSH    IX                      ;  Save stack pointer
        POP     HL                      ;  And store in HL
        LD      DE,-128                 ;  Correction factor
        ADD     HL,DE                   ;  Get pointer to storage
        CALL    FREEVEC                 ;  Free the storage

        PUSH    IY                      ;  Save global vector pointer
        POP     HL                      ;  And store in HL
        LD      DE,-128                 ;  Correction factor
        ADD     HL,DE                   ;  Get pointer to storage
        CALL    FREEVEC                 ;  Free the storage

        JP      STARTCO                 ;  Enter the coroutine system

;  If we come here, then some sort of initialisation error occured, and hence
;  we should enter the debugger.

INIO0   ERRTRAP                         ;  Enter the debugger
        LAYOUT
;  INAME, ONAME
;  ------------
;
;    Names of the default input and output files selected on entry to the
;    user program.


        ALIGN
INAME   DEFB    "**"                    ;  Default input file name

        ALIGN
ONAME   DEFB    "**"                    ;  Default output file name
        LAYOUT
;  SARDCH
;  ------
;
;    ch  :=  sardch()


        ALIGN

SARD    PUSH    HL                      ;  Save return address
        CALL    C.RDCH                  ;  Read a character
        LD      H,0                     ;  High byte of result
        LD      L,A                     ;  Character
        RET                             ;  And return
        LAYOUT
;  SAWRCH
;  ------
;
;    sawrch( ch )


        ALIGN

SAWR    PUSH    HL                      ;  Save return address
        EXX                             ;  Switch register sets
        LD      A,L                     ;  Load character to be printed
        CALL    C.WRCH                  ;  Write it out
        RET                             ;  And return
        LAYOUT
;  C.RDCH, C.WRCH
;  --------------
;
;    Raw console read and console write routines.  These routines are called
;    from SARDCH and SAWRCH, and also from the console handler.  Entry is
;    via Z80 CALL, and arguments/results are passed in A.  No other registers
;    are corrupted.


C.RDCH  IN      A,(U.STAT)              ;  Read status register
        AND     U.RDRF                  ;  Is there a character ?
        JP      NZ,C.RDCH0              ;  Yes, so read it, and return

        CALL    NXTCO                   ;  Give other coroutines a chance
        JP      C.RDCH                  ;  Carry on looping

C.RDCH0 IN      A,(U.RX)                ;  Read the character
        RET                             ;  And return


C.WRCH  PUSH    AF                      ;  Save character

C.WRCH0 IN      A,(U.STAT)              ;  Read status register
        AND     U.TDRE                  ;  Is register empty ?
        JP      NZ,C.WRCH1              ;  Yes, so write character

        CALL    NXTCO                   ;  Give other coroutines a chance
        JP      C.WRCH0                 ;  Carry on looping

C.WRCH1 POP     AF                      ;  Restore character
        OUT     (U.TX),A                ;  Write it out
        RET                             ;  And return
        LAYOUT
;  NXTCO
;  -----
;
;    Save all the registers, and call the NEXTCO routine in order to give
;    other (ring) coroutines a chance.


NXTCO   PUSH    HL                      ;  Save HL
        PUSH    DE                      ;  Save DE
        PUSH    BC                      ;  Save BC
        PUSH    AF                      ;  Save AF

        EXX                             ;  Swap main register set
        EX      AF,AF'                  ;  And AF as well

        PUSH    HL                      ;  Save HL'
        PUSH    DE                      ;  Save DE'
        PUSH    BC                      ;  Save BC'
        PUSH    AF                      ;  Save AF'

        PUSH    IX                      ;  Save IX
        PUSH    IY                      ;  Save IY

        CALL    NEXTCO                  ;  Go to next coroutine

        POP     IY                      ;  Restore IY
        POP     IX                      ;  Restore IX

        POP     AF                      ;  Restore AF'
        POP     BC                      ;  Restore BC'
        POP     DE                      ;  Restore DE'
        POP     HL                      ;  Restore HL'

        EXX                             ;  Swap back to main register set
        EX      AF,AF'                  ;  And AF as well

        POP     AF                      ;  Restore AF
        POP     BC                      ;  Restore BC
        POP     DE                      ;  Restore DE
        POP     HL                      ;  Restore HL

        RET                             ;  Finally!
        LAYOUT
;  IOLIST
;  ------
;
;    List of device names and their associated "findinput" and "findoutput"
;    routines.


        ALIGN

IOLS    DEFW    C.NAME,C.FNDI,C.FNDO    ;  Console handler
        DEFW    B.NAME,B.FNDI,B.FNDO    ;  Byte stream handler
        DEFW    V.NAME,V.FNDI,V.FNDO    ;  Virtual terminal handler
        DEFW    0,0,0                   ;  End of list marker


C.NAME  DEFB    "**"                    ;  Name of console device
B.NAME  DEFB    "BSP"                   ;  Name of byte stream device
V.NAME  DEFB    "VTP"                   ;  Name of virtual terminal device
        LAYOUT
;  RINGTIMER
;  ---------
;
;    Coroutine to handle timing of ring transactions.  It repeatedly calls
;    TRYTX, TRYRX and when necessary,  DOSSPTIM and TIMER.


        ALIGN

RGTM    CALL    NEXTCO                  ;  On entry, no initialisation to do

RGTM0   LD      HL,500                  ;  Loop counter

RGTM1   PUSH    HL                      ;  Save loop counter
        CALL    NEXTCO                  ;  Give other coroutines a chance
        CALL    TRYTX                   ;  Attempt a transmission
        CALL    TRYRX                   ;  Attempt a reception
        POP     HL                      ;  Restore loop counter

        DEC     HL                      ;  Decrement loop counter
        LD      A,H                     ;  Load high byte of counter
        OR      L                       ;  Check for zero ?
        JP      NZ,RGTM1                ;  Continue looping

        CALL    DOSSPTIM                ;  Call the SSP timer
        CALL    TIMER                   ;  Call the BSP timer

        JP      RGTM0                   ;  Start all over again
        LAYOUT
        ALIGN

        DEFW    0                       ;  End of global list
        DEFW    G.SARD,SARD             ;  Entry of SARDCH
        DEFW    G.SAWR,SAWR             ;  Entry of SAWRCH
        DEFW    G.INIO,INIO             ;  Entry of INITIO
        DEFW    G.IOLS,IOLS             ;  Address of IOLIST
        DEFW    G.MAXG                  ;  HRG
        LAYOUT
INITIOE END


