THE 'RESET' RESTART
This is the normal 'reset'. Addresses 1019 and 101A contain zero
so the returnaddress is 0, the 'restart'-routine in the Spectrum
ROM.
0000 RESET DI
LD SP,+1019 Stack 0000 and return to
JP 1748,PAGE_OUT the Spectrum ROM.
DEFB +FF
THE 'SHADOW_ROM ERROR' RESTART
The Discovery-ROM error handler. It takes a one-byte immediate
error code.
0008 SH_ERROR JP 0168,CONTSIG
THE 'ENTRY_1' ROUTINE
This is the entry point when coming from a RST 0008-instruction
when the Spectrum ROM is paged in. When a RST 0008 is encoun-
tered, the instruction 'LD HL,(CH_ADD)' is executed, the
Discovery ROM is paged in, and the execution continues at
address 000B.
000B ENTRY_1 POP HL
PUSH AF
JP 007A,ENTRY_2 Jump forward.
THE 'CALL A SPECTRUM ROM SUBROUTINE' RESTART
This restart calls a subroutine in the Spectrum ROM. The address
of the subroutine should be put immediately after the RST 0010
(or CALL 0010) instruction.
0010 CAL_SPEC EX (SP),HL Pass HL to IY and fetch
POP IY the returnaddress in HL.
PUSH DE Store the old value of DE.
LD E,(HL) Read the required address
INC HL in DE. (continues forward)
JR 0067,CAL_SPEC2
THE 'RUNNING OR CHECKING SYNTAX ?' RESTART
Returns the zero flag set if the Spectrum is currently checking
syntax rather than running a statement.
0018 TEST_RUN BIT 7,(FLAGS)
RET
DEFB +FF
THE 'PRINT THE COPYRIGHT MESSAGE' ROUTINE
001E (C)_MSG RST 0008,SH_ERROR Report
DEFB +47 (c) Dave Corney 1984-86
THE 'NEXT CHARACTER' RESTART
Identical to the Spectrum RST 0020. Gets the next character from
the current BASIC-line.
0020 NEXT_CHAR RST 0010,CAL_SPEC Call Spectrum ROM.
DEFW +0020,NEXT_CHAR
RET
DEFB +FF, +FF, +FF Unused locations.
DEFB +FF
THE 'FLOATING POINT CALCULATOR' RESTART
Identical to the Spectrum ROM RST 0028, except that a length
byte should be inserted as first offset (nr. of offsets + 2).
0028 FP_CALC POP HL Fetch the returnaddress in
LD C,(HL) HL and the lengthbyte in C
LD B,+00
PUSH HL Restack returnaddress
JP 0648,FP_CALC_2
THE 'LOOKUP' RESTART
This is the most important restart. It gives access to all the
tables which the Discovery uses. It should be called by loading
the table-entry in the B-register, and placing a 1-byte immedia-
te table-number after the RST 0030.
0030 LOOKUP POP HL Read the table-number in
LD C,(HL) the C-register and restore
INC HL the Program Counter.
PUSH HL
JP 05B1,LOOKUP_2 Jump to continue.
THE 'MASKABLE INTERRUPT' ROUTINE
It behaves exactly the same as the Spectrum's RST 0038.
0038 MASK_INT PUSH IY Call the Spectrum ROM, but
RST 0010,CAL_SPEC also preserve the IY-regis
DEFW 0038,MASK_INT ter.
POP IY
RET
DEFB +FF, +FF, +FF Unused locations.
DEFB +FF, +FF, +FF
DEFB +FF, +FF
THE 'KEY_INT' ROUTINE
This routine initialises the IC 6116's RAM and continues with
the KEY_INT subroutine in the Spectrum ROM at address +004A.
0048 KEY_INT PUSH BC Prepare the stack for the
PUSH HL KEY_INT subroutine.
PUSH DE
PUSH BC
PUSH AF
LD A,(3001)
RRCA Is IC 6116 present and
CALL NC,1554,INIT_RAM2 initialised? Try to initia
lise the RAM if not.
RST 0030,LOOKUP Lookup the address to page
DEFB +0E out and/or return.
CALL 0087,CALL_JP Perform that operation.
POP AF Restore the stack.
POP BC
POP DE
LD HL,+0049 The address in the Spec-
EX (SP),HL trum ROM. Store it.
JP 1748,PAGE_OUT Make an indirect jump via
the PAGE_OUT subroutine.
THE 'NON-MASKABLE INTERRUPT' ROUTINE
This is the address the program is forced to call during a NMI.
In the Spectrum ROM the routine contains a 'bug', but the
Discovery uses it to call the hardware directly. On entry the
HL-reisterpair contains the address of the appropriate service-
routine (load, save or format).
0065 INT_ALL RST 0038,MASK_INT This address is called if
both interrupttypes should
be taken care of.
0066 NMI JP (HL)
THE 'CALL A SPECTRUM ROM ROUTINE (2)' ROUTINE
The continuation of the CAL_SPEC restart at 0010. The stack is
set up to make sure that the program resumes its execution at
the address following the DEFW-instruction. The return address
is stacked, followed by the PAGE_IN address and the ROM address
to be called.
0067 CAL_SPEC2 LD D,(HL) Take higher part of DEFW.
INC HL Move to the return address
EX (SP),HL Stack return address.
EX DE,HL Pass the ROM address to HL
PUSH HL and stack it too.
LD HL,+1708 Exchange with PAGE_IN.
EX (SP),HL
PUSH IY
EX (SP),HL Exchange again.
LD IY,+5C3A Restore IY.
JP 1748,PAGE_OUT Call the required routine
indirectly.
THE 'ENTRY_2' ROUTINE
The HL register-pair points to the address of the error-code or
hook-code (possibly in the Spectrum ROM). It must be fetched and
the hook-code table is searched. Finally a jump if made to the
correct routine.
007A ENTRY_2 RST 0010,CAL_SPEC Fetch err-nr or hook-code.
DEFW +007B,TEMP_PTR2+3
INC HL HL := returnaddress.
EX (SP),HL Stack HL; HL := AF.
PUSH HL Stack AF.
PUSH DE Store DE temporarely.
LD B,+FF Signal: no subtables.
RST 0030,LOOKUP Lookup the startaddress in
DEFB +00 the hook-code table.
POP DE
POP BC BC := AF.
0087 CALL_JP JP (HL) Jump to the routine.
THE 'VERSION' HOOKCODE
The version number of this Discovery ROM is put on the calcula-
tor stack.
0088 VERSION RST 0028,FP_CALC Call the calculator.
DEFB +09 (lengthbyte)
DEFB +34,stk-data 2.2
DEFB +F2, exponent +82
DEFB +0C, +CC, +CC, +CD
DEFB +38,end-calc 2.2
JR 009B,RET_SPEC1 Jump to page out.
THE 'LENGTH' HOOKCODE
The length of the current channel is calculated. The same as USR
432d.
0093 LENGTH_1 LD IX,(CURCHL) Fetch the startaddress.
CALL 03D9,LEN_CHAN Calculate the length.
POP HL Unstack unwanted addresses
009B RET_SPEC1 POP HL
JR 00CB,RET_SPEC2 Return to Spectrum ROM.
THE 'INITIALISE RAM' HOOKCODE
The IC 6116 is initialised. The same as USR 14070d.
009E INIT_RAM DI No interrupts allowed.
CALL 1554,INIT_RAM2 Initialise IC 6116.
EI
JR 00CB,RET_SPEC2 Return to Spectrum ROM.
THE 'CHARACTER I/O' HOOKCODE
The A register is used for input or output. DE points to the
appropriate address in the channel descriptor, so the offset is
0 for output and 2 for input. The offset is used as an index in
the subtable for that channel, and a jump is made to the correct
routine.
00A5 CHAR_IO RES 3,(TV-FLAG) Signal: mode not changed.
PUSH BC BC contains AF.
LD HL,(CURCHL) HL := current channel.
PUSH HL Copy HL to IX.
POP IX
LD A,(IX+04) A := channel name.
OR +20 Force to lower case.
EX DE,HL Switch pointers.
SCF
SBC HL,DE HL := HL - DE - 1.
LD B,L Output if B=0, input if 2.
RST 0030,LOOKUP Lookup the startaddress in
DEFB +04 channel-info table.
POP AF Restore AF.
CALL 0087,CALL_JP Call the current routine.
THE 'INPUT EXTRA CHARACTERS' ROUTINE
If the CHAR_IO routine has been called by the INPUT command,
then continue to read or write the rest of the string until a
is encountered. Else, the routine was used by the INKEY$
#stream command and no extra characters may be read.
00C0 INPUT_EX POP HL Unstack unwanted addresses
POP HL
EXX
PUSH AF
BIT 5,(FLAG-X) INPUT or INKEY$ mode?
JR NZ,00CE,INPUT_EX2 INPUT? Then continue.
POP AF
00CB RET_SPEC2 JP 1748,PAGE_OUT Jump to Spectrum ROM.
00CE INPUT_EX2 POP AF Read or write successfull?
JR NC,00CB,RET_SPEC2 Exit if not.
CP +0D Is it a ?
JR Z,00DD,INPUT_CR Step if so.
RST 0010,CAL_SPEC Call ADD_CHAR to process
DEFW +0F85,ADD_CHAR this character.
CALL 0623,NEAT_RET Jump to ED_LOOP, enter a
DEFW +0F38,ED_LOOP loop.
00DD INPUT_CR CALL 0623,NEAT_RET Jump to ED_ENTER, finish
DEFW 1025,ED_ENTER the loop.
THE 'MDRIVE' HOOKCODE
If the microdrive system variables are not present already, then
they are created. However, no extra channels may be created.
00E2 M_DRIVE LD HL,(CHANS) HL := start of channels.
LD DE,+5CB6 DE := start of MD maps.
AND A Test if the microdrive
SBC HL,DE system variables are pre-
JR NZ,009B,RET_SPEC1 sent. Exit if so.
00ED M_DRIVE2 EX DE,HL HL := start of MD maps - 1
DEC HL
LD BC,+003A Reserve 58 bytes.
RST 0010,CAL_SPEC Make the room.
DEFW +1655,MAKE_ROOM
JR 009B,RET_SPEC1 Jump to Spectrum ROM.
THE 'HOOKCODE ERROR' ROUTINE
If it is not a Spectrum error, then report i is given.
00F7 HOOKERROR CP +1B Is it not a Spectrum error
JR C,00FD,HOOKERR2 then skip.
00FB REPORT_i RST 0008,SH_ERROR Report 'Hookcode error'.
DEFB +31
00FD HOOKERR2 LD (ERR-NR),A Store the error-code.
THE 'BASIC ERROR' HOOKCODE
If it is an error generated by an extended command, then clean
up the memory and the stack and jump to the correct error-hand-
ling routine.
0100 BASIC_ERR LD HL,(CH-ADD) Store the address of the
LD (X-PTR),HL current character.
BIT 7,(ERR-NR) Did an error occur?
LD (ERR-NR),A (Store the error-code)
JR Z,016D,CONTSIG_2 then continue signalling.
BIT 7,(SUBPPC) Statementnr > 127?
JR NZ,016D,CONTSIG_2 then signal the error.
0115 BAS_ERR2 LD DE,(WORKSP) DE := end of E-LINE + 1
AND A
SBC HL,DE Current character past end
ADD HL,DE of the line?
JR NC,016D,CONTSIG_2 then signal the error.
LD DE,(E-LINE)
AND A
SBC HL,DE Current character in edit-
JR C,0131,BAS_ERR4 buffer? step if not.
0128 BAS_ERR3 RST 0010,CAL_SPEC Get the line-number in the
DEFW +19FB,E_LINE_NO edit-buffer.
LD HL,(CH-ADD) Prepare HL and step.
DEC HL
JR 013E,BAS_ERR5
0131 BAS_ERR4 LD HL,(PPC) Fetch current line-number.
BIT 7,H Give an error if past the
JR NZ,016D,CONTSIG_2 program area.
RST 0010,CAL_SPEC Get the startaddress of
DEFW +196E,LINE_ADDR this BASIC line.
INC HL Make HL point one byte
INC HL before the first character
INC HL of the line.
013E BAS_ERR5 LD D,(SUBPPC) Find the startaddress of
LD E,+00 the current statement.
RST 0010,CAL_SPEC
DEFW +198B,EACH_STMT
RST 0018,TEST_RUN Running or checking syntax
JR NZ,014C,BAS_ERR6 Step if running.
RST 0010,CAL_SPEC Remove the hidden floating
DEFW +11A7,REMOVE_FP point forms in the line.
014C BAS_ERR6 RST 0010,CAL_SPEC Clear the working areas.
DEFW +16BF,SET_WORK
RST 0020,NEXT_CHAR Fetch the keyword in A.
LD B,+FF Signal: no subtables.
RST 0030,LOOKUP Lookup the startaddress of
DEFB +02 the service routine in the
keyword table.
PUSH HL Store it while preparing
RST 0020,NEXT_CHAR CH-ADD to point to the 1st
POP HL parameter.
CALL 0087,CALL_JP Call the service routine.
Open the 'S' channel and continue with the next statement.
015A OPEN_'S' LD A,+FE Open the 'S' channel.
RST 0010,CAL_SPEC
DEFW +1601,CHAN_OPEN
LD (ERR-NR),+FF Reset ERR-NR.
CALL 0623,NEAT_RET Return to the Spectrum ROM
DEFW +1B76,STMT_RET continue the program.
THE 'CONTINUE SIGNALLING OF AN ERROR' ROUTINE
All errors can be signalled. The error-code is fetched and set.
0168 CONTSIG POP HL HL points to the error-
LD A,(HL) code. Fetch it and store
LD (ERR-NR),A it.
THE 'CONTSIG_2' ROUTINE
Enter here when coming from the service routines of the
different commands. On entry ERR-NR holds the code of the error
to be reported. When an error occurs, a direct jump is made to
this address, so no error-code has to be fetched. A check is
made whether it was an edit-error or a runtime-error. If it was
an edit-error then a jump is made to the address that is pointed
to by ERR-SP. A third check is made for an error-routine at
address 5B1D, that is only present in 128K mode. This way BASIC
extensions such as Beta-Basic should still be compatible.
016D CONTSIG_2 RES 3,(TVFLAG) Signal: mode not changed.
RST 0010,CAL_SPEC Clear the calculator stack
DEFW +16C5,SET_STK
0174 CONTSIG_3 LD SP,(ERR-SP) Restore the stackpointer.
POP DE Fetch the error-address.
LD HL,+107F This is ED_ERROR.
AND A Test if it was an edit-
SBC HL,DE error.
JR NZ,0187,CONTSIG_4 Skip if not.
POP HL Fetch the error-address.
LD (ERR-SP),HL Prepare to check this
JR 0174,CONTSIG_3 address and loop back.
0187 CONTSIG_4 PUSH DE Restack the error-address.
LD HL,+1303 This is MAIN_4.
AND A Test if it was a runtime-
SBC HL,DE error.
JR Z,019E,CONTSIG_5 Jump if so.
BIT 4,(FLAGS) Test if in 128K mode.
JR Z,01BF,CONTSIG_6 Step out if not.
LD HL,+5B1D Test for an error-routine
AND A from this address.
SBC HL,DE
JR NZ,01BF,CONTSIG_6 Step out if not.
019E CONTSIG_5 LD A,(ERR-NR) Fetch the error-code.
PUSH AF Store it temporarely.
CP +57 Was it 'RAM corrupt'?
CALL NZ,05FB,CL_T_CHNS Tidy the chan area first
POP AF if not. Restore error-code
INC A Is it a Spectrum error?
CP +1C
JR C,01BF,CONTSIG_6 Step out if so.
CALL 062F,CD_TO_STK Copy code to stack:
DEFW +1303,MAIN_4 print error-number.
DEFW +0041 (length)
CALL 0656,EXEC_STK Execute the code.
DEC A A = error-code.
CALL 05EB,P_ERR_MSG Print the error-message.
LD HL,+1349 Prepare the returnaddress
EX (SP),HL print comma, line- and
statement number.
01BF CONTSIG_6 JP 1748,PAGE_OUT Return to the Spectrum ROM
THE 'AUTO_RUN' HOOKCODE
If there is no BASIC program in memory AND the only character in
the edit-buffer is 'RUN' then the file 'run' is loaded from
drive 1.
01C2 AUTO_RUN LD HL,(CHANS) Standard channels take 21
LD BC,+0019 bytes and an edit-buffer
ADD HL,BC with 1 command 3 bytes.
LD BC,(WORKSP) Test if this configuration
SBC HL,BC is true.
JR NZ,016D,CONTSIG_2 Otherwise report 'Program
finished'.
01D1 AUTO_RUN2 DEC BC BC := endmarker.
DEC BC BC := .
DEC BC BC := 'RUN'.
LD A,(BC) Get command-code.
CP +F7 Was it 'RUN'?
JR NZ,016D,CONTSIG_2 Exit if not.
01D9 AUTO_RUN3 LD HL,(E-LINE) HL := start of edit-buffer
PUSH HL Stack HL.
LD BC,+0009 Reserve 9 extra bytes.
PUSH BC Stack this number too.
RST 0010,CAL_SPEC Make the required room in
DEFW +1655,MAKE_ROOM the edit-buffer.
POP BC Restore 9.
INC BC Include the byte 'RUN'.
POP DE DE := start of edit-buffer
LD HL,+01F6 Start of the LOAD command.
LDIR Move it.
DEC (NS-PPC) Correct command pointer.
LD (T-ADDR),+01 Signal 'LOAD'.
01F3 REP_NONS RST 0010,CAL_SPEC Force Spectrum ROM to re-
DEFW +1C8A,REPORT_C port 'Nonsense in BASIC'.
01F6 LOAD_MSG DEFB +EF, +2A, +BC LOAD *SGN PI;"run"
DEFB +A7, +3B, +22
DEFB +72, +75, +6E
DEFB +22
THE 'OPEN #' COMMAND ROUTINE
On entry (CH-ADD) points to the first character after the
'OPEN #' token.
0200 OPEN_# RST 0010,CAL_SPEC Get a numerical value from
DEFW +1C82,EXPT_1NUM the BASIC line.
CALL 0525,TEST_SEP Seperator after it?
JP NZ,01F3,REP_NONS Report the error if not.
CALL 053A,CHK_CHAN Find a channel + next char
CP +BF Is it 'IN'?
JR NZ,0215,#_NOT_IN Step if not.
'IN' takes no parameters.
0210 #_IN RST 0020,NEXT_CHAR Get the next character.
LD B,+02 Signal: input.
JR 0258,#_CONT_1 Step forward.
0215 #_NOT_IN CP +DF Is it 'OUT'?
LD B,+05 Signal: output, create.
JR Z,0221,#_OUT_EXP Step if so.
CP +89 Is it 'EXP'?
JR NZ,022D,#_NOT_EXP Step if not.
LD B,+01 Signal: output.
'OUT' and 'EXP' can take one numerical parameter. If no
parameter follows, the default 0 is assumed.
0221 #_OUT_EXP RST 0020,NEXT_CHAR Get the next character.
RST 0010,CAL_SPEC End of statement reached?
DEFW +2048,PR_ST_END (so, no parameter?)
JR Z,0258,#_CONT_1 Step if so.
PUSH BC Store OPEN parameters.
RST 0010,CAL_SPEC Get a numerical value.
DEFW +1C82,EXPT_1NUM
JR 025C,#_CONT_2 Step forward.
022D #_NOT_EXP CP +A5 Is it 'RND'?
JR NZ,0256,#_NO_PAR Step if not.
If 'RND' takes only parameter, then the file should already
exist, else it should be created.
0231 #_RND_1 RST 0020,NEXT_CHAR Get the next character.
RST 0010,CAL_SPEC Get the (first) parameter.
DEFW +1C82,EXPT_1NUM
CALL 0525,TEST_SEP Another following?
JR Z,0241,#_RND_2 Then step.
CALL 0532,USE_M1 Use -1 as second parameter
LD +03 Signal: input, output.
JR 0246,#_RND_3 Step forward.
0241 #_RND_2 RST 0010,CAL_SPEC Get the second parameter.
DEFW +1C82,EXPT_1NUM
LD B,+07 Signal: in, out, create.
0246 #_RND_3 PUSH BC Store OPEN parameters.
CALL 050E,CHK_END Test for end of statement.
RST 0028,FP_CALC stream, P1, P2
DEFB +07 (lengthbyte)
DEFB +01,exchange stream, P2, P1
DEFB +C0,st-mem-0 (mem-0 holds P1)
DEFB +04,multiply stream, P2*P1 (filesize)
DEFB +E0,get-mem-0 stream, filesize, P1
DEFB +38,end-calc stream, filesize, P1
RST 0010,CAL_SPEC BC := P1 (record-length)
DEFW +1E99,FIND_INT2
JR 0262,#_CONT_3 Step forward.
0256 #_NO_PAR LD B,+00 Signal: no I/O, no create.
0258 #_CONT_1 PUSH BC Store OPEN parameters.
CALL 0532,USE_M1 Use -1 as default.
025C #_CONT_2 CALL 050E,CHK_END Test for end of statement.
LD BC,+0000 BC := record-length
BC contains the record-length (0 if not specified), the calcula-
tor stack holds the stream number (at the bottom) and the
required filesize at the top (-1 for 'IN').
0262 #_CONT_3 PUSH BC Store the record-length.
RST 0028,FP_CALC stream, filesize
DEFB +05 (lengthbyte)
DEFB +C0,st-mem-0 (mem-0 holds the filesize)
DEFB +02,delete stream
DEFB +38,end-calc stream
CALL 062F,CD_TO_STK Copy code to stack:
DEFW +1736,OPEN OPEN # command.
DEFW +0021 (length)
DEC HL point to relative address
DEC HL 1754.
LD (HL),+C9 Put a RET in place.
CALL 0656,EXEC_STK Execute the code.
JR Z,027A,#_CONT_4 If the stream was already
RST 0008,SH_ERROR open and not K, S or P,
DEFB +2A then report the error.
027A #_CONT_4 PUSH HL Store channel-address in
the stream-info table.
RST 0028,FP_CALC -
DEFB +04 (lengthbyte)
DEFB +E0,get-mem-0 filesize
DEFB +38,end-calc filesize
POP DE DE := address in stream-
POP HL info table, HL := record-
POP AF length, AF := OPEN flags.
PUSH DE Store DE again.
CALL 06A9,OPEN_CHAN Now open the channel.
POP HL
LD BC,+5C18 BC := start of streams.
AND A Is HL an internal channel?
SBC HL,BC
ADD HL,BC
JR C,0294,#_CONT_5 Then skip.
RES 5,(IX+04) Make channel permanent.
0294 #_CONT_5 EX DE,HL DE := address in stream-
info table
CALL 080C,GETOFFSET HL := offset of channel.
EX DE,HL Swith pointers again.
LD (HL),E Store the offset in the
INC HL stream-info table.
LD (HL),D
RET Finished.
THE 'LOAD, SAVE, VERIFY AND MERGE' COMMAND ROUTINES
The A register holds the first character after the keyword,
(T-ADDR) holds 0 for SAVE, 1 for LOAD, 2 for VERIFY and 3 for
MERGE. The length of the file is the length of the data + 7,
since the first 7 bytes are used to store header apart from the
filename. In case of SAVE, the channel should be opened for
output and create, in all other cases just for input. The
routine uses large parts of the tape-routine in the Spectrum
ROM, so sufficient room for the stack is needed.
029D LD_SA_ETC CP +2A Is it '*'?
JP NZ,016D,CONTSIG_2 Signal the error if not.
RST 0018,TEST_RUN Run-time or checking?
JR Z,02AB,LD_SA_2 Skip if checking.
LD BC,+0022 Make 34 bytes room, for 2
RST 0010,CAL_SPEC headers.
DEFW +0030,BC_SPACES
02AB LD_SA_2 PUSH IX Store 128K register.
PUSH DE Store start of the room.
RST 0020,NEXT_CHAR Point to the next char.
CALL 053A,CHK_CHAN Find a channel + next char
CALL 062F,CD_TO_STK Copy code to stack:
DEFW +0652,SA_DATA test parameters.
DEFW +0109 (length)
LD IX,+0071 IX := start of code + 113.
ADD IX,DE
LD (IX-26),+C9 Put a RET at relative
address 069D.
LD A,+C3 1FC3 = UNSTACK_Z.
CALL 0319,STOREADDR Store this address in
INC IX stead of 1BEE (CHECK_END)
LD A,+1F in order to keep the stack
CALL 0319,STOREADDR also when checking syntax.
POP IX IX := start of headers.
CALL 0656,EXEC_STK Execute the code.
02D4 LD_SA_3 EX (SP),IX Stack header-start, fetch
PUSH HL 128K register.
CALL 050E,CHK_END Store start for LOAD, SAVE
POP HL etc while testing for the
end of the statement.
LD (K-CUR),HL Store HL, since K-CUR is
POP IX also moved when creating a
PUSH HL channel.
LD B,(IX+0C) BC := length of datablock.
LD C,(IX+0B)
RST 0010,CAL_SPEC Stack BC.
DEFW +2D2B,STACK_BC
RST 0028,FP_CALC len data
DEFB +09 (lengthbyte)
DEFB +34,stk-data len data,7 (len file-info)
DEFB +40
DEFB +B0, exponent +00
DEFB +00, +07, (+00, +00)
DEFB +0F,addition len data + len file-info
DEFB +38,end-calc file-size
LD A,(T-ADDR) Fetch the command type.
AND A Was it 'SAVE'?
PUSH AF (store the command type)
LD A,+05 Signal: output, create.
JR Z,02FE,LD_SA_4 Step if 'SAVE'.
LD A,+02 Signal: input.
02FE LD_SA_4 LD HL,+0000 HL := record-length.
CALL 06A9,OPEN_CHAN Now open the channel.
POP AF Restore command type.
POP DE DE := start of data.
LD HL,(STK-END) Has the startaddress been
SBC HL,DE shifted due to the crea-
LD HL,(WORKSP) tion of a channel?
EX DE,HL Then use the saved value
JR C,0314,LD_SA_5 instead.
LD HL,(K-CUR)
0314 LD_SA_5 CALL 06F4,SAVE_CHAN Now execute the command.
JR 0368,CLOSE_CHN And close the channel.
Store the address of UNSTACK_Z rather than CHECK_END into the
correct locations in the stackcode, otherwise the stack will be
severely corrupted when checking syntax, which will cause a
system crash. The location are (relative) 069A, 06AE, 06FA, 0716
and 072F.
0319 STOREADDR LD (IX-29),A
LD (IX-15),A
LD (IX+37),A
LD (IX+58),A
LD (IX+6C),A
RET
THE 'MOVE' COMMAND ROUTINE
The first channel is checked and opened for input, then the
second channel is checked and opened for output and create. Then
the input-channel is moved to the output-channel, until the end
of the input-file has been reached. Finally the two channels are
closed. If an error occurs in checking or opening the
output-channel, then the input-channel has already been created.
Eg. MOVE 1;"name" TO "x"
0329 MOVE CALL 053A,CHK_CHAN Find a channel + next char
CP +CC Is it 'TO'?
JP NZ,01F3,REP_NONS Report the error if not.
RST 0018,TEST_RUN Run-time or checking?
JR Z,0341,MOVE_2 Skip if checking.
CALL 0532,USE_M1 Use -1.
LD +02 Signal: input.
LD HL,+0000 HL := record-length.
CALL 06A9,OPEN_CHAN Open the channel.
PUSH IX Store input-channel.
0341 MOVE_2 RST 0020,NEXT_CHAR Get the next character.
CALL 053A,CHK_CHAN Find a channel + next char
CALL 050E,CHK_END test for end of statement.
POP IX IX := start of input-chan
PUSH IX
CALL 03D9,LEN_CHAN Fetch channel-length.
LD A,+05 Signal: output, create.
LD HL,+0000 HL := record-length.
CALL 06A9,OPEN_CHAN Open the channel.
RST 0010,CAL_SPEC Clear workspace and the
DEFW +16BF,SET_WORK calculator stack.
POP HL HL := start of input-chan
IX := start of output-chan
CALL 06BF,MOVE_CHAN Move the channel.
LD (K-CUR),HL Store start of input-chan
CALL 0368,CLOSE_CHN Close the output-channel.
LD IX,(K-CUR) Prepare to close the input
channel.
THE 'CLOSE A CHANNEL' ROUTINE
On entry the IX registerpair holds the start of the channel. If
a change is made to the buffer then it is written to disk. No
check is made whether the file is still on the drive or an other
disk has been inserted.
0368 CLOSE_CHN LD B,+06 Signal: close.
CALL 03DB,CALL_CHAN Close the channel.
THE 'CLEAR A CHANNEL' ROUTINE
On entry the IX registerpair holds the start of the channel. The
channel is removed from memory, the space is reclaimed and the
relevant pointers are adjusted. The buffer is NOT written to
disk.
036D CLEAR_CHN CALL 080C,GETOFFSET HL := offset of channel.
LD BC +0014 The internal channels (K,
AND A S, R and P) take 20 bytes.
SBC HL,BC Is HL an internal channel?
RET C Finished if it is.
ADD HL,BC Restore HL.
PUSH HL Store the offset.
PUSH IX Pass the start of the
POP HL Channel to HL.
LD C,(IX+05) Fetch channel-length in BC
LD B,(IX+06)
PUSH BC Keep a copy on the stack.
RST 0010,CAL_SPEC Reclaim the space.
DEFW +19E8,RECLAIM_2
CALL 062F,CD_TO_STK Copy code to stack:
DEFW +1663,POINTERS-1 Adjust pointers (start-1!)
DEFW +0023 (length)
LD H,D Change the first two in-
LD L,E structions in the code.
LD (HL),+42 LD B,D
INC HL
LD (HL),+48 LD C,E
INC HL
INC HL
INC HL
LD (HL),+16 Start at 5C16, the start
INC HL of the stream-info table.
INC HL
INC HL
LD (HL),+10 And consider all 16 chans.
POP BC BC := channel-length.
LD A,B BC := - BC.
CPL
LD B,A
LD A,C
CPL
LD C,A
INC BC
POP HL Restore the offset.
JP 0656,EXEC_STK Execute the code and exit.
THE 'POINT' COMMAND ROUTINE
On entry the A register holds the first character after the
keyword. The parameters are passed to the calculator stack and a
jump is made to the required routine.
03AA POINT CP +23 Is it a '#'?
JP NZ,01F3,REP_NONS Report the error if not.
RST 0020,NEXT_CHAR Get the next character.
RST 0010,CAL_SPEC Read the stream number.
DEFW +1C82,EXPT_1NUM
CALL 0525,TEST_SEP Test for a separator.
JP NZ,01F3,REP_NONS Report the error if absent
RST 0010,CAL_SPEC Read the position.
DEFW +1C82,EXPT_1NUM
CALL 050E,CHK_END Test for end of statement.
RST 0028,FP_CALC stream, position
DEFB +04 (lengthbyte)
DEFB +01,exchange position, stream
DEFB +38,end-calc position, stream
RST 0010,CAL_SPEC Fetch the stream-data in
DEFW +171E,STR_DATA the BC registerpair.
LD A,B Stream not initialized?
OR C
JR NZ,03CD,POINT_2 Skip if initialised.
03CA REPORT_O RST 0010,CAL_SPEC Report 'Invalid stream'.
DEFW +160E,REPORT_O
03CD POINT_2 LD IX,(CHANS) IX := start of channels.
ADD IX,BC Add the offset.
DEC IX Form the startaddress.
03D5 POS_CHAN LD B,+0A Signal: position
JR 03DB,CALL_CHAN And skip.
03D9 LEN_CHAN LD B,+08 Signal: length
THE 'CALL A CHANNEL' ROUTINE
On entry the B register contains 0 for save, 2 for load, 4 for
open, 6 for close, 8 for length and 10 for position. A contains
the OPEN flag. Table 4 is searched with the channel-identifier
in A and a jump is made to the apropriate routine.
03DB CALL_CHAN PUSH AF Store the OPEN flags.
LD A,(IX+04) Get the channel-name.
OR +20 Force to lower case.
RST 0030,LOOKUP Lookup the startaddress in
DEFB +04 the channel-info table.
POP AF Restore the OPEN flags.
JP (HL) Jump to the routine.
THE 'CLEAR' COMMAND ROUTINE
On entry the A register holds the first character after the
keyword. A test is made for 'CLEAR #', 'CLEAR DATA' and 'CLEAR
LINE'.
03E5 CLEAR CP +CA Is it 'LINE'?
JR NZ,0427,CL_N_LINE Step if not.
The command 'CLEAR LINE , ' is to be used to
remove all BASIC lines from to inclusive.
03E9 CLEAR_LN RST 0010,CAL_SPEC Pass the line numbers to
DEFW +1C79,NEXT_2NUM the calculator stack.
CALL 050E,CHK_END Test for end of statement.
CALL 04A7,LINE_STRT HL := last line address.
JR NZ,03F8,CLEAR_LN2 Skip if in program area.
RST 0010,CAL_SPEC Otherwise point to the
DEFW +19B8,NEXT_ONE variables area.
EX DE,HL
03F8 CLEAR_LN2 PUSH HL Store last line address.
CALL 04A7,LINE_STRT HL := first line address.
POP BC BC := last line address.
SBC HL,BC Last line before first? Or
RET NC both equal? Then exit.
ADD HL,BC Restore HL.
EX DE,HL DE := first line address.
LD HL,(CH-ADD) Test if DE <= CH-ADD <= BC
CALL 04B6,CHK_LINE
JR C,0413,CLEAR_LN3 Skip if not.
LD H,D The current line should be
LD L,E removed too. Make the com-
LD (NXTLIN),HL mand pointer point to the
DEC HL first command in the first
LD (CH-ADD),HL line after the block.
0413 CLEAR_LN4 LD HL,(DATADD) Test if DE <= DATADD <= BC
CALL 04B6,CHK_LINE
JR C,0421,CLEAR_LN5 Skip if not.
DEC DE Reinitialize it to the
LD (DATADD),DE first line after the block
INC DE (Restore DE)
0421 CLEAR_LN5 LD H,B HL := last line address.
LD L,C
RST 0010,CAL_SPEC Delete all line from DE to
DEFW +19E5,RECLAIM_1 HL inclusive.
RET Finished.
0427 CL_N_LINE CP +E4 Is it 'DATA'?
JR NZ,045D,CL_N_DATA Step if not.
The command 'CLEAR DATA ' removes the BASIC variable.
may be either a simple variable or an array, in which
case the name should have () after it, eg. 'CLEAR DATA A()'.
These commands are particularly useful when overlaying parts of
BASIC programs from disk or ram disk.
042B CLEAR_DT RST 0020,NEXT_CHAR Get the next character.
RST 0010,CAL_SPEC Find the startaddress of
DEFW +28B2,LOOK_VARS variable in the VARS area.
PUSH HL Store end of var-name.
LD A,C Fetch discriminator in A.
RLA Now toggle bit 5 without
AND +C1 disturbing the carry flag
XOR +40 (set if found, reset if
SRL A not).
PUSH AF Store the result.
Bit 5 and 6 determine the var-type.
00 = numerical variable with name-length > 1.
10 = simple numerical/string variable or FOR/NEXT variable.
11 = string array
01 = numerical array
JR Z,0449,CLEAR_DT3 Jump if var-name > 1 char.
043B CLEAR_DT2 RST 0010,CAL_SPEC Fetch current character
DEFW +0018,GET_CHAR again.
CP +28 Is it a '('? (ie. array?)
JR NZ,0449,CLEAR_DT3 Step if not.
RST 0020,NEXT_CHAR But if it is, then check
CP +29 that the next character is
the finishing ')'.
JP NZ,01F3,REP_NONS Report the error if not.
RST 0020,NEXT_CHAR Point to next character.
0449 CLEAR_DT3 CALL 050E,CHK_END Test for end of statement.
POP AF Restore discriminator.
POP HL Restore pointer to var.
RET C Exit if var. doesn't exist
JR NZ,0456,CLEAR_DT5 Step if var-name = 1 char.
0451 CLEAR_DT4 DEC HL Find the first character
BIT 7,(HL) of the var-name.
JR Z,0451,CLEAR_DT4
0456 CLEAR_DT5 RST 0010,CAL_SPEC Point to the next variable
DEFW +19B8,NEXT_ONE in the variables area.
RST 0010,CAL_SPEC And erase the requested
DEFW +19E8,RECLAIM_2 variable.
RET Finished.
045D CL_N_DATA CP +23 Is it '#'?
JP NZ,016D,CONTSIG_2 Report the error if not.
The command 'CLEAR #' is used to clear the channel that
is attached to . If is omitted, then all
channels are cleared.
0462 CLEAR_# RST 0020,NEXT_CHAR Get the next character.
RST 0010,CAL_SPEC At the end of the state-
DEFW +2048,PR_ST_END ment (ie. no ?)
JR Z,0483,CLEAR_ALL Then clear all channels.
RST 0010,CAL_SPEC Expect a number: .
DEFW +1C82,EXPT_1NUM
CALL 050E,CHK_END Test for end of statement.
RST 0010,CAL_SPEC BC := offset in streamdata
DEFW +171E,STR_DATA
LD A,B Channel closed already?
OR C
RET Z Finished if so.
LD IX,(CHANS) IX := startaddress of the
ADD IX,BC channel.
DEC IX
PUSH HL Store start in stream-
CALL 036D,CLEAR_CHN table and clear the chan-
JP 1730,OPEN_S nel. Open the 'S' channel.
0483 CLEAR_ALL CALL 050E,CHK_END Test for end of statement.
THE 'WIPE CHANNELS FROM MEMORY' ROUTINE
All the channels are cleared, but the internal channels are
reinitialized again.
0486 WIPE_CHAN LD HL,(CHANS) Point past the internal
LD DE,+0014 channels.
ADD HL,DE
EX DE,HL DE := start of channels.
LD HL,(PROG) HL := end of channels.
DEC HL
RST 0010,CAL_SPEC Reclaim the space between.
DEFW +19E5,RECLAIM_1
CALL 062F,CD_TO_STK Copy code to stack:
DEFW +127C Initalise internal chans.
DEFW +000C (length)
CALL 0656,EXEC_STK Execute the code.
XOR A Clear streams 4-15.
LD B,+18 12 streams (so 24 bytes)
04A2 WIPE_CHN2 LD (DE),A
INC DE
DJNZ 04A2,WIPE_CHN2
RET
THE 'GET LINE STARTADDRESS' SUBROUTINE
This subroutine is used to find the startaddress of the line of
which the line-number is on the calculator stack.
04A7 LINE_STRT RST 0010,CAL_SPEC Fetch the line-number into
DEFW +1E99,FIND_INT2 the BC registerpair.
LD A,B Jump if it is over 16,384.
CP +40
JP NC,0983,REPORT_B
LD H,B HL := line-number.
LD L,C
RST 0010,CAL_SPEC Find the startaddress of
DEFW +196E,LINE_ADDR the line (or the first
RET line after if the line
doesn't exist).
THE 'CHECK LINE ADDRESS' SUBROUTINE
On entry the HL registerpair holds the line startaddress, DE the
lower edge to be tested and BC the higher edge. The subroutine
returns carry reset if DE <= HL <= BC.
04B6 CHK_LINE AND A Return the carry set if
SBC HL,DE HL < DE.
RET C
ADD HL,DE (correct HL)
SBC HL,BC Return the carry set if
CCF HL > BC. Carry reset means
RET DE <= HL <= BC.
THE 'CAT' COMMAND ROUTINE
The requested channel for output is opened and a jump is made to
the CALL_UTIL routine.
04BF CAT RST 0018,TEST_RUN Run-time or checking?
JR Z,04C9,CAT_2 Skip if checking syntax.
PUSH AF Store current character
LD A,+02 while opening #2.
RST 0010,CAL_SPEC
DEFW +1601,CHAN_OPEN
POP AF
04C9 CAT_2 RST 0010,CAL_SPEC Consider a '#'
DEFW +2070,STR_ALTER after the keyword.
JR C,04D4,CAT_3 Skip if no other stream.
CALL 0525,TEST_SEP Test for a separator.
JP NZ,01F3,REP_NONS Report the error if absent
04D4 CAT_3 LD B,+04 Signal: CAT and handle it.
JR 04DE,PREP_UTIL
THE 'ERASE' COMMAND ROUTINE
A direct jump is made to the CALL_UTIL routine.
04D8 ERASE LD B,+00 Signal: ERASE and jump.
JR 04DE,PREP_UTIL
THE 'FORMAT' COMMAND ROUTNE
Continue with the CALL_UTIL routine.
04DC FORMAT LD B,+06 Signal: FORMAT
THE 'PREPARE UTILITY' ROUTINE
A channel is fetched from the BASIC line.
04DE PREP_UTIL PUSH BC Store command offset.
CALL 053A,CHK_CHAN Check the channel.
CALL 050E,CHK_END Test for end of statement.
RST 0010,CAL_SPEC Fetch the parameters from
DEFW +2BF1,STK_FETCH the calculator stack.
POP HL H := command offset.
THE 'CALL UTILITY' ROUTINE
On entry the H register holds 0 for ERASE, 4 for CAT and 6 for
FORMAT, the DE registerpair points to the parameter-block and
the BC registerpair holds the length of that block. Table 6 is
searched for the handling routine.
04E9 CALL_UTIL PUSH DE Store the start and length
PUSH BC of the parameter-block.
LD A,(DE) A := channel-name.
LD B,H B := command offset.
RST 0030,LOOKUP Lookup the startaddress in
DEFB +06 the channel_table_2 table.
POP BC Restore pointers and make
POP DE a direct jump to the hand-
JP (HL) ling routine.
THE 'CLS #' COMMAND ROUTINE
On entry the A register holds the first character after the
keyword. The colour system variables are all reset to their
default values and the screen is cleared.
04F2 CLS_# CP +23 Is it '#'?
JP NZ,016D,CONTSIG_2 Report the error if not.
RST 0020,NEXT_CHAR Get the next character.
CALL 050E,CHK_END Test for end of statement.
LD A,+07 Make the border white.
RST 0010,CAL_SPEC
DEFW +229B,BORDER+7
LD (ATTR-P),A Set PAPER 7, INK, BRIGHT
XOR A and FLASH 0.
LD (MASK-P),A No transparant colours.
LD (P-FLAG),A OVER and INVERSE 0.
RST 0010,CAL_SPEC Clear the screen.
DEFW +0D6B,CLS
RET
THE 'CHECK END OF STATEMENT' SUBROUTINE
The possible characters for end-of-statement are ':' and .
When running, a return to the calling routine is made, otherwise
the next statement is considered.
050E CHK_END RST 0010,CAL_SPEC Read the character into A.
DEFW +0018,GET_CHAR
CP +3A Is it ':'?
JR Z,051A,CHK_END2 Jump if so.
CP +0D Is it ?
JP NZ,01F3,REP_NONS Report the error if not.
051A CHK_END2 RST 0018,TEST_RUN Run-time or checking?
RET NZ Return if run-time.
LD (ERR-NR),+FF Signal: no errors yet.
CALL 0623,NEAT_RET Consider a next statement.
DEFW +1BF4,STMT_NEXT
THE 'TEST FOR A SEPERATOR' SUBROUTINE
Possible separators are ',' and ';'. On exit the zero flag is
set if a separator has been found.
0525 TEST_SEP RST 0010,CAL_SPEC Read the character into A.
DEFW +0018,GET_CHAR
CP +3B Is it a ';'?
JR Z,052F,TEST_SEP2 Jump if so.
CP +2C Is it a ','?
RET NZ Return zero reset if not.
052F TEST_SEP2 RST 0020,NEXT_CHAR Get the next character.
CP A Return zero set.
RET
THE 'USE MINUS ONE' SUBROUTINE
During run-time, the value -1 is put as last entry on the
calculator stack.
0532 USE_M1 RST 0018,TEST_RUN Return immediately if
RET Z just checking syntax.
RST 0028,FP_CALC Call the calculator.
DEFB +05 (lengthbyte)
DEFB +A1,stk-one 1
DEFB +1B,negate -1
DEFB +38,end-calc -1
RET
THE 'CHECK THE CHANNEL' ROUTINE
On entry the A register contains the first character of the
channel identifier. During run-time, a parameter-block is
created in the workspace. DE holds the startaddress, BC holds
the length and both registerpairs are stored on the calculator
stack.
053A CHK_CHAN CP +23 Is it a '#'?
JR NZ,054A,CHK_CHAN3 Step if not.
RST 0018,TEST_RUN Run-time or checking?
JR Z,0544,CHK_CHAN2 Skip if checking syntax.
RST 0010,CAL_SPEC Store the '#'.
DEFW +35D0,chr$+7
0544 CHK_CHAN2 RST 0020,NEXT_CHAR Get the next character.
RST 0010,CAL_SPEC Expect a number, the
DEFW +1C82,EXPT_1NUM stream number.
JR 0575,CHK_CHAN5 Step forward.
054A CHK_CHAN3 RST 0010,CAL_SPEC Scan the next expression
DEFW +24FB,SCANNING and store it on the calcu-
lator stack.
BIT 6,(FLAGS) Numerical or string?
JR Z,0560,STRING_1 Step if is was a string.
RST 0018,TEST_RUN Run-time or checking?
JR Z,0568,STRING_2 Step if checking syntax.
XOR A Default = '' or 'm'
RST 0010,CAL_SPEC Store the ''.
DEFW +35D0,chr$+7
RST 0028,FP_CALC drive-nr, ''
DEFB +04 (lengthbyte)
DEFB +01,exchange '', drive-nr
DEFB +38,end-calc '', drive-nr
JR 0568,STRING_2 Step forward.
0560 STRING_1 CALL 0525,TEST_SEP Test for a separator.
JR NZ,0572,CHK_CHAN4 Stack 0 twice if not found
RST 0010,CAL_SPEC Expect a number (the
DEFW +1C82,EXPT_1NUM status number)
0568 STRING_2 CALL 0525,TEST_SEP Test for a separator.
JR NZ,0575,CHK_CHAN5 Stack 0 once if not found.
RST 0010,CAL_SPEC Expect a string; the file-
DEFW +1C8C,EXPT_EXP name if using "m";1;"name"
JR 0578,CHK_CHAN6 Step forward.
0572 CHK_CHAN4 RST 0010,CAL_SPEC Stack zero or empty string
DEFW +1CE6,USE_ZERO
0575 CHK_CHAN5 RST 0010,CAL_SPEC Stack zero or empty string
DEFW +1CE6,USE_ZERO
0578 CHK_CHAN6 RST 0018,TEST_RUN Run-time or checking?
JR Z,0582,CHK_CHAN7 Skip if checking syntax.
LD B,+02 Signal: not page-out
RST 0030,LOOKUP Lookup the exit table.
DEFB +0E
CALL 0087,CALL_JP Call the exit routine.
RST 0010,CAL_SPEC Fetch current character.
DEFW +0018,GET_CHAR
RET
THE 'EXIT' ROUTINE
On entry, the calculator stack contains the device-name, a
number (the drive-nr for 'm', 'd', 'CAT' and 'ram disk'; the
status for 'j', 't' and 'b'; the stream number for '#') and a
file-name (if present). The device-name should be 1 character
long, otherwise an error will be generated. The three entries
are chained together and stored in the workspace. The DE and BC
registerpairs are set up correctly and stored on the calculator
stack.
0586 EXIT RST 0028,FP_CALC dev-name, numb, file-name
DEFB +06 (lengthbyte)
DEFB +C0,st-mem-0 (mem-0 holds file-name)
DEFB +02,delete dev-name, numb
DEFB +01,exchange numb, dev-name
DEFB +38,end-calc numb, dev-name
RST 0010,CAL_SPEC Fetch parameters of the
DEFW +2BF1,STK_FETCH device-name. DE := start,
DEC BC BC := length - 1
LD A,B Device-name 1 character?
OR C
JR Z,0596,EXIT_2 Then skip.
0594 REPORT_2A RST 0008,SH_ERROR Report 'Invalid device
DEFB +29 name'.
0596 EXIT_2 LD A,(DE) A := device-name.
SET 5,A Force to lower case.
PUSH AF Store channel-identifier.
RST 0010,CAL_SPEC Fetch the number from the
DEFW +1E94,FIND_INT1 calculator stack into A.
PUSH AF Store status/drive-nr too.
LD BC,+0002 Create 2 spaces into the
RST 0010,CAL_SPEC workspace.
DEFW +0030,BC_SPACES
POP AF Store status/drive-nr into
LD (HL),A the second byte.
POP AF Store the channel-identi-
LD (DE),A fier in the first byte.
RST 0010,CAL_SPEC Store the string of these
DEFW +2AB6,STK_STORE 2 bytes on the stack.
RST 0028,FP_CALC string
DEFB +05 (lengthbyte)
DEFB +E0,get-mem-0 string, file-name
DEFB +17,strs-add string + file-name
DEFB +38,end-calc string + file-name
RET
THE 'LOOKUP (2)' ROUTINE
The continuation of RST 30. On entry the C register contains the
table number, the A register the value to search for in table
'C', and B contains the offset in table 'A'. If IC 6116 is
present, then the search is made using the tables copied in it,
otherwise the ROM tables are used.
05B1 LOOKUP_2 LD HL,+3001 IC 6116 present?
BIT 1,(HL)
LD HL,+1FFF Point to the tables in ROM
JR Z,05BD,LOOKUP_3 Not present? Then skip.
INC HL Point to the tables in RAM
INC HL
05BD LOOKUP_3 LD D,(HL) Read the startaddress into
INC HL the DE registerpair.
LD E,(HL)
EX DE,HL HL := start of tables.
INC C Simply return with this
RET Z address if C = 255.
PUSH BC Stack B (offsetnumber)
LD B,+00
ADD HL,BC HL := entry 'C'.
POP BC Restore B.
LD D,(HL) Read the startaddress of
DEC HL table 'C' into DE.
LD E,(HL) (= (HL - 1)).
EX DE,HL HL := start of table 'C'.
LD C,A Search for value 'A'.
05CD LOOKUP_4 LD A,(HL) Read a value from table.
INC HL Skip the address.
INC HL
AND A End of table (0=endmarker)
JR Z,05D9,LOOKUP_5 Then use that address.
CP C Is it value 'A'?
JR Z,05D9,LOOKUP_5 Then use that address.
INC HL Else point to the new byte
JR 05CD,LOOKUP_4 and try to match that one.
05D9 LOOKUP_5 LD A,C Restore A.
LD C,B Offset number into C.
LD D,(HL) Read the startaddress of
DEC HL the subtable 'A'.
LD E,(HL)
EX DE,HL HL := start of subtable
INC C simply return with this
RET Z address if C = 255. (also
used when a tables has no
subtables)
PUSH BC Store B.
LD B,+00
ADD HL,BC HL := entry 'C'.
POP BC Restore B.
LD D,(HL) Read the startaddress of
DEC HL the handling routine into
LD E,(HL) the DE registerpair.
EX DE,HL Now HL holds that address.
RET
THE 'PRINT AN ERROR-MESSAGE' SUBROUTINE
On entry the A register holds the message-number. The required
message is printed.
05EB P_ERR_MSG LD B,+FF Signal: no subtables.
RST 0030,LOOKUP Find the startaddress in
DEFB +0C the error-message table.
THE 'PRINT A MESSAGE AT HL' SUBROUTINE
HL points to the start of the message to be printed. The last
byte of the message has it's 7th bit set.
05EF PRINT_MSG LD A,(HL) Read a character.
AND +7F Mask out bit 7.
RST 0010,CAL_SPEC Print the character.
DEFW +0010,PRINT_A_1
BIT 7,(HL) Last character printed?
INC HL (point to next character)
JR Z,05EF,PRINT_MSG Loop back if not.
RET
THE 'CLEAR ALL TEMPORARY CHANNELS' SUBROUTINE
All the channels that have a lower case channel name are
temporary, ie. not created with an OPEN # command. These
channels are removed from momory.
05FB CL_T_CHNS LD HL,(CHANS) Make HL point after the
LD BC,+0014 internal channels (they
0601 CL_T_NXT ADD HL,BC take 20 bytes).
PUSH HL Pass the result to IX.
POP IX
LD DE,(PROG) DE := end of channel area.
DEC DE
AND A
SBC HL,DE Last channel checked?
RET Z Finished if so.
JP NC,1246,REPORT_k Report 'RAM corrupt' if
even past channel area.
ADD HL,DE Correct HL.
LD C,(IX+05) BC := length of channel.
LD B,(IX+06)
BIT 5,(IX+04) Lower case channel name?
JR Z,0601,CL_T_NXT Check next channel if not.
CALL 036D,CLEAR_CHN Clear the channel.
JR 05FB,CL_T_CHNS Find the next channel.
THE 'NEAT RETURN' ROUTINE
Fetch the two bytes immediately after the CALL and form the
jump-address. Restore the stackpointer and jump to the address.
It leaves no trace on the stack.
0623 NEAT_RET POP HL Fetch 'old' return address
LD E,(HL) Read the next two bytes
INC HL (the jump-address) into DE
LD D,(HL)
LD SP,(ERR-SP) Restore the stackpointer.
PUSH DE Make an indirect jump via
JP 1748,PAGE_OUT the PAGE_OUT routine.
THE 'COPY CODE TO THE STACK' SUBROUTINE
The four bytes following the CALL are fetched and stored in the
DE and BC registerpairs respectively. DE is the startaddress and
BC the length of the routine in the Spectrum ROM to be copied to
the stack. The first two bytes that are stacked contain the last
address + 1 of the code, so the room that is needed is BC + 2.
On exit HL holds the last address of the code in the stack, DE
the first and BC the length.
062F CD_TO_STK POP HL Fetch 'old' return address
LD E,(HL) DE := startaddress of the
INC HL code to be copied.
LD D,(HL)
INC HL
LD C,(HL) BC := length of the code.
INC HL
LD B,(HL)
INC HL
PUSH HL Store 'new' return address
PUSH BC Store the length.
PUSH DE And the startaddress.
CALL 0679,MK_RM_STK Make room in the stack.
POP HL HL := startaddress of code
PUSH DE DE = startaddress in stack
RST 0010,CAL_SPEC LDIR from the Spectrum ROM
DEFW +33C3,MOVE_FP+3
POP HL DE := start of code in stk
EX DE,HL HL := end of code in stack
DEC HL
POP BC BC := length of the code.
RET
THE 'FLOATING POINT CALCULATOR (2)' ROUTINE
The continuation of RST 0028,FP_CALC. On entry HL points to the
address of the first byte following the RST instruction (which
is the lengthbyte) and the BC registerpair holds the length of
the space to be created in the stack. The RST instruction in
stored in the stack, followed by BC - 2 databytes. The return
address is stacked and the code is executed.
0648 FP_CALC_2 CALL 0679,MK_RM_STK Make room in the stack.
POP HL HL := 'old' return address
PUSH DE DE = start of code in stk.
DEC BC BC := length of data.
DEC HL Point to RST instruction.
LDI Stack it.
INC HL Skip the lengthbyte.
LDIR Move the databytes.
POP DE DE := start of code in stk
PUSH HL Stack 'new' return address
THE 'EXECUTE CODE IN THE STACK' SUBROUTINE
On entry DE holds the first address in the stack and BC the
length of the code. The last address of the code is fetched and
a RET instruction is placed there. So, the value of BC should be
one more than the length of the actual routine. The routine is
CALLed and the stackroom reclaimed.
0656 EXEC_STK PUSH HL HL = end of code in stack.
PUSH DE DE = start of code in stk.
PUSH BC BC = length of the code.
EX DE,HL
DEC HL
LD D,(HL) DE := last address + 1.
DEC HL
LD E,(HL)
EX DE,HL
DEC HL HL := last address.
LD (HL),+C9 Put RET in place.
INC HL HL := last address + 1.
POP DE DE := length of the code.
POP BC BC := start of the code.
EX (SP),HL Store HL before the start.
PUSH BC
RST 0010,CAL_SPEC CALL the code while in the
DEFW +1C1D,JUMP_C_R+7 Spectrum ROM.
POP BC BC := start of code.
EX (SP),HL HL := last address + 1.
PUSH DE Store DE and AF (result of
PUSH AF CALLed routine).
LD D,B DE := start of code.
LD E,C
DEC DE DE := first roomaddress of
DEC DE the stack.
CALL 069B,FR_RM_STK Free the room.
POP AF Restore AF and DE.
POP DE
RET Finished.
THE 'MAKE ROOM IN THE STACK' SUBROUTINE
On entry BC holds the length of the code to be moved to the
stack. A test is made for room and the stackpointer made to
point to a position BC + 2 bytes lower. The extra 2 bytes are
used to store the last address + 1 of the code (= 'old' value of
the stackpointer). The last 6 values on the stack are moved also
so some registerpairs/return addresses can be stacked before
calling this subroutine. On exit BC holds the length and DE the
startaddress of the code in the stack.
0679 MK_RM_STK RST 0010,CAL_SPEC Test for room (and report
DEFW +1F05,TEST_ROOM 'Out of memory' if not).
LD HL,+0000 Fetch the current value of
ADD HL,SP the stackpointer into HL.
LD D,H Make a copy in DE.
LD E,L
SCF
SBC HL,BC HL := HL - BC - 1
DEC HL HL := HL - BC - 2, the new
LD SP,HL value for the stackpointer
PUSH DE Store 'old' stackbottom.
PUSH BC Store length.
LD BC,+000C Move 12 bytes (6 stack
EX DE,HL entries) down the stack.
LDIR
EX DE,HL
LD (HL),E Store last address + 1 as
INC HL first 2 bytes into the
LD (HL),D created room.
INC HL
EX DE,HL DE := start of room in stk
POP BC BC := length of room.
POP HL HL := 'old' stackbottom.
RET Finished.
THE 'FREE THE ROOM IN THE STACK' SUBROUTINE
On entry DE should point to the start and HL to the last address
+ 1 of the room in the stack. The stackpointer and stack are
moved up in memory again.
0698 FR_RM_STK EX DE,HL HL := startaddress of room
DEC DE DE := last address of room
PUSH HL (these 2 bytes should not
SCF be moved)
SBC HL,SP HL := HL - SP - 1
DEC HL HL := HL - SP - 2 = length
LD B,H of room to free.
LD C,L Copy the length to BC.
POP HL HL := last address of the
DEC DE current stack.
LDDR Move up the stack.
INC DE DE := 'new' stackbottom.
EX DE,HL Move it to HL.
LD SP,HL Restore the stackpointer.
RET
THE 'OPEN A CHANNEL' SUBROUTINE
On entry the calculator stack holds a pointer to a channel
parameter block and the length of the required channel. The A
register holds the OPEN flags, bit 0 set for output, bit 1 set
for input and bit 2 set for create. HL holds the record-length
of the channel. Table 4 is searched and a jump is made. On exit
IX holds the startaddress of the channel.
06A9 OPEN_CHAN PUSH HL Store the record-length.
PUSH AF And the OPEN flags.
RST 0028,FP_CALC pointer, length
DEFB +05 (lengthbyte)
DEFB +C1,st-mem-1 (mem-1 holds the length)
DEFB +02,delete pointer
DEFB +38,end-calc pointer
RST 0010,CAL_SPEC Fetch the parameters of
DEFW +2BF1,STK_FETCH the pointer.
PUSH BC BC = length of parameters.
PUSH DE DE = start of parameters.
LD A,(DE) Read the channel-name in A
LD B,+04 Signal: OPEN.
RST 0030,LOOKUP Lookup the startaddress in
DEFB +04 the channel table.
POP DE DE := start of parameters.
POP BC BC := length of parameters
POP AF AF := OPEN flags.
EX (SP),HL HL := record-length.
RET Jump to the handling rou-
tine.
THE 'MOVE A CHANNEL' ROUTINE
On entry HL points to the start of the input-channel and IX to
the output-channel. A buffer as large as possible is created in
the workspace. The buffer is filled from the input-buffer and
emptied into the output-buffer until the end of the
input-channel has been reached. Then the buffer is reclaimed.
06BF MOVE_CHAN PUSH IX IX = output-channel.
PUSH HL HL = input-channel.
RST 0010,CAL_SPEC BC := free memory.
DEFW +1F1A,FREE_MEM
LD HL,+FF00 Make buffer as large as
06C8 MOVE_CHN2 SBC HL,BC possible, with an overhead
JR Z,06C8,MOVE_CHN2 of 256 bytes, unless this
makes the buffer 0 bytes.
LD B,H BC := free space.
LD C,L
RST 0010,CAL_SPEC Create BC spaces in the
DEFW +0030,BC_SPACES workspace. DE := first
byte in the created room.
POP IX IX := input-channel.
06D3 MOVE_CHN3 PUSH DE Store start of buffer.
PUSH BC Store bufferlength.
CALL 07CF,LOAD_BYTS Load the bytes into the
POP HL buffer. HL := bufferlength
POP DE DE := start of buffer.
EX (SP),IX Exchange input- and output
PUSH DE channel. Store start and
PUSH HL length of the buffer.
PUSH AF Store EOF flag.
AND A BC = unloaded bytes (when
SBC HL,BC EOF). HL := loaded bytes.
LD B,H BC := bytes to save.
LD C,L
CALL 0711,SAVE_BYTS Write buffer to output-
POP AF channel. Restore EOF flag.
POP BC BC := bufferlength.
POP DE DE := start of buffer.
EX (SP),IX Exchange input- and output
JR Z,06D3,MOVE_CHN3 channel and repeat while
not EOF.
EX DE,HL Switch pointers over and
RST 0010,CAL_SPEC reclaim the buffer-space.
DEFW +19E8,RECLAIM_2
POP HL HL := input-channel.
RET IX = output-channel.
THE 'SAVE OR LOAD A CHANNEL' ROUTINE
On entry the A register should hold 0 for SAVE, 1 for LOAD, 2
for VERIFY or 3 for MERGE. IX should point to the start of the
channel, HL to the position in memory from where the data should
be loaded or saved. A header should have been set up in the
workspace which is identical to the header used by a cassette
load or save with the exception that a filename must be given.
DE should point to the start of this header. Depending on A, a
different part of this routine is used.
06F4 SAVE_CHAN PUSH HL HL = startaddress of data.
LD BC,+0001 Signal: 1 byte.
LD (T-ADDR),A Store request.
AND A Was it 'SAVE'?
JR NZ,0727,LD_VER_ME Step if not.
First the file-type is saved, the file-name is skipped, and
the length- and start-bytes are saved. Finally the described
block is saved.
06FC SAVE_CTRL CALL 0711,SAVE_BYTS Save file-type (1 byte).
EX DE,HL HL := start of header.
LD C,+0A Skip the file-name (10
ADD HL,BC bytes).
PUSH HL HL points to the length.
EX DE,HL Restore pointers.
LD C,+06 Save the file-parameters.
CALL 0711,SAVE_BYTS (6 bytes)
POP HL HL points to the length.
LD C,(HL) BC := file-length.
INC HL
LD B,(HL)
POP DE DE := startaddress of data
BC bytes are saved from address DE onward.
0711 SAVE_BYTS EXX Use alternate registers.
LD B,+00 Signal: save.
CALL 07EE,I/O_ADDR Find I/O address in HL.
0717 SAVE_BYT2 LD A,B
OR C Last byte saved?
RET Z Exit if so.
LD A,(DE) Get byte to be saved.
EXX Use alternate registers.
PUSH HL Save I/O address while
CALL 0087,CALL_JP CALLing it.
POP HL
EXX Use normal registers.
INC DE Increase startaddress.
DEC BC Decrease length.
JP 0717,SAVE_BYT2 Loop back.
The file-type is verified and stored in the 2nd header. The next
6 bytes (the file-parameters) are stored in the appropriate
places in the 2nd header and a jump is made to the LOAD, VERIFY
or MERGE routine. A special case is a bytes-file. The header has
to be verified first and cannot be MERGEd.
0727 LD_VER_ME CALL 07AB,VER_BYTES Check the file-types.
JR Z,072E,L/V/M_2 Skip if they match.
072C REPORT_m RST 0008,SH_ERROR Report 'Wrong file type'.
DEFB +35
072E L/V/M_2 EX DE,HL HL := start of header.
DEC HL
LD A,(HL) Fetch the file-type into A
LD C,+11 Skip 17 bytes (ie. point
ADD HL,BC to the 2nd header).
LD (HL),A Copy the file-type.
PUSH HL Store start of 2nd header.
PUSH AF Store file-type.
LD C,+0B Skip 11 bytes (ie. point
ADD HL,BC to the file-parameters).
LD C,+06 Now load the file-parame-
EX DE,HL ters into their correct
CALL 0785,TRY_LOAD places in the 2nd header.
POP AF Restore file-type.
EX (SP),IX IX := start of 2nd header.
POP HL HL := start of channel.
EX (SP),HL HL := startaddress, stack
PUSH HL start of channel and start
address.
CP +03 File-type = CODE?
JR Z,0753,VERI_CTRL Use VERIFY routine if so.
LD A,(T-ADDR) Fetch request into A.
DEC A Is it 'LOAD'?
JR Z,076D,LOAD_CTRL Step if so.
DEC A Is it 'VERIFY'?
JR NZ,0792,MERG_CTRL Step if not.
The header is verified and a jump is made into the LOAD-routine.
0753 VERI_CTRL CALL 062F,CD_TO_STK Copy code to stack:
DEFW +07CB,VE_CONTRL VERIFY control.
DEFW +003D (length)
DEC HL
LD (HL),+E1 Place POP HL at relative
DEC HL address 0806.
DEC HL
DEC HL
LD (HL),+C9 Place RET at relative
DEC HL address 0803.
LD (HL),+3C And INC A at address 0802.
POP HL HL := startaddress of data
CALL 0656,EXEC_STK Execute the code.
JR Z,077E,LOAD_CTR2 Skip if no error in the
076B REPORT_j RET 0008,SH_ERROR header. Otherwise report
DEFB +32 'File size error'.
The current BASIC program and/or variables are cleared when
loading a program or an array. Depending on the carry flag the
data is loaded or verified.
076D LOAD_CTRL CALL 062F,CD_TO_STK Copy code to stack:
DEFW +0808,LD_CONTRL LOAD control.
DEFW +00AC (length)
LD HL,+0068
ADD HL,DE HL := start of code + 104.
LD (HL),+C9 Place RET at relative
address 0870.
POP HL HL := startaddress of data
CALL 0656,EXEC_STK Execute the code.
077E LOAD_CTR2 LD B,D IX := startaddress of data
LD C,E BC := length of data
EX (SP),IX IX := start of channel
POP DE DE := startaddress of data
JR NC,078C,TRY_VERI Step if verifying.
0785 TRY_LOAD CALL 07C7,LOAD_BYTS Load the data-block.
RET Z Exit if no errors.
0789 REPORT_8 RST 0010,CAL_SPEC Report 'End of file'.
DEFW +15E4,REPORT_8
078C TRY_VERI CALL 07A8,VERI_BYTS Verify the data-block.
RET Z Exit if no errors.
0790 REPORT_k RST 0008,SH_ERROR Report 'Verification has
DEFB +34 failed'.
Room is created to hold the data to be MERGEd and the data is
loaded into it. The program and its variables are merged with
the current program. The Spectrum ROM routine is used, so all
the tape-protections still work.
0792 MERG_CTRL CALL 062F,CD_TO_STK Copy code to stack:
DEFW +08B6,ME_CONTRL MERGE control.
DEFW +000E (length)
POP HL HL := startaddress of data
CALL 0656,EXEC_STK Execute the code.
LD B,D BC := length of data.
LD C,E
POP IX IX := start of channel.
PUSH HL HL = startaddress of data.
EX DE,HL DE := startaddress of data
CALL 0785,TRY_LOAD Load bytes in the buffer.
POP HL HL := startaddress of data
RST 0010,CAL_SPEC Now merge the program and
DEFW +08CE,ME_NEW_LP-6 its variables.
RET
The disk-load-address is fetched and the routine called BC times
verifying each byte and returning if there is a verification
failure. On exit the zero flag is set when the end of file has
been reached, reset if an error occured.
07AB VERI_BYTS EXX Use alternate registers.
LD B,+02 Signal: load.
CALL 07EE,I/O_ADDR Find the I/O address.
07B1 VERI_BYT2 LD A,B
OR C All bytes loaded?
RET Z Exit if so.
07B4 VERI_BYT3 EXX Use alternate registers.
PUSH HL Store the I/O address
CALL 0087,CALL_JP while loading a byte.
POP HL
EXX Use normal registers.
JR NC,07C6,VER_B_ERR Jump if not succesful.
EX DE,HL
CP (HL) The actual verification.
EX DE,HL
RET NZ Return if they mismatch.
INC DE Increase startaddress.
DEC BC Decrease length.
JP 07B1,VERI_BYT3 Loop back.
07C6 VER_B_ERR JR NZ,0789,REPORT_8 Report EOF if that is true
CALL 07FF,EOF_PRSSD presssed?
JR C,07B4,VERI_BYT2 Try to re-load if not.
JR 0789,REPORT_8 Report the error.
The disk-load-address is fetched and the routine is called BC
times. The bytes are stored from address DE onward. On exit the
zero flag is set when the end of the file has been reached,
reset in case of an error.
07CF LOAD_BYTS EXX Use alternate registers.
LD B,+02 Signal: load.
CALL 07EE,I/O_ADDR Find the I/O address.
07D5 LOAD_BYT2 LD A,B
OR C All bytes loaded?
RET Z Exit if so.
07D8 LOAD_BYT3 EXX Use alternate registers.
PUSH HL Store the I/O address
CALL 0087,CALL_JP while loading a byte.
POP HL
EXX Use normal registers.
JR NC,07E7,LD_B_ERR Jump if not successful.
LD (HL),A The actual loading.
INC DE Increase startaddress.
DEC BC Decrease length.
JP 07D5,LOAD_BYT2 Loop back.
07E7 LD_B_ERR RET NZ Handle EOF in the calling
routine.
CALL 07FF,EOF_PRSSD pressed?
JR C,07D8,LOAD_BYT3 Try to re-load if not.
RET Handle EOF in the calling
routine.
THE 'FIND THE I/O ADDRESS' SUBROUTINE
On entry B holds 0 for save, 2 for load. The channel table is
searched and on exit HL holds the I/O address.
07EE I/O_ADDR PUSH BC Store entry-number.
PUSH IX HL := start of channel.
POP HL
RST 0010,CAL_SPEC Set the appropriate flags
DEFW +1615,CHAN_FLAG for this channel.
LD A,(IX+04) Fetch channel-name in A.
OR +20 Force to lower case.
POP BC Restore entry-number.
RST 0030,LOOKUP Lookup the startaddress in
DEFB +04 the channel table.
EXX Switch registers before
RET returning.
THE 'TEST IF END OF FILE PRESSED' SUBROUTINE
A test is made whether the keys and are
being pressed together. The carry flag is returned reset if so.
07FF EOF_PRSSD LD A,+BF First test .
AND A
IN A,(+FE)
RRCA
RET C Return if not pressed.
LD A,+FE Now test .
IN A,(+FE)
RRCA This resets the carry if
RET it is pressed.
THE 'GET THE OFFSET OF A CHANNEL' SUBROUTINE
The startaddress of the channel area is subtracted from the
start of the current channel and increased by one. This way the
first channel does not have an offset of 0, since an offset of 0
means that the channel has not been opened yet.
080C GETOFFSET PUSH IX HL := start of current
POP HL channel.
LD BC,(CHANS) BC := start of chan area.
AND A
SBC HL,BC
INC HL HL := offset of channel.
RET
THE 'MAKE A CHANNEL IN MEMORY' SUBROUTINE
On entry A holds the OPEN flags: bit 0 SET for output, bit 1 SET
for input and bit 2 SET for create. The other bits should be
cleared. DE points to the start of the parameter block in the
workspace and BC contains the length. HL holds the length of the
channel to be created. The start of the parameter block is
stored in the system variable X-PTR, the space is created and if
the parameter block is after the created channel area (and is
moved), this system variable will still point to it. The I/O
addresses, the device-name, the length of the channel and (if
present) the file-name are inserted into the channel area. The
routine continues with a test for the correct channel info.
0818 MAKE_CHAN PUSH BC BC = length of parameters.
PUSH AF A = OPEN flags.
LD (X-PTR),DE DE = start of parameters.
LD B,H Copy the channel length to
LD C,L BC.
PUSH BC And store it.
LD HL,(PROG) HL := start of the channel
DEC HL
RST 0010,CAL_SPEC Make the room.
DEFW +1655,MAKE_ROOM
POP BC BC := channel length.
POP AF A := OPEN flags.
INC HL Step into the created room
PUSH HL IX := start of channel.
POP IX
PUSH BC Store the channel length.
LD (HL),+20 Position a 'space'.
DEC BC Now copy this 'space' into
LD D,H all locations of the crea-
LD E,L ted channel.
INC DE
PUSH HL (Store channel start)
LDIR
POP HL (Restore channel start)
AND A No I/O allowed?
JR NZ,083E,MAKE_CHN3 Skip if allowed.
083C MAKE_CHN2 LD A,+03 Signal: input, output.
083E MAKE_CHN3 CALL 0888,SET_ADDRS Insert address.
EX DE,HL DE := location of device-
name in channel.
LD HL,(X-PTR) HL := start of parameters.
LDI Move the device-name.
EX DE,HL Switch pointers again.
POP BC BC := channel-length.
CALL 0897,STORE_BC Insert BC in descriptor.
EX (SP),HL HL := length of parameters
PUSH HL Store position in descrip-
tor and length of parm.
INC HL Prepare to insert 5 more
INC HL bytes (file-name + number
INC HL + 7 (7 is the number of
INC HL bytes already inserted in
INC HL the channel descriptor)).
AND A
SBC HL,BC Enough room for the bytes?
POP BC BC := length of parameters
POP HL HL := position in descript
PUSH BC Store length again.
DEC BC Exclude the device-name.
PUSH DE DE = start of parm. + 1.
EX DE,HL Switch pointers.
JR NC,0860,MAKE_CHN4 Move the file-name and the
LDIR number if present.
0860 MAKE_CHN4 POP DE DE := start of parameters.
DEC DE
POP BC BC := length of parameters
THE 'TEST THE CHANNEL PARAMETERS' ROUTINE
On entry DE points to the start of the parameter block and BC
holds the length. Two immediate data-bytes are used; the first
to check if the parameter after the OPEN-command is valid
(drive-number for 'm', 'd' and 'CAT', status for 't' and 'j' and
stream-number for '#'), the second to check if a string of
characters may be passed on to the routine (file-name for 'm').
On exit the carry flag is SET for an error. If SET, then the
zero flag is set for an error in the first byte, RESET for an
error in the second byte.
0863 TEST_C EX DE,HL HL := start of parameters.
POP DE Unstack return address.
PUSH BC BC = length of parameters.
INC HL
LD C,(HL) C := drive/status.
LD B,+00
LD A,(DE) A := first byte after CALL
INC DE Point to the second byte.
CALL 0878,TEST_C_2 Check the first byte.
BIT 0,B SET the zero flag.
POP BC BC := length of parameters
LD A,(DE) A := second byte.
INC DE Point to the 'new' return
PUSH DE address and stack it.
RET C Return if an error.
DEC BC BC := BC - 2.
DEC BC (length of file-name)
0878 TEST_C_2 AND A
JP P,0881,TEST_C_3 Skip if A < 128.
CPL A := 255 - A.
CP +7F A = 127 (so, A was 128?)
RET Z Exit without error if so.
DEC C
0881 TEST_C_3 CP C A < C?
RET C Exit with error if so.
LD A,B B = 0 (length of file-name
AND A < 256 characters)?
RET Z Exit without error if so.
SCF Signal: error.
RET
Store the I/O addresses in the channel descriptor pointed to by
IX. A has bit 0 set for output and bit 1 for input. Depending on
these bytes, the addresses #0008 (DO_I/O hookcode) or #15C4
('Invalid I/O device') are used.
0888 SETADDRS PUSH IX HL := start of the channel
POP HL
CALL 088E,SETADDRS2 Repeat twice:
088E SETADDRS2 LD BC,+0008 Prepare for a DO_I/O.
RRCA Rotate bits of A right.
JR C,0897,STORE_BC Skip if bit 0 was set.
LD BC,+15C4 Prepare for error-message.
Store BC at address HL and update HL two addresses.
0897 STORE_BC LD (HL),C
INC HL
LD (HL),B
INC HL
RET
The 'K', 'S' and 'P' subtables of table #04. This table is only
used by the MOVE-command.
089C TAB_4:KSP DEFW +08E4,WRITE_KSP Write.
DEFW +08C6,READ_KSP Read.
DEFW +08A8,OPEN_KSP Open.
DEFW +001C Close (return).
DEFW +0532,USE_M1 Length (use -1).
DEFW +08CC,POSN_KSP Position.
THE 'OPEN THE 'K', 'S' OR 'P' CHANNEL' SUBROUTINE
On entry DE points to the start of the parameter block and BC
holds the length. Depending on the device-name, the offset is
formed in the BC register and IX is made to point to the start
of the channel.
08A8 OPEN_KSP LD A,(DE) A := device-name.
PUSH AF Keep a copy on the stack.
CALL 0863,TEST_C Test for channel info.
DEFB +00 No extra parameters.
DEFB +00 No file-name.
JR C,0902,REPORT_A Report the error if found.
LD IX,(CHANS) IX := start of chan area.
POP AF A := device-name.
OR +20 Force to lower case.
CP +70 Is it 'p'?
08BA OPEN_'s' RET C Exit if it is 's'.
08BB OPEN_'k' LD BC,+0005 Signal: 'k'.
JR NZ,08C3,OPEN_KP Skip if it is 'k'.
08C0 OPEN_'p' LD BC,+000F Signal: 'p'.
08C3 OPEN_KP ADD IX,BC IX := start of channel.
RET
THE 'READ FROM THE 'K', 'S' OR 'P' CHANNEL' SUBROUTINE
Call the Spectrum ROM immediately.
08C6 READ_KSP EXX
RST 0010,CAL_SPEC
DEFW +15E6,INPUT_AD Call INPUT_AD.
EXX
RET
THE 'POSITION THE 'K', 'S' OR 'P' CHANNEL' SUBROUTINE
If printing to the screen ('K' or 'S'), then the position value
is fetched and used as the X-coordinate in a 'PRINT AT'
instruction.
08CC POSN_KSP PUSH IX HL := start of channel.
POP HL
RST 0010,CAL_SPEC Set the appropriate chan-
DEFW +1615,CHAN_FLAG nel flags.
BIT 1,(FLAGS) Printer or screen?
JP NZ,0BF3,REPORT_J Report an error if printer
RST 0010,CAL_SPEC A := position value.
DEFW +1E94,FIND_INT1
DEC A
LD B,A B := X-coordinate.
LD C,+00 C := Y-coordinate.
RST 0010,CAL_SPEC Position the cursor.
DEFW +0A9B (The 'PRINT AT' routine)
RET
THE 'WRITE TO THE 'K', 'S' OR 'P' CHANNEL' SUBROUTINE
Call the Specrum ROM immediately.
08E4 WRITE_KSP EXX
RST 0010,CAL_SPEC
DEFW +15F2,PRINT_A_2 Call PRINT_A_2.
EXX
RET
The '#' subtable of table #04.
08EA TAB_4:# DEFW +0905,WRITE_# Write.
DEFW +0911,READ_# Read.
DEFW +08F6,OPEN_# Open.
DEFW +001C Close (return).
DEFW +091C,LENGTH_# Length.
DEFW +0926,POSN_# Position.
The '#' channel is 8 bytes long:
bytes 0 and 1 : the output address
bytes 2 and 3 : the input address
byte 4 : the device-name (upper case when permanent)
bytes 5 and 6 : the channel length.
byte 7 : the stream being pointed to.
THE 'OPEN THE '#' CHANNEL' SUBROUTINE
The channel descriptor is made and checked for validity.
08F6 OPEN_# LD HL,+0008 Reserve 8 bytes.
CALL 0818,MAKE_CHAN Make the channel.
DEFB +0F Stream number <= 15.
DEFB +00 No file-name.
RET NC Exit if no error occured.
JP Z,03CA,REPORT_O 'Invalid stream' if >15.
0902 REPORT_A RST 0010,CAL_SPEC Report 'Invalid argument'.
DEFW +34E7,REPORT_A
THE 'WRITE TO THE '#' CHANNEL' SUBROUTINE
On entry IX points to the start of the channel. The output-
channel is fetched and a call made to the output routine in the
Spectrum ROM.
0905 WRITE_# PUSH IX Store start of channel.
PUSH AF Store output character.
CALL 0930,CHK_LOOP IX := output-channel.
POP AF Restore output character.
CALL 08E4,WRITE_KSP Process the character.
JR 0919,#_END Step forward.
THE 'READ FROM THE '#' CHANNEL' SUBROUTINE
On entry IX points to the start of the channel. The input-
channel is fetched and a call is made to the input routine in
the Spectrum ROM.
0911 READ_# PUSH IX Store start of channel.
CALL 0930,CHK_LOOP IX := input-channel.
CALL 08C6,READ_KSP Get a character in A.
0919 #_END POP IX Restore start of channel.
RET
THE 'LENGTH OF THE '#' CHANNEL' SUBROUTINE
The output-channel is fetched and the length calculated.
091C LENGTH_# PUSH IX Store start of channel.
CALL 0930,CHK_LOOP IX := output-channel.
CALL 03D9,LEN_CHAN Calculate the length.
JR 0919,#_END Step back.
THE 'POSITION THE '#' CHANNEL' SUBROUINE
The output-channel is fetched and the channel is positioned.
0926 POSN_# PUSH IX Store start of channel.
CALL 0930,CHK_LOOP IX := output-channel.
CALL 03D5,POS_CHAN Position the channel.
JR 0919,#_END Step back.
THE 'CHECK FOR AN INFINITE LOOP' SUBROUTINE
On entry IX points to the start of the '#' channel. A test is
made for an infinite loop. When after 15 times no channel is
found that is not a '#' stream, then the 'Wally' error is
printed. On exit IX points to the requested channel.
0930 CHK_LOOP LD B,+0F Maximal repetition.
0932 CHK_LOOP2 LD A,(IX+07) Fetch pointed stream.
RST 0010,CAL_SPEC Open the attached channel.
DEFW +1601,CHAN_OPEN
LD IX,(CURCHL) IX := start of this chan.
LD A,(IX+04) A := device-name.
OR +20 Force to lower case.
CP +23 Is it a '#'?
RET NZ Exit if not.
DJNZ 0932,CHK_LOOP2 Repeat the test B times.
RST 0008,SH_ERROR Report 'Don't be a Wally'.
DEFB +48
The 'T' subtable of table #04.
0948 TAB_4:T DEFW +0988,WRITE_T Write.
DEFW +0A6B,READ_T Read.
DEFW +096B,OPEN_T Open.
DEFW +0986,CLOSE_T Close.
DEFW +0532,USE_M1 Length (use -1).
DEFW +0BF3,REPORT_J Position (error-message).
The 'T' channel is 10 bytes long:
bytes 0 and 1 : the output address.
bytes 2 and 3 : the input address.
byte 4 : the device-name (upper case when permanent).
bytes 5 and 6 : the channel-length.
byte 7 : bit 0 : SET for ZX PRINTER emulation.
bit 1 : SET for sequence.
bit 2 : SET for true backspacing.
bit 5 : SET for 'AT' character.
bit 6 : SET for 'TAB' character.
bit 7 : SET for a second parameter (AT and TAB)
byte 8 : last column (paper width).
byte 9 : current column.
The 'B' subtable of table #04.
0954 TAB_4:B DEFW +0A63,WRITE_B Write.
DEFW +0A71,READ_B Read.
DEFW +0960,OPEN_B Open.
DEFW +001C Close (return).
DEFW +0532,USE_M1 Length (use -1).
DEFW +0BF3,REPORT_J Position (error-message).
The 'B' channel is 7 bytes long:
bytes 0 and 1 : the output address.
bytes 2 and 3 : the input address.
byte 4 : the device-name (upper case when permanent).
bytes 5 and 6 : the channel-length.
THE 'OPEN THE 'B' CHANNEL' SUBROUTINE
The channel is created and filled with the I/O address, the
device-name and length.
0960 OPEN_B LD HL,+0007 Reserve 7 bytes.
CALL 0818,MAKE_CHAN Make the channel.
DEFB +00 No extra parameters.
DEFB +00 No file-name.
RET NC Exit if no error occured.
JR 0902,REPORT_A Otherwise report the error
THE 'OPEN THE 'T' CHANNEL' SUBROUTINE
The channel is created and filled with the I/O address, the
device-name and length. The line-length is checked and if it is
less than 256, the len-length - 1 (or the number of the last
character in the line, starting with 0) is inserted as byte 8.
Byte 9 is cleared.
096B OPEN_T PUSH HL HL = line-length.
LD HL,+000A Reserve 10 bytes.
CALL 0818,MAKE_CHAN Make the channel.
DEFB +0F Status <= 15.
DEFB +00 No file-name.
JR C,0902,REPORT_A Report an error if found.
POP HL HL := line-length.
LD A,H Report 'Out of range' when
AND A HL is more than 255.
JR NZ,0983,REPORT_B
DEC L
LD (IX+08),L Store max-column.
LD (IX+09),H Clear current-column.
RET
0983 REPORT_B CALL 0010,CAL_SPEC Report 'Integer out of
DEFW +1E9F,REPORT_B range'.
THE 'CLOSE THE 'T' CHANNEL' SUBROUTINE
Only a is sent to the printer.
0986 CLOSE_T LD A,+0D Prepare for a .
THE 'WRITE TO THE 'T' CHANNEL' SUBROUTINE
On entry the A register holds the character to be printed and IX
the start of the channel. If the ZX PRINTER emulation is to be
used and the character does not belong to an 'AT' or a 'TAB',
then the address of the print routine will be the last address
in table #16 (A is set to zero), otherwise the table has to be
searched for the correct address.
0988 WRITE_T LD D,A Store the character.
LD A,(IX+07) Char does not belong to a
'TAB' or an 'AT' and the
AND +F1 ZX PRINTER is to be
DEC A emulated?
LD A,D (Restore A)
JR Z,0993,WRITE_T_2 Then skip.
XOR A Zero A.
0993 WRITE_T_2 PUSH DE Store the character (in D)
LD B,+FF Signal: no subtables.
RST 0030,LOOKUP Lookup the startaddress in
DEFB +16 the character table.
POP AF Restore the character.
PUSH IX Store start of channel.
EX (SP),HL Stack the address.
LD BC,+0007 HL := start of channel + 7
ADD HL,BC
LD C,(IX+08) C := max-column.
RET Jump indirectly.
THE 'PRINT_OUT' SUBROUTINES
The print addresses for the various control-codes (only when
emulating the ZX PRINTER). Cursor right means just another space
'AT' and 'TAB' are stored in the channel descriptor (byte 7) and
PRINT_SECOND is used by 'INK', 'PAPER', 'FLASH', 'BRIGHT',
'INVERSE' and also for 'AT' and 'TAB', that use two bytes each.
PRINT_COMMA makes the current column-number the next 16th
position and cursor-left only decreases the column-number when
not at the start of the line.
09A4 PR_CSRRIG SET 0,(FLAGS) Signal: a leading space.
RET
09A9 PRINT_AT SET 5,(HL) Signal: 'AT'.
JR 09AF,PRINT_2ND and a second character.
09AD PRINT_TAB SET 6,(HL) Signal: 'TAB'.
09AF PRINT_2ND SET 7,(HL) Signal: another character.
RET
09B2 PRINT_, LD A,(IX+09) A := current column.
OR +0F A := next 16th column.
INC A
INC C No check on line-length?
JR Z,09CE,TAB_LOOP Then simply add the spaces
CP C End of line reached?
JR C,09CE,TAB_LOOP Add the spaces if not.
XOR A Jump to the start of a new
JR 09CE,TAB_LOOP line and add the spaces.
THE 'PRINT OTHER CHARACTERS' SUBROUTINE
Enter here when not emulating the ZX PRINTER, or else when the
character that has to be printed is not a control character, or
when still receiving characters from an 'AT' or a 'TAB'. First
'TAB' is handled, since the first character is used as a
positioner, then 'AT' is handled, since the first charactr is
not to be used. The second character holds the position for 'AT'
09C1 PR_OTHER BIT 6,(HL) No 'TAB'?
JR Z,09DE,PR_OTHER2 Then skip.
09C5 TAB RES 6,(HL) Signal: no 'TAB'.
INC C No check on line-length?
JR Z,09CE,TAB_LOOP Then simply add the spaces
09CA TAB_WRAP SUB C If tabbing beyond the line
JR NC,09CA,TAB_WRAP length, then subtract the
ADD A,C line-length (a few times).
09CE TAB_LOOP CP (IX+09) Return once the position
RET Z to tab to has been reached
PUSH AF
CALL NC,0A47,PR_SPACE If before the position,
POP AF print a space.
PUSH AF
CALL C,0A1E,PR_NEWLIN If after the position, go
POP AF to the next line.
JR 09CE,TAB_LOOP Repeat the process.
09DE PR_OTHER2 BIT 7,(HL) No '2nd character'?
JR Z,09E5,PR_OTHER3 Then skip.
09E2 2ND_CHAR RES 7,(HL) Signal: no '2nd character'
RET
09E5 PR_OTHER3 BIT 5,(HL) No 'AT'?
JR Z,0A1A,PR_OTHER4 Then skip.
09E9 AT RES 5,(HL) Signal: no 'AT'.
INC C No check on line-length?
JR Z,09F1,AT_2 Then skip the test.
CP C Past the end of the line?
JR NC,0983,REPORT_B Then report 'Out of range'
09F1 AT_2 CP (IX+09) Return once the position
RET Z to go to has been reached.
PUSH AF Store position to go to.
JR C,0A04,CHAR_LEFT Step if moving backward.
CALL 0A4B,PR_SPACE2 Print a space (forward).
JR 0A17,AT_3 Step forward.
09FD PR_CRSLEF LD A,(IX+09) A := current column.
AND A Return if already at the
RET Z beginning of the line.
DEC A A := column to go to.
PUSH AF Store it.
0A04 CHAR_LEFT BIT 2,(IX+07) 'True backspacing'?
JR NZ,0A0F,CHAR_LEF2 Then skip.
CALL 0A5D,RESET_COL Go to start of the line.
JR 0A17,AT_3 Step forward.
0A0F CHAR_LEF2 LD A,+08 A := .
DEC (IX+09) Column := column - 1.
CALL 0A63,WRITE_B Write the character.
0A17 AT_3 POP AF A := position to go to.
JR 09F1,AT_2 Repeat the process.
A test is made for , the only control character that can be
used when not emulating the ZX PRINTER. The status-byte (IX+07)
is tested whether just a or a sequence should be
sent to the printer.
0A1A PR_OTHER4 CP +0D Is it a ?
JR NZ,0A2A,PR_OTHER5 Skip if not.
0A1E PR_NEWLIN CALL 0A5D,RESET_COL Go to start of the line.
BIT 1,(IX+07) Send a too?
RET NZ Exit if not.
LD A,+0A A := .
JR 0A63,WRITE_B Write the character.
Graphics are replaced with a '?', tokens are expanded (Spectrum
ROM routine) and the control characters (with codes less than
32) are replaced with a '?' too when not emulating the ZX
PRINTER.
0A2A PR_OTHER5 AND A Graphics/tokens?
JP P,0A36,PR_OTHER6 Skip if not.
SUB +A5 Graphic character?
JR C,0A43,PR_? Then print a '?'.
RST 0010,CAL_SPEC Expand tokens with the
DEFW +0C10,PO_TOKENS Spectrum ROM.
RET
0A36 PR_OTHER6 RES 0,(FLAGS) Signal: no leading space.
CP +20 Is it a space?
JR Z,0A47,PR_SPACE Then print a space.
JR NC,0A4D,WRITECHAR Print the printables.
BIT 0,(HL) Exit when not emulating
RET Z the ZX PRINTER.
0A43 PR_? LD A,+3F A := '?'.
JR 0A4D,WRITECHAR Print it.
0A47 PR_SPACE SET 0,(FLAGS) Signal: leading space.
LD A,+20 A := ' '.
If the end of the line has been reached, a (with or without
a ) is printed first, followed by the character.
0A4D WRITECHAR PUSH AF Store char temporarely.
LD A,(IX+08) A := max-column.
CP (IX+09) End of the line reached?
CALL C,0A1E,PR_NEWLIN Then go to the next line.
INC (IX+09) Column := column + 1.
POP AF Restore character to print
JR 0A63,WRITE_B Write the character.
The current column is made zero and a is printed.
0A5D RESET_COL LD (IX+09),+00 Zero current column.
LD A,+0D Print a .
THE 'WRITE TO THE 'B' CHANNEL' SUBROUTINE
On entry IX points to the start of the channel and A holds the
character to be printed.
0A63 WRITE_B LD B,+00 Signal: write.
0A65 GO_PRINT LD H,A H := character to print.
LD A,+81 Signal: printer port.
JP 0FE5,CAL_PHY Write the character.
THE 'READ FROM THE 'T' CHANNEL' SUBROUTINE
Any character is read, but returned with bit 7 RESET.
0A6B READ_T CALL 0A71,READ_B Read a character.
RES 7,A RESET bit 7.
RET
THE 'READ FROM THE 'B' CHANNEL' SUBROUTINE
Just read a character.
0A71 READ_B LD B,+02 Signal: read.
JR 0A65,GO_PRINT Step back.
The 'm' subtable of table #04.
0A75 TAB_4:m DEFW +0B6E,WRITE_CT Write.
DEFW +0B7C,READ_CT Read.
DEFW +0A81,OPEN_M Open.
DEFW +0BF8,CLOSE_M Close.
DEFW +0B81,LENGTH_M Length.
DEFW +0BB8,POSN_M Position.
The 'm' channel is 33 + the block-size long.
(the block-size can be 128, 256, 512 or 1024 bytes).
bytes 0 and 1 : the output address.
bytes 2 and 3 : the input address.
byte 4 : the device-name (upper case when permanent).
bytes 5 and 6 : the channel-length.
byte 7 : the drive-number.
bytes 8 to 17 : the file-name.
bytes 18 and 19 : the block-size.
bytes 20 and 21 : the record-size.
bytes 22 and 23 : the number of bytes in the last block.
bytes 24 and 25 : the location of the first block.
bytes 26 and 27 : the location of the last block.
bytes 28 and 29 : the location of the current block.
bytes 30 and 31 : the position in the current block.
byte 32 : bit 0 : SET if changes were made to the buffer
bytes 33 and up : the buffer.
THE 'OPEN THE 'M' CHANNEL' SUBROUTINE
On entry DE points to the start of the parameter block, BC holds
the length, HL contains the record-length, A holds the OPEN
flags and mem-1 contains the required length. Depending on
whether the file has been found and the 'create'-flag, the
channel is opened or the report 'File not found' generated.
0A81 OPEN_M PUSH HL HL = record-length.
PUSH AF A = OPEN flags.
CALL 0E38,CATSEARCH Open 'CAT' and find file.
POP DE D := OPEN flags.
LD A,D A := OPEN flags.
BIT 2,D 'create' allowed?
JR NZ,0A9D,CREATE Then skip.
JR C,0A95,FILEFOUND Skip too if file was found
AND A No 'I/O' nor 'create'?
LD A,+05 Then signal: output, cre-
JR Z,0A9D,CREATE ate and step forward.
0A93 REPORT_h RST 0008,SH_ERROR Report 'File not found'.
DEFB +30
The file has been found, so it should not be created. If it
should be opened for output only, then continue with the OUTPUT
part of the routine, else signal: input and go to the INPUT part
0A95 FILEFOUND CP +01 'output' only?
JR Z,0AF6,OUTPUT Then go to the OUTPUT part
SET 1,A Signal: input.
JR 0ADC,INPUT Go to the INPUT part.
Test whether there is enough room on the disk for the channel
and store in mem-1 the actual length (in bytes) of the room
needed. BC holds the record-number of the last file + 1 or the
number of a file with the same name in the catalog. If another
file with the same name exists on the disk, then it should be
erased. A record for the 'CAT'-file containing the information
is created and stored on the disk.
0A9D CREATE PUSH AF Store the OPEN flags.
PUSH BC Store the record-number of
the last file.
CALL 0E64,GET_FREE Find free space on disk.
PUSH DE DE = start of the gap.
RST 0010,CAL_SPEC BC = length of the gap.
DEFW +2D2B,STACK_BC Stack the length.
CALL 0F51,STCK_SIZE Stack the block-size.
RST 0028,FP_CALC lng, bls (len-gap, blcksz)
DEFB +1F (lengthbyte)
DEFB +E1,get-mem-1 lng, bls, lnf (len-file)
DEFB +36,less-0 lng, bls, (0/1) (lnf=-1?)
DEFB +00,jump-true
DEFB +10,TO 0ABE,HALF_SIZE
DEFB +E1,get-mem-1 lng, bls, lnf
DEFB +37,greater-0 lng, bls, (0/1)
DEFB +30,not lng, bls, (1/0) (lnf=0?)
DEFB +00,jump-true
DEFB +11,TO 0AC4,FULL_SIZE
DEFB +04,multiply lng * bls
DEFB +E1,get-mem-1 lng * bls, lnf
DEFB +03,subtract lng * bls - lnf
DEFB +36,less-0 (0/1)
DEFB +30,not (does file fit in gap?)
DEFB +00,jump-true (Exit if so)
DEFB +TO 0AC7,CREATE_E
DEFB +A0,stk-zero 0 (signal: no room)
DEFB +33,jump (And exit)
DEFB +08,TO 0AC5,STORE_LEN
0ABE HALF_SIZE DEFB +01,exchange bls, lng
DEFB +A1,stk-one bls, lng, 1
DEFB +0F,addition bls, lng + 1
DEFB +A2,stk-half bls, lng + 1, 0.5
DEFB +04,multiply bls, (lng + 1) / 2
DEFB +3A,truncate bls, INT ((lng + 1) / 2)
0AC4 FULL_SIZE DEFB +04,multiply actual length of file
0AC5 STORE_LEN DEFB +C1,stk-mem-1 (store it in mem-1)
DEFB +02,delete -
0AC7 CREATE_E DEFB +38,end-calc -
POP BC BC := start of the gap.
CALL 0F48,POSN_BC Position the catalog-file.
CALL 0EE3,READ_REC Find the record.
LD Hl,(5CA0) HL := first free block of
INC HL the new file.
INC BC BC := new record-number.
POP DE DE := last record-number
in the 'CAT'-file.
CALL 0EA4,MAKE_FILE Make a file-record in the
CALL 0C63,PUT_BLOCK catalog and write buffer.
POP AF Restore the OPEN flags.
On entry A holds the OPEN flags and IX points to the start of
the channel. All the information is inserted in the channel
descriptor. The first block - 1 is inserted as the current
block and the block-size as the position in the block, so when
the INKEYS# is used, the first byte in the first block
is fetched after it is loaded.
0ADC INPUT CALL 0888,SETADDRS Set I/O addresses.
POP BC BC := record-length.
CALL 0E18,STOREINFO Store info in the channel
descriptor.
LD HL,(5C9E) HL := first block location
DEC HL Point 1 block before it.
EX DE,HL Switch pointers.
LD (HL),E Store it.
INC HL
LD (HL),D
LD C,(IX+12) BC := block-size.
LD B,(IX+13)
INC HL Store the block-size as
LD (HL),C current position in the
INC HL block.
LD (HL),D
RET
On entry A holds the OPEN flags, BC the number of records in the
'CAT'-file. The new file-size is calculated and the channel
descriptor is filled with the data.
0AF6 OUTPUT PUSH AF Store the OPEN flags.
PUSH BC Store no. records in 'CAT'
LD BC,(5C9C) BC := number of bytes in
INC BC the last block.
RST 0010,CAL_SPEC Stack this number.
DEFW +2D2B,STACK_BC
XOR A A := 0.
RST 0010,CAL_SPEC Store the 0 too.
DEFW +2D28,STACK_A
CALL 0F51,STCK_SIZE Store the block-size too.
LD BC,(5CA0) BC := position last block.
RST 0010,CAL_SPEC Store it too.
DEFW +2D2B,STACK_BC
LD BC,(5C9E) BC := position first block
PUSH BC Keep a copy.
RST 0010,CAL_SPEC Store it too.
DEFW +2D2B,STACK_BC
CALL 0BA9,CALC_LEN Calculate the file-size.
Stack it (call it S).
CALL 0EE3,READ_REC Find right file-record.
LD HL,(5C9E) HL := new first block
POP BC BC := previous first block
PUSH BC = new last block - 1.
AND A
SBC HL,BC HL := last - first.
LD B,H Copy this to BC.
LD C,L
RST 0010,CAL_SPEC And stack it.
DEFW +2D2B,STACK_BC (Call it B)
CALL 0F51,STCK_SIZE Store the block-size too.
RST 0028,FP_CALC S, B, block-size (say L)
DEFB +2B (lengthbyte)
DEFB +04,multiply S, B * L (length of gap)
DEFB +C0,st-mem-0 (mem-0 holds B * L)
DEFB +02,delete S
DEFB +31,duplicate S, S
DEFB +A1,stk-one S, S, 1
DEFB +0F,addition S, S + 1 (also byte 0)
DEFB +01,exchange S + 1, S
DEFB +E1,get-mem-1 S + 1, S, len
DEFB +36,less-0 S + 1, S, (0/1) (len=-1?)
DEFB +00,jump-true (then go to DOUBLE)
DEFB +12,TO 0B4A,DOUBLE
DEFB +E1,get-mem-1 S + 1, S, len
DEFB +37,greater-0 S + 1, S, (0/1)
DEFB +30,not S + 1, S, (1/0) (len=0?)
DEFB +00,jump-true (then go to FULLSIZE2)
DEFB +15,TO 0B52,FULLSIZE2
DEFB +E1,get-mem-1 S + 1, S, len
DEFB +0F,addition S + 1, S + len
DEFB +C1,st-mem-1 (mem-1 holds new file-len)
DEFB +E0,get-mem-0 S + 1, S + len, B * L
DEFB +03,subtract S + 1, (S + len) - (B * L)
DEFB +37,greater-0 S + 1, (0/1)
DEFB +30,not S + 1, (1/0) (enough room)
DEFB +00,jump-true (Exit if so)
DEFB +10,TO 0B56,OUTPUT_E
DEFB +A0,stk-zero S + 1, 0 (Signal: no room)
DEFB +33,jump (And exit)
DEFB +0B,TO 0B54,STORE_RM
0B4A DOUBLE DEFB +A2,stk-half S + 1, S, 0.5
DEFB +05,division S + 1, S * 2
DEFB +31,duplicate S + 1, S * 2, S * 2
DEFB +E0,get-mem-0 S + 1, S * 2, S * 2, B * L
DEFB +03,subtract S+1, S*2, (S*2)-(B*L)
DEFB +36,less-0 S + 1, S * 2, (0/1)
DEFB +00,jump-true (enough room?)
DEFB +03,TO 0B54,STORE_RM (Exit if so)
0B52 FULLSIZE2 DEFB +02,delete S + 1
DEFB +E1,get-mem-0 S + 1, B * L
0B54 STORE_RM DEFB +C1,st-mem-1 (mem-1 holds new file-len)
DEFB +02,delete S + 1
0B56 OUTPUT_E DEFB +38,end-calc S + 1
POP HL HL := last block position
POP BC BC := first block position
LD D,B Copy it to DE.
LD E,C
CALL 0EA4,MAKE_FILE Make a file-record in the
CALL 0C63,PUT_BLOCK catalog and write buffer.
POP AF A := OPEN flags.
POP BC BC := record-length.
PUSH AF
CALL 0E18,STOREINFO Store info in the channel
CALL 0BB8,POSN_M descriptor and position
POP AF the 'CAT'-file.
JP 0888,SETADDRS Set the I/O addresses.
THE 'WRITE TO THE 'M' CHANNEL' SUBROUTINE
On entry A holds the byte to be written to the disk, and IX
points to the start of the channel. The byte is stored in the
buffer at the correct position (possibly after loading the right
block from disk) and bit 0 of (IX+32d) is SET to indicate that
the buffer has been changed, so it has to be written to disk
when loading a new block or closing the channel.
0B6E WRITE_CT PUSH AF Store byte temporarely.
CALL 0F5B,ENDOFFILE Find the correct position.
JP NC,076B,REPORT_j Report an error if found.
POP AF Restore the byte and
LD (HL),A insert it into the buffer.
SET 0,(IX+20) Signal: change made.
RET
THE 'READ FROM THE 'M' CHANNEL' SUBROUTINE
On entry IX points to the start of the channel. The current byte
is fetched (possibly after loading the right block from disk).
0B7C READ_CT CALL 0F5B,ENDOFFILE Find the correct position.
LD A,(HL) Read the byte.
RET
THE 'CALCULATE THE LENGTH OF THE 'M' CHANNEL' SUBROUTINE
The appropriate values from the channeldescriptor are stored on
the calculator stack and the length is calculated.
0B81 LENGTH_M LD C,(IX+16) BC := number of bytes in
LD B,(IX+17) the last block.
INC BC Include byte 0.
RST 0010,CAL_SPEC Store this number.
DEFW +2D2B,STACK_BC (Call it B)
LD C,(IX+1E) BC := position in current
LD B,(IX+1F) block.
RST 0010,CAL_SPEC Store it too.
DEFW +2D2B,STACK_BC (Call it P)
CALL 0F51,STCK_SIZE Stack block-size (say S)
LD C,(IX+1A) BC := last block
LD B,(IX+1B)
RST 0010,CAL_SPEC Stack it too.
DEFW +2D2B,STACK_BC (Call it L)
LD C,(IX+1C) BC := current block.
LD B,(IX+1D)
RST 0010,CAL_SPEC Stack it too.
DEFW +2D2B,STACK_BC (Call it F)
0BA9 CALC_LEN RST 0028,FP_CALC B, P, S, L, F
DEFB +0E (lengthbyte)
DEFB +03,subtract B, P, S, L - F
DEFB +04,multiply B, P, S * (L - F)
DEFB +01,exchange B, S * (L - F), P
DEFB +03,subtract B, S * (L - F) - P
DEFB +0F,addition B + S * (L - F) - P
(The remaining length, X)
DEFB +31,duplicate X, X
DEFB +36,less-0 X, (0/1)
DEFB +30,not X, (1/0) (length>=0?)
DEFB +00,jump-true (Exit if so)
DEFB +02,TO 0BB6,CALC_L_E
DEFB +30,not 0 (length:=0)
0BB6 CALC_L_E DEFB +38,end-calc The remaining length.
RET
THE 'POSITION THE 'M' CHANNEL' SUBROUTINE
On entry IX points to the start of the channel and the
calculator stack holds the new position of the channel. The
position is multiplied with the record-size, giving the position
in the file in bytes.
0BB8 POSN_M BIT 3,(IX+02) Input allowed?
JR Z,0BF3,REPORT_n Report the error if not.
LD C,(IX+14) BC := record-size.
LD B,(IX+15)
LD A,B Record-size unspecified?
OR C
JR NZ,0BC9,POSN_M_2 Skip if not.
INC BC Use 1 as record-size.
0BC9 POSN_M_2 RST 0010,CAL_SPAC Stack the record-size.
DEFW +2D2B,STACK_BC
RST 0028,FP_CALC new-pos, rec-sz
DEFB +08 (lengthbyte)
DEFB +01,exchange rec-sz, new-pos
DEFB +A2,stk-half rec-sz, new-pos, 0.5
DEFB +03,subtract rec-sz, new-pos - 0.5
DEFB +27,int rec-sz, INT (new-pos - .5)
DEFB +04,multiply rec-sz*INT(new-pos-0.5)
DEFB +38,end-calc The position in bytes.
CALL 0F51,STCK_SIZE Stack the block-size.
RST 0010,CAL_SPEC The calculator stack holds
DEFW +36A0,n-mod-m the block-number in the
file and the position in
RST 0010,CAL_SPEC this block.
DEFW +1E99,FIND_INT2 BC := block-number in file
LD L,(IX+18) HL := first block position
LD H,(IX+19)
ADD HL,BC HL := current block pos.
LD B,H Copy this number to BC.
LD C,L
CALL 0FAA,READ_NEXT Load right block.
RST 0010,CAL_SPEC BC := position in this
DEFW +1E99,FIND_INT2 block.
LD (IX+1E),C Insert this address into
LD (IX+1F),B the channel descriptor.
RET
0BF3 REPORT_J RST 0010,CAL_SPEC Report 'Invalid I/O device
DEFW +15C4
0BF6 REPORT_n RST 0008,SH_ERROR Report 'Wrong disk'.
DEFB +36
THE 'CLOSE THE 'M' CHANNEL' SUBROUTINE
On entry IX points to the start of the current channel. If input
was allowed, then write the buffer to disk; else the channel was
opened with 'OUT' and the correct file-length should be written
in the catalog-file.
0BF8 CLOSE_M BIT 3,(IX+02) Input was allowed?
JR NZ,0C63,PUT_BLOCK Then write buffer to disk.
LD E,(IX+1C) DE := current block.
LD D,(IX+1D)
A test is made whether the current position is the last position
in the file.
LD L,(IX+16) HL := number of bytes in
LD H,(IX+1