!%externalroutinespec Console %alias "3L_IMP_CONSOLE"(%string(255) S)
!
! >>> STREAMS <<<
!
! Basic stream-handling package for the new I/O system.
!
! This module provides the basic stream manipulation primitives,
! without knowing quite what a stream might be made of.
!

%from IMP %include Formats, Buffers, HandCall, MCode, Depio

%record %format PDL Fm (%record(PDL Fm) %name PDL,
                        %record(SCB Fm) %name SCB,
                        %integer Level)

%const %record(PDL Fm) %name PDL == 0

%const %integer Max Streams = 15

%record %format Stream Vector(%record(PDL Fm)%name %array Stream(0: Max Streams) )

%external %integer %spec Level %alias "3L___io_level"

%external %integer -
  In Str %alias "3L___in_stream"  = 0, {number of currently selected input}
 Out Str %alias "3L___out_stream" = 0  {number of currently selected output}

%external %record(Stream Vector) In  %alias "3L___in_vector"  = 0
%external %record(Stream Vector) Out %alias "3L___out_vector" = 0

{###################################################################}
{###################################################################}
{###                                                             ###}
{###   E R R O R   C H E C K I N G   A N D   R E P O R T I N G   ###}
{###                                                             ###}
{###################################################################}
{###################################################################}

!
! >> EOF <<
!
! This routine signals EOF (9,1,0) in a consistent way throughout the RTS
!
%external %routine Eof %alias "3L___eof"
   %signal 9, 1, 0
%end

!
! >> CHECK STREAM <<
!
%external %routine Check Stream %alias "3L___check_stream" (%integer S)
   %signal 9, 2, S %unless 0 <= S <= Max Streams
%end

{###########################################}
{###########################################}
{###                                     ###}
{###   S T R E A M   S E L E C T I O N   ###}
{###                                     ###}
{###########################################}
{###########################################}

!
! >> SELECT INPUT <<
!
%external %routine Select Input %alias "3L_IMP_SELECT_INPUT" (%integer Stream)
   %signal 9, 2, Stream %unless 0 <= Stream <= Max Streams
   In Str = Stream
   In SCB == In_Stream(Stream)_SCB
%end

!
! >> SELECT OUTPUT <<
!
%external %routine Select Output %alias "3L_IMP_SELECT_OUTPUT" (%integer Stream)
   %signal 9, 2, Stream %unless 0 <= Stream <= Max Streams
   Out Str = Stream
   Out SCB == Out_Stream(Stream)_SCB
%end

{#########################################}
{#########################################}
{###                                   ###}
{###   S T R E A M   P U S H / P O P   ###}
{###                                   ###}
{#########################################}
{#########################################}

!
! >> PUSH INPUT STREAM <<
!
%external %routine Push Input %alias "3L___push_input_stream" -   
                            (%integer Stream, %record(SCB Fm) %name S)
   %record(PDL Fm) %name P
   Check Stream(Stream)
   P == New(PDL)
   P_SCB == S;  P_PDL == In_Stream(Stream);  P_Level = Level
   In_Stream(Stream) == P
   Select Input(In Str)
%end

!
! >> PUSH OUTPUT STREAM <<
!
%external %routine Push Output %alias "3L___push_output_stream" (
                               %integer Stream, %record(SCB Fm) %name S )
   %record(PDL Fm) %name P
   Check Stream(Stream)
   P == New(PDL)
   P_SCB == S;  P_PDL == Out_Stream(Stream);  P_Level = Level
   Out_Stream(Stream) == P
   Select Output(Out Str)
%end

{#######################################################################}
{#######################################################################}
{###                                                                 ###}
{###   I N I T I A L I S A T I O N   &   N U L L   F A C I L I T Y   ###}
{###                                                                 ###}
{#######################################################################}
{#######################################################################}

%own %record(SCB Fm) Null In  = 0,
                     Null Out = 0

%constinteger Null Buffer Size = 15
%own %string(13) Null Name = "<null stream>"
%own %byte %array Null Chs (0: Null Buffer Size-1)

!
! >> NULL FULL <<
!
%external %routine Null Full %alias "3L___null_full"
   {R1 = addr(SCB)}
   {R1 = Char}
   %record(SCB Fm) %name S
   *STR _ R1, S
   S_Next = S_ThisB_Base
%end

!
! >> OPEN NULL INPUT <<
!
%external %routine Open Null Input %alias "3L_IMP_OPEN_NULL_INPUT" -
                                 (%integer Stream)
   Push Input(Stream,Null In)
%end

!
! >> OPEN NULL OUTPUT <<
!
%external %routine Open Null Output %alias "3L_IMP_OPEN_NULL_OUTPUT" -
                                  (%integer Stream)
   Push Output(Stream,Null Out)
%end

!
! >> STREAM INIT <<
!
! Initialise the stream I/O package. This opens a NULL stream on
! every stream, both for input and output. These are opened at level 0.
! Level is then raised to 1. NB level 0 streams are NEVER EVER popped.
!
! Subsequent calls on STREAM INIT just up the level number again.
! The initialisation package calls us twice, once after the definitions
! for stream 0 and 1 have been overlaid. This leaves LEVEL at 2 for
! execution of user programs.
!
%external %routine Stream Init %alias "3L___stream_init"
   %integer I, D1, D2

   %if Level = 0 %start
      D1 = Addr 1(Dummy 1)
      D2 = Addr 2(Dummy 2)
      Null In _Buffer Handler = Addr 0 (EOF)
      Null In_Reset    Handler = D1;        Null Out_Reset    Handler = D1
      Null In_Close    Handler = D2;        Null Out_Close    Handler = D2
      Null In_Position Handler = D2;        Null Out_Position Handler = D2
      Null  In_NameB_Base = Addr(Null Name)
      Null Out_NameB_Base = Addr(Null Name)
      Null Out_Next       = Addr(Null Chs(0))
      Null Out_ThisB_Base = Addr(Null Chs(0))
      Null Out_ThisB_Size = Null Buffer Size
      Null Out_Limit = Null Out_Next + Null Buffer Size
      Null Out_Breaks           = -1
      Null Out_Complete Handler = D1
      Null Out_Reset Handler    = D1
      Null Out_Buffer Handler   = Addr 0 (Null Full)

      %for I = 0, 1, Max Streams %cycle
         Open Null Input (I)
         Open Null Output(I)
      %repeat
   %finish

   Select Input(1)
   Select Output(1)
   Level = Level+1 {protect level 0 streams}
%end

{#################################################}
{#################################################}
{###                                           ###}
{###   C O M M O N   C L O S I N G   C O D E   ###}
{###                                           ###}
{#################################################}
{#################################################}

%routine ABC(%record(Stream Vector) %name SV, %integer AC, Str)
   %record(SCB Fm) %name S
   %record(PDL Fm) %name P == SV_Stream(Str)
   %return %if P_Level = 0 %or P_Level < Level {not ours or bottom level}
   SV_Stream(Str) == P_PDL
   Dispose(P)
   S == P_SCB
   S_Flags = S_Flags ! AC
   Close SCB(S, AC)
%end

{###################################################}
{###################################################}
{###                                             ###}
{###   W I N D   D O W N   P R O C E D U R E S   ###}
{###                                             ###}
{###################################################}
{###################################################}

%external %routine Wind Down %alias "3L___stream_wind"
   %integer Sno
   Level = Level - 1
   %for Sno = 0, 1, Max Streams %cycle
      ABC( In, 0, Sno) %while  In_Stream(Sno)_Level > Level
      ABC(Out, 0, Sno) %while Out_Stream(Sno)_Level > Level
   %repeat
   Select Input(1);  Select Output(1)
%end

{#####################################################}
{#####################################################}
{###                                               ###}
{###   U S E R   L E V E L   P R O C E D U R E S   ###}
{###                                               ###}
{#####################################################}
{#####################################################}

!
! >> CLOSE INPUT <<
!
%external %routine Close Input %alias "3L_IMP_CLOSE_INPUT"
   ABC(In, 0, In Str)
   Select Input(In Str)
%end

!
! >> CLOSE OUTPUT <<
!
%external %routine Close Output %alias "3L_IMP_CLOSE_OUTPUT"
   ABC(Out, 0, Out Str)
   Select Output(Out Str)
%end

!
! >> ABANDON INPUT <<
!
%external %routine Abandon Input %alias "3L_IMP_ABANDON_INPUT"
   ABC(In, SCB Flag Abandoned, In Str)
   Select Input(In Str)
%end

!
! >> ABANDON OUTPUT <<
!
%external %routine Abandon Output %alias "3L_IMP_ABANDON_OUTPUT"
   ABC(Out, SCB Flag Abandoned, Out Str)
   Select Output(Out Str)
%end
