THE 'RESET' RESTART
This is the normal 'reset'. Addresses 1019 and 101A contain zero
so the returnaddress is 0, the 'restart'-routine in the Spectrum
ROM.

0000 RESET     DI
               LD   SP,+1019          Stack 0000 and  return  to
               JP   1748,PAGE_OUT     the Spectrum ROM.
               DEFB +FF


THE 'SHADOW_ROM ERROR' RESTART
The Discovery-ROM error handler. It takes a one-byte immediate
error code.

0008 SH_ERROR  JP   0168,CONTSIG


THE 'ENTRY_1' ROUTINE
This is the entry point when coming from a RST 0008-instruction
when the Spectrum ROM is paged in. When a RST  0008  is  encoun-
tered,  the  instruction  'LD  HL,(CH_ADD)'  is  executed,   the
Discovery ROM is  paged  in,  and  the  execution  continues  at
address 000B.

000B ENTRY_1   POP  HL
               PUSH AF
               JP   007A,ENTRY_2      Jump forward.


THE 'CALL A SPECTRUM ROM SUBROUTINE' RESTART
This restart calls a subroutine in the Spectrum ROM. The address
of the subroutine should be put immediately after the  RST  0010
(or CALL 0010) instruction.

0010 CAL_SPEC  EX   (SP),HL           Pass HL to  IY  and  fetch
               POP  IY                the returnaddress in HL.
               PUSH DE                Store the old value of DE.
               LD   E,(HL)            Read the required  address
               INC  HL                in DE. (continues forward)
               JR   0067,CAL_SPEC2


THE 'RUNNING OR CHECKING SYNTAX ?' RESTART
Returns the zero flag set if the Spectrum is currently  checking
syntax rather than running a statement.

0018 TEST_RUN  BIT  7,(FLAGS)
               RET
               DEFB +FF


THE 'PRINT THE COPYRIGHT MESSAGE' ROUTINE

001E (C)_MSG   RST  0008,SH_ERROR     Report
               DEFB +47               (c) Dave Corney 1984-86


THE 'NEXT CHARACTER' RESTART
Identical to the Spectrum RST 0020. Gets the next character from
the current BASIC-line.

0020 NEXT_CHAR RST  0010,CAL_SPEC     Call Spectrum ROM.
               DEFW +0020,NEXT_CHAR
               RET

               DEFB +FF, +FF, +FF     Unused locations.
               DEFB +FF


THE 'FLOATING POINT CALCULATOR' RESTART
Identical to the Spectrum ROM RST 0028,  except  that  a  length
byte should be inserted as first offset (nr. of offsets + 2).

0028 FP_CALC   POP  HL                Fetch the returnaddress in
               LD   C,(HL)            HL and the lengthbyte in C
               LD   B,+00
               PUSH HL                Restack returnaddress
               JP   0648,FP_CALC_2


THE 'LOOKUP' RESTART
This is the most important restart. It gives access to  all  the
tables which the Discovery uses. It should be called  by loading
the table-entry in the B-register, and placing a 1-byte immedia-
te table-number after the RST 0030.

0030 LOOKUP    POP  HL                Read the  table-number  in
               LD   C,(HL)            the C-register and restore
               INC  HL                the Program Counter.
               PUSH HL
               JP   05B1,LOOKUP_2     Jump to continue.


THE 'MASKABLE INTERRUPT' ROUTINE
It behaves exactly the same as the Spectrum's RST 0038.

0038 MASK_INT  PUSH IY                Call the Spectrum ROM, but
               RST  0010,CAL_SPEC     also preserve the IY-regis
               DEFW 0038,MASK_INT     ter.
               POP  IY
               RET

               DEFB +FF, +FF, +FF     Unused locations.
               DEFB +FF, +FF, +FF
               DEFB +FF, +FF


THE 'KEY_INT' ROUTINE
This routine initialises the IC 6116's RAM  and  continues  with
the KEY_INT subroutine in the Spectrum ROM at address +004A.

0048 KEY_INT   PUSH BC                Prepare the stack for  the
               PUSH HL                KEY_INT subroutine.
               PUSH DE
               PUSH BC
               PUSH AF
               LD   A,(3001)
               RRCA                   Is  IC  6116  present  and
               CALL NC,1554,INIT_RAM2 initialised? Try to initia
                                      lise the RAM if not.
               RST  0030,LOOKUP       Lookup the address to page
               DEFB +0E               out and/or return.
               CALL 0087,CALL_JP      Perform that operation.
               POP  AF                Restore the stack.
               POP  BC
               POP  DE
               LD   HL,+0049          The address in  the  Spec-
               EX   (SP),HL           trum ROM. Store it.
               JP   1748,PAGE_OUT     Make an indirect jump  via
                                      the PAGE_OUT subroutine.


THE 'NON-MASKABLE INTERRUPT' ROUTINE
This is the address the program is forced to call during a NMI.
In the Spectrum ROM  the  routine  contains  a  'bug',  but  the
Discovery uses it to call the hardware directly.  On  entry  the
HL-reisterpair contains the address of the appropriate  service-
routine (load, save or format).

0065 INT_ALL   RST  0038,MASK_INT     This address is called  if
                                      both interrupttypes should
                                      be taken care of.
0066 NMI       JP   (HL)


THE 'CALL A SPECTRUM ROM ROUTINE (2)' ROUTINE
The continuation of the CAL_SPEC restart at 0010. The  stack  is
set up to make sure that the program resumes  its  execution  at
the address following the DEFW-instruction. The  return  address
is stacked, followed by the PAGE_IN address and the ROM  address
to be called.

0067 CAL_SPEC2 LD   D,(HL)            Take higher part of DEFW.
               INC  HL                Move to the return address
               EX   (SP),HL           Stack return address.
               EX   DE,HL             Pass the ROM address to HL
               PUSH HL                and stack it too.
               LD   HL,+1708          Exchange with PAGE_IN.
               EX   (SP),HL
               PUSH IY
               EX   (SP),HL           Exchange again.
               LD   IY,+5C3A          Restore IY.
               JP   1748,PAGE_OUT     Call the required  routine
                                      indirectly.


THE 'ENTRY_2' ROUTINE
The HL register-pair points to the address of the error-code  or
hook-code (possibly in the Spectrum ROM). It must be fetched and
the hook-code table is searched. Finally a jump if made  to  the
correct routine.

007A ENTRY_2   RST  0010,CAL_SPEC     Fetch err-nr or hook-code.
               DEFW +007B,TEMP_PTR2+3
               INC  HL                HL := returnaddress.
               EX   (SP),HL           Stack HL; HL := AF.
               PUSH HL                Stack AF.
               PUSH DE                Store DE temporarely.
               LD   B,+FF             Signal: no subtables.
               RST  0030,LOOKUP       Lookup the startaddress in
               DEFB +00               the hook-code table.
               POP  DE
               POP  BC                BC := AF.
0087 CALL_JP   JP   (HL)              Jump to the routine.


THE 'VERSION' HOOKCODE
The version number of this Discovery ROM is put on the  calcula-
tor stack.

0088 VERSION   RST  0028,FP_CALC      Call the calculator.
               DEFB +09               (lengthbyte)
               DEFB +34,stk-data      2.2
               DEFB +F2, exponent +82
               DEFB +0C, +CC, +CC, +CD
               DEFB +38,end-calc      2.2
               JR   009B,RET_SPEC1    Jump to page out.


THE 'LENGTH' HOOKCODE
The length of the current channel is calculated. The same as USR
432d.

0093 LENGTH_1  LD   IX,(CURCHL)       Fetch the startaddress.
               CALL 03D9,LEN_CHAN     Calculate the length.
               POP  HL                Unstack unwanted addresses
009B RET_SPEC1 POP  HL
               JR   00CB,RET_SPEC2    Return to Spectrum ROM.


THE 'INITIALISE RAM' HOOKCODE
The IC 6116 is initialised. The same as USR 14070d.

009E INIT_RAM  DI                     No interrupts allowed.
               CALL 1554,INIT_RAM2    Initialise IC 6116.
               EI
               JR   00CB,RET_SPEC2    Return to Spectrum ROM.


THE 'CHARACTER I/O' HOOKCODE
The A register is used for input or output.  DE  points  to  the
appropriate address in the channel descriptor, so the offset  is
0 for output and 2 for input. The offset is used as an index  in
the subtable for that channel, and a jump is made to the correct
routine.

00A5 CHAR_IO   RES  3,(TV-FLAG)       Signal: mode not changed.
               PUSH BC                BC contains AF.
               LD   HL,(CURCHL)       HL := current channel.
               PUSH HL                Copy HL to IX.
               POP  IX
               LD   A,(IX+04)         A := channel name.
               OR   +20               Force to lower case.
               EX   DE,HL             Switch pointers.
               SCF
               SBC  HL,DE             HL := HL - DE - 1.
               LD   B,L               Output if B=0, input if 2.
               RST  0030,LOOKUP       Lookup the startaddress in
               DEFB +04               channel-info table.
               POP  AF                Restore AF.
               CALL 0087,CALL_JP      Call the current routine.


THE 'INPUT EXTRA CHARACTERS' ROUTINE
If the CHAR_IO routine has been called  by  the  INPUT  command,
then continue to read or write the rest of the  string  until  a
 is encountered. Else, the routine was used  by  the  INKEY$
#stream command and no extra characters may be read.

00C0 INPUT_EX  POP  HL                Unstack unwanted addresses
               POP  HL
               EXX
               PUSH AF
               BIT  5,(FLAG-X)        INPUT or INKEY$ mode?
               JR   NZ,00CE,INPUT_EX2 INPUT? Then continue.
               POP  AF
00CB RET_SPEC2 JP   1748,PAGE_OUT     Jump to Spectrum ROM.
00CE INPUT_EX2 POP  AF                Read or write successfull?
               JR   NC,00CB,RET_SPEC2 Exit if not.
               CP   +0D               Is it a ?
               JR   Z,00DD,INPUT_CR   Step if so.
               RST  0010,CAL_SPEC     Call ADD_CHAR  to  process
               DEFW +0F85,ADD_CHAR    this character.
               CALL 0623,NEAT_RET     Jump to ED_LOOP,  enter  a
               DEFW +0F38,ED_LOOP     loop.
00DD INPUT_CR  CALL 0623,NEAT_RET     Jump to  ED_ENTER,  finish
               DEFW 1025,ED_ENTER     the loop.


THE 'MDRIVE' HOOKCODE
If the microdrive system variables are not present already, then
they are created. However, no extra channels may be created.

00E2 M_DRIVE   LD   HL,(CHANS)        HL := start of channels.
               LD   DE,+5CB6          DE := start of MD maps.
               AND  A                 Test  if  the   microdrive
               SBC  HL,DE             system variables are  pre-
               JR   NZ,009B,RET_SPEC1 sent. Exit if so.
00ED M_DRIVE2  EX   DE,HL             HL := start of MD maps - 1
               DEC  HL
               LD   BC,+003A          Reserve 58 bytes.
               RST  0010,CAL_SPEC     Make the room.
               DEFW +1655,MAKE_ROOM
               JR   009B,RET_SPEC1    Jump to Spectrum ROM.


THE 'HOOKCODE ERROR' ROUTINE
If it is not a Spectrum error, then report i is given.

00F7 HOOKERROR CP   +1B               Is it not a Spectrum error
               JR   C,00FD,HOOKERR2   then skip.
00FB REPORT_i  RST  0008,SH_ERROR     Report 'Hookcode error'.
               DEFB +31
00FD HOOKERR2  LD   (ERR-NR),A        Store the error-code.


THE 'BASIC ERROR' HOOKCODE
If it is an error generated by an extended command,  then  clean
up the memory and the stack and jump to the correct  error-hand-
ling routine.

0100 BASIC_ERR LD   HL,(CH-ADD)       Store the address  of  the
               LD   (X-PTR),HL        current character.
               BIT  7,(ERR-NR)        Did an error occur?
               LD   (ERR-NR),A        (Store the error-code)
               JR   Z,016D,CONTSIG_2  then continue signalling.
               BIT  7,(SUBPPC)        Statementnr > 127?
               JR   NZ,016D,CONTSIG_2 then signal the error.
0115 BAS_ERR2  LD   DE,(WORKSP)       DE := end of E-LINE + 1
               AND  A
               SBC  HL,DE             Current character past end
               ADD  HL,DE             of the line?
               JR   NC,016D,CONTSIG_2 then signal the error.
               LD   DE,(E-LINE)
               AND  A
               SBC  HL,DE             Current character in edit-
               JR   C,0131,BAS_ERR4   buffer? step if not.
0128 BAS_ERR3  RST  0010,CAL_SPEC     Get the line-number in the
               DEFW +19FB,E_LINE_NO   edit-buffer.
               LD   HL,(CH-ADD)       Prepare HL and step.
               DEC  HL
               JR   013E,BAS_ERR5
0131 BAS_ERR4  LD   HL,(PPC)          Fetch current line-number.
               BIT  7,H               Give an error if past  the
               JR   NZ,016D,CONTSIG_2 program area.
               RST  0010,CAL_SPEC     Get  the  startaddress  of
               DEFW +196E,LINE_ADDR   this BASIC line.
               INC  HL                Make  HL  point  one  byte
               INC  HL                before the first character
               INC  HL                of the line.
013E BAS_ERR5  LD   D,(SUBPPC)        Find the  startaddress  of
               LD   E,+00             the current statement.
               RST  0010,CAL_SPEC
               DEFW +198B,EACH_STMT
               RST  0018,TEST_RUN     Running or checking syntax
               JR   NZ,014C,BAS_ERR6  Step if running.
               RST  0010,CAL_SPEC     Remove the hidden floating
               DEFW +11A7,REMOVE_FP   point forms in the line.
014C BAS_ERR6  RST  0010,CAL_SPEC     Clear the working areas.
               DEFW +16BF,SET_WORK
               RST  0020,NEXT_CHAR    Fetch the keyword in A.
               LD   B,+FF             Signal: no subtables.
               RST  0030,LOOKUP       Lookup the startaddress of
               DEFB +02               the service routine in the
                                      keyword table.
               PUSH HL                Store it  while  preparing
               RST  0020,NEXT_CHAR    CH-ADD to point to the 1st
               POP  HL                parameter.
               CALL 0087,CALL_JP      Call the service routine.

Open the 'S' channel and continue with the next statement.

015A OPEN_'S'  LD   A,+FE             Open the 'S' channel.
               RST  0010,CAL_SPEC
               DEFW +1601,CHAN_OPEN
               LD   (ERR-NR),+FF      Reset ERR-NR.
               CALL 0623,NEAT_RET     Return to the Spectrum ROM
               DEFW +1B76,STMT_RET    continue the program.


THE 'CONTINUE SIGNALLING OF AN ERROR' ROUTINE
All errors can be signalled. The error-code is fetched and set.

0168 CONTSIG   POP  HL                HL points  to  the  error-
               LD   A,(HL)            code. Fetch it  and  store
               LD   (ERR-NR),A        it.


THE 'CONTSIG_2' ROUTINE
Enter  here  when  coming  from  the  service  routines  of  the
different commands. On entry ERR-NR holds the code of the  error
to be reported. When an error occurs, a direct jump is  made  to
this address, so no error-code has to be  fetched.  A  check  is
made whether it was an edit-error or a runtime-error. If it  was
an edit-error then a jump is made to the address that is pointed
to by ERR-SP. A third check is  made  for  an  error-routine  at
address 5B1D, that is only present in 128K mode. This way  BASIC
extensions such as Beta-Basic should still be compatible.

016D CONTSIG_2 RES  3,(TVFLAG)        Signal: mode not changed.
               RST  0010,CAL_SPEC     Clear the calculator stack
               DEFW +16C5,SET_STK
0174 CONTSIG_3 LD   SP,(ERR-SP)       Restore the stackpointer.
               POP  DE                Fetch the error-address.
               LD   HL,+107F          This is ED_ERROR.
               AND  A                 Test if it  was  an  edit-
               SBC  HL,DE             error.
               JR   NZ,0187,CONTSIG_4 Skip if not.
               POP  HL                Fetch the error-address.
               LD   (ERR-SP),HL       Prepare  to   check   this
               JR   0174,CONTSIG_3    address and loop back.
0187 CONTSIG_4 PUSH DE                Restack the error-address.
               LD   HL,+1303          This is MAIN_4.
               AND  A                 Test if it was a  runtime-
               SBC  HL,DE             error.
               JR   Z,019E,CONTSIG_5  Jump if so.
               BIT  4,(FLAGS)         Test if in 128K mode.
               JR   Z,01BF,CONTSIG_6  Step out if not.
               LD   HL,+5B1D          Test for an  error-routine
               AND  A                 from this address.
               SBC  HL,DE
               JR   NZ,01BF,CONTSIG_6 Step out if not.
019E CONTSIG_5 LD   A,(ERR-NR)        Fetch the error-code.
               PUSH AF                Store it temporarely.
               CP   +57               Was it 'RAM corrupt'?
               CALL NZ,05FB,CL_T_CHNS Tidy the chan  area  first
               POP  AF                if not. Restore error-code
               INC  A                 Is it a Spectrum error?
               CP   +1C
               JR   C,01BF,CONTSIG_6  Step out if so.
               CALL 062F,CD_TO_STK    Copy code to stack:
               DEFW +1303,MAIN_4      print error-number.
               DEFW +0041             (length)
               CALL 0656,EXEC_STK     Execute the code.
               DEC  A                 A = error-code.
               CALL 05EB,P_ERR_MSG    Print the error-message.
               LD   HL,+1349          Prepare the  returnaddress
               EX   (SP),HL           print  comma,  line-   and
                                      statement number.
01BF CONTSIG_6 JP   1748,PAGE_OUT     Return to the Spectrum ROM


THE 'AUTO_RUN' HOOKCODE
If there is no BASIC program in memory AND the only character in
the edit-buffer is 'RUN' then the  file  'run'  is  loaded  from
drive 1.

01C2 AUTO_RUN  LD   HL,(CHANS)        Standard channels take  21
               LD   BC,+0019          bytes and  an  edit-buffer
               ADD  HL,BC             with 1 command 3 bytes.
               LD   BC,(WORKSP)       Test if this configuration
               SBC  HL,BC             is true.
               JR   NZ,016D,CONTSIG_2 Otherwise report  'Program
                                      finished'.
01D1 AUTO_RUN2 DEC  BC                BC := endmarker.
               DEC  BC                BC := .
               DEC  BC                BC := 'RUN'.
               LD   A,(BC)            Get command-code.
               CP   +F7               Was it 'RUN'?
               JR   NZ,016D,CONTSIG_2 Exit if not.
01D9 AUTO_RUN3 LD   HL,(E-LINE)       HL := start of edit-buffer
               PUSH HL                Stack HL.
               LD   BC,+0009          Reserve 9 extra bytes.
               PUSH BC                Stack this number too.
               RST  0010,CAL_SPEC     Make the required room  in
               DEFW +1655,MAKE_ROOM   the edit-buffer.
               POP  BC                Restore 9.
               INC  BC                Include the byte 'RUN'.
               POP  DE                DE := start of edit-buffer
               LD   HL,+01F6          Start of the LOAD command.
               LDIR                   Move it.
               DEC  (NS-PPC)          Correct command pointer.
               LD   (T-ADDR),+01      Signal 'LOAD'.
01F3 REP_NONS  RST  0010,CAL_SPEC     Force Spectrum ROM to  re-
               DEFW +1C8A,REPORT_C    port 'Nonsense in BASIC'.
01F6 LOAD_MSG  DEFB +EF, +2A, +BC     LOAD *SGN PI;"run"
               DEFB +A7, +3B, +22
               DEFB +72, +75, +6E
               DEFB +22


THE 'OPEN #' COMMAND ROUTINE
On entry (CH-ADD)  points  to  the  first  character  after  the
'OPEN #' token.

0200 OPEN_#    RST  0010,CAL_SPEC     Get a numerical value from
               DEFW +1C82,EXPT_1NUM   the BASIC line.
               CALL 0525,TEST_SEP     Seperator after it?
               JP   NZ,01F3,REP_NONS  Report the error if not.
               CALL 053A,CHK_CHAN     Find a channel + next char
               CP   +BF               Is it 'IN'?
               JR   NZ,0215,#_NOT_IN  Step if not.

'IN' takes no parameters.

0210 #_IN      RST  0020,NEXT_CHAR    Get the next character.
               LD   B,+02             Signal: input.
               JR   0258,#_CONT_1     Step forward.
0215 #_NOT_IN  CP   +DF               Is it 'OUT'?
               LD   B,+05             Signal: output, create.
               JR   Z,0221,#_OUT_EXP  Step if so.
               CP   +89               Is it 'EXP'?
               JR   NZ,022D,#_NOT_EXP Step if not.
               LD   B,+01             Signal: output.

'OUT'  and  'EXP'  can  take  one  numerical  parameter.  If  no
parameter follows, the default 0 is assumed.

0221 #_OUT_EXP RST  0020,NEXT_CHAR    Get the next character.
               RST  0010,CAL_SPEC     End of statement reached?
               DEFW +2048,PR_ST_END   (so, no parameter?)
               JR   Z,0258,#_CONT_1   Step if so.
               PUSH BC                Store OPEN parameters.
               RST  0010,CAL_SPEC     Get a numerical value.
               DEFW +1C82,EXPT_1NUM
               JR   025C,#_CONT_2     Step forward.
022D #_NOT_EXP CP   +A5               Is it 'RND'?
               JR   NZ,0256,#_NO_PAR  Step if not.

If 'RND' takes only parameter,  then  the  file  should  already
exist, else it should be created.

0231 #_RND_1   RST  0020,NEXT_CHAR    Get the next character.
               RST  0010,CAL_SPEC     Get the (first) parameter.
               DEFW +1C82,EXPT_1NUM
               CALL 0525,TEST_SEP     Another following?
               JR   Z,0241,#_RND_2    Then step.
               CALL 0532,USE_M1       Use -1 as second parameter
               LD   +03               Signal: input, output.
               JR   0246,#_RND_3      Step forward.
0241 #_RND_2   RST  0010,CAL_SPEC     Get the second parameter.
               DEFW +1C82,EXPT_1NUM
               LD   B,+07             Signal: in, out, create.
0246 #_RND_3   PUSH BC                Store OPEN parameters.
               CALL 050E,CHK_END      Test for end of statement.
               RST  0028,FP_CALC      stream, P1, P2
               DEFB +07               (lengthbyte)
               DEFB +01,exchange      stream, P2, P1
               DEFB +C0,st-mem-0      (mem-0 holds P1)
               DEFB +04,multiply      stream, P2*P1  (filesize)
               DEFB +E0,get-mem-0     stream, filesize, P1
               DEFB +38,end-calc      stream, filesize, P1
               RST  0010,CAL_SPEC     BC := P1 (record-length)
               DEFW +1E99,FIND_INT2
               JR   0262,#_CONT_3     Step forward.
0256 #_NO_PAR  LD   B,+00             Signal: no I/O, no create.
0258 #_CONT_1  PUSH BC                Store OPEN parameters.
               CALL 0532,USE_M1       Use -1 as default.
025C #_CONT_2  CALL 050E,CHK_END      Test for end of statement.
               LD   BC,+0000          BC := record-length

BC contains the record-length (0 if not specified), the calcula-
tor stack holds the  stream  number  (at  the  bottom)  and  the
required filesize at the top (-1 for 'IN').

0262 #_CONT_3  PUSH BC                Store the record-length.
               RST  0028,FP_CALC      stream, filesize
               DEFB +05               (lengthbyte)
               DEFB +C0,st-mem-0      (mem-0 holds the filesize)
               DEFB +02,delete        stream
               DEFB +38,end-calc      stream
               CALL 062F,CD_TO_STK    Copy code to stack:
               DEFW +1736,OPEN        OPEN # command.
               DEFW +0021             (length)
               DEC  HL                point to relative  address
               DEC  HL                1754.
               LD   (HL),+C9          Put a RET in place.
               CALL 0656,EXEC_STK     Execute the code.
               JR   Z,027A,#_CONT_4   If the stream  was already
               RST  0008,SH_ERROR     open and not K,  S  or  P,
               DEFB +2A               then report the error.
027A #_CONT_4  PUSH HL                Store  channel-address  in
                                      the stream-info table.
               RST  0028,FP_CALC      -
               DEFB +04               (lengthbyte)
               DEFB +E0,get-mem-0     filesize
               DEFB +38,end-calc      filesize
               POP  DE                DE := address  in  stream-
               POP  HL                info table, HL :=  record-
               POP  AF                length, AF := OPEN flags.
               PUSH DE                Store DE again.
               CALL 06A9,OPEN_CHAN    Now open the channel.
               POP  HL
               LD   BC,+5C18          BC := start of streams.
               AND  A                 Is HL an internal channel?
               SBC  HL,BC
               ADD  HL,BC
               JR   C,0294,#_CONT_5   Then skip.
               RES  5,(IX+04)         Make channel permanent.
0294 #_CONT_5  EX   DE,HL             DE := address  in  stream-
                                      info table
               CALL 080C,GETOFFSET    HL := offset of channel.
               EX   DE,HL             Swith pointers again.
               LD   (HL),E            Store the  offset  in  the
               INC  HL                stream-info table.
               LD   (HL),D
               RET                    Finished.


THE 'LOAD, SAVE, VERIFY AND MERGE' COMMAND ROUTINES
The A register holds the  first  character  after  the  keyword,
(T-ADDR) holds 0 for SAVE, 1 for LOAD, 2 for VERIFY  and  3  for
MERGE. The length of the file is the length of  the  data  +  7,
since the first 7 bytes are used to store header apart from  the
filename. In case of SAVE, the  channel  should  be  opened  for
output and create, in  all  other  cases  just  for  input.  The
routine uses large parts of the  tape-routine  in  the  Spectrum
ROM, so sufficient room for the stack is needed.

029D LD_SA_ETC CP   +2A               Is it '*'?
               JP   NZ,016D,CONTSIG_2 Signal the error if not.
               RST  0018,TEST_RUN     Run-time or checking?
               JR   Z,02AB,LD_SA_2    Skip if checking.
               LD   BC,+0022          Make 34 bytes room, for  2
               RST  0010,CAL_SPEC     headers.
               DEFW +0030,BC_SPACES
02AB LD_SA_2   PUSH IX                Store 128K register.
               PUSH DE                Store start of the room.
               RST  0020,NEXT_CHAR    Point to the next char.
               CALL 053A,CHK_CHAN     Find a channel + next char
               CALL 062F,CD_TO_STK    Copy code to stack:
               DEFW +0652,SA_DATA     test parameters.
               DEFW +0109             (length)
               LD   IX,+0071          IX := start of code + 113.
               ADD  IX,DE
               LD   (IX-26),+C9       Put  a  RET  at   relative
                                      address 069D.
               LD   A,+C3             1FC3 = UNSTACK_Z.
               CALL 0319,STOREADDR    Store  this   address   in
               INC  IX                stead of 1BEE  (CHECK_END)
               LD   A,+1F             in order to keep the stack
               CALL 0319,STOREADDR    also when checking syntax.
               POP  IX                IX := start of headers.
               CALL 0656,EXEC_STK     Execute the code.
02D4 LD_SA_3   EX   (SP),IX           Stack header-start,  fetch
               PUSH HL                128K register.
               CALL 050E,CHK_END      Store start for LOAD, SAVE
               POP  HL                etc while testing for  the
                                      end of the statement.
               LD   (K-CUR),HL        Store HL, since  K-CUR  is
               POP  IX                also moved when creating a
               PUSH HL                channel.
               LD   B,(IX+0C)         BC := length of datablock.
               LD   C,(IX+0B)
               RST  0010,CAL_SPEC     Stack BC.
               DEFW +2D2B,STACK_BC
               RST  0028,FP_CALC      len data
               DEFB +09               (lengthbyte)
               DEFB +34,stk-data      len data,7 (len file-info)
               DEFB +40
               DEFB +B0, exponent +00
               DEFB +00, +07, (+00, +00)
               DEFB +0F,addition      len data + len file-info
               DEFB +38,end-calc      file-size
               LD   A,(T-ADDR)        Fetch the command type.
               AND  A                 Was it 'SAVE'?
               PUSH AF                (store the command type)
               LD   A,+05             Signal: output, create.
               JR   Z,02FE,LD_SA_4    Step if 'SAVE'.
               LD   A,+02             Signal: input.
02FE LD_SA_4   LD   HL,+0000          HL := record-length.
               CALL 06A9,OPEN_CHAN    Now open the channel.
               POP  AF                Restore command type.
               POP  DE                DE := start of data.
               LD   HL,(STK-END)      Has the startaddress  been
               SBC  HL,DE             shifted due to  the  crea-
               LD   HL,(WORKSP)       tion of a channel?
               EX   DE,HL             Then use the  saved  value
               JR   C,0314,LD_SA_5    instead.
               LD   HL,(K-CUR)
0314 LD_SA_5   CALL 06F4,SAVE_CHAN    Now execute the command.
               JR   0368,CLOSE_CHN    And close the channel.

Store the address of UNSTACK_Z rather than  CHECK_END  into  the
correct locations in the stackcode, otherwise the stack will  be
severely corrupted when checking  syntax,  which  will  cause  a
system crash. The location are (relative) 069A, 06AE, 06FA, 0716
and 072F.

0319 STOREADDR LD   (IX-29),A
               LD   (IX-15),A
               LD   (IX+37),A
               LD   (IX+58),A
               LD   (IX+6C),A
               RET


THE 'MOVE' COMMAND ROUTINE
The first channel is checked and  opened  for  input,  then  the
second channel is checked and opened for output and create. Then
the input-channel is moved to the output-channel, until the  end
of the input-file has been reached. Finally the two channels are
closed.  If  an  error  occurs  in  checking  or  opening    the
output-channel, then the input-channel has already been created.
Eg. MOVE 1;"name" TO "x"

0329 MOVE      CALL 053A,CHK_CHAN     Find a channel + next char
               CP   +CC               Is it 'TO'?
               JP   NZ,01F3,REP_NONS  Report the error if not.
               RST  0018,TEST_RUN     Run-time or checking?
               JR   Z,0341,MOVE_2     Skip if checking.
               CALL 0532,USE_M1       Use -1.
               LD   +02               Signal: input.
               LD   HL,+0000          HL := record-length.
               CALL 06A9,OPEN_CHAN    Open the channel.
               PUSH IX                Store input-channel.
0341 MOVE_2    RST  0020,NEXT_CHAR    Get the next character.
               CALL 053A,CHK_CHAN     Find a channel + next char
               CALL 050E,CHK_END      test for end of statement.
               POP  IX                IX := start of input-chan
               PUSH IX
               CALL 03D9,LEN_CHAN     Fetch channel-length.
               LD   A,+05             Signal: output, create.
               LD   HL,+0000          HL := record-length.
               CALL 06A9,OPEN_CHAN    Open the channel.
               RST  0010,CAL_SPEC     Clear  workspace  and  the
               DEFW +16BF,SET_WORK    calculator stack.
               POP  HL                HL := start of input-chan
                                      IX := start of output-chan
               CALL 06BF,MOVE_CHAN    Move the channel.
               LD   (K-CUR),HL        Store start of input-chan
               CALL 0368,CLOSE_CHN    Close the output-channel.
               LD   IX,(K-CUR)        Prepare to close the input
                                      channel.


THE 'CLOSE A CHANNEL' ROUTINE
On entry the IX registerpair holds the start of the channel.  If
a change is made to the buffer then it is written  to  disk.  No
check is made whether the file is still on the drive or an other
disk has been inserted.

0368 CLOSE_CHN LD   B,+06             Signal: close.
               CALL 03DB,CALL_CHAN    Close the channel.


THE 'CLEAR A CHANNEL' ROUTINE
On entry the IX registerpair holds the start of the channel. The
channel is removed from memory, the space is reclaimed  and  the
relevant pointers are adjusted. The buffer  is  NOT  written  to
disk.

036D CLEAR_CHN CALL 080C,GETOFFSET    HL := offset of channel.
               LD   BC +0014          The internal channels  (K,
               AND  A                 S, R and P) take 20 bytes.
               SBC  HL,BC             Is HL an internal channel?
               RET  C                 Finished if it is.
               ADD  HL,BC             Restore HL.
               PUSH HL                Store the offset.
               PUSH IX                Pass  the  start  of   the
               POP  HL                Channel to HL.
               LD   C,(IX+05)         Fetch channel-length in BC
               LD   B,(IX+06)
               PUSH BC                Keep a copy on the stack.
               RST  0010,CAL_SPEC     Reclaim the space.
               DEFW +19E8,RECLAIM_2
               CALL 062F,CD_TO_STK    Copy code to stack:
               DEFW +1663,POINTERS-1  Adjust pointers (start-1!)
               DEFW +0023             (length)
               LD   H,D               Change the first  two  in-
               LD   L,E               structions in the code.
               LD   (HL),+42          LD   B,D
               INC  HL
               LD   (HL),+48          LD   C,E
               INC  HL
               INC  HL
               INC  HL
               LD   (HL),+16          Start at 5C16,  the  start
               INC  HL                of the stream-info table.
               INC  HL
               INC  HL
               LD   (HL),+10          And consider all 16 chans.
               POP  BC                BC := channel-length.
               LD   A,B               BC := - BC.
               CPL
               LD   B,A
               LD   A,C
               CPL
               LD   C,A
               INC  BC
               POP  HL                Restore the offset.
               JP   0656,EXEC_STK     Execute the code and exit.


THE 'POINT' COMMAND ROUTINE
On entry the A register holds  the  first  character  after  the
keyword. The parameters are passed to the calculator stack and a
jump is made to the required routine.

03AA POINT     CP   +23               Is it a '#'?
               JP   NZ,01F3,REP_NONS  Report the error if not.
               RST  0020,NEXT_CHAR    Get the next character.
               RST  0010,CAL_SPEC     Read the stream number.
               DEFW +1C82,EXPT_1NUM
               CALL 0525,TEST_SEP     Test for a separator.
               JP   NZ,01F3,REP_NONS  Report the error if absent
               RST  0010,CAL_SPEC     Read the position.
               DEFW +1C82,EXPT_1NUM
               CALL 050E,CHK_END      Test for end of statement.
               RST  0028,FP_CALC      stream, position
               DEFB +04               (lengthbyte)
               DEFB +01,exchange      position, stream
               DEFB +38,end-calc      position, stream
               RST  0010,CAL_SPEC     Fetch the  stream-data  in
               DEFW +171E,STR_DATA    the BC registerpair.
               LD   A,B               Stream not initialized?
               OR   C
               JR   NZ,03CD,POINT_2   Skip if initialised.
03CA REPORT_O  RST  0010,CAL_SPEC     Report 'Invalid stream'.
               DEFW +160E,REPORT_O
03CD POINT_2   LD   IX,(CHANS)        IX := start of channels.
               ADD  IX,BC             Add the offset.
               DEC  IX                Form the startaddress.
03D5 POS_CHAN  LD   B,+0A             Signal: position
               JR   03DB,CALL_CHAN    And skip.
03D9 LEN_CHAN  LD   B,+08             Signal: length


THE 'CALL A CHANNEL' ROUTINE
On entry the B register contains 0 for save, 2 for load,  4  for
open, 6 for close, 8 for length and 10 for position. A  contains
the OPEN flag. Table 4 is searched with  the  channel-identifier
in A and a jump is made to the apropriate routine.

03DB CALL_CHAN PUSH AF                Store the OPEN flags.
               LD   A,(IX+04)         Get the channel-name.
               OR   +20               Force to lower case.
               RST  0030,LOOKUP       Lookup the startaddress in
               DEFB +04               the channel-info table.
               POP  AF                Restore the OPEN flags.
               JP   (HL)              Jump to the routine.


THE 'CLEAR' COMMAND ROUTINE
On entry the A register holds  the  first  character  after  the
keyword. A test is made for 'CLEAR #', 'CLEAR DATA'  and  'CLEAR
LINE'.

03E5 CLEAR     CP   +CA               Is it 'LINE'?
               JR   NZ,0427,CL_N_LINE Step if not.

The command 'CLEAR LINE , ' is  to  be  used  to
remove all BASIC lines from  to  inclusive.

03E9 CLEAR_LN  RST  0010,CAL_SPEC     Pass the line  numbers  to
               DEFW +1C79,NEXT_2NUM   the calculator stack.
               CALL 050E,CHK_END      Test for end of statement.
               CALL 04A7,LINE_STRT    HL := last line address.
               JR   NZ,03F8,CLEAR_LN2 Skip if in program area.
               RST  0010,CAL_SPEC     Otherwise  point  to   the
               DEFW +19B8,NEXT_ONE    variables area.
               EX   DE,HL
03F8 CLEAR_LN2 PUSH HL                Store last line address.
               CALL 04A7,LINE_STRT    HL := first line address.
               POP  BC                BC := last line address.
               SBC  HL,BC             Last line before first? Or
               RET  NC                both equal? Then exit.
               ADD  HL,BC             Restore HL.
               EX   DE,HL             DE := first line address.
               LD   HL,(CH-ADD)       Test if DE <= CH-ADD <= BC
               CALL 04B6,CHK_LINE
               JR   C,0413,CLEAR_LN3  Skip if not.
               LD   H,D               The current line should be
               LD   L,E               removed too. Make the com-
               LD   (NXTLIN),HL       mand pointer point to  the
               DEC  HL                first command in the first
               LD   (CH-ADD),HL       line after the block.
0413 CLEAR_LN4 LD   HL,(DATADD)       Test if DE <= DATADD <= BC
               CALL 04B6,CHK_LINE
               JR   C,0421,CLEAR_LN5  Skip if not.
               DEC  DE                Reinitialize  it  to   the
               LD   (DATADD),DE       first line after the block
               INC  DE                (Restore DE)
0421 CLEAR_LN5 LD   H,B               HL := last line address.
               LD   L,C
               RST  0010,CAL_SPEC     Delete all line from DE to
               DEFW +19E5,RECLAIM_1   HL inclusive.
               RET                    Finished.
0427 CL_N_LINE CP   +E4               Is it 'DATA'?
               JR   NZ,045D,CL_N_DATA Step if not.

The command 'CLEAR DATA ' removes the BASIC  variable.
 may be either a simple variable or an array, in which
case the name should have () after it,  eg.  'CLEAR  DATA  A()'.
These commands are particularly useful when overlaying parts  of
BASIC programs from disk or ram disk.

042B CLEAR_DT  RST  0020,NEXT_CHAR    Get the next character.
               RST  0010,CAL_SPEC     Find the  startaddress  of
               DEFW +28B2,LOOK_VARS   variable in the VARS area.
               PUSH HL                Store end of var-name.
               LD   A,C               Fetch discriminator in A.
               RLA                    Now toggle bit  5  without
               AND  +C1               disturbing the carry  flag
               XOR  +40               (set if  found,  reset  if
               SRL  A                 not).
               PUSH AF                Store the result.

Bit 5 and 6 determine the var-type.
00 = numerical variable with name-length > 1.
10 = simple numerical/string variable or FOR/NEXT variable.
11 = string array
01 = numerical array

               JR   Z,0449,CLEAR_DT3  Jump if var-name > 1 char.
043B CLEAR_DT2 RST  0010,CAL_SPEC     Fetch  current   character
               DEFW +0018,GET_CHAR    again.
               CP   +28               Is it a '('? (ie. array?)
               JR   NZ,0449,CLEAR_DT3 Step if not.
               RST  0020,NEXT_CHAR    But if it is,  then  check
               CP   +29               that the next character is
                                      the finishing ')'.
               JP   NZ,01F3,REP_NONS  Report the error if not.
               RST  0020,NEXT_CHAR    Point to next character.
0449 CLEAR_DT3 CALL 050E,CHK_END      Test for end of statement.
               POP  AF                Restore discriminator.
               POP  HL                Restore pointer to var.
               RET  C                 Exit if var. doesn't exist
               JR   NZ,0456,CLEAR_DT5 Step if var-name = 1 char.
0451 CLEAR_DT4 DEC  HL                Find the  first  character
               BIT  7,(HL)            of the var-name.
               JR   Z,0451,CLEAR_DT4
0456 CLEAR_DT5 RST  0010,CAL_SPEC     Point to the next variable
               DEFW +19B8,NEXT_ONE    in the variables area.
               RST  0010,CAL_SPEC     And  erase  the  requested
               DEFW +19E8,RECLAIM_2   variable.
               RET                    Finished.
045D CL_N_DATA CP   +23               Is it '#'?
               JP   NZ,016D,CONTSIG_2 Report the error if not.

The command 'CLEAR #' is used to clear the channel  that
is attached to  .  If    is  omitted,  then  all
channels are cleared.

0462 CLEAR_#   RST  0020,NEXT_CHAR    Get the next character.
               RST  0010,CAL_SPEC     At the end of  the  state-
               DEFW +2048,PR_ST_END   ment (ie. no ?)
               JR   Z,0483,CLEAR_ALL  Then clear all channels.
               RST  0010,CAL_SPEC     Expect a number: .
               DEFW +1C82,EXPT_1NUM
               CALL 050E,CHK_END      Test for end of statement.
               RST  0010,CAL_SPEC     BC := offset in streamdata
               DEFW +171E,STR_DATA
               LD   A,B               Channel closed already?
               OR   C
               RET  Z                 Finished if so.
               LD   IX,(CHANS)        IX := startaddress of  the
               ADD  IX,BC             channel.
               DEC  IX
               PUSH HL                Store  start  in   stream-
               CALL 036D,CLEAR_CHN    table and clear the  chan-
               JP   1730,OPEN_S       nel. Open the 'S' channel.
0483 CLEAR_ALL CALL 050E,CHK_END      Test for end of statement.


THE 'WIPE CHANNELS FROM MEMORY' ROUTINE
All the channels are cleared,  but  the  internal  channels  are
reinitialized again.

0486 WIPE_CHAN LD   HL,(CHANS)        Point  past  the  internal
               LD   DE,+0014          channels.
               ADD  HL,DE
               EX   DE,HL             DE := start of channels.
               LD   HL,(PROG)         HL := end of channels.
               DEC  HL
               RST  0010,CAL_SPEC     Reclaim the space between.
               DEFW +19E5,RECLAIM_1
               CALL 062F,CD_TO_STK    Copy code to stack:
               DEFW +127C             Initalise internal chans.
               DEFW +000C             (length)
               CALL 0656,EXEC_STK     Execute the code.
               XOR  A                 Clear streams 4-15.
               LD   B,+18             12 streams (so 24 bytes)
04A2 WIPE_CHN2 LD   (DE),A
               INC  DE
               DJNZ 04A2,WIPE_CHN2
               RET


THE 'GET LINE STARTADDRESS' SUBROUTINE
This subroutine is used to find the startaddress of the line  of
which the line-number is on the calculator stack.

04A7 LINE_STRT RST  0010,CAL_SPEC     Fetch the line-number into
               DEFW +1E99,FIND_INT2   the BC registerpair.
               LD   A,B               Jump if it is over 16,384.
               CP   +40
               JP   NC,0983,REPORT_B
               LD   H,B               HL := line-number.
               LD   L,C
               RST  0010,CAL_SPEC     Find the  startaddress  of
               DEFW +196E,LINE_ADDR   the  line  (or  the  first
               RET                    line  after  if  the  line
                                      doesn't exist).


THE 'CHECK LINE ADDRESS' SUBROUTINE
On entry the HL registerpair holds the line startaddress, DE the
lower edge to be tested and BC the higher edge.  The  subroutine
returns carry reset if DE <= HL <= BC.

04B6 CHK_LINE  AND  A                 Return the  carry  set  if
               SBC  HL,DE             HL < DE.
               RET  C
               ADD  HL,DE             (correct HL)
               SBC  HL,BC             Return the  carry  set  if
               CCF                    HL > BC. Carry reset means
               RET                    DE <= HL <= BC.


THE 'CAT' COMMAND ROUTINE
The requested channel for output is opened and a jump is made to
the CALL_UTIL routine.

04BF CAT       RST  0018,TEST_RUN     Run-time or checking?
               JR   Z,04C9,CAT_2      Skip if checking syntax.
               PUSH AF                Store  current   character
               LD   A,+02             while opening #2.
               RST  0010,CAL_SPEC
               DEFW +1601,CHAN_OPEN
               POP  AF
04C9 CAT_2     RST  0010,CAL_SPEC     Consider   a   '#'
               DEFW +2070,STR_ALTER   after the keyword.
               JR   C,04D4,CAT_3      Skip if no other stream.
               CALL 0525,TEST_SEP     Test for a separator.
               JP   NZ,01F3,REP_NONS  Report the error if absent
04D4 CAT_3     LD   B,+04             Signal: CAT and handle it.
               JR   04DE,PREP_UTIL


THE 'ERASE' COMMAND ROUTINE
A direct jump is made to the CALL_UTIL routine.

04D8 ERASE     LD   B,+00             Signal: ERASE and jump.
               JR   04DE,PREP_UTIL


THE 'FORMAT' COMMAND ROUTNE
Continue with the CALL_UTIL routine.

04DC FORMAT    LD   B,+06             Signal: FORMAT


THE 'PREPARE UTILITY' ROUTINE
A channel is fetched from the BASIC line.

04DE PREP_UTIL PUSH BC                Store command offset.
               CALL 053A,CHK_CHAN     Check the channel.
               CALL 050E,CHK_END      Test for end of statement.
               RST  0010,CAL_SPEC     Fetch the parameters  from
               DEFW +2BF1,STK_FETCH   the calculator stack.
               POP  HL                H := command offset.


THE 'CALL UTILITY' ROUTINE
On entry the H register holds 0 for ERASE, 4 for CAT and  6  for
FORMAT, the DE registerpair points to  the  parameter-block  and
the BC registerpair holds the length of that block. Table  6  is
searched for the handling routine.

04E9 CALL_UTIL PUSH DE                Store the start and length
               PUSH BC                of the parameter-block.
               LD   A,(DE)            A := channel-name.
               LD   B,H               B := command offset.
               RST  0030,LOOKUP       Lookup the startaddress in
               DEFB +06               the channel_table_2 table.
               POP  BC                Restore pointers and  make
               POP  DE                a direct jump to the hand-
               JP   (HL)              ling routine.


THE 'CLS #' COMMAND ROUTINE
On entry the A register holds  the  first  character  after  the
keyword. The colour system variables  are  all  reset  to  their
default values and the screen is cleared.

04F2 CLS_#     CP   +23               Is it '#'?
               JP   NZ,016D,CONTSIG_2 Report the error if not.
               RST  0020,NEXT_CHAR    Get the next character.
               CALL 050E,CHK_END      Test for end of statement.
               LD   A,+07             Make the border white.
               RST  0010,CAL_SPEC
               DEFW +229B,BORDER+7
               LD   (ATTR-P),A        Set PAPER 7,  INK,  BRIGHT
               XOR  A                 and FLASH 0.
               LD   (MASK-P),A        No transparant colours.
               LD   (P-FLAG),A        OVER and INVERSE 0.
               RST  0010,CAL_SPEC     Clear the screen.
               DEFW +0D6B,CLS
               RET


THE 'CHECK END OF STATEMENT' SUBROUTINE
The possible characters for end-of-statement are ':'  and  .
When running, a return to the calling routine is made, otherwise
the next statement is considered.

050E CHK_END   RST  0010,CAL_SPEC     Read the character into A.
               DEFW +0018,GET_CHAR
               CP   +3A               Is it ':'?
               JR   Z,051A,CHK_END2   Jump if so.
               CP   +0D               Is it ?
               JP   NZ,01F3,REP_NONS  Report the error if not.
051A CHK_END2  RST  0018,TEST_RUN     Run-time or checking?
               RET  NZ                Return if run-time.
               LD   (ERR-NR),+FF      Signal: no errors yet.
               CALL 0623,NEAT_RET     Consider a next statement.
               DEFW +1BF4,STMT_NEXT


THE 'TEST FOR A SEPERATOR' SUBROUTINE
Possible separators are ',' and ';'. On exit the  zero  flag  is
set if a separator has been found.

0525 TEST_SEP  RST  0010,CAL_SPEC     Read the character into A.
               DEFW +0018,GET_CHAR
               CP   +3B               Is it a ';'?
               JR   Z,052F,TEST_SEP2  Jump if so.
               CP   +2C               Is it a ','?
               RET  NZ                Return zero reset if not.
052F TEST_SEP2 RST  0020,NEXT_CHAR    Get the next character.
               CP   A                 Return zero set.
               RET


THE 'USE MINUS ONE' SUBROUTINE
During run-time, the value -1  is  put  as  last  entry  on  the
calculator stack.

0532 USE_M1    RST  0018,TEST_RUN     Return   immediately    if
               RET  Z                 just checking syntax.
               RST  0028,FP_CALC      Call the calculator.
               DEFB +05               (lengthbyte)
               DEFB +A1,stk-one       1
               DEFB +1B,negate        -1
               DEFB +38,end-calc      -1
               RET


THE 'CHECK THE CHANNEL' ROUTINE
On entry the A register contains  the  first  character  of  the
channel  identifier.  During  run-time,  a  parameter-block   is
created in the workspace. DE holds the  startaddress,  BC  holds
the length and both registerpairs are stored on  the  calculator
stack.

053A CHK_CHAN  CP   +23               Is it a '#'?
               JR   NZ,054A,CHK_CHAN3 Step if not.
               RST  0018,TEST_RUN     Run-time or checking?
               JR   Z,0544,CHK_CHAN2  Skip if checking syntax.
               RST  0010,CAL_SPEC     Store the '#'.
               DEFW +35D0,chr$+7
0544 CHK_CHAN2 RST  0020,NEXT_CHAR    Get the next character.
               RST  0010,CAL_SPEC     Expect   a   number,   the
               DEFW +1C82,EXPT_1NUM   stream number.
               JR   0575,CHK_CHAN5    Step forward.
054A CHK_CHAN3 RST  0010,CAL_SPEC     Scan the  next  expression
               DEFW +24FB,SCANNING    and store it on the calcu-
                                      lator stack.
               BIT  6,(FLAGS)         Numerical or string?
               JR   Z,0560,STRING_1   Step if is was a string.
               RST  0018,TEST_RUN     Run-time or checking?
               JR   Z,0568,STRING_2   Step if checking syntax.
               XOR  A                 Default = '' or 'm'
               RST  0010,CAL_SPEC     Store the ''.
               DEFW +35D0,chr$+7
               RST  0028,FP_CALC      drive-nr, ''
               DEFB +04               (lengthbyte)
               DEFB +01,exchange      '', drive-nr
               DEFB +38,end-calc      '', drive-nr
               JR   0568,STRING_2     Step forward.
0560 STRING_1  CALL 0525,TEST_SEP     Test for a separator.
               JR   NZ,0572,CHK_CHAN4 Stack 0 twice if not found
               RST  0010,CAL_SPEC     Expect   a   number   (the
               DEFW +1C82,EXPT_1NUM   status number)
0568 STRING_2  CALL 0525,TEST_SEP     Test for a separator.
               JR   NZ,0575,CHK_CHAN5 Stack 0 once if not found.
               RST  0010,CAL_SPEC     Expect a string; the file-
               DEFW +1C8C,EXPT_EXP    name if using "m";1;"name"
               JR   0578,CHK_CHAN6    Step forward.
0572 CHK_CHAN4 RST  0010,CAL_SPEC     Stack zero or empty string
               DEFW +1CE6,USE_ZERO
0575 CHK_CHAN5 RST  0010,CAL_SPEC     Stack zero or empty string
               DEFW +1CE6,USE_ZERO
0578 CHK_CHAN6 RST  0018,TEST_RUN     Run-time or checking?
               JR   Z,0582,CHK_CHAN7  Skip if checking syntax.
               LD   B,+02             Signal: not page-out
               RST  0030,LOOKUP       Lookup the exit table.
               DEFB +0E
               CALL 0087,CALL_JP      Call the exit routine.
               RST  0010,CAL_SPEC     Fetch current character.
               DEFW +0018,GET_CHAR
               RET


THE 'EXIT' ROUTINE
On entry, the  calculator  stack  contains  the  device-name,  a
number (the drive-nr for 'm', 'd', 'CAT'  and  'ram  disk';  the
status for 'j', 't' and 'b'; the stream number for  '#')  and  a
file-name (if present). The device-name should  be  1  character
long, otherwise an error will be generated.  The  three  entries
are chained together and stored in the workspace. The DE and  BC
registerpairs are set up correctly and stored on the  calculator
stack.

0586 EXIT      RST  0028,FP_CALC      dev-name, numb, file-name
               DEFB +06               (lengthbyte)
               DEFB +C0,st-mem-0      (mem-0 holds file-name)
               DEFB +02,delete        dev-name, numb
               DEFB +01,exchange      numb, dev-name
               DEFB +38,end-calc      numb, dev-name
               RST  0010,CAL_SPEC     Fetch parameters  of   the
               DEFW +2BF1,STK_FETCH   device-name. DE := start,
               DEC  BC                BC := length - 1
               LD   A,B               Device-name 1 character?
               OR   C
               JR   Z,0596,EXIT_2     Then skip.
0594 REPORT_2A RST  0008,SH_ERROR     Report   'Invalid   device
               DEFB +29               name'.
0596 EXIT_2    LD   A,(DE)            A := device-name.
               SET  5,A               Force to lower case.
               PUSH AF                Store channel-identifier.
               RST  0010,CAL_SPEC     Fetch the number from  the
               DEFW +1E94,FIND_INT1   calculator stack into A.
               PUSH AF                Store status/drive-nr too.
               LD   BC,+0002          Create 2 spaces  into  the
               RST  0010,CAL_SPEC     workspace.
               DEFW +0030,BC_SPACES
               POP  AF                Store status/drive-nr into
               LD   (HL),A            the second byte.
               POP  AF                Store the  channel-identi-
               LD   (DE),A            fier in the first byte.
               RST  0010,CAL_SPEC     Store the string of  these
               DEFW +2AB6,STK_STORE   2 bytes on the stack.
               RST  0028,FP_CALC      string
               DEFB +05               (lengthbyte)
               DEFB +E0,get-mem-0     string, file-name
               DEFB +17,strs-add      string + file-name
               DEFB +38,end-calc      string + file-name
               RET


THE 'LOOKUP (2)' ROUTINE
The continuation of RST 30. On entry the C register contains the
table number, the A register the value to search  for  in  table
'C', and B contains the offset in  table  'A'.  If  IC  6116  is
present, then the search is made using the tables copied in  it,
otherwise the ROM tables are used.

05B1 LOOKUP_2  LD   HL,+3001          IC 6116 present?
               BIT  1,(HL)
               LD   HL,+1FFF          Point to the tables in ROM
               JR   Z,05BD,LOOKUP_3   Not present? Then skip.
               INC  HL                Point to the tables in RAM
               INC  HL
05BD LOOKUP_3  LD   D,(HL)            Read the startaddress into
               INC  HL                the DE registerpair.
               LD   E,(HL)
               EX   DE,HL             HL := start of tables.
               INC  C                 Simply  return  with  this
               RET  Z                 address if C = 255.
               PUSH BC                Stack B (offsetnumber)
               LD   B,+00
               ADD  HL,BC             HL := entry 'C'.
               POP  BC                Restore B.
               LD   D,(HL)            Read the  startaddress  of
               DEC  HL                table 'C' into DE.
               LD   E,(HL)            (= (HL - 1)).
               EX   DE,HL             HL := start of table 'C'.
               LD   C,A               Search for value 'A'.
05CD LOOKUP_4  LD   A,(HL)            Read a value from table.
               INC  HL                Skip the address.
               INC  HL
               AND  A                 End of table (0=endmarker)
               JR   Z,05D9,LOOKUP_5   Then use that address.
               CP   C                 Is it value 'A'?
               JR   Z,05D9,LOOKUP_5   Then use that address.
               INC  HL                Else point to the new byte
               JR   05CD,LOOKUP_4     and try to match that one.
05D9 LOOKUP_5  LD   A,C               Restore A.
               LD   C,B               Offset number into C.
               LD   D,(HL)            Read the  startaddress  of
               DEC  HL                the subtable 'A'.
               LD   E,(HL)
               EX   DE,HL             HL := start of subtable
               INC  C                 simply  return  with  this
               RET  Z                 address if C = 255.  (also
                                      used when a tables has  no
                                      subtables)
               PUSH BC                Store B.
               LD   B,+00
               ADD  HL,BC             HL := entry 'C'.
               POP  BC                Restore B.
               LD   D,(HL)            Read the  startaddress  of
               DEC  HL                the handling routine  into
               LD   E,(HL)            the DE registerpair.
               EX   DE,HL             Now HL holds that address.
               RET


THE 'PRINT AN ERROR-MESSAGE' SUBROUTINE
On entry the A register holds the message-number.  The  required
message is printed.

05EB P_ERR_MSG LD   B,+FF             Signal: no subtables.
               RST  0030,LOOKUP       Find the  startaddress  in
               DEFB +0C               the error-message table.


THE 'PRINT A MESSAGE AT HL' SUBROUTINE
HL points to the start of the message to be  printed.  The  last
byte of the message has it's 7th bit set.

05EF PRINT_MSG LD   A,(HL)            Read a character.
               AND  +7F               Mask out bit 7.
               RST  0010,CAL_SPEC     Print the character.
               DEFW +0010,PRINT_A_1
               BIT  7,(HL)            Last character printed?
               INC  HL                (point to next character)
               JR   Z,05EF,PRINT_MSG  Loop back if not.
               RET


THE 'CLEAR ALL TEMPORARY CHANNELS' SUBROUTINE
All the channels  that  have  a  lower  case  channel  name  are
temporary, ie.  not  created  with  an  OPEN  #  command.  These
channels are removed from momory.

05FB CL_T_CHNS LD   HL,(CHANS)        Make HL  point  after  the
               LD   BC,+0014          internal  channels   (they
0601 CL_T_NXT  ADD  HL,BC             take 20 bytes).
               PUSH HL                Pass the result to IX.
               POP  IX
               LD   DE,(PROG)         DE := end of channel area.
               DEC  DE
               AND  A
               SBC  HL,DE             Last channel checked?
               RET  Z                 Finished if so.
               JP   NC,1246,REPORT_k  Report  'RAM  corrupt'  if
                                      even past channel area.
               ADD  HL,DE             Correct HL.
               LD   C,(IX+05)         BC := length of channel.
               LD   B,(IX+06)
               BIT  5,(IX+04)         Lower case channel name?
               JR   Z,0601,CL_T_NXT   Check next channel if not.
               CALL 036D,CLEAR_CHN    Clear the channel.
               JR   05FB,CL_T_CHNS    Find the next channel.


THE 'NEAT RETURN' ROUTINE
Fetch the two bytes immediately after  the  CALL  and  form  the
jump-address. Restore the stackpointer and jump to the  address.
It leaves no trace on the stack.

0623 NEAT_RET  POP  HL                Fetch 'old' return address
               LD   E,(HL)            Read the  next  two  bytes
               INC  HL                (the jump-address) into DE
               LD   D,(HL)
               LD   SP,(ERR-SP)       Restore the stackpointer.
               PUSH DE                Make an indirect jump  via
               JP   1748,PAGE_OUT     the PAGE_OUT routine.


THE 'COPY CODE TO THE STACK' SUBROUTINE
The four bytes following the CALL are fetched and stored in  the
DE and BC registerpairs respectively. DE is the startaddress and
BC the length of the routine in the Spectrum ROM to be copied to
the stack. The first two bytes that are stacked contain the last
address + 1 of the code, so the room that is needed is BC  +  2.
On exit HL holds the last address of the code in the  stack,  DE
the first and BC the length.

062F CD_TO_STK POP  HL                Fetch 'old' return address
               LD   E,(HL)            DE := startaddress of  the
               INC  HL                code to be copied.
               LD   D,(HL)
               INC  HL
               LD   C,(HL)            BC := length of the code.
               INC  HL
               LD   B,(HL)
               INC  HL
               PUSH HL                Store 'new' return address
               PUSH BC                Store the length.
               PUSH DE                And the startaddress.
               CALL 0679,MK_RM_STK    Make room in the stack.
               POP  HL                HL := startaddress of code
               PUSH DE                DE = startaddress in stack
               RST  0010,CAL_SPEC     LDIR from the Spectrum ROM
               DEFW +33C3,MOVE_FP+3
               POP  HL                DE := start of code in stk
               EX   DE,HL             HL := end of code in stack
               DEC  HL
               POP  BC                BC := length of the code.
               RET


THE 'FLOATING POINT CALCULATOR (2)' ROUTINE
The continuation of RST 0028,FP_CALC. On entry HL points to  the
address of the first byte following the RST  instruction  (which
is the lengthbyte) and the BC registerpair holds the  length  of
the space to be created in the stack.  The  RST  instruction  in
stored in the stack, followed by BC - 2  databytes.  The  return
address is stacked and the code is executed.

0648 FP_CALC_2 CALL 0679,MK_RM_STK    Make room in the stack.
               POP  HL                HL := 'old' return address
               PUSH DE                DE = start of code in stk.
               DEC  BC                BC := length of data.
               DEC  HL                Point to RST instruction.
               LDI                    Stack it.
               INC  HL                Skip the lengthbyte.
               LDIR                   Move the databytes.
               POP  DE                DE := start of code in stk
               PUSH HL                Stack 'new' return address


THE 'EXECUTE CODE IN THE STACK' SUBROUTINE
On entry DE holds the first address in  the  stack  and  BC  the
length of the code. The last address of the code is fetched  and
a RET instruction is placed there. So, the value of BC should be
one more than the length of the actual routine. The  routine  is
CALLed and the stackroom reclaimed.

0656 EXEC_STK  PUSH HL                HL = end of code in stack.
               PUSH DE                DE = start of code in stk.
               PUSH BC                BC = length of the code.
               EX   DE,HL
               DEC  HL
               LD   D,(HL)            DE := last address + 1.
               DEC  HL
               LD   E,(HL)
               EX   DE,HL
               DEC  HL                HL := last address.
               LD   (HL),+C9          Put RET in place.
               INC  HL                HL := last address + 1.
               POP  DE                DE := length of the code.
               POP  BC                BC := start of the code.
               EX   (SP),HL           Store HL before the start.
               PUSH BC
               RST  0010,CAL_SPEC     CALL the code while in the
               DEFW +1C1D,JUMP_C_R+7  Spectrum ROM.
               POP  BC                BC := start of code.
               EX   (SP),HL           HL := last address + 1.
               PUSH DE                Store DE and AF (result of
               PUSH AF                CALLed routine).
               LD   D,B               DE := start of code.
               LD   E,C
               DEC  DE                DE := first roomaddress of
               DEC  DE                the stack.
               CALL 069B,FR_RM_STK    Free the room.
               POP  AF                Restore AF and DE.
               POP  DE
               RET                    Finished.


THE 'MAKE ROOM IN THE STACK' SUBROUTINE
On entry BC holds the length of the code  to  be  moved  to  the
stack. A test is made for room  and  the  stackpointer  made  to
point to a position BC + 2 bytes lower. The extra  2  bytes  are
used to store the last address + 1 of the code (= 'old' value of
the stackpointer). The last 6 values on the stack are moved also
so some registerpairs/return addresses  can  be  stacked  before
calling this subroutine. On exit BC holds the length and DE  the
startaddress of the code in the stack.

0679 MK_RM_STK RST  0010,CAL_SPEC     Test for room (and  report
               DEFW +1F05,TEST_ROOM   'Out of memory' if not).
               LD   HL,+0000          Fetch the current value of
               ADD  HL,SP             the stackpointer into HL.
               LD   D,H               Make a copy in DE.
               LD   E,L
               SCF
               SBC  HL,BC             HL := HL - BC - 1
               DEC  HL                HL := HL - BC - 2, the new
               LD   SP,HL             value for the stackpointer
               PUSH DE                Store 'old' stackbottom.
               PUSH BC                Store length.
               LD   BC,+000C          Move  12  bytes  (6  stack
               EX   DE,HL             entries) down the stack.
               LDIR
               EX   DE,HL
               LD   (HL),E            Store last address + 1  as
               INC  HL                first  2  bytes  into  the
               LD   (HL),D            created room.
               INC  HL
               EX   DE,HL             DE := start of room in stk
               POP  BC                BC := length of room.
               POP  HL                HL := 'old' stackbottom.
               RET                    Finished.


THE 'FREE THE ROOM IN THE STACK' SUBROUTINE
On entry DE should point to the start and HL to the last address
+ 1 of the room in the stack. The  stackpointer  and  stack  are
moved up in memory again.

0698 FR_RM_STK EX   DE,HL             HL := startaddress of room
               DEC  DE                DE := last address of room
               PUSH HL                (these 2 bytes should  not
               SCF                    be moved)
               SBC  HL,SP             HL := HL - SP - 1
               DEC  HL                HL := HL - SP - 2 = length
               LD   B,H               of room to free.
               LD   C,L               Copy the length to BC.
               POP  HL                HL := last address of  the
               DEC  DE                current stack.
               LDDR                   Move up the stack.
               INC  DE                DE := 'new' stackbottom.
               EX   DE,HL             Move it to HL.
               LD   SP,HL             Restore the stackpointer.
               RET


THE 'OPEN A CHANNEL' SUBROUTINE
On entry the calculator stack  holds  a  pointer  to  a  channel
parameter block and the length of the required  channel.  The  A
register holds the OPEN flags, bit 0 set for output, bit  1  set
for input and bit 2 set for create. HL holds  the  record-length
of the channel. Table 4 is searched and a jump is made. On  exit
IX holds the startaddress of the channel.

06A9 OPEN_CHAN PUSH HL                Store the record-length.
               PUSH AF                And the OPEN flags.
               RST  0028,FP_CALC      pointer, length
               DEFB +05               (lengthbyte)
               DEFB +C1,st-mem-1      (mem-1 holds the length)
               DEFB +02,delete        pointer
               DEFB +38,end-calc      pointer
               RST  0010,CAL_SPEC     Fetch  the  parameters  of
               DEFW +2BF1,STK_FETCH   the pointer.
               PUSH BC                BC = length of parameters.
               PUSH DE                DE = start of parameters.
               LD   A,(DE)            Read the channel-name in A
               LD   B,+04             Signal: OPEN.
               RST  0030,LOOKUP       Lookup the startaddress in
               DEFB +04               the channel table.
               POP  DE                DE := start of parameters.
               POP  BC                BC := length of parameters
               POP  AF                AF := OPEN flags.
               EX   (SP),HL           HL := record-length.
               RET                    Jump to the handling  rou-
                                      tine.


THE 'MOVE A CHANNEL' ROUTINE
On entry HL points to the start of the input-channel and  IX  to
the output-channel. A buffer as large as possible is created  in
the workspace. The buffer is filled from  the  input-buffer  and
emptied  into  the  output-buffer  until  the   end    of    the
input-channel has been reached. Then the buffer is reclaimed.

06BF MOVE_CHAN PUSH IX                IX = output-channel.
               PUSH HL                HL = input-channel.
               RST  0010,CAL_SPEC     BC := free memory.
               DEFW +1F1A,FREE_MEM
               LD   HL,+FF00          Make buffer  as  large  as
06C8 MOVE_CHN2 SBC  HL,BC             possible, with an overhead
               JR   Z,06C8,MOVE_CHN2  of 256 bytes, unless  this
                                      makes the buffer 0 bytes.
               LD   B,H               BC := free space.
               LD   C,L
               RST  0010,CAL_SPEC     Create BC  spaces  in  the
               DEFW +0030,BC_SPACES   workspace.  DE  :=   first
                                      byte in the created room.
               POP  IX                IX := input-channel.
06D3 MOVE_CHN3 PUSH DE                Store start of buffer.
               PUSH BC                Store bufferlength.
               CALL 07CF,LOAD_BYTS    Load the  bytes  into  the
               POP  HL                buffer. HL := bufferlength
               POP  DE                DE := start of buffer.
               EX   (SP),IX           Exchange input- and output
               PUSH DE                channel. Store  start  and
               PUSH HL                length of the buffer.
               PUSH AF                Store EOF flag.
               AND  A                 BC = unloaded bytes  (when
               SBC  HL,BC             EOF). HL := loaded bytes.
               LD   B,H               BC := bytes to save.
               LD   C,L
               CALL 0711,SAVE_BYTS    Write  buffer  to  output-
               POP  AF                channel. Restore EOF flag.
               POP  BC                BC := bufferlength.
               POP  DE                DE := start of buffer.
               EX   (SP),IX           Exchange input- and output
               JR   Z,06D3,MOVE_CHN3  channel and  repeat  while
                                      not EOF.
               EX   DE,HL             Switch pointers  over  and
               RST  0010,CAL_SPEC     reclaim the buffer-space.
               DEFW +19E8,RECLAIM_2
               POP  HL                HL := input-channel.
               RET                    IX = output-channel.


THE 'SAVE OR LOAD A CHANNEL' ROUTINE
On entry the A register should hold 0 for SAVE, 1  for  LOAD,  2
for VERIFY or 3 for MERGE. IX should point to the start  of  the
channel, HL to the position in memory from where the data should
be loaded or saved. A header should have  been  set  up  in  the
workspace which is identical to the header used  by  a  cassette
load or save with the exception that a filename must  be  given.
DE should point to the start of this header. Depending on  A,  a
different part of this routine is used.

06F4 SAVE_CHAN PUSH HL                HL = startaddress of data.
               LD   BC,+0001          Signal: 1 byte.
               LD   (T-ADDR),A        Store request.
               AND  A                 Was it 'SAVE'?
               JR   NZ,0727,LD_VER_ME Step if not.

First the file-type is saved,  the  file-name  is  skipped,  and
the length- and start-bytes are  saved.  Finally  the  described
block is saved.

06FC SAVE_CTRL CALL 0711,SAVE_BYTS    Save file-type (1 byte).
               EX   DE,HL             HL := start of header.
               LD   C,+0A             Skip  the  file-name   (10
               ADD  HL,BC             bytes).
               PUSH HL                HL points to the length.
               EX   DE,HL             Restore pointers.
               LD   C,+06             Save the  file-parameters.
               CALL 0711,SAVE_BYTS    (6 bytes)
               POP  HL                HL points to the length.
               LD   C,(HL)            BC := file-length.
               INC  HL
               LD   B,(HL)
               POP  DE                DE := startaddress of data

BC bytes are saved from address DE onward.

0711 SAVE_BYTS EXX                    Use alternate registers.
               LD   B,+00             Signal: save.
               CALL 07EE,I/O_ADDR     Find I/O address in HL.
0717 SAVE_BYT2 LD   A,B
               OR   C                 Last byte saved?
               RET  Z                 Exit if so.
               LD   A,(DE)            Get byte to be saved.
               EXX                    Use alternate registers.
               PUSH HL                Save  I/O  address   while
               CALL 0087,CALL_JP      CALLing it.
               POP  HL
               EXX                    Use normal registers.
               INC  DE                Increase startaddress.
               DEC  BC                Decrease length.
               JP   0717,SAVE_BYT2    Loop back.

The file-type is verified and stored in the 2nd header. The next
6 bytes (the file-parameters)  are  stored  in  the  appropriate
places in the 2nd header and a jump is made to the LOAD,  VERIFY
or MERGE routine. A special case is a bytes-file. The header has
to be verified first and cannot be MERGEd.

0727 LD_VER_ME CALL 07AB,VER_BYTES    Check the file-types.
               JR   Z,072E,L/V/M_2    Skip if they match.
072C REPORT_m  RST  0008,SH_ERROR     Report 'Wrong file type'.
               DEFB +35
072E L/V/M_2   EX   DE,HL             HL := start of header.
               DEC  HL
               LD   A,(HL)            Fetch the file-type into A
               LD   C,+11             Skip 17 bytes  (ie.  point
               ADD  HL,BC             to the 2nd header).
               LD   (HL),A            Copy the file-type.
               PUSH HL                Store start of 2nd header.
               PUSH AF                Store file-type.
               LD   C,+0B             Skip 11 bytes  (ie.  point
               ADD  HL,BC             to the file-parameters).
               LD   C,+06             Now load the  file-parame-
               EX   DE,HL             ters  into  their  correct
               CALL 0785,TRY_LOAD     places in the 2nd header.
               POP  AF                Restore file-type.
               EX   (SP),IX           IX := start of 2nd header.
               POP  HL                HL := start of channel.
               EX   (SP),HL           HL := startaddress,  stack
               PUSH HL                start of channel and start
                                      address.
               CP   +03               File-type = CODE?
               JR   Z,0753,VERI_CTRL  Use VERIFY routine if so.
               LD   A,(T-ADDR)        Fetch request into A.
               DEC  A                 Is it 'LOAD'?
               JR   Z,076D,LOAD_CTRL  Step if so.
               DEC  A                 Is it 'VERIFY'?
               JR   NZ,0792,MERG_CTRL Step if not.

The header is verified and a jump is made into the LOAD-routine.

0753 VERI_CTRL CALL 062F,CD_TO_STK    Copy code to stack:
               DEFW +07CB,VE_CONTRL   VERIFY control.
               DEFW +003D             (length)
               DEC  HL
               LD   (HL),+E1          Place POP HL  at  relative
               DEC  HL                address 0806.
               DEC  HL
               DEC  HL
               LD   (HL),+C9          Place  RET   at   relative
               DEC  HL                address 0803.
               LD   (HL),+3C          And INC A at address 0802.
               POP  HL                HL := startaddress of data
               CALL 0656,EXEC_STK     Execute the code.
               JR   Z,077E,LOAD_CTR2  Skip if no  error  in  the
076B REPORT_j  RET  0008,SH_ERROR     header.  Otherwise  report
               DEFB +32               'File size error'.

The current BASIC program  and/or  variables  are  cleared  when
loading a program or an array. Depending on the carry  flag  the
data is loaded or verified.

076D LOAD_CTRL CALL 062F,CD_TO_STK    Copy code to stack:
               DEFW +0808,LD_CONTRL   LOAD control.
               DEFW +00AC             (length)
               LD   HL,+0068
               ADD  HL,DE             HL := start of code + 104.
               LD   (HL),+C9          Place  RET   at   relative
                                      address 0870.
               POP  HL                HL := startaddress of data
               CALL 0656,EXEC_STK     Execute the code.
077E LOAD_CTR2 LD   B,D               IX := startaddress of data
               LD   C,E               BC := length of data
               EX   (SP),IX           IX := start of channel
               POP  DE                DE := startaddress of data
               JR   NC,078C,TRY_VERI  Step if verifying.
0785 TRY_LOAD  CALL 07C7,LOAD_BYTS    Load the data-block.
               RET  Z                 Exit if no errors.
0789 REPORT_8  RST  0010,CAL_SPEC     Report 'End of file'.
               DEFW +15E4,REPORT_8
078C TRY_VERI  CALL 07A8,VERI_BYTS    Verify the data-block.
               RET  Z                 Exit if no errors.
0790 REPORT_k  RST  0008,SH_ERROR     Report 'Verification   has
               DEFB +34               failed'.

Room is created to hold the data to be MERGEd and  the  data  is
loaded into it. The program and its variables  are  merged  with
the current program. The Spectrum ROM routine is  used,  so  all
the tape-protections still work.

0792 MERG_CTRL CALL 062F,CD_TO_STK    Copy code to stack:
               DEFW +08B6,ME_CONTRL   MERGE control.
               DEFW +000E             (length)
               POP  HL                HL := startaddress of data
               CALL 0656,EXEC_STK     Execute the code.
               LD   B,D               BC := length of data.
               LD   C,E
               POP  IX                IX := start of channel.
               PUSH HL                HL = startaddress of data.
               EX   DE,HL             DE := startaddress of data
               CALL 0785,TRY_LOAD     Load bytes in the buffer.
               POP  HL                HL := startaddress of data
               RST  0010,CAL_SPEC     Now merge the program  and
               DEFW +08CE,ME_NEW_LP-6 its variables.
               RET

The disk-load-address is fetched and the routine called BC times
verifying each byte and returning if  there  is  a  verification
failure. On exit the zero flag is set when the end of  file  has
been reached, reset if an error occured.

07AB VERI_BYTS EXX                    Use alternate registers.
               LD   B,+02             Signal: load.
               CALL 07EE,I/O_ADDR     Find the I/O address.
07B1 VERI_BYT2 LD   A,B
               OR   C                 All bytes loaded?
               RET  Z                 Exit if so.
07B4 VERI_BYT3 EXX                    Use alternate registers.
               PUSH HL                Store  the   I/O   address
               CALL 0087,CALL_JP      while loading a byte.
               POP  HL
               EXX                    Use normal registers.
               JR   NC,07C6,VER_B_ERR Jump if not succesful.
               EX   DE,HL
               CP   (HL)              The actual verification.
               EX   DE,HL
               RET  NZ                Return if they mismatch.
               INC  DE                Increase startaddress.
               DEC  BC                Decrease length.
               JP   07B1,VERI_BYT3    Loop back.
07C6 VER_B_ERR JR   NZ,0789,REPORT_8  Report EOF if that is true
               CALL 07FF,EOF_PRSSD     presssed?
               JR   C,07B4,VERI_BYT2  Try to re-load if not.
               JR   0789,REPORT_8     Report the error.

The disk-load-address is fetched and the routine  is  called  BC
times. The bytes are stored from address DE onward. On exit  the
zero flag is set when the end of  the  file  has  been  reached,
reset in case of an error.

07CF LOAD_BYTS EXX                    Use alternate registers.
               LD   B,+02             Signal: load.
               CALL 07EE,I/O_ADDR     Find the I/O address.
07D5 LOAD_BYT2 LD   A,B
               OR   C                 All bytes loaded?
               RET  Z                 Exit if so.
07D8 LOAD_BYT3 EXX                    Use alternate registers.
               PUSH HL                Store  the   I/O   address
               CALL 0087,CALL_JP      while loading a byte.
               POP  HL
               EXX                    Use normal registers.
               JR   NC,07E7,LD_B_ERR  Jump if not successful.
               LD   (HL),A            The actual loading.
               INC  DE                Increase startaddress.
               DEC  BC                Decrease length.
               JP   07D5,LOAD_BYT2    Loop back.
07E7 LD_B_ERR  RET  NZ                Handle EOF in the  calling
                                      routine.
               CALL 07FF,EOF_PRSSD     pressed?
               JR   C,07D8,LOAD_BYT3  Try to re-load if not.
               RET                    Handle EOF in the  calling
                                      routine.


THE 'FIND THE I/O ADDRESS' SUBROUTINE
On entry B holds 0 for save, 2 for load. The  channel  table  is
searched and on exit HL holds the I/O address.

07EE I/O_ADDR  PUSH BC                Store entry-number.
               PUSH IX                HL := start of channel.
               POP  HL
               RST  0010,CAL_SPEC     Set the appropriate  flags
               DEFW +1615,CHAN_FLAG   for this channel.
               LD   A,(IX+04)         Fetch channel-name in A.
               OR   +20               Force to lower case.
               POP  BC                Restore entry-number.
               RST  0030,LOOKUP       Lookup the startaddress in
               DEFB +04               the channel table.
               EXX                    Switch  registers   before
               RET                    returning.


THE 'TEST IF END OF FILE PRESSED' SUBROUTINE
A test is made whether the keys   and    are
being pressed together. The carry flag is returned reset if so.

07FF EOF_PRSSD LD   A,+BF             First test .
               AND  A
               IN   A,(+FE)
               RRCA
               RET  C                 Return if not pressed.
               LD   A,+FE             Now test .
               IN   A,(+FE)
               RRCA                   This resets the  carry  if
               RET                    it is pressed.


THE 'GET THE OFFSET OF A CHANNEL' SUBROUTINE
The startaddress of the channel  area  is  subtracted  from  the
start of the current channel and increased by one. This way  the
first channel does not have an offset of 0, since an offset of 0
means that the channel has not been opened yet.

080C GETOFFSET PUSH IX                HL  := start  of   current
               POP  HL                channel.
               LD   BC,(CHANS)        BC := start of chan area.
               AND  A
               SBC  HL,BC
               INC  HL                HL := offset of channel.
               RET


THE 'MAKE A CHANNEL IN MEMORY' SUBROUTINE
On entry A holds the OPEN flags: bit 0 SET for output, bit 1 SET
for input and bit 2 SET for create. The  other  bits  should  be
cleared. DE points to the start of the parameter  block  in  the
workspace and BC contains the length. HL holds the length of the
channel to be created. The  start  of  the  parameter  block  is
stored in the system variable X-PTR, the space is created and if
the parameter block is after the created channel  area  (and  is
moved), this system variable will still point  to  it.  The  I/O
addresses, the device-name, the length of the  channel  and  (if
present) the file-name are inserted into the channel  area.  The
routine continues with a test for the correct channel info.

0818 MAKE_CHAN PUSH BC                BC = length of parameters.
               PUSH AF                A = OPEN flags.
               LD   (X-PTR),DE        DE = start of parameters.
               LD   B,H               Copy the channel length to
               LD   C,L               BC.
               PUSH BC                And store it.
               LD   HL,(PROG)         HL := start of the channel
               DEC  HL
               RST  0010,CAL_SPEC     Make the room.
               DEFW +1655,MAKE_ROOM
               POP  BC                BC := channel length.
               POP  AF                A := OPEN flags.
               INC  HL                Step into the created room
               PUSH HL                IX := start of channel.
               POP  IX
               PUSH BC                Store the channel length.
               LD   (HL),+20          Position a 'space'.
               DEC  BC                Now copy this 'space' into
               LD   D,H               all locations of the crea-
               LD   E,L               ted channel.
               INC  DE
               PUSH HL                (Store channel start)
               LDIR
               POP  HL                (Restore channel start)
               AND  A                 No I/O allowed?
               JR   NZ,083E,MAKE_CHN3 Skip if allowed.
083C MAKE_CHN2 LD   A,+03             Signal: input, output.
083E MAKE_CHN3 CALL 0888,SET_ADDRS    Insert address.
               EX   DE,HL             DE := location of  device-
                                      name in channel.
               LD   HL,(X-PTR)        HL := start of parameters.
               LDI                    Move the device-name.
               EX   DE,HL             Switch pointers again.
               POP  BC                BC := channel-length.
               CALL 0897,STORE_BC     Insert BC in descriptor.
               EX   (SP),HL           HL := length of parameters
               PUSH HL                Store position in descrip-
                                      tor and length of parm.
               INC  HL                Prepare to insert  5  more
               INC  HL                bytes (file-name +  number
               INC  HL                + 7 (7 is  the  number  of
               INC  HL                bytes already inserted  in
               INC  HL                the channel descriptor)).
               AND  A
               SBC  HL,BC             Enough room for the bytes?
               POP  BC                BC := length of parameters
               POP  HL                HL := position in descript
               PUSH BC                Store length again.
               DEC  BC                Exclude the device-name.
               PUSH DE                DE = start of parm. + 1.
               EX   DE,HL             Switch pointers.
               JR   NC,0860,MAKE_CHN4 Move the file-name and the
               LDIR                   number if present.
0860 MAKE_CHN4 POP  DE                DE := start of parameters.
               DEC  DE
               POP  BC                BC := length of parameters


THE 'TEST THE CHANNEL PARAMETERS' ROUTINE
On entry DE points to the start of the parameter  block  and  BC
holds the length. Two immediate data-bytes are used;  the  first
to check if  the  parameter  after  the  OPEN-command  is  valid
(drive-number for 'm', 'd' and 'CAT', status for 't' and 'j' and
stream-number for '#'), the second  to  check  if  a  string  of
characters may be passed on to the routine (file-name for  'm').
On exit the carry flag is SET for an error.  If  SET,  then  the
zero flag is set for an error in the first byte,  RESET  for  an
error in the second byte.

0863 TEST_C    EX   DE,HL             HL := start of parameters.
               POP  DE                Unstack return address.
               PUSH BC                BC = length of parameters.
               INC  HL
               LD   C,(HL)            C := drive/status.
               LD   B,+00
               LD   A,(DE)            A := first byte after CALL
               INC  DE                Point to the second byte.
               CALL 0878,TEST_C_2     Check the first byte.
               BIT  0,B               SET the zero flag.
               POP  BC                BC := length of parameters
               LD   A,(DE)            A := second byte.
               INC  DE                Point to the 'new'  return
               PUSH DE                address and stack it.
               RET  C                 Return if an error.
               DEC  BC                BC := BC - 2.
               DEC  BC                (length of file-name)
0878 TEST_C_2  AND  A
               JP   P,0881,TEST_C_3   Skip if A < 128.
               CPL                    A := 255 - A.
               CP   +7F               A = 127 (so, A was 128?)
               RET  Z                 Exit without error if so.
               DEC  C
0881 TEST_C_3  CP   C                 A < C?
               RET  C                 Exit with error if so.
               LD   A,B               B = 0 (length of file-name
               AND  A                 < 256 characters)?
               RET  Z                 Exit without error if so.
               SCF                    Signal: error.
               RET

Store the I/O addresses in the channel descriptor pointed to  by
IX. A has bit 0 set for output and bit 1 for input. Depending on
these bytes, the addresses  #0008  (DO_I/O  hookcode)  or  #15C4
('Invalid I/O device') are used.

0888 SETADDRS  PUSH IX                HL := start of the channel
               POP  HL
               CALL 088E,SETADDRS2    Repeat twice:
088E SETADDRS2 LD   BC,+0008          Prepare for a DO_I/O.
               RRCA                   Rotate bits of A right.
               JR   C,0897,STORE_BC   Skip if bit 0 was set.
               LD   BC,+15C4          Prepare for error-message.

Store BC at address HL and update HL two addresses.

0897 STORE_BC  LD   (HL),C
               INC  HL
               LD   (HL),B
               INC  HL
               RET

The 'K', 'S' and 'P' subtables of table #04. This table is  only
used by the MOVE-command.

089C TAB_4:KSP DEFW +08E4,WRITE_KSP   Write.
               DEFW +08C6,READ_KSP    Read.
               DEFW +08A8,OPEN_KSP    Open.
               DEFW +001C             Close (return).
               DEFW +0532,USE_M1      Length (use -1).
               DEFW +08CC,POSN_KSP    Position.


THE 'OPEN THE 'K', 'S' OR 'P' CHANNEL' SUBROUTINE
On entry DE points to the start of the parameter  block  and  BC
holds the length. Depending on the device-name,  the  offset  is
formed in the BC register and IX is made to point to  the  start
of the channel.

08A8 OPEN_KSP  LD   A,(DE)            A := device-name.
               PUSH AF                Keep a copy on the stack.
               CALL 0863,TEST_C       Test for channel info.
               DEFB +00               No extra parameters.
               DEFB +00               No file-name.
               JR   C,0902,REPORT_A   Report the error if found.
               LD   IX,(CHANS)        IX := start of chan area.
               POP  AF                A := device-name.
               OR   +20               Force to lower case.
               CP   +70               Is it 'p'?
08BA OPEN_'s'  RET  C                 Exit if it is 's'.
08BB OPEN_'k'  LD   BC,+0005          Signal: 'k'.
               JR   NZ,08C3,OPEN_KP   Skip if it is 'k'.
08C0 OPEN_'p'  LD   BC,+000F          Signal: 'p'.
08C3 OPEN_KP   ADD  IX,BC             IX := start of channel.
               RET


THE 'READ FROM THE 'K', 'S' OR 'P' CHANNEL' SUBROUTINE
Call the Spectrum ROM immediately.

08C6 READ_KSP  EXX
               RST  0010,CAL_SPEC
               DEFW +15E6,INPUT_AD    Call INPUT_AD.
               EXX
               RET


THE 'POSITION THE 'K', 'S' OR 'P' CHANNEL' SUBROUTINE
If printing to the screen ('K' or 'S'), then the position  value
is fetched  and  used  as  the  X-coordinate  in  a  'PRINT  AT'
instruction.

08CC POSN_KSP  PUSH IX                HL := start of channel.
               POP  HL
               RST  0010,CAL_SPEC     Set the appropriate  chan-
               DEFW +1615,CHAN_FLAG   nel flags.
               BIT  1,(FLAGS)         Printer or screen?
               JP   NZ,0BF3,REPORT_J  Report an error if printer
               RST  0010,CAL_SPEC     A := position value.
               DEFW +1E94,FIND_INT1
               DEC  A
               LD   B,A               B := X-coordinate.
               LD   C,+00             C := Y-coordinate.
               RST  0010,CAL_SPEC     Position the cursor.
               DEFW +0A9B             (The 'PRINT AT' routine)
               RET


THE 'WRITE TO THE 'K', 'S' OR 'P' CHANNEL' SUBROUTINE
Call the Specrum ROM immediately.

08E4 WRITE_KSP EXX
               RST  0010,CAL_SPEC
               DEFW +15F2,PRINT_A_2   Call PRINT_A_2.
               EXX
               RET

The '#' subtable of table #04.
08EA TAB_4:#   DEFW +0905,WRITE_#     Write.
               DEFW +0911,READ_#      Read.
               DEFW +08F6,OPEN_#      Open.
               DEFW +001C             Close (return).
               DEFW +091C,LENGTH_#    Length.
               DEFW +0926,POSN_#      Position.

The '#' channel is 8 bytes long:
bytes 0 and 1 : the output address
bytes 2 and 3 : the input address
byte  4       : the device-name (upper case when permanent)
bytes 5 and 6 : the channel length.
byte  7       : the stream being pointed to.


THE 'OPEN THE '#' CHANNEL' SUBROUTINE
The channel descriptor is made and checked for validity.

08F6 OPEN_#    LD   HL,+0008          Reserve 8 bytes.
               CALL 0818,MAKE_CHAN    Make the channel.
               DEFB +0F               Stream number <= 15.
               DEFB +00               No file-name.
               RET  NC                Exit if no error occured.
               JP   Z,03CA,REPORT_O   'Invalid stream' if >15.
0902 REPORT_A  RST  0010,CAL_SPEC     Report 'Invalid argument'.
               DEFW +34E7,REPORT_A


THE 'WRITE TO THE '#' CHANNEL' SUBROUTINE
On entry IX points to the start  of  the  channel.  The  output-
channel is fetched and a call made to the output routine in  the
Spectrum ROM.

0905 WRITE_#   PUSH IX                Store start of channel.
               PUSH AF                Store output character.
               CALL 0930,CHK_LOOP     IX := output-channel.
               POP  AF                Restore output character.
               CALL 08E4,WRITE_KSP    Process the character.
               JR   0919,#_END        Step forward.


THE 'READ FROM THE '#' CHANNEL' SUBROUTINE
On entry IX points to the  start  of  the  channel.  The  input-
channel is fetched and a call is made to the  input  routine  in
the Spectrum ROM.

0911 READ_#    PUSH IX                Store start of channel.
               CALL 0930,CHK_LOOP     IX := input-channel.
               CALL 08C6,READ_KSP     Get a character in A.
0919 #_END     POP  IX                Restore start of channel.
               RET


THE 'LENGTH OF THE '#' CHANNEL' SUBROUTINE
The output-channel is fetched and the length calculated.

091C LENGTH_#  PUSH IX                Store start of channel.
               CALL 0930,CHK_LOOP     IX := output-channel.
               CALL 03D9,LEN_CHAN     Calculate the length.
               JR   0919,#_END        Step back.


THE 'POSITION THE '#' CHANNEL' SUBROUINE
The output-channel is fetched and the channel is positioned.

0926 POSN_#    PUSH IX                Store start of channel.
               CALL 0930,CHK_LOOP     IX := output-channel.
               CALL 03D5,POS_CHAN     Position the channel.
               JR   0919,#_END        Step back.


THE 'CHECK FOR AN INFINITE LOOP' SUBROUTINE
On entry IX points to the start of the '#' channel.  A  test  is
made for an infinite loop. When after 15  times  no  channel  is
found that is not a  '#'  stream,  then  the  'Wally'  error  is
printed. On exit IX points to the requested channel.

0930 CHK_LOOP  LD   B,+0F             Maximal repetition.
0932 CHK_LOOP2 LD   A,(IX+07)         Fetch pointed stream.
               RST  0010,CAL_SPEC     Open the attached channel.
               DEFW +1601,CHAN_OPEN
               LD   IX,(CURCHL)       IX := start of this chan.
               LD   A,(IX+04)         A := device-name.
               OR   +20               Force to lower case.
               CP   +23               Is it a '#'?
               RET  NZ                Exit if not.
               DJNZ 0932,CHK_LOOP2    Repeat the test B times.
               RST  0008,SH_ERROR     Report 'Don't be a Wally'.
               DEFB +48

The 'T' subtable of table #04.
0948 TAB_4:T   DEFW +0988,WRITE_T     Write.
               DEFW +0A6B,READ_T      Read.
               DEFW +096B,OPEN_T      Open.
               DEFW +0986,CLOSE_T     Close.
               DEFW +0532,USE_M1      Length (use -1).
               DEFW +0BF3,REPORT_J    Position (error-message).

The 'T' channel is 10 bytes long:
bytes 0 and 1 : the output address.
bytes 2 and 3 : the input address.
byte  4       : the device-name (upper case when permanent).
bytes 5 and 6 : the channel-length.
byte  7       : bit 0 : SET for ZX PRINTER emulation.
                bit 1 : SET for   sequence.
                bit 2 : SET for true backspacing.
                bit 5 : SET for 'AT' character.
                bit 6 : SET for 'TAB' character.
                bit 7 : SET for a second parameter (AT and TAB)
byte  8       : last column (paper width).
byte  9       : current column.

The 'B' subtable of table #04.
0954 TAB_4:B   DEFW +0A63,WRITE_B     Write.
               DEFW +0A71,READ_B      Read.
               DEFW +0960,OPEN_B      Open.
               DEFW +001C             Close (return).
               DEFW +0532,USE_M1      Length (use -1).
               DEFW +0BF3,REPORT_J    Position (error-message).

The 'B' channel is 7 bytes long:
bytes 0 and 1 : the output address.
bytes 2 and 3 : the input address.
byte  4       : the device-name (upper case when permanent).
bytes 5 and 6 : the channel-length.


THE 'OPEN THE 'B' CHANNEL' SUBROUTINE
The channel is created and filled  with  the  I/O  address,  the
device-name and length.

0960 OPEN_B    LD   HL,+0007          Reserve 7 bytes.
               CALL 0818,MAKE_CHAN    Make the channel.
               DEFB +00               No extra parameters.
               DEFB +00               No file-name.
               RET  NC                Exit if no error occured.
               JR   0902,REPORT_A     Otherwise report the error


THE 'OPEN THE 'T' CHANNEL' SUBROUTINE
The channel is created and filled  with  the  I/O  address,  the
device-name and length. The line-length is checked and if it  is
less than 256, the len-length - 1 (or the  number  of  the  last
character in the line, starting with 0) is inserted as  byte  8.
Byte 9 is cleared.

096B OPEN_T    PUSH HL                HL = line-length.
               LD   HL,+000A          Reserve 10 bytes.
               CALL 0818,MAKE_CHAN    Make the channel.
               DEFB +0F               Status <= 15.
               DEFB +00               No file-name.
               JR   C,0902,REPORT_A   Report an error if found.
               POP  HL                HL := line-length.
               LD   A,H               Report 'Out of range' when
               AND  A                 HL is more than 255.
               JR   NZ,0983,REPORT_B
               DEC  L
               LD   (IX+08),L         Store max-column.
               LD   (IX+09),H         Clear current-column.
               RET
0983 REPORT_B  CALL 0010,CAL_SPEC     Report  'Integer  out   of
               DEFW +1E9F,REPORT_B    range'.


THE 'CLOSE THE 'T' CHANNEL' SUBROUTINE
Only a  is sent to the printer.

0986 CLOSE_T   LD   A,+0D             Prepare for a .


THE 'WRITE TO THE 'T' CHANNEL' SUBROUTINE
On entry the A register holds the character to be printed and IX
the start of the channel. If the ZX PRINTER emulation is  to  be
used and the character does not belong to an 'AT'  or  a  'TAB',
then the address of the print routine will be the  last  address
in table #16 (A is set to zero), otherwise the table has  to  be
searched for the correct address.

0988 WRITE_T   LD   D,A               Store the character.
               LD   A,(IX+07)         Char does not belong to  a
                                      'TAB' or an 'AT'  and  the
               AND  +F1               ZX  PRINTER  is   to    be
               DEC  A                 emulated?
               LD   A,D               (Restore A)
               JR   Z,0993,WRITE_T_2  Then skip.
               XOR  A                 Zero A.
0993 WRITE_T_2 PUSH DE                Store the character (in D)
               LD   B,+FF             Signal: no subtables.
               RST  0030,LOOKUP       Lookup the startaddress in
               DEFB +16               the character table.
               POP  AF                Restore the character.
               PUSH IX                Store start of channel.
               EX   (SP),HL           Stack the address.
               LD   BC,+0007          HL := start of channel + 7
               ADD  HL,BC
               LD   C,(IX+08)         C := max-column.
               RET                    Jump indirectly.


THE 'PRINT_OUT' SUBROUTINES
The print addresses for the  various  control-codes  (only  when
emulating the ZX PRINTER). Cursor right means just another space
'AT' and 'TAB' are stored in the channel descriptor (byte 7) and
PRINT_SECOND is  used  by  'INK',  'PAPER',  'FLASH',  'BRIGHT',
'INVERSE' and also for 'AT' and 'TAB', that use two bytes  each.
PRINT_COMMA  makes  the  current  column-number  the  next  16th
position and cursor-left only decreases the  column-number  when
not at the start of the line.

09A4 PR_CSRRIG SET  0,(FLAGS)         Signal: a leading space.
               RET
09A9 PRINT_AT  SET  5,(HL)            Signal: 'AT'.
               JR   09AF,PRINT_2ND    and a second character.
09AD PRINT_TAB SET  6,(HL)            Signal: 'TAB'.
09AF PRINT_2ND SET  7,(HL)            Signal: another character.
               RET
09B2 PRINT_,   LD   A,(IX+09)         A := current column.
               OR   +0F               A := next 16th column.
               INC  A
               INC  C                 No check on line-length?
               JR   Z,09CE,TAB_LOOP   Then simply add the spaces
               CP   C                 End of line reached?
               JR   C,09CE,TAB_LOOP   Add the spaces if not.
               XOR  A                 Jump to the start of a new
               JR   09CE,TAB_LOOP     line and add the spaces.


THE 'PRINT OTHER CHARACTERS' SUBROUTINE
Enter here when not emulating the ZX PRINTER, or else  when  the
character that has to be printed is not a control character,  or
when still receiving characters from an 'AT' or a  'TAB'.  First
'TAB' is handled,  since  the  first  character  is  used  as  a
positioner, then 'AT' is handled, since the  first  charactr  is
not to be used. The second character holds the position for 'AT'

09C1 PR_OTHER  BIT  6,(HL)            No 'TAB'?
               JR   Z,09DE,PR_OTHER2  Then skip.
09C5 TAB       RES  6,(HL)            Signal: no 'TAB'.
               INC  C                 No check on line-length?
               JR   Z,09CE,TAB_LOOP   Then simply add the spaces
09CA TAB_WRAP  SUB  C                 If tabbing beyond the line
               JR   NC,09CA,TAB_WRAP  length, then subtract  the
               ADD  A,C               line-length (a few times).
09CE TAB_LOOP  CP   (IX+09)           Return once  the  position
               RET  Z                 to tab to has been reached
               PUSH AF
               CALL NC,0A47,PR_SPACE  If  before  the  position,
               POP  AF                print a space.
               PUSH AF
               CALL C,0A1E,PR_NEWLIN  If after the position,  go
               POP  AF                to the next line.
               JR   09CE,TAB_LOOP     Repeat the process.
09DE PR_OTHER2 BIT  7,(HL)            No '2nd character'?
               JR   Z,09E5,PR_OTHER3  Then skip.
09E2 2ND_CHAR  RES  7,(HL)            Signal: no '2nd character'
               RET
09E5 PR_OTHER3 BIT  5,(HL)            No 'AT'?
               JR   Z,0A1A,PR_OTHER4  Then skip.
09E9 AT        RES  5,(HL)            Signal: no 'AT'.
               INC  C                 No check on line-length?
               JR   Z,09F1,AT_2       Then skip the test.
               CP   C                 Past the end of the line?
               JR   NC,0983,REPORT_B  Then report 'Out of range'
09F1 AT_2      CP   (IX+09)           Return once  the  position
               RET  Z                 to go to has been reached.
               PUSH AF                Store position to go to.
               JR   C,0A04,CHAR_LEFT  Step if moving backward.
               CALL 0A4B,PR_SPACE2    Print a space (forward).
               JR   0A17,AT_3         Step forward.
09FD PR_CRSLEF LD   A,(IX+09)         A := current column.
               AND  A                 Return if already  at  the
               RET  Z                 beginning of the line.
               DEC  A                 A := column to go to.
               PUSH AF                Store it.
0A04 CHAR_LEFT BIT  2,(IX+07)         'True backspacing'?
               JR   NZ,0A0F,CHAR_LEF2 Then skip.
               CALL 0A5D,RESET_COL    Go to start of the line.
               JR   0A17,AT_3         Step forward.
0A0F CHAR_LEF2 LD   A,+08             A := .
               DEC  (IX+09)           Column := column - 1.
               CALL 0A63,WRITE_B      Write the character.
0A17 AT_3      POP  AF                A := position to go to.
               JR   09F1,AT_2         Repeat the process.

A test is made for , the only control character that can  be
used when not emulating the ZX PRINTER. The status-byte  (IX+07)
is tested whether just a  or a   sequence should  be
sent to the printer.

0A1A PR_OTHER4 CP   +0D               Is it a ?
               JR   NZ,0A2A,PR_OTHER5 Skip if not.
0A1E PR_NEWLIN CALL 0A5D,RESET_COL    Go to start of the line.
               BIT  1,(IX+07)         Send a  too?
               RET  NZ                Exit if not.
               LD   A,+0A             A := .
               JR   0A63,WRITE_B      Write the character.

Graphics are replaced with a '?', tokens are expanded  (Spectrum
ROM routine) and the control characters (with  codes  less  than
32) are replaced with a  '?'  too  when  not  emulating  the  ZX
PRINTER.

0A2A PR_OTHER5 AND  A                 Graphics/tokens?
               JP   P,0A36,PR_OTHER6  Skip if not.
               SUB  +A5               Graphic character?
               JR   C,0A43,PR_?       Then print a '?'.
               RST  0010,CAL_SPEC     Expand  tokens  with   the
               DEFW +0C10,PO_TOKENS   Spectrum ROM.
               RET
0A36 PR_OTHER6 RES  0,(FLAGS)         Signal: no leading space.
               CP   +20               Is it a space?
               JR   Z,0A47,PR_SPACE   Then print a space.
               JR   NC,0A4D,WRITECHAR Print the printables.
               BIT  0,(HL)            Exit  when  not  emulating
               RET  Z                 the ZX PRINTER.
0A43 PR_?      LD   A,+3F             A := '?'.
               JR   0A4D,WRITECHAR    Print it.
0A47 PR_SPACE  SET  0,(FLAGS)         Signal: leading space.
               LD   A,+20             A := ' '.

If the end of the line has been reached, a  (with or without
a ) is printed first, followed by the character.

0A4D WRITECHAR PUSH AF                Store char temporarely.
               LD   A,(IX+08)         A := max-column.
               CP   (IX+09)           End of the line reached?
               CALL C,0A1E,PR_NEWLIN  Then go to the next line.
               INC  (IX+09)           Column := column + 1.
               POP  AF                Restore character to print
               JR   0A63,WRITE_B      Write the character.

The current column is made zero and a  is printed.

0A5D RESET_COL LD   (IX+09),+00       Zero current column.
               LD   A,+0D             Print a .


THE 'WRITE TO THE 'B' CHANNEL' SUBROUTINE
On entry IX points to the start of the channel and A  holds  the
character to be printed.

0A63 WRITE_B   LD   B,+00             Signal: write.
0A65 GO_PRINT  LD   H,A               H := character to print.
               LD   A,+81             Signal: printer port.
               JP   0FE5,CAL_PHY      Write the character.


THE 'READ FROM THE 'T' CHANNEL' SUBROUTINE
Any character is read, but returned with bit 7 RESET.

0A6B READ_T    CALL 0A71,READ_B       Read a character.
               RES  7,A               RESET bit 7.
               RET


THE 'READ FROM THE 'B' CHANNEL' SUBROUTINE
Just read a character.

0A71 READ_B    LD   B,+02             Signal: read.
               JR   0A65,GO_PRINT     Step back.

The 'm' subtable of table #04.
0A75 TAB_4:m   DEFW +0B6E,WRITE_CT    Write.
               DEFW +0B7C,READ_CT     Read.
               DEFW +0A81,OPEN_M      Open.
               DEFW +0BF8,CLOSE_M     Close.
               DEFW +0B81,LENGTH_M    Length.
               DEFW +0BB8,POSN_M      Position.

The 'm' channel is 33 + the block-size long.
(the block-size can be 128, 256, 512 or 1024 bytes).

bytes  0 and  1 : the output address.
bytes  2 and  3 : the input address.
byte   4        : the device-name (upper case when permanent).
bytes  5 and  6 : the channel-length.
byte   7        : the drive-number.
bytes  8 to  17 : the file-name.
bytes 18 and 19 : the block-size.
bytes 20 and 21 : the record-size.
bytes 22 and 23 : the number of bytes in the last block.
bytes 24 and 25 : the location of the first block.
bytes 26 and 27 : the location of the last block.
bytes 28 and 29 : the location of the current block.
bytes 30 and 31 : the position in the current block.
byte  32        : bit 0 : SET if changes were made to the buffer
bytes 33 and up : the buffer.


THE 'OPEN THE 'M' CHANNEL' SUBROUTINE
On entry DE points to the start of the parameter block, BC holds
the length, HL contains the  record-length,  A  holds  the  OPEN
flags and mem-1  contains  the  required  length.  Depending  on
whether the file has  been  found  and  the  'create'-flag,  the
channel is opened or the report 'File not found' generated.

0A81 OPEN_M    PUSH HL                HL = record-length.
               PUSH AF                A = OPEN flags.
               CALL 0E38,CATSEARCH    Open 'CAT' and find file.
               POP  DE                D := OPEN flags.
               LD   A,D               A := OPEN flags.
               BIT  2,D               'create' allowed?
               JR   NZ,0A9D,CREATE    Then skip.
               JR   C,0A95,FILEFOUND  Skip too if file was found
               AND  A                 No 'I/O' nor 'create'?
               LD   A,+05             Then signal: output,  cre-
               JR   Z,0A9D,CREATE     ate and step forward.
0A93 REPORT_h  RST  0008,SH_ERROR     Report 'File not found'.
               DEFB +30

The file has been found, so it should  not  be  created.  If  it
should be opened for output only, then continue with the  OUTPUT
part of the routine, else signal: input and go to the INPUT part

0A95 FILEFOUND CP   +01               'output' only?
               JR   Z,0AF6,OUTPUT     Then go to the OUTPUT part
               SET  1,A               Signal: input.
               JR   0ADC,INPUT        Go to the INPUT part.

Test whether there is enough room on the disk  for  the  channel
and store in mem-1 the actual length  (in  bytes)  of  the  room
needed. BC holds the record-number of the last file + 1  or  the
number of a file with the same name in the catalog.  If  another
file with the same name exists on the disk, then  it  should  be
erased. A record for the 'CAT'-file containing  the  information
is created and stored on the disk.

0A9D CREATE    PUSH AF                Store the OPEN flags.
               PUSH BC                Store the record-number of
                                      the last file.
               CALL 0E64,GET_FREE     Find free space on disk.
               PUSH DE                DE = start of the gap.
               RST  0010,CAL_SPEC     BC = length of the gap.
               DEFW +2D2B,STACK_BC    Stack the length.
               CALL 0F51,STCK_SIZE    Stack the block-size.
               RST  0028,FP_CALC      lng, bls (len-gap, blcksz)
               DEFB +1F               (lengthbyte)
               DEFB +E1,get-mem-1     lng, bls, lnf (len-file)
               DEFB +36,less-0        lng, bls, (0/1)  (lnf=-1?)
               DEFB +00,jump-true
               DEFB +10,TO 0ABE,HALF_SIZE
               DEFB +E1,get-mem-1     lng, bls, lnf
               DEFB +37,greater-0     lng, bls, (0/1)
               DEFB +30,not           lng, bls, (1/0)   (lnf=0?)
               DEFB +00,jump-true
               DEFB +11,TO 0AC4,FULL_SIZE
               DEFB +04,multiply      lng * bls
               DEFB +E1,get-mem-1     lng * bls, lnf
               DEFB +03,subtract      lng * bls - lnf
               DEFB +36,less-0        (0/1)
               DEFB +30,not              (does file fit in gap?)
               DEFB +00,jump-true                   (Exit if so)
               DEFB +TO 0AC7,CREATE_E
               DEFB +A0,stk-zero      0        (signal: no room)
               DEFB +33,jump                          (And exit)
               DEFB +08,TO 0AC5,STORE_LEN
0ABE HALF_SIZE DEFB +01,exchange      bls, lng
               DEFB +A1,stk-one       bls, lng, 1
               DEFB +0F,addition      bls, lng + 1
               DEFB +A2,stk-half      bls, lng + 1, 0.5
               DEFB +04,multiply      bls, (lng + 1) / 2
               DEFB +3A,truncate      bls, INT ((lng + 1) / 2)
0AC4 FULL_SIZE DEFB +04,multiply      actual length of file
0AC5 STORE_LEN DEFB +C1,stk-mem-1            (store it in mem-1)
               DEFB +02,delete        -
0AC7 CREATE_E  DEFB +38,end-calc      -
               POP  BC                BC := start of the gap.
               CALL 0F48,POSN_BC      Position the catalog-file.
               CALL 0EE3,READ_REC     Find the record.
               LD   Hl,(5CA0)         HL := first free block  of
               INC  HL                the new file.
               INC  BC                BC := new record-number.
               POP  DE                DE :=  last  record-number
                                      in the 'CAT'-file.
               CALL 0EA4,MAKE_FILE    Make a file-record in  the
               CALL 0C63,PUT_BLOCK    catalog and write buffer.
               POP  AF                Restore the OPEN flags.

On entry A holds the OPEN flags and IX points to  the  start  of
the channel. All the information  is  inserted  in  the  channel
descriptor. The first block -  1  is  inserted  as  the  current
block and the block-size as the position in the block,  so  when
the INKEYS# is used, the first byte in the  first  block
is fetched after it is loaded.

0ADC INPUT     CALL 0888,SETADDRS     Set I/O addresses.
               POP  BC                BC := record-length.
               CALL 0E18,STOREINFO    Store info in the  channel
                                      descriptor.
               LD   HL,(5C9E)         HL := first block location
               DEC  HL                Point 1 block before it.
               EX   DE,HL             Switch pointers.
               LD   (HL),E            Store it.
               INC  HL
               LD   (HL),D
               LD   C,(IX+12)         BC := block-size.
               LD   B,(IX+13)
               INC  HL                Store  the  block-size  as
               LD   (HL),C            current  position  in  the
               INC  HL                block.
               LD   (HL),D
               RET

On entry A holds the OPEN flags, BC the number of records in the
'CAT'-file. The new file-size  is  calculated  and  the  channel
descriptor is filled with the data.

0AF6 OUTPUT    PUSH AF                Store the OPEN flags.
               PUSH BC                Store no. records in 'CAT'
               LD   BC,(5C9C)         BC := number of  bytes  in
               INC  BC                the last block.
               RST  0010,CAL_SPEC     Stack this number.
               DEFW +2D2B,STACK_BC
               XOR  A                 A := 0.
               RST  0010,CAL_SPEC     Store the 0 too.
               DEFW +2D28,STACK_A
               CALL 0F51,STCK_SIZE    Store the block-size too.
               LD   BC,(5CA0)         BC := position last block.
               RST  0010,CAL_SPEC     Store it too.
               DEFW +2D2B,STACK_BC
               LD   BC,(5C9E)         BC := position first block
               PUSH BC                Keep a copy.
               RST  0010,CAL_SPEC     Store it too.
               DEFW +2D2B,STACK_BC
               CALL 0BA9,CALC_LEN     Calculate the file-size.
                                      Stack it (call it S).
               CALL 0EE3,READ_REC     Find right file-record.
               LD   HL,(5C9E)         HL := new first block
               POP  BC                BC := previous first block
               PUSH BC                    = new last block - 1.
               AND  A
               SBC  HL,BC             HL := last - first.
               LD   B,H               Copy this to BC.
               LD   C,L
               RST  0010,CAL_SPEC     And stack it.
               DEFW +2D2B,STACK_BC    (Call it B)
               CALL 0F51,STCK_SIZE    Store the block-size too.
               RST  0028,FP_CALC      S, B, block-size (say L)
               DEFB +2B               (lengthbyte)
               DEFB +04,multiply      S, B * L   (length of gap)
               DEFB +C0,st-mem-0      (mem-0 holds B * L)
               DEFB +02,delete        S
               DEFB +31,duplicate     S, S
               DEFB +A1,stk-one       S, S, 1
               DEFB +0F,addition      S, S + 1     (also byte 0)
               DEFB +01,exchange      S + 1, S
               DEFB +E1,get-mem-1     S + 1, S, len
               DEFB +36,less-0        S + 1, S, (0/1)  (len=-1?)
               DEFB +00,jump-true            (then go to DOUBLE)
               DEFB +12,TO 0B4A,DOUBLE
               DEFB +E1,get-mem-1     S + 1, S, len
               DEFB +37,greater-0     S + 1, S, (0/1)
               DEFB +30,not           S + 1, S, (1/0)   (len=0?)
               DEFB +00,jump-true         (then go to FULLSIZE2)
               DEFB +15,TO 0B52,FULLSIZE2
               DEFB +E1,get-mem-1     S + 1, S, len
               DEFB +0F,addition      S + 1, S + len
               DEFB +C1,st-mem-1      (mem-1 holds new file-len)
               DEFB +E0,get-mem-0     S + 1, S + len, B * L
               DEFB +03,subtract      S + 1, (S + len) - (B * L)
               DEFB +37,greater-0     S + 1, (0/1)
               DEFB +30,not           S + 1, (1/0) (enough room)
               DEFB +00,jump-true                   (Exit if so)
               DEFB +10,TO 0B56,OUTPUT_E
               DEFB +A0,stk-zero      S + 1, 0 (Signal: no room)
               DEFB +33,jump                          (And exit)
               DEFB +0B,TO 0B54,STORE_RM
0B4A DOUBLE    DEFB +A2,stk-half      S + 1, S, 0.5
               DEFB +05,division      S + 1, S * 2
               DEFB +31,duplicate     S + 1, S * 2, S * 2
               DEFB +E0,get-mem-0     S + 1, S * 2, S * 2, B * L
               DEFB +03,subtract      S+1, S*2, (S*2)-(B*L)
               DEFB +36,less-0        S + 1, S * 2, (0/1)
               DEFB +00,jump-true                 (enough room?)
               DEFB +03,TO 0B54,STORE_RM            (Exit if so)
0B52 FULLSIZE2 DEFB +02,delete        S + 1
               DEFB +E1,get-mem-0     S + 1, B * L
0B54 STORE_RM  DEFB +C1,st-mem-1      (mem-1 holds new file-len)
               DEFB +02,delete        S + 1
0B56 OUTPUT_E  DEFB +38,end-calc      S + 1
               POP  HL                HL := last block position
               POP  BC                BC := first block position
               LD   D,B               Copy it to DE.
               LD   E,C
               CALL 0EA4,MAKE_FILE    Make a file-record in  the
               CALL 0C63,PUT_BLOCK    catalog and write buffer.
               POP  AF                A := OPEN flags.
               POP  BC                BC := record-length.
               PUSH AF
               CALL 0E18,STOREINFO    Store info in the  channel
               CALL 0BB8,POSN_M       descriptor  and   position
               POP  AF                the 'CAT'-file.
               JP   0888,SETADDRS     Set the I/O addresses.


THE 'WRITE TO THE 'M' CHANNEL' SUBROUTINE
On entry A holds the byte to be written  to  the  disk,  and  IX
points to the start of the channel. The byte is  stored  in  the
buffer at the correct position (possibly after loading the right
block from disk) and bit 0 of (IX+32d) is SET to  indicate  that
the buffer has been changed, so it has to  be  written  to  disk
when loading a new block or closing the channel.

0B6E WRITE_CT  PUSH AF                Store byte temporarely.
               CALL 0F5B,ENDOFFILE    Find the correct position.
               JP   NC,076B,REPORT_j  Report an error if found.
               POP  AF                Restore   the   byte   and
               LD   (HL),A            insert it into the buffer.
               SET  0,(IX+20)         Signal: change made.
               RET


THE 'READ FROM THE 'M' CHANNEL' SUBROUTINE
On entry IX points to the start of the channel. The current byte
is fetched (possibly after loading the right block from disk).

0B7C READ_CT   CALL 0F5B,ENDOFFILE    Find the correct position.
               LD   A,(HL)            Read the byte.
               RET


THE 'CALCULATE THE LENGTH OF THE 'M' CHANNEL' SUBROUTINE
The appropriate values from the channeldescriptor are stored  on
the calculator stack and the length is calculated.

0B81 LENGTH_M  LD   C,(IX+16)         BC := number of  bytes  in
               LD   B,(IX+17)         the last block.
               INC  BC                Include byte 0.
               RST  0010,CAL_SPEC     Store this number.
               DEFW +2D2B,STACK_BC    (Call it B)
               LD   C,(IX+1E)         BC := position in  current
               LD   B,(IX+1F)         block.
               RST  0010,CAL_SPEC     Store it too.
               DEFW +2D2B,STACK_BC    (Call it P)
               CALL 0F51,STCK_SIZE    Stack block-size (say S)
               LD   C,(IX+1A)         BC := last block
               LD   B,(IX+1B)
               RST  0010,CAL_SPEC     Stack it too.
               DEFW +2D2B,STACK_BC    (Call it L)
               LD   C,(IX+1C)         BC := current block.
               LD   B,(IX+1D)
               RST  0010,CAL_SPEC     Stack it too.
               DEFW +2D2B,STACK_BC    (Call it F)
0BA9 CALC_LEN  RST  0028,FP_CALC      B, P, S, L, F
               DEFB +0E               (lengthbyte)
               DEFB +03,subtract      B, P, S, L - F
               DEFB +04,multiply      B, P, S * (L - F)
               DEFB +01,exchange      B, S * (L - F), P
               DEFB +03,subtract      B, S * (L - F) - P
               DEFB +0F,addition      B + S * (L - F) - P
                                       (The remaining length, X)
               DEFB +31,duplicate     X, X
               DEFB +36,less-0        X, (0/1)
               DEFB +30,not           X, (1/0)      (length>=0?)
               DEFB +00,jump-true                   (Exit if so)
               DEFB +02,TO 0BB6,CALC_L_E
               DEFB +30,not           0              (length:=0)
0BB6 CALC_L_E  DEFB +38,end-calc      The remaining length.
               RET


THE 'POSITION THE 'M' CHANNEL' SUBROUTINE
On entry  IX  points  to  the  start  of  the  channel  and  the
calculator stack holds the new  position  of  the  channel.  The
position is multiplied with the record-size, giving the position
in the file in bytes.

0BB8 POSN_M    BIT  3,(IX+02)         Input allowed?
               JR   Z,0BF3,REPORT_n   Report the error if not.
               LD   C,(IX+14)         BC := record-size.
               LD   B,(IX+15)
               LD   A,B               Record-size unspecified?
               OR   C
               JR   NZ,0BC9,POSN_M_2  Skip if not.
               INC  BC                Use 1 as record-size.
0BC9 POSN_M_2  RST  0010,CAL_SPAC     Stack the record-size.
               DEFW +2D2B,STACK_BC
               RST  0028,FP_CALC      new-pos, rec-sz
               DEFB +08               (lengthbyte)
               DEFB +01,exchange      rec-sz, new-pos
               DEFB +A2,stk-half      rec-sz, new-pos, 0.5
               DEFB +03,subtract      rec-sz, new-pos - 0.5
               DEFB +27,int           rec-sz, INT (new-pos - .5)
               DEFB +04,multiply      rec-sz*INT(new-pos-0.5)
               DEFB +38,end-calc      The position in bytes.
               CALL 0F51,STCK_SIZE    Stack the block-size.
               RST  0010,CAL_SPEC     The calculator stack holds
               DEFW +36A0,n-mod-m     the  block-number  in  the
                                      file and the  position  in
               RST  0010,CAL_SPEC     this block.
               DEFW +1E99,FIND_INT2   BC := block-number in file
               LD   L,(IX+18)         HL := first block position
               LD   H,(IX+19)
               ADD  HL,BC             HL := current block pos.
               LD   B,H               Copy this number to BC.
               LD   C,L
               CALL 0FAA,READ_NEXT    Load right block.
               RST  0010,CAL_SPEC     BC  :=  position  in  this
               DEFW +1E99,FIND_INT2   block.
               LD   (IX+1E),C         Insert this  address  into
               LD   (IX+1F),B         the channel descriptor.
               RET
0BF3 REPORT_J  RST  0010,CAL_SPEC     Report 'Invalid I/O device
               DEFW +15C4
0BF6 REPORT_n  RST  0008,SH_ERROR     Report 'Wrong disk'.
               DEFB +36


THE 'CLOSE THE 'M' CHANNEL' SUBROUTINE
On entry IX points to the start of the current channel. If input
was allowed, then write the buffer to disk; else the channel was
opened with 'OUT' and the correct file-length should be  written
in the catalog-file.

0BF8 CLOSE_M   BIT  3,(IX+02)         Input was allowed?
               JR   NZ,0C63,PUT_BLOCK Then write buffer to disk.
               LD   E,(IX+1C)         DE := current block.
               LD   D,(IX+1D)

A test is made whether the current position is the last position
in the file.

               LD   L,(IX+16)         HL := number of  bytes  in
               LD   H,(IX+1