The restart routines THE 'POWER UP RESET' RESTART When the Spectrum is turned on or the reset button is pressed, the DISCiPLE ROM is paged in at address 0, i.e. the routine, listed here at address #2000 is executed. 2000 POWER_UP DI Not executed, DISCiPLE pages-in at #0001. 2001 XOR A #00 'start' (#FF for 'NEW'). 2002 OUT (31),A Reset control lines. 2004 JP #02F2,POWER_UP2 Jump to main routine (#22F2). 2007 DEFB #00 Unused byte. THE 'MAIN ROM ERROR' RESTART Whenever #0008 is reached in the Spectrum ROM with no system file loaded this routine is executed. This routine handles the execution of the "RUN" command or the 'pupil' commands. 2008 ROM_START LD HL,(23645) The address reached by the interpreter 200B LD (23647),HL (CH_ADD) is copied to the error pointer 200E JR #2086,ROM_START2 (X_PTR) before proceeding. THE 'CALL BASIC ROM ROUTINE' RESTART This routine is used to CALL a subroutine in the 'main' ROM, the way to use it is a RST 16 followed by the address to be called. 2010 CALBAS JP #0190,CALBAS_2 Jump forward. THE 'INITIALIZE SYSTEM' ROUTINE This routine immediately jumps to #0013 in RAM. 2013 INIT_SYS OUT (123),A Resets boot, i.e. ROM at #2000. 2015 JP #0013,INIT_SYS So this jumps to the RAM. THE 'FLAGS ADDRESS' RESTART This is part of the -BIT- routines at address #23C6, the original HL is saved and the flags address is loaded into HL, in the routines a POP HL restores. 2018 F_ADDR_ROM EX (SP),HL HL to stack and return address to HL. 2019 PUSH HL Re-stack return address. 201A LD HL,#1ACF Address of flags. 201D RET Return so only original HL on stack. 201E DEFB #00,#00 Unused locations. THE 'DISCIPLE ERROR' RESTART DISCiPLE error routine, a RST 32 followed by a byte will print the appropriate message unless hookcodes are being executed. 2020 DISC_ERR LD HL,(23645) The address reached by the interpreter 2023 LD (23647),HL is copied to the error pointer before 2026 JR #2035,DISC_ERR2 proceeding. THE 'NEXT CHARACTER' RESTART Get next character of BASIC line to be interpreted. 2028 NEXT_C_ROM RST #10,CALBAS CALL Spectrum ROM 2029 DEFW #0020,NEXT_CHAR routine NEXT_CHAR. 202B RET THE 'GET CHARACTER' ROUTINE Get character of BASIC line to be interpreted. 202C GET_C_ROM RST #10,CALBAS CALL Spectrum ROM 202D DEFW #0018,GET_CHAR routine GET_CHAR. 202F RET THE 'SYNTAX-Z' RESTART Test the sytax checking flag, NZ means runtime, Z means syntax checking. 2030 SYNTAX_Z BIT 7,(IY+1) Flag is in system-var MODE (23617). 2034 RET THE 'DISC_ERR2' ROUTINE 2035 DISC_ERR2 JP #3CBC,D_ERROR Jump to main DISCiPLE error routine. THE 'MASKABLE INTERRUPT' RESTART In case of an interupt while the DISCiPLE is paged in, 'nothing' is done. 2038 INT_ROM EI Re-enable interupts before returning. 2039 RET SOME 'LOST' ASSEMBLER TEXT Miles-Gordon seem to have lost some of their assembler text: This is the source code for the routine in RAM at address #154F. 203A DEFW #5013 203C DEFW #0000 203E DEFW #0000 2040 DEFW #5014 2042 DEFB #05 2043 DEFM "HOFLE" 2048 DEFB #04 2049 DEFM "CALL" 204D DEFB #05 204E DEFM "H" THE 'UNPAGE' SUBROUTINE As an intermezzo, here is the routine that pages out the DISCiPLE ROM and returns to the Spectrum ROM at address #0052. This is just a RET so effectively a jump is made to HL. This routine is used by CALBAS and to jump to the Spectrum ROM. 204F UNPAGE_HL PUSH HL Put address to jump to on the stack. 2050 UNPAGE_1 OUT (187),A Page out the DISCiPLE. 2052 RET This statement is not reached. Now the assembler text continues: 2053 DEFB #00 2054 DEFW #5015 2056 DEFB #00 2057 DEFB #04 2058 DEFM "CALL" 205C DEFB #04 205D DEFW "SVHD" 2061 DEFB #00 2062 DEFW #5016 2064 DEFB #00 2065 DEFB #03 THE 'NON-MASKABLE INTERRUPT' ROUTINE In case the Spectrum ROM address #0066 is reached, the DISCiPLE is paged-in and this routine is executed. It is also used by CALBAS. With no system file loaded, only CALBAS is valid, so in all other cases, a return is made. 2066 NMI_ROM PUSH AF Save the A register on the stack. 2067 LD A,(#1DE5) 206A CP #47 Check if returning from a CALBAS. 206C JP Z,#2074,RET_BASROM Jump if returning from a CALBAS. 206F OUT (187),A Else page out the DISCiPLE. This code is never reached. 2071 POP AF Same as in Spectrum ROM, retrieve A 2072 RETN and return with old interrupt status. A return is made to the CALBASsing routine. 2074 RET_BASROM XOR A Clear the A register. 2075 LD (#1DE5),A Clear the CALBAS flag. 2078 POP AF Retrieve A register. 2079 RET Return to address after RST #10 + DEFW. THE 'INCREMENT RPT' ROUTINE This routine is probably a leftover from an older ROM version. It increments the DISCiPLE's internal RPT. 207A LD HL,(#1AD0) 207D INC HL 207E LD (#1AD0),HL 2081 LD HL,(#1AD0) 2084 LD A,(HL) 2085 RET The ROM control routine This is the entry point for all 'new' BASIC commands and the channels supported by the 'pupil' system. This routine is executed in RAM (the first KEY_SCAN after a reset jumps to the appropriate routine, which copies the first 2335 ROM bytes to RAM). 2086 ROM_START2 LD (#1DD6),HL DISCiPLE's CH_ADD (D_CH_ADD). 2089 LD (#1DEA),A 208C LD A,(#1DE5) Get CALBAS indicator. 208F CP #47 2091 JR NZ,#20A0,ROM_START3 Jump if not executing a CALBAS. The error occurred during CALBAS, so go to Spectrum error routine. 2093 XOR A 2094 LD (#1DE5),A Clear the CALBAS flag. 2097 LD (#1ACF),A Clear FLAGS3. 209A LD HL,#0054 Address in the Spectrum error routine. 209D EX (SP),HL Put new return address on the stack. 209E JR #2050,UNPAGE_1 Page out DISCiPLE and then return. 20A0 ROM_START3 POP HL Get RETurn address (usually points to 20A1 PUSH HL the error code). 20A2 PUSH DE Now see if a DISCiPLE's channel has been requested. 20A3 AND A 20A4 LD DE,#15FE If a channel has been requested this is the RETurn address stored by the CALL #162C in the 'CALL_SUB' subroutine in the 'main' ROM. 20A7 SBC HL,DE Compare the addresses. 20A9 POP DE 20AA JR NZ,#20CA,ROM_START4 No channels requested, so jump. Now see if the channel requested is the "P" channel. The DISCiPLE's "P" channel has to be handled seperately from "D" channels because it doesn't hold information about the DISCiPLE systems in/out addresses. 20AC LD HL,#0050 This makes UNPAGE_1 the return address. 20AF PUSH HL 20B0 PUSH BC 20B1 LD HL,(23631) Fetch CHANS. 20B4 LD BC,16 Point to the "P" channel. 20B7 ADD HL,BC 20B8 SBC HL,DE Compare "P" address with used address. 20BA POP BC 20BB LD A,(#1DEA) Restore A. 20BE JP Z,#089F,PCHAN_OUT Jump if DISCiPLE "P" channel was used. 20C1 LD HL,4 DE holds address of routine pointer-4. 20C4 ADD HL,DE HL now points to the routine pointer. 20C5 LD E,(HL) Fetch routine address. 20C6 INC HL 20C7 LD D,(HL) 20C8 EX DE,HL HL now points to the routine. 20C9 JP (HL) Jump to the appropriate 'input' or 'output' routine. At this point the DISCiPLE has been paged-in by an error in the 'main' ROM (not during a CALBAS). 20CA ROM_START4 POP HL Fetch address of error code. 20CB RST #10,CALL_BAS CALBAS in CH_ADD+1, this gets the byte 20CC DEFW #007B pointed by HL, i.e. the errorcode. 20CE LD (IY+0),A Store it in ERR_NR. 20D1 CP #FF Is the error 'OK'? 20D3 JR Z,#20EB,TEST_INPUT Jump if so. 20D5 SUB 27 Adjust the range, error codes now below 0. 20D7 JP NC,#0899,REPORT_17 Give 'Invalid CODE' error. Hook- and command codes are not supported. Now the reduced error code is tested for the values wich might indicate DISCiPLE BASIC commands. 20DA CP 240 Jump if the error is 20DC JR Z,#20EB,TEST_INPUT 'Nonsense in BASIC'. 20DE CP 243 20E0 JR Z,#20EB,TEST_INPUT Also if it is 'Invalid file name'. 20E2 CP 252 20E4 JR Z,#20EB,TEST_INPUT Or 'Invalid stream'. 20E6 CP 230 20E8 JP NZ,#0146,SPEC_ERR Exit if not 'Variable not found'. Now follows the infamous 'Backstepping' routine, from the address where the BASIC interpreter failed a backward search is made for a character code larger or equal to 206, i.e. a keyword. There are many ways this algorithm can fail, e.g.: -A number in a BASIC line is followed by a CHR$ 14 and the binary representation of the number, this could contain a byte >= 206. -If there is no keyword on the line, searching continous within the line length, line number and then on to previous line(s). 20EB TEST_INPUT BIT 5,(IY+55) Test FLAG_X. 20EF JP NZ,#0146,SPEC_ERR Exit if in input mode. 20F2 LD HL,(23645) Get address reached by interpreter 20F5 FIND_COM DEC HL (CH_ADD). 20F6 LD A,(HL) 20F7 CP 206 See if character is a keyword (>=206, i.e. DEF FN, the first BASIC command). 20F9 JR C,#20F5,FIND_COM If not, repeat 'Backstepping.' 20FB LD (23645),HL Update CH_ADD. 20FE RST #30,SYNTAX_Z 20FF JR NZ,#212E,RUNTIME Jump during runtime. 2101 DEC HL Compensate for increase at #2104. 2102 LD C,0 ???? C isn't used. Now the routine removes numbers from the current BASIC line. 2104 RECLM_NUM INC HL 2105 LD A,(HL) Next character. 2106 CP 14 2108 JR NZ,#2129,NO_NUM Jump if it's not a 'number' marker. 210A PUSH BC 210B PUSH HL 210C LD BC,6 Reclaim the six bytes forming a number 210F RST #10,CALBAS with it's marker. 2110 DEFW #19E8,RECLAIM_2 2112 POP HL Retrieve address in line. 2113 PUSH HL But keep it on the stack also. 2114 LD DE,(#1DD6) Address reached by interpreter. 2118 AND A Jump if the reclaimed bytes were after 2119 SBC HL,DE the character pointed to by D_CH_ADD. 211B JR NC,#2127,NEXT_1 211D EX DE,HL Otherwise D_CH_ADD has to be updated. 211E LD BC,6 2121 AND A The character pointed by D_CH_ADD has 2122 SBC HL,BC been moved '6' bytes down. 2124 LD (#1DD6),HL Update D_CH_ADD. 2127 NEXT_1 POP HL 2128 POP BC 2129 NO_NUM LD A,(HL) 212A CP 13 212C JR NZ,#2104,RECLM_NUM Repeat until 'end of line' is found. Now clear the BASIC workspace and test if the Spectrum is a pupil in a network. 212E RUNTIME RST #10,CALBAS CALL the Spectrum ROM routine SET_WORK 212F DEFW #16BF,SET_WORK to clear the workspace. 2131 LD A,(#1DE4) 2134 CP #78 2136 JP Z,#0315,PUPIL Jump if this is a PUPIL station. If not a pupil and because the DISCiPLE isn't booted the only valid commands are FORMAT (for network) and RUN (to boot). 2139 CALL #002C,GET_C_ROM Get next character from line. 213C CP 208 213E JP Z,#080C,NET_FORMAT Jump with 'FORMAT' command. 2141 CP 247 2143 JP Z,#01BC,RUN Also jump with 'RUN'. THE 'SPECTRUM ERROR' ROUTINE This routine must be entered with the error code in (ERR_NR), and does the same as the 'main' ROM 'ERROR' restart. 2146 SPEC_ERR LD HL,(#1DD6) Get D_CH_ADD. 2149 LD (23645),HL Store it in CH_ADD and X_PTR. 214C LD (23647),HL 214F LD HL,#0058 Address in Spectrum error routine. 2152 JP #004F,UNPAGE_HL Jump to Spectrum error routine. Miscalleneous routines THE 'END OF STATEMENT' ROUTINE After the syntax of the 'new' commands has been checked, a jump is made here to confirm that the statement is finished. An error is given when it isn't finished. A return to the calling routine is made only during runtime, otherwise the control returns to the 'main' ROM interpreter. 2155 ST_END_ROM CALL #002C,GET_C_ROM Get the current character. 2158 CP 13 215A JR Z,#2161,TEST_RET Jump if the statement ends with ENTER. 215C CP ":" Give an error if statement doesn't end 215E JP NZ,#2924,REP_2 with a colon. 2161 TEST_RET RST #30,SYNTAX_Z 2162 RET NZ Return during runtime. 2163 JR #2169,END1 THE 'RETURN TO THE INTERPRETER' ROUTINE The control is returned to the BASIC interpreter for interpretation of the next statement. 2165 END CALL #0403,TEST_SERV 2168 RET NZ Exit if serving the network. 2169 END1 LD SP,(23613) Clear machine stack (ERR_SP). 216D LD (IY+0),#FF Clear error code (ERR_NR). 2171 LD HL,#1BF4,STMT_NEXT Return address in 'main' ROM is 2174 RST #30,SYNTAX_Z 'STMT_NEXT' during syntax checking. 2175 JP Z,#004F,UNPAGE_HL 2178 LD HL,#1B7D,STMT_R_1 Return address during runtime is 'STMT_R_1'. 217B CALL #0181,TST_BREAK But first test for BREAK. 217E JP #004F,UNPAGE_HL No BREAK so jump to Spectrum ROM. THE 'TEST BREAK' SUBROUTINE Test for BREAK key and jump to error routine if pressed. 2181 TST_BREAK LD A,#7F 2183 IN A,(254) 2185 RRA 2186 RET C Return if SPACE wasn't pressed. 2187 LD A,#FE 2189 IN A,(254) 218B RRA 218C RET C Return if CAPS wasn't pressed. 218D JP #2926,REP_3 THE 'CALBAS_2' ROUTINE This routine calls the required 'main' ROM routine. 2190 CALBAS_2 LD (#1AC5),DE Free DE and HL. 2194 LD (#1AC8),HL 2197 POP HL Get return address, points to address 2198 LD E,(HL) of 'main' ROM routine to be called. 2199 INC HL Fetch address of routine to be called. 219A LD D,(HL) 219B INC HL 219C PUSH HL Push the new return address. 219D LD HL,#1DE5 21A0 LD (HL),#47 Signal 'CALBAS executing'. 21A2 LD HL,#0066 Return address to DISCiPLE system is 21A5 PUSH HL 'NMI_ROM'. 21A6 PUSH DE Push address of routine to be called. 21A7 LD HL,(#1AC8) Restore callers HL and DE. 21AA LD DE,(#1AC5) 21AE JP #0050,UNPAGE_1 Do the CALBAS. THE 'SYSTEM-FILE' NAME The following 11 bytes form the file description (directory description and filename) of a System-file. 21B1 DEFB 4 21B2 DEFM "SYS* " THE 'RUN' COMMAND ROUTINE The RUN command loads the system file, this routine is only reached when the DISCiPLE isn't initialized as a PUPIL station. 21BC RUN RST #30,SYNTAX_Z Exit immediately if syntax is being 21BD JP Z,#0165,END checked. 21C0 LD IX,#1AC3 21C4 LD A,1 Signal 'drive 1 is being used'. 21C6 LD (IX+11),A 21C9 LD A,%00010001 Set drive 1 and DISCiPLE on. 21CB LD (#1DDA),A Store it in the control port state. 21CE OUT (31),A Activate. 21D0 CALL #3030,REST Move drive head to track 0. 21D3 LD HL,#01B1 Copy the file description to UFIA1. 21D6 LD DE,#1E05 21D9 LD BC,11 21DC LDIR 21DE LD A,8 Signal 'matching name and directory description have to be found'. 21E0 CALL #335B,SCAN_CAT Search for the file. 21E3 JP NZ,#292E,REP_7 Give 'No "SYSTEM" file' error when file not found. 21E6 RST #10,CALBAS Clear the whole screen. 21E7 DEFW #0DAF,CL_ALL 21E9 CALL #3C38,MESG_0 Print the 'SYSTEM LOADING' message. 21EC CALL #35C2,LOAD_1ST Copy the file description (directory description and filename) to UFIA2 and load the first sector of the file into the disk buffer. 21EF LD HL,#1BD6 Copy the file header (the 9 bytes 21F2 LD DE,#1E29 consisting of filetype, length, etc.) 21F5 LD BC,9 to UFIA2. 21F8 LDIR 21FA LD (IX+13),9 Update the buffer pointer for the bytes 21FE LD HL,#020C,LOAD_SYS read. 2201 LD DE,#1E3D Because the current routine is executed 2204 LD BC,13 in RAM (see #028E and further) and that 2207 LDIR is where the system-file will be LOADed, all this will be overwritten. So copy the next routine to a safe 2209 JP #1E3D,LOAD_SYS place and jump to it. (Was it not possible to PUSH the #0013 and jump to #3145 ?) 220C LOAD_SYS LD DE,(#1E2A) Fetch the systemfile's length. 2210 LD HL,#0000 Loadaddress is #0000. 2213 CALL #3145,LOAD_FILE Load the system. 2216 JP #0013,INIT_SYS Exit via 'INIT_SYS'. 2219 DEFB #00 Unused location. The periodic routines THE '"P" CHANNEL DATA' TABLE Here follow the '5' bytes that compose the DISCiPLE "P" channel. 221A P_CHANNEL DEFW #0008 221C DEFW #0008 221E DEFB "P" THE 'TAKEOVER PRINTER' SUBROUTINE Being a PUPIL the Spectrum/DISCiPLE can't use its own printer. Instead all characters send to the "P" channel are send to the master station. By changing the "P" channels in- and output addresses, the data can be transmitted to the master. This routine is executed every interrupt. 221F TAKE_PRTR LD HL,(23631) Get address of channel data (CHANS). 2222 LD BC,15 Offset for channel "P". 2225 ADD HL,BC 2226 EX DE,HL 2227 LD HL,#021A Copy the "P" channel data. 222A LD BC,5 222D LDIR 222F LD L,#2F Restore registers for 'main' ROM 2231 LD DE,#FFFF 'KEY_SCAN'. 2234 LD BC,#FEFE 2237 JR #2294,KSCAN_RET Return into the 'KEY_SCAN' routine. MORE 'LOST' ASSEMBLER TEXT Again Miles-Gordon have lost some assembler text: This time it's the source code for the routine in RAM at address #1595. 2239 DEFB #00,#05 223B DEFM "BTCNT" 2240 DEFB #00 2241 DEFW #5055 2243 DEFB #00,#04 2245 DEFM "LDIR" 2249 DEFB #00,#00 224B DEFW #5056 224E DEFB #04 224F DEFM "PUSH" 2253 DEFB #02 2254 DEFM "DE" 2256 DEFB #00 2257 DEFW #5057 2259 DEFB #00,#03 225B DEFM "POP" 225E DEFB #02 225F DEFM "IX" 2261 DEFB #00 2262 DEFW #5058 2264 DEFB #00,#03 2266 DEFM "POP" 2269 DEFB #02 226A DEFM "DE" 226C DEFB #00 226D DEFW #5059 226F DEFB #00,#03 2271 DEFM "POP" 2274 DEFB #02 2275 DEFM "BC" 2277 DEFB #00 2278 DEFW #5060 227A DEFB #00,#03 227C DEFM "RET" 227F DEFB #00,#00 2281 DEFW #5061 2283 DEFB #00,#00,#00,#00 2287 DEFW #5062 2289 DEFB #00,#00,#00,#00 228D DEFB #50 THE 'KEY_SCAN' ROUTINE Whenever the Z80 reaches address #028E, i.e. the 'main' ROM 'KEY_SCAN' routine, the DISCiPLE is paged-in. The action taken depends on the contents of #1DE4 (or #3DE4 when the RAM is high). The DISCiPLE ROM is low after power-up, reset and IN 123. 228E KSCAN_ROM LD L,#2F Same instruction as in 'main' ROM. 2290 JR #229D,KSCAN_ROM1 Jump over the page-out part. 2292 KSCAN_RES OUT (123),A Reset boot, ROM high. 2294 KSCAN_RET OUT (187),A Page-out into the 'main' ROM 'KEY_SCAN'. SOME 'LEFTOVER' SYSTEM VARIABLES It seems that MGT has lost some system variables also. 2296 'D_ERRSP' DEFW #0000 2298 'RBCC' DEFB 0 2299 'TRAKS1' DEFB 208 229A 'TRAKS2' DEFB 208 229B 'STPRAT' DEFB 12 229C 'NSTAT' DEFB 0 Now the 'KSCAN_ROM' routine continues. 229D KSCAN_ROM1 LD DE,#FFFF Set registers as needed by 'main' ROM 22A0 LD BC,#FEFE 'KEY_SCAN'. By examining the contents of #1DE4 when RAM is paged-in low, or #3DE4 when it's paged-in high, this routine decides what has to be done: - Whenever the power is turned on, the ROM resides low. #1DE4 contains #45, so #3DE4 is examined which contains some random value (should that be #44, the Spectrum will crash). Otherwise a 'minimal' system is created by copying the first 2335 bytes of ROM to RAM and then setting RAM low. This is indicated by #1DE4 containing #53. - In case of a minimal system #1DE4 contains #53, and the routine returns immediately to the keyboard routine in 'main' ROM. - By giving FORMAT nn (2nd n>=10) a minimal system can be changed to a PUPIL system. This is indicated by a #4E value in #1DE4. In this case the routine continues with scanning the network for activity. - By giving RUN at a minimal system, the system file is loaded from disk. If a system file has been loaded #1DE4 contains #44. - To get rid of a system file the reset button has to be pressed twice or IN 123 has to be given twice. After the first of these ROM resides low, #1DE4 holds #45 so #3DE4 is inspected which holds #44. The contents of #3DE4 are set to #00 and some DISCiPLE system variables are reset. Before returning the RAM is set low again. - When pressing reset or giving IN 123 for the second time, #3DE4 (RAM is now high) holds #00. This means a minimal system will be created. - By giving IN 123 or a reset once, a minimal or PUPIL system can be reset, which results in a minimal system being created. 22A3 LD A,(#1DE4) 22A6 CP #4E 22A8 JP Z,#035D,NET_SERVER Jump if this is a PUPIL. 22AB CP #53 Exit immediately if the 'minimal' system 22AD JR Z,#2294,KSCAN_RET is active. When the DROM is low #1DE4 contains #45. 22AF LD A,(#3DE4) RAM is now paged-in high. 22B2 CP #44 22B4 JR NZ,#22BC,SET_BOOT Jump if system file no longer supposed to be active. 22B6 XOR A Otherwise the next time the ROM is 22B7 LD (#3DE4),A found low the system will become 22BA JR #22D4,RESET_VARS inactive. Jump to reset some variables. The routine now initialises a 'minimal' system, i.e. no pupil and no system loaded. 22BC SET_BOOT PUSH BC 22BD PUSH DE 22BE LD HL,#0000 Copy the first 2335 bytes in ROM to the 22C1 LD DE,#2000 RAM. 22C4 LD BC,#091F 22C7 LDIR 22C9 POP DE 22CA POP BC 22CB LD A,#53 Signal 'minimal system initialized'. 22CD LD (#3DE4),A 22D0 XOR A 22D1 LD (#3DDA),A Reset control port state. 22D4 RESET_VARS LD (#3DEF),A Reset @6999. 22D7 LD (#3DE5),A 22DA LD (#3ACF),A Reset FLAGS3. 22DD LD HL,#0000 Reset D_ERR_SP. 22E0 LD (#2296),HL 22E3 LD A,(#3DDA) Activate new control port settings. 22E6 OUT (31),A 22E8 LD HL,275 The disk buffer offset. 22EB LD (#3AD2),HL 22EE LD L,#2F Restore registers for 'main' ROMs 22F0 JR #2292,KSCAN_RES 'KEY_SCAN' and exit resetting boot. THE 'POWER_UP2' ROUTINE The routine continues, with the proper register contents, in the 'main' ROM 'START/NEW' routine. 22F2 POWER_UP2 LD A,#02 Red instead of black border. 22F4 OUT (254),A 22F6 LD A,#3F Set interrupt vector. 22F8 LD I,A 22FA NOP 22FB NOP 22FC NOP 22FD NOP 22FE NOP 22FF NOP 2300 XOR A The resetting of the control lines has 2301 OUT (31),A been done already (at #0001). 2303 OUT (251),A Reset the printer port too. 2305 LD HL,#7FFF The stackpointer has to point into RAM, 2308 LD SP,HL otherwise: trouble (for the UNPAGE_HL routine). 2309 IM 1 Set interrupt mode 1. 230B XOR A Restore registers for 'main' ROM 230C LD DE,65535 'START/NEW'. 230F LD HL,#11CB,START/NEW 2312 JP #004F,UNPAGE_HL Jump to 'START/NEW' in the 'main' ROM. The PUPIL control routines THE 'PUPIL COMMANDS' ROUTINE This routine handles the 'new' commands supported by the 'PUPIL system'. 2315 PUPIL LD HL,#1DF4 Reset some DISCiPLE variables and work 2318 LD BC,60 areas. 231B PUP_CLRV LD (HL),255 231D INC HL 231E DEC BC 231F LD A,B 2320 OR C 2321 JR NZ,#231B,PUP_CLRV 2323 LD (#1DDA),A Reset control port state. 2326 LD (#1ACF),A Clear FLAGS3. 2329 LD (23729),A Clear an unused Spectrum system variable. 232C CALL #002C,GET_C_RAM Fetch the command. 232F LD (#1DFF),A 2332 CP 207 Is it 'CAT' ? 2334 JP Z,#083C,CAT Jump to the CAT routine if so. 2337 CP 239 Also for 'LOAD',... 2339 JP Z,#06A5,LOAD 233C CP 248 ...'SAVE',... 233E JP Z,#0672,SAVE 2341 CP 213 ...'MERGE',... 2343 JP Z,#06AF,MERGE 2346 CP 214 ...'VERIFY',... 2348 JP Z,#06AA,VERIFY 234B CP 208 ...'FORMAT',... 234D JP Z,#080C,FORMAT 2350 CP 210 ...'ERASE',... 2352 JP Z,#086D,ERASE 2355 CP 255 ...'COPY',... 2357 JP Z,#089C,COPY 235A JP #0146,SPEC_ERR If non of the above, the Spectrum has to handle the error. THE 'NETWORK SERVER' ROUTINE This routine checks if a 'command block' is send over the network. In a master/pupil network (Shared Access Network) the master can 'steal' a file from or 'force' a file upon a pupil station. Whenever the master does this the pupil executes the command and loads from or saves to the master station. 235D NET_SERVER CALL #036E,NET_ACTIVE Test network activity. 2360 CALL NZ,#038A,GET_COMBLK Fetch the command block if the network was active. 2363 LD L,#2F Restore registers for 'KEY_SCAN' 2365 LD DE,#FFFF routine in 'main' ROM. 2368 LD BC,#FEFE 236B JP #021F,TAKE_PRTR Exit via 'TAKE_PRTR'. THE 'TEST NETWORK ACTIVITY' SUBROUTINE Before a command block can be received the network has to be active for about 10400 T cycles and then has to go inactive in less than 52000 T states. Whenever one of both requirements isn't fulfilled a return is made with the Zero flag set indicating 'no command block to be received'. 236E NET_ACTIVE LD HL,200 This counter allows the network to be tested for 10395 T cycles. 2371 NET_ACT1 IN A,(31) 2373 CPL 2374 BIT 7,A 2376 RET Z Exit if the network is inactive. 2377 DEC HL 2378 LD A,H 2379 OR L 237A JR NZ,#2371,NET_ACT1 Repeat activity-test until time out. Now the network has to go inactive within about 52000 T cycles. 237C LD HL,1000 237F NET_ACT2 DEC HL Decrease counter. 2380 LD A,H 2381 OR L 2382 RET Z Exit if network isn't inactive within 2383 IN A,(31) the right time. 2385 BIT 7,A 2387 JR Z,#237F,NET_ACT2 Repeat until network is inactive. 2389 RET Return so the command block can be fetched. THE 'GET COMMAND BLOCK' SUBROUTINE This routine fetches the command block from the network and executes the command. 238A GET_COMBLK LD HL,#1DFD Point to the data buffer. 238D LD E,29 238F CALL #3A87,INPAK Read 29 bytes from the network. 2392 RET NZ Return if no bytes received. 2393 LD HL,#1DFD Restore the pointer. 2396 XOR A Clear checksum. 2397 LD B,28 Calculate checksum for 28 bytes. 2399 GET_C_CHKS ADD A,(HL) Add this byte. 239A INC HL 239B DJNZ #2399,GET_C_CHKS Repeat for all command block bytes. 239D CP (HL) Compare with received checksum. 239E RET NZ Exit if they don't match. 239F LD HL,#1DFD Restore the pointer again. 23A2 LD A,(#029C) Fetch own station number. 23A5 CP (HL) Return if the command block isn't for 23A6 RET NZ this station. 23A7 INC HL 23A8 LD A,(HL) 23A9 CP 1 Return also if the command block isn't 23AB RET NZ from the master station. 23AC INC HL Point to the command. 23AD PUSH HL 23AE CALL #3AB5,SEND_RESP Send response code. 23B1 CALL #03DB,SIGN_SERV Signal 'serving the master'. 23B4 LD A,(#1DFE) Fetch the destination station number 23B7 LD (#1E01),A (which is always 1). 23BA POP HL 23BB LD A,(HL) Fetch the command. NOTE: The commands are the commands used by the master so when the master states 'LOAD', the pupil has to 'SAVE'. 23BC CP 245 23BE JR Z,#23CF,PRINT Jump with 'PRINT'. 23C0 CP 248 This is 'SAVE'. 23C2 CALL Z,#06A5,LOAD Call the 'LOAD' routine. 23C5 CP 239 This is 'LOAD'. 23C7 CALL Z,#0672,SAVE Call the 'SAVE' routine. 23CA GET_EXIT XOR A 23CB LD (#1ACF),A Clear FLAGS3. 23CE RET THE 'PRINT CHAR. FROM MASTER' SUBROUTINE This subroutine prints the character send by the master on the current stream. 23CF PRINT INC HL Fetch character. 23D0 LD A,(HL) 23D1 RST #10,CALBAS Print it. 23D2 DEFW #0010,PRINT_A_1 23D4 JR #23CA,GET_EXIT The flag set and test routines THE 'SIGNAL ..' SUBROUTINES These subroutines are used to signal various states of the DISCiPLE system. The corresponding test routines are located from #23FE and onwards. Clearing is done by loading 0 into FLASG3, resetting all flags at once. Some are not used from ROM. 23D6 SIGN_ RST #18,F_ADDR_ROM Not used. 23D7 SET 0,(HL) 23D9 POP HL 23DA RET 23DB SIGN_SERV RST #18,F_ADDR_ROM Signal 'serving the network'. 23DC SET 1,(HL) 23DE POP HL 23DF RET 23E0 SIGN_ RST #18,F_ADDR_ROM Not used. 23E1 SET 2,(HL) 23E3 POP HL 23E4 RET 23E5 SIGN_NET RST #18,F_ADDR_ROM Signal 'networking'. 23E6 SET 3,(HL) 23E8 POP HL 23E9 RET 23EA SIGN_LOAD RST #18,F_ADDR_ROM Signal 'LOADing'. 23EB SET 4,(HL) 23ED POP HL 23EE RET 23EF SIGN_SAVE RST #18,F_ADDR_ROM Signal 'SAVEing'. 23F0 SET 5,(HL) 23F2 POP HL 23F3 RET 23F4 SIGN_MERGE RST #18,F_ADDR_ROM Signal 'MERGEing'. 23F5 SET 6,(HL) 23F7 POP HL 23F8 RET 23F9 SIGN_VERIF RST #18,F_ADDR_ROM Signal 'VERIFYing'. 23FA SET 7,(HL) 23FC POP HL 23FD RET THE 'TEST ..' SUBROUTINES These subroutines are used to test the various states of the DISCiPLE system. 23FE TEST_ RST #18,F_ADDR_ROM 23FF BIT 0,(HL) 2401 POP HL 2402 RET 2403 TEST_SERV RST #18,F_ADDR_ROM 2404 BIT 1,(HL) 2406 POP HL 2407 RET 2408 TEST_ RST #18,F_ADDR_ROM 2409 BIT 2,(HL) 240B POP HL 240C RET 240D TEST_NET RST #18,F_ADDR_ROM 240E BIT 3,(HL) 2410 POP HL 2411 RET 2412 TEST_LOAD RST #18,F_ADDR_ROM 2413 BIT 4,(HL) 2415 POP HL 2416 RET 2417 TEST_SAVE RST #18,F_ADDR_ROM 2418 BIT 5,(HL) 241A POP HL 241B RET 241C TEST_MERGE RST #18,F_ADDR_ROM 241D BIT 6,(HL) 241F POP HL 2420 RET 2421 TEST_VERIF RST #18,F_ADDR_ROM 2422 BIT 7,(HL) 2424 POP HL 2425 RET The syntax checking routines THE 'SEPARATOR' SUBROUTINE This small subroutine tests whether the current character is a separator or a quote. It returns with the Zero flag set if it was a ';', a ',' or a '"'. With the first two the A register holds the next character of the BASIC line. 2426 SEPARATOR CP """ Return with Zero set if the current 2428 RET Z character is a '"'. 2429 CP "," 242B JR Z,#2430,SEPAR_1 Jump if it's a ','. 242D CP ";" 242F RET NZ Return Zero reset if it isn't a ';'. 2430 SEPAR_1 RST #28,NEXT_C_ROM Get the next character. 2431 LD (#1DEA),A 2434 XOR A Set Zero flag. 2435 LD A,(#1DEA) 2438 RET THE 'EVALUATE STRING EXPR.' SUBROUTINE A call is made to the 'main' ROM 'EXPT_EXP' (class-0A) subroutine, to evaluate a string expression. During runtime, the parameters of the string (start and length) are returned in the DE and BC register pairs. 2439 EXPT_STR RST #10,CALBAS Evaluate the string expression. 243A DEFW #1C8C,EXPT_EXP 243C RST #30,SYNTAX_Z 243D RET Z Return if the syntax is being checked. 243E PUSH AF Save the character following the string 243F RST #10,CALBAS and the Zero flag. 2440 DEFW #2BF1,STK_FETCH Fetch the string parameters. 2442 POP AF 2443 RET THE 'EVAL. MICRODRIVE SYNTAX' SUBROUTINE This subroutine is entered at 'MD_SYNTAX' or 'MD_SYNTAX1' depending upon whether or not the character pointer is to be updated to the next character. A single character string is evaluated, and its ASCII value is stored during runtime. If a separator isn't present after the single character string, an error will be given. 2444 MD_SYNTAX RST #28,NEXT_C_ROM Next character. 2445 MD_SYNTAX1 CALL #0439,EXPT_STR Evaluate the string expression. 2448 JR Z,#245C,MD_SYNT1 Jump if syntax is being checked. 244A PUSH AF Save the character following the 244B LD A,C string. A holds low byte of string 244C DEC A length. 244D OR B Give 'Nonsense in GNOS' error if the 244E JP NZ,#2922,REP_1 length of the string isn't one. 2451 LD A,(DE) Otherwise fetch the character. 2452 RST #10,CALBAS Call 'ALPHA' to see if it's a letter. 2453 DEFW #2C8D,ALPHA 2455 JP NC,#2922,REP_1 Give error if not a valid one. 2458 LD (#1E04),A Store the channel specifier. 245B POP AF Restore next character. 245C MD_SYNT1 CP ";" 245E RET Z Return if it's a semicolon. 245F CP "," 2461 RET Z Return if it's a comma. 2462 JP #2922,REP_1 Otherwise error. THE 'EVALUATE DEVICE NUMBER' SUBROUTINE This subroutine is used to evaluate the device number. 2465 EXPT_DEVN AND #DF Make upper case. 2467 CP "P" 2469 JR NZ,#247C,EXPT_DEVN1 Jump if the device isn't 'P'. 246B RST #28,NEXT_C_ROM Get next character. 246C CALL #0494,EXPT_NUM Fetch the program number. 246F RET Z Return if syntax checking. 2470 PUSH AF 2471 LD A,(#1E01) Store the program number. 2474 LD (#1E02),A 2477 CALL #0487,LAST_DRV Drive is last used drive. 247A POP AF 247B RET Now a check is made whether the last used drive is wanted. 247C EXPT_DEVN1 RST #28,NEXT_C_ROM Update CH_ADD. 247D CP "*" 247F JR NZ,#2494,EXPT_NUM Jump if it isn't a '*'. 2481 RST #30,SYNTAX_Z 2482 CALL NZ,#0487,LAST_DRV Store last drive number during runtime. 2485 RST #28,NEXT_C_ROM Next character. 2486 RET THE 'SET LAST DRIVE' SUBROUTINE This subroutine is used whenever the last used drive is to be used again. 2487 LAST_DRV LD A,(#1DDA) Fetch current control port status. 248A AND #01 Keep only drive select. 248C ADD A,#01 A holds 1 for drive 2, 2 for drive 1. 248E XOR #03 1 becomes 2, 2 becomes 1. 2490 LD (#1E01),A Store drive number. 2493 RET THE 'EVALUATE NUMERIC EXPR.' SUBROUTINE This subroutine is used to evaluate a single numeric expression. The result is returned during runtime into the BC register pair and into UFIA1. 2494 EXPT_NUM RST #10,CALBAS Evaluate the expression by calling 2495 DEFW #1C82,EXPT_1NUM 'EXPT_1NUM' in the 'main' ROM. 2497 RST #30,SYNTAX_Z 2498 RET Z Return if syntax is being checked. 2499 PUSH AF 249A RST #10,CALBAS Fetch the value from the calculator 249B DEFW #1E99,FIND_INT2 stack. 249D LD A,C 249E LD (#1E01),A Store it in UFIA1. 24A1 POP AF 24A2 RET THE 'EVALUATE 2ND FILENAME' SUBROUTINE This routine evaluates the second filename of a BASIC command. Because 'EXP_F_NAME' stores the filename in UFIA1, both UFIAs are swapped first, then 'EXP_F_NAME' is called and an exit is made via 'SWAP_UFIAS' to get the UFIAs in the right order again. 24A3 EXP_F_NAM2 CALL #04A9,SWAP_UFIAS Swap UFIA1 and 2. 24A6 CALL #04C1,EXP_F_NAME Evaluate filename. Exit via 'SWAP_UFIAS'. THE 'SWAP UFIAS' SUBROUTINE This subroutine swaps the contents of UFIA1 and UFIA2 in the DFCA. 24A9 SWAP_UFIAS PUSH BC 24AA PUSH DE 24AB PUSH HL 24AC LD B,24 An UFIA is 24 bytes long. 24AE LD DE,#1E01 Start of UFIA1. 24B1 LD HL,#1E1A Start of UFIA2. 24B4 SWAP_LOOP LD A,(DE) Exchange the contents. 24B5 LD C,(HL) 24B6 EX DE,HL 24B7 LD (DE),A 24B8 LD (HL),C 24B9 INC DE 24BA INC HL 24BB DJNZ #24B4,SWAP_LOOP Repeat for all 24 bytes. 24BD POP HL 24BE POP DE 24BF POP BC 24C0 RET THE 'EVALUATE A FILENAME' SUBROUTINE A string expression is evaluated and, provided that the length is within the range 1..10 characters, stored in UFIA1. 24C1 EXP_F_NAME CALL #0439,EXPT_STR Evaluate the string. 24C4 RET Z Return if checking syntax. 24C5 PUSH AF 24C6 LD A,C 24C7 OR B Give 'Invalid FILE NAME' error with a 24C8 JP Z,#2930,REP_8 null string. 24CB LD HL,10 24CE SBC HL,BC 24D0 JP C,#2930,REP_8 Also with string length > 10. 24D3 LD HL,#1E05 Clear the filename and the directory 24D6 LD A,11 description of UFIA1. 24D8 CLR_FNAME LD (HL)," " 24DA INC HL 24DB DEC A 24DC JR NZ,#24D8,CLR_FNAME Repeat for all 11 bytes. 24DE LD HL,#1E06 Copy the filename into UFIA1. 24E1 EX DE,HL 24E2 LDIR 24E4 POP AF 24E5 RET THE 'CHECK STATION NUMBER' SUBROUTINE A return to the calling routine is made only if DEV_NUM1 holds a valid station number, i.e. in the range 0..63. 24E6 TEST_STAT LD A,(#1E01) Fetch (DEV_NUM1). 24E9 INC A Give 'Invalid STATION' error if it 24EA JP Z,#2932,REP_9 holds #FF, no station number has been evaluated. 24ED SUB 64 Give the error again if the value is 24EF JP NC,#2932,REP_9 greater than or equal to 64. 24F2 RET THE 'EVALUATE PARAMETERS' SUBROUTINE This very important subroutine is called to evaluate the syntax of the PUPIL's 'SAVE', 'LOAD', 'MERGE' and 'VERIFY' commands. The routine is entered with CH_ADD pointing to the command; on exit during runtime UFIA1 is filled with the proper values. 24F3 EXPT_PARMS CALL #0403,TEST_SERV Return if serving the network, the 24F6 RET NZ parameters have been evaluated already by the master station. 24F7 RST #28,NEXT_C_ROM Get next character from BASIC line. 24F8 CP " " Give 'Nonsense in GNOS' error with 24FA JP C,#2922,REP_1 codes below 32, i.e. colour codes, etc. 24FD LD (#1E04),A Store the character in DEV_TYPE1. 2500 CP "*" If it was an '*' evaluate microdrive 2502 CALL Z,#0444,MD_SYNTAX syntax by calling 'MD_SYNTAX'. 2505 CALL #0465,EXPT_DEVN Evaluate device or program number. 2508 CALL #0426,SEPARATOR Test for a separator. 250B PUSH AF 250C RST #30,SYNTAX_Z 250D JR Z,#252F,FILENAME Jump if syntax checking. 250F LD A,(#1E04) Fetch device descriptor. 2512 AND #DF Make capital. 2514 CP "D" 2516 JR Z,#252A,NOT_NET Jump if device is disk. 2518 CP "M" 251A JR Z,#252A,NOT_NET Or disk with Microdrive syntax. 251C CP "P" 251E JR Z,#2534,PARAMS Jump with program. 2520 CP "N" Give 'Invalid DEVICE' error with 2522 JP NZ,#2934,REP_10 unknown devices. 2525 CALL #03E5,SIGN_NET Device is network, signal 'networking'. 2528 JR #2534,PARAMS Jump to evaluate paremeters. 252A NOT_NET POP AF Give 'Nonsense in GNOS' error if no 252B JP NZ,#2922,REP_1 separator or quote found. 252E PUSH AF Balance the 'POP AF' below. 252F FILENAME POP AF 2530 CALL Z,#04C1,EXP_F_NAME Evaluate filename if necessary. 2533 PUSH AF Balance next instruction. 2534 PARAMS POP AF 2535 CP 13 2537 JR Z,#2571,NO_PARAMS Jump with ENTER. 2539 CP ":" 253B JR Z,#2571,NO_PARAMS Jump with a colon. 253D CP 170 253F JP Z,#05A7,SCREEN$ Jump with 'SCREEN$'. 2542 CP 175 2544 JP Z,#05C2,CODE Jump with 'CODE'. 2547 CP 228 2549 JP Z,#061C,DATA Jump with 'DATA'. 254C CP 202 254E JR Z,#2561,LINE Jump with 'LINE'. 2550 AND #DF Only capitals. 2552 CP 83 Give 'Statement END error' error if it 2554 JP NZ,#2924,REP_2 isn't 'S'. 2557 RST #28,NEXT_C_ROM Next character. 2558 CALL #0155,ST_END_ROM Confirm end of statement and exit if syntax checking. 255B LD A,5 Signal '48K Snapshot' ?? 255D LD (#1E05),A Could it be possible ? 2560 RET Finished. Now deal with LINE. 2561 LINE RST #28,NEXT_C_ROM Advance CH_ADD. 2562 RST #10,CALBAS Evaluate autostart line number by 2563 DEFW #1C82,EXPT_1NUM calling 'EXPT_1NUM' in the 'main' ROM. 2565 CALL #0155,ST_END_ROM Confirm end of statement and exit if syntax checking. 2568 RST #10,CALBAS Fetch the autostart line number. 2569 DEFW #1E99,FIND_INT2 256B LD (#1E17),BC Store it in AUTOSTART1. 256F JR #2574,PROG If there are no parameters, as with a BASIC program the syntax checking ends here. 2571 NO_PARAMS CALL #0155,ST_END_ROM Confirm end of statement and exit during syntax checking. 2574 PROG LD A,(#1E04) 2577 AND #DF Only capitals. 2579 CP "P" Jump if the device isn't "P", i.e. no 257B JR NZ,#2584,PROG_1 program number was specified. 257D CALL #0417,TEST_SAVE 'SAVE pn' is not supported, so exit if 2580 RET Z not SAVEing. 2581 JP #2922,REP_1 Otherwise 'Nonsense in GNOS'. 2584 PROG_1 XOR A File type is 'BASIC'. 2585 LD (#1E10),A 2588 LD A,1 Signal 'BASIC file'. 258A LD (#1E05),A 258D LD HL,(23641) Fetch (E_LINE), the first location past the variables area. 2590 LD DE,(23635) Fetch (PROG), the 'start' of the BASIC 2594 LD (#1E13),DE program and store it in FILE_ADDR1. 2598 SCF Calculate ((E_LINE)-(PROG)-1), i.e. the 2599 SBC HL,DE length of the program and its 259B LD (#1E11),HL variables. Store it into LENGTH1_1. 259E LD HL,(23627) Fetch (VARS) and calculate 25A1 SBC HL,DE ((VARS)-(PROG)), i.e. the length of the program without its variables. 25A3 LD (#1E15),HL Store it into LENGTH1_2. 25A6 RET Finished. If the token is SCREEN$, the parameters are entered directly into the file header in UFIA1. 25A7 SCREEN$ RST #28,NEXT_C_ROM Get the next character. 25A8 CALL #0155,ST_END_ROM Confirm end of statement and exit during syntax checking. 25AB LD HL,6912 The size of the display file is stored 25AE LD (#1E11),HL into LENGTH1_1. 25B1 LD HL,16384 The startaddress is stored into 25B4 LD (#1E13),HL FILE_ADDR1. 25B7 LD A,3 File type is 'CODE'. 25B9 LD (#1E10),A 25BC LD A,7 Signal 'SCREEN$'. 25BE LD (#1E05),A 25C1 RET Finished. Now deal with CODE, three parameters are needed: "start", "length" and "execute address". With LOAD there may be none to three parameters, but with SAVE at least two parameters must be present. 25C2 CODE RST #28,NEXT_C_ROM Update CH_ADD. 25C3 CP 13 If there are no further parameters, 25C5 JR Z,#25CB,CODE_1 jump to use '0' as default value. 25C7 CP ":" Jump if there are parameters to be 25C9 JR NZ,#25D6,CODE_2 evaluated (i.e. the next character isn't a colon). 25CB CODE_1 CALL #0417,TEST_SAVE With SAVE there must be at least two 25CE JP NZ,#293E,REP_15 parameters, give 'CODE error' if none present. 25D1 RST #10,CALBAS A call to the 'main' ROM routine 25D2 DEFW #1CE6,USE_ZERO 'USE_ZERO' is made to use a value of 25D4 JR #25DE,CODE_3 zero as default. Now the routine evaluates the first parameter, if present. 25D6 CODE_2 RST #10,CALBAS Use the 'main' ROM routine to evaluate 25D7 DEFW #1C82,EXPT_1NUM the first parameter. 25D9 CALL #0426,SEPARATOR 25DC JR Z,#25E9,CODE_4 Jump if a separator is present. 25DE CODE_3 CALL #0417,TEST_SAVE With SAVE a second parameter is needed, 25E1 JP NZ,#293E,REP_15 so again 'CODE error' if not present. 25E4 RST #10,CALBAS Use zero as default again if not 25E5 DEFW #1CE6,USE_ZERO SAVEing. 25E7 JR #25F1,CODE_5 Evaluate the second parameter. 25E9 CODE_4 RST #10,CALBAS 25EA DEFW #1C82,EXPT_1NUM 25EC CALL #0426,SEPARATOR Jump if a second separator is found, 25EF JR Z,#25F6,CODE_6 i.e. there is a third parameter. 25F1 CODE_5 RST #10,CALBAS Otherwise again a zero is used as 25F2 DEFW #1CE6,USE_ZERO default. 25F4 JR #25F9,CODE_7 Evaluate the third parameter. 25F6 CODE_6 RST #10,CALBAS 25F7 DEFW #1C82,EXPT_1NUM 25F9 CODE_7 CALL #0155,ST_END_ROM Confirm end of statement end exit during syntax checking. 25FC RST #10,CALBAS Fetch the "autoexecute" address from 25FD DEFW #1E99,FIND_INT2 the calculator stack and store it into 25FF LD (#1E17),BC AUTOSTART1. 2603 RST #10,CALBAS Fetch the "length". 2604 DEFW #1E99,FIND_INT2 2606 LD (#1E11),BC Store it into LENGTH1_1. 260A RST #10,CALBAS Fetch the "start". 260B DEFW #1E99,FIND_INT2 260D LD (#1E13),BC Store it into FILE_ADDR1. 2611 LD A,3 File type is 'CODE'. 2613 LD (#1E10),A 2616 LD A,4 Signal 'CODE file'. 2618 LD (#1E05),A 261B RET Finished. Finally the routine to evaluate DATA parameters. 261C DATA CALL #041C,TEST_MERGE Give 'MERGE error' error if trying to 261F JP NZ,#293C,REP_14 merge an array. 2622 RST #28,NEXT_C_ROM Next character. 2623 RST #10,CALBAS Call 'LOOK_VARS' to look for the array 2624 DEFW #28B2,LOOK_VARS name. 2626 SET 7,C 2628 JR NC,#2635,DATA_1 Jump if handling an existing array or if checking syntax. 262A LD HL,#0000 Signal 'using new array'. 262D CALL #0412,TEST_LOAD 2630 JR NZ,#2650,DATA_3 Jump if LOADing the array. 2632 JP #2936,REP_11 Otherwise 'VARIABLE not found'. 2635 DATA_1 JP NZ,#2922,REP_1 Give 'Nonsense in GNOS' if not an array variable. NOTE: This test fails to exclude simple strings, but the 'bug' (present in the 'main' ROM) is corrected at #0640. 2638 RST #30,SYNTAX_Z 2639 JR Z,#2662,DATA_5 Jump if syntax is being checked. 263B CALL #0417,TEST_SAVE 263E JR Z,#2645,DATA_2 Jump if not SAVEing. 2640 BIT 7,(HL) Error 'Nonsense in GNOS' if trying to 2642 JP Z,#2922,REP_1 SAVE a simple string. 2645 DATA_2 INC HL Point to the 'length' of the array. 2646 LD A,(HL) Store it into LENGTH1_1. 2647 LD (#1E11),A 264A INC HL 264B LD A,(HL) 264C LD (#1E12),A 264F INC HL Advance to the start of the array. 2650 DATA_3 LD A,C Store the array name into the MSB of 2651 LD (#1E15),A LENGTH1_2. 2654 LD A,1 File type is 'NUM ARRAY'. 2656 BIT 6,C 2658 JR Z,#265B,DATA_4 Jump if really a numeric array. 265A INC A Otherwise file type is 'STR ARRAY'. 265B DATA_4 LD (#1E10),A Store the file type into FILE_TYPE1. 265E INC A Signal: (A=2) 'Numeric array', or 265F LD (#1E05),A (A=3) 'String array'. 2662 DATA_5 EX DE,HL DE holds 'start' of the array (or #0000 with a 'new' array to be LOADed). 2663 RST #28,NEXT_C_ROM Next character. 2664 CP 41 Check that the ')' is present. 2666 JP NZ,#2922,REP_1 If not 'Nonsense in GNOS'. 2669 RST #28,NEXT_C_ROM Next character. 266A CALL #0155,ST_END_ROM Confirm end of statement and exit while syntax checking. 266D LD (#1E13),DE Store "start" of the array into 2671 RET FILE_ADDR1 and exit. The BASIC command execution routines THE 'SAVE' COMMAND SYNTAX ROUTINE This routine deals with the SAVEing of a file. As with all PUPIL "disk" commands, a command block is send over the network to the master or an assistant station. The master or assistant than takes care of the SAVEing to disk. 2672 SAVE CALL #03EF,SIGN_SAVE Signal 'SAVEing'. 2675 CALL #04F3,EXPT_PARMS Evaluate and store the parameters. 2678 CALL #08B1,REQ_SERV Request service by a master or assistant station (if needed). 267B CALL #04E6,TEST_STAT Check if the destination station number is in range. 267E CALL #39A9,OPEN_N Open the "N" channel. 2681 CALL #068A,SAVE_NET SAVE file and header over the network. 2684 CALL #39DF,SEND_NEOF Send the EOF block. 2687 JP #0165,END Finished. THE 'SEND HEADER AND FILE' SUBROUTINE This subroutine sends the file header and the file itself over the network. The End Of File block isn't send. 268A SAVE_NET LD DE,9 Length of the header. 268D LD HL,#1E10 Address of the header. 2690 CALL #069A,SAVE_NET1 Send the header over the network. 2693 LD DE,(#1E11) Fetch length of file. 2697 LD HL,(#1E13) Fetch address. 269A SAVE_NET1 LD A,D 269B OR E 269C RET Z Return if no more bytes left. 269D LD A,(HL) Fetch a byte. 269E CALL #386D,N_OUTPUT Send it over the network. 26A1 INC HL Point to next byte. 26A2 DEC DE One less. 26A3 JR #269A,SAVE_NET1 Repeat for all bytes. THE 'LOAD' COMMAND SYNTAX ROUTINE The 'LOAD' flag is set and the routine continues into the 'LOAD_VERIFY_MERGE' routine below. 26A5 LOAD CALL #03EA,SIGN_LOAD Signal 'LOADing'. 26A8 JR #26B2,LD_VF_MR THE 'VERIFY' COMMAND SYNTAX ROUTINE The 'VERIFY' flag is set and again the 'LOAD_VERIFY_MERGE' routine handles the rest. 26AA VERIFY CALL #03F9,SIGN_VERIF Signal 'VERIFYing'. 26AD JR #26B2,LD_VF_MR THE 'MERGE' COMMAND SYNTAX ROUTINE The 'MERGE' flag is set and 'LOAD_VERIFY_MERGE' continues the syntax checking. 26AF MERGE CALL #03F4,SIGN_MERGE Signal 'MERGEing'. THE 'LOAD_VERIFY_MERGE' COMMAND ROUTINE This subroutine checks the syntax of the LOAD, VERIFY and MERGE commands and executes them also. 26B2 LD_VF_MR CALL #04F3,EXPT_PARMS Evaluate and store the parameters. 26B5 CALL #08B1,REQ_SERV Request service by a master or an assistant station (if needed). 26B8 CALL #04E6,TEST_STAT Check if the destination station number is in range. 26BB CALL #39A9,OPEN_N Open the "N" channel. 26BE LD B,9 Length of a header. 26C0 LD HL,#1E29 Start of HEADER 2. 26C3 LD_ETC1 CALL #3808,N_INPUT LOAD a byte from the network. 26C6 LD (HL),A Store it into UFIA2. 26C7 INC HL 26C8 DJNZ #26C3,LD_ETC1 Repeat for all header bytes. 26CA LD A,(#1E10) Fetch type of program to be LOADed. 26CD LD B,A 26CE LD A,(#1E29) 26D1 CP B Fetch type of program found. 26D2 JP NZ,#293A,REP_13 Give 'Wrong FILE type' if types are unequal. 26D5 CP 3 26D7 JR Z,#26E7,LD_ETC2 Jump if it's a 'CODE' file. 26D9 JP NC,#293A,REP_13 Give 'Wrong FILE type' if file type >= 4. 26DC CALL #041C,TEST_MERGE 26DF JR NZ,#273D,MERGE_CTRL Jump if 'MERGEing'. 26E1 CALL #0421,TEST_VERIF 26E4 JP Z,#0759,LOAD_CTRL Jump if not 'VERIFYing' (i.e. doing a 26E7 LD_ETC2 CALL #041C,TEST_MERGE LOAD). Now deal with LOADing of all files with type 3, like 'CODE' and 'SCREEN$', or VERIFYing of all file types. 26EA JP NZ,#293C,REP_14 Give 'MERGE error' if trying to MERGE a 'CODE' file. 26ED LD HL,(#1E11) Fetch length of requested file. 26F0 LD DE,(#1E2A) Fetch length of file found. 26F4 LD A,H 26F5 OR L 26F6 JR Z,#2705,LD_ETC3 Jump if length unspecified. 26F8 SBC HL,DE Jump if file to be LOADed is shorter 26FA JR NC,#2705,LD_ETC3 than or of equal length as the requested file. 26FC CALL #0412,TEST_LOAD 26FF JP Z,#2938,REP_12 'VERIFY failed' if not LOADing. 2702 JP #293E,REP_15 Otherwise 'CODE error'. 2705 LD_ETC3 LD HL,(#1E13) Fetch startaddress from FILE_ADDR1. 2708 LD A,H 2709 OR L 270A JR NZ,#270F,LD_ETC4 Jump if a startaddress was specified. 270C LD HL,(#1E2C) Otherwise use the startaddress of the found file. 270F LD_ETC4 LD A,(#1E29) But if the file is a BASIC program the 2712 AND A startaddress is held in (PROG). 2713 JR NZ,#2718,LD_ETC5 Jump if not a BASIC file. 2715 LD HL,(23635) Fetch 'start' from (PROG). 2718 LD_ETC5 CALL #07FE,LV_ANY Load or verify the file. 271B LD HL,(#1E17) Fetch execute address. 271E CALL #072A,EXEC_CODE Use it if it was specified. 2721 LD HL,(#1E30) Otherwise use the files execute 2724 CALL #072A,EXEC_CODE address. 2727 JP #0165,END Finished. THE 'EXECUTE CODE FILE' SUBROUTINE This routine is supposed to jump to the address in the HL register pair (if it is valid) but because of a mistake strange things can happen after the CODE file has been 'executed'. The mistake is made at #2737-#273A, now a return is made to 'STACK_BC' in the 'main' ROM first and than the code file is executed. It had to be the other way around. (See RAM disassembly from #0F23 onwards.) 272A EXEC_CODE LD A,H 272B OR L 272C RET Z Return if the execute address is zero. 272D LD A,H 272E CP #FF 2730 JR NZ,#2736,EXEC_CODE1 2732 LD A,L Also return when the execute address is 2733 CP #FF #FFFF. 2735 RET Z 2736 EXEC_CODE1 EX (SP),HL Place execute address onto the stack. 2737 LD HL,#2D2B,STACK_BC 273A JP #004F,UNPAGE_HL Now consider the MERGEing of a program. 273D MERGE_CTRL LD BC,(#1E2A) Fetch the length of the program to be 2741 PUSH BC MERGEd. 2742 INC BC Extra location for the 'end marker'. 2743 RST #10,CALBAS Make the required room in workspace by 2744 DEFW #0030,BC_SPACES calling 'BC_SPACES'. 2746 LD (HL),128 Place the 'end marker'. 2748 EX DE,HL Move start pointer to HL. 2749 POP DE Length to DE. 274A PUSH HL 274B CALL #07FE,LV_ANY Load the file into workspace. 274E POP HL Fetch 'start' of new program. 274F LD DE,(23635) Fetch 'start' of old program (PROG). 2753 RST #10,CALBAS Do the mergeing by calling the 'main' 2754 DEFW #08D2,ME_NEW_LP ROM 'MERGE' routine. 2756 JP #0165,END Finished. The final part of the routine deals with the LOADing of a program or an array. 2759 LOAD_CTRL LD DE,(#1E2A) Fetch 'new' length. 275D LD HL,(#1E13) Fetch 'old' start (=0 when loading a 'new' array). 2760 PUSH HL 2761 LD A,H 2762 OR L 2763 JR NZ,#276B,LD_CTRL1 Jump if not a 'new' array. 2765 INC DE Increment length by 3, i.e. allows for 2766 INC DE the insertion of the array name and the 2767 INC DE two-byte length. 2768 EX DE,HL Move 'length' to HL. 2769 JR #2774,LD_CTRL2 Jump forward. The array to be loaded will replace an existing one. 276B LD_CTRL1 LD HL,(#1E11) Fetch 'old' length (i.e. length of existing program or array) from UFIA1. 276E EX DE,HL Move 'new' length to HL. 276F SCF Jump if the program or array to be 2770 SBC HL,DE loaded isn't longer than the existing 2772 JR C,#277D,LD_CTRL3 one. 2774 LD_CTRL2 LD DE,5 Otherwise a check must be made to 2777 ADD HL,DE ensure that there is sufficient space 2778 LD B,H in memory for the program (or array) to 2779 LD C,L be loaded. 277A RST #10,CALBAS Make the check by calling the 'main' 277B DEFW #1F05,TEST_ROOM ROM 'TEST_ROOM' subroutine. 277D LD_CTRL3 POP HL Restore 'old' start (#0000 when handling a 'new' array). 277E LD A,(#1E29) 2781 AND A 2782 JR Z,#27B5,LD_PROG Jump if it's a BASIC program. 2784 LD A,H 2785 OR L Jump unless an 'old' array is to be 2786 JR Z,#2793,LD_CTRL4 erased before loading the 'new' one. 2788 DEC HL Points to high byte of 'array length'. 2789 LD B,(HL) Fetch the 'length'. 278A DEC HL 278B LD C,(HL) 278C DEC HL Now point to the 'array name'. 278D INC BC Include 'length' and 'name' in the 278E INC BC array length. 278F INC BC 2790 RST #10,CALBAS Call 'RECLAIM_2' to delete the array. 2791 DEFW #19E8,RECLAIM_2 2793 LD_CTRL4 LD HL,(23641) (E_LINE) points to the end of the 2796 DEC HL variables area+1. 2797 LD BC,(#1E2A) Fetch length of array to be loaded. 279B PUSH BC 279C INC BC Include in the length one byte for the 279D INC BC 'array name' and two bytes for the 279E INC BC 'array length'. 279F LD A,(#1E15) Fetch the array name from 'LENGTH1_2'. 27A2 PUSH AF 27A3 RST #10,CALBAS Call 'MAKE_ROOM' to create the space 27A4 DEFW #1655,MAKE_ROOM for the array. 27A6 INC HL Point to first 'new' location inserted. 27A7 POP AF 27A8 LD (HL),A Store array name into first location. 27A9 POP DE 27AA INC HL Store array length into the following 27AB LD (HL),E two locations. 27AC INC HL 27AD LD (HL),D 27AE INC HL 27AF CALL #07FE,LV_ANY Load the file. 27B2 JP #0165,END Finished. Now deal with the LOADing of a BASIC program and its variables. 27B5 LD_PROG LD DE,(23635) Fetch start of existing program (PROG). 27B9 LD HL,(23641) Fetch end of existing program, i.e. 27BC DEC HL (E_LINE)-1. 27BD RST #10,CALBAS Delete the program by calling 27BE DEFW #19E5,RECLAIM_1 'RECLAIM_1' in the 'main' ROM. 27C0 LD BC,(#1E2A) Fetch length of program and variables. 27C4 LD HL,(23635) Fetch (PROG), start of BASIC program. 27C7 RST #10,CALBAS Create the required space by calling 27C8 DEFW #1655,MAKE_ROOM 'MAKE_ROOM'. 27CA INC HL Point to the first location. 27CB LD BC,(#1E2E) Fetch length without variables. 27CF ADD HL,BC Calculate and store the start of the 27D0 LD (23627),HL variables area (VARS). 27D3 LD A,(#1E31) When no autostart line is given, this 27D6 LD H,A ('AUTOSTART2-hi') holds #FF. 27D7 AND #C0 27D9 JR NZ,#27E6,LD_PROG1 Jump with no autostart line. 27DB LD A,(#1E30) Otherwise store it into 'NEWPPC' and 27DE LD L,A clear 'NSPPC'. 27DF LD (23618),HL These hold the line and the statement 27E2 LD (IY+10),0 to be executed respectively. 27E6 LD_PROG1 LD HL,(23635) Fetch the start of the BASIC program. 27E9 LD DE,(#1E2A) Fetch the length. 27ED DEC HL Reset the DATA pointer 'DATADD' to the 27EE LD (23639),HL beginning of the program. 27F1 INC HL Balance the 'DEC HL'. 27F2 CALL #07FE,LV_ANY Load the file. 27F5 JP #0165,END Finished. THE 'LOAD OR VERIFY' SUBROUTINE This subroutine is used to LOAD or VERIFY (signalled by FLAGS3) a block of bytes. It must be entered with HL and DE holding 'start' and 'length'. 27F8 LV_VERIFY CP (HL) The actual VERIFY, i.e. compare the fetched byte with that held in memory. 27F9 JP NZ,#2938,REP_12 Give 'VERIFY failed' if they don't match. 27FC LV_NEXT INC HL Next memory address. 27FD DEC DE One byte less to go. 27FE LV_ANY LD A,D 27FF OR E 2800 RET Z Exit if no more bytes left. 2801 CALL #3808,N_INPUT Load one byte from the network. 2804 CALL #0421,TEST_VERIF 2807 JR NZ,#27F8,LV_VERIFY Jump if VERIFYing. 2809 LD (HL),A Otherwise store the byte. 280A JR #27FC,LV_NEXT Repeat for all bytes in the file. THE 'FORMAT' COMMAND SYNTAX ROUTINE This routine tests if the command has the appropriate syntax and executes it during runtime. 280C FORMAT RST #28,NEXT_C_ROM Advance CH_ADD to next character. 280D AND #DF Only capitals. 280F CP "N" Give 'Nonsense in GNOS' if device isn't 2811 JP NZ,#2922,REP_1 equal to "N". 2814 CALL #0465,EXPT_DEVN Evaluate the station number. 2817 CALL #0155,ST_END_ROM Confirm end of statement and exit during syntax checking. 281A CALL #04E6,TEST_STAT Check the station number. 281D LD A,(#1E01) 2820 AND A Give 'Invalid STATION' error if station 2821 JP Z,#2932,REP_9 number is 0. 2824 CP 10 Give the error also if station number 2826 JP C,#2932,REP_9 in the range 1..9. 2829 LD (#029C),A Store the station number. 282C LD A,(#1DE4) 282F CP #4E Finished if DISCiPLE was a PUPIL 2831 JP Z,#0165,END already. 2834 LD A,#4E 2836 LD (#1DE4),A Otherwise signal 'PUPIL mode'. 2839 JP #2940,REP_16 And report 'PUPIL set'. THE 'CAT' COMMAND SYNTAX ROUTINE This routine handles the syntax checking and the execution of the 'CAT' command. 283C CAT LD HL,#1E06 "*" is the default name of the files 283F LD (HL),"*" being CATted. 2841 CALL #0465,EXPT_DEVN Evaluate drive number. 2844 CALL #0426,SEPARATOR Evaluate the filename if there is a 2847 CALL Z,#04C1,EXP_F_NAME separator. 284A CP "!" If an "!" is present it is thrown away. 284C JR NZ,#284F,CAT1 284E RST #28,NEXT_C_ROM Ignore "!". 284F CAT1 CALL #0155,ST_END_ROM Confirm end of statement and exit if syntax checking. 2852 CALL #08B1,REQ_SERV Request service by a master or an assistant station. 2855 RST #10,CALBAS Clear the screen. 2856 DEFW #0DAF,CL_ALL 2858 CALL #39A9,OPEN_N Open the "N" channel. 285B CAT2 CALL #3819,NCHAN_IN Read a byte from the network. 285E JR C,#2868,CAT3 Jump if acceptable byte. 2860 JR Z,#285B,CAT2 Try again if no byte has been read. 2862 CALL #39DF,SEND_NEOF ? Why send EOF block, it's a read channel. 2865 JP #0165,END Finished. 2868 CAT3 RST #10,CALBAS Print the received character on the 2869 DEFW #0010,PRINT_A_1 current stream. 286B JR #285B,CAT2 THE 'ERASE' COMMAND SYNTAX ROUTINE The ERASE command can be given by a PUPIL but the 'NET_SERVER' routine in the system file ignores it completely. 286D ERASE RST #28,NEXT_C_ROM Advance CH_ADD. 286E LD (#1E04),A Store the device descriptor. 2871 CP """ Test for Microdrive syntax if it was a 2873 CALL Z,#0445,MD_SYNTAX1 quote. 2876 CALL #0465,EXPT_DEVN Evaluate the drive number. 2879 CALL #0426,SEPARATOR A filename must be specified, give 287C JP NZ,#2922,REP_1 'Nonsense in GNOS' if no separator present. 287F CALL #04C1,EXP_F_NAME Evaluate filename. 2882 CALL #0155,ST_END_ROM Confirm end of statement and exit during syntax checking. 2885 LD A,(#1E04) Fetch device descriptor. 2888 AND #DF Drop lower case bit. 288A CP "D" 288C JR Z,#2893,ERASE1 Jump if it's "D". 288E CP "M" Give 'Invalid DEVICE' error if it isn't 2890 JP NZ,#2934,REP_10 "M". 2893 ERASE1 CALL #08B1,REQ_SERV Request service by a master or 2896 JP #0165,END assistant. Finished. THE 'HOOKCODES NOT IMPLEMENTED' ROUTINE A PUPIL system doesn't support hook and command codes so give an error. 2899 REPORT_17 JP #2942,REP_17 Give 'Invalid CODE'. THE 'COPY NOT IMPLEMENTED' ROUTINE Making screendumps on the network printer isn't allowed. 289C COPY JP #2942,REP_17 Give 'Invalid CODE'. THE '"P" CHANNEL OUTPUT' ROUTINE This routine handles the PUPIL's "P" channel, the only thing it has to do is sending a PRINT command block to the master. 289F PCHAN_OUT PUSH AF 28A0 LD A,1 Destination station is the master 28A2 LD (#1DFD),A station. 28A5 LD A,245 This is the 'PRINT' code. 28A7 LD (#1DFF),A 28AA POP AF 28AB LD (#1E00),A Store byte to be printed in command block. 28AE JP #08D2,REQ_SERV3 Send the command block and exit. THE 'REQUEST SERVICE' SUBROUTINE This routine handles the sending of a command block to the master or one of its assistant stations. The station to which it is sended is determined by the drive number. E.g. the 'CAT' command; when CAT n is given with n between 0 and 9 the command block is send to the master station. Lies n within the range 20..29 then the command block is send to station 2. So to select a certain destination station (within the range 1..9), add it's station number times 10 to the drive number. (The master station is selected when the range 0..19 is used.) 28B1 REQ_SERV CALL #0403,TEST_SERV Exit when serving the network (master 28B4 RET NZ has given a F command). 28B5 CALL #040D,TEST_NET Exit when using the network in the 28B8 RET NZ 'normal' way (with a N command). 28B9 LD A,(#1E01) Otherwise fetch drive number. 28BC LD B,1 B holds destination station number. 28BE SUB 10 Use master station if drive number is 28B0 JR C,#28C9,REQ_SERV2 in the range 0..9. 28C2 REQ_SERV1 SUB 10 Now calculate destination station. 28C4 JR C,#28C9,REQ_SERV2 1=0..19, 2=20..29, . . , 9=90..99. 28C6 INC B 28C7 JR #28C2,REQ_SERV1 28C9 REQ_SERV2 ADD A,10 Balance last 'SUB 10'. 28CB LD (#1E01),A Store real drive number. 28CE LD A,B Store destination station number. 28CF LD (#1DFD),A 28D2 REQ_SERV3 LD A,(#029C) Fetch own station number from NSTAT. 28D5 LD (#1DFE),A Store it also. 28D8 LD A,3 Default stream. 28DA LD (#1E03),A Now the command block checksum is calculated. Then the block and its checksum are send over the network. #28DD is also the entry point for the send command block routine of System 3d. 28DD REQ_SERV4 LD HL,#1DFD Point to the data buffer. 28E0 LD B,28 Calculate checksum for 28 bytes. 28E2 XOR A Clear checksum. 28E3 REQ_SERV5 ADD A,(HL) Add this byte. 28E4 INC HL 28E5 DJNZ #28E3,REQ_SERV5 Repeat for all bytes. 28E7 LD (HL),A Store the checksum in command block. 28E8 REQ_SERV6 CALL #39ED,NET_STATE Wait until the network is resting. 28EB LD HL,1000 Make the network active for about 57000 28EE LD A,(#1DDA) T cycles. 28F1 OR #80 28F3 OUT (31),A 28F5 REQ_SERV7 DEC HL 28F6 LD A,H 28F7 OR L 28F8 JR NZ,#28F5,REQ_SERV7 28FA LD A,(#1DDA) Make the network inactive. 28FD OUT (31),A 28FF LD B,10 Wait for 125 T cycles. 2901 REQ_SERV8 DJNZ #2901,REQ_SERV8 2903 LD HL,#1DFD Start of command block. 2906 LD E,29 Length. 2908 CALL #3ABC,OUTPAK Send command block. 290B LD HL,#1DF4 Address to store network response code. 290E LD (HL),0 2910 LD E,1 A single byte is to be received. 2912 CALL #3A87,INPAK Get response byte. 2915 JR NZ,#28E8,REQ_SERV6 Send the command block until a response is received. 2917 LD A,(#1DF4) 291A CP 1 The response byte must be '1'. 291C JR NZ,#28E8,REQ_SERV6 Resend the command block until a valid response byte is received. 291E RET Finished. 291F DEFB #00 An unused location. The error restarts & the ROM jumps THE 'ERROR' RESTARTS The following 32 routines consist each of a call to the DISCiPLE error routine directly followed by the error byte. 2920 REP_0 RST #20,DISC_ERR 2921 DEFB 0 'Nonsense in GDOS' 2922 REP_1 RST #20,DISC_ERR 2923 DEFB 1 'Nonsense in GNOS' 2924 REP_2 RST #20,DISC_ERR 2925 DEFB 2 'Statement END error' 2926 REP_3 RST #20,DISC_ERR 2927 DEFB 3 'BREAK requested' 2928 REP_4 RST #20,DISC_ERR 2929 DEFB 4 ',SECTOR error' 292A REP_5 RST #20,DISC_ERR 292B DEFB 5 'FORMAT data lost' 292C REP_6 RST #20,DISC_ERR 292D DEFB 6 'NO DISC in drive' 292E REP_7 RST #20,DISC_ERR 292F DEFB 7 'No "SYSTEM" file' 2930 REP_8 RST #20,DISC_ERR 2931 DEFB 8 'Invalid FILE NAME' 2932 REP_9 RST #20,DISC_ERR 2933 DEFB 9 'Invalid STATION' 2934 REP_10 RST #20,DISC_ERR 2935 DEFB 10 'Invalid DEVICE' 2936 REP_11 RST #20,DISC_ERR 2937 DEFB 11 'VARIABLE not found' 2938 REP_12 RST #20,DISC_ERR 2939 DEFB 12 'VERIFY failed' 293A REP_13 RST #20,DISC_ERR 293B DEFB 13 'Wrong FILE type' 293C REP_14 RST #20,DISC_ERR 293D DEFB 14 'MERGE error' 293E REP_15 RST #20,DISC_ERR 293F DEFB 15 'CODE error' 2940 REP_16 RST #20,DISC_ERR 2941 DEFB 16 'PUPIL set' 2942 REP_17 RST #20,DISC_ERR 2943 DEFB 17 'Invalid CODE' 2944 REP_18 RST #20,DISC_ERR 2945 DEFB 18 'Reading a WRITE file' 2946 REP_19 RST #20,DISC_ERR 2947 DEFB 19 'Writing a READ file' 2948 REP_20 RST #20,DISC_ERR 2949 DEFB 20 'O.K. GDOS 3' 294A REP_21 RST #20,DISC_ERR 294B DEFB 21 'Network OFF' 294C REP_22 RST #20,DISC_ERR 294D DEFB 22 'Wrong DRIVE' 294E REP_23 RST #20,DISC_ERR 294F DEFB 23 'Disc WRITE protected' 2950 REP_24 RST #20,DISC_ERR 2951 DEFB 24 'Not enough SPACE on disc' 2952 REP_25 RST #20,DISC_ERR 2953 DEFB 25 'Directory FULL' 2954 REP_26 RST #20,DISC_ERR 2955 DEFB 26 'File NOT FOUND' 2956 REP_27 RST #20,DISC_ERR 2957 DEFB 27 'END of file' 2958 REP_28 RST #20,DISC_ERR 2959 DEFB 28 'File NAME used' 295A REP_29 RST #20,DISC_ERR 295B DEFB 29 'Not a MASTER station' 295C REP_30 RST #20,DISC_ERR 295D DEFB 30 'STREAM used' 295E REP_31 RST #20,DISC_ERR 295F DEFB 31 'CHANNEL used' THE 'ROM JUMP' TABLE The following jumps are used by routines located in RAM to CALL ROM routines without 'knowing' the exact address where the ROM routine is located. As long as all routines use these jumps to access ROM routines no problems will arise with other ROM versions. 2960 JOPEN_N JP #39A9,OPEN_N 2963 JN_INPUT JP #3808,N_INPUT 2966 JN_OUTPUT JP #386D,N_OUTPUT 2969 JINPAK JP #3A87,INPAK 296C JOUTPAK JP #3ABC,OUTPAK 296F JNET_STATE JP #39ED,NET_STATE 2972 JSEND_RESP JP #3AB5,SEND_RESP 2975 JSEND_NEOF JP #39DF,SEND_NEOF 2978 JFLASH_BOR JP #3B17,FLASH_BORD 297B JBORD_REST JP #3B21,BORD_REST 297E JDISC_BEEP JP #3528,DISC_BEEP 2981 JCFSM JP #353C,CFSM 2984 JTEST_DRV JP #3099,TEST_DRV 2987 JTEST_DRV1 JP #309C,TEST_DRV1 298A JTEST_Y JP #3513,TEST_Y 298D JFRMT_RUN JP #35E9,FRMT_RUN 2990 JD_ERROR JP #3CBC,D_ERROR 2993 JSCAN_CAT JP #335B,SCAN_CAT 2996 JMK_ALLOC JP #32DE,MK_ALLOC 2999 JHL_BUFFER JP #37C3,HL_BUFFER 299C JHGFLE_2 JP #358D,HGFLE_2 299F JLOAD_1ST JP #35C2,LOAD_1ST 29A2 JLBYT JP #3126,LBYT 29A5 JLOAD_FILE JP #3145,LOAD_FILE 29A8 JOFSM_2 JP #3496,OFSM_2 29AB JSURE_MSG JP #3C9B,SURE_MSG 29AE JPRT_A JP #3C2C,PRT_A 29B1 JPRT_NUM JP #3BDF,PRT_NUM 29B4 JRPT_HL JP #37D0,RPT_HL 29B7 JPRT_MSG JP #3C11,PRT_MSG 29BA JREST JP #3030,REST 29BD JRSAD JP #2F4F,RSAD 29C0 JLD_COMREG JP #3085,LD_COMREG 29C3 JSBYT JP #3108,SBYT 29C6 JSLCT_DRV JP #30B3,SET_DRVSD 29C9 JHSVBK_2 JP #3204,HSVBK_2 29CC JDRV_CAP JP #333B,DRV_CAP 29CF JWSAD JP #2F04,WSAD The 'Hook code' routines The following routines are called by the 'hook and command code' routine at #04C3 (in RAM) whenever a hook code was used. THE 'HOOK CODE ADDRESSES' TABLE This jump table consists of the 24 addresses of the routines called by using the various 'hook codes' in the range #1B..#32 (27..50). 29D2 DEFW #2A03,CONS_IN Hook code #1B, 27. 29D4 DEFW #2A16,CONS_OUT Hook code #1C, 28. 29D6 DEFW #2A02,BCHAN_IN Hook code #1D, 29. 29D8 DEFW #2A02,BCHAN_OUT Hook code #1E, 30. 29DA DEFW #2A26,PRT_OUT Hook code #1F, 31. 29DC DEFW #2A2B,KBD_TEST Hook code #20, 32. 29DE DEFW #2A33,SEL_DRIVE Hook code #21, 33. 29E0 DEFW #2A4F,OP_TEMP_M Hook code #22, 34. 29E2 DEFW #2CFF,CLOSE_M2 Hook code #23, 35. 29E4 DEFW #2D1B,ERASE Hook code #24, 36. 29E6 DEFW #2D9E,READ_SEQ Hook code #25, 37. 29E8 DEFW #2DDE,WR_RECD Hook code #26, 38. 29EA DEFW #2DAA,RD_RANDOM Hook code #27, 39. 29EC DEFW #2E66,RD_SECTOR Hook code #28, 40. 29EE DEFW #2E67,RD_NEXT Hook code #29, 41. 29F0 DEFW #2E68,WR_SECTOR Hook code #2A, 42. 29F2 DEFW #2AF1,SET_T_MCH Hook code #2B, 43. 29F4 DEFW #2E69,DEL_M_BUF Hook code #2C, 44. 29F6 DEFW #2E73,OP_TEMP_N Hook code #2D, 45. 29F8 DEFW #2E74,CLOSE_NET Hook code #2E, 46. 29FA DEFW #2E75,GET_PACK Hook code #2F, 47. 29FC DEFW #2E76,SEND_PACK Hook code #30, 48. 29FE DEFW #2E77,HOOK_31 Hook code #31, 49. 2A00 DEFW #2EBE,HOOK_32 Hook code #32, 50. THE 'RS232 NOT SUPPORTED' SUBROUTINE The 'BCHAN_IN' and 'BCHAN_OUT' 'hook codes' are not supported (the DISCiPLE has no RS232 link). 2A02 UNDEFINED1 RET THE 'CONSOLE INPUT' SUBROUTINE Called by using 'hook code' 27 (#1B), it simply waits until a key is pressed. A return is made with the A register holding the character code. 2A03 CONS_IN RES 5,(IY+1) Signal 'ready for a new key'. 2A07 WTKEY EI Enable interrupts. 2A08 HALT Wait for an interrupt. 2A09 RST #10,CALBAS Call the keyboard scan routine in the 2A0A DEFW #02BF,KEYBOARD 'main' ROM. 2A0C BIT 5,(IY+1) Repeat the scan until a key has been 2A10 JR Z,#2A03,WTKEY pressed. 2A12 LD A,(23560) Fetch the character code from (LAST_K). 2A15 RET THE 'CONSOLE OUTPUT' SUBROUTINE By using 'hook code' 28 (#1C) the character held in the A register is printed on the screen, with scroll suppressed. 2A16 CONS_OUT PUSH AF 2A17 LD A,254 Use stream '-2' (attached to "S" the channel). 2A19 OUT_CODE LD HL,23692 This is SCR_CT. 2A1C LD (HL),255 Set scroll counter. 2A1E RST #10,CALBAS Call 'CHAN_OPEN' in the 'main' ROM to 2A1F DEFW #1601,CHAN_OPEN make stream -2 the current. 2A21 POP AF 2A22 RST #10,CALBAS Print the character to the current 2A23 DEFW #0010,PRINT_A_1 stream. 2A25 RET THE 'PRINTER OUTPUT' SUBROUTINE This subroutine is called by using 'hook code' 31 (#1F). It is identical to the preceding one, but the output is directed to stream 3 (normally the printer). 2A26 PRT_OUT PUSH AF 2A27 LD A,3 Select stream 3. 2A29 JR #2A19,OUT_CODE THE 'KEYBOARD TEST' SUBROUTINE This is called using 'hook code' 32 (#20). A return is made with the Zero flag reset if a key is pressed. 2A2B KBD_TEST XOR A Clear A, allowing for the whole keyboard to be examined. 2A2C IN A,(254) Read the keyboard. 2A2E AND #1F Keep only the keyboard bits. 2A30 SUB #1F Return with sign negative and Zero flag 2A32 RET reset if a key is pressed. THE 'SELECT DRIVE' SUBROUTINE This subroutine is called by using 'hook code' 33 (#21). On entry, A holds the drive number; if A isn't equal to 1 or 2 nothing is done. 2A33 SEL_DRIVE CP 1 2A35 JR Z,#2A3D,SEL_DRIVE1 Jump if drive 1 is to be selected. 2A37 CP 2 2A39 JR Z,#2A3D,SEL_DRIVE1 Jump if drive 2 is to be selected. 2A3B XOR A 2A3C RET Otherwise exit. 2A3D SEL_DRIVE1 AND #01 Keep only bit 0. 2A3F LD B,A 2A40 LD (#1ACE),A 2A43 LD A,(#1DDA) Fetch current control port state. 2A46 AND #FE Drop the drive select bit. 2A48 OR B Use the new drive. 2A49 LD (#1DDA),A Exit setting both current control port 2A4C OUT (31),A state and the control port itself. 2A4E RET THE 'OPEN TEMP. "M" CHANNEL' SUBROUTINE This subroutine is used to open a temporary "M" channel in the CHANS area. It is called by using 'hook code' 34 (#22). First a temporary "M" channel is created, then the drive whose number is held into 'D_STR1' is searched for a file whose name is held into 'N_STR1'. A sector map is created with each reset bit indicating a free sector. Various flags are returned as follows: - bit 0 of CHFLAG set with 'write' files. - bit 1 of RECFLG set with 'EOF' block. - bit 2 of RECFLG set with PRINT-type files. On exit, HL holds a 'stream data' displacement that may be used to attach the channel to a stream. 2A4F OP_TEMP_M CALL #2AF1,SET_T_MCH Create a temporary "M" channel. 2A52 PUSH HL Save 'stream displacement'. 2A53 LD A,(IX+25) Fetch the drive number (CHDRIV). 2A56 CALL #2A33,SEL_DRIVE Select the drive. 2A59 PUSH IX 2A5B LD DE,14 Make IX point to CHNAME, the name of 2A5E ADD IX,DE the wanted file. 2A60 CALL #2D43,FIND_FILE Search for the filename. 2A63 JR NZ,#2A95,OP_T_2 Jump if not found. 2A65 INC HL Skip number of sectors used. 2A66 INC HL 2A67 LD D,(HL) Fetch first track and sector. 2A68 INC HL 2A69 LD E,(HL) 2A6A LD IX,#1AC3 Point to the DRAM channel. 2A6E CALL #2F4F,RSAD Read the first sector. 2A71 POP IX Restore channel pointer (in 'main' 2A73 PUSH IX RAM). 2A75 LD DE,540 Microdrive type files consist of records with a length of 540 bytes each. 2A78 OP_T_1 PUSH IX Store data buffer pointer (in 'main' RAM). 2A7A LD IX,#1AC3 Point to the DRAM channel again. 2A7E CALL #3126,LBYT Fetch a byte. 2A81 POP IX Restore data buffer pointer. 2A83 LD (IX+55),A Loading starts with the data block preamble (offset 55). 2A86 INC IX 2A88 CALL #2E62,DEC_DE Decrement DE. 2A8B JR NZ,#2A78,OP_T_1 Repeat until DE=0. 2A8D POP IX 2A8F RES 0,(IX+24) Signal 'read file'. 2A93 POP HL Restore stream 'stream data'. 2A94 RET Finished. 2A95 OP_T_2 LD HL,#1A00 Clear the disk bitmap. 2A98 LD B,195 2A9A OP_T_3 LD (HL),0 2A9C INC HL 2A9D DJNZ #2A9A,OP_T_3 2A9F LD A,#20 Make new disk bitmap. 2AA1 CALL #335B,SCAN_CAT 2AA4 POP IX 2AA6 PUSH IX 2AA8 LD HL,#1AD6 Points to dir. descr. of DFCA. 2AAB LD (HL),6 File is a 'Microdrive file'. 2AAD INC HL 2AAE LD B,10 Copy the 10 characters of the name. 2AB0 OP_T_4 LD A,(IX+14) 2AB3 LD (HL),A 2AB4 INC IX 2AB6 INC HL 2AB7 DJNZ #2AB0,OP_T_4 2AB9 LD B,245 Clear the rest of the CATalogue entry 2ABB OP_T_5 LD (HL),0 of this file. 2ABD INC HL 2ABE DJNZ #2ABB,OP_T_5 2AC0 LD HL,#0000 Reset RPT. 2AC3 LD (#1AD0),HL 2AC6 LD HL,#1BD6 Clear the first 256 bytes of the data 2AC9 LD B,0 buffer. 2ACB OP_T_6 LD (HL),0 2ACD INC HL 2ACE DJNZ #2ACB,OP_T_6 2AD0 LD IX,#1AC3 2AD4 CALL #32DE,MK_ALLOC Allocate the first free sector. 2AD7 LD (#1AD4),DE Store the track and sector number. 2ADB LD IX,#1AD6 Store them also into the CATalogue 2ADF LD (IX+13),D entry. 2AE2 LD (IX+14),E 2AE5 POP IX 2AE7 RES 1,(IX+67) Signal 'Not the EOF block' (RECFLG). 2AEB RES 2,(IX+67) Signal 'PRINT-type file' (opened for 2AEF POP HL writing). Restore stream 'stream data'. 2AF0 RET Finished. THE 'SET A TEMP. "M" CHANNEL' SUBROUTINE This subroutine is also called by using 'hook code' 43 (#2B). It sets a temporary "M" channel in the CHANS area. The subroutine returns with IX pointing to the start of the channel and HL holding a suitable displacement to be eventually inserted in the STRMS area to attach the channel to a stream. 2AF1 SET_T_MCH LD IX,(23631) Fetch (CHANS), the start of the channel area. 2AF5 LD DE,20 Make IX point to the start of the 'new' 2AF8 ADD IX,DE channels. 2AFA SET_T_1 LD A,(IX+0) 2AFD CP 128 2AFF JR Z,#2B31,SET_T_3 Jump if the CHANS area is finished. 2B01 LD A,(IX+4) Fetch the channel specifier. 2B04 AND #7F Clear bit 7 (drop temporary/permanent flag). 2B06 CP "M" 2B08 JR NZ,#2B27,SET_T_2 Jump if not a "M" channel. 2B0A LD A,(23766) Fetch drive number (D_STR1). 2B0D CP (IX+25) Compare it with (CHDRIV). 2B10 JR NZ,#2B27,SET_T_2 Jump if this channel uses a different drive. 2B12 LD BC,(23770) Fetch length of filename (NSTR_1). 2B16 LD HL,(23772) And its startaddress (NSTR_1+2). 2B19 CALL #2BC9,CHK_NAME Check name against 'CHNAME' of this channel. 2B1C JR NZ,#2B27,SET_T_2 Jump if not the same file. 2B1E BIT 0,(IX+24) 2B22 JR Z,#2B27,SET_T_2 Jump if it's a 'read file'. 2B24 JP #2EBF,MD_ERROR Exit if the file is already opened for writing. 2B27 SET_T_2 LD E,(IX+9) Fetch the length of the channel. 2B2A LD D,(IX+10) 2B2D ADD IX,DE Point to the next channel. 2B2F JR #2AFA,SET_T_1 Check next channel. Now the space for the new channel is created at the end of the CHANS area. 2B31 SET_T_3 LD HL,(23635) Calculate end of CHANS area ((PROG)-1), 2B34 DEC HL i.e. the start of the channel. 2B35 PUSH HL 2B36 LD BC,595 Length is '595' bytes. 2B39 RST #10,CALBAS Create the required space by calling 2B3A DEFW #1655,MAKE_ROOM 'MAKE_ROOM'. 2B3C POP DE Restore start address of the channel. 2B3D PUSH DE 2B3E LD HL,#2CE6 Start of "M" channel data. 2B41 LD BC,25 2B44 LDIR Store channel data into the channel. 2B46 LD A,(23766) Fetch drive number (D_STR1). 2B49 LD (IX+25),A Store it into the channel (CHDRIV). 2B4C LD BC,595 Length of the channel. 2B4F PUSH IX Make HL point to the start of the 2B51 POP HL channel. 2B52 CALL #2BA0,REST_F_AD Restore 'start of filename' possibly moved during the 'insertion' of the channel. 2B55 EX DE,HL The start address of the filename goes to HL. 2B56 LD BC,(23770) Fetch length of filename (N_STR1). 2B5A BIT 7,B Jump if the name doesn't exist 2B5C JR NZ,#2B6C,SET_T_6 (N_STR1 = #FFFF). The channel name is transferred into CHNAME. 2B5E SET_T_5 LD A,B 2B5F OR C 2B60 JR Z,#2B6C,SET_T_6 Jump if no more bytes left. 2B62 LD A,(HL) Transfer a character of the name into 2B63 LD (IX+14),A (CHNAME). 2B66 INC HL Point to next locations. 2B67 INC IX 2B69 DEC BC One byte less. 2B6A JR #2B5E,SET_T_5 Continue with next character. Now the 'preambles' are stored into the channel. 2B6C SET_T_6 POP IX Restore start address of channel. 2B6E LD DE,28 Offset for header block preamble. 2B71 CALL #2B86,SETUP_PRE Set-up header preamble. 2B74 LD DE,55 Offset for data block preamble. 2B77 CALL #2B86,SETUP_PRE Set-up data block preamble. 2B7A PUSH IX Make HL point to the start of the 2B7C POP HL channel. 2B7D LD DE,(23631) Calculate the required 'stream offset' 2B81 OR A into HL (i.e. channel start-(CHANS)+1). 2B82 SBC HL,DE 2B84 INC HL 2B85 RET Finished. THE 'SET-UP A PREAMBLE' SUBROUTINE The following subroutine passes a preamble to the specified channel position. On entry IX points to the start of the channel and DE holds the required offset. 2B86 SETUP_PRE PUSH IX Pass start of channel to HL. 2B88 POP HL 2B89 ADD HL,DE Add the offset. 2B8A EX DE,HL DE now points to the preamble area. 2B8B LD HL,#2B94 Start of 'preamble' data. 2B8E LD BC,12 Preamble is 12 bytes long. 2B91 LDIR 2B93 RET THE 'PREAMBLE DATA' TABLE The header and data block preambles consist of the following bytes: 2B94 DEFB #00,#00,#00,#00,#00 2B99 DEFB #00,#00,#00,#00,#00 2B9E DEFB #FF,#FF The Microdrive needs these bytes to fetch the start of a block of bytes when reading a cartridge. THE 'RESTORE FILENAME ADDRESS' ROUTINE After the 'insertion' of some space, the 'filename' whose start addresses are held into (N_STR1+2) and (N_STR2+2) have been moved up in the workspace area. This routine is entered with HL holding the channel start address, and with BC holding the number of 'inserted' bytes. The addresses held into (N_STR1+2) and (N_STR2+2) are then updated, unless the filenames are stored into 'no-dynamic' areas (i.e. before the channel or after STKEND). 2BA0 REST_F_AD PUSH HL Save 'start of channel' twice. 2BA1 PUSH HL 2BA2 LD DE,(23780) Restore start address of the second 2BA6 CALL #2BBB,TST_PLACE filename. 2BA9 LD (23780),DE 2BAD POP HL Restore channel start address. 2BAE LD DE,(23772) Restore start address of the first 2BB2 CALL #2BBB,TST_PLACE filename. 2BB5 LD (23772),DE 2BB9 POP HL Restore channel start address. 2BBA RET Finished. The following subroutine calculates the new filename address. 2BBB TST_PLACE SCF Allow for a further byte. 2BBC SBC HL,DE No action is made if the filename is 2BBE RET NC before the channel. 2BBF LD HL,(23653) Or if it is after (STKEND). 2BC2 SBC HL,DE 2BC4 RET C 2BC5 EX DE,HL Add to DE the number of 'inserted' 2BC6 ADD HL,BC bytes, so returning the new filename 2BC7 EX DE,HL address. 2BC8 RET Finished. THE 'CHECK NAME' SUBROUTINE Whenever a 'filename' is to be compared against the channel name CHNAME, this subroutine is called. On entry, HL must point to the filename to be compared, while C must contain its length. If the comparision is succesful, the Zero flag is returned set. 2BC9 CHK_NAME PUSH IX Save start of channel. 2BCB LD B,10 Length of a filename. 2BCD CHK_NAME1 LD A,(HL) Fetch a byte from the name. 2BCE CP (IX+14) 2BD1 JR NZ,#2BE9,CHK_NAME3 Jump if it doesn't match. 2BD3 INC HL Point to the next character. 2BD4 INC IX 2BD6 DEC B One byte less. 2BD7 DEC C Repeat until all bytes of the name have 2BD8 JR NZ,#2BCD,CHK_NAME1 been matched. 2BDA LD A,B CHNAME remaining length. 2BDB OR A 2BDC JR Z,#2BE9,CHK_NAME3 Exit if all bytes of CHNAME matched. 2BDE CHK_NAME2 LD A,(IX+14) Otherwise the remaining characters of 2BE1 CP 32 CHNAME have to be spaces. 2BE3 JR NZ,#2BE9,CHK_NAME3 Exit if not a space. 2BE5 INC IX Repeat until all bytes of CHNAME have 2BE7 DJNZ #2BDE,CHK_NAME2 been examined. 2BE9 CHK_NAME3 POP IX Restore channel start address. 2BEB RET Finished. THE 'CALL INP' ROUTINE This routine is the same as the Interface 1 'CALL_INP' routine which handles all IF1's channels. The DISCiPLE uses this routine only for "M" channels, for "D" channels a similar routine located in RAM is used. (The only difference is the test of FLAGS3, i.e. IY+124 while emulating the IF1, #1ACF otherwise.) On entry HL holds the address of the service 'input' routine. The routine handles both INPUT and INKEY$ commands. 2BEC CALL_INP RES 3,(IY+2) Signal 'the mode is to be considered as being unchanged'. 2BF0 PUSH HL Store address of service routine. 2BF1 LD HL,(23613) HL points to error address (ERR_SP). 2BF4 LD E,(HL) Fetch the error address. 2BF5 INC HL 2BF6 LD D,(HL) 2BF7 AND A 2BF8 LD HL,#107F,ED_ERROR If the error address is 'ED_ERROR' 2BFB SBC HL,DE ('main' ROM) then an INPUT command was 2BFD JR NZ,#2C28,INKEY$ used. Jump if unequal to 'ED_ERROR'. Now deal with an 'INPUT #' command referring to a "M" channel. 2BFF POP HL Restore address of service routine. 2C00 LD SP,(23613) Clear the machine stack (ERR_SP). 2C04 POP DE Remove 'ED_ERROR'. 2C05 POP DE 2C06 LD (23613),DE Restore the old value of ERR_SP. 2C0A IN_AGAIN PUSH HL Store address of service routine. 2C0B LD DE,#2C10,INPUT_END Return address is 'INPUT_END' below. 2C0E PUSH DE 2C0F JP (HL) Jump to the service routine. When the byte has been read from the required channel, a return is made here to add the byte to the INPUT line, or to return if the byte is equal to CHR$ 13, i.e. ENTER. 2C10 INPUT_END JR C,#2C1A,ACC_CODE Jump with acceptable codes. 2C12 JR Z,#2C17,NO_READ Jump with no data read. 2C14 INPUT_ERR JP #2EBF,MD_ERROR Otherwise jump to the 'hook code' error routine. 2C17 NO_READ POP HL Restore address of service routine and 2C18 JR #2C0A,IN_AGAIN try again. An acceptable code was received, it is added to the INPUT line. 2C1A ACC_CODE CP 13 2C1C JR Z,#2C24,END_INPUT Jump if the code is ENTER. 2C1E RST #10,CALBAS Otherwise the byte is to be added to 2C1F DEFW #0F85,ADD_CHAR0 the INPUT line. This is done by calling into the 'ADD_CHAR' subroutine. 2C21 POP HL Restore address of the service routine 2C22 JR #2C0A,IN_AGAIN and read the next byte. 2C24 END_INPUT POP HL Drop the address of the service routine 2C25 JP #0050,UNPAGE_1 and page-out the DISCiPLE. Enter here to deal with the INKEY$ function (a single character is returned). 2C28 INKEY$ POP HL Restore address of the service routine. 2C29 LD DE,#2C2E,INK$_END Return address is 'INK$_END' below. 2C2C PUSH DE 2C2D JP (HL) Jump to the service routine. 2C2E INK$_END RET C Return with acceptable codes or 2C2F RET Z with no byte read. 2C30 BIT 4,(IY+124) Otherwise EOF was reached, so jump to 2C34 JR Z,#2C14,INPUT_ERR the error routine except when executing a 'MOVE' command. 2C36 OR 1 Then return with Zero and Carry flags 2C38 RET both reset. THE '"M" CHANNEL INPUT' ROUTINE The actual 'input' is handled via 'CALL_INP' above. The service routine is 'MCHAN_IN' below. 2C39 M_INPUT LD IX,(23633) Make IX point to start of channel. 2C3D LD HL,#2C43,MCHAN_IN Address of the service routine. 2C40 JP #2BEC,CALL_INP Jump to the control routine. THE '"M" CHANNEL INPUT' SERVICE ROUTINE This is the actual read a byte from the "M" channel routine. The byte is read from the data buffer in the channel, if it is empty the next sector is loaded from disk (provided that the 'current' data block is not the EOF one) before reading the byte. 2C43 MCHAN_IN BIT 0,(IX+24) Jump to the 'hook code' error routine 2C47 JP NZ,#2EBF,MD_ERROR if (CHFLAG) indicates 'read' file. 2C4A TEST_M_BUF LD E,(IX+11) Fetch current byte counter from 2C4D LD D,(IX+12) (CHBYTE). 2C50 LD L,(IX+69) Fetch record length from (RECLEN). 2C53 LD H,(IX+70) 2C56 SCF Include byte to be read. 2C57 SBC HL,DE 2C59 JR C,#2C6E,CHK_M_EOF Jump if all bytes have been read. 2C5B INC DE Include byte to be read in the byte counter. 2C5C LD (IX+11),E And store it. 2C5F LD (IX+12),D 2C62 DEC DE Position of character to be read. 2C63 PUSH IX Save start address of channel. 2C65 ADD IX,DE IX now points to 'byte to be read - 82'. 2C67 LD A,(IX+82) Fetch the byte. 2C6A POP IX Restore start of channel. 2C6C SCF Signal 'acceptable code'. 2C6D RET Finished. If all bytes in the data block have been read, a check is made to see if it is the 'end of file' block, i.e. the last one. 2C6E CHK_M_EOF BIT 1,(IX+67) Jump if (RECFLG) indicates 'not the End 2C72 JR Z,#2C78,NEW_BUFF Of File' block. 2C74 XOR A Otherwise Zero and Carry flag are reset to signal 'EOF'. 2C75 ADD A,13 Returned byte is CHR$ 13, i.e. ENTER. 2C77 RET Finished. A new data block is now read from the disk drive. 2C78 NEW_BUFF LD DE,0 Clear the byte counter. 2C7B LD (IX+11),E 2C7E LD (IX+12),D 2C81 INC (IX+13) Increment (CHREC), i.e. record number. 2C84 CALL #2C89,GET_RECD Fetch a new data block. 2C87 JR #2C4A,TEST_M_BUF Read the byte. THE 'GET A RECORD' SUBROUTINE This subroutine is used to load a record of a 'MICRODRIVE'-type file. 2C89 GET_RECD LD C,3 Three retries will be made before the routine is exitted when an error occurs. 2C8B BIT 1,(IX+67) 2C8F JR NZ,#2CBA,GET_R3 Jump if (RECFLG) indicates 'EOF' block. 2C91 GET_R1 PUSH IX Store channel pointer (in 'main' RAM). 2C93 LD DE,540 Microdrive file records have 540 bytes each. 2C96 GET_R2 PUSH IX Store data buffer pointer. 2C98 LD IX,#1AC3 Point to the DRAM channel. 2C9C CALL #3126,LBYT Fetch a byte. 2C9F POP IX Restore data buffer pointer. 2CA1 LD (IX+55),A Loading starts with the data block preamble (offset 55). 2CA4 INC IX 2CA6 CALL #2E62,DEC_DE Decrement DE. 2CA9 JR NZ,#2C96,GET_R2 Repeat until DE=0. 2CAB POP IX Restore channel pointer. 2CAD LD A,(IX+68) Fetch number of this record (RECNUM). 2CB0 CP (IX+13) Test it against wanted record number 2CB3 RET Z (CHREC), exit if they are equal. 2CB4 BIT 1,(IX+67) Jump to load the next record if 2CB8 JR Z,#2C91,GET_R1 (RECFLG) indicates that this isn't the EOF one. 2CBA GET_R3 DEC C Decrement retry counter. 2CBB JR Z,#2CE3,MD_ERROR1 Exit via the 'hook code' error routine when three retries have been made. Now the routine reloads the first sector of the file. With 'MICRODRIVE'-type files it is possible to have a 'read' channel attached to a file to which is also a 'write' channel attached. So the last record could have been read into the 'read' channels data buffer after which a new record was added by the 'write' channel. The reason why three retries are made is probably because of the ignoring of errors, signalled by a set Carry flag (remember this is a 'hook code' executing), reported by 'LBYT' and 'RSAD'. The routine does some retrying before quitting. 2CBD PUSH HL Store the registers needed by the 2CBE PUSH IX routine above. 2CC0 PUSH BC 2CC1 LD A,(IX+25) Fetch the drive number from CHDRIV. 2CC4 CALL #2A33,SEL_DRIVE Select the drive. 2CC7 LD DE,14 Make IX point to CHNAME, the name of 2CCA ADD IX,DE the requested file. 2CCC CALL #2D43,FIND_FILE Search for the filename. 2CCF JR NZ,#2CE3,MD_ERROR1 Jump if not found. 2CD1 INC HL Skip 'number of sectors used'. 2CD2 INC HL 2CD3 LD D,(HL) Fetch first track and sector. 2CD4 INC HL 2CD5 LD E,(HL) 2CD6 LD IX,#1AC3 Point to the DRAM channel. 2CDA CALL #2F4F,RSAD Read the first sector. 2CDD POP BC Restore registers. 2CDE POP IX 2CE0 POP HL 2CE1 JR #2C91,GET_R1 Try to find the right record again. THE 'JUMP TO ERROR ROUTINE' Because this jump is only two times 'jump relatived to', it wastes one byte. 2CE3 MD_ERROR1 JP #2EBF,MD_ERROR Jump to the 'hook code' error routine. THE '"M" CHANNEL DATA' TABLE The '25' bytes that compose the initial part of an "M" channel are as follows: 2CE6 DEFW #0008 Main ROM 'output' routine. 2CE8 DEFW #0008 Main ROM 'input' routine. 2CEA DEFB "M"+128 Channel specifier. 2CEB DEFW #2DB8 DISCiPLE ROM 'output' routine. 2CED DEFW #2C39 DISCiPLE ROM 'input' routine. 2CEF DEFW 595 Channel length. 2CF1 DEFW 0 Default for CHBYTE. 2CF3 DEFB 0 Default for CHREC. 2CF4 DEFM " " Default for CHNAME (10 spaces). 2CFE DEFB 255 Default for CHFLAG ('write' channel). THE 'CLOSE FILE' SUBROUTINE By using 'hook code' 35 (#23) the following subroutine is called. It CLOSEs an "M" channel which start adddress is held in the IX register. If the channel is used for reading, then it is reclaimed; but if it is used for writing, any unsent data in the buffer is written to disk before reclaiming the channel. 2CFF CLOSE_M2 BIT 0,(IX+24) Jump if (CHFLAG) indicates that this is 2D03 JR Z,#2D17,CLOSE_M1 a 'read' channel. 2D05 SET 1,(IX+67) Otherwise signal 'EOF record', 2D09 CALL #2DDE,WR_RECD and save it on disk. 2D0C PUSH IX Save channel pointer. 2D0E LD IX,#1AC3 Point to the DRAM channel. 2D12 CALL #353C,CFSM Close the File Sector Map. 2D15 POP IX Restore channel pointer. 2D17 CLOSE_M1 CALL #2E69,DEL_M_BUF Reclaim the channel. 2D1A RET THE 'ERASE' SUBROUTINE This subroutine is called using 'hook code' 36 (#24). It deals with the ERASEing of all file types. It differs from the DISCiPLE's ERASE 'command code': this routine uses a temporary channel and the IF1's extra system variables to ERASE a file. On entry 'D_STR1' must hold the drive number and 'N_STR1' the length and the start of the filename. 2D1B ERASE CALL #2AF1,SET_T_MCH Create a temporary "M" channel. 2D1E PUSH IX Store channel pointer. 2D20 LD DE,14 Make IX point to CHNAME, the name of 2D23 ADD IX,DE the file to be ERASEd. 2D25 CALL #2D43,FIND_FILE Search for the file. 2D28 POP IX Restore channel pointer. 2D2A PUSH AF Save flags. 2D2B JR NZ,#2D3E,ERASE_1 Jump if the file wasn't found. 2D2D LD HL,#1BD6 Point to the start of the disk buffer. 2D30 ADD HL,BC BC holds the directory entries offset (i.e. 0 for first, 256 for second). 2D31 LD (HL),0 Signal 'ERASEd file'. 2D33 PUSH IX Store channel pointer. 2D35 LD IX,#1AC3 Point to the DRAM channel. 2D39 CALL #2F04,WSAD Write sector DE. 2D3C POP IX Restore channel pointer. 2D3E ERASE_1 CALL #2E69,DEL_M_BUF Reclaim the channel. 2D41 POP AF Exit with Zero reset indicating 'file 2D42 RET not found'. THE 'FIND A FILE' SUBROUTINE This routine searches the disk CATalogue for the filename pointed to by the IX register. On exit, Zero reset signals 'file not found'. 2D43 FIND_FILE CALL #3030,REST Reset drive to track 0. 2D46 LD (#1ACA),IX Store the pointer to filename requested. 2D4A FIND_FILE1 LD IX,#1AC3 Point to the DRAM channel. 2D4E CALL #2F4F,RSAD Load a sector to the disk buffer. 2D51 LD BC,0 First entry's offset. 2D54 FIND_FILE2 LD HL,#1BD6 Point to the start of the disk buffer. 2D57 ADD HL,BC Point to directory entry. 2D58 LD A,(HL) 2D59 CP 0 2D5B JR Z,#2D7C,FIND_FILE4 Jump if the file is ERASEd. 2D5D INC HL Point to the filename. 2D5E LD IX,(#1ACA) Fetch pointer to filename requested. 2D62 LD A,10 Length of filename. 2D64 LD (#1DEA),A 2D67 FIND_FILE3 LD A,(IX+0) Fetch a byte from requested name. 2D6A XOR (HL) Compare against found name. 2D6B AND #DF Capitalize. 2D6D JR NZ,#2D7C,FIND_FILE4 Jump if they don't match. 2D6F INC IX Next characters. 2D71 INC HL 2D72 LD A,(#1DEA) 2D75 DEC A 2D76 LD (#1DEA),A 2D79 JR NZ,#2D67,FIND_FILE3 Repeat for all characters in the name. 2D7B RET Return with Zero set to indicate 'file found'. The requested filename wasn't found yet, so examine the next file. 2D7C FIND_FILE4 LD A,B 2D7D CP 1 2D7F JR Z,#2D8D,FIND_FILE5 Jump if second entry handled. 2D81 LD A,(#1DDA) 2D84 AND #04 Jump if using single density, i.e. each 2D86 JR NZ,#2D8D,FIND_FILE5 sector holds one entry. 2D88 LD BC,256 Otherwise examine second entry. 2D8B JR #2D54,FIND_FILE2 The next CATalogue sector has to be loaded, if present. 2D8D FIND_FILE5 INC E Next sector. 2D8E LD A,E 2D8F CP 11 Jump if last sector on current track 2D91 JR NZ,#2D4A,FIND_FILE1 hasn't been loaded yet. 2D93 LD E,1 Otherwise start with sector 1 2D95 INC D on the next track. 2D96 LD A,D 2D97 CP 4 Jump if last track in CATalogue hasn't 2D99 JR NZ,#2D4A,FIND_FILE1 been handled yet. 2D9B CP 0 Otherwise reset Zero flag to signal 2D9D RET 'file not found' and exit. THE 'READ SEQUENTIAL' SUBROUTINE This is called by using 'hook code' 37 (#25). The subroutine reads into the data block of the current "M" channel, the next record of a named PRINT-type file. On entry IX must hold the "M" channel start address, and CHREC the number of the current record. CHREC will be automatically incremented. CHDRIV must hold the drive number and CHNAME must hold the filename. 2D9E READ_SEQ BIT 1,(IX+67) Jump if (RECFLG) indicates that the 2DA2 JR Z,#2DA7,INCREC current record isn't the EOF one. 2DA4 JP #2EBF,MD_ERROR Otherwise exit via the 'hook code' error routine. 2DA7 INCREC INC (IX+13) Increment the record number (CHREC) and continue into 'RD_RANDOM'. THE 'READ RANDOM' SUBROUTINE This subroutine is called by using 'hook code' 39 (#27). The record number CHREC of a PRINT-type file is loaded into the data block. The other variables are to be set as for 'READ_SEQ' above. 2DAA RD_RANDOM CALL #2C89,GET_RECD Load CHREC record. 2DAD BIT 2,(IX+67) Return only if (RECFLG) indicates that 2DB1 RET Z it is a PRINT-type file. 2DB2 CALL #2E69,DEL_M_BUF Otherwise reclaim the channel and exit 2DB5 JP #2EBF,MD_ERROR via the 'hook code' error routine. THE '"M" CHANNEL OUTPUT' ROUTINE This routine handles the "M" channel output. The byte stored in the A register is stored into the 512-byte buffer. When it is filled, the record is written onto disk. 2DB8 MCHAN_OUT LD IX,#FFFA This is -6. 2DBC ADD IX,DE Point to the start of the channel. 2DBE BIT 0,(IX+24) Continue only if (CHFLAG) indicates 2DC2 JP Z,#2EBF,MD_ERROR that this is a 'write' file. 2DC5 LD E,(IX+11) Fetch the byte pointer (CHBYTE). 2DC8 LD D,(IX+12) 2DCB PUSH IX Save start address of channel. 2DCD ADD IX,DE Point to 'first free byte in buffer'-82. 2DCF LD (IX+82),A Store the byte into the buffer. 2DD2 POP IX Restore start of channel. 2DD4 INC DE Update (CHBYTE). 2DD5 LD (IX+11),E 2DD8 LD (IX+12),D 2DDB BIT 1,D Return if the buffer is not filled 2DDD RET Z (position 512 has not been reached). If the buffer is filled, the routine continues into 'WR_RECD' below. THE 'WRITE RECORD' SUBROUTINE This subroutine is called by using 'hook code' 38 (#26). The record held in the "M" channel pointed by the IX register (with name CHNAME and number CHREC), is written onto the disk inserted into drive CHDRIV. 2DDE WR_RECD LD A,(IX+25) Fetch the drive number (CHDRIV). 2DE1 CALL #2A33,SEL_DRIVE Select the drive. 2DE4 PUSH IX Save start address of channel. 2DE6 LD B,10 Counts ten characters. 2DE8 CP_NAME LD A,(IX+14) Copy CHNAME into RECNAM. 2DEB LD (IX+71),A 2DEE INC IX 2DF0 DJNZ #2DE8,CP_NAME 2DF2 POP IX Restore start of channel. 2DF4 LD C,(IX+11) Copy CHBYTE into RECLEN. 2DF7 LD (IX+69),C 2DFA LD A,(IX+12) 2DFD LD (IX+70),A 2E00 LD A,(IX+13) Copy CHREC into RECNUM. 2E03 LD (IX+68),A 2E06 PUSH IX Make HL point to the start of the data 2E08 POP HL workspace, 2E09 LD DE,67 i.e. RECFLG. 2E0C ADD HL,DE 2E0D CALL #2E27,CHKS_HD_R Calculate DESCHK checksum. 2E10 LD DE,15 Make HL point to the start of the 2E13 ADD HL,DE 512-byte buffer. 2E14 CALL #2E2C,CHKS_BUFF Calculate DCHK checksum. 2E17 CALL #2E45,SAVE_RECD Save the record to disk. 2E1A LD DE,0 Clear CHBYTE. 2E1D LD (IX+11),E 2E20 LD (IX+12),D 2E23 INC (IX+13) Increment the record number (CHREC). 2E26 RET Finished. THE 'CALCULATE/COMPARE CHECKSUM' ROUTINE This routine is used to calculate DESCHK and DCHK checksums, or to compare the previous checksum against the current one; the Zero flag is returned set if the checksums match. The entry point is CHK_HD_R for DESCHK, or CHKS_BUFF for DCHK checksum. On entry in both cases HL must contain the start address of the block for which the checksum is to be obtained. 2E27 CHKS_HD_R LD BC,14 The block length. 2E2A JR #2E2F,CHKS_ALL Calculate checksum for the block. 2E2C CHKS_BUFF LD BC,512 The block length. 2E2F CHKS_ALL PUSH HL The start address is preserved. 2E30 LD E,0 Clear checksum. 2E32 CHKS_1 LD A,E Add the current byte to the previous 2E33 ADD A,(HL) sum. 2E34 INC HL Point to next location. 2E35 ADC A,1 Include also the carry + 1. 2E37 JR Z,#2E3A,CHKS_2 Jump if A reaches zero. 2E39 DEC A Otherwise balance the 'ADC' above. 2E3A CHKS_2 LD E,A Update sum. 2E3B DEC BC One byte less to add. 2E3C LD A,B 2E3D OR C 2E3E JR NZ,#2E32,CHKS_1 Repeat until all bytes have been added. 2E40 LD A,E 2E41 CP (HL) Compare with previous checksum. 2E42 LD (HL),A Store the new one. 2E43 POP HL Restore start address. 2E44 RET Finished. THE 'SAVE A RECORD' SUBROUTINE This subroutine saves the 540 byte Microdrive-file record to disk. 2E45 SAVE_RECD PUSH IX Save the channel pointer. 2E47 PUSH IX 2E49 POP HL Calculate the address of the first byte 2E4A LD DE,55 to SAVE, i.e. the data block preamble 2E4D ADD HL,DE (offset 55). 2E4E LD DE,540 Length of the record to be written. 2E51 LD IX,#1AC3 Point to the DRAM channel. 2E55 SAVE_REC1 LD A,(HL) Fetch a byte. 2E56 CALL #3108,SBYT Save it to disk. 2E59 INC HL 2E5A CALL #2E62,DEC_DE Decrement DE. 2E5D JR NZ,#2E55,SAVE_REC1 Repeat until DE=0. 2E5F POP IX Restore the channel pointer. 2E61 RET Finished. THE 'DECREMENT DE' SUBROUTINE This very small subroutine decrements DE and returns with the Zero flag indicating if DE holds zero on exit. The purpose of this three-byte subroutine is unclear, it doesn't save a single byte (a CALL instruction takes also three bytes so this subroutine makes the code only longer), and the readability of the code (or the source) doesn't improve really. 2E62 DEC_DE DEC DE DE=DE-1. 2E63 LD A,D 2E64 OR E Set Zero flag if DE=0. 2E65 RET Finished. THE 'READ SECTOR' SUBROUTINE This subroutine is not implemented, it is called by using 'hook code' 40 (#28). When used with an IF1 the sector, which number is held in CHREC, is read into the channel area. If the sector doesn't belong to a PRINT-type file the data buffer is cleared before returning. On entry the required drive motor has to be turned on. 2E66 RD_SECTOR RET THE 'READ NEXT SECTOR' SUBROUTINE This subroutine called by using 'hook code' 41 (#29) isn't implemented either. It should load into the channel area the first header and data block that pass through the Microdrive head. The required drive motor has to be started before calling this routine. 2E67 RD_NEXT RET THE 'WRITE SECTOR' SUBROUTINE Another unimplemented subroutine, called by using 'hook code' 42 (#2A) it writes the data block in the current channel (pointed to by IX) to the sector specified by CHREC. The required Microdrive has to be started and all channel variables, such as CHNAME, are to be set as required before calling the routine. 2E68 WR_SECTOR RET THE 'RECLAIM "M" CHANNEL' SUBROUTINE This subroutine (also called by using 'hook code' 44 (#2C)) is used to reclaim the "M" channel pointed by the IX register. Unlike the IF1 routine this routine neither closes the stream(s) attached to this channel, nor updates the stream data for channels moved down after the reclaiming. 2E69 DEL_M_BUF PUSH IX Make HL point to the start of the 2E6B POP HL channel. 2E6C LD BC,595 Length of the channel. 2E6F RST #10,CALBAS Delete the channel area by calling 2E70 DEFW #19E8,RECLAIM_2 'RECLAIM_2' in the 'main' ROM. 2E72 RET Finished. THE 'OPEN TEMP. "N" CHANNEL' SUBROUTINE The DISCiPLE doesn't support the use of "N" channels, temporary or permanent, by the user. This routine is called by using 'hook code' 45 (#2D). 2E73 OP_TEMP_N RET THE 'CLOSE NETWORK CHANNEL' SUBROUTINE Because the DISCiPLE doesn't support "N" channels, the CLOSEing of them isn't supported either ('hook code' 46 (#2E)). 2E74 CLOSE_NET RET THE 'GET PACKET FROM NETWORK' SUBROUTINE Called by using 'hook code' 47 (#2F), this subroutine to fetch a header and data block from the network isn't implemented. 2E75 GET_PACK RET THE 'SEND PACKET' SUBROUTINE The DISCiPLE doesn't support the sending of a header and data block over the network ('hook code' 48 (#30)). 2E76 SEND_PACK RET THE 'CREATE IF1 VARIABLES' SUBROUTINE This subroutine (called by using 'hook code' 49 (#31)) has the task of creating the IF1's new system variables if nonexistent. Many variables are initialised to their default values. 2E77 HOOK_31 LD HL,(23631) Fetch start of channel area (CHANS). 2E7A LD DE,#A349 This is -23735. 2E7D ADD HL,DE The Carry flag is now set if the CHANS area starts after address 23734, i.e. the 'new' variables exist already. 2E7E JR C,#2EA6,VAR_EXIST Jump if they exist already. 2E80 LD HL,(23651) Clear the calculator stack by copying 2E83 LD (23653),HL (STKBOT) into (STKEND). 2E86 LD HL,23698 Set (MEM) with the address of the 2E89 LD (23656),HL MEMBOT area. 2E8C LD HL,23733 One location before the new space is needed. 2E8F LD BC,58 There are 58 new variables. 2E92 RST #10,CALBAS Use 'main' ROM 'MAKE_ROOM' to create 2E93 DEFW #1655,MAKE_ROOM the space. 2E95 LD HL,#2EAB Address of 'default values' table. 2E98 LD BC,19 There are 19 default values. 2E9B LD DE,23734 Start of 'new' variables area. 2E9E LDIR Store default values. 2EA0 LD A,1 Set (COPIES) to 1. 2EA2 LD (23791),A 2EA5 RET Finished. 2EA6 VAR_EXIST RES 1,(IY+124) Signal 'new variables already exist'. 2EAA RET THE 'SYSTEM VARS DEFAULT VALUES' TABLE This table contains the default values of all the 'new' IF1 system variables from FLAGS3 to SER_FL. 2EAB DEFB #02 Default for FLAGS3 (bit 1 is set to signal that the shadow ROM has been paged in for the first time). 2EAC DEFW #01F0 Default for VECTOR is the IF1's 'ERR_6' address (nonsense for DISCiPLE). 2EAE LD HL,0 This short subroutine is used to call 2EB1 CALL 0 'main' ROM routines from the IF1 ROM, 2EB4 LD (23738),HL it isn't used with the DISCiPLE. 2EB7 RET 2EB8 DEFW 12 Default for BAUD, i.e. 9600 baud. 2EBA DEFB 1 Default for NSTAT. 2EBB DEFB 0 Default for IOBORD, the colour during IF1 I/O (black). 2EBC DEFW 0 Default for SER_FL. THE 'CALL IF1 SUBROUTINE' SUBROUTINE Not supported by the DISCiPLE this subroutine, called by using 'hook code' 50 (#32), is designed to call IF1 ROM-routines when the 'main' ROM is paged in. 2EBE RET THE 'HOOK CODE ERROR' ROUTINE Whenever an error is encountered in the 'hook code' routines a jump is made here to signal the error and clear the machine stack when necessary. 2EBF MD_ERROR CALL #3B21,BORD_REST Restore the border colour. 2EC2 LD HL,(#0296) 2EC5 LD A,H 2EC6 OR L 2EC7 JR Z,#2ECA,MD_ERR1 Jump if the stack isn't to be cleared. 2EC9 LD SP,HL Otherwise clear the stack. 2ECA MD_ERR1 XOR A 2ECB DEC A 2ECC SCF Exit with A holding 255 and Carry flag 2ECD RET set. The disk routines THE 'STORE INTERRUPT STATE' SUBROUTINE This subroutine stores the Interrupt Flip Flop of the Z80 and returns with interrupts disabled. Whenever the DISCiPLE needs the interrupts to be disabled with disk operations the status of the IFF (DI or EI) is stored. When the disk operation is finished the IFF is restored to the state it was in before the interrupts were disabled. NOTE: As a result of a bug in the Z80 itself the stored state of the IFF can be wrong if interrupts are enabled. The problem occurs when an interrupt is accepted (implying: interrupts enabled) during the execution of the 'LD A,R' or 'LD A,I' instruction. A solution to this problem is a second test if the IFF indicates interrupts disabled. With a Spectrum it is unlikely that two interrupts follow each other within a very short time, so a second test should cure the problem. A better method can be found in the 'Zilog Z80 Family Data Book'. The best method is replacing the Z80 with a CMOS version, the bug has been fixed in that Z80 type. 2ECE STORE_IFF PUSH AF 2ECF LD A,I Set the P/V flag according to the state 2ED1 PUSH AF of the IFF2. 2ED2 DI 2ED3 EX (SP),HL Get the Flag register in L while saving HL. 2ED4 LD (#1ACC),HL Store it. (IFF) 2ED7 POP HL Restore HL and AF. 2ED8 POP AF 2ED9 RET Finished. THE 'RESTORE INTERRUPT STATE' SUBROUTINE This subroutine restores the interrupt state to the original state (DI or EI) (see NOTE above). 2EDA REST_IFF PUSH AF Save the contents of the needed 2EDB PUSH HL registers. 2EDC LD HL,(#1ACC) Fetch the previous IFF state. 2EDF EX (SP),HL Restore HL and store IFF state. 2EE0 POP AF The IFF state is now contained in the P/V flag. 2EE1 JP PO,#2EE5,REST_IFF1 Jump if interrupts were disabled. 2EE4 EI Otherwise enable interrupts. 2EE5 REST_IFF1 POP AF 2EE6 RET Finished. THE 'WRITE PRECOMPENSATION' SUBROUTINE This subroutine is called before a write command is send to the Floppy Disk Controller (FDC). Its task is to enable write precompensation on the inner tracks to get a more reliable working of the data transfers. On entry C holds the FDC command. 2EE7 PRECOMP LD A,(#1DDA) Fetch current control port state. 2EEA AND #04 Write precompensation isn't needed when 2EEC JR NZ,#2F01,PRECOMP_2 using single density, so skip the following routine when using SD. 2EEE LD B,64 Start write precomp. at track 64. 2EF0 CALL #333B,DRV_CAP Get drive capacity in A. 2EF3 AND #7F Keep only the number of tracks. 2EF5 CP 80 2EF7 JR Z,#2EFB,PRECOMP_1 Jump if drive has 80 tracks. 2EF9 SRL B Otherwise precomp. starts at track 32. 2EFB PRECOMP_1 LD A,D Fetch current track. 2EFC AND B 2EFD JR Z,#2F01,PRECOMP_2 Jump if not at tracks above 63 or 31. 2EFF RES 1,C Otherwise enable write precompensation (reset bit 1 of the command). 2F01 PRECOMP_2 JP #3085,LD_COM_REG Give the command to the FDC. THE 'WRITE SECTOR' SUBROUTINE This subroutine writes the contents of the data buffer to sector E on track D. 2F04 WSAD XOR A Reset retry counter. 2F05 LD (#1DDB),A 2F08 WSAD_1 CALL #2FAC,SET_TRKSEC Select drive, side, density and sector and position the head above the correct track. 2F0B LD C,%10100010 Write a single sector, enable spin-up sequence, no settling delay, disable precompensation, normal data mark. 2F0D CALL #2EE7,PRECOMP Enable precompensation when neccesary and give the command to the FDC. 2F10 CALL #37C3,HL_BUFFER Make HL point to the data buffer. 2F13 CALL #2F1F,WR_OP Write the sector. 2F16 CALL #2F94,SECTOR_ERR Check if there was an error, report it when retried often enough. 2F19 RET Z Exit with no errors. 2F1A CALL #2FF8,WRONGTRACK Check if head is above correct track. 2F1D JR #2F08,WSAD_1 Try again if no succes. THE 'SEND DATA TO FDC' SUBROUTINE This subroutine handles the actual saving of a sector. It keeps sending a byte at a time to the FDC as long as it asks for one (sector length doesn't matter). 2F1F WR_OP CALL #2ECE,STORE_IFF Store maskable interrupt state and disable maskable interrupts. 2F22 LD BC,219 BC holds the I/O port address of the data register of the FDC. 2F25 JR #2F2A,WR_TST_DRQ Jump into the save loop. 2F27 WR_LOOP OUTI Send a byte to the FDC (port BC) then increment HL (and decrement B). 2F29 NOP Waste some time. 2F2A WR_TST_DRQ IN A,(27) Fetch FDC status. 2F2C BIT 1,A Test Data ReQuest bit. 2F2E JR NZ,#2F27,WR_LOOP Jump if FDC requests a byte. 2F30 IN A,(27) Otherwise fetch FDC status again. 2F32 BIT 1,A 2F34 JR NZ,#2F27,WR_LOOP Jump if FDC requests a byte. 2F36 IN A,(27) 2F38 BIT 1,A 2F3A JR NZ,#2F27,WR_LOOP 2F3C IN A,(27) 2F3E BIT 1,A 2F40 JR NZ,#2F27,WR_LOOP 2F42 BIT 0,A Test Busy bit. 2F44 JR NZ,#2F2A,WR_TST_DRQ Repeat until FDC is ready. 2F46 CALL #2EDA,REST_IFF Restore the interrupt state. 2F49 BIT 6,A Test Write Protected bit. 2F4B RET Z Return if not write protected. 2F4C JP #294E,REP_23 Otherwise give 'Disc WRITE protected' error. THE 'READ SECTOR' SUBROUTINE This subroutine loads the contents of the data buffer from sector E on track D. 2F4F RSAD XOR A Clear retry counter. 2F50 LD (#1DDB),A 2F53 RSAD_1 CALL #2FAC,SET_TRKSEC Set drive, side, density, sector and position the head above the correct track. 2F56 LD C,%10000000 Read a single sector, enable spin-up sequence, no settling delay. 2F58 CALL #3085,LD_COM_REG Give the command to the FDC. 2F5B CALL #37C3,HL_BUFFER Make HL point to the data buffer. 2F5E CALL #2F6A,RD_OP Read the sector. 2F61 CALL #2F94,SECTOR_ERR Check if there was an error, report it when retried often enough. 2F64 RET Z Exit with no errors. 2F65 CALL #2FF8,WRONGTRACK Check if head is above correct track. 2F68 JR #2F53,RSAD_1 Try again if no succes. THE 'GET DATA FROM FDC' SUBROUTINE This subroutine handles the actual loading of a sector. It keeps fetching a byte at a time from the FDC as long as it asks to get one (sector length doesn't matter). 2F6A RD_OP CALL #2ECE,STORE_IFF Store the maskable interrupt state and disable interrupts. 2F6D LD BC,219 I/O address of the FDCs data register. 2F70 JR #2F75,RD_TST_DRQ Jump into the load loop. 2F72 RD_LOOP INI Get a byte from the FDC and increment HL (and decrement B). 2F74 NOP Wait for a moment. 2F75 RD_TST_DRQ IN A,(27) Fetch FDC status. 2F77 BIT 1,A Test Data ReQuest bit. 2F79 JR NZ,#2F72,RD_LOOP Jump if FDC has read a byte. 2F7B IN A,(27) Otherwise fetch FDC status again. 2F7D BIT 1,A 2F7F JR NZ,#2F72,RD_LOOP Jump if FDC has read a byte. 2F81 IN A,(27) 2F83 BIT 1,A 2F85 JR NZ,#2F72,RD_LOOP 2F87 IN A,(27) 2F89 BIT 1,A 2F8B JR NZ,#2F72,RD_LOOP 2F8D BIT 0,A Test Busy bit. 2F8F JR NZ,#2F75,RD_TST_DRQ Repeat until FDC is ready. 2F91 JP #2EDA,REST_IFF Restore interrupt state and exit. THE 'CHECK SECTOR ERROR' SUBROUTINE This subroutine checks if the FDC reported an error, on entry A holds the FDC status byte. If there wasn't one HL points to the start of the data buffer and the RPT is reset, a return is made with the Zero flag set. If there was an error a return is made with the Zero flag reset unless ten retries have been made, then an error is reported. 2F94 SECTOR_ERR AND %00011100 Mask the non error bits. 2F96 JR NZ,#2F9F,SEC_ERR1 Jump with an error. 2F98 CALL #37E7,RES_RPT Otherwise reset the data buffer pointer (RPT). 2F9B CALL #37C3,HL_BUFFER Make HL point to the data buffer. 2F9E RET Z Return with Zero flag set (always set here). 2F9F SEC_ERR1 LD A,(#1DDB) 2FA2 INC A Increment the retry counter. 2FA3 LD (#1DDB),A 2FA6 CP 10 If 10 retries have been made 'SECTOR 2FA8 RET NZ error' is given, else exit with Zero 2FA9 JP #2928,REP_4 reset. THE 'SET TRACK AND SECTOR' SUBROUTINE This subroutine is used to select the required drive, side, density, sector to be handled and to position the drive head above the required track. Note I: The routine starts with a test if DE=0, but when the ROM is paged in high (System loaded) the DISCiPLE will crash on the 'CALL #0408' (which ends in the RAM located 'END OF STATEMENT' routine) whenever DE is 0. It seems that this part of the routine is from an earlier ROM version. Note II: The head is moved relative to the current position (fetched from the FDCs track register), when the drive selected is not the same as the previous one the DISCiPLE can get confused. 2FAC SET_TRKSEC LD A,D 2FAD OR E 2FAE JR NZ,#2FBB,SET_TRK1 Jump if DE<>0. 2FB0 CALL #0408,TEST_ Test the .. flag. 2FB3 JP Z,#2956,REP_27 Give 'END of file' error when reset. 2FB6 LD SP,(#0296) Otherwise clear the machine stack. 2FBA RET 2FBB SET_TRK1 CALL #30B3,SET_DRVSD Select drive, side and density. 2FBE LD A,E Store the required sector number into 2FBF OUT (155),A the FDC's sector register. 2FC1 CALL #3B17,FLASH_BORD Change the border colour when wanted. 2FC4 SET_TRK2 LD A,D Track to A. 2FC5 AND #7F Mask highest bit which indicates side. 2FC7 LD B,A 2FC8 CALL #3067,FDC_READY Wait until FDC is ready, test BREAK. 2FCB IN A,(91) Fetch contents of FDC's track register. 2FCD CP B Compare against required track. 2FCE RET Z Exit if already on right track. 2FCF PUSH BC 2FD0 LD C,%01111000 Step-out, update track register, disable spin-up sequence, no verify, step rate 6 ms. 2FD2 JR NC,#2FD6,SET_TRK3 Jump if required track lies outside (more towards track 0) current track. 2FD4 LD C,%01011000 Step-in, update track register, disable spin-up sequence, no verify, step rate 6 ms. 2FD6 SET_TRK3 CALL #3085,LD_COM_REG Execute the command, move one track. 2FD9 CALL #3067,FDC_READY Wait until FDC is ready, test BREAK. 2FDC IN A,(91) Fetch current track again. 2FDE POP BC 2FDF CP B Compare with required track. 2FE0 RET Z Exit if required track reached. 2FE1 CALL #2FE6,STEP_DELAY Wait for the number of msec's specified 2FE4 JR #2FC4,SET_TRK2 by (STPRAT) before giving a next step. THE 'STEP DELAY' SUBROUTINE This subroutine does the waiting between the executing of two step commands. By altering the value of the (STPRAT) system variabele (POKE @3,n) the time being waited can be altered. There is no reason, except for drive specs, why (STPRAT) couldn't be POKEd to any value below 6 as stated in the DISCiPLE manual. 2FE6 STEP_DELAY LD A,(#029B) Fetch (STPRAT). 2FE9 AND A 2FEA STEP_D1 RET Z Exit if 'msec-counter' reaches zero. 2FEB WAIT_1MSEC PUSH AF 2FEC LD BC,135 With this value the following loop takes 3505 T states (about 1msec) to complete. 2FEF WAIT_1M1 DEC BC 2FF0 LD A,B 2FF1 OR C 2FF2 JR NZ,#2FEF,WAIT_1M1 Repeat until counter reaches zero. 2FF4 POP AF 2FF5 DEC A Decrease 'msec-counter'. 2FF6 JR #2FEA,STEP_D1 THE 'WRONG TRACK' SUBROUTINE This subroutine checks whether the head is above the right track. The current track number is found by reading the ID Field of the first encountered sector on this track. The track number is then stored into the track register of the FDC. When no ID Field can be found the retry counter is incremented, when this reaches 5 the other density is selected and when it reaches 10 the 'FORMAT data lost' error is given. 2FF8 WRONGTRACK LD C,%11001000 Read Address, disable spinup, no delay. 2FFA CALL #3085,LD_COM_REG Execute the command. 2FFD LD HL,#1DDC Address where the ID Field is loaded. 3000 CALL #2F6A,RD_OP Get the six byte ID Field of the first sector encountered. 3003 AND %00011100 3005 JR NZ,#300D,WRONGT_1 Jump if there was an error. 3007 LD A,(#1DDC) Otherwise store the current track 300A OUT (91),A number into the FDC's track register. 300C RET 300D WRONGT_1 LD A,(#1DDB) 3010 INC A Increment retry counter. 3011 LD (#1DDB),A 3014 CP 5 3016 JR NZ,#3025,WRONGT_2 Jump if it hasn't reached 5. 3018 LD A,(#1DDA) Otherwise fetch current control port 301B XOR #04 status and invert Density bit. 301D LD (#1DDA),A 3020 OUT (31),A Set other density. 3022 LD A,(#1DDB) Fetch retry counter 3025 WRONGT_2 CP 10 3027 JR NZ,#2FF8,WRONGTRACK Retry if it hasn't reached 10. 3029 JP #292A,REP_5 Otherwise 'FORMAT data lost' is given. THE 'TRACK_0' SUBROUTINE This subroutine resets the head of the current drive to track 0. It has two entry points, the first is used by the ROM located routines, while the second (at #3030) is used by the 'REST' command code (code 64 or #40). After the head has been resetted, a test is made whether there is a disk in the drive. NOTE: This test fails for 31/2" drives. 302C TRACK_0 LD B,1 Wait for 1 INDEX pulse. 302E JR #3032,REST_1 3030 REST LD B,4 Wait for 4 INDEX pulses. 3032 REST_1 PUSH BC 3033 LD DE,1 Signal 'track 0, sector 1'. 3036 CALL #30B3,SET_DRVSD Set drive, side and density. 3039 LD C,%11010000 Terminate all operations. 303B CALL #3088,LD_COM_R1 Execute the FDC command. 303E LD B,0 Wait about 1 msec. 3040 REST_2 DJNZ #3040,REST_2 3042 JR #304F,REST_HEAD1 Jump into the 'reset head' routine. The following code resets the drive head to track 0. 3044 REST_HEAD LD C,%01111000 Step-out, update track register, disable spin-up sequence, no verify, step rate 6 msec. 3046 CALL #3085,LD_COM_REG Execute the command. 3049 CALL #3067,FDC_READY Wait until the FDC is ready. 304C CALL #2FE6,STEP_DELAY Wait for (STPRAT) msec before continuing. 304F REST_HEAD1 IN A,(27) Fetch the FDC status register. 3051 BIT 2,A 3053 JR Z,#3044,REST_HEAD Repeat until head is above track 0. 3055 POP BC The routine now checks whether there is a disk in the drive. This works correct only with 51/4" drives, with 31/2" the routine stays in the loop at #305F forever when there is no disk in the drive. A solution would be to exit the loops whenever the FDC isn't busy anymore and discard the time limit. 3056 REST_DISK LD HL,8192 The INDEX signal has to become low within about 0.13 sec. 3059 REST_DISK1 IN A,(27) Fetch FDC status. 305B BIT 1,A 305D JR NZ,#307D,REST_DISK3 Jump if INDEX signal is high. 305F REST_DISK2 IN A,(27) Otherwise fetch the FDC status again. 3061 BIT 1,A 3063 JR Z,#305F,REST_DISK2 Repeat until it gets high again. 3065 DJNZ #3056,REST_DISK Repeat for B times. Exit via 'FDC_READY' below when B gets to zero. THE 'WAIT UNTIL FDC IS READY' SUBROUTINE This small subroutine waits until the FDC is ready. When the BREAK key is pressed during the waiting, an error is reported. 3067 FDC_READY IN A,(27) Fetch the FDC status. 3069 BIT 0,A 306B RET Z Exit if it's indicating 'FDC ready'. Now the BREAK key is tested. 306C LD A,#7F 306E IN A,(254) 3070 RRA 3071 JR C,#3067,FDC_READY Jump if the SPACE key isn't pressed. 3073 LD A,#FE 3075 IN A,(254) 3077 RRA 3078 JR C,#3067,FDC_READY Jump if CAPS isn't pressed also. 307A JP #2926,REP_3 Otherwise 'BREAK requested'. THE 'TRACK_0' ROUTINE CONTINUED 307D REST_DISK3 DEC HL Decrease time limit. 307E LD A,H 307F OR L 3080 JR NZ,#3059,REST_DISK1 Continue if limit isn't exceeded. 3082 JP #292C,REP_6 Otherwise 'NO DISC in drive'. THE 'LOAD FDC COMMAND REG.' SUBROUTINE This subroutine loads the FDC command register with the command held in the Z80's C register. The entry point 'LD_COM_R1' is used to give the 'terminate all operations' command to the FDC, it makes no sense to wait for the FDC to get ready if the current command is to be aborted. 3085 LD_COM_REG CALL #3067,FDC_READY Wait until FDC is ready, test BREAK. 3088 LD_COM_R1 LD A,C Load the command in the FDC's command 3089 OUT (27),A register. 308B LD B,30 This value is for a 110 µsec wait. 308D LD A,(#1DDA) Fetch current control port state. 3090 AND #04 Keep only density select bit. 3092 JR NZ,#3096,LD_COM_R2 Jump with Single Density. 3094 LD B,15 Otherwise a 54 µsec wait is taken. 3096 LD_COM_R2 DJNZ #3096,LD_COM_R2 Waste some time. 3098 RET Finished. THE 'TEST DRIVE' SUBROUTINE This subroutine checks if the specified drive is defined (only if it's number isn't 1, then it is accepted right away). The entry point at #3099 is used when the drive is specified in UFIA1. The entry point at #309C is used whenever the drive is specified in the A register. On exit (IX+11) holds the hardware representation of the drive to be used. 3099 TEST_DRV LD A,(#1E01) Fetch drive number from UFIA1. 309C TEST_DRV1 CP 1 309E JR Z,#30AF,TEST_DRV2 Jump if drive one is to be used. 30A0 CP 2 Otherwise give 'Wrong DRIVE' error if 30A2 JP NZ,#294C,REP_22 drive isn't drive two. 30A5 LD A,(#029A) Fetch (TRAKS2) system variable. 30A8 CP 0 30AA JP Z,#294C,REP_22 Give error if drive isn't defined. 30AD LD A,0 Zero is hardware representation of drive two. 30AF TEST_DRV2 LD (IX+11),A Store hardware representation. 30B2 RET Finished. THE 'SET DRIVE PARAMETERS' SUBROUTINE This subroutine selects the drive, side and density by setting the right bits in the control port (I/O address 31). 30B3 SET_DRVSD LD B,(IX+11) Fetch hardware drive representation. 30B6 LD A,(#1DDA) Fetch current control port status. 30B9 AND %00000001 Keep only drive 1/2 select bit. 30BB CP B Set Zero flag if drive isn't changed. 30BC PUSH AF 30BD LD A,(#1DDA) Fetch current control port status 30C0 AND %11111100 again. Mask drive and side select bits. 30C2 LD C,A Store result temporary. 30C3 LD A,D Fetch track. 30C4 RLCA Rotate side select bit to bit 1. 30C5 RLCA 30C6 AND %00000010 Only keep side select. 30C8 OR B Include drive select. 30C9 OR C Include all other bits. 30CA LD (#1DDA),A Set current control port status. 30CD OUT (31),A Activate settings. 30CF POP AF Get Zero flag. 30D0 RET Z Exit if drive hasn't changed. 30D1 LD A,30 Otherwise wait for 30 msec. 30D3 JP #2FEB,WAIT_1MSEC Exit via 'WAIT_1MSEC'. THE 'PROGRAM NUMBER' SUBROUTINE This subroutine calculates the program number from track and sector number and the contents of RPT-high (which holds 0 for odd program numbers and 1 for even ones (double density only)). It is used to get the program number printed in the extended CATalogue. 30D6 PROG_NUM PUSH DE Track and sector to BC. 30D7 POP BC 30D8 XOR A Clear A. 30D9 DEC B 30DA JP M,#30E3,PROG_NUM2 Jump with track 0, B now holds -1. 30DD PROG_NUM1 ADD A,10 Otherwise set A to 10*track number. 30DF DEC B 30E0 JP P,#30DD,PROG_NUM1 Repeat until B gets below zero. 30E3 PROG_NUM2 LD B,A 30E4 LD A,(#1DDA) Fetch current control port status. 30E7 AND #04 30E9 JR NZ,#30F0,PROG_NUM3 Jump if using single density. 30EB SLA B Otherwise double number of tens. 30ED SLA C Together with the next instruction the effect is 'INC C'. 30EF DEC C 30F0 PROG_NUM3 LD A,(IX+14) Fetch high byte of RPT. 30F3 ADD A,C Add adjusted sector. 30F4 ADD A,B Add adjusted track. 30F5 RET Exit with A holding the program number. THE 'SECT_END_Z' SUBROUTINE This subroutine returns with the Zero flag set if RPT has reached the sector end, that is if RPT points to the next track and sector numbers present in each sector. 30F6 SECT_END_Z CALL #37D4,RPT_HL1 Get RPT in HL and the disk buffer 30F9 LD A,C position in BC. 30FA CP 254 Exit if disk buffer position 254 (or 30FC RET NZ 510) hasn't been reached, Zero reset. 30FD LD A,(#1DDA) Fetch current control port status. 3100 CPL 3101 AND #04 Exit if single density is used with 3103 RET Z Zero set. 3104 LD A,B Otherwise position 510 has to be 3105 CP 1 reached before returning with Zero set. 3107 RET THE 'SAVE A BYTE TO DISK' SUBROUTINE This subroutine saves the byte in A in the data buffer at the location pointed to by RPT (the disk buffer pointer). If the buffer is full, an automatic sector save to disk will take place, RPT will be reset to the start of the buffer and the value will then be saved. 3108 SBYT PUSH BC 3109 PUSH DE 310A PUSH HL 310B PUSH AF 310C CALL #30F6,SECT_END_Z Check if the data buffer is full. 310F JR NZ,#311E,SBYT_1 Jump if data buffer not full. 3111 CALL #32DE,MK_ALLOC Allocate the first free sector. 3114 LD (HL),D Store it's track and sector number into 3115 INC HL the last two bytes of the data buffer. 3116 LD (HL),E 3117 EX DE,HL 3118 CALL #37FE,GET_SECTOR Fetch track and sector number of the current sector into DE, store the next track and sector number. 311B CALL #2F04,WSAD Write the sector to disk. 311E SBYT_1 POP AF 311F LD (HL),A Store value. 3120 POP HL 3121 POP DE 3122 POP BC 3123 JP #37DF,INC_RPT Exit while increasing RPT. THE 'LOAD A BYTE FROM DISK' SUBROUTINE This subroutine loads the byte pointed to by RPT from the data buffer, and returns with it in A and RPT updated. If the buffer is empty, another sector is read from the disk. NOTE: If there isn't another sector, so track and sector are both zero, the routine will try to load one anyway. The result is then that the 'SET_TRKSEC' routine crashes (see NOTE II before #2FAC). 3126 LBYT PUSH BC 3127 PUSH DE 3128 PUSH HL 3129 CALL #30F6,SECT_END_Z Check if the data buffer is empty. 312C JR NZ,#3134,LBYT_1 Jump if data buffer not empty. 312E LD D,(HL) Otherwise fetch track and sector number 312F INC HL of next sector into DE. 3130 LD E,(HL) 3131 CALL #2F4F,RSAD Load the next sector. 3134 LBYT_1 LD A,(HL) Get a byte. 3135 POP HL 3136 POP DE 3137 POP BC 3138 JP #37DF,INC_RPT Exit while increasing RPT. THE 'LOAD FILE' ROUTINE This very important routine handles the loading of any file from disk. The entry point is at address #3145. On entry HL holds the load address, while DE holds the number of bytes to be loaded. The routine first empties the data buffer, which was loaded with the first sector to obtain the 9 byte file header. When the data buffer is empty the routine loads all sectors, but the last, into the memory directly. The last sector is loaded into the data buffer again and then the remaining bytes are loaded from it. 313B LD_BUF LD A,(HL) Fetch a byte from the data buffer. 313C CALL #37DF,INC_RPT Increment RPT. 313F LD HL,(#1AC8) Fetch load address. 3142 LD (HL),A Load the byte into memory. 3143 INC HL 3144 DEC DE 3145 LOAD_FILE LD (#1AC8),HL Store load address into (FILEADDR). 3148 LD A,D 3149 OR E 314A RET Z Exit if no more bytes left. 314B LD_BUF1 CALL #30F6,SECT_END_Z The data buffer has to be empty before 314E JR NZ,#313B,LD_BUF sectors can be loaded directly into memory. Jump if data buffer not empty. 3150 LD (#1AC5),DE Store the number of bytes left to load into (BYTESLEFT). 3154 LD D,(HL) Fetch next track and sector. 3155 INC HL 3156 LD E,(HL) 3157 CALL #31DD,STO_BUFLEN Store the data buffer length. 315A LD_OP CALL #31EF,LAST_SEC_C Check if this sector is the last one. 315D JP C,#31D3,LD_LAST Jump if last sector. 3160 INC HL Balance the Carry flag subtracted in 'LAST_SEC_C'. 3161 LD (#1AC5),HL Store number of bytes left after this sector has been loaded. 3164 XOR A Clear retry counter. 3165 LD (#1DDB),A 3168 CALL #37F7,STORE_SEC Store track and sector. 316B LD_AGAIN CALL #2FAC,SET_TRKSEC Set drive, side, density, sector and track. 316E LD C,%10000000 Read a single sector, enable spin-up, no settling delay. 3170 CALL #3085,LD_COM_REG Execute the FDC command. 3173 EXX 3174 PUSH HL HL' has to be rescued because the 'main' ROM needs it. 3175 LD BC,219 I/O address of FDC's data register. 3178 LD DE,2 DE' holds the length of the next sector address in each sector. 317B CALL #37C3,HL_BUFFER HL' points to the data buffer. 317E EXX 317F LD BC,219 I/O address of FDC's data register. 3182 LD DE,(#1ACA) DE holds length of data buffer. DE + DE' hold the length of a sector. 3186 LD HL,(#1AC8) HL holds the load address. 3189 CALL #2ECE,STORE_IFF Store interrupt state and disable. 318C JR #3196,LD_TST_DRQ Jump into the load loop. 318E LD_LOOP INI Get a byte from the FDC, increment HL. 3190 DEC DE Decrement byte counter. 3191 LD A,D 3192 OR E 3193 JR NZ,#3196,LD_TST_DRQ Jump if not zero. 3195 EXX Otherwise select the other HL and DE. 3196 LD_TST_DRQ IN A,(27) Fetch FDC status. 3198 BIT 1,A Test Data ReQuest bit. 319A JR NZ,#318E,LD_LOOP Jump if FDC has read a byte. 319C IN A,(27) Otherwise fetch FDC status again. 319E BIT 1,A 31A0 JR NZ,#318E,LD_LOOP Jump if FDC has read a byte. 31A2 IN A,(27) 31A4 BIT 1,A 31A6 JR NZ,#318E,LD_LOOP 31A8 IN A,(27) 31AA BIT 1,A 31AC JR NZ,#318E,LD_LOOP 31AE BIT 0,A Test Busy bit. 31B0 JR NZ,#3196,LD_TST_DRQ Repeat until FDC is ready. 31B2 EXX When the FDC is ready, DE and DE' both 31B3 POP HL are 0, and the 'EXX' at #3195 has been 31B4 EXX executed twice, so to restore HL' a 'EXX' has to be executed first. 31B5 CALL #2EDA,REST_IFF Restore interrupt state. 31B8 AND %00011100 Mask non error bits of FDC status. 31BA JR Z,#31C7,LD_OK Jump with no errors. 31BC CALL #37F0,FETCH_SEC Otherwise fetch track and sector again. 31BF CALL #2F9F,SEC_ERR1 Check if there was a sector error. 31C2 CALL #2FF8,WRONGTRACK Check if head is above correct track. 31C5 JR #316B,LD_AGAIN Try to load the sector again. If there are no errors the next sector can be loaded. 31C7 LD_OK LD (#1AC8),HL Store the load address into (FILEADDR). 31CA CALL #37C3,HL_BUFFER Make HL point to the data buffer. 31CD LD D,(HL) Fetch the next track and sector number. 31CE INC HL 31CF LD E,(HL) 31D0 JP #315A,LD_OP Load the next sector. The last sector is loaded into the data buffer. 31D3 LD_LAST CALL #2F4F,RSAD Load the last sector. 31D6 LD DE,(#1AC5) Fetch number of bytes left (BYTESLEFT) 31DA JP #314B,LD_BUF1 and copy them to 'main' RAM. THE 'STORE BUFFER LENGTH' SUBROUTINE This subroutine stores the length of the data buffer into (#1ACA). For double density this is 510, for single density 254. 31DD STO_BUFLEN LD BC,510 Length of DD data buffer. 31E0 LD A,(#1DDA) 31E3 AND #04 31E5 JR Z,#31EA,STO_BUF1 Jump if using double density. 31E7 LD BC,254 Length of SD data buffer. 31EA STO_BUF1 LD (#1ACA),BC Store the length into (BUFLEN). 31EE RET THE 'LAST_SEC_C' SUBROUTINE This subroutine returns with the Carry flag set if the last sector is to be loaded. 31EF LAST_SEC_C LD HL,(#1AC5) Fetch the number of bytes left to be loaded from (BYTESLEFT). 31F2 LD BC,(#1ACA) Fetch the data buffer length from (BUFLEN). 31F6 SCF Set the Carry flag, now the Carry flag will be set after the 'SBC' if HL=BC. 31F7 SBC HL,BC Exit with Carry set signalling 'last 31F9 RET sector to be loaded'. THE 'SAVE FILE' ROUTINE This is the opposite of the 'LOAD_FILE' routine above. The entry address is #3204, on entry HL holds the save address and DE holds the number of bytes to be saved. The routine first fills up the data buffer, which contains the 9 byte file header already. The data buffer is saved to disk, after which a sector address table is build for all but the last sector. All sectors, the addresses of which are contained in the table, are saved directly from memory. The last sector is saved into the data buffer again after which the file should be closed. 31FA SA_BUF LD (HL),D Save the byte in the data buffer. 31FB CALL #37DF,INC_RPT Increment RPT. 31FE LD HL,(#1AC8) Fetch save address from (FILEADDR). 3201 INC HL 3202 POP DE 3203 DEC DE 3204 HSVBK_2 LD A,D 3205 OR E 3206 RET Z Exit if no more bytes to save. 3207 PUSH DE 3208 LD D,(HL) Fetch a byte from memory. 3209 LD (#1AC8),HL Store save address into (FILEADDR). 320C CALL #30F6,SECT_END_Z The data buffer has to be full before 320F JR NZ,#31FA,SA_BUF the sector can be saved. Jump if data buffer isn't full. 3211 POP DE Fetch number of bytes left to save and 3212 LD (#1AC5),DE store it into (BYTESLEFT). 3216 CALL #32DE,MK_ALLOC Allocate the first free sector. 3219 LD (HL),D Store track and sector number into the 321A INC HL data buffer. 321B LD (HL),E 321C EX DE,HL 321D CALL #37FE,GET_SECTOR Fetch track and sector number of the current sector in DE, store the next track and sector number. 3220 CALL #2F04,WSAD Write the sector to disk. 3223 XOR A Clear sector counter. 3224 LD (#1DEA),A 3227 CALL #31DD,STO_BUFLEN Store the data buffer length. 322A CALL #31EF,LAST_SEC_C Check if this is the last sector. 322D JP C,#32D1,SA_LAST Jump if it is. 3230 CALL #37C3,HL_BUFFER HL points to the data buffer. 3233 SA_ALLOC PUSH HL Store data buffer address. 3234 CALL #31EF,LAST_SEC_C Check if this is the last sector. 3237 PUSH HL DE now holds the number of bytes left 3238 POP DE -1. 3239 POP HL Restore data buffer pointer. 323A JR C,#3251,SA_OP Jump if all but last sector allocated. 323C INC DE Balance the Carry subtracted in 323D LD (#1AC5),DE 'LAST_SEC_C' before storing the number of bytes left into (BYTESLEFT). 3241 CALL #32DE,MK_ALLOC Allocate a sector. 3244 LD (HL),D Store its track and sector number into 3245 INC HL the data buffer. 3246 LD (HL),E 3247 INC HL 3248 LD A,(#1DEA) Increase sector counter. 324B INC A 324C LD (#1DEA),A 324F JR NZ,#3233,SA_ALLOC Repeat until all sectors have been allocated or the sector counter overflows. 3251 SA_OP XOR A Reset retry counter. 3252 LD (#1DDB),A 3255 CALL #37F0,FETCH_SEC Fetch the sector to be saved. 3258 SA_AGAIN CALL #2FAC,SET_TRKSEC Set drive, side, etc. 325B LD C,%10100010 Write a single sector, enable spin-up sequence, no settling delay, disable precompensation, normal data mark. 325D CALL #2EE7,PRECOMP Enable precompensation when neccesary and execute the command. 3260 EXX 3261 PUSH HL HL' has to be stored because the 'main' ROM needs it. 3262 CALL #37D4,RPT_HL1 HL' points to the sector address table, build up in the data buffer. 3265 LD DE,2 DE' holds the length of the next sector address in each sector. 3268 LD BC,219 BC' holds the I/O address of the FDC's data register. 326B EXX 326C LD HL,(#1AC8) HL holds the save address. 326F LD DE,(#1ACA) DE holds the length of the data space inside a sector. DE+DE' hold the length of a complete sector. 3273 LD BC,219 BC holds the same as BC'. 3276 CALL #2ECE,STORE_IFF Store interrupt state and disable. 3279 JR #3283,SA_TST_DRQ Jump into the save loop. 327B SA_LOOP OUTI Send a byte to the FDC, increment HL. 327D DEC DE Decrement byte counter. 327E LD A,D 327F OR E 3280 JR NZ,#3283,SA_TST_DRQ Jump if not zero. 3282 EXX Otherwise select the other HL and DE. 3283 SA_TST_DRQ IN A,(27) Fetch FDC status. 3285 BIT 1,A Test Data ReQuest bit. 3287 JR NZ,#327B,SA_LOOP Jump if FDC requests a byte. 3289 IN A,(27) Otherwise fetch FDC status again. 328B BIT 1,A 328D JR NZ,#327B,SA_LOOP Jump if FDC requests a byte. 328F IN A,(27) 3291 BIT 1,A 3293 JR NZ,#327B,SA_LOOP 3295 IN A,(27) 3297 BIT 1,A 3299 JR NZ,#327B,SA_LOOP 329B BIT 0,A Test Busy bit. 329D JR NZ,#3283,SA_TST_DRQ Repeat until FDC is ready. 329F CALL #2EDA,REST_IFF Restore interrupt state. 32A2 AND %00011100 Mask non error bits of FDC status. 32A4 JR Z,#32B4,SA_OK Jump with no errors. 32A6 EXX 32A7 POP HL Restore HL'. 32A8 EXX 32A9 CALL #37F0,FETCH_SEC Fetch track and sector again. 32AC CALL #2F9F,SEC_ERR1 Check if there was a sector error. 32AF CALL #2FF8,WRONGTRACK Check if head is above correct track. 32B2 JR #3258,SA_AGAIN Try to save the sector again. If there are no errors the next sector can be saved, but first its track and sector number have to be retrieved. 32B4 SA_OK LD (#1AC8),HL Store the save address into (FILEADDR). 32B7 EXX 32B8 DEC HL Fetch track and sector number of next 32B9 LD E,(HL) sector. 32BA DEC HL 32BB LD D,(HL) 32BC CALL #37DF,INC_RPT Update RPT. 32BF CALL #37DF,INC_RPT 32C2 CALL #37F7,STORE_SECT Store the next sector's track and sector number. 32C5 POP HL Restore HL'. 32C6 EXX 32C7 LD A,(#1DEA) Decrease sector counter. 32CA DEC A 32CB LD (#1DEA),A Save the next sector as long as it 32CE JP NZ,#3251,SA_OP isn't the last one. 32D1 SA_LAST CALL #37E7,RES_RPT The bytes of the last sector are saved 32D4 LD DE,(#1AC5) into the data buffer again. Fetch the 32D8 LD HL,(#1AC8) number of bytes left and the save 32DB JP #3204,HSVBK_2 address. Then save the bytes into the data buffer. THE 'ALLOCATE SECTOR' SUBROUTINE This subroutine allocates the first free sector, which track and sector number are returned in the DE register pair. The routine searches the disk bitmap at #1A00 for a free sector, if there isn't one an error is reported. 32DE MK_ALLOC PUSH HL 32DF PUSH BC 32E0 LD HL,#1A00 Address where disk bitmap is located. 32E3 LD DE,#0401 Start with track 4, sector 1. 32E6 LD C,0 Clear bitmap offset. 32E8 MK_ALLOC1 LD A,(HL) 32E9 CP #FF 32EB JR NZ,#32FF,MK_ALLOC3 Jump if there is a free sector here. 32ED LD A,E Otherwise update sector number. 32EE ADD A,8 Each byte holds 8 sectors. 32F0 LD E,A 32F1 SUB 10 But each track holds 10. 32F3 JR C,#32FB,MK_ALLOC2 Jump if still on the same track, i.e. 32F5 JR Z,#32FB,MK_ALLOC2 with sectors <=9 and 10. 32F7 LD E,A Otherwise the next sector has been 32F8 CALL #330F,NEXT_TRACK computed, next track is computed now. 32FB MK_ALLOC2 INC C Increase bitmap offset. 32FC INC HL Next byte of bitmap. 32FD JR #32E8,MK_ALLOC1 Find a free sector. Now the routine continues to find which sector is free. 32FF MK_ALLOC3 LD B,1 Reset bit pointer. 3301 MK_ALLOC4 LD A,(HL) 3302 AND B 3303 JR Z,#331E,MK_ALLOC5 Jump if free sector has been found. 3305 CALL #37BB,NEXT_SEC Increase sector number. 3308 CALL Z,#330F,NEXT_TRACK Next track if sector is on it. 330B RLC B Test next sector. 330D JR #3301,MK_ALLOC4 THE 'NEXT TRACK' SUBROUTINE This subroutine checks whether the next track (current track held in D) still exists and returns holding the next track in D when it does exist. If the drive capacity is exceeded, the 'Not enough SPACE on disc' error is given. 330F NEXT_TRACK INC D Increase track. 3310 CALL #333B,DRV_CAP Get number of tracks on current drive in the A register. 3313 CP D Give error if drive capacity is 3314 JP Z,#2950,REP_24 exceeded. 3317 AND #7F Mask off side bit. 3319 CP D 331A RET NZ Return if side 0 isn't full. 331B LD D,128 Otherwise return with track 0, side 1. 331D RET THE 'ALLOCATE SECTOR' ROUTINE CONTINUED Now the 'ALLOCATE SECTOR' routine continues by unfreeing the found sector. 331E MK_ALLOC5 LD A,(HL) Make found sector unfree in disk 331F OR B bitmap. 3320 LD (HL),A 3321 LD A,B 3322 LD B,0 3324 PUSH IX 3326 ADD IX,BC Add bitmap offset. 3328 OR (IX+34) Set new sector in file bitmap. 332B LD (IX+34),A 332E POP IX Restore disk channel pointer. 3330 INC (IX+31) Increment number of sectors used. 3333 JR NZ,#3338,MK_ALLOC6 3335 INC (IX+30) 3338 MK_ALLOC6 POP BC 3339 POP HL 333A RET Finished. THE 'GET DRIVE CAPACITY' SUBROUTINE This small subroutine returns with the A register holding the capacity of the selected drive, as found in the system variables. 333B DRV_CAP PUSH HL 333C LD HL,#0299 This is TRAKS1, drive 1's capacity. 333F LD A,(#1DDA) Fetch current control port state. 3342 BIT 0,A 3344 JR NZ,#3347,DRV_CAP1 Jump if drive 1 selected. 3346 INC HL Otherwise point to TRAKS2. 3347 DRV_CAP1 LD A,(HL) Fetch drive capacity. 3348 POP HL 3349 RET Finished. THE 'PRINT NAME' SUBROUTINE This subroutine is used to print the name of a file during a 'CAT' command and when the 'overwrite' message is printed. 334A PRT_NAME LD (IX+13),1 Point to the first character of the name. 334E CALL #37D4,RPT_HL1 Make HL point to it. 3351 LD B,10 A name has 10 characters. 3353 PRT_NAME1 LD A,(HL) Fetch a character. 3354 CALL #3C2C,PRT_A Print it. 3357 INC HL 3358 DJNZ #3353,PRT_NAME1 Repeat for all 10 characters. 335A RET THE 'SCAN CATALOGUE' SUBROUTINE This very important subroutine scans the CATalogue of a disk, whether this is for a free entry, a matching filename, or for printing the directory. On entry all needed parameters other than the A register should be contained in UFIA1. The A register determines where to scan for as follows: (bits set) - bit 0 : Search for the file with the specified program number. - bit 1 : Print a 'names only' CATalogue to the current stream. A filename must be specified. - bit 2 : Print an 'extended' CATalogue to the current stream. A filename has to be specified. - bit 3 : Search for a file with the specified type and name. - bit 4 : Search for a file with the specified filename. - bit 5 : Produce the disk bitmap. - bit 6 : Find the first unused entry. Note that some functions exclude others. A return is made with DE holding the track and sector number of the found entry, the data buffer holding the sector, RPT pointing to the entry and the Zero flag signalling 'success' when set. 335B SCAN_CAT LD IX,#1AC3 IX points to the disk channel. 335F LD (IX+4),A Store scan-type. 3362 XOR A Clear column counter. 3363 LD (#1DEB),A 3366 CALL #302C,TRACK_0 Reset drive head to track 0, DE = 1. 3369 EACH_ENTRY CALL #2F4F,RSAD Load a CATalogue sector. 336C EACH_E1 CALL #37D0,RPT_HL HL points to the start of data buffer. 336F LD A,(HL) Fetch file type. 3370 AND A Jump if it's an unused entry (could be 3371 JP Z,#3464,SCAN_FREE ERASEd). 3374 BIT 0,(IX+4) 3378 JR Z,#3386,NO_PRGNUM Jump if not searching for a filenumber. 337A CALL #30D6,PROG_NUM Otherwise load program number into A. 337D LD B,A 337E LD A,(#1E02) Fetch specified program number. 3381 CP B 3382 RET Z Exit if they are equal. 3383 JP #343E,SCAN_NEXT Otherwise continue scanning. NOTE: All entries with numbers below the specified one are considered, this isn't really needed. 3386 NO_PRGNUM BIT 1,(IX+4) Jump if a short CATalogue should be 338A JR NZ,#3392,PRINT_CAT printed. 338C BIT 2,(IX+4) 3390 JR Z,#340B,NO_CAT Jump if no CATalogue is desired. 3392 PRINT_CAT LD (IX+13),11 RPT points to number of sectors used. 3396 CALL #37D4,RPT_HL1 Make HL hold RPT. 3399 LD B,(HL) Fetch number of sectors used. 339A INC HL 339B LD C,(HL) 339C LD (#1AC3),BC Store it for printing. 33A0 LD HL,(#1DD8) Add it to total number of sectors used. 33A3 ADD HL,BC 33A4 LD (#1DD8),HL 33A7 BIT 7,A 33A9 JP NZ,#343E,SCAN_NEXT Jump if this entry is hidden. 33AC CALL #346D,MATCH_NAME 33AF JP NZ,#343E,SCAN_NEXT Jump if filename doesn't match. 33B2 BIT 1,(IX+4) 33B6 JR NZ,#33C8,SCAN_1 Jump with short CAT. 33B8 CALL #30D6,PROG_NUM Calculate program number. 33BB PUSH DE 33BC LD H,0 Program number to HL. 33BE LD L,A 33BF LD A,32 Use leading spaces. 33C1 CALL #3BEB,PRT_N10 Print the program number. 33C4 POP DE Restore sector address. 33C5 CALL #3C2A,PRT_SPACE Print a space. 33C8 SCAN_1 CALL #334A,PRT_NAME Print filename. 33CB BIT 1,(IX+4) 33CF JR Z,#33F5,EXT_CAT Jump with extended CAT. 33D1 LD B,3 Otherwise print three columns wide. 33D3 LD A,(#1E03) Except when using stream 3. 33D6 CP 3 33D8 JR NZ,#33DC,SCAN_2 33DA SLA B Then print six columns wide. 33DC SCAN_2 LD A,(#1DEB) Increment column counter. 33DF INC A 33E0 CP B 33E1 JR Z,#33EA,SCAN_3 Jump if last column reached. 33E3 LD (#1DEB),A Otherwise store column counter and 33E6 LD A,32 separate the columns with a SPACE. 33E8 JR #33F0,SCAN_4 This line is full, the next entry will be printed on the next line. 33EA SCAN_3 XOR A Clear column counter. 33EB LD (#1DEB),A 33EE LD A,13 Print a NEWLINE. 33F0 SCAN_4 CALL #3C2C,PRT_A 33F3 JR #343E,SCAN_NEXT Continue with the next entry. With an extended CAT there has to be printed somewhat more. 33F5 EXT_CAT PUSH DE Store track and sector number. 33F6 LD HL,(#1AC3) Fetch length of file in sectors. 33F9 LD A,32 Print it with leading spaces. 33FB CALL #3BE5,PRT_N100 33FE CALL #3C2A,PRT_SPACE Print a trailing space. 3401 CALL #37D0,RPT_HL HL points to the start of the entry. 3404 LD A,(HL) Fetch file type 3405 CALL #3B2E,PRT_TYPE and print it. 3408 POP DE Restore track and sector number. 3409 JR #343E,SCAN_NEXT Continue with the next entry. Now the routine continues with the search part. 340B NO_CAT BIT 3,(IX+4) 340F JR NZ,#3417,SCAN_NAME Jump if searching for name and type. 3411 BIT 4,(IX+4) 3415 JR Z,#3415,SCAN_5 Jump if not searching for name alone. 3417 SCAN_NAME CALL #346D,MATCH_NAME Return with Zero flag set to signal 341A RET Z 'matching name (and type) found'. 341B SCAN_5 BIT 5,(IX+4) 341F JR Z,#343E,SCAN_NEXT Jump if no disk map wanted. This part of the routine builds up the bitmap. 3421 PUSH IX 3423 LD (IX+13),15 RPT points to the start of file bitmap. 3427 CALL #37D4,RPT_HL1 Make HL hold RPT. 342A LD IX,#1A00 Start of disk bitmap. 342E LD B,195 There are 1560 bits in the bitmap. 3430 SCAN_MAP LD A,(IX+0) Fetch a disk map byte. 3433 OR (HL) Incorporate the corresponding file map 3434 LD (IX+0),A byte. 3437 INC IX Point to the next map bytes. 3439 INC HL 343A DJNZ #3430,SCAN_MAP Repeat for all map bytes. 343C POP IX Restore disk channel pointer. Another entry has been handled, go on with the next. 343E SCAN_NEXT LD A,(IX+14) Fetch RPT-hi. 3441 CP 1 3443 JR Z,#3455,SCAN_6 Jump if the second entry has been handled. 3445 LD A,(#1DDA) Fetch current control port state. 3448 AND #04 344A JR NZ,#3455,SCAN_6 Jump if using single density. 344C CALL #37E7,RES_RPT Reset RPT. 344F INC (IX+14) Point to the second entry. 3452 JP #336C,EACH_E1 Repeat for this entry. The next CAT sector has to be retrieved (if there is one). 3455 SCAN_6 CALL #37BB,NEXT_SEC Calculate next sector. 3458 JP NZ,#3369,EACH_ENTRY Jump if on same track. 345B INC D Otherwise next track. 345C LD A,D 345D CP 4 345F JP NZ,#3369,EACH_ENTRY Jump if still a CATalogue track. 3462 AND A Otherwise signal 'unsuccessfull' and 3463 RET exit. An unused entry was found, so if we are searching for one then exit else continue. 3464 SCAN_FREE LD A,(IX+4) Fetch scan-type. 3467 CPL Invert all bits. 3468 BIT 6,A 346A RET Z Return if searching for a free entry. 346B JR #343E,SCAN_NEXT Otherwise continue with next entry. THE 'MATCH NAME' SUBROUTINE This subroutine checks whether the filename and, when needed, directory description of the current entry matches the specification. If they don't match the Zero flag is returned reset. 346D MATCH_NAME PUSH IX Store disk channel pointer. 346F CALL #37D0,RPT_HL HL points to the start of the entry. 3472 LD B,11 Length of file description. 3474 BIT 3,(IX+4) 3478 LD IX,#1E05 IX points to the file description in UFIA1. 347C JR Z,#348E,MATCH_N2 Jump if directory description doesn't need to match. 347E MATCH_N1 LD A,(IX+0) Fetch character of search string. 3481 CP "*" Jump if it's a '*', then all other 3483 JR Z,#3493,MATCH_N3 characters don't matter. 3485 CP "?" Jump if it's a '?', then this character 3487 JR Z,#348E,MATCH_N2 doesn't matter. 3489 XOR (HL) Compare with entries character. 348A AND #DF Capitalize. 348C JR NZ,#3493,MATCH_N3 Jump if they don't match. 348E MATCH_N2 INC IX Next character. 3490 INC HL 3491 DJNZ #347E,MATCH_N1 Repeat for all characters. 3493 MATCH_N3 POP IX Restore disk channel pointer. 3495 RET Return with Zero set signalling 'match'. THE 'OPEN A FILE FOR SAVE' SUBROUTINE This subroutine opens a file, with the specified filename, for saving. If the filename wasn't used the file is opened, a return is made with the Zero flag set to signal 'successfull'. If the filename was used, the 'OVERWRITE' message is printed, when the 'Y' key is pressed the existing file is ERASEd and the opening is retried. Otherwise the routine returns with the Zero flag reset to signal 'unsuccessfull'. 3496 OFSM_2 PUSH IX Store disk channel pointer. 3498 LD A,(#1DEF) This is (MAPUSED), the (in)famous @6999. It holds the number of files which are using the disk bitmap. 349B CP 0 349D LD A,16 Scan catalogue for specified filename. 349F JR NZ,#34AD,OFSM_SCAN Jump if (@6999)<>0, the disk bitmap isn't to be rebuild. 34A1 LD HL,#1A00 Otherwise clear the disk bitmap. 34A4 LD B,195 34A6 OFSM_CLR LD (HL),0 34A8 INC HL 34A9 DJNZ #34A6,OFSM_CLR 34AB LD A,48 Scan catalogue for specified filename and produce a disk bitmap. 34AD OFSM_SCAN CALL #335B,SCAN_CAT 34B0 JR NZ,#34D9,OFSM_FREE Jump if filename not used. 34B2 PUSH DE Otherwise store sector address. 34B3 RST #10,CALBAS Clear the lower part of the screen by 34B4 DEFW #0D6E,CLS_LOWER calling 'CLS_LOWER' in 'main' ROM. 34B6 SET 5,(IY+2) Signal 'lower screen has to be cleared'. (TV_FLAG) 34BA CALL #3C8D,MESG_1 Print 'OVERWRITE' message. 34BD CALL #334A,PRT_NAME Print filename. 34C0 CALL #3CB2,MESG_3 Print 'Y/N' message. 34C3 CALL #3513,TEST_Y Test the 'Y' key. 34C6 JR Z,#34CC,OFSM_ERASE Jump if 'Y' was pressed. 34C8 POP DE When any other key was pressed the 34C9 POP IX routine returns with Zero reset to 34CB RET signal 'unsuccessfull'. The filename existed already, the user wants it to be overwritten, so ERASE it. 34CC OFSM_ERASE POP DE Restore track and sector number. 34CD CALL #37D0,RPT_HL Make HL point to the entry to be overwritten. 34D0 LD (HL),0 ERASE this file. 34D2 CALL #2F04,WSAD Write the entry back to disk. 34D5 POP IX Restore disk channel pointer. 34D7 JR #3496,OFSM_2 Retry opening the file. The filename wasn't used so now the file can be opened. 34D9 OFSM_FREE POP IX Restore disk channel pointer. 34DB PUSH IX 34DD LD B,0 Clear the file entry space in the disk 34DF OFSM_CLR1 LD (IX+19),0 channel. 34E3 INC IX 34E5 DJNZ #34DF,OFSM_CLR1 34E7 POP IX Restore disk channel pointer. 34E9 PUSH IX 34EB LD HL,#1E05 HL points to the file descriptor in UFIA1. 34EE LD B,11 Length of file descriptor. 34F0 OFSM_FDESC LD A,(HL) Copy file descriptor to the file entry 34F1 LD (IX+19),A space in the disk channel. 34F4 INC HL 34F5 INC IX 34F7 DJNZ #34F0,OFSM_FDESC 34F9 POP IX Restore disk channel pointer. 34FB CALL #32DE,MK_ALLOC Allocate a sector. 34FE CALL #37F7,STORE_SEC Store its track and sector number. 3501 LD (IX+32),D Store its sector address also into the 3504 LD (IX+33),E file entry space. 3507 CALL #37E7,RES_RPT Reset RPT. 350A LD A,(#1DEF) Increment (MAPUSED), there is one more 350D INC A file which uses the disk bitmap. 350E LD (#1DEF),A 3511 XOR A Return with Zero flag set to signal 3512 RET 'successfull'. THE 'TEST FOR YES' SUBROUTINE This subroutine tests whether the 'Y'-key is pressed, it returns with the Zero flag set if it was, reset otherwise. 3513 TEST_Y CALL #3528,DISC_BEEP Produce a 'middle C' for one second. 3516 TEST_Y1 RST #10,CALBAS The 'main' ROM is called to scan the 3517 DEFW #028E,KEY_SCAN keyboard. 3519 RST #10,CALBAS It is also called to determine if a key 351A DEFW #031E,KEY_TEST was pressed. 351C JR NC,#3516,TEST_Y1 Repeat scanning and testing until a key has been pressed. 351E AND #DF Capitalize. 3520 CP "Y" Set the Zero flag if it was the 3522 PUSH AF 'Y'-key. 3523 RST #10,CALBAS Again the 'main' ROM is called, this 3524 DEFW #0D6E,CLS_LOWER time for clearing the lower screen. 3526 POP AF Retrieve Zero flag. 3527 RET Finished. THE 'MAKE A BEEP' SUBROUTINE This subroutine produces a beep (the note 'middle C' in fact) for one second. 3528 DISC_BEEP PUSH HL 3529 PUSH DE 352A PUSH BC 352B PUSH IX 352D LD HL,1642 Parameters needed by 'BEEPER' to 3530 LD DE,261 produce a 'middle C'. 3533 RST #10,CALBAS Produce the note. 3534 DEFW #03B5,BEEPER 3536 POP IX 3538 POP BC 3539 POP DE 353A POP HL 353B RET THE 'CLOSE A SAVE FILE' SUBROUTINE This subroutine closes a save file, by writing the last sector (contained in the data buffer) to disk and by writing the CATalogue entry. 353C CFSM CALL #37D4,RPT_HL1 HL points to the first unused position in the data buffer. 353F LD A,C C holds buffer offset-lo. 3540 AND A 3541 JR NZ,#354F,CFSM_FILL Jump if buffer isn't full yet. 3543 LD A,(#1DDA) Fetch current control port state. 3546 AND #04 3548 JR NZ,#3556,CFSM_SAVE Jump if using single density. 354A LD A,B B holds buffer offset-hi. 354B CP 2 354D JR Z,#3556,CFSM_SAVE Jump if buffer is full. 354F CFSM_FILL LD (HL),0 Otherwise fill up buffer with zero's. 3551 CALL #37DF,INC_RPT Increment RPT. 3554 JR #353C,CFSM And close the file. The last sector is ready to be saved, it has been filled up with zero's if needed. 3556 CFSM_SAVE CALL #37F0,FETCH_SEC Fetch last sector's track and sector 3559 CALL #2F04,WSAD number and save it to disk. 355C LD A,(#1DEF) Decrease (MAPUSED), the number of files 355F DEC A using the disk bitmap. 3560 LD (#1DEF),A 3563 PUSH IX Store disk channel pointer. 3565 LD A,64 Search the CATalogue for an unused 3567 CALL #335B,SCAN_CAT entry. 356A JP NZ,#2952,REP_25 If none found report 'Directory FULL'. 356D CALL #37D0,RPT_HL HL points to the entry. 3570 LD (#1ACA),IX Store disk channel pointer 2, this one points to the DFCA. 3574 POP IX Restore disk channel pointer 1, this one can point to 'main' RAM (OPENTYPE). 3576 PUSH IX Store it again. 3578 LD B,0 Copy the file entry to the CAT entry in 357A CFSM_ENTRY LD A,(IX+19) the data buffer. 357D LD (HL),A 357E INC IX 3580 INC HL 3581 DJNZ #357A,CFSM_ENTRY 3583 LD IX,(#1ACA) Restore disk channel pointer 2. 3587 CALL #2F04,WSAD Write the sector to disk. 358A POP IX Restore disk channel pointer 1. 358C RET Finished. THE 'OPEN A FILE FOR LOAD' SUBROUTINE This subroutine opens a file, with the specified filename, for loading. If the filename isn't found an error is given. If the filename is found it is opened and the first sector is loaded into the data buffer. 358D HGFLE_2 LD A,(#1E04) Fetch device description from UFIA1. 3590 AND #DF Capitalize. 3592 CP "P" 3594 JR NZ,#35BA,HGFL_NONUM Jump if no program number specified. 3596 LD A,1 Search for the specified program 3598 CALL #335B,SCAN_CAT number. 359B JP NZ,#2954,REP_26 If file isn't found report 'File NOT FOUND'. 359E CALL #37D0,RPT_HL HL points to the entry. 35A1 LD DE,#1E05 Copy the 11 byte file descriptor to 35A4 LD BC,11 UFIA1. 35A7 LDIR 35A9 LD (IX+13),211 RPT points to the file header of the file. 35AD CALL #37D4,RPT_HL1 Make HL point to it. 35B0 LD DE,#1E10 Copy the 9 byte file header to UFIA1. 35B3 LD BC,9 35B6 LDIR 35B8 JR #35C2,LOAD_1ST Jump forward to load the first sector. Now search for the file with the given name. 35BA HGFL_NONUM LD A,16 Search for the specified filename. 35BC CALL #335B,SCAN_CAT If file isn't found report 'File NOT 35BF JP NZ,#2954,REP_26 FOUND'. Otherwise the first sector is loaded by entering the 'LOAD FIRST SECTOR' subroutine below. THE 'LOAD FIRST SECTOR' SUBROUTINE When the CAT entry of the file to be loaded is found, this routine can be used to fetch the first sector of the file. The first sector holds the 9 byte file header (with certain filetypes) which should be identical to the 9 byte file header present in the CAT entry. 35C2 LOAD_1ST CALL #37D0,RPT_HL HL points to the entry. 35C5 LD DE,#1E1E Copy the 11 byte file descriptor to 35C8 LD BC,11 UFIA2. 35CB LDIR 35CD LD (IX+13),220 RPT points to the SNAPSHOT registers, that is when they are present. 35D1 CALL #37D4,RPT_HL1 Make HL point to it. 35D4 LD DE,#1FEA Copy the 22 SNAPSHOT values to the 35D7 LD BC,22 internal stack bottom. 35DA LDIR 35DC LD (IX+13),13 RPT points to track and sector number. 35E0 CALL #37D4,RPT_HL1 Now HL points to it also. 35E3 LD D,(HL) Fetch track and sector number. 35E4 INC HL 35E5 LD E,(HL) 35E6 JP #2F4F,RSAD And exit while loading the first sector. THE 'FORMAT A DISK' ROUTINE This routine formats a disk by writing one track at a time to disk. The track is first build up in 'main' RAM. After the formatting is completed, the other disk is completely copied (cloned) or the disk is checked for bad sectors. 35E9 FRMT_RUN CALL #3030,REST Reset drive head, DE = 1. 35EC LD IX,#1AC3 IX points to the DFCA. 35F0 FRMT_TRACK LD A,(#1DDA) Fetch current control port state. 35F3 AND #04 35F5 JR Z,#35FC,FRMT_DD Jump if using double density. 35F7 CALL #3744,MK_TRK_SD Build up a single density track in 35FA JR #35FF,FRMT_ING 'main' memory and start formatting. 35FC FRMT_DD CALL #36C0,MK_TRK_DD Build up a double density track. 35FF FRMT_ING CALL #3099,TEST_DRV See if the drive is defined. 3602 LD C,%11111000 Write track, disable spin-up sequence, no delay, enable precompensation. 3604 CALL #2EE7,PRECOMP Why call this routine ? The precomp. has already been enabled. 3607 LD HL,49152 HL points to the track build up in 'main' memory. 360A CALL #2F1F,WR_OP Write the track. 360D INC D Next track. 360E CALL #333B,DRV_CAP Get drive capacity in A. 3611 CP D 3612 JR Z,#3639,FRMT_DONE Jump if all tracks have been formatted. 3614 AND #7F Mask off side. 3616 CP D 3617 JR Z,#362E,FRMT_SIDE1 Jump if side1 hasn't been formatted. 3619 LD C,%01011000 Step-in, update track register, disable spinup, no verify, step rate 6 msec. 361B CALL #3085,LD_COM_REG Execute the command. 361E CALL #2FE6,STEP_DELAY Wait for the step delay. The following code determines the skew, i.e. the shifting between the sectors of a track and the previous track. The DISCiPLE uses a skew of +2, so sector 1 on track T lies adjacent to sector 9 on track T+1. 3621 DEC E 3622 JR NZ,#3626,FRMT_1 Jump if sector >= 1. 3624 LD E,10 Sector numbers have range 1..10. 3626 FRMT_1 DEC E 3627 JR NZ,#362B,FRMT_2 Jump if sector >= 1. 3629 LD E,10 This instruction is never reached ?? 362B FRMT_2 JP #35F0,FRMT_TRACK Format the next track. If a double sided drive is used, the formatting continues on track 0 side 1. 362E FRMT_SIDE1 CALL #3030,REST Reset drive head. 3631 LD D,128 Track 0, side 1. 3633 CALL #30B3,SET_DRVSD Set drive, side, density, etc. 3636 JP #35F0,FRMT_TRACK Continue formatting. When the formatting of all tracks is completed, the routine checks whether it is supposed to copy another disk to this one, or to verify the disk. 3639 FRMT_DONE CALL #302C,TRACK_0 Reset drive head. 363C LD A,(#1E1A) 363F CP 255 3641 JR Z,#3692,FRMT_CHECK Jump if UFIA2 is empty. When the disk in the other drive is to be cloned, it will be copied a track at a time. 3643 FRMT_COPY LD HL,49152 Address of track buffer. 3646 LD (#1AC5),HL Store load address. 3649 LD (#1AC8),HL Store save address. 364C LD A,(#1E1A) Fetch source drive number from UFIA2. 364F CALL #309C,TEST_DRV1 Check and set drive. 3652 FRMT_READ CALL #2F4F,RSAD Load a sector. 3655 PUSH DE Store track and sector number. 3656 LD HL,#1BD6 DRAM sector buffer address. 3659 LD DE,(#1AC5) Fetch load address. 365D CALL #36B3,GET_SECLEN BC holds sector length. 3660 LDIR Copy the contents of the buffer to 'main' RAM. 3662 LD (#1AC5),DE Store new load address. 3666 POP DE Restore track and sector number. 3667 CALL #37BB,NEXT_SEC Compute next sector number. 366A JR NZ,#3652,FRMT_READ Jump if there is still a sector on this track. 366C LD A,(#1E01) Fetch destination drive from UFIA1. 366F CALL #309C,TEST_DRV1 Check and set drive. 3672 FRMT_WRITE PUSH DE Store track and sector number. 3673 LD HL,(#1AC8) Fetch save address. 3676 LD DE,#1BD6 DRAM sector buffer address. 3679 CALL #36B3,GET_SECLEN BC holds sector length. 367C LDIR Copy a sector to the sector buffer. 367E LD (#1AC8),HL Store new save address. 3681 POP DE Restore track and sector number. 3682 CALL #2F04,WSAD Save the sector. 3685 CALL #37BB,NEXT_SEC Compute next sector number. 3688 JR NZ,#3672,FRMT_WRITE Jump if not all sectors on this track have been written. 368A CALL #36A2,NXT_TRK Compute next track number. 368D JR NZ,#3643,FRMT_COPY Jump if not all tracks have been copied. 368F JP #302C,TRACK_0 Otherwise exit via 'TRACK_0'. The routine now verifies if all sectors are readable. An error is given if a sector can't be read, it would have been more usefull if a 'badsectors' file was saved. 3692 FRMT_CHECK CALL #2F4F,RSAD Load a sector. 3695 CALL #37BB,NEXT_SEC Compute next sector number. 3698 JR NZ,#3692,FRMT_CHECK Jump if not all sectors on one track have been loaded. 369A CALL #36A2,NXT_TRK Compute next track number. 369D JR NZ,#3692,FRMT_CHECK Jump if not all tracks on the disk have been verified. 369F JP #302C,TRACK_0 Exit via 'TRACK_0'. THE 'NXT_TRK' SUBROUTINE This subroutine is almost the same as the 'NEXT_TRACK' subroutine at #330F. But the differences are essential. No error is given when a non existent track is reached and the Zero flag is used to signal 'no more tracks on this side' when set. 36A2 NXT_TRK INC D Increase track number. 36A3 CALL #333B,DRV_CAP Get drive capacity in A. 36A6 CP D 36A7 RET Z Return with Zero set if last track reached. 36A8 AND #7F Mask off side bit. 36AA CP D 36AB RET NZ Return with Zero reset if last track on side0 hasn't been reached. 36AC CALL #302C,TRACK_0 Reset drive head. 36AF LD D,128 Track 0, side 1. 36B1 CP D Reset Zero flag. 36B2 RET THE 'GET SECTOR LENGTH' SUBROUTINE This subroutine returns with BC holding the sector length in bytes. With double density this is 512, with single density 256. 36B3 GET_SECLEN LD BC,512 Length of a DD sector. 36B6 LD A,(#1DDA) 36B9 AND #04 36BB RET Z Return if using double density. 36BC LD BC,256 Length of a SD sector. 36BF RET THE 'BUILD UP A DD TRACK' SUBROUTINE This subroutine builds up a double density track in the 'main' memory starting at address 49152. 36C0 MK_TRK_DD LD HL,49152 Start of track buffer. 36C3 LD BC,#3C4E Store 60 bytes #4E. GAP I. 36C6 CALL #37B6,B_TIMES_C 36C9 LD B,10 Number of sectors on a track. 36CB MK_TRK_DD1 PUSH BC 36CC LD BC,#0C00 12 bytes #00. Last part of GAP III. 36CF CALL #37B6,B_TIMES_C 36D2 LD BC,#03F5 3 bytes #F5 (written as #A1). 36D5 CALL #37B6,B_TIMES_C 36D8 LD BC,#01FE 1 byte #FE (ID field ID). 36DB CALL #37B6,B_TIMES_C 36DE LD A,D Fetch track number. 36DF AND #7F Mask side bit. 36E1 LD C,A 36E2 LD B,1 1 byte track number. 36E4 CALL #37B6,B_TIMES_C 36E7 LD A,D Fetch track again. 36E8 AND #80 Keep only side bit. 36EA RLCA Rotate it to bit 0. 36EB LD C,A 36EC LD B,1 1 byte side number. 36EE CALL #37B6,B_TIMES_C 36F1 LD C,E Fetch sector number. 36F2 CALL #37BB,NEXT_SEC Increment sector number. 36F5 LD B,1 1 byte sector number. 36F7 CALL #37B6,B_TIMES_C 36FA LD BC,#0102 1 byte #02 (sector length = 512). 36FD CALL #37B6,B_TIMES_C 3700 LD BC,#01F7 1 byte #F7 (two CRC bytes written). 3703 CALL #37B6,B_TIMES_C 3706 LD BC,#164E 22 bytes #4E. GAP II. 3709 CALL #37B6,B_TIMES_C 370C LD BC,#0C00 12 bytes #00. 370F CALL #37B6,B_TIMES_C 3712 LD BC,#03F5 3 bytes #F5 (written as #A1). 3715 CALL #37B6,B_TIMES_C 3718 LD BC,#01FB 1 byte #FB (data field ID). 371B CALL #37B6,B_TIMES_C 371E LD BC,#0000 512 bytes #00. Data bytes. 3721 CALL #37B6,B_TIMES_C 3724 CALL #37B6,B_TIMES_C 3727 LD BC,#01F7 1 byte #F7 (two CRC bytes written). 372A CALL #37B6,B_TIMES_C 372D LD BC,#184E 24 bytes #4E. First part of GAP III. 3730 CALL #37B6,B_TIMES_C 3733 POP BC Retrieve sector counter. 3734 DEC B Repeat until all 10 sectors have been 3735 JP NZ,#36CB,MK_TRK_DD1 build up. 3738 LD BC,#004E 768 bytes #00. GAP IV. 373B CALL #37B6,B_TIMES_C 373E CALL #37B6,B_TIMES_C 3741 JP #37B6,B_TIMES_C THE 'BUILD UP A SD TRACK' SUBROUTINE This subroutine builds up a single density track in the 'main' memory starting at address 49152. 3744 MK_TRK_SD LD HL,49152 Start of track buffer. 3747 LD BC,#28FF Store 40 bytes #FF. GAP I. 374A CALL #37B6,B_TIMES_C 374D LD B,10 There are 10 sectors on a track. 374F MK_TRK_SD1 PUSH BC 3750 LD BC,#0600 6 bytes #00. Last part of GAP III. 3753 CALL #37B6,B_TIMES_C 3756 LD BC,#01FE 1 byte #FE (ID field ID). 3759 CALL #37B6,B_TIMES_C 375C LD A,D Fetch track number. 375D AND #7F Drop side bit. 375F LD C,A 3760 LD B,1 1 byte track number. 3762 CALL #37B6,B_TIMES_C 3765 LD A,D Fetch track number again. 3766 AND #80 Keep only side bit. 3768 RLCA Rotate it to bit 0. 3769 LD C,A 376A LD B,1 1 byte side number. 376C CALL #37B6,B_TIMES_C 376F LD C,E Fetch sector number. 3770 CALL #37BB,NEXT_SEC Compute next sector. 3773 LD B,1 1 byte sector number. 3775 CALL #37B6,B_TIMES_C 3778 LD BC,#0101 1 byte #01 (sector length = 256). 377B CALL #37B6,B_TIMES_C 377E LD BC,#01F7 1 byte #F7 (two CRC bytes written). 3781 CALL #37B6,B_TIMES_C 3784 LD BC,#0BFF 11 bytes #FF. GAP II. 3787 CALL #37B6,B_TIMES_C 378A LD BC,#0600 6 bytes #00. 378D CALL #37B6,B_TIMES_C 3790 LD BC,#01FB 1 byte #FB (data field ID). 3793 CALL #37B6,B_TIMES_C 3796 LD BC,#0000 256 bytes #00. Data bytes. 3799 CALL #37B6,B_TIMES_C 379C LD BC,#01F7 1 byte #F7 (two CRC bytes written). 379F CALL #37B6,B_TIMES_C 37A2 LD BC,#0AFF 10 bytes #FF. First part of GAP III. 37A5 CALL #37B6,B_TIMES_C 37A8 POP BC Retrieve sector counter. 37A9 DEC B Repeat until all 10 sectors have been 37AA JP NZ,#374FMK_TRK_SD1 build up. 37AD LD BC,#00FF 512 bytes #FF. Gap IV. 37B0 CALL #37B6,B_TIMES_C 37B3 JP #37B6,B_TIMES_C THE 'STORE B TIMES BYTE C' SUBROUTINE This subroutine is used in the construction of a track in memory, it stores the byte held in the C register, B times. 37B6 B_TIMES_C LD (HL),C Store C. 37B7 INC HL Next address. 37B8 DJNZ #37B6,B_TIMES_C Repeat until B=0. 37BA RET THE 'NEXT SECTOR' SUBROUTINE This subroutine computes the next sector number in E, it returns with the Zero flag set indicating 'next track'. 37BB NEXT_SEC INC E Increment sector number. 37BC LD A,E 37BD CP 11 37BF RET NZ Return with Zero reset signalling 'same track'. 37C0 LD E,1 Otherwise start with sector 1 again. 37C2 RET Return with Zero set signalling 'next track'. THE 'MAKE HL POINT TO BUFFER' SUBROUTINE This subroutine returns with HL holding the start of the data buffer. On entry IX must point to the start of the disk channel. 37C3 HL_BUFFER PUSH BC 37C4 PUSH IX Disk channel pointer to BC. 37C6 POP BC 37C7 LD L,(IX+15) Fetch data buffer offset. 37CA LD H,(IX+16) 37CD ADD HL,BC HL now points to the data buffer. 37CE POP BC 37CF RET THE 'FETCH RPT INTO HL' SUBROUTINE This subroutine returns with HL holding the RAM PoinTer, which points to the next data byte in the data buffer. When entering at #37D0, RPT-lo is first reset. 37D0 RPT_HL LD (IX+13),0 Reset RPT-lo. 37D4 RPT_HL1 CALL #37C3,HL_BUFFER HL points to the data buffer. 37D7 LD B,(IX+14) Fetch RPT offset into BC. 37DA LD C,(IX+13) 37DD ADD HL,BC Add the offset to the start of the data buffer. 37DE RET Finished. THE 'INCREMENT RPT' SUBROUTINE This small subroutine increments the RAM PoinTer offset. 37DF INC_RPT INC (IX+13) Increment RPT-lo. 37E2 RET NZ 37E3 INC (IX+14) Increment RPT-hi when necessary. 37E6 RET THE 'RESET RPT' SUBROUTINE This small subroutine resets the RAM PoinTer offset. 37E7 RES_RPT LD (IX+13),0 Clear RPT offset. 37EB LD (IX+14),0 37EF RET THE 'FETCH SECTOR ADDRESS' SUBROUTINE This subroutine returns with DE holding the stored track and sector number. 37F0 FETCH_SEC LD D,(IX+18) 37F3 LD E,(IX+17) 37F6 RET THE 'STORE SECTOR ADDRESS' SUBROUTINE This subroutine stores the track and sector number held in DE into the disk channel. 37F7 STORE_SEC LD (IX+18),D 37FA LD (IX+17),E 37FD RET THE 'GET SECTOR ADDRESS' SUBROUTINE This subroutine returns with DE holding the stored track and sector number and the track and sector number held in HL stored into the disk channel. 37FE GET_SECTOR CALL #37F0,FETCH_SEC Fetch the stored track and sector 3801 LD (IX+18),H number. And store the track and sector 3804 LD (IX+17),L number held in HL. 3807 RET The Network routines THE '"N" CHANNEL INPUT' ROUTINE This routine handles the input of a single byte. When 'EOF' occurs, an error is given, in case of an other error retries will be made. 3808 N_INPUT PUSH BC Save registers before getting a byte. 3809 PUSH DE 380A PUSH HL 380B NCI_RETRY CALL #3819,NCHAN_IN 380E JR C,#3815,NCI_ACCEPT Jump if byte acceptable. 3810 JR Z,#380B,NCI_RETRY Try again if no byte has been read. 3812 JP #2956,REP_27 Give error if end of file. 3815 NCI_ACCEPT POP HL Restore registers and exit. 3816 POP DE 3817 POP BC 3818 RET THE '"N" CHANNEL INPUT' ROUTINE The actual "n" input involves the reading of the byte from the network buffer. If no other bytes are found in the buffer, a further data block is to be received (provided that the 'current' block is not the 'EOF' one) before reading the byte. 3819 NCHAN_IN LD IX,#1E3D Fetch start of "n" channel. 381D LD A,(IX+16) 3820 AND A 3821 JP NZ,#2944,REP_18 Jump if it isn't a "read" file. 3824 TEST_BUFF LD A,(IX+20) Fetch number of bytes in input buffer. 3827 AND A 3828 JR Z,#383E,TEST_N_EOF Jump if buffer is empty. 382A LD E,(IX+19) Otherwise fetch the current position. 382D DEC A Decrease number of bytes to be read. 382E SUB E 382F JR C,#383E,TEST_N_EOF Jump if all bytes have been read. 3831 LD D,0 Clear D. 3833 INC E 3834 LD (IX+19),E Update byte position. 3837 ADD IX,DE Point to 'byte to be read - 20'. 3839 LD A,(IX+20) Read the byte. 383C SCF Signal 'acceptable byte'. 383D RET If no data is found in the buffer, consider whether this is the last data block. 383E TEST_N_EOF LD A,(IX+15) Fetch packet type. 3841 AND A Jump if it is a normal packet (packet 3842 JR Z,#3845,GET_N_BUF type of EOF = 1). 3844 RET Otherwise return with both Zero and Carry flags reset to signal 'EOF'. A further data block is to be received if the current data block is not the 'EOF' one. 3845 GET_N_BUF CALL #3AF1,NET_BORD Set border colour, store IFF and DI. 3848 TRY_AGAIN CALL #3A09,WT_SCOUT Wait for a 'scout' leader. 384B JR NC,#3861,TIME_OUT Jump if 'time out' occurs. 384D CALL #391E,GET_NBLK Get header and data block. 3850 JR NZ,#3861,TIME_OUT Jump with any error. 3852 CALL #3B02,REST_BORD Restore border and interrupt status. 3855 LD (IX+19),#00 Reset byte position. 3859 LD A,(#1DF9) 385C LD (IX+15),A Store packet type. 385F JR #3824,TEST_BUFF Now try to fetch the byte. Something went wrong, ignore it when broadcasting. 3861 TIME_OUT LD A,(IX+11) Fetch destination station number. 3864 AND A Jump if broadcasting, then there is no 3865 JR Z,#3848,TRY_AGAIN 'time out'. 3867 CALL #3B02,REST_BORD Restore border and interrupt status. 386A AND 0 Return with Zero flag set and Carry 386C RET reset, to signal 'no data read'. THE '"N" CHANNEL OUTPUT' ROUTINE This routine handles the output of a single byte. 386D N_OUTPUT PUSH BC Save registers before outputting the 386E PUSH DE byte. 386F PUSH HL 3870 CALL #3877,NCHAN_OUT 3873 POP HL Restore registers and exit. 3874 POP DE 3875 POP BC 3876 RET THE '"N" CHANNEL OUTPUT' ROUTINE The routine that handles the "n" channel output is quite straightforward. It involves the storing of the byte held in the A register into the 255-byte buffer; whenever this is filled, the subroutine 'S_PACK_1' is called, so the data block is sent over the network. 3877 NCHAN_OUT LD IX,#1E3D Point to start of channel. 387B LD B,A 387C LD A,(IX+20) 387F AND A 3880 LD A,B 3881 JP NZ,#2946,REP_19 Give error if it is a 'read' channel. 3884 LD E,(IX+16) Fetch position of previous byte. 3887 INC E Include current byte. 3888 JR NZ,#3892,ST_BUF_POS Jump if the buffer isn't full. 388A PUSH AF 388B XOR A Signal normal packet type. 388C CALL #38B5,S_PACK_1 Send the packet. 388F POP AF 3890 LD E,1 Only 1 byte in buffer now. 3892 ST_BUF_POS LD (IX+16),E Store byte position. 3895 LD D,0 3897 ADD IX,DE Point to 'first free byte - 20'. 3899 LD (IX+20),A Store the byte. 389C RET THE 'OUT_BLK_N' SUBROUTINE This subroutine calls the 'OUTPAK' subroutine to send over the network the block of bytes starting from (HL), and whose length is held in the E register. Then a 'response byte' is to be received (provided there isn't broadcasted). The Zero flag is returned reset if no response byte has been received. This subroutine is called from 'SEND_PACK' below to send the header and then the data block over the network. 389D OUT_BLK_N CALL #3ABC,OUTPAK Send the block. 38A0 LD A,(IX+11) 38A3 AND A 38A4 RET Z Return if broadcasting. 38A5 LD HL,#1DF4 Address of store for network response 38A8 LD (HL),0 code. 38AA LD E,1 A single byte is to be received. 38AC CALL #3A87,INPAK Get response byte. 38AF RET NZ Return if the network was inactive. 38B0 LD A,(#1DF4) 38B3 DEC A The response byte must be 1. 38B4 RET Return with NZ if it wasn't. THE 'S_PACK_1' SUBROUTINE This subroutine simply calls 'SEND_PACK' below. If broadcasting, a certain delay is to be inserted after having sent the packet. 38B5 S_PACK_1 CALL #38BC,SEND_PACK Send the packet. 38B8 RET NZ Return if not broadcasting. 38B9 JP #3915,BR_DELAY Otherwise return via the delay routine. THE 'SEND_PACK' SUBROUTINE This subroutine sends over the network a SCOUT leader, followed by the header and the data block for the "n" channel. On entry, the A register may hold 1 or 0 depending on whether the block is the 'end of file' one or not. The block number (IX+13,14) is incremented before returning. The Zero flag is returned set if 'broadcasting' is being used. 38BC SEND_PACK LD (IX+15),A Store packet type, i.e. #00 for normal, #01 for end of file. 38BF LD B,(IX+16) Fetch block length. 38C2 CALL #3AF1,NET_BORD Set border, store IFF and DI. 38C5 PUSH IX Make DE point to the start of the 38C7 POP DE "n" channel. 38C8 LD HL,21 Offset to the 255 byte buffer. 38CB ADD HL,DE Now the checksum of the data block is calculated. 38CC XOR A Clear checksum. 38CD CHKS1 ADD A,(HL) Add this byte. 38CE INC HL Next byte. 38CF DJNZ #38CD,CHKS1 Repeat for all bytes in the block. 38D1 LD (IX+17),A Store the obtained checksum. 38D4 LD HL,11 Now point to the location where the 38D7 ADD HL,DE destination station number is held. 38D8 PUSH HL The checksum of the header is calculated. 38D9 LD B,7 Checksum is for 7 bytes. 38DB XOR A Clear checksum. 38DC CHKS2 ADD A,(HL) Add this byte. 38DD INC HL Next byte. 38DE DJNZ #38DC,CHKS2 Repeat for all header bytes. 38E0 LD (HL),A Store the checksum. 38E1 SENDSCOUT CALL #3A4A,SEND_SC Send the SCOUT i.e. a leader followed by the station number. 38E4 POP HL Restore pointer to start of header 38E5 PUSH HL block. 38E6 LD E,8 The header consists of '8' bytes. 38E8 CALL #389D,OUT_BLK_N The header is sent, and the response code received. 38EB JR NZ,#38E1,SENDSCOUT Repeat until successfull. 38ED PUSH IX Make HL point to the start of the 38EF POP HL "n" channel. 38F0 LD DE,21 38F3 ADD HL,DE Point to the start of the data block. 38F4 LD E,(IX+16) Get block length. 38F7 LD A,E 38F8 AND A If the buffer is empty; no data needs 38F9 JR Z,#3904,INC_BLKN to be sent. 38FB LD B,32 Wait 418 T cycles before proceeding. 38FD SP_DL_1 DJNZ #38FD,SP_DL_1 Now the data block is sent and the block number incremented. 38FF CALL #389D,OUT_BLK_N Send the data block, receive response. 3902 JR NZ,#38E1,SENDSCOUT Repeat until successfull. 3904 INC_BLKN INC (IX+13) Increment low byte of block number. 3907 JR NZ,#390C,SP_N_END The high byte of block number is only 3909 INC (IX+14) incremented when necessary. 390C SP_N_END POP HL Restore pointer to start of header. 390D CALL #3B02,REST_BORD Restore border and interrupt status. 3910 LD A,(IX+11) Get destination station number. 3913 AND A 3914 RET Return with Zero set if broadcasting. THE 'BR_DELAY' SUBROUTINE This short subroutine inserts a delay of about 40 msec. when it is called. Its task is that of separating outputs during broadcast transmissions. 3915 BR_DELAY LD DE,5376 This costs 139791 T states including 3918 DL_LOOP DEC DE the RET instruction. The 3.5 MHz Z80 in 3919 LD A,E the Spectrum takes 39.94 msec to 391A OR D complete this subroutine. 391B JR NZ,#3918,DL_LOOP 391D RET THE 'GET A HEADER & DATA BLOCK' ROUTINES The following two subroutines are used to get from the network an 8-byte header and a data block respectively. Both require that the IX register points to the start of the "n" channel. The Zero flag is returned reset with any error. 391E GET_NBLK LD HL,#1DF5 Point to the first byte of the header. 3921 LD E,8 A header is 8 bytes long. 3923 CALL #3A87,INPAK Receive the header. 3926 RET NZ Return when no header received. 3927 LD HL,#1DF5 Restore pointer. Now the header checksum is calculated, and compared with the received checksum. 392A XOR A First clear the checksum. 392B LD B,7 Length of header (without checksum). 392D CHKS3 ADD A,(HL) Add this byte. 392E INC HL Next byte. 392F DJNZ #392D,CHKS3 Repeat for all bytes. 3931 CP (HL) Compare with received checksum. 3932 RET NZ Return if they don't match. Now some tests are being made: Is the block for this Spectrum, is it from the right Spectrum and is it the right block? 3933 LD A,(#1DF5) Fetch destination station number. 3936 AND A 3937 JR Z,#3936,BRCAST Jump if broadcasting. 3939 CP (IX+12) Otherwise compare with own station 393C RET NZ number, return if for another Spectrum. 393D LD A,(#1DF6) Source station number. 3940 CP (IX+11) Return if the transmitting Spectrum is 3943 RET NZ not the required one. 3944 JR #394B,TEST_BLKN 3946 BRCAST LD A,(IX+11) 3949 OR A Return if this station is not waiting 394A RET NZ for a broadcast. 394B TEST_BLKN LD HL,(#1DF7) Fetch number of block being 394E LD E,(IX+13) transmitted, fetch number of expected 3951 LD D,(IX+14) block. 3954 AND A 3955 SBC HL,DE Jump if the block being transmitted is 3957 JR Z,#396B,GET_NBUFF the expected one. 3959 DEC HL Accept also the previous block (already 395A LD A,H received, but 'response' has not 395B OR L reached transmitter). 395C RET NZ But refuse other blocks. 395D CALL #396B,GET_NBUFF Receive the block. 3960 DEC (IX+13) Decrease block number, i.e. 'ignore' 3963 JR NC,#3968,GETNB_END this block. 3965 DEC (IX+14) 3968 GETNB_END OR 1 Return with Zero flag reset, so that 396A RET the procedure is repeated. Now follows the second routine, used to get the data block from the network. 396B GET_NBUFF LD A,(#1DF5) 396E AND A Send the 'response' code for the header 396F CALL NZ,#3AB5,SEND_RESP unless broadcasting. 3972 LD A,(#1DFA) Fetch data block length. 3975 AND A 3976 JR Z,#3999,STORE_LEN Jump if the data block is empty. 3978 PUSH IX Make HL point to the start of the 397A POP HL channel. 397B LD DE,21 397E ADD HL,DE Point to the start of the data buffer. 397F PUSH HL 3980 LD E,A E is holds the length. 3981 CALL #3A87,INPAK Receive the data block. 3984 POP HL 3985 RET NZ Return if the network was inactive. 3986 LD A,(#1DFA) Load the loop variable with the length 3989 LD B,A of the received block. 398A LD A,(#1DFB) Fetch checksum of received block. 398D CHKS4 SUB (HL) Subtract current byte. 398E INC HL Point to next byte. 398F DJNZ #398D,CHKS4 repeat for all bytes in the block. 3991 RET NZ Return if the checksum is wrong. 3992 LD A,(#1DF5) 3995 AND A Send the 'response' code for the data 3996 CALL NZ,#3AB5,SEND_RESP block, unless broadcasting. 3999 STORE_LEN LD A,(#1DFA) Fetch data block length. 399C LD (IX+20),A Store number of bytes in input buffer. 399F INC (IX+13) Increment block number. 39A2 JR NZ,#39A7,GETBF_END 39A4 INC (IX+14) 39A7 GETBF_END CP A Return with Zero flag set to signal 39A8 RET 'block successfully received'. THE 'OPEN "N" CHANNEL' ROUTINE The DISCiPLE doesn't support "n" channel use from BASIC. This routine creates a "n" channel in the DISCiPLE's own RAM. The channel is used for network serving purposes. 39A9 OPEN_N LD A,(#1DDA) Clear network bit of current control 39AC AND #7F port state. 39AE LD (#1DDA),A 39B1 LD DE,#1E3D Start of the channel area. 39B4 LD HL,#39D4,N_CH_DATA Start of "n" channel data. 39B7 LD BC,11 Copy the 11 bytes channel data to the 39BA LDIR channel area. 39BC LD A,(#1E01) Fetch destination station number. 39BF LD (DE),A 39C0 INC DE 39C1 LD A,(#029C) Fetch own station number from NSTAT. 39C4 LD (DE),A 39C5 INC DE 39C6 XOR A 39C7 LD (DE),A Clear the remaining 262 bytes. 39C8 LD H,D 39C9 LD L,E 39CA INC DE 39CB LD BC,262 39CE LDIR 39D0 LD DE,#1E3D Point to the start of the channel. 39D3 RET THE '"N" CHANNEL DATA' TABLE The '11' bytes that compose the initial part of a "N" channel are as follows: 39D4 N_CH_DATA DEFW #0008 Main ROM 'output' routine. 39D6 DEFW #0008 Main ROM 'input' routine. 39D8 DEFB "N" Channel specifier. 39D9 DEFW #3877 DISCiPLE ROM 'output' routine. 39DB DEFW #3819 DISCiPLE ROM 'input' routine. 39DD DEFW 276 Length of channel. THE 'SEND EOF BLOCK' SUBROUTINE This subroutine is used whenever the remaining buffer contents of the "n" channel have to be sent as the "end of file" block. 39DF SEND_NEOF LD IX,#1E3D Start of channel. 39E3 LD A,(IX+16) Fetch data block length. 39E6 AND A 39E7 RET Z Return if this is a 'read' channel. 39E8 LD A,#01 Signal 'EOF packet'. 39EA JP #38B5,S_PACK_1 Send packet and exit. THE 'NETWORK STATE' SUBROUTINE This subroutine returns when the network is considered to be 'resting', i.e. when the network line is inactive for 3-4 ms. The exact time is 'randomised' to prevent Spectrums from claiming the network at the same time over and over again. 39ED NET_STATE LD A,R Get a 'random' value. 39EF OR #C0 Allow only the range 192..255. 39F1 LD B,A Pass it to the loop count variable. 39F2 CALL #39F8,CHK_REST Check network state. 39F5 JR C,#39ED,NET_STATE Repeat until the network is resting, or SPACE is pressed. 39F7 RET Return when 'ready to claim'. THE 'CHECK_RESTING' SUBROUTINE This subroutine checks the state of the network and returns with carry reset if it is inactive for a 'sufficient' time determined by the value passed in the B register ((B * 54) - 22) T cycles). 39F8 CHK_REST LD A,#7F First check SPACE key. 39FA IN A,(254) 39FC RRCA 39FD JR NC,#3A39,NET_BREAK Give an error if it is pressed. 39FF MAKESURE PUSH BC Wait 21 T cycles. 3A00 POP BC 3A01 IN A,(31) Check network state. 3A03 CPL Invert the bits. 3A04 RLCA Only bit 7. Return if the network is 3A05 RET C already claimed by another Spectrum. 3A06 DJNZ #39FF,MAKESURE Repeat the test. 3A08 RET Return with Carry reset when network is resting. THE 'WAIT_SCOUT' SUBROUTINE This subroutine is used to identify a SCOUT leader from the network. This is done by checking the network line for about 7000 T cycles, to prove whether it is resting. After this, the network is examined until it is active. At this point, the SCOUT is identified. The subroutine returns with the Carry flag reset if 'time-out' has occurred and no SCOUT has been identified. Remember that there are no 'time-outs' when broadcasting. 3A09 WT_SCOUT LD HL,450 Set a counter. 3A0C CLAIMED LD B,128 This constant allows the network to be tested for 6890 T cycles. 3A0E CALL #39F8,CHK_REST Check network state. 3A11 JR NC,#3A20,WT_SYNC Jump for waiting the SCOUT. 3A13 DEC HL Loop again if the network is active, 3A14 DEC HL until the counter reaches zero. 3A15 LD A,H 3A16 OR L 3A17 JR NZ,#3A0C,CLAIMED 3A19 LD A,(IX+11) There is no 'time-out' when 3A1C AND A broadcasting. 3A1D JR Z,#3A0C,CLAIMED 3A1F RET Carry set signals 'time-out'. Now the SCOUT pulse is waited for. 3A20 WT_SYNC IN A,(31) Read the network line. 3A22 CPL Invert the bits. 3A23 RLCA Only bit 7. 3A24 JR C,#3A3F,SCOUT_END Jump if the SCOUT is identified. 3A26 LD A,#7F Check the SPACE key. 3A28 IN A,(254) 3A2A RRCA 3A2B JR NC,#3A39,NET_BREAK Give an error if it is pressed. 3A2D DEC HL Decrease counter. 3A2E LD A,H 3A2F OR L 3A30 JR NZ,#3A20,WT_SYNC Repeat until it reaches zero. 3A32 LD A,(IX+11) 3A35 AND A Again there is no 'time-out' when 3A36 JR Z,#3A20,WT_SYNC broadcasting. 3A38 RET Carry reset signals 'time-out'. THE 'NETWORK BREAK' ROUTINE Whenever the SPACE key is pressed during a network operation, the 'BREAK requested' error is given. 3A39 NET_BREAK CALL #3B02,REST_BORD Restore border colour. 3A3C JP #2926,REP_3 Give the error. THE 'WAIT SCOUT' SUBROUTINE CONTINUED When a SCOUT has been identified, the routine waits until it is finished. 3A3F SCOUT_END LD L,9 Set a counter. 3A41 LP_SCOUT DEC L Decrease it. 3A42 SCF Return with Carry set (to signal 3A43 RET Z 'SCOUT identified') when the counter 3A44 LD B,14 reaches zero. 3A46 DELAY_SC DJNZ #3A46,DELAY_SC Wait a while. 3A48 JR #3A48,LP_SCOUT Go back into the loop. THE 'SEND_SCOUT' SUBROUTINE This is the opposite of the preceding routine; the SCOUT leader followed by an 8-bit station number, is sent over the network. After having sent every bit, a test is made to see if the network has the expected state from the current bit value; if any error is found, then the whole procedure is repeated. 3A4A SEND_SC CALL #39ED,NET_STATE Wait until the network is resting. 3A4D LD C,31 Control port. 3A4F LD A,(#1DDA) Fetch current state of control port. 3A52 OR #80 Set network bit. 3A54 LD H,A H holds port 31 value. 3A55 LD L,9 Bit counter. 3A57 LD A,(#029C) Fetch own station number from NSTAT. 3A5A CPL Invert the bits. 3A5B LD E,A 3A5C IN A,(31) 3A5E RLCA Start again if some data is found on 3A5F JR NC,#3A4A,SEND_SC the network line. 3A61 ALL_BITS OUT (C),H Send a bit. 3A63 LD D,0 3A65 RL H Save the bit in D. 3A67 RR D 3A69 RLC E Rotate 'station number' left. 3A6B RR H Bit 7 of station number goes into bit 7 of H. 3A6D LD B,7 Wait 93 T cycles. 3A6F S_SC_DEL DJNZ #3A6F,S_SC_DEL 3A71 IN A,(31) Read the network. 3A73 AND #80 Only bit 7. 3A75 CP D Repeat again the procedure if the 3A76 JR Z,#3A4A,SEND_SC network does not have the right state from the current bit value. 3A78 DEC L Decrease bit counter. 3A79 JR NZ,#3A61,ALL_BITS Send all bits. 3A7B LD A,(#1DDA) Make network inactive. 3A7E OUT (31),A 3A80 LD A,1 3A82 LD B,13 Wait 171 T cycles before returning. 3A84 END_S_DEL DJNZ #3A84,END_S_DEL 3A86 RET THE 'INPAK' SUBROUTINE This basic subroutine is used to receive from the network a block of bytes. On entry, HL must hold the address from which the bytes will be loaded, while E must hold the length of the block. The subroutine returns with the Zero flag reset if the network is found inactive and no data has been read. The bits are read every 40 T cycles. 3A87 INPAK LD B,255 Set a counter. 3A89 N_ACTIVE IN A,(31) Read the network. 3A8B RLA Only bit 7. 3A8C JR NC,#3A92,INPAK_2 Jump if found active. 3A8E DJNZ #3A89,N_ACTIVE Otherwise try again. 3A90 INC B Return with Zero flag reset to indicate 3A91 RET 'network inactive'. Now the block is received. 3A92 INPAK_2 LD B,E B holds the length of the block. 3A93 LD A,0 Balance the CPL instruction below. 3A95 INPAK_L LD E,#80 Set a marker into bit 7. 3A97 OUT (59),A Set NET-WAIT line. (WAIT if NET=1) 3A99 NOP Wait 48 T cycles at the start of each 3A9A NOP byte. 3A9B INC IX 3A9D DEC IX 3A9F INC IX 3AA1 DEC IX 3AA3 UNTIL_MK LD A,0 Wait 7 T cycles. 3AA5 IN A,(31) Get a bit into the 3AA7 RLA Carry flag. 3AA8 RR E Shift the bit into E. 3AAA JP NC,#3AA3,UNTIL_MK Repeat for 8 bits. 3AAD LD A,E Because of the inverting port in the 3AAE CPL DISCiPLE the byte has to be inverted. 3AAF LD (HL),A Store the received byte. 3AB0 INC HL Next location. 3AB1 DJNZ #3A95,INPAK_L Get next byte. 3AB3 CP A Return with Zero flag set to signal 3AB4 RET 'successfully read'. THE 'SEND RESPONSE BYTE' SUBROUTINE A 'response byte' is simply a byte 1 that is sent over the network to confirm that some data has been successfully received. 3AB5 SEND_RESP LD A,1 This is the response byte. 3AB7 LD HL,#1DF4 Store it. 3ABA LD (HL),A 3ABB LD E,A A 'single byte' is to be sent. The subroutine continues into 'OUTPAK' below. THE 'OUTPAK' SUBROUTINE This is the opposite to the 'INPAK' subroutine and thus, is used to send over the network a block of bytes. Again, on entry HL must hold the address from which the block is stored, and E must hold its length. Initially a leader (length 98 T cycles) is sent, followed by each byte, with an initial 'start' period, and then the 8 bits (each 40 T cycles). 3ABC OUTPAK LD A,(#1DDA) Reset network line of current state of 3ABF AND #7F control port. 3AC1 LD (#1DDA),A 3AC4 OR #80 Begin with a 'start' leader. 3AC6 OUT (31),A 3AC8 LD B,3 Insert the required delay. 3ACA DEL_O_1 DJNZ #3ACA,DEL_O_1 3ACC OUTPAK_L LD C,(HL) C holds the byte to be sent. 3ACD INC HL Next location. 3ACE AND #7F The start bit is low (high on net). 3AD0 LD B,9 '9' bits to be sent (start, data). 3AD2 JR #3AD8,UNT_MARK_2 Send start bit. Now a byte is send over the network. 3AD4 UNT_MARK_1 RLA Rotate bit into A while keeping the 3AD5 SRL C other control bits intact. 3AD7 RRA 3AD8 UNT_MARK_2 OUT (31),A Send the bit. 3ADA DJNZ #3AD4,UNT_MARK_1 Loop for all bits. 3ADC RLA 3ADD SCF The stop bit is high (low on net). 3ADE RET NC Wait 5 T cycles 3ADF RR A Extra 4 T cycles (RRA is 4, RR A is 8). 3AE1 OUT (31),A Send the stop bit. 3AE3 PUSH HL Wait 44 T cycles. 3AE4 POP HL 3AE5 PUSH HL 3AE6 POP HL 3AE7 DEC E Decrease block length. 3AE8 JP NZ,#3ACC,OUTPAK_L Loop until all bytes have been sent. 3AEB LD A,(#1DDA) 3AEE OUT (31),A Make the network inactive before 3AF0 RET returning. THE 'NET_BORDER' SUBROUTINE This subroutine sets the border colour at "station MOD 8" and stores the state of the IFF at address #1ACC. It returns with the interrupts disabled. 3AF1 NET_BORD LD A,(#029C) Fetch own station number from NSTAT. 3AF4 AND #07 Keep only bits 0-2. 3AF6 OUT (254),A Set border colour. 3AF8 LD A,I Obtain interrupt status. 3AFA PUSH AF 3AFB EX (SP),HL Move it to HL and 3AFC LD (#1ACC),HL store it into IFF. 3AFF POP HL Restore HL. 3B00 DI Exit with interrupts disabled. 3B01 RET THE 'RESTORE BORDER COLOUR' SUBROUTINE This is the opposite to the 'NET_BORD' subroutine and thus, is used to restore the BORDER colour and the state of the IFF. Note that because of a bug in the Z80 itself the interrupts can be disabled although they were enabled when the "LD A,I" above was executed. (See NOTE before #3ECE.) 3B02 REST_BORD LD A,(23624) Get BORDCR (lower screen attribute). 3B05 AND #38 Only the BORDER bits. 3B07 RRCA Rotate colour into bits 0-2. 3B08 RRCA 3B09 RRCA 3B0A OUT (254),A Restore colour. 3B0C PUSH HL 3B0D LD HL,(#1ACC) Fetch previous state of the IFF. 3B10 EX (SP),HL 3B11 POP AF 3B12 JP PO,#3B16,R_B_EXIT Jump if interrupts were disabled. 3B15 EI Otherwise enable interrupts. 3B16 R_B_EXIT RET Exit. Miscalleneous routines II THE 'FLASH BORDER' SUBROUTINE When DISCiPLE system variable RBCC (address @0) doesn't hold zero, this subroutine flashes the border. The border colour is then obtained by masking the E register (holds sectornumber) with RBCC. 3B17 FLASH_BORD LD A,(#0298) Fetch RBCC. 3B1A AND E Incorporate sectornumber. 3B1B RET Z Return if 'no flashing'. 3B1C AND #07 Keep border colour only. 3B1E OUT (254),A Set the border and finished. 3B20 RET THE 'BORDER COLOUR RESTORE' SUBROUTINE This subroutine is used whenever the border colour was changed during an I/O operation, and needs to be restored to its original state. 3B21 BORD_REST PUSH AF 3B22 LD A,(23624) Fetch lower screen attribute (BORDCR). 3B25 AND #38 Only the border bits. 3B27 RRCA Move the bits to 0-2. 3B28 RRCA 3B29 RRCA 3B2A OUT (254),A Restore colour. 3B2C POP AF 3B2D RET THE 'PRINT DIRECTORY DESCR.' SUBROUTINE This subroutine is used to print the directory description of a file during an 'extended CAT' command. On entry the A register holds the directory description. 3B2E PRT_TYPE PUSH AF 3B2F LD HL,#3B7C,TYPE_TABLE Start of messages table. 3B32 LD BC,85 Length of table excluding 'WHAT?'. 3B35 CPIR Make HL point to right message. HL points to 'WHAT?' with unknown types. 3B37 CALL #3D47,PRT_MSG_HL Print the message. 3B3A POP AF Restore file type. 3B3B CP 1 3B3D JR NZ,#3B54,PRT_NOBAS Jump with no 'BASIC' files. 3B3F LD (IX+13),219 Make RPT point to autostart line high. 3B43 CALL #37D4,RPT_HL1 HL points to it now. 3B46 LD A,(HL) 3B47 AND 192 3B49 JR NZ,#3B77,PRT_EXIT Jump if no autostart line present. 3B4B LD D,(HL) Otherwise fetch it. 3B4C DEC HL 3B4D LD E,(HL) 3B4E EX DE,HL 3B4F CALL #3BD7,PRT_NUM Print it. 3B52 JR #3B77,PRT_EXIT Now the other directory descriptions are handled. 3B54 PRT_NOBAS CP 4 3B56 JR NZ,#3B77,PRT_EXIT Jump with no 'CODE' files. 3B58 LD (IX+13),215 RPT points to file address high byte. 3B5C CALL #37D4,RPT_HL1 Make HL hold RPT. 3B5F LD D,(HL) Fetch file address. 3B60 DEC HL 3B61 LD E,(HL) 3B62 EX DE,HL 3B63 PUSH DE 3B64 CALL #3BD7,PRT_NUM Print file address. 3B67 LD A,"," Print a ','. 3B69 CALL #3C2C,PRT_A 3B6C POP HL 3B6D DEC HL 3B6E LD D,(HL) Fetch file length. 3B6F DEC HL 3B70 LD E,(HL) 3B71 EX DE,HL 3B72 LD A,0 Ignore leading zero's. 3B74 CALL #3BD9,PRT_N10000 Print the length. 3B77 PRT_EXIT LD A,13 Print a NEWLINE and exit. 3B79 JP #3C2C,PRT_A THE 'DIRECTORY DESCRIPTION' TABLE This table contains the directory description messages as printed with an 'extended CAT'. Each message is preceeded by is description value. 3B7C TYPE_TABLE DEFB 1 3B7D DEFM "BAS " 3B81 DEFB 2 3B82 DEFM "D.ARRAY" 3B89 DEFB 3 3B8A DEFM "$.ARRAY" 3B91 DEFB 4 3B92 DEFM "CDE " 3B96 DEFB 5 3B97 DEFM "SNP 48k" 3B9E DEFB 6 3B9F DEFM "MD.FILE" 3BA6 DEFB 7 3BA7 DEFM "SCREEN$" 3BAE DEFB 8 3BAF DEFM "SPECIAL" 3BB6 DEFB 9 3BB7 DEFM "SNP 128k" 3BBF DEFB 10 3BC0 DEFM "OPENTYPE" 3BC8 DEFB 11 3BC9 DEFM "EXECUTE" 3BD0 DEFB 12 3BD1 DEFM "WHAT?" 3BD6 DEFB 0 THE 'PRINT NUMBER' SUBROUTINE This subroutine prints the number held in the HL register. Entering the routine at #3BD7 prints leading spaces, while the other entry points prints the character held in the A register in place of leading zero's. A value of 0 means don't print anything. 3BD7 PRT_NUM LD A,32 Spaces are printed in place of leading zero's. 3BD9 PRT_N10000 LD DE,10000 Start printing with tens-of-thousands. 3BDC CALL #3BF6,PRT_DIGIT 3BDF PRT_N1000 LD DE,1000 Start printing with thousands. 3BE2 CALL #3BF6,PRT_DIGIT 3BE5 PRT_N100 LD DE,100 Start printing with hundreds. 3BE8 CALL #3BF6,PRT_DIGIT 3BEB PRT_N10 LD DE,10 Start printing with tens. 3BEE CALL #3BF6,PRT_DIGIT 3BF1 LD A,L Print units. 3BF2 ADD A,"0" Add ASCII offset for digits. 3BF4 JR #3C2C,PRT_A THE 'PRINT DIGIT' SUBROUTINE This subroutine is used to print a digit, the HL register holds the number and the DE register the value for 'repeated subtraction'. 3BF6 PRT_DIGIT PUSH AF Preserve leading character. 3BF7 XOR A Clear Carry and counter. 3BF8 PRT_DIG1 SBC HL,DE The 'trial' subtraction. 3BFA JR C,#3BFF,PRT_DIG2 Jump if exhausted. 3BFC INC A Count each trial. 3BFD JR #3BF8,PRT_DIG1 Jump back for next try. The A register now holds the digit to be printed. 3BFF PRT_DIG2 ADD HL,DE Restore last subtraction. 3C00 AND A 3C01 JR NZ,#3C01,PRT_DIG3 Jump if a non zero value is to be printed. 3C03 POP DE Retrieve the leading character into D. 3C04 ADD A,D Add it to zero. 3C05 RET Z Return if nothing has to be printed. 3C06 JR #3C2C,PRT_A Otherwise print the leading character. Now print the digit. 3C08 PRT_DIG3 ADD A,"0" Add ASCII offset for digits. 3C0A CALL #3C2C,PRT_A Print the digit. 3C0D POP DE Balance the stack. 3C0E LD A,"0" All zeroes after any non zero digit will 3C10 RET be printed as '0'. THE 'PRINT OUT MESSAGE' SUBROUTINE This subroutine handles the printing of messages directly following the 'CALL' instruction to this routine. When the DISCiPLE's own error stack pointer (D_ERR_SP) holds a non-zero value, i.e during hook and command codes, no printing has to take place. The carry flag is set and the A register then holds 32, signalling 'error during hook/command code execution' as usual. 3C11 PO_MSG LD HL,(#0296) Fetch (D_ERR_SP). 3C14 LD A,H 3C15 OR L 3C16 JR Z,#3C1D,PO_MSG1 Jump if it isn't used. 3C18 LD SP,HL Clear machine stack. 3C19 LD A,32 Signal 'error 32'. 3C1B SCF 3C1C RET Exit. Now the message can be printed. 3C1D PO_MSG1 POP HL HL points to the message to be printed. 3C1E PO_MSG2 LD A,(HL) Fetch a character. 3C1F AND #7F 3C21 CALL #3C2C,PRT_A Print it. 3C24 BIT 7,(HL) Bit 7 set signals 'End of message'. 3C26 RET NZ 3C27 INC HL 3C28 JR #3C1E,PO_MSG2 Repeat for all characters. THE 'PRINT A SPACE' SUBROUTINE This subroutine prints a space to the current stream. 3C2A LD A,32 Continue in the 'PRT_A' routine. THE 'PRINT CHARACTER' SUBROUTINE This subroutine prints the character held in the A register to the current stream. 3C2C PRT_A PUSH AF 3C2D PUSH BC 3C2E PUSH DE 3C2F PUSH HL 3C30 RST #10,CALBAS Print the character in the A register 3C31 DEFW #0010,PRINT_A_1 by calling the 'main' ROM routine. 3C33 POP HL 3C34 POP DE 3C35 POP BC 3C36 POP AF 3C37 RET THE 'ROM' MESSAGES Now follow some messages used in the ROM. 3C38 MESG_0 CALL #3C11,PO_MSG 3C3B DEFM #0D,#0D,#0D 3C3E DEFB #0D,#0D,#0D 3C41 DEFM " "SYSTEM" LOADING" 3C59 DEFB #0D,#0D 3C5B DEFM " PLEASE WAIT" 3C71 DEFB #0D,#0D 3C73 DEFM " (ROM ISSUE 3.0)" 3C8B DEFB #0D,#8D 3C8D MESG_1 CALL #3C11,PO_MSG 3C90 DEFM "OVERWRITE " 3C9A DEFB """+128 3C9B SURE_MSG CALL #3C11,PO_MSG 3C9E DEFM "Are you SURE ? (y/n" 3CB1 DEFB ")"+128 3CB2 MESG_3 CALL #3C11,PO_MSG 3CB5 DEFM "" (y/n" 3CBB DEFB ")"+128 The error routines THE 'DISCIPLE ERROR' ROUTINE Whenever a DISCiPLE error is encountered this routine is executed to handle it. During syntax checking the error is handled by the 'main' ROM error handler. Whenever an error is found while executing a command code, a return is made with the Carry flag set and the A register holding the error number. Only during runtime the error message is printed. 3CBC D_ERROR CALL #3B21,BORD_REST Restore normal border colour. 3CBF LD (#1DED),DE Track & sector with sector error. 3CC3 XOR A 3CC4 LD (#1ACF),A Clear FLAGS3. 3CC7 POP HL Fetch return address. 3CC8 LD DE,(#0296) Fetch (D_ERR_SP). 3CCC LD A,D 3CCD OR E 3CCE LD A,(HL) Fetch error code. 3CCF JR Z,#3CD7,D_ERROR1 Jump if (D_ERR_SP) isn't used. Whenever hook or command codes are being executed, errors are reported to the calling routine by setting the carry flag. The A register holds the error code. 3CD1 LD SP,(#0296) Clear the machine stack. 3CD5 SCF Carry set to signal 'error'. 3CD6 RET The routine continues here when an error message is to be printed. 3CD7 D_ERROR1 LD (23610),A Store error code into (ERR_NR). 3CDA RES 5,(IY+1) Signal 'ready for a new key'. 3CDE LD SP,(23613) Clear machine stack by using (ERR_SP). 3CE2 RST #30,SYNTAX_Z Return via 'main' ROM 'SET_STK' routine 3CE3 LD HL,#16C5,SET_STK to the error handler during syntax 3CE6 JP Z,#004F,UNPAGE_HL checking. 3CE9 LD HL,0 3CEC LD (IY+55),H Clear FLAGX. 3CEF LD (IY+38),H And X_PTR-hi. 3CF2 LD (23563),HL Clear DEFADD. 3CF5 INC L Ensure that stream 0 points to channel 3CF6 LD (23574),HL 'K'. 3CF9 RST #10,CALBAS Clear all the work areas and the 3CFA DEFW #16B0,SET_MIN calculator stack. 3CFC LD A,(23563) If (DEFADD-lo) <> 0 then a jump is made 3CFF AND A to the 'STMT_R_1' routine at #1B7D in 3D00 JP NZ,#0165,UNUSED_X the 'main' ROM. When this happens is not clear, DEFADD has just been cleared at #3CF2. 3D03 RES 5,(IY+55) Signal 'EDIT mode' FLAGX. 3D07 RST #10,CALBAS Clear the lower screen. 3D08 DEFW #0D6E,CLS_LOWER 3D0A SET 5,(IY+2) Signal 'lower screen will require clearing'. (TV_FLAG) 3D0E RES 3,(IY+2) Signal 'mode is to be considered unchanged'. 3D12 LD A,(23610) 3D15 CP 4 3D17 JR NZ,#3D34,D_ERROR2 Jump if error isn't 'SECTOR error'. 3D19 PUSH AF 3D1A LD DE,(#1DED) Fetch track and sector where error 3D1E LD H,0 was signalled. 3D20 LD L,D 3D21 PUSH DE 3D22 XOR A Print the track number, no leading 3D23 CALL #3BE5,PRT_N100 characters. 3D26 LD A,58 Print a comma. 3D28 CALL #3C2C,PRT_A 3D2B POP DE 3D2C LD H,0 3D2E LD L,E 3D2F XOR A Print the sector number, no leading 3D30 CALL #3BEB,PRT_N10 characters. 3D33 POP AF The routine now continues with the printing of the error message. 3D34 D_ERROR2 LD HL,#3D51,ERR_MSGS HL points to the error message table. 3D37 LD BC,505 Length of error message table. 3D3A CPIR Search for the message. 3D3C CALL #3D47,PRT_MSG_HL Print it. 3D3F INC SP Drop address of 'main' ROM error handler. 3D40 INC SP 3D41 LD HL,#1349,MAIN_5A Print the line and statement number by 3D44 JP #004F,UNPAGE_HL jumping into the Spectrum error handler. THE 'PRINT MESSAGE HL' SUBROUTINE This subroutine prints the message pointed to by the HL register. 3D47 PRT_MSG_HL LD A,(HL) Fetch a character. 3D48 CP 32 3D4A RET C Exit if it isn't printable, message is finished. 3D4B CALL #3C2C,PRT_A Print the character. 3D4E INC HL 3D4F JR #3D47,PRT_MSG_HL Continue until message end is reached. THE 'ERROR MESSAGES' TABLE The following table contains the DISCiPLE's error messages. Each message starts with it's error number. 3D51 ERR_MSGS DEFB #00 3D52 DEFM "Nonsense in GDOS" 3D62 DEFB #01 3D63 DEFM "Nonsense in GNOS" 3D73 DEFB #02 3D74 DEFB "Statement END error" 3D87 DEFB #03 3D88 DEFM "BREAK requested" 3D97 DEFB #04 3D98 DEFM ",SECTOR error" 3DA5 DEFB #05 3DA6 DEFM "FORMAT data lost" 3DB6 DEFB #06 3DB7 DEFM "NO DISC in drive" 3DC7 DEFB #07 3DC8 DEFM "No "SYSTEM" file" 3DD8 DEFB #08 3DD9 DEFM "Invalid FILE NAME" 3DEA DEFB #09 3DEB DEFM "Invalid STATION" 3DFA DEFB #0A 3DFB DEFM "Invalid DEVICE" 3E09 DEFB #0B 3E0A DEFM "VARIABLE not found" 3E1C DEFB #0C 3E1D DEFM "VERIFY failed" 3E2A DEFB #0D 3E2B DEFM "Wrong FILE type" 3E3A DEFB #0E 3E3B DEFM "MERGE error" 3E46 DEFB #0F 3E47 DEFM "CODE error" 3E51 DEFB #10 3E52 DEFM "PUPIL set" 3E5B DEFB #11 3E5C DEFM "Invalid CODE" 3E68 DEFB #12 3E69 DEFM "Reading a WRITE file" 3E7D DEFB #13 3E7E DEFM "Writing a READ file" 3E91 DEFB #14 3E92 DEFM "O.K. GDOS 3" 3E9D DEFB #15 3E9E DEFM "Network OFF" 3EA9 DEFB #16 3EAA DEFM "Wrong DRIVE" 3EB5 DEFB #17 3EB6 DEFM "Disc write PROTECTED" 3ECA DEFB #18 3ECB DEFM "Not enough SPACE on disc" 3EE3 DEFB #19 3EE4 DEFM "Directory FULL" 3EF2 DEFB #1A 3EF3 DEFM "File NOT FOUND" 3F01 DEFB #1B 3F02 DEFM "END of file" 3F0D DEFB #1C 3F0E DEFM "File NAME used" 3F1C DEFB #1D 3F1D DEFM "Not a MASTER station" 3F31 DEFB #1E 3F32 DEFM "STREAM used" 3F3D DEFB #1F 3F4E DEFM "CHANNEL used" 3F4A - 3FFF Unused locations (all set to #00).