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.