Opus Discovery Code Snippets

Please note this content is from the original WoS site, and may no longer be relevant. If you have any queries, please contact us.

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+17)         the last block.
               LD   C,(IX+1E)         BC := current position  in
               LD   B,(IX+1F)         the block.
               DEC  BC
               AND  A
               SBC  HL,BC             BC at the end of the file?
               JR   NZ,0C21,CLOSE_M_2 Skip if not.
               LD   L,(IX+1A)         HL := last block.
               LD   H,(IX+1B)
               AND  A
               SBC  HL,DE             Current = Last block?
               JR   Z,0C63,PUT_BLOCK  Then write buffer to disk.

Not all the space available is  used  by  the  file.  Write  the
correct length in the file-record in the catalog.

0C21 CLOSE_M_2 PUSH IX                Store start of channel.
               PUSH BC                Store current position.
               PUSH DE                Store last block position.
               LD   L,(IX+18)         HL := first block position
               LD   H,(IX+19)
               PUSH HL                Store it too.
               LD   DE,+FFE5          This is -27d.
               CALL 0FA1,HL:=IX+DE    HL := start of parameters.
               EX   DE,HL             Pass it to DE.
               LD   BC,+000C          BC := length of parameters
               CALL 0E38,CATSEARCH    Open 'CAT'-file.
               LD   (IX+04),+EF       Signal: 'CAT' channel.
               JP   NC,0A93,REPORT_h  Report an error if found.
               LD   HL,(5C9E)         HL := first block ('CAT')
               POP  DE                DE := first block (descr.)
               AND  A
               SBC  HL,DE             Info on  disk  =  info  in
               JR   NZ,0BF6,REPORT_n  descriptor? Report  'Wrong
                                      disk' if not.
               LD   HL,(5CA0)         HL := last block ('CAT')
               POP  DE                DE := last block (descr.)
               SBC  HL,DE             Repeat the test.
               JR   C,0BF6,REPORT_n
               LD   (5CA0),DE         Store DE.
               POP  HL                Store the number of  bytes
               LD   (5C9C),HL         in the last block too.
               LD   H,B               HL := current record
               LD   L,C
               CALL 0F07,MAKE_FREE    Store record in 'CAT'-file
               CALL 0368,CLOSE_CHN    Close the 'CAT'-file.
               POP  IX                IX := start of 'm' channel


THE 'PUT A BLOCK ON DISK' SUBROUTINE
On entry IX points to the start of the channel. If changes  have
been made to the buffer then write it out on the disk.

0C63 PUT_BLOCK BIT  0,(IX+20)         Exit if  no  changes  were
               RET  Z                 made to the buffer.
               LD   BC,+0000          Signal:  write  an  entire
               LD   DE,+0000          block.
               CALL 0FA1,HL:=IX+DE    HL := IX + 33 = buffer
               EX   DE,HL             Pass the bufferstart to DE
               LD   L,(IX+1C)         HL := current block.
               LD   H,(IX+1D)
               LD   A,(IX+07)         A := drive-number.
               CALL 0FE5,CAL_PHY      Write the block.
               RES  0,(IX+20)         Signal: no changes yet.
               RET

The 'm' and ' ' subtables of table #06. The only  difference  in
these two tables is the entry for erase, since the  error  'File
not found' should not be generated when using the 'm' identifier

0C83 TAB_6:m   DEFW +0CF7,ERASE_M     Erase.
               DEFW +0594,REPORT_2A   Error 'Invalid I/O device'
               DEFW +0C93,CAT_M       Catalog.
               DEFW +0D12,FORMAT_M    Format.

0C8B TAB_6:' ' DEFW +0CFE,ERASE_' '   Erase.
               DEFW +0594,REPORT_2A   Error 'Invalid I/O device'
               DEFW +0C93,CAT_M       Catalog.
               DEFW +0D12,FORMAT_M    Format.


THE 'CATALOG THE 'M' CHANNEL' SUBROUTINE
The 'CAT'-file is opened, the records  are  printed  (the  names
only), followed by the free space on the disk; rounded downwards
to the nearest K.

0C93 CAT_M     CALL 0DCC,OPEN_CAT     Open the 'CAT'-file.
               CALL 0F45,POS_START    Position it at the start.
               LD   HL,+0000          HL := free-space counter.
               CALL 0CC8,PR_NAME      Print the disk-name.
               CALL 0CEC,PRINT_CR     Print a .
0CA2 CAT_M_NXT CALL 0CC8,PR_NAME      Print the next file-name.
               JR   NC,0CA2,CAT_M_NXT Repeat until end of 'CAT'.
               LD   B,H               BC := free-space.
               LD   C,L
               RST  0010,CAL_SPEC     Stack it.
               DEFW +2D2B,STACK_BC
               CALL 0F51,STCK_SIZE    Also stack the block-size.
               RST  0028,FP_CALC      free, size
               DEFB +09               (lengthbyte)
               DEFB +04,multiply      free * size
               DEFB +34,stk-data      free * size, 1024  (is 1K)
               DEFB +3B, exponent +8B
               DEFB +00, (+00,+00,+00)
               DEFB +05,division      (free * size) / 1024
               DEFB +3A,truncate      TRUNC (free * size) / 1024
               DEFB +38,end-calc      The free space on the disk
               CALL 0CEC,PRINT_CR     Print a .
               PUSH IX                Save start of the channel,
               RST  0010,CAL_SPEC     while  printing  the  free
               DEFW +2DE3,PRINT_FP    space on the disk.
               POP  IX
               CALL 0CEC,PRINT_CR     Again, print a .
               JP   0368,CLOSE_CHN    Close the 'CAT'-file.

Print the name of the current block and move to the next record.
HL is used to  store  the  number  of  used  blocks.  First  the
start-block of the new file is added to HL, then the last  block
is subtracted from it. When the endmarker is reached,  only  the
first value is added. This is the number of blocks on the  disk,
so HL holds the number of blocks that have not been used.

0CC8 PR_NAME   PUSH HL                Store block counter.
               CALL 0EE3,READ_REC     Read the next record.
               POP  HL                Restore the block counter.
               LD   DE,(5C9E)         DE := start-block of file.
               ADD  HL,DE             Add it to HL.
               LD   DE,(5CA0)         DE := end-block of file.
               LD   A,D
               OR   E                 Endmarker reached?
               INC  A
               SCF                    Signal: last record.
               RET  Z                 Return if so.
               SBC  HL,DE             Subtract (end-block + 1).
               LD   DE,+5CA2          DE := start of file-name.
               LD   A,(DE)            Read first character of  a
               AND  A                 file-name. CHR$ 0? Then it
               RET  Z                 is a hidden file, so exit.
               LD   B,+0A             10 characters in a name.
0CE5 PR_NAME2  LD   A,(DE)            Read a character.
               CALL 0CEE,PRINT_A      Print it.
               INC  DE                Point to the next one.
               DJNZ 0CE5,PR_NAME2     Loop back until done all.
0CEC PRINT_CR  LD   A,+0D             A := .
0CEE PRINT_A   PUSH IX                Save start of the channel,
               RST  0010,CAL_SPEC     while printing CHR$ 'A'.
               DEFW +15F2,PRINT_A_2
               POP  IX
               AND  A                 Signal: more records to go
               RET


THE 'ERASE FROM THE 'M' CHANNEL' SUBROUTINE
The catalog is searched for the file and if it was  found,  then
the entry in the 'CAT'-file should  be  erased.  If  not  found,
simply return without an error-message.

0CF7 ERASE_M   CALL 0E38,CATSEARCH    Find the file-record.
               JR   NC,0D0F,ERASE_M_E Exit if not found.
               JR   0D04,ER_FOUND     Step to erase the entry.


THE 'ERASE FROM THE ' ' CHANNEL' SUBROUTINE
The catalog is searched for the file and if it was  found,  then
the entry in the 'CAT'-file should  be  erased.  If  not  found,
report 'File not found'.

0CFE ERASE_' ' CALL 0E38,CATSEARCH    Find the file-record.
               JP   NC,0A93,REPORT_h  Report the error if absent

The number of records in the catalog is found in BC.

0D04 ER_FOUND  PUSH BC                Store the record-number.
0D05 ER_FOUND2 INC  BC                Point to the next record.
               CALL 0E3D,FIND_FILE    Find the next record  with
               JR   C,0D05,ER_FOUND2  the same name (or: just go
                                      to the end of the catalog)
               POP  HL                HL := record-number.
               CALL 0F07,MAKE_FREE    Erase the entry.
0D0F ERASE_M_E JP   0368,CLOSE_CHN    Close the 'CAT'-file.


THE 'FORMAT THE 'M' CHANNEL' SUBROUTINE
On entry DE points to the start of the parameter  block  and  BC
holds the length. The disk is FORMATted and  the  'CAT'-file  is
written to the disk.

0D12 FORMAT_M  LD   HL,(PROG)         HL := start of new channel
               DEC  HL
               PUSH HL                Store it.
               LD   HL,(ERR-SP)       Also store the address  of
               PUSH HL                the error routine.
               LD   (ERR-NR),+2F      Signal: 'Disk I/O error'.
               CALL 0D6B,FORMATCHK    Disk FORMATted already?
               POP  HL                Restore the error  routine
               LD   (ERR-SP),HL       address.
               LD   A,(ERR-NR)        Fetch error status.
               CP   +2F               Still 'Disk I/O error'?
               JP   NZ,016D,CONTSIG_2 Report new error if not.
               POP  IX                IX := start of the channel
               LD   A,(IX+07)         A := drive-number.
               LD   B,+06             Signal: format.
               CALL 0FE5,CAL_PHY      Format the disk.

Now create the two catalog entries. The first holds  the  start-
and end-block of the'CAT'-file, the number of bytes in the  last
block and the disk-name. The second  is  an  endmarker  and  has
+FFFF as it's 5th and 6th byte and the total number of blocks on
the disk as it's 3rd and 4th byte. On entry BC holds the  block-
size, DE the last block-number of  the  'CAT'-file  and  HL  the
total number of blocks on the disk that are  accessible  to  the
system. The extra block with block-number -1 is not counted.

0D38 FORMAT_M2 DEC  BC                BC := block-size - 1.
               LD   (5C9C),BC         Store it.
               LD   (5CA0),DE         DE = catalog-size.
               PUSH HL                HL = number of blocks.
               LD   DE,+FFE7          This is -25d.
               CALL 0FA1,HL:=IX+DE    HL := disk-name in channel
               LD   DE,+5CA2          DE := file-name in record.
               LD   BC,+000A          Copy 10 characters.
               LDIR
               LD   (5C9E),BC         Zero the start-block.
               INC  HL                HL points to the 2nd entry
               CALL 0E2E,PUT_12FF     Put 12 FF's after HL.
               CALL 0EF6,WRITE_REC    Write the record on disk.
               POP  HL                HL := number of blocks.
               LD   (5C9E),HL         Store it.
               LD   HL,+FFFF          HL := endmarker.
               LD   (5CA0),HL         Store it too.
               CALL 0EF6,WRITE_REC    Write the record on disk.
               JP   0368,CLOSE_CHN    Close the 'CAT'-file.


THE 'CHECK FOR A FORMATTED DISK' SUBROUTINE
The catalog is read and when this is impossible  then  the  disk
has not been FORMATted yet. (or has a different format).

0D6B FORMATCHK LD   HL,+1708          If an error  occurs,  then
               PUSH HL                return to the calling rou-
               LD   (ERR-SP),SP       tine, even  when  the  ROM
                                      has been paged out.  (1708
                                      is PAGE_IN)
               LD   L,+80             Signal: find configuration
               CALL 0DD4,MAKE_CAT     Make a 'CAT' channel.
               CALL 0D82,CHECK_2      Try to read the catalog.
               POP  HL                Clear the stack.
               RET  Z                 Return if no errors.
               LD   (ERR-NR),+0C      Else report 'BREAK -  CONT
               RET                    repeats'.
0D82 CHECK_2   CALL 0F45,POS_START    Point to the first record.
               CALL 0EE3,READ_REC     Read it.
               LD   A,+FD             Open channel 'K'.
               RST  0010,CAL_SPEC
               DEFW +1601,OPEN_CHAN
               LD   A,+E0             Print 'Destroy "'.
               CALL 05EB,P_ERR_MSG
               LD   DE,+5CA2          DE := start of disk-name
               LD   B,+0A             10 characters in a name.
0D97 CHECK_3   LD   A,(DE)            Read a character.
               CP   +20               Not a control character?
               CALL NC,0CEE,PRINT_A   Then print it.
               INC  DE                Point to the next one.
               DJNZ 0D97,CHECK_3      Loop back until done all.
               LD   A,+E1             Print '" ?' and wait for a
               CALL 0DB1,PR_&_WAIT    key.
               PUSH AF                Store the key.
               LD   A,+E4             Signal: destroy question.
               LD   B,+FF             Signal: no subtables.
               RST  0030,LOOKUP       Lookup the answer  key  in
               DEFB +0C               the error-message table.
               POP  AF                Restore the key.
               OR   +20               Force to lower case.
               CP   (HL)              Test the key.
               RET

Print the message in A and wait for a keypress. Return the value
of the key in A.

0DB1 PR_&_WAIT CALL 05EB,P_ERR_MSG    Print the message.
               SET  5,(TVFLAG)        Signal: clear bottom lines
               SET  3,(FLAGS)         Signal: 'L' mode.
               RST  0010,CAL_SPEC     Wait for a key.
               DEFW +15DE,WAIT_KEY
               RET                    Return with the key in A.

The 'CAT' subtable of table #04.
0DC0 TAB_4:CAT DEFW +0B6E,WRITE_CT    Write.
               DEFW +0B7C,READ_CT     Read.
               DEFW +0DCC,OPEN_CAT    Open.
               DEFW +0C63,PUT_BLOCK   Close.
               DEFW +0B81,LENGTH_M    Length.
               DEFW +0BB8,POSN_M      Position.

The 'CAT' channel is the same as the 'm'  channel,  except  that
the file-name is not initialised, so this channel  could  easily
be changed into a 'm' channel.


THE 'OPEN THE 'CAT' CHANNEL' SUBROUTINE
On entry DE points to the start of the parameter block, BC holds
the length and A holds the OPEN flags. The  channel  is  created
and the info is stored in it. Sometimes the space of  the  'CAT'
channel is used as the 'm' channel. Then the info in the channel
is changed. Therefor the parameter block should  be  allowed  to
hold a file-name. The  first  part  of  the  routine  adds  this
file-name to the parameter block.

0DCC OPEN_CAT  LD   HL,+000A          10 bytes in a file-name.
               ADD  HL,BC             Add the parameter length.
               LD   B,H               BC := length of the  'new'
               LD   C,L               parameter block.
               LD   L,+00             Signal:  read  the  format
                                      from the disk.
0DD4 MAKE_CAT  PUSH BC                BC = length of parameters.
               PUSH DE                DE = start of parameters.
               PUSH HL                L = format check type.
               CALL 0863,TEST_C       Test the channel info.
               DEFB +81               A drive-number is needed.
               DEFB +F6               File-name needed and <= 10
                                      characters in it.
               JR   NC,0DE5,MAKE_CAT2 Skip if no errors.
               JR   NZ,0DE2,REPORT_F  Skip if error in file-name
0DE0 REPORT_c  RST  0008,SH_ERROR     Report   'Invalid    drive
               DEFB +2B               number.
0DE2 REPORT_F  RST  0010,CAL_SPEC     Report 'Invalid filename'.
               DEFW +1765,REPORT_F
0DE5 MAKE_CAT2 POP  BC                C := format check type.
               POP  DE                DE := start of parameters.
               PUSH DE                Store it again.
               INC  DE                Point to the 2nd location.
               LD   A,(DE)            A := drive-number.
               LD   B,+04             Signal: enquire.
               CALL 0FE5,CAL_PHY      Inquire the disk.
               POP  DE                DE := start of parameters.
               POP  HL                HL := length of parameters
               XOR  A
               PUSH BC                BC = block-size.
               PUSH HL                Store length of parameters
               LD   HL,+0021          HL := channel-length.
               ADD  HL,BC
               POP  BC                BC := length of parameters
               CALL 0818,MAKE_CHAN    Make the 'CAT' channel.
               DEFB +81               A drive-number is needed.
               DEFB +F6               A file-name is needed with
                                      <= 10 characters.
               LD   DE,+FFF1          This is -15d.
               CALL 0FA1,HL:=IX+DE    HL := channel start + 18d.
               POP  BC                BC := block-size.
               LD   (HL),C            Store it  in  the  channel
               INC  HL                descriptor.
               LD   (HL),B
               CALL 0E2E,PUT_12FF     Put 12 FF's after it.
               CALL 0EE3,READ_REC     Find the right file.
               LD   HL,(5C9E)         HL := start-block of file.
               LD   A,H               Start-block <>  0?  (start
               OR   L                 of the 'CAT'-file)
               JP   NZ,1304,REPORT_h  Then report the error.
               LD   C,+10             Record-size of 'CAT' is 16

Store the record-size, the number of bytes in the last block and
the first- and end-block numbers in the descriptor.

0E18 MAKE_CAT3 LD   DE,+FFF3          This is -13d.
               CALL 0FA1,HL:=IX+DE    HL := channel start + 20d.
               LD   (HL),C            Store the record-size.
               INC  HL
               LD   (HL),B
               INC  HL
               EX   DE,HL             Move the number  of  bytes
               LD   HL,+5C9C          in the last block and  the
               LD   BC,+0006          first- and end-block  num-
               LDIR                   bers to the descriptor.
               LD   C,+02             Signal: point to record 2.
               RET


THE 'STORE 12 FF'S AFTER HL' SUBROUTINE
HL is incremented and the next 12 bytes are made +FF.  The  13th
byte is made 0.

0E2E PUT_12FF  LD   B,+0C             Count 12 bytes.
0E30 PUT_12FF2 INC  HL                Increase address.
               LD   (HL),+FF          Store the +FF in it.
               DJNZ 0E30,PUT_12FF2    And repeat.
               INC  HL                Clear the 13th byte.
               LD   (HL),B
               RET


THE 'SEARCH THE CATALOG FROM THE START' SUBROUTINE
The 'CAT'-file is opened and searched from  the  start  for  the
file-name.

0E38 CATSEARCH LD   L,+00             Signal:  read  the  format
                                      from the disk.
               CALL 0DD4,MAKE_CAT     Make the 'CAT' channel.


THE 'FIND THE FILE-RECORD' SUBROUTINE
On entry BC holds  the  record-number  in  the  'CAT'-file.  The
catalog is searched for a file whose name is  the  same  as  the
name that is stored at address (IX+08). On exit the  carry  flag
is SET when the file has been found. If found, then BC holds the
record-number of that file.

0E3D FIND_FILE PUSH BC                Store record-number.
0E3E FIND_FIL2 CALL 0EE3,READ_REC     Fetch the file-record.
               LD   HL,(5CA0)         Is it the last record?
               LD   A,H
               AND  L                 (RESET the carry flag)
               INC  A                 SET the zero flag if so.
               POP  BC                BC := record-number.
               INC  BC                Point to the next record.
               RET  Z                 Return if it was the last.
               PUSH BC                Store the record-number.
               LD   DE,+FFE7          This is -25d.
               CALL 0FA1,HL:=IX+DE    HL := channel start + 8.
                                      (= start of file-name)
               LD   BC,+000A          10 characters in a name.
               LD   DE,+5CA2          DE := file-name in 'CAT'.
0E57 FIND_FIL3 LD   A,(DE)            Get a character.
               CPI                    Does  it  match  with  the
               JR   NZ,0E3E,FIND_FIL2 record? Loop back if not.
               INC  DE                Point to the next one.
               JP   PE,0E57,FIND_FIL3 Loop back until BC = 0.
               POP  BC                BC := next record-number.
               DEC  BC                BC := right record-number.
               SCF                    Signal: found.
               RET


THE 'GET FREE SPACE ON THE DISK' SUBROUTINE
On entry BC holds the record-number of a file, or when  no  such
file is present, the record-number of the endmarker. If BC holds
the record-number of the endmarker and the endmarker is  at  the
end of the catalog file, then there is no room in  the  catalog,
else a new file-record can be created, or  the  descripted  file
can be replaced. The file-name is skipped and the length of  the
gap between each two files is found. The largest gap  is  stored
and when two gaps of equal size are found, then the  second  gap
is used. On exit BC holds the length of the biggest gap  and  DE
holds the record-number of the start of the gap.

0E64 GET_FREE  CALL 0F48,POSN_BC      Position the 'CAT'-file.
               LD   BC,+1001          16 bytes, to read.
0E6A GET_FREE2 PUSH BC
               CALL 0B7C,READ_CT      Read a byte.
               JR   NC,0EA2,REPORT_f  Report the error if at end
               POP  BC
               DJNZ 0E6A,GET_FREE2    Read next byte.
               CALL 0F48,POSN_BC      Position the 'CAT'-file at
               CALL 0EE3,READ_REC     BC (1) and read the record
               LD   DE,+0000          DE := start-record of gap.
               PUSH DE                Stack it.

On entry the stack holds the record-number of the file after the
gap and DE holds the length of the gap. The length of  the  next
gap is calculated and used as the next maximal gap.

0E7D FREE_LOOP PUSH DE                DE = length of max gap.
               LD   HL,(5CA0)         HL := last block of file.
               LD   A,H
               OR   L
               INC  A                 Endmarker of 'CAT'-file?
               JR   Z,0E9D,FREE_EXIT  Then exit.
               PUSH HL                Store last block position.
               INC  BC                Increase record-number.
               CALL 0EE3,READ_REC     Read the next record.
               LD   HL,(5C9E)         HL := start of new file.
               POP  DE                DE := end of previous file
               AND  A
               SBC  HL,DE             HL := length of gap.
               POP  DE                DE := length of max gap.
               SBC  HL,DE             New maximum found?
               JR   C,0E7D,FREE_LOOP  Loop back if not.
0E97 NEW_MAX   ADD  HL,DE             Restore gap-length.
               EX   DE,HL             Pass it to DE.
               POP  HL                Delete 'old' max gap.
               PUSH BC                BC = start of 'new' gap.
               JR   0E7D,FREE_LOOP    Loop back.
0E9D FREE_EXIT POP  BC                BC := length of max gap.
               DEC  BC
               POP  DE                DE := start of the gap.
               DEC  DE
               RET
0EA2 REPORT_f  RST  0008,SH_ERROR     Report 'No room on disk'.
               DEFB +2F


THE 'MAKE A FILE IN THE CATALOG' SUBROUTINE
On entry HL holds the  first  block-number,  DE  the  number  of
records in the 'CAT'-file, BC  the  record-number  of  the  file
after the gap, mem-0 the size of the gap in bytes and mem-1  the
required length of the file.

0EA4 MAKE_FILE PUSH BC                BC = record after gap.
               PUSH DE                DE = no. records in 'CAT'.
               LD   (5C9E),HL         HL = first block.
               PUSH HL
               CALL 0F51,STCK_SIZE    Stack the block-size.
               RST  0028,FP_CALC      block-size (say B)
               DEFB +0F               (lengthbyte)
               DEFB +E1,get-mem-1     B, length (say L)
               DEFB +37,greater-0     B, (0/1)  (file-size > 0?)
               DEFB +00,jump-true              (then don't exit)
               DEFB +04,TO 0EB6,MAKE_2
               DEFB +38,end-calc      B
               XOR  A                 Clear A and carry flag.
               RET                    Jump indirectly to 0EBC.
0EB6 MAKE_2    DEFB +E1,get-mem-1     B, L
               DEFB +A1,stk-one       B, L, 1
               DEFB +03,subtract      B, L - 1     (also byte 0)
               DEFB +01,exchange      L - 1, B
               DEFB +32,n-mod-m       (file-size MOD block-size)
               DEFB +38,end-calc
0EBC MAKE_3    JR   NC,0EA2,REPORT_f  Report error if size=0.
               RST  0010,CAL_SPEC
               DEFW +1E99,FIND_INT2   BC := no. blocks in file.
               POP  HL                HL := first block of file.
               ADD  HL,BC             HL := last block of file.
               LD   (5CA0),HL         Store it.
               RST  0010,CAL_SPEC     BC := no.  bytes  in  last
               DEFW +1E99,FIND_INT2   block - 1.
               LD   (5C9C),BC         Store it too.
               LD   DE,+FFE7          This is -25d.
               CALL 0FA1,HL:=IX+DE    HL := channel start + 8.
               LD   DE,+5CA2          DE := start of file-name.
               LD   BC,+000A          10 characters in a name.
               LDIR                   Copy the file-name too.
               POP  HL                HL := no. records in 'CAT'
               POP  BC                BC := record-number of new
               CALL 0F07,MAKE_FREE    file. Make room for it.
               CALL 0F48,POSN_BC      Position the 'CAT'-file.


THE 'READ A FILE-RECORD FROM THE CATALOG' SUBROUTINE
On entry BC holds the record-number of the file to be read.  The
16 bytes of the current record are stored at address  5C9C,  and
the next record is made the current.

0EE3 READ_REC  PUSH BC                Store the record-number.
               LD   HL,5C9C           HL := startaddress of data
               LD   B,+10             16 bytes in a record.
0EE9 READ_REC2 PUSH BC                Store the counter.
               PUSH HL                Store load-address.
               CALL 0F3F,READ_CAT     Read a byte.
               POP  HL                Retrieve load-address  and
               LD   (HL),A            store the read byte.
               INC  HL                Point to next address.
               POP  BC                Restore counter.
               DJNZ 0EE9,READ_REC2    Loop back until done all.
               POP  BC                Restore the record-number.
               RET


THE 'WRITE A FILE-RECORD TO THE CATALOG' SUBROUTINE
The file-record stored at address 5C9C to  5CAB  is  written  to
disk.

0EF6 WRITE_REC LD   HL,+5C9C          HL := startaddress of data
               LD   B,+10             16 bytes in a record.
0EFB WRITE_RC2 PUSH BC                Store the counter.
               LD   A,(HL)            Get a byte to write.
               INC  HL                Point to next address.
               PUSH HL                Store save-address.
               CALL 0B6E,WRITE_CT     Write the byte to disk.
               POP  HL                Restore save-address.
               POP  BC                Restore counter.
               DJNZ 0EFB,WRITE_RC2    Loop back until done all.
               RET


THE 'MAKE OR FREE SPACE IN THE CATALOG' SUBROUTINE
The routine is called on three  occations,  the  first  when  an
entry should be deleted, the second  when  an  entry  should  be
created, and the third when  an  entry  should  be  replaced  by
another. When a file is  saved  with  a  name  that  is  already
present on the disk, the old file-record should be  deleted,  or
overwritten. When creating an entry, HL holds the  total  number
of file-records in the 'CAT'-file and BC  the  record-number  of
the file to be created. When deleting an  entry,  BC  holds  the
record-number of the endmarker and HL the record-number  of  the
file to be deleted. When a entry should be  replaced,  HL  holds
the record-number of the second file with the same name  and  BC
the record-number of the file to be created. In all three cases,
BC points to the record to be moved and the moving  is  finished
when BC reaches HL.

0F07 MAKE_FREE AND  A
               SBC  HL,BC
               ADD  HL,BC             If an entry should be cre-
               JR   NC,0F0E,MK_FR_2   ated then skip.
               DEC  BC
0F0E MK_FR_2   PUSH BC                BC = moving pointer.
               PUSH HL                HL = end pointer.

The record is found and exchanged with the entry in memory.

0F10 MK_FR_3   CALL 0F48,POSN_BC      Position the 'CAT'-file.
0F13 MK_FR_4   PUSH BC                Store this position.
               LD   DE,5C9C           DE := start in memory.
               LD   B,+10             16 bytes in a record.
0F19 MK_FR_5   PUSH BC                Store the counter.
               PUSH DE
               CALL 0F3F,READ_CAT     Read a byte from the 'CAT'
               POP  DE
               LD   B,A               Switch this byte with  the
               LD   A,(DE)            memory contents.
               LD   (HL),A
               LD   A,B
               LD   (DE),A
               INC  DE                Point to next byte in mem.
               SET  0,(IX+20)         Signal: change made.
               POP  BC                Restore the counter.
               DJNZ 0F19,MK_FR_5      Loop back until done all.
               POP  BC                BC := moving pointer.
               POP  HL                HL := end pointer.
               PUSH HL                Store end again.
               AND  A
               SBC  HL,BC             More records to go?
               JR   Z,0F3C,MK_FR_8    Step if not.
               JR   C,0F39,MK_FR_7    Step if not creating.
0F36 MK_FR_6   INC  BC                Move to  the  next  record
               JR   0F13,MK_FR_4      and repeat.
0F39 MK_FR_7   DEC  BC                Move  to  previous  record
               JR   0F10,MK_FR_3      and repeat.
0F3C MK_FR_8   POP  HL                HL = end pointer.
               POP  BC                BC = moving pointer.
               RET


THE 'READ FROM THE 'CAT' CHANNEL' SUBROUTINE
The pointers in the channel descriptor  are  used  to  determine
which byte is to be read. On exit the A register holds this byte
and HL the address in the buffer of that byte.

0F3F READ_CAT  CALL 0B7C,READ_CT      Read a byte.
               RET  C                 Return if no error occured
               RST  0008,SH_ERROR     Report 'Disk I/O error'.
               DEFB +2F


THE 'POSITION THE 'CAT'-FILE AT THE START' SUBROUTINE
The 'start' is signalled and the file is positioned.

0F45 POS_START LD   BC,+0001          Record-number := 1.


THE 'POSITION THE 'CAT'-FILE AT BC' SUBROUTINE
On entry BC holds the record-number. This number is stacked  and
the channel is positioned.

0F48 POSN_BC   PUSH BC                BC = record-number.
               RST  0010,CAL_SPEC     Move it to the  calculator
               DEFW +2D2B,STACK_BC    stack as well.
               CALL 0BB8,POSN_M       Position the 'm' channel.
               POP  BC                BC := record-number.
               RET


THE 'STACK THE BLOCK-SIZE' SUBROUTINE
The block-size is fetched from the channel descriptor and stored
on the calculator stack.

0F51 STCK_SIZE LD   C,(IX+12)         BC := block-size.
               LD   B,(IX+13)
               RST  0010,CAL_SPEC     Stack it.
               DEFW +2D2B,STACK_BC
               RET


THE 'END OF FILE TEST' SUBROUTINE
A test  is made whether the next  byte  may  be  read  from  the
channel and when so,  HL  is  returned  pointing  to  the  byte.
(possibly after saving the current block and/or loading the next
block).

0F5B ENDOFFILE LD   E,(IX+1E)         DE := position in  current
               LD   D,(IX+1F)         block.
               LD   C,(IX+1C)         BC := current block.
               LD   B,(IX+1D)
               LD   L,(IX+1A)         HL := last block.
               LD   H,(IX+1B)
               AND  A
               SBC  HL,BC             Past the last block?
               CCF                    (signal: error)
               RET  NC                Then return.
               JR   NZ,0F7F,EOF_3     Skip if current <> last.
0F74 EOF_2     LD   L,(IX+16)         HL := number of  bytes  in
               LD   H,(IX+17)         the last block.
               AND  A
               SBC  HL,DE             Past last position in last
               CCF                    block? (signal: error)
               RET  NC                Then return.
0F7F EOF_3     PUSH DE                Store position.
               LD   L,(IX+12)         HL := block-size.
               LD   H,(IX+13)
               SCF                    Include byte 0.
               SBC  HL,DE             Past the end of the block?
               JR   NC,0F94,EOF_5     Skip if not.
0F8B EOF_4     INC  BC                Load the next block.
               CALL 0FAA,READ_NEXT
               LD   DE,+0000          Position in block := 0.
               POP  BC                Erase unwanted stack entry
               PUSH DE                Store position.
0F94 EOF_5     CALL 0FA1,HL:=IX+DE    HL := address of byte.
               POP  BC                BC := current position.
               INC  BC                BC := new current position
               LD   (IX+1E),C         Store it.
               LD   (IX+1F),B
               SCF                    Signal: succes.
               RET


THE 'HL := IX + DE + 33' SUBROUTINE
On entry IX holds the start of the channel and DE a  value  that
has to be added to IX and stored in HL. On  exit  HL  holds  the
required  position  (depending  on  DE  only)  in  the   channel
descriptor. This routine is used to make HL point to the various
variables in the descriptor (when -34 < DE < 0) or to a location
in the buffer (when DE >= 0).

0FA1 HL:=IX+DE PUSH IX
               POP  HL                HL := IX.
               ADD  HL,DE             HL := IX + DE.
               LD   DE,+0021          DE := 33.
               ADD  HL,DE             HL := IX + DE + 33.
               RET


THE 'READ NEXT BLOCK FROM DISK' SUBROUTINE
On entry BC holds the number of the block to be loaded. If BC is
not the current block and less than or equal to the  last  block
and input is allowed, then the block is loaded from disk.

0FAA READ_NEXT LD   L,(IX+1C)         HL := current block.
               LD   H,(IX+1D)
               AND  A
               SBC  HL,BC             BC = current block?
               RET  Z                 Ready if so.
0FB4 READ_NXT2 PUSH BC                Store the 'new' block num-
               CALL 0C63,PUT_BLOCK    ber while saving the 'old'
               POP  BC                current block on disk.
               BIT  3,(IX+02)         Input allowed?
               JR   Z,0FDE,RD_N_END   Skip if not.
0FBF READ_NXT3 LD   L,(IX+1A)         HL := last block.
               LD   H,(IX+1B)
               AND  A
               SBC  HL,BC             BC = past last block?
               JR   C,0FDE,RD_N_END   Skip if so.
0FCA READ_NXT4 PUSH BC                Store block-number.
               LD   BC,+0200          Signal: read a whole block
               LD   DE,+0000
               CALL 0FA1,HL:=IX+DE    HL := start of buffer.
               EX   DE,HL             Pass it to DE.
               POP  HL                HL := block-number to load
               PUSH HL                Keep a copy.
               LD   A,(IX+07)         A := drive-number.
               CALL 0FE5,CAL_PHY      Load the block.
0FDE RD_N_END  POP  BC                BC := block-number.
               LD   (IX+1C),C         Store it.
               LD   (IX+1D),B
               RET


THE 'CALL A PHYSICAL DEVICE' SUBROUTINE
On entry A holds the drive-number or the device-number (+81  for
the printer port, +82 for the joystick port)  and  B  holds  the
offset (0 for write, 2 for read, 4 for inquire and 6 for format)
See the CAL_PHY subroutines for  more  information  on  how  the
registers should be set up.

0FE5 CAL_PHY   PUSH IX                Store  IX  while   calling
               CALL 0FED,CAL_PHY_2    further.
               POP  IX
               RET

IX is made to hold the startaddress of  the  disk-info  for  the
correct  drive  and  HL  the  startaddress  of  the  appropriate
routine. A jump is made to that routine.

0FED CAL_PHY_2 PUSH HL                Save main registers.
               PUSH DE
               PUSH BC
               LD   B,+FF             Signal: no subtables.
               RST  0030,LOOKUP       Lookup the startaddress of
               DEFB +0A               the disk-info in the table
               PUSH HL
               POP  IX                IX := start of disk-info.
               POP  BC                B := the offset.
               PUSH BC                Store it again.
               RST  0030,LOOKUP       Lookup the startaddress in
               DEFB +08               the device table.
               POP  BC                Restore the main registers
               POP  DE
               EX   (SP),HL           Jump  indirectly  to   the
               RET                    handling routine.

The 'CODE' subtable of table #04
0FFF TAB_4:COD DEFW +101E,WRITE_COD   Write.
               DEFW +1014,READ_CODE   Read.
               DEFW +100B,OPEN_CODE   Open.
               DEFW +001C             Close (return).
               DEFW +0532,USE_M1      Length (use -1).
               DEFW +102D,POSN_CODE   Position.

The 'CODE' channel is 9 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.
bytes 7 and 8 : the pointed address.


THE 'OPEN THE 'CODE' CHANNEL' SUBROUTINE
The space is created and the channel-info checked for validity.

100B OPEN_CODE LD   HL,+0009          Reserve 9 bytes.
               CALL 0818,MAKE_CHAN    Make the channel.
               DEFB +80               No check on extra parms.
               DEFB +01               Filename of 1 char allowed
               RET


THE 'READ FROM THE 'CODE' CHANNEL' SUBROUTINE
The pointer is fetched, the byte is read and  the  next  address
pointed to. On exit A holds the read byte.

1014 READ_CODE LD   B,(IX+08)         BC := pointed address.
               LD   C,(IX+07)
               LD   A,(BC)            The actual read.
               SCF                    Signal: succes.
               JR   1025,CODE_END     Step forward.


THE 'WRITE TO THE 'CODE' CHANNEL' SUBROUTINE
On entry A holds the byte to be written. The pointer is fetched,
the byte written and the pointer incremented.

101E WRITE_COD LD   B,(IX+08)         BC := pointed address.
               LD   C,(IX+07)
               LD   (BC),A            The actual write.
1025 CODE_END  INC  BC                Increment pointer.
1026 CODE_END2 LD   (IX+08),B         And store it again.
               LD   (IX+07),C
               RET


THE 'POSITION THE 'CODE' CHANNEL' SUBROUTINE
On entry the calculator stack holds the position. The  value  is
fetched and stored in the descriptor.

102D POSN_CODE RST  0010,CAL_SPEC     BC := (new) position.
               DEFW +1E99,FIND_INT2
               JR   1026,CODE_END2    Step back to store it.

The 'd' subtable of table #04
1032 TAB_4:D   DEFW +104E,WRITE_D     Write.
               DEFW +1060,READ_D      Read.
               DEFW +103E,OPEN_D      Open.
               DEFW +1076,CLOSE_D     Close.
               DEFW +106E,LENGTH_D    Length.
               DEFW +0BF3,REPORT_J    Position (error-message).

The 'd' channel is 9 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 output drive-number.
byte  8       : the input drive-number.

The 'd' channel is only used by the  MOVE  command,  however  it
should act as a normal channel; you should be able to  read  and
write to it. The MOVE-command first reads  the  bytes  from  the
input channel. This way the input drive-number is loaded as data
Next the data is stored  in  the  output  channel.  Finally  the
channel is closed.  The  close-routine  fetches  the  input-  an
output drive-numbers and moves the data from the input-drive  to
the output-drive, but only when the drives have the same format.


THE 'OPEN THE 'D' CHANNEL' SUBROUTINE
The channel is created and the parameters checked for validity.

103E OPEN_D    LD   HL,+0009          Reserve 9 bytes.
               CALL 0818,MAKE_CHAN    Make the channel.
               DEFB +81               A drive-number is needed.
               DEFB +00               But no file-name allowed.
               LD   (IX+08),+00       Signal: no input drive-num
                                      ber present.
               JP   C,0902,REPORT_A   Report 'Invalid  argument'
               RET                    if an error occured.


THE 'WRITE TO THE 'D' CHANNEL' SUBROUTINE
On entry A holds the input drive-number. It is stored as byte  8
of the output channel. Only 1 byte is written. If  more  than  1
byte is written, then the channel  is  not  being  used  by  the
'MOVE' command, so an error should be reported.

104E WRITE_D   LD   B,(IX+08)         Has the input drive-number
               INC  B                 already been specified?
               DEC  B
               JR   NZ,1059,WR_D_ERR  Then report the error.
               LD   (IX+08),A         Store the drive-number.
               RET
1059 WR_D_ERR  LD   (IX+07),+00       Clear the output drive-num
               JP   0BF3,REPORT_J     ber  and  report  'Invalid
                                      I/O device'.


THE 'READ FROM THE 'D' CHANNEL' SUBROUTINE
A is made to hold the input drive-number. If A = 0  then  it  is
the second byte read and the end of the input-file is signalled.

1060 READ_D    LD   A,(IX+07)         A := output drive-number.
               AND  A                 Skip if no drive-number is
               JR   Z,106C,RD_D_ERR   present.
               LD   (IX+07),+00       Clear output drive-number.
               SCF                    Signal: success.
               RET
106C RD_D_ERR  INC  A                 A := 1; signal: no success
               RET


THE 'LENGTH OF THE 'D' CHANNEL' SUBROUTINE
The length of the channel is stored on the calculator stack.  It
is always 2^31.

106E LENGTH_D  RST  0028,FP_CALC      -
               DEFB +07               (lengthbyte)
               DEFB +34,stk-data      2^31
               DEFB +00
               DEFB +50, exponent +A0
               DEFB +00, (+00,+00,+00)
               DEFB +38,end-calc      2^31
               RET


THE 'CLOSE THE 'D' CHANNEL' SUBROUTINE
On entry IX holds the start of  the  channel.  If  this  is  the
channel that is used as the output channel, then  byte  7  holds
the output drive-number and byte 8  the  input  drive-number.  A
buffer is created in the workspace which is  made  as  large  as
possible. The buffer is filled with the data from the input disk
and emptied on the output disk. After all the  used  blocks  are
moved, the catalog-file is adapted to the new situation. All the
blocks are stored on consecutive block-numbers, so there is just
one gap; at the end. The pointers in the catalogfile still point
to the old positions of the files, so they have to  be  altered.
This is done at the end of the routine.

1076 CLOSE_D   LD   A,(IX+07)         A := output drive-number.
               AND  A                 Exit if no drive-number is
               RET  Z                 specified.
               LD   B,A               Store the drive-number.
               LD   A,(IX+08)         A := input drive-number.
               AND  A                 Exit too if no  drive-num-
               RET  Z                 ber is present here.
               CP   B                 input disk = output disk?
               JR   Z,10B5,CLOSE_DS   Then skip.
1084 CLOSE_D2  PUSH BC                Store output drive-number.
               LD   BC,+0400          Signal: inquire.
               CALL 0FE5,CAL_PHY      Inquire input-disk.
               POP  AF                A := output drive-number.
               PUSH IX                IX = input-channel start.
               PUSH AF                A = output drive-number.
               PUSH BC                BC = block-size input-disk
               PUSH HL                HL = no. blocks input-disk
               LD   BC,+0400          Signal: inquire.
               CALL 0FE5,CAL_PHY      Inquire output-disk.
               POP  DE                DE := no. blocks input.
               AND  A                 Test if the input and out-
               SBC  HL,DE             put disks  have  the  same
109B REPORT_n2 JP   NZ,0BF6,REPORT_n  no. blocks. Report  'Wrong
                                      disk' if not.
               POP  HL                HL := block-size input.
               SBC  HL,BC             Same block-size?
               JR   NZ,109B,REPORT_n2 Report 'Wrong disk' if not
10A3 CLOSE_D3  LD   DE,+FFE5          This is -27.
               CALL 11AD,CONV_CAT     Open the 'CAT'-file of the
                                      output disk.
               CALL 0D82,CHECK_2      Test if the disk has  been
                                      formatted already.
               JP   NZ,1545,REP_BREAK Jump out if not.
               CALL 0368,CLOSE_CHN    Close the 'CAT'-file.
               POP  BC
10B5 CLOSE_DS  POP  IX                IX := input-channel start.
               PUSH IX
               PUSH BC
10B8 CLOSE_D4  LD   DE,+FFE6          This is -26.
               CALL 11AD,CONV_CAT     Open the 'CAT'-file of the
                                      the input-disk.
               RST  0010,CAL_SPEC     Clear the workspace.
               DEFW +16BF,SET_WORK
               RST  0010,CAL_SPEC     BC := free memory.
               DEFW +1F1A,FREE_MEM
               LD   HL,+FF00          As large as possible.
               SBC  HL,BC             HL := bytes free in memory
               LD   B,H               Copy it to BC.
               LD   C,L
               LD   E,(IX+12)         DE := block-size.
               LD   D,(IX+13)
               SBC  HL,DE             Enough room for a block?
               JR   NC,10D7,CLOSE_D5  Then skip.
               LD   B,+FF             Try with less room.
10D7 CLOSE_D5  RST  0010,CAL_SPEC     Make the room.
               DEFW +0030,BC_SPACES
               LD   HL,+FFFF          Store startblock on disk.
               PUSH HL                (-1)

The blocks on the disk that are used are loaded into  the  work-
space until there are no more blocks, or the workspace is full.

10DE LOAD_LOOP LD   DE,(WORKSP)       DE := start of workspace.
               LD   BC,+0000          BC := blocks loaded (=0)
10E5 LOAD_LP2  PUSH BC
               CALL 1176,AT_END?      Load a block if available.
               POP  BC
               JR   C,10FF,SAVE_LOOP  Step if not.
               INC  BC                Include block in counter.
               PUSH DE                Store current position.
               LD   L,(IX+12)         HL := block-size.
               LD   H,(IX+13)
               ADD  HL,DE             HL := end of next block.
               LD   DE,(STKBOT)       DE := end of workspace.
               AND  A                 Enough  room  for  another
               SBC  HL,DE             block?
               POP  DE                (Restore current position)
               JR   C,10E5,LOAD_LP2   Then loop back.

The data is saved on  consecutive  blocks  on  the  output-disk.
There are BC blocks loaded in the workspace.

10FF SAVE_LOOP LD   DE,(WORKSP)       DE := start of workspace.
               POP  HL                HL := startblock on disk.
1104 SAVE_LP2  LD   A,B               No (more) blocks to save?
               OR   C
               JR   Z,1122,MOVE_LOOP  Then step out.
               DEC  BC                One block less to go.
               POP  AF                A := output drive-number.
               PUSH AF
               INC  HL                HL := new startblock disk.
               PUSH HL                Store it.
               PUSH BC                BC = no. blocks to go.
               PUSH DE                DE = position in workspace
               LD   BC,+0000          Signal: write a full block
               CALL 0FE5,CAL_PHY      Save the block.
               POP  HL                HL := pos. in workspace.
               LD   C,(IX+12)         BC := block-size.
               LD   B,(IX+13)
               ADD  HL,BC             HL := start of next block.
               EX   DE,HL             Pass it to DE.
               POP  BC                BC := no. blocks to go.
               POP  HL                HL := startblock on disk.
               JR   1104,SAVE_LP2     Loop back for the next one

Data is moved from the input- to the output-disk. If the end  of
the catalog-file has not yet been reached, then there  is  still
data to be moved.

1122 MOVE_LOOP POP  AF                A := output drive-number.
               PUSH AF
               PUSH HL                HL = last block saved.
               LD   HL,(5CA0)
               LD   A,H
               AND  L
               INC  A                 Last block in the catalog?
               JR   NZ,10DE,LOAD_LOOP Loop back if not.

Now the input channel can be closed. The records in  the  'CAT'-
file on the output-disk still have to be made to  point  to  the
right positions on the disk. All the data in the files  take  up
one big space without gaps, but the records are still  the  same
as they were on the input-disk, possibly with gaps between them,
so the catalog still has to be compressed.

112D CAT_LOOP  POP  HL                Clear the stack.
               POP  AF
               CALL 0368,CLOSE_CHN    Close the input channel.
               POP  IX                IX := start of new  output
               PUSH IX                channel.
               LD   DE,+FFE5          This is -27.
               CALL 11AD,CONV_CAT     Create a catalog channel.
               LD   DE,+0000          DE := gap-length (0).
               LD   BC,+0002          BC := record-number (2).

Ajust the start-block.

1142 AJST_STRT LD   HL,(5C9E)         HL := old start-block.
               ADD  HL,DE             Add gap-length.
               LD   (5C9E),HL         Store new start-block.

Ajust the end-block.

1149 AJUST_END LD   HL,(5CA0)         HL := old end-block.
               ADD  HL,DE             Add gap-length.
               LD   (5CA0),HL         Store new end-block.
               PUSH HL                Save end of old block.
               PUSH BC                Save record-number.
               DEC  BC                Correct record-number.
               CALL 0F48,POSN_BC      Position the 'CAT'-file.
               CALL 0EF6,WRITE_REC    Write record to disk.
               CALL 0EE3,READ_REC     Read the next record.
               POP  BC                BC := record-number.
               INC  BC                Point to next record.

Form the next gap-length.

115E NEXT_GAP  POP  HL                HL := old end-block.
               LD   DE,(5C9E)         DE := new start-block.
               AND  A
               SBC  HL,DE             HL := - gap-length.
               INC  HL                HL := 1 - gap-length.
               EX   DE,HL             Pass it to DE.
               LD   HL,(5CA0)         HL := end-block.
               LD   A,H
               AND  L
               INC  A                 Last block done?
               JR   NZ,1142,AJST_STRT Loop back if not.
1170 MOVE_EXIT CALL 0368,CLOSE_CHN    Close the 'CAT' channel.
               POP  IX                Clear the stack.
               RET


THE 'AT-END TEST AND LOAD THE NEXT BLOCK' SUBROUTINE
Addresses #5C9C to #5CAB are used  to  hold  the  current  file-
record. Address #5C9E holds the number of  blocks  that  can  be
loaded into the buffer. After loading that block, the  value  on
this address is increased. When the last block has been  loaded,
the next file-record should be loaded from the catalog.

1176 AT_END?   LD   BC,(5C9E)         BC := current block.
               LD   HL,(5CA0)         HL := last block.
               AND  A
               SBC  HL,BC             Current block <> last?
               JR   NC,1191,LOAD_NEXT Step if so.
               PUSH DE                DE = startaddress to load.
               CALL 0EE3,READ_REC     Find next file-record.
               POP  DE
               LD   HL,(5CA0)         HL := last block number.
               LD   A,H
               AND  L
               INC  A                 Is it the end-marker?
               JR   NZ,1176,AT_END?   Load the  first  block  of
                                      the next file if not.
               SCF                    Signal: no success.
               RET

The current block is loaded and  the  next  block  is  made  the
current block.

1191 LOAD_NEXT LD   H,B               HL := current block.
               LD   L,C
               INC  BC                BC := next block.
               LD   (5C9E),BC         Store it in the descriptor
               LD   BC,+0200          Signal: load a full block.
               LD   A,(IX+07)         A := drive-number.
               PUSH DE                DE = startaddress to load.
               CALL 0FE5,CAL_PHY      Load the block.
               POP  HL                HL := startaddress to load
               LD   C,(IX+12)         BC := block-size.
               LD   B,(IX+13)
               ADD  HL,BC             HL := new startaddress.
               EX   DE,HL             Pass it to DE.
               AND  A                 Signal: success.
               RET


THE 'OPEN A 'CAT'-FILE ON THE CURRENT 'M' CHANNEL' SUBROUTINE
Byte 7 or 8 of the channel-descriptor  holds  the  drive-number.
This byte is used as the parameter block for the creation of the
'CAT'-file (Together with the  previous  which  has  no  meaning
here). On entry DE holds the displacement in the descriptor (-27
or -26), so the first CALL makes HL point to the right byte -1.

11AB CONV_CAT  CALL 0FA1,HL:=IX+DE    HL := parameter start.
               EX   DE,HL             It is needed in DE.
               LD   BC,+0002          BC := parameter length.
               CALL 0DCC,OPEN_CAT     Create the 'CAT'-file.
               LD   (IX+04),+EF       Device-name := 'CAT'.
               RET

The 'j' subtable of table #04

11BC TAB_4:J   DEFW +0BF3,REPORT_J    Write (error-message).
               DEFW +11D4,READ_J      Read.
               DEFW +11C8,OPEN_J      Open.
               DEFW +001C             Close (return).
               DEFW +0BF3,REPORT_J    Length (error-message).
               DEFW +0BF3,REPORT_J    Position (error-message).

The 'j' 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 joystick-number : 1 for QWERT and 67890.
                                      2 for QWERT and 12345.


THE 'OPEN THE 'J' CHANNEL' SUBROUTINE
The channel is created.

11C8 OPEN_J    LD   HL,+0008          Reserve 8 bytes.
               CALL 0818,MAKE_CHAN    Make the channel.
               DEFB +FE               Status 1 or 2.
               DEFB +00               No file-name allowed.
               JP   C,0902,REPORT_A   Report an error if found.
               RET


THE 'READ FROM THE 'J' CHANNEL' SUBROUTINE
The registers are setup to read from  the  joystick-port  and  a
jump is made to CAL_PHY.

11D4 READ_J    LD   B,+02             Signal: read.
               LD   H,(IX+07)         H := status.
               LD   A,+82             Signal: joystick port.
11D9 JP_PHY    JP   0FE5,CAL_PHY      Read the port.


THE 'FORMAT THE 'J' CHANNEL' SUBROUTINE
On entry DE points to the parameter block. The registers are set
up to format the 'j' channel and a jump is made to CAL_PHY.

11DE FORMAT_J  PUSH DE                DE = parameter start.
               CALL 0863,TEST_C       Test the parameters.
               DEFB +0F               status < 16.
               DEFB +00               No file-name allowed.
               JP   C,0902,REPORT_A   Report an error if found.
               POP  HL                HL := parameter start.
               INC  HL
               LD   H,(HL)            H := status.
               LD   B,+06             Signal: format.
               JR   11D9,JP_PHY       Format the port.

The joystick port subtabe of table #08.
11EE TAB_8:J   DEFW +1246,REPORT_k    Write (error-message).
               DEFW +11F6,READ_J      Read.
               DEFW +001C             Inquire (return).
               DEFW +122F,FORMAT_J    Format.


THE 'READ FROM THE 'J' CHANNEL' SUBROUTINE
On entry H holds the status-number of the 'j' channel.  H  is  1
for the QWERT-keys and interface 2 port 1, H is 2 for the QWERT-
keys and interface 2 port 2. Depending on whether the  joystick-
port was formatted yet (bit 7 of address #3000 SET), kempston or
interface 2 is read.

11F6 READ_J    LD   A,(3000)
               AND  +80               Joystick-port formatted?
               JR   NZ,1201,READ_J_F  Step if so.
11FD READ_J_NF IN   A,(+1F)           Read the kempston port.
               SCF                    Signal: success.
               RET
1201 READ_J_F  LD   A,H               Use interface 2.
               RLCA                   Bit 0 or 1 is SET; move it
               RLCA                   to bit 3 or 4 respectively
               RLCA
               OR   +E3               A holds +E7 (for H = 1) or
                                      +F3 (for H = 2), so port 1
                                      or 2 + QWERT-keys is used.
               IN   A,(+FE)           Read the port.
               CPL                    A bit is SET for pressed.
               AND  +1F               Keep only bits 0 - 5.
               DEC  H                 H = 2?
               JR   NZ,1223,PORT_2    Then handle port 2.

The bits are moved to their positions according to port 1.
(move ...RLDUF to ...FUDRL)

120F PORT_1    SRL  A                 'Fire' pressed?
               JR   NC,1215,PORT_1_NF Skip if not.
               OR   +40               Include 'fire'.
1215 PORT_1_NF SRL  A                 'Up' pressed?
               JR   NC,121B,PORT_1_NU Skip if not.
               OR   +10               Include 'up'.
121B PORT_1_NU SRL  A                 'Down' pressed?
               JR   NC,1221,READ_J_E  Skip if not.
               OR   +04               Include 'down'.
1221 READ_J_E  SCF                    Signal: success.
               RET

The bits are moved to their positions according to port 2.

1223 PORT_2    RRCA                   Swap left and right.
               RRCA                   (bits 0 and 1)
               SLA  A
               RL   A
               JR   NC,0221,READ_J_E
               OR   +02
               SCF                    Signal: success.
               RET


THE 'FORMAT THE JOYSTICK PORT' SUBROUTINE
On entry H holds the status for formatting.  Bit  7  of  address
#3000 is returned SET when the joystick-port is formatted.

122F FORMAT_J  LD   A,H               A := status.
               LD   HL,+3000
               SET  7,(HL)            Signal: not formatted.
               AND  A                 Status = 0?
               RET  Z                 Then exit.
               RES  7,(HL)            Signal: formatted.
               RET

The disk subtable of table #08.
123A TAB_8:M   DEFW +1142,WRITE_M     Write.
               DEFW +1248,READ_M      Read.
               DEFW +1414,INQUIRE_M   Inquire.
               DEFW +132C,FORMAT_M    Format.


THE 'WRITE TO DISK' SUBROUTINE
On entry HL holds the block number,  DE  the  startaddress  from
where the data should be written, C the number of bytes,  A  the
drive number and IX points to the start of the disk-info table.

1242 WRITE_M   LD   B,+AF             Signal: write.
               JR   124A,RD_WR_M_1    Step to the routine.

1246 REPORT_k  RST  0008,SH_ERROR     Report 'Ram corrupt'.
               DEFB +57


THE 'READ FROM DISK' SUBROUTINE
On entry HL holds the block number,  DE  the  startaddress  from
where the data should be loaded, C the number of  bytes,  A  the
drive number and IX points to the start of the disk-info table.

1248 READ_M    LD   B,+8C             Signal: read.
124A RD_WR_M_1 PUSH DE                DE = startaddress.
               PUSH BC                C = number of bytes.
               EXX                    Save alternate registers.
               PUSH BC
               PUSH DE
               PUSH HL
               EXX
               PUSH HL                HL = block number.

The current disk number is fetched and when the the current  and
new drive have different drive numbers but  refer  to  the  same
physical drive, then the disks have to be swapped.

1252 RD_WR_M_2 LD   E,A               Store the drive number.
               LD   A,(3000)          A := current drive number.
               XOR  (IX+02)           Same side (left/right)?
               AND  (IX+08)           Same drive?
               JR   Z,127F,RD_WR_M_5  Then skip.
125E RD_WR_M_3 LD   HL,(CURCHL)       HL := start of channel.
               PUSH HL                Store it.
               PUSH DE                E = drive number.
               LD   A,+FD             Open channel K.
               RST  0010,CAL_SPEC
               DEFW +1601,CHAN_OPEN
               LD   A,+E2             Print 'Insert disk '
               CALL 05EB,P_ERR_MSG
               POP  BC                C := drive number.
               LD   B,+00             BC := drive number.
               RST  0010,CAL_SPEC     Print the drive number.
               DEFW +1A1B,OUT_NUM_1
               LD   A,+E3             Print ',  then  press  any
               CALL 0DB1,PR_&_WAIT    key' and wait for a key.
               CALL 1540,TEST_BREAK   Test the 'BREAK' key.
127B RD_WR_M_4 POP  HL                HL := start of channel.
               RST  0010,CAL_SPEC     Set the channel flags.
               DEFW +1615,CHAN_FLAG
127F RD_WR_M_5 CALL 14F1,TAB_START    HL := start of disk tables
               LD   D,A               D := current drive number.
               XOR  (IX+02)           Same side (left/right)?
               AND  (HL)
               PUSH AF                Store result.
               LD   A,(HL)            Get primary info
               OR   (IX+08)           OR virtual info
               LD   E,A               E := 00000111 or 00001011
               CPL                    A := 11111000 or 11110100
               AND  D
               LD   B,A
               LD   A,E
               AND  (IX+02)
               OR   B
               AND  +EF
               LD   (3000),A
               POP  AF
               JR   Z,12AA,RD_WR_M_7
               XOR  D
129E RD_WR_M_6 INC  HL
               RRA
               JR   NC,129E,RD_WR_M_6
               LD   A,(HL)            Read current trackregister
               LD   (2801),A          Pass it to the hardware.
               INC  A                 If at end, then restore.
               CALL Z,14D3
12AA RD_WR_M_7 POP  HL                HL := block number.
               LD   C,(IX+04)         BC := no. extra blocks.
               LD   B,(IX+05)
               ADD  HL,BC             HL := physical block num.
               LD   B,H               Copy it to BC.
               LD   C,L
               RST  0010,CAL_SPEC     Stack it.
               DEFW +2D2B,STACK_BC
               LD   A,(IX+01)         A := number of sectors.
               RST  0010,CAL_SPEC     Stack it too.
               DEFW +2D28,STACK_A
               RST  0010,CAL_SPEC     blocknum MOD sectors
               DEFW +36A0,n-mod-m
               LD   A,(IX+00)         A := number of tracks.
               RST  0010,CAL_SPEC     Stack it too.
               DEFW +2D28,STACK_A
               RST  0010,CAL_SPEC     (bl MOD sect) MOD tracks.
               DEFW +36A0,n-mod-m     sect, track, side.
               RST  0010,CAL_SPEC     A := side of disk.
               DEFW +1E94,FIND_INT1
               AND  A                 Handling side 1?
               JR   Z,12DD,RD_WR_M_9  Then skip.
12D2 RD_WR_M_8 DEC  A                 Handling side 2?
               JR   NZ,1304,REPORT_h  Report 'Disk I/O error' if
               BIT  4,(IX+02)         not. Has disk 2 sides?
               JR   Z,1304,REPORT_h   Report the error if not.
               LD   HL,+3000          Signal: handling side 2.
               SET  4,(HL)
12DD RD_WR_M_9 RST  0010,CAL_SPEC     B := track number.
               DEFW +2307,STK_TO_BC   C := sector number.
               LD   A,C               A := track number.
               ADD  A,(IX+03)         Add extra sectors.
               LD   (2802),A          Signal: go to sector 'A'.
               LD   H,B               H := track number.
               EXX                    Restore   the    alternate
               POP  HL                registers.
               POP  DE
               POP  BC
               EXX
               LD   B,(IX+06)         B := number of retries.
               LD   C,+1F
12F2 RD_WR_M10 LD   A,H               A := track number.
               LD   (2803),A          Signal: go to track 'A'.
               CALL 14D5,CL_CNTRL     Call disk-controller.
               AND  +18               Found & no CRC errors?
               JR   Z,1306,RD_WR_M11  Then skip.
               PUSH BC                B = number of retries.
               CALL 14D3,CL_RESTOR    Restore disk-controller.
               POP  BC                Loop back if more  retries
               DJNZ 12F2,RD_WR_M10    are allowed.
1304 REPORT_h  RST  0008,SH_ERROR     Report 'Disk I/O error'.
               DEFB +2F
1306 RD_WR_M11 POP  BC                C := number of bytes.
               LD   L,B               L := +AF  for  write,  +8C
               LD   B,+00             for read.
               LD   A,C
               AND  A                 Read/write a whole block?
               JR   NZ,1311,RD_WR_M12 Step if not.
               CALL 1467,BC:=SIZE     BC := block-size.
1311 RD_WR_M12 POP  DE                DE := startaddress
               LD   H,(IX+06)         H := number of retries.
1315 RD_WR_M13 PUSH BC
               PUSH DE
               PUSH HL
               CALL 14FE,FIND_ADDR    Load the block.
               POP  HL
               POP  DE
               POP  BC
               AND  +7C               Return if no error occured
               RET  Z
               AND  +40               Error = 'write protected'?
               JR   Z,1327,RD_WR_M14  Step if not.
1325 REPORT_f  RST  0008,SH_ERROR     Report 'Write protected'.
               DEFB +2D
1327 RD_WR_M14 DEC  H                 Another retry allowed?
               JR   NZ,1315,RD_WR_M13 Then try again.
               RST  0008,SH_ERROR     Report 'Disk I/O error'.
               DEFB +2F


THE 'FORMAT THE 'M' CHANNEL' SUBROUTINE
On entry IX points to the start of the subtable of table #0A for
the specified drive-number, in A. The disk is formatted, and the
extra block (boot-block) is formed and stored on the first block
on the first track.

132C FORMAT_M  PUSH AF                A = drive-number.
               RST  0010,CAL_SPEC     Clear the work-space.
               DEFW +16BF,SET_WORK
               LD   B,+00
               LD   C,(IX+01)         BC := number of sectors.
               INC  BC
               INC  BC
               INC  BC
               INC  BC                Make room for  (number  of
               RST  0010,CAL_SPEC     sectors) + 4 bytes.
               DEFW +0030,BC_SPACES
               INC  DE                Step to the 3rd location.
               INC  DE
               LD   A,(IX+02)         A := disk-info flag.
               AND  +C0               Keep only  the  block-size
               RLCA                   (bits 6 and 7).
               RLCA                   Copy it to bits 0 and 1.
               LD   (DE),A            Store it in the work-space
               INC  A                 0-3 := 1-4
               LD   B,A               B := counter.
               LD   A,+10

A will be made to hold 32 (for B=1), 64 (for B=2), 128 (for B=3)
or 0 (for B=4).

134A FORMAT_M2 ADD  A,A               A := (2 ^ B) * 16.
               DJNZ 134A,FORMAT_M2
               INC  DE                Store the  result  in  the
               LD   (DE),A            4th location.
               LD   B,(IX+01)         B := number of sectors.
1352 FORMAT_M3 LD   (HL),+F7          HL  pointed  to  the  last
               DEC  HL                byte of the work-space.
               DJNZ 1352,FORMAT_M3    Fill the space with F7's.
               INC  HL                HL := 1st byte of sec-info
               LD   D,(IX+0B)         D := interleave.
               LD   B,(IX+01)         B := number of sectors.
               LD   C,(IX+03)         C := no. extra sectors.
1361 FORMAT_M4 LD   (HL),C            Store it.
               INC  C
               PUSH DE                D = interleave.
1364 FORMAT_M5 CALL 14B9,SHIFTLEFT    Shift the buffer left.
               DEC  D
               JR   NZ,1364,FORMAT_M5 Repeat 'D' times.
               LD   A,(HL)            A := first byte in buffer.
               CP   +F7               Is it a F7?
               CALL NZ,14B9,SHIFTLEFT Shift left if not.
               POP  DE                D = interleave.
               DJNZ 1361,FORMAT_M4    Next sector.
               LD   DE,+0000          Start-sector = 0.
               CALL 1478,SETUP_FMD    Setup the format-data.
               LD   B,D
               LD   C,E
               RST  0010,CAL_SPEC
               DEFW +0030,BC_SPACES
               PUSH DE                DE = start of new space.
               CALL 14D3,CL_RESTOR    Restore disk-controller.
               POP  DE
1383 NEXTTRACK LD   HL,+3000
               BIT  4,(IX+02)         Drive single sided?
               JR   Z,1392,FORMAT_M6  Then skip.
               SET  4,(HL)            Signal: side 2.
               LD   A,+01
               JR   1395,FORMAT_M7    Step forward.
1392 FORMAT_M6 RES  4,(HL)            Signal: side 1
               XOR  A
1395 FORMAT_M7 LD   HL,(WORKSP)       Store the side in the  1st
               LD   (HL),A            location of the work-space
               INC  HL
               LD   A,(2801)          A := current track number.
               LD   (HL),A            Store it in the 2nd byte.
               PUSH DE                DE = format-data start.
               CALL 1478,SETUP_FMD    Setup the format-data.
               POP  DE
               PUSH DE
               LD   A,(DE)
               LD   B,A
               INC  DE
               LD   A,(DE)
               EX   AF,AF'
               LD   L,+FE             Signal: format.
               CALL 14FE,FIND_ADDR    Format the described track
               AND  +44               Disk write protected?
               JP   NZ,1325,REPORT_f  Then report the error.
               POP  DE                DE = format-data location.
               LD   HL,+3000
               BIT  4,(HL)            Double sided drive?
               JR   NZ,1392,FORMAT_M6 Then format side 1 too.
               LD   A,(2801)          A := current track.
               INC  A
               CP   (IX+00)           Is it the last one?
               JR   Z,13D6,EX_BLOCK   Then step out.
13C4 FORMAT_M8 LD   C,+5B
               PUSH DE
               CALL 14D5,CL_CNTRL     Step-in the drive-head.
               LD   L,(IX+0C)         L := number of retries.
13CD FORMAT_M9 CALL 14B9,SHIFTLEFT
               DEC  L
               JR   NZ,13CD,FORMAT_M9
               POP  DE
               JR   1383,NEXTTRACK    Handle the next track.

Now the boot-block is stored on the disk on real-block-number 0.

13D6 EX_BLOCK  LD   B,+06             Signal: entry 4.
               CALL 14B1,LOOKUP_18    HL := start of routine.
               LD   BC,+0080          Reserve 128 bytes.
               LD   DE,(WORKSP)       Start of bytes.
               PUSH DE
               LDIR                   Copy boot-block to buffer.
               POP  HL                HL := start of block.
               PUSH HL
               INC  HL                HL := start of parameters.
               INC  HL
               LD   A,(IX+00)         A := number of tracks.
               LD   (HL),A            Store it.
               INC  HL
               LD   A,(IX+01)         A := number of sectors.
               LD   (HL),A            Store it too.
               INC  HL
               LD   A,(IX+02)         A := disk-info flag.
               AND  +D0               Keep only bits 4, 6 and 7.
               LD   (HL),A            Store block-size and sides
               INC  HL
               LD   DE,(FRAMES)       Make the disk unique.
               LD   (HL),E
               INC  HL
               LD   (HL),D
1401 SAVE_BOOT POP  DE                DE := start of block.
               LD   HL,+0000          HL := real-block-number.
               LD   C,(IX+04)         BC := - boot-location.
               LD   B,(IX+05)
               AND  A
               SBC  HL,BC             HL := boot-location.
               POP  AF
               CALL 1242,WRITE_M      Store new configuration.
               JR   1446,HL:=BLCKS    Jump to set registers.


THE 'INQUIRE THE DISK' SUBROUTINE
On entry C holds 0 when the info should be read from  the  disk,
and +80 when the info should come from the disk tables. A  holds
the drive-number and IX points to the start of the corresponding
subtable of table #0A.

1414 INQUIRE_M BIT  7,C               Get new configuration?
               JR   NZ,1446,HL:=BLCKS Skip if not.
1418 GET_CNFG  LD   HL,+3001
               BIT  1,(HL)            IC 6116 present?
               JR   Z,1446,HL:=BLCKS  Skip if not.
141F GET_CNFG2 LD   HL,+0000          HL := real-block-number.
               LD   C,(IX+04)         BC := - boot-location.
               LD   B,(IX+05)
               AND  A
               SBC  HL,BC             HL := boot-location.
               LD   C,+80             Load 128 bytes.
               LD   DE,+2780          From address +2780.
               PUSH AF                From drive 'A'.
               CALL 1248,READ_M
               POP  AF
               AND  A
               LD   HL,(2780)         Test  if  the  routine  is
               LD   DE,+0518          still there. 0518 is JR +5
               SBC  HL,DE
               LD   HL,+2780          (HL := start of routine)
               INC  HL
               INC  HL                (HL := start of disk-data)
               CALL Z,2780            If present, call it.

HL is made to hold the number of blocks on the disk.

1446 HL:=BLCKS LD   H,+00
               LD   L,(IX+00)         HL := number of tracks.
               LD   D,H
               LD   E,(IX+01)         DE := number of sectors.
               BIT  4,(IX+02)         Double sided disk?
               JR   Z,1456,NEXT_1     Skip if not.
               ADD  HL,HL             Count tracks of both sides
1456 NEXT_1    RST  0010,CAL_SPEC     HL := number of blocks.
               DEFW +30A9,HL:=HL*DE
               LD   C,(IX+04)         BC := no. extra blocks.
               LD   B,(IX+05)
               AND  A
               SBC  HL,BC             HL := usable blocks number

DE is made to hold the number of blocks in the catalog-file.

1462 DE:=CATLN LD   D,+00
               LD   E,(IX+0D)         DE := catalog length.

BC is made to hold the block-size.

1467 BC:=SIZE  LD   A,(IX+02)         A := disk-info byte.
               AND  +C0               Keep only bits 6 and 7.
               LD   BC,+0040          BC := 64.
146F BC:=SIZE2 SLA  C                 BC := 64*(2^((A/64)+1))
               RL   B
               SUB  +40
               JR   NZ,146F,BC:=SIZE2
               RET


THE 'SETUP THE DATA FOR FORMATTING' SUBROUTINE
First the data from the first subtable  of  table  #18  is  used
once, then (number of sectors) times the data  from  the  second
subtable and finally one time the data from the third subtable.

1478 SETUP_FMD LD   B,+00             Signal: entry 1.
               CALL 148E,SETUP_2      Store the data.
               LD   B,(IX+01)         B := number of sectors.
1480 NXTSECTOR PUSH BC                Store it.
               LD   B,+02             Signal: entry 2.
               CALL 148E,SETUP_2
               CALL 14B9,SHIFTLEFT    Shift the buffer left.
               POP  BC                Restore sector counter.
               DJNZ 1480,NXTSECTOR
               LD   B,+04             Signal: entry 3.

The data from subtable 'B' of table #18 is used.

148E SETUP_2   PUSH DE                DE = format-data start.
               CALL 14B1,LOOKUP_18    Lookup table #18.
               POP  DE
               LD   B,(HL)            B := length-byte.
1494 NXT_FM_DT INC  HL
               LD   A,(HL)            A := format-data.
               CP   +F0               Test if +F0 <= A <= +F4
               JR   C,94AC,ST_FN_DT   Simply store byte if not.
               CP   +F5
               JR   NZ,94AC,ST_FM_DT
               PUSH HL
               LD   HL,(WORKSP)       HL := start of buffer.
               SUB  +F0               F0-F4 := 0-4.
               PUSH BC                Store length-byte.
               LD   B,+00
               LD   C,A               Copy A to BC.
               ADD  HL,BC             HL := location of byte 'A'
               POP  BC                (restore length-byte)
               LD   A,(HL)            Read byte 'A' from buffer.
               POP  HL
14AC ST_FM_DT  LD   (DE),A            Store byte in format-data
               INC  DE                Point to next location.
               DJNZ 1494,NXT_FM_DT    Repeat 'length-byte' times
               RET
14B1 LOOKUP_18 LD   A,(IX+0A)         A := drive density.
               RST  0030,LOOKUP       Lookup   the   format-data
               DEFB +18               startaddress.
               LD   D,H               It is wanted in DE.
               LD   E,L
               RET


THE 'SHIFT FORMAT-DATA LEFT' SUBROUTINE
The format data for each sector  is  moved  left  (downwards  in
memory) one location.

14B9 SHIFTLEFT PUSH BC                Save HL and BC during this
               PUSH HL                loop.
               LD   HL,(WORKSP)       HL := start of sector-data
               INC  HL                in the work-space.
               INC  HL
               INC  HL
               INC  HL
               LD   A,(HL)            A := first sector-data.
               LD   B,+00
               LD   C,(IX+01)         BC := number of sectors.
               ADD  HL,BC             HL := end of sector-data.
               LD   B,C               B := number of sectors.
14CA SHIFTLEF2 DEC  HL                Go down one location.
               LD   C,(HL)            Read the entry in C.
               LD   (HL),A            A = data previous sector.
               LD   A,C               A := data this sector.
               DJNZ 14CA,SHIFTLEF2    Repeat for all sectors.
               POP  HL
               POP  BC
               RET


THE 'RESTORE THE DISK-CONTROLLER' SUBROUTINE
All controller error-registers are cleared, and  the  drive-head
is repositioned at track 0.

14D3 CL_RESTOR LD   C,+03


THE 'CALL THE DISK-CONTROLLER' SUBROUTINE
A is made to hold %00000000 (restore (enable spinup, no  verify) for C=+03), %00011100 (seek (disable spinup, verify for  C=+1F),
or %01011000 (step-in (disable spinup, update) for C=+5B). Bit 7 is SET if an NMI should occur. (see appendix D for more informa-
tion on the controller hardware).

14D5 CL_CNTRL  LD   A,(IX+07)         A := control register.
               RRCA                   Swap nibbles.
               RRCA
               RRCA
               RRCA
               OR   +F0               Keep bits 0-3, SET others.
               AND  C
               CALL 1519,DO_M_I/O     Pass the command  directly
               PUSH AF                to the controller.
               CALL 14F1,TAB_START    Find track register buffer
               AND  (HL)              of current drive.
14E7 NEXT_2    INC  HL
               RRA
               JR   NC,14E7,NEXT_2
               LD   A,(2801)          A := current track.
               LD   (HL),A            Store it.
               POP  AF                A :=  result  of  hardware
               RET                    call.


THE 'HL := START OF DISK TABLES' SUBROUTINE
This subroutine is used to made HL point to  the  start  of  the
disk-info tables.

14F1 TAB_START XOR  A
               LD   B,+FF             Signal: no subtables.
               RST  0030,LOOKUP       Find the  startaddress  of
               DEFB +0A               the disk-info tables.
               INC  HL
               INC  HL
               INC  HL
               INC  HL
               LD   A,(3000)          A := current drive number.
               RET


THE 'FIND THE ADDRESS FOR THE NMI' SUBROUTINE
This subroutine is used to form the address of  the  routine  to
jump to with the NMI, since address +66 holds the instruction JP
(HL). A is set up as follows: bit 5 is RESET  for  read,  bit  6
RESET for write. When both are SET, this indicates format. Bit 7
is SET to trigger  the  NMI,  bit  1  is  RESET  when  a  write-
protection should be tested. L holds +8C for read, +AF for write
and +FE for format.

14FE FIND_ADDR LD   A,(IX+07)         Use only bits 0-3.
               OR   +F0               Set bits 4-7.
               AND  L                 Include the command bits.
               LD   HL,+1886          HL := +1886,NMI_READ.
               BIT  5,A               Read if bit 5 is RESET.
               JR   Z,1513,GO_M_I/O
               LD   L,+95             HL := +1895,NMI_WRITE.
               BIT  6,A               Write if bit 6 is RESET.
               JR   Z,1513,GO_M_I/O
               LD   L,+9F             HL := +189F,NMI_FORMT.
1513 GO_M_I/O  DI                     No interrupts allowed.
               CALL 1519,DO_M_I/O     Call the hardware.
               EI                     Restore interrupt status.
               RET
1519 DO_M_I/O  LD   (2800),A          Pass the command  directly
               XOR  A                 to the hardware.
151E WAIT_NMI  DEC  A                 Wait a while for  the  NMI
               JR   NZ,151E,WAIT_NMI  to finish. (4095 T states)
1520 WAIT_I/O  LD   A,(2800)          Is  the  diskdrive   still
               BIT  0,A               doing I/O?
               RET  Z                 Finished if not.
               CALL 1548,TST_BREAK    Test the 'BREAK'-key.
               JR   C,1520,WAIT_I/O   Loop back if not pressed.
152B BREAK_I/O LD   A,+D0             Stop the drive.
               LD   (2800),A
               LD   B,+70             Wait +70 interrupts (about
               EI                     1.5 seconds).
1533 BREAK_WT  HALT
               DJNZ 1533,BREAK_WT
               CALL 14F1,TAB_START
               OR   (HL)
               XOR  (HL)
               LD   (3000),A
               JR   1544,BREAK_REP    Step forward.


THE 'TEST FOR 'BREAK' AND REPORT IF PRESSED' SUBROUTINE
The 'BREAK' key is tested and when it is pressed, then the error
message 'BREAK - CONT repeats' is printed.

1540 T/R_BREAK CALL 1548,TST_BREAK    Test the 'BREAK' key.
               RET  C                 Ready if not pressed.
1544 BREAK_REP EI                     Ensure interrupt allowence
1545 REP_BREAK RST  0010,CAL_SPEC     Report the error from  the
               DEFW +0552,REPORT_D    Spectrum ROM.


THE 'TEST FOR 'BREAK' PRESSED' SUBROUTINE
A test is made for the  and  keys. If pressed
both, then the carry flag is returned RESET.

1548 TST_BREAK LD   A,+FE             First test .
               IN   A,(+FE)
               RRCA
               RET  C                 Exit if not pressed.
               LD   A,+7F             Else test  too.
               IN   A,(FE)
               RRCA                   This RESETS the carry flag
               RET                    if  is pressed too.


THE 'INITIALIZE THE IC 6116' SUBROUTINE
First a check is made whether the 8K ROM is not  currupted.  The
main table and all subtables are moved to  the  IC  6116  and  a
check is made whether the tables are still there.  If  so,  then
the presence of the IC is signalled to the hardware.

1554 INIT_RAM2 LD   HL,+7132          HL := check-sum.
               LD   DE,+0000          DE := address to test.
               LD   B,D               B := 0.
155B TEST_ROM  LD   A,(DE)            Read a byte.
               LD   C,A               Pass it to BC.
               ADD  HL,BC             Add it to the check-sum.
               INC  DE                Point to the next byte.
               LD   A,D               Last byte checked?
               CP   +20
               JP   NZ,155B,TEST_ROM  Loop back if not.
1565 TEST_ROM2 LD   A,H               Everything is still OK  if
               OR   L                 the check-sum = 0.
               JR   NZ,1533,BREAK_WT  Report an error if found.
1569 INIT_RAM3 LD   HL,+18AD          HL := start of tables - 4.
               LD   BC,+0089          BC := length of tables + 4
               LDIR                   Move them to the IC 6116.
               LD   B,+89             +89 bytes to test.
1573 TEST_RAM  DEC  HL
               DEC  DE
               LD   A,(DE)            A := data in RAM.
               CP   (HL)              Data in ROM = data in RAM?
               LD   A,+05             (prepare for 'absent')
               JR   NZ,1585,REP_RAM   Step out if not.
               DJNZ 1573,TEST_RAM     Test next byte.
               SET  1,A               A := +07.
1585 REP_RAM   LD   HL,+2023          Make alterations in RAM.
               LD   (2013),HL         (Point to new subtable 0A)
               LD   HL,+3001
               LD   (HL),C            (3001) := %00000000
               DEC  HL
               LD   (HL),+FF          (3000) := %11111111
               INC  HL
               LD   (HL),A            (3001) := %000001b1
               DEC  HL                b = IC 6116 present?
               LD   (HL),+C0          (3000) := %11000000
               RET

The addresses 1592 to 1707 are unused in version 2.2 and contain
+FF's.


THE 'PAGE IN' ROUTINE
This address can be reached in two ways.  Firstly  it  could  be
called directly, with the intension to  page  in  the  Discovery
ROM, secondly by the CLOSE_2 subroutine in the Spectrum ROM.
When coming from the CLOSE_2 subroutine, the  last value on  the
stack points to a position in the channel information  area  and
is between +5C16 and +5C36. Note that when the routine is called
to page in the Discovery ROM, this cannot be the  case,  as  the
returnaddress is the last entry then.
A test is made whether the Discovery ROM should be paged in,  or
a channel should be closed. When closing a channel, HL points to
the start if  this  channel  and  the  CLOSE-routines  from  the
Spectrum ROM are used. This is why some subtables of  table  #04
do not  contain  an  entry  to  close  the  channel.  The  first
instruction is fetched from the Spectrum ROM and is an  INC  HL.
That is why the  next  instruction  decreases  the  registerpair
again.

1708 PAGE_IN   NOP                    = INC HL.
               DEC  HL                Compensate.
               EX   (SP),HL           PUSH HL, POP returnaddress
               PUSH AF                Store other registers too.
               PUSH DE
               PUSH HL                And the returnaddress.
               LD   DE,+A3EA          DE := - 5C15.
               ADD  HL,DE             HL >= 5C15?
               JR   NC,171A,JUST_PAGE Skip if not.
               LD   DE,+FFE0          DE := -32.
               ADD  HL,DE             HL < 5C36?
               JR   NC,171F,CLOSE_2   Then close a channel.

The routine was called to page in the Discovery ROM.

171A JUST_PAGE POP  HL                Restore the  stack  as  it
               POP  DE                was before the call.
               POP  AF
               EX   (SP),HL
               RET                    Finished paging.

The routine was called to close a channel.

171F CLOSE_2   POP  HL                Restore the  stack  as  it
               POP  DE                was before the call.
               POP  AF
               EX   (SP),HL
               LD   A,B               Channel already closed?
               OR   C
               JR   Z,1734,P_IN_EXIT  Then exit.
               DEC  HL                HL := start of the channel
               DEC  HL                to be closed.
               DEC  HL
               PUSH HL                Pass it to IX.
               POP  IX
               CALL 0368,CLOSE_CHN    Close the channel.
               POP  HL                Clear stack (Spectrum ROM)
               RST  0010,CAL_SPEC     Resume closing the channel
               DEFW +16EB,CLOSE+6
1734 P_IN_EXIT JP   015A,OPEN_'S'     Open the 'S' channel.

               DEFW +FFFF, +FFFF      Unused locations.
               DEFW +FFFF, +FFFF
               DEFW +FFFF, +FFFF
               DEFW +FFFF, +FFFF
               DEFB +FF


THE 'PAGE OUT' ADDRESS
When the Discovery ROM should be paged out, this address  is  to
be called. Although it is merely just  a  RET,  the  address  is
detected by the hardware, and the Spectrum ROM is paged in again

1748 PAGE_OUT  RET                    Page out the Discovery ROM

The '48K ramdisk' subtable of table #08
1749 TAB_8:5   DEFW +1763,WRITE_5     Write.
               DEFW +1769,READ_5      Read.
               DEFW +1751,FM/INQ_R    Inquire.
               DEFW +1751,FM/INQ_R    Format.


THE 'FORMAT & INQUIRE THE RAMDISK' SUBROUTINE
The registers are set up correctly; no catalog should be written

1751 FM/INQ_R  LD   C,(IX+02)         BC := block-size.
               LD   B,(IX+03)
               LD   L,(IX+04)         HL := total no. blocks.
               LD   H,(IX+05)
               LD   E,(IX+06)         DE := no. blocks in 'CAT'
               LD   D,+00
               RET


THE 'WRITE TO THE 48K RAMDISK' SUBROUTINE
On entry DE holds the startaddress of the data to be written  to
the ramdisk, BC the length and HL the block number. The  address
within the ramdisk is calculated and the data moved.

1763 WRITE_5   CALL 1770,FD_ADD_48    Find the location.
               LDIR                   Move the data.
               RET


THE 'READ FROM THE 48K RAMDISK' SUBROUTINE
On entry DE holds the startaddress of the data to be  read  from
the ramdisk, BC the length and HL the block number. The  address
within the ramdisk is calculated and the data moved.

1769 READ_5    CALL 1770,FD_ADD_48    Find the location.
               EX   DE,HL             Switch source/destination.
               LDIR                   Move the data.
               RET


THE 'FIND THE ADDRESS IN THE 48K RAMDISK' SUBROUTINE
Find the address of  block  HL  in  the  ramdisk.  First  HL  is
multiplied with the block-size, then the startaddress is added.

1770 FD_ADD_48 PUSH DE                Save startaddress.
               LD   C,(IX+02)         BC := block-size.
               LD   B,(IX+03)
               LD   D,B               Copy it to DE.
               LD   E,C
               RST  0010,CAL_SPEC     Multiply HL by DE.
               DEFW +30A9,HL:=HL*DE
               LD   E,(IX+00)         DE := start of ramdisk.
               LD   D,(IX+01)
               ADD  HL,DE             HL := required address.
               EX   DE,HL             Pass it to DE.
               LD   HL,(RAMTOP)       Ramtop below start of  the
               AND  A                 ramdisk?
               SBC  HL,DE
               POP  HL                (HL = memory, DE = disk)
               RET  C                 Exit if so.
               RST  0008,SH_ERROR     Report 'Disk I/O error'.
               DEFB +2F

The '128K ramdisk' subtable of table #08.
178E TAB_8:6   DEFW +1796,WRITE_6     Write.
               DEFW +179F,READ_6      Read.
               DEFW +1751,FM/INQ_R    Inquire.
               DEFW +1751,FM_INQ_R    Format.


THE 'WRITE TO THE 128K RAMDISK' SUBROUTINE
On entry DE holds the startaddress of the data to be written  to
the ramdisk, BC holds the length and HL the  block  number.  The
address within the ramdisk is calculated and the data moved.

1796 WRITE_6   CALL 17D3,FD_AD_128    Find the location.
               PUSH HL                Store location.
               PUSH BC                Copy start to HL.
               POP  HL
               LD   D,A
               JR   17A4,RD_WR_R_1    Step forward.


THE 'READ FROM THE 128K RAMDISK' SUBROUTINE
On entry DE holds the startaddress of thedata to  be  read  from
the ramdisk, BC holds the length and HL the  block  number.  The
address within the ramdisk is calculated and the data moved.

179F READ_6    CALL 17D3,FD_AD_128    Find the location.
               LD   E,A
               PUSH BC                BC = startaddress.
17A4 RD_WR_R_1 LD   C,(IX+08)         BC := page port (+7FFD)
               LD   B,(IX+09)
               EXX
               EX   (SP),HL           HL' := start/location.
               PUSH DE                Stack DE' and BC'.
               PUSH BC                The tack holds HL' too.
               LD   C,(IX+02)         BC := block-size.
               LD   B,(IX+03)
               LD   D,A
               DI
17B6 RD_WR_R_2 EXX
               LD   A,D               Go to current ramdisk.
               OUT  (C),A
               LD   A,(HL)            Read byte into A'.
               INC  HL                Point to next location.
               EX   AF,AF'
               LD   A,E               GO to ramdisk to read from
               OUT  (C),A             or write to.
               EXX
               EX   AF,AF'            Get A' in A.
               LD   (HL),A            Store the byte.
               INC  HL                Point to next location.
               DEC  BC                1 Less to go.
               LD   A,B               Test if finished.
               OR   C
               JR   NZ,17B6,RD_WR_R_2 Loop back if not.
17C9 RD_WR_R_3 LD   A,D
               EXX
               OUT  (C),A             Go to current ramdisk.
               EI
               POP  BC                Clear the stack and resto-
               POP  DE                re the registers.
               POP  HL
               EXX
               RET


THE 'FIND THE ADDRESS IN THE 128K RAMDISK' SUBROUTINE
On entry HL holds the blocknumber in the RAM-disk.  On  exit  HL
holds the 'real' address in 128K-block 'D' and A will  hold  the
current 128K-block number.

17D3 FD_AD_128 XOR  A
               LD   C,(IX+02)         BC := block-size.
               LD   B,(IX+03)
17DA F_A_128_2 SRL  B                 AHL := blocknumber * size.
               RR   C
               JR   C,17E4,F_A_128_3
               ADD  HL,HL
               RLA
               JR   17DA,F_A_128_2
17E4 F_A_128_3 LD   B,(IX+07)         AHL := AHL * 2^(IX+07)
               LD   C,B
17E8 F_A_128_4 ADD  HL,HL
               RLA
               DJNZ 17E8,F_A_128_4
               LD   B,C               HL := HL / 2^(IX+07)
17ED F_A_128_5 SRL  H
               RR   L
               DJNZ 17ED,F_A_128_5
               LD   C,(IX+00)         BC := start of ramdisk.
               LD   B,(IX+01)
               ADD  HL,BC
               PUSH IX                Store location, IX to HL.
               EX   (SP),HL           HL := HL + 11 + A.
               LD   BC,+000B
               ADD  HL,BC
               LD   C,A
               ADD  HL,BC
               LD   B,D               Start in BC.
               LD   C,E
               LD   D,(HL)            D := E := (HL) :=  ramdisk
               LD   E,D               to write to.
               POP  HL                HL := location.
               LD   A,(IX+0A)         A := current ramdisk.
               BIT  4,(FLAGS)         If in 128K mode, then A :=
               RET  Z                 (5B5C).
               LD   A,(5B5C)
               RET

The printerport subtable of table #08:
1814 TAB_8:T   DEFW +181C,WRITE_T     Write.
               DEFW +1853,READ_T      Read.
               DEFW +001C             Inquire (return).
               DEFW +001C             Format (return).


THE 'WRITE TO THE PRINTERPORT' SUBROUTINE
On entry H holds the characer to be written.

181C WRITE_T   LD   A,H               A := character.
               LD   HL,+3003
               LD   (HL),+38          Clock on for bits program
               DEC  HL
               LD   (HL),+FF          Signal: all databits = out
               DEC  HL
               RES  2,(HL)            Clock on for BUSY program
               DEC  HL
               RES  6,(HL)            Signal: BUSY = input.
               INC  HL
               SET  2,(HL)            Clock off for BUSY program
               INC  HL
               INC  HL
               LD   (HL),+3C          Clock off for bits program
               DEC  HL
               LD   (HL),A            The actual write.
1834 WR_T_WAIT CALL 1540,T/R_BREAK    Test the 'BREAK' key.
               LD   A,(3000)
               BIT  6,A               Printer BUSY?
               JR   NZ,1834,WR_T_WAIT Then wait until ready.
               INC  HL
               DI                     No interrupts while timing
               LD   (HL),34           STROBE on.
               LD   B,+05
1844 WR_T_STB  DJNZ 1844,WR_T_STB     Wait 66T states.
               LD   (HL),+3C          STROBE off.
               DEC  HL
               DEC  HL
184A WR_T_ACK  CALL 1540,T_R_BREAK    Test the 'BREAK' key.
               BIT  6,(HL)            Wait for ACK to go high.
               JR   Z,184A,WR_T_ACK   (Don't wait for it  to  go
               EI                     low again). Restore inter-
               RET                    rupt status before return.


THE 'READ FROM THE PRINTERPORT' SUBROUTINE
On exit A holds the read character. If the read was successfull,
then the carry flag is returned SET.

1853 READ_T    CALL 1540,T/R_BREAK    Test the 'BREAK' key.
               LD   HL,+3003
               XOR  A
               LD   (HL),A            Clock on for bits program.
               DEC  HL
               LD   (HL),A            Signal: all databits = in.
               DEC  HL
               RES  2,(HL)            Clock on for BUSY program.
               DEC  HL
               SET  6,(HL)            Signal: BUSY = output.
               INC  HL
               SET  2,(HL)            Clock off for BUSY program
               DEC  HL
               RES  6,(HL)            Signal: ready to read.
               INC  HL
               INC  HL
               INC  HL
               LD   (HL),+04          Clock off for bits program
               BIT  6,(HL)            STROBE signal detected?
               RET  Z                 Step out if not.
               DEC  HL
               LD   A,(HL)            The actual read.
               DEC  HL
               DEC  HL
               SET  6,(HL)            Signal: BUSY.
               INC  HL
               SET  4,(HL)            Signal: ACK = output.
               SET  5,(HL)            ACK on.
               LD   B,+05
187E RD_T_ACK  DJNZ 187E,RD_T_ACK     Wait 66T states.
               RES  5,(HL)            ACK off.
               RES  4,(HL)            Signal: ACK = input.
               SCF                    Signal: succesfully read.
               RET


THE 'NMI READ A BYTE' SUBROUTINE
BC holds the number of bytes to be read, and DE the  address  to
be read into.

1886 NMI_READ  EX   AF,AF'
               LD   A,B               No more bytes to be read?
               OR   C
               LD   A,(2803)          (get byte from hardware)
               JP   Z,1892,NMI_READ2  Then skip.
               LD   (DE),A            Store the read byte.
               INC  DE                Point to next location.
               DEC  BC                1 Byte less to go.
1892 NMI_READ2 EX   AF,AF'
               RETN                   Return from NMI.


THE 'NMI WRITE A BYTE' SUBROUTINE
BC holds the number of bytes to be written, and DE  the  address
from which the data should be read.

1895 NMI_WRITE EX   AF,AF'
               LD   A,(DE)            Get byte from memory.
               LD   (2803),A          Give byte to hardware.
               INC  DE                Point to next location.
               DEC  BC                1 Byte less to go.
               EX   AF,AF'
               RETN                   Return from NMI.


THE 'NMI FORMAT A BYTE' SUBROUTINE
On entry DE holds the address in  the  format-data,  and  B  the
number of repetitions of the byte in the A' register.

189F NMI_FORMT EX   AF,AF'
               LD   (2803),A          Give byte to hardware.
               DJNZ 18AA,NMI_FRMT2    Skip if this  byte  should
                                      be written more times.
               INC  DE                Point to next location.
               LD   A,(DE)            Get byte from memory  into
               LD   B,A               B; the no. repetitions  of
               INC  DE                the next byte.
               LD   A,(DE)            Read that byte in A.
18AA NMI_FRMT2 EX   AF,AF'            Byte is now in A'.
               RETN                   Return from NMI.

These two addresses are the first to be copied into the IC 6116.
They contain the start of the main table and the  start  of  the
free space after all tables.

18AB COPY_RAM  DEFW +2009,MAIN_TABL   Start of main table.
18AD COPY_RAM2 DEFW +2089,FREE_MEM    Start of free space.

               DEFB +03               Which drives are primary?
               DEFB +FF               Current track of drives
               DEFB +FF
               DEFB +FF
               DEFB +FF


THE MAIN TABLE
This table holds the startaddresses of the other tables.

18B6 MAIN_TABL DEFW +1936,TABLE_00    Hook-code table.
               DEFW +1957,TABLE_02    Command table.
               DEFW +1B25,TABLE_04    Channel table 1.
               DEFW +1B58,TABLE_06    Channel table 2.
               DEFW +1B6E,TABLE_08    Device table.
               DEFW +1B91,TABLE_0A    Disk-info table.
               DEFW +197E,TABLE_0C    Error message table.
               DEFW +1ADD,TABLE_0E    Exit table.
               DEFW +1AE4,TABLE_10    General utility table.
               DEFW +1AF7,TABLE_12    System utility table.
               DEFW +1B12,TABLE_14    File manipulation table.
               DEFW +1BA6,TABLE_16    Print character table.
               DEFW +1BCA,TABLE_18    Format-data table.


THE DISK-INFO TABLE #0A IN RAM
This table and the subtables following it are copied to  the  IC
6116 if it is present. The pointer in to main table  is  altered
when it is copied. It is not used when the IC is not present.

18D0 TB_0A_RAM DEFB +01               Drive 1.
               DEFW +2038,DRIVE_1
               DEFB +02               Drive 2.
               DEFW +2046,DRIVE_2
               DEFB +03               Drive 3.
               DEFW +2054,DRIVE_3
               DEFB +04               Drive 4.
               DEFW +2063,DRIVE_4
               DEFB +05               Ramdisk 48K.
               DEFW +2070,RAMD_48
               DEFB +06               Ramdisk 128K.
               DEFW +2077,RAMD_128
               DEFB +00               Error.
               DEFW +2000


THE SUBTABLES OF TABLE #0A
Byte 2 is used as follows: bit 0: SET for righthand drive;
                           bit 1: SET for lefthand drive;
                           bit 2: SET if drive has a virtual;
                           bit 3: RESET if drive is primary.
                           bit 4: SET if disk is double sided.
                           bit 5: SET if drive is double sided.
                           bits 6 and 7: block-size : 00 = 128
                                                      01 = 256
                                                      10 = 512
                                                      11 = 1024
Bit 4 and 5 of byte 7 hold the  steprate  for  the  arm  of  the
drive: 00 = 6 ms., 01 = 12 ms., 10 = 20 ms., 11 = 30 ms.

The information for drive 1:
18E5 DRIVE_1   DEFB +28               (00) 40 tracks.
               DEFB +12               (01) 18 sectors/track.
               DEFB +41               (02) %01000001
               DEFB +00               (03) no extra sectors.
               DEFW +0001             (04) boot-block = -1
               DEFB +05               (06) number of retries
               DEFB +CE               (07) control register
               DEFB +04               (08) Drive 3 as virtual
               DEFB +28               (09)
               DEFB +00               (0A) format type 0
               DEFB +0D               (0B) interleave = 13
               DEFB +05               (0C) number of retries
               DEFB +06               (0D) 6 + 1 blocks in 'CAT'

The information for drive 2:
18F3 DRIVE_2   DEFB +28               (00) 40 tracks.
               DEFB +12               (01) 18 sectors/track.
               DEFB +42               (02) %01000010
               DEFB +00               (03) no extra sectors.
               DEFW +0001             (04) boot-block = -1
               DEFB +05               (06) number of retries
               DEFB +CE               (07) control register
               DEFB +08               (08) Drive 4 as virtual
               DEFB +28               (09)
               DEFB +00               (0A) format type 0
               DEFB +0D               (0B) interleave = 13
               DEFB +05               (0C) number of retries
               DEFB +06               (0D) 6 + 1 blocks in 'CAT'

The information for drive 3:
1901 DRIVE_3   DEFB +28               (00) 40 tracks.
               DEFB +12               (01) 18 sectors/track.
               DEFB +45               (02) %01000101
               DEFB +00               (03) no extra sectors.
               DEFW +0001             (04) boot-block = -1
               DEFB +05               (06) number of retries
               DEFB +CE               (07) control register
               DEFB +04               (08) Virtual of drive 1
               DEFB +28               (09)
               DEFB +00               (0A) format type 0
               DEFB +0D               (0B) interleave = 13
               DEFB +05               (0C) number of retries
               DEFB +06               (0D) 6 + 1 blocks in 'CAT'

The information for drive 4:
190F DRIVE_4   DEFB +28               (00) 40 tracks.
               DEFB +12               (01) 18 sectors/track.
               DEFB +4A               (02) %01001010
               DEFB +00               (03) no extra sectors.
               DEFW +0001             (04) boot-block = -1
               DEFB +05               (06) number of retries
               DEFB +CE               (07) control register
               DEFB +08               (08) Virtual of drive 2
               DEFB +28               (09)
               DEFB +00               (0A) format type 0
               DEFB +0D               (0B) interleave = 13
               DEFB +05               (0C) number of retries
               DEFB +06               (0D) 6 + 1 blocks in 'CAT'

The information for the 48K ramdisk
191D RAMD_48   DEFW +8000             (00) startaddress = 32768
               DEFW +0020             (02) block-size = 32
               DEFW +0400             (04) no. blocks = 1024
               DEFB +07               (06) 7 + 1 blocks in 'CAT'

The information for the 128K ramdisk
1924 RAMD_128  DEFW +C000             (00) startaddress = 49152
               DEFW +0100             (02) block-size = 256
               DEFW +0100             (04) no. blocks = 256
               DEFB +03               (06) 3 + 1 blocks in 'CAT'
               DEFB +02               (07) 16K boundary multipl.
               DEFW +7FFD             (08) page port 128K
               DEFB +16               (0A) Default 'current' bl.
               DEFB +10, +13, +14,    (0B) 128K  block  replace-
               DEFB +17, +11, +12,         ment numbers.
               DEFB +10


THE HOOK-CODE TABLE
In this table the startaddress for the different hook-codes  are
stored.

1936 TABLE_00  DEFB +E1               Character I/O
               DEFW +00A5,CHAR_I/O
               DEFB +0B               Spectrum error
               DEFW +0100,BASIC_ERR   ('Nonsense in BASIC')
               DEFB +0E               Spectrum error
               DEFW +0100,BASIC_ERR   ('Invalid file name')
               DEFB +17               Spectrum error
               DEFW +0100,BASIC_ERR   ('Invalid I/O device')
               DEFB +47               Length current channel
               DEFW +0093,LENGTH_1
               DEFB +FF               Spectrum error
               DEFW +01C2,AUTO_RUN    ('OK')
               DEFB +FD               Version number ROM
               DEFW +0088,VERSION
               DEFB +E3               Initialize IC 6116
               DEFW +009E,INIT_RAM
               DEFB +D4               Create Mdrive syvars.
               DEFW +00E2,M_DRIVE
               DEFB +2D               Copyright message
               DEFW +001E,(C)_MSG
               DEFB +00               Other
               DEFW +00F7,HOOKERROR


THE COMMAND TABLE
The ASCII-values of the different commands are  stored  in  this
table. The 'CLOSE #'  character  is  not  included,  since  this
command is handled by the +1708 entry point.

1957 TABLE_02  DEFB +F8               SAVE
               DEFW +029D,LD_SA_ETC
               DEFB +EF               LOAD
               DEFW +029D,LD_SA_ETC
               DEFB +D6               VERIFY
               DEFW +029D,LD_SA_ETC
               DEFB +D5               MERGE
               DEFW +029D,LD_SA_ETC
               DEFB +D1               MOVE
               DEFW +0329,MOVE
               DEFB +D3               OPEN #
               DEFW +0200,OPEN_#
               DEFB +A9               POINT
               DEFW +03AA,POINT
               DEFB +FD               CLEAR
               DEFW +03E5,CLEAR
               DEFB +CF               CAT
               DEFW +04BF,CAT
               DEFB +D0               FORMAT
               DEFW +04DC,FORMAT
               DEFB +D2               ERASE
               DEFW +04D8,ERASE
               DEFB +FB               CLS
               DEFW +04F2,CLS_#
               DEFB +00               Other
               DEFW +016D,CONTSIG_2


THE ERROR MESSAGE TABLE
This table holds the startaddresses for the  characters  of  the
various error reports.

197E TABLE_0C  DEFB +29               a
               DEFW +19D0,REPORT_A
               DEFB +2A               b
               DEFW +19E3,REPORT_B
               DEFB +2B               c
               DEFW +19F6,REPORT_C
               DEFB +2D               e
               DEFW +1A0A,REPORT_E
               DEFB +2E               f
               DEFW +1A19,REPORT_F
               DEFB +2F               g
               DEFW +1A28,REPORT_G
               DEFB +30               h
               DEFW +1A36,REPORT_H
               DEFB +31               i
               DEFW +1A44,REPORT_I
               DEFB +32               j
               DEFW +1A53,REPORT_J
               DEFB +34               l
               DEFW +1A62,REPORT_L
               DEFB +35               m
               DEFW +1A75,REPORT_M
               DEFB +36               n
               DEFW +1A84,REPORT_N
               DEFB +47               (c)
               DEFW +19BD,REP_(C)
               DEFB +48               Wally
               DEFW +1A8E,REP_WALLY
               DEFB +57               RAM
               DEFW +1A9E,REP_RAM
               DEFB +E0               Message 1
               DEFW +1AB3,MESSAGE_1
               DEFB +E1               Message 2
               DEFW +1ABC,MESSAGE_2
               DEFB +E2               Message 3
               DEFW +1ABE,MESSAGE_3
               DEFB +E3               Message 4
               DEFW +1ACA,MESSAGE_4
               DEFB +E4               Message 5
               DEFW +1ADC,MESSAGE_5
               DEFB +00               Other
               DEFW +1AA9,UNKNOWN

The error messages and other reports. Each last byte has b7 SET.

19BC REP_(C)   DEFM 1984-86 Dave Corney
19D0 REPORT_A  DEFM Invalid device name
19E3 REPORT_B  DEFM Stream already open
19F6 REPORT_C  DEFM Invalid drive number
1A0A REPORT_E  DEFM Write protected
1A19 REPORT_F  DEFN No room on disk
1A28 REPORT_G  DEFM Disk I/O error
1A36 REPORT_H  DEFM File not found
1A44 REPORT_I  DEFM Hook code error
1A53 REPORT_J  DEFM File size error
1A62 REPORT_L  DEFM Verification failed
1A75 REPORT_M  DEFM Wrong file type
1A84 REPORT_N  DEFM Wrong disk
1A8E REP_WALLY DEFM Don't be a wally
1A9E REP_RAM   DEFM Ram corrupt
1AA9 UNKNOWN   DEFM No message
1AB3 MESSAGE_1 DEFM Destroy "
1ABC MESSAGE_2 DEFM "?
1ABE MESSAGE_3 DEFM Insert disk
1ACA MESSAGE_4 DEFM , then press any key
1ADC MESSAGE_5 DEFM y+CHR$(0)+LPRINT         (Probably an error)


THE EXIT TABLE
This table holds the addresses of the return and EXIT routines.

1ADD TABLE_0E  DEFB +00
               DEFW +1AE0,SUB_0E
1AE0 SUB_0E    DEFW +001C             Simply return.
               DEFW +0586,EXIT        Use the EXIT routine.


THE GENERAL UTILITY TABLE
This table holds the startaddresses of  the  routines  that  are
accessible from the BASIC-level.

1AE4 TABLE_10  DEFB +00
               DEFW +1AE7,SUB_10
1AE7 SUB_10    DEFW +03D8,CALL_CHAN
               DEFW +04E9,CALL_UTIL
               DEFW +06A9,OPEN_CHAN
               DEFW +0368,CLOSE_CHN
               DEFW +036D,CLEAR_CHN
               DEFW +0486,WIPE_CHAN
               DEFW +06BF,MOVE_CHAN
               DEFW +06F4,SAVE_CHAN   Both SAVE and LOAD.


THE SYSTEM UTILITY TABLE
This table holds the startaddresses of  the  routines  that  are
used by the interpreter routines.

1AF7 TABLE_12  DEFB +00
               DEFW +1AFA,SUB_10
1AFA SUB_12    DEFW +0FE5,CAL_PHY     Call the hardware.
               DEFW +1540,T/R_BREAK   Test/report 'BREAK' key.
               DEFW +016D,CONTSIG_2   Keep signalling an error.
               DEFW +050E,CHK_END     Check for end of statement
               DEFW +053A,CHK_CHAN    Check channel parameters.
               DEFW +0525,TEST_SEP    Check for a separator.
               DEFW +0532,USE_M1      Pass -1 to the calc. stack
               DEFW +0818,MAKE_CHAN   Make channel in memory.
               DEFW +0863,TEST_C      Test channel parameters.
               DEFW +05FB,CL_T_CHNS   Clear all temporary chans.
               DEFW +062F,CD_TO_STK   Copy code to the stack.
               DEFW +0656,EXEC_STK    Execute code in the stack.


THE FILE MANIPULATION TABLE
This table holds the startaddresses of  the  routines  that  are
used by the OPEN_CHAN, CLOSE_CHN and SAVE_CHAN routines.

1B12 TABLE_14  DEFB +00
               DEFW +1B15,SUB_14
1B15 SUB_14    DEFW +0DD4,MAKE_CAT    Make a 'CAT' channel.
               DEFW +0C63,PUT_BLOCK   Put a block on disk.
               DEFW +0E3D,FIND_FILE   Search catalog for file.
               DEFW +0EA4,MAKE_FILE   Make a file in catalog.
               DEFW +0E64,GET_FREE    Get free space on the disk
               DEFW +0F48,POSN_BC     Position 'CAT'-file at BC.
               DEFW +0F3F,READ_CAT    Read from the 'CAT' chan.
               DEFW +0B6E,WRITE_CT    Write to the 'M' channel.


THE CHANNEL TABLE 1
This table holds the startaddresses of the subtables for all the
channels. The pointers allow writing, reading, opening, closing,
length calculation and positioning of channels to be done.

1B25 TABLE_04  DEFB +20               ' '
               DEFW +0A75,TAB_4:M
               DEFB +6D               'M'
               DEFW +0A75,TAB_4:M
               DEFB +23               '#'
               DEFW +08EA,TAB_4:#
               DEFB +62               'B'
               DEFW +0954,TAB_4:B
               DEFB +74               'T'
               DEFW +0948,TAB_4:T
               DEFB +6B               'K'
               DEFW +089C,TAB_4:KSP
               DEFB +73               'S'
               DEFW +089C,TAB_4:KSP
               DEFB +70               'P'
               DEFW +089C,TAB_4:KSP
               DEFB +6A               'J'
               DEFW +11BC,TAB_4:J
               DEFB +64               'D'
               DEFW +1032,TAB_4:D
               DEFB +EF               'CAT'
               DEFW +0DC0,TAB_4:CAT
               DEFB +AF               'CODE'
               DEFW +0FFF,TAB_4:COD
               DEFB +00               Other
               DEFW +1B4C,ERROR_04
1B4C ERROR_04  DEFW +08E4,WRITE_KSP   Write.
               DEFW +08C6,READ_KSP    Read.
               DEFW +0594,REPORT_2A   Open (error-message).
               DEFW +1246,REPORT_k    Close (error-message).
               DEFW +1246,REPORT_k    Length (error-message).
               DEFW +1246,REPORT_k    Position (error-message).


THE CHANNEL TABLE 2
This table holds the startaddresses of the subtables of the ' ',
'M' and 'J' channels. The subtables  allow  erase,  catalog  and
format to be done to the channel.

1B58 TABLE_06  DEFB +20               ' '
               DEFW +0C8B,TAB_6:' '
               DEFB +6D               'M'
               DEFW +0C83,TAB_6:'M'
               DEFB +6A               'J'
               DEFW +1B66,TAB_6:'J'
               DEFB +00               Other
               DEFW +1B64,ERROR_06
1B64 ERROR_06  DEFW +0594,REPORT_2A   Error-message.
1B66 TAB_6:'J' DEFW +0594,REPORT_2A   Erase (error-message).
               DEFW +0594,REPORT_2A   Inquire (error-message).
               DEFW +0594,REPORT_2A   Catalog (error-message).
               DEFW +11DE,FORMAT_J    Format.


THE DEVICE TABLE
This table holds the startaddresses  of  the  subtables  of  the
devices. The subtables allow read, write, inquire and format  to
be done to the device.

1B6E TABLE_08  DEFB +01               Drive 1.
               DEFW +123A,TAB_8:M
               DEFB +02               Drive 2.
               DEFW +123A,TAB_8:M
               DEFB +03               Drive 3.
               DEFW +123A,TAB_8:M
               DEFB +04               Drive 4.
               DEFW +123A,TAB_8:M
               DEFB +82               Joystick port.
               DEFW +11EE,TAB_8:J
               DEFB +05               Ramdisk 48K.
               DEFW +1749,TAB_8:5
               DEFB +06               Ramdisk 128K.
               DEFW +178E,TAB_8:6
               DEFB +81               Printer port.
               DEFW +1814,TAB_8:T
               DEFB +00               Other.
               DEFW +1B89,ERROR_08
1B89 ERROR_08  DEFW +1246,REPORT_k    Write (error-message).
               DEFW +1246,REPORT_k    Read (error-message).
               DEFW +0DE0,REPORT_c    Inquire (error-message).
               DEFW +0DE0,REPORT_c    Format (error-message).


THE DISK-INFO TABLE #0A IN ROM
This table holds the startaddresses of the  subtables  with  the
information for the drives and the ramdisks. It is not used when
the RAM tables are used (they include their own disk-info table)

1B91 TABLE_0A  DEFB +01               Drive 1.
               DEFW +18E5,DRIVE_1
               DEFB +02               Drive 2.
               DEFW +18F3,DRIVE_2
               DEFB +03               Drive 3.
               DEFW +1901,DRIVE_3
               DEFB +04               Drive 4.
               DEFW +190F,DRIVE_4
               DEFB +05               Ramdisk 48K.
               DEFW +191D,RAMD_48
               DEFB +06               Ramdisk 128K.
               DEFW +1924,RAMD_128
               DEFB +00               Other.
               DEFW +18AD,COPY_RAM2


THE PRINT CHARACTER TABLE
This table holds the ASCII-values of the characters that can  be
printed when emulating the ZX PRINTER.

1BA6 TABLE_16  DEFB +06               Print ,
               DEFW +09B2,PRINT_,
               DEFB +08               Cursor left
               DEFW +09FD,PR_CRSLEF
               DEFB +09               Cursor right
               DEFW +09A4,PR_CRSRIG
               DEFB +10               Ink
               DEFW +09AF,PRINT_2ND
               DEFB +11               Paper
               DEFW +09AF,PRINT_2ND
               DEFB +12               Flash
               DEFW +09AF,PRINT_2ND
               DEFB +13               Bright
               DEFW +09AF,PRINT_2ND
               DEFB +14               Inverse
               DEFW +09AF,PRINT_2ND
               DEFB +15               Over
               DEFW +09AF,PRINT_2ND
               DEFB +16               At
               DEFW +09A9,PRINT_AT
               DEFB +17               Tab
               DEFW +09AD,PRINT_TAB
               DEFB +00               Any other charater
               DEFW +09C1,PR_OTHER


THE FORMAT-DATA TABLE
This table holds the  addresse  of  the  data  that  the  format
routine uses, and also the code for the boot-block.

1BCA TABLE_18  DEFB +01               Format type special
               DEFW +1C09,TAB_18_1
               DEFB +00               Format type normal
               DEFW +1BD0,TAB_18_0
1BD0 TAB_18_0  DEFW +1BD8,TAB_18_00   Entry 1
               DEFW +1BDB,TAB_18_01   Entry 2
               DEFW +1C00,TAB_18_02   Entry 3
               DEFW +1C3E,LOST_BLCK   Location boot-block.
1BD8 TAB_18_00 DEFB +02               Length-byte.
               DEFB +0B, +4E
1BDB TAB_18_01 DEFB +24               Length-byte.
               DEFB +18, +4E
               DEFB +0C, +00
               DEFB +03, +F5
               DEFB +01, +FE
               DEFB +01, +F1          (track)
               DEFB +01, +F0          (side)
               DEFB +01, +F4          (sector)
               DEFB +01, +F2          (block-size)
               DEFB +01, +F7
               DEFB +16, +4E
               DEFB +0C, +00
               DEFB +03, +F5
               DEFB +01, +FB
               DEFB +F3, +E5
               DEFB +F3, +E5
               DEFB +F3, +E5
               DEFB +F3, +E5
               DEFB +01, +F7
1C00 TAB_18_02 DEFB +08               Length-byte.
               DEFB +00, +4E
               DEFB +00, +4E
               DEFB +00, +4E
               DEFB +00, +4E
1C09 TAB_18_1  DEFW +1C11,TAB_18_10   Entry 1.
               DEFW +1C14,TAB_18_11   Entry 2.
               DEFW +1C35,TAB_18_12   Entry 3.
               DEFW +1C3E,LOST_BLCK   Location boot-block.
1C11 TAB_18_10 DEFB +02               Length-byte.
               DEFB +06, +FF
1C14 TAB_18_11 DEFB +20               Length-byte.
               DEFB +0A, +FF
               DEFB +06, +00
               DEFB +01, +FE
               DEFB +01, +F1          (track)
               DEFB +01, +F0          (side)
               DEFB +01, +F4          (sector)
               DEFB +01, +F2          (block-size)
               DEFB +01, +F7
               DEFB +0B, +FF
               DEFB +06, +00
               DEFB +01, +FB
               DEFB +F3, +E5
               DEFB +F3, +E5
               DEFB +F3, +E5
               DEFB +F3, +E5
               DEFB +01, +F7
1C35 TAB_18_12 DEFB +08               Length-byte.
               DEFB +00, +FF
               DEFB +00, +FF
               DEFB +00, +FF
               DEFB +00, +FF

The next block is stored on a  specially  reserved  block.  It's
number can be found by subtracting from 65536 the 4th (lo-)  and
5th (hi-) byte in the disk-info table (table #0A).

1C3E LOST_BLCK JR   1C45,GO_BOOT      Step over the databytes.
               DEFB +28               40 tracks.
               DEFB +12               18 sectors/track.
               DEFB +40               256 bytes a block, SS.
               DEFW +0000             Room for unique number.
1C45 GO_BOOT   LD   A,(HL)            Get number of tracks.
               LD   (IX+00),A         Store it in the table.
               INC  HL
               LD   A,(HL)            Get number of sectors.
               LD   (IX+01),A
               LD   A,(IX+02)         Get disk-info byte.
               AND  +2F               This is %00101111
               LD   D,A               Store drive-info.
               INC  HL
               LD   A,(HL)            Get disk-info from disk.
               AND  +D0               This is %11010000
               OR   D                 Add disk + drive info.
               LD   (IX+02),A         Store it in the table.
               RET                    Finished.

The addresses +1C5D to +1FFD are not used  in  version  2.2  and
contain +FF's.

1FFE TAB_POINT DEFW +18B6,MAIN_TABL   Pointer to ROM tables.

After this ROM comes the 2K RAM, to which the tables are copied.