;************************************************************************
;    FIFO BUFFERS FOR CP/M BIOS
;
; The following code is submitted by Glenn Ewing and Bob Richardson
; of MicroPro.  It is intended as an aid to those who wish to implement
; interrupts in their systems.    Also included in the submission is a
; sample port driver and console bios routine intended to show the
; implementation of the routines as ring buffers for a Bios.
;
;  Further Distribution of this file should contain the notice:
;         This code Copyright (c) 1981 MicroPro International Corp.
;                       made available by permission of the authors
;                       and by MicroPro. Not to be Resold.
;     Good Luck!
;               ps: most of this code is Glenn's - Bob R.
z80
size0   equ     16      ; size definition for the fifo queue -must be power of
;                         2 - actual size depends on the device in questionc
;
;       fifo stuf
       defb    'COPYRIGHT (c) 1981 MicroPro International Corp'
;
;
;       This module impliments first-in-first-out buffers for the
;       various BIOS I/O paths.  The buffers function as follows:
;
;       If:
;               cnt = number of characters currently stored in buffer, and
;
;               nout = relative position in buffer of characer to be output
;                         next, and
;
;               size = size of buffer (even power of 2), and
;
;               base = address of bottom of buffer, then
;
;       A character is stored in the buffer at address:
;
;               base + ((cnt + nout) mod size)
;
;       and the variables are updated:
;
;               cnt = (cnt + 1) mod size
;
;       Alternately, a character is retrieved from address:
;
;               base + nout
;
;       and the variables are updated:
;
;               cnt = (cnt - 1) mod size
;
;               nout = (nout + 1) mod size.
;
;
;       These items are stored in memory as follows:
;
fifo    macro   size
;
       defb    0               ; cnt
       defb    0               ; nout
       defb    size-1          ; mask for mod ops
       defs    size            ; actual buffer
       endm
;
;
;
;       The fifo input and output routines provide no protection
;       from underflow and overflow.  The calling code must use
;       the fstat routine to ensure that these conditions are
;       avoided.  Also, the calling code must enable and disable
;       interupts as appropriate to ensure proper maintainance of
;       the variables.
;
;
fstat:
;       routine to determine status (fullness) of a buffer.
;       enter with ix = adr of cnt.
;       return Z-flag set if buffer empty, Cy set if buffer full.
;       note that buffer capacity is actually size-1.
;
       ld      a, (ix + 0)             ; get cnt
       push    de
       ld      e, (ix + 2)             ; get mask
       and     e                       ; cnt = cnt mod size
       dec     e                       ; e = size - 2
       cp      e                       ; test for full
       pop     de
       inc     a                       ; clear z leaving cy
       dec     a
       ccf
       ret
;
;
fin:
;       routine to enter a character into a buffer.
;       enter with c=chr, ix=.cnt
;
       ld      a, (ix + 0)             ; compute: (cnt + nout) mod size
       inc     (ix + 0)                ; first update cnt
       add     a, (ix + 1)
       and     (ix + 2)
       push    de
       ld      e, a                    ; compute base + nin
       ld      d, 0
       inc     ix
       inc     ix
       inc     ix
       add     ix, de
       pop     de
       ld      (ix+0), c               ; store character
       ret
;
;
;
fout:
;       routine to retreve a character from a buffer.
;       enter with ix=.cnt
;       return with c=chr
;
       dec     (ix + 0)                ; update cnt
       ld      a, (ix + 1)             ; compute: base + nout
       inc     (ix + 1)
       and     (ix + 2)
       push    de
       ld      e, a
       ld      d, 0
       inc     ix
       inc     ix
       inc     ix
       add     ix, de
       pop     de
       ld      c, (ix + 0)             ; get chr
       ret
;
fifo0:  fifo    size0                   ;  sample invocation of the fifo macro
fifo1:  fifo    size0                   ; dummy queues for sample codes
;
sample:
       jp      cboot                   ; phoney jp
       jp      wboot                   ; phoney jp
       jp      const                   ; console status
       jp      conin                   ; console input vector
       jp      conout                  ; console output routine
; end of phoney bios branch vector-
; note that for this end of the queueing, all that is necessary is to test
; the status of the queue and return a value based on the queue status-
; not the status of the device-
;
;*************************************************************************
; start of the front end queue - dequeue routines - these routines are
; code samples only and are not intended to run on any particular machine
; and in fact ignore specifically some details of implementation
const:
       ld      ix,fifo0                ; note on real sys, you must save ix
;                                         this sets up the pointer for the
;                                         fifo routines
       call    fstat                   ; check on the status of the queue
       ret     z                       ; return if z-flag set, and hence
;                                       ; no char is ready - this will
;                                       ; also have zero in a at this point
       ld      a,0ffh                  ; get flag to show that a char is rdy
       ret                             ; and return that value -
;
;
conin:                                  ; read a char from the queue
       call    const                   ; first, wait until a char is
                                       ; available - necessary to avoid
                                       ; underflow
       jp      z,conin                 ; loop until char is ready
       di                              ; interrupts must be off to use
                                       ; fin and fout routines
       call    fout                    ; get a character from the queue
       ld      a,c                     ; and put it in correct register
       and     07fh                    ; strip parity per spec
       ret                             ; to caller
;
;
conout:                                 ; send a character to the console
       ld      ix,fifo1                ; setup for console output queue
       call    fstat                   ; test queue status
       jp      c,conout                ; wait for a space in the queue
                                       ; note: carry set if queue is full
       di                              ; interrupts off for queue manipulation
       push    ix                      ; save queue pointer for conditional
                                       ; output routine
       call    fin                     ; put a character into the fifo queue
       pop     ix                      ; restore pointer
       call    tryout                  ; routine to output a character and
                                       ; turn on interrupts from console
                                       ; if a character is waiting and if
                                       ; port is not busy
       ei                              ; restart interrupts
       ret                             ; to caller

tryout:
;     This routine is installation dependant-
;     it must output a character to the console if and only if
;     the port is ready to accept a character
;     I will not supply the routine, but rather the logic for the routine
;     which would run as follows:
;         select the status port for the console or modem
;         test the tx ready bit
;         return if not ready
;         otherwise if tx is ready to go
;         call  fout <- gets next char in c register
;         output the character to the data port
;         then return
;*****************************************************************************
;*****************************************************************************
;  now - the easy part - servicing the interrupts from the console/printer/
;  whatever you have hooked up - I am going to just give you the logic for
; the routine we have been working on, without getting into details as this
; is obviously a highly installation dependant part of the code.
;        the philosophy of the coding is, however, the same as the front end
;   when the device needs a character, get one from the queue with
;          call  fout                   ; and then
;   send the character to the device through whatever mode you use to speak
;   to it - if the queue becomes empty, you generally want to ignore the next
;   interrupt
;  ++++++++++++++++++++++++++++++++++++++++++++
;   if, therefore
;   it is an input device with a character ready
;   read the character from the device and then
;   use
;          call  fin            ; to put the newly read character into  queue
;  if the input queue fills up, you will want to ignore further characters-
;  one standard thing to do is just throw them into the bit bucket and return
;  from the interrupt
;  sample code for the output interrupt on an sio might read:
sint0:
       ld      ix,fifo1        ; get output queue
       call    fstat           ; check queue status
;
       jp      z,underf        ; the queue is empty - no more chars to send
       call    tryout          ; note: this is the same routine we created
                               ; prviously to handle output to port if not
                               ; busy -
;  This is interrupt code so it is not necessary to disable the interrupts
;  before calling the fin and fout routines
underf:
;this routine should turn off the transmit interrupt with a device dependant
; code - ie, for the sio it would send a 28h to the status port for the sio
; and then
       ret
;the code for an input interrupt would be similar, except the routine would
; read a byte from the device
; call fstat
; ret  c        ; throw away character if queue full
; call fin      ; else put it in queue
; ret           ; and return
;
;
;
;
;******************************************************************************
; the end - note - this file is copyright (c) Micropro International Corp.
; and may not be duplicated in whole or in part for resale. The code
; contained here does not exist in any real implementation of bios except for
; the fifo routines and are presented here as a service and in expression
; of our support for this group.  Any source copy of the fifo stuff must
; retain the credits for this piece of code.  Good luck, may all your
; interruptions be quick ones.
;                             -bob-