Opus Discovery Shadow ROM v2.2 Disassembly ========================================== THE 'RESET' RESTART This is the normal 'reset'. Addresses 1019 and 101A contain zero so the return address 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 return address 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 return address in LD C,(HL) HL and the lengthbyte in C LD B,+00 PUSH HL Restack return address 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 Look up 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 register pair 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 interrupt types 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 := return address. 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 Look up 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 Look up 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 Look up 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 return address 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 register pair 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 register pair 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 register pair. 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 Look up 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 register pair. 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 register pair 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 register pair points to the parameter-block and the BC register pair 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 Look up 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 register pairs 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 Look up 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 register pairs 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 register pair. 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 register pair. 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 register pairs 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 register pair 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 register pairs/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 Look up 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 Look up 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 Look up 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 Look up 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 Look up 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 Look up 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 d