Vous êtes sur la page 1sur 149

CONTENTS INTRODUCTION USE OF HOOK CODES EXTENSIONS TO BASIC Basic loader program Assembler Listings Main program A simple

double POKE routine: POKE * x,y Pokeing strings into memory: POKE n,s Memory dump: *L n Modified EDIT function: *E n An improved sound command: BEEP *a,b,c,d A faster and more complete catalogue: * CAT n Pseudorandom file handling: READ #S,N Adding data to a file: RESTORE #S Extending the RS232 channel Extending Spectrum BASIC THE SHADOW ROM DISASSEMBLY The The The The The The The The The restart routines control routine syntax checking routines RS232 link routines Network routines Microdrive routines Hook code routines Microdrive command routines not used routines

APPENDIX Labels sorted by address value Labels sorted alphabetically Shadow system variables Channels Bibliography Index to routines Shadow ROM issue 2 How to tell which edition Interface 1 you have Basic loader program for edition 2 Shadow ROM Extended BASIC commands from assembler for edition 2 Shadow ROM What to do if you have an unknown ROM

INTRODUCTION The Sinclair ZX Interface I adds enormously to the power of the ZX Spectrum, allowing it to send and receive data over an RS232 link, to use the Local Area Network, and to store information on the Microdrives. Equally impressive is its ability to expand the BASIC provided with the computer, adding new and powerful commands. Much of this power is due to the 8K monitor program which is housed in an 8K Read Only Memory in the Interface 1. This ROM, which is kept between memory adresses 0000H and 1FFFH is "paged" back and forth with the main ROM so that both ROMs appear to occupy the same section of memory. This book provides a complete listing of this program, fully commented and explained, permitting you to use these routines in your programs. In addition, many examples are given of how to expand the Spectrum's BASIC by using the Interface 1 to define new commands. I feel that this book should be of interest to all users of the Interface 1. Those without experience in machine code programming can use the new BASIC commands provided, whether or not they own an assembler. More ambitious programmers will find numerous hints for the creation of the their own routines, and the algorithms controlling the Microdrive, the RS232 link, and the Local Area Network will be of interest to even the most expert programmer. I should like, finally, to thank Massimiliano, Carlo, Giovanna, Sylvia, and Mr. Alfred Milgrom (of Melbourne House Ltd.). A special thanks must go to the entire staff of Sinclair Research Ltd. Gianluca Carri Florence, Italy, January 1985.

USE OF HOOK CODES While the disassembly of the ROM given here will be a valuable aid to machine code programmers wishing to use the Interface I, there is one problem: Sinclair Research have not used the same ROM in every Interface 1. There are two different editions of the ROM. Each, of course, does the same job of running the Microdrives, the RS232 interface, and the Local Area Network, but there are a number of minor differences in the actual code contained in the ROMs. The differences between the two editions are described in appendix 7. Does this mean that we can never rely on being able to write programs that can run on all machines? Far from it. Sinclair Research have provided a way to access a number of the ROM routines in a way which can be guaranteed to work on all machines. To do this, we use what are called hook codes. These consist of a RST 8 instruction followed by a byte whose value depends on which hook code you are using. Their effect is to page the ROM in, call a routine in the Interface 1's 'shadow ROM', then page the main ROM back in and return control to the users program. This provides 'transparant' access to the Interface 1 the user doesn't need to know anything about the ROM, only about the various hook codes. The following example shows the use of hook code 20H (which tests to see if a key has been pressed) from machine code: EXAMPLE ORG 7D00H RST 0008H DEFB 20H RET END ;32000 dec ;hook code restart ;check keypress

To do the same thing from BASIC: POKE 32000,207: POKE 32001,32: POKE 32002,201: RANDONISE USR 32000 The hook codes available for your use are numbered from 1BH to 32H (27 to 50 decimal). The hook codes are all fully described in the disassembly, from 1981H to 1B6DH. If you use the hook codes within your programs, instead of calling the shadow ROM subroutines, there will be no need to change your program if Sinclair make further changes to the ROM. Otherwise you will need to update all the absolute references to the shadow ROM. In using hook codes, only the contents of the A register may be passed to the subroutines being called. The subroutines do not preserve any registers, so be sure to save all of the main set of registers before you use any hook codes. Some of the more complex routines corrupt HL', the return address to BASIC, so you may wish to preserve this. Although the individual hook codes are dealt with in detail in the disassembly, here is a brief summary of their actions: 1BH - Accepts a character from the keyboard. 1CH - A character is output to stream 2, which is normally the upper part of the screen. 1DH - Accepts a byte of data from the RS232 link. 1EH - Sends a byte of data to the RS232 link. 1FH - A character is output to stream 3, which is normally the ZX printer. 20H - Tests for keypress. The next twelve hook codes refer to the Microdrives. 21H - Select drive. Can be used to turn any drive on, or all drives off. 22H - Open channel. Allows the user to create a microdrive channel. 23H - Close channel. The Microdrive channel is reclaimed. 24H - Delete file. Used to erase a named file from a Microdrive channel. 25H - Read sequential. Allows the user to fetch the 'next' record of a named file. 26H - Write record. Writes a new record onto the next free sector of a microdrive cartridge. 27H - Read random. Similar to 'read sequential', except that the record read need not be the next one in the file.

28H - Reads a record from a specified sector of tape. 29H - Reads the record from the next sector of tape that passes under the read head of the Microdrive. 2AH - Writes a record to a specified sector of tape. 2BH - Due to a programming error, this has the same effect as 'create channel' in the first issue ROM. This should be corrected in later issues. 2CH - Reclaims the memory used by a Microdrive channel. The following four hook codes refer to the local area network. 2DH - Opens a network channel. 2EH - Closes a network channel. 2FH - Fetches a 'packet' of information from a network channel. 30H - Sends a packet over a network channel. 31H - Creates the new system variables used by the Interface 1. This happens when the system is powered up, and when NEW is typed, but it also can be done manually. 32H - This routine is different from the other hook codes in that it can be used to call any routine in the shadow ROM. The shadow ROM is paged in, the routine whose address is held in the system variable HD-11 (held at address 5CEDH) is called, and the main ROM is paged back in. Another use for hook code 32H is paging in the Shadow ROM. If you want the Shadow ROM to sit in main memory, where the main ROM normally is, and to be directly accessible to your program, use the following program fragment to page the Shadow ROM in: LD LD RST DEFB POP POP HL,LABEL (23789),HL 8 32H HL HL

LABEL

To page the main ROM back in, simply do CALL 0700H For more information about the hook codes and the ZX Interface 1, see the 'Spectrum Microdrive Book' by Dr. Ian Logan. also published by Melbourne House.

EXTENSIONS TO BASIC This section of the book shows how to add a number of new commands to the Spectrums BASIC. Those with no knowledge of machine code, or simply no interest in it can simply type the routines that follow into their Spectrums, save them onto tape or Microdrive, and instantly have a more powerful BASIC. Machine code programmers will find the programs valuable examples of how to use the Interface 1 ROM routines, and should be interested by the way in which new commands are added to BASIC. A later section will explain how this is done. The extensions to BASIC consist of a number of machine code routines, each of which makes up a new BASIC command, along with a main program which will prepare the computer to run the new commands. The new BASIC includes improvements to the POKE, CAT, and BEEP commands, a better version of EDIT, more versatile file handling for the Microdrives, an extension to the RS232 channel, and a useful memory dump command which will list the contents of an area of memory, in both hexadecimal and ASCII form. Sinclair Research have released two different versions of the Interface 1. Each of the versions contains a slightly different program in its ROM. The newer version of the ROM has corrected a number of errors contained in the previous edition, as well as adding some new routines. Because new code has been added to the Shadow ROM at various points, many of the routines in the ROM are no longer located at the same address as before. As the extensions to BASIC given in this book make use of many routines from the Shadow ROM, you will have to do slightly different things to implement the BASIC extensions, depending on which edition of the Interface 1 you own. First of all you'll have to figure out which edition of the Interface you own. Instructions on how to do this are given in Appendix 8 on page 155. If you find you have an edition 1 ROM in your Interface 1, you will find instructions on how to add the new commands to Basic on page 11 for those who have an assembler, or on page 7 for those who don't have access to an assembler. If you have an edition 2 ROM, then turn to Appendix 10 on page 161 if you have an assembler, or Appendix 9 on page 157 if you don't, for instructions on how to add the new BASIC commands to your Spectrum. Although there are only two editions of the Shadow ROM at the moment, there is some possibility that Sinclair Research may release further editions of the Interface 1. Should you own one of these, it is likely that the extended BASIC commands given here will not work properly with it. If the extended BASIC commands do not work for you, and you suspect that you might have a third edition or later Shadow ROM, turn to Appendix 11 on page 165 to find out how you can implement the new commands.

Basic loader program (For owners of Interface 1s equipped with the edition 1 ROM) If you do not have an Assembler, you may enter the routines into your Spectrum with the following BASIC loader program. Enter this program into your Spectrum. 1 10 20 30 40 50 60 70 80 PRINT 90 100 110 120 CLEAR 63743 FOR A=63744 TO 65046 STEP 12 PRINT "ADDRESS:";A'' LET C=0 FOR B=l TO 12 LET Z=A+B-1: IF Z<=65046 THEN INPUT X: PRINT X: POKE Z,X: LET =C+X NEXT B PRINT '"CHECKSUM=";C INPUT "THIS IS WROMG ? (Y/N) ";LINE Y$: IF CODE Y$=89 OR CODE Y$=l2l THEN "Retype from address ";A: PAUSE 100: CLS: GO TO 20 IF CODE Y$<>78 AND CODE Y$<>l10 THEN GO TO 80 CLS: NEXT A PRINT "SAVING THE PROGRAM" SAVE *"M";1;"SHADP"CODE 63744,1303

When you have finished typing this program into the Spectrum, you should 'RUN' it. You should then type, from the listing on the next page, the 12 bytes from the address shown on the screen. When you have typed the first 12 bytes, a 'checksum' should be displayed on the screen; if it matches with the one printed on the listing, at the righthand side of each line, then you have not made mistakes in typing the numbers, and you may enter "N" or "n" to continue with the next line. If the checksums do not match, you must enter 'y' or 'Y', and then retype the whole line. When all numbers have been entered, the program will automatically be saved on Microdrive cartridge (there must be a cartridge with at least 2K free into Microdrive 1). IF you wish to save the program on tape, line 120 of the listing should be modified. When at later time you wish to use the routines, you have simply to place the cartridge into Microdrive I, and then to enter the following direct commands: CLEAR 63743: LOAD *"M";1;"SHADP"CODE: RANDOMIZE USR 63744 And the new commands should be available. Note that if you use NEW command, you must reenter the command 'RANDOMIZE USR 63744' to reinitialise the VECTOR system variable.

63744: 63756: 63768: 63780: 63792: 63804: 63816: 63828: 63840: 63852: 63864: 63876: 63888: 63900: 63912: 63924: 63936: 63948: 63960: 63972: 63984: 63996: 64008: 64020: 64032: 64044: 64056: 64068: 64080: 64092: 64104: 64116: 64128: 64140: 64152: 64164: 64176: 64188: 64200: 64212: 64224: 64236: 64248: 64260: 64272: 64284: 64296: 64308: 64320: 64332: 64344: 64356: 64368: 64380: 64392: 64404: 64416: 64428:

207 254 202 252 254 153 195 32 213 5 102 215 205 229 225 24 205 0 12 67 61 54 195 121 5 6 197 229 167 34 0 215 205 205 58 205 10 22 128 229 44 123 221 29 0 0 251 203 251 245 96 32 94 3 0 82 102 102

49 244 11 254 42 30 193 0 215 237 250 130 135 6 126 2 169 215 126 73 92 0 180 28 215 0 215 245 237 237 205 1 196 65 9 254 251 205 250 175 0 203 225 229 0 0 205 87 126 62 205 251 35 205 205 48 29 29

33 202 250 224 194 197 5 215 153 176 246 28 30 6 230 62 30 130 202 92 229 255 18 254 148 197 153 197 66 92 30 22 18 19 251 18 183 33 221 205 25 63 205 195 0 0 178 194 183 228 102 126 86 143 143 250 35 225

9 45 254 202 75 215 215 140 30 195 32 205 125 197 127 32 24 28 240 237 33 215 215 44 30 215 30 229 48 207 6 205 33 32 33 24 32 251 126 247 205 215 196 173 0 0 251 62 202 205 29 230 235 251 251 25 229 195

249 249 227 175 249 153 130 28 80 193 254 183 205 126 254 205 202 205 1 123 127 169 32 194 245 148 197 213 240 50 205 232 9 239 12 215 203 205 41 23 178 40 16 24 0 0 62 251 99 102 241 192 17 17 17 246 35 112

34 254 202 252 215 30 28 205 89 5 108 5 135 205 32 169 254 183 120 61 16 15 0 240 215 30 205 205 61 253 183 15 251 33 251 58 221 0 221 205 251 45 195 0 0 33 32 62 251 29 200 192 16 100 1 48 35 251

183 42 188 195 121 197 254 183 225 215 194 215 30 158 56 30 101 5 230 92 229 33 254 1 148 167 0 181 225 54 5 221 17 9 182 13 126 251 119 0 205 215 193 0 0 140 205 245 254 58 62 43 39 0 0 195 205 6

92 202 251 240 28 225 44 5 193 32 197 153 62 30 6 35 194 215 192 33 237 56 42 215 30 40 7 3 32 0 205 126 24 251 230 251 41 221 13 251 0 227 5 0 0 92 102 195 3 29 36 62 205 205 62 102 112 10

201 114 254 1 205 193 194 215 120 0 249 30 32 35 254 16 240 153 194 7 115 15 194 121 167 44 225 209 235 0 109 25 0 203 2 183 221 126 195 221 251 45 62 0 0 54 29 102 202 251 195 202 143 143 255 29 251 126

215 249 229 215 183 113 240 241 177 254 215 96 205 16 128 235 1 30 240 250 61 229 240 28 40 79 209 225 33 239 6 205 205 78 194 40 190 13 128 229 205 205 13 0 0 255 58 29 157 230 102 205 251 251 60 62 62 205

24 254 202 32 5 35 1 43 202 207 32 105 169 249 48 62 215 253 1 229 92 199 1 205 54 6 193 193 193 215 62 247 5 32 169 209 13 183 250 225 56 0 195 0 0 33 9 33 251 31 29 102 17 17 183 175 44 102

0 215 12 0 215 112 215 197 193 202 0 124 30 193 2 13 32 203 237 42 253 225 215 183 79 0 241 241 5 32 2 23 251 232 250 58 40 194 221 17 29 251 102 0 0 13 251 24 61 198 33 29 232 19 237 205 205 29

1296 2324 2282 2042 1786 1653 1714 1504 1890 1721 1879 1489 1363 1450 1488 885 1667 1620 1795 1306 1545 1465 1586 1634 1376 932 1872 2370 1542 1413 1018 1537 1214 1390 1641 1528 1531 1704 1913 2027 1449 1632 1628 650 0 620 1633 1487 2040 1668 1330 1668 1370 1345 1414 1467 1344 1392

64440: 64452: 64464: 64476: 64488: 64500: 64512: 64524: 64536: 64548: 64560: 64572: 64584: 64596: 64608: 64620: 64632: 64644: 64656: 64668: 64680: 64692: 64704: 64716: 64728: 64740: 64752: 64764: 64776: 64788: 64800: 64812: 64824: 64836: 64848: 64860: 64872: 64884: 64896: 64908: 64920: 64932: 64944: 64956: 64968: 64980: 64992: 65004: 65016: 65028: 65040:

35 215 30 92 70 247 221 215 254 221 194 34 67 205 202 221 198 41 24 238 175 40 1 245 195 50 254 5 124 23 11 62 64 253 60 60 201 254 58 17 13 200 254 4 13 92 17 15 10 24 20

16 121 254 221 194 23 119 32 16 126 2 201 78 254 197 102 205 32 221 211 205 1l 215 253 193 17 195 215 142 33 213 3 12 203 12 50 62 167 18 254 32 24 71 48 32 1 229 92 239 187 12

249 28 16 126 50 33 11 0 210 4 9 92 49 18 26 70 62 245 229 239 247 254 121 50 5 254 193 148 205 17 33 50 253 1 58 14 1 196 254 167 12 194 14 251 246 5 253 71 58 197 60

201 205 210 4 17 255 221 215 99 254 221 205 11 205 205 221 30 62 225 205 23 47 28 16 215 215 5 30 24 0 5 216 203 198 15 254 50 90 71 196 58 254 0 24 201 0 24 58 14 205 1

215 183 99 254 241 0 119 130 6 71 126 165 221 18 75 117 205 230 11 254 195 40 205 254 121 148 215 254 23 167 0 92 1 245 254 201 19 12 58 90 19 6 12 243 254 9 237 15 254 105 13

32 5 6 77 221 34 12 28 215 194 25 17 126 19 26 11 196 211 55 18 193 33 183 215 28 30 32 1 195 237 25 209 134 62 71 205 254 58 22 12 254 32 44 197 23 115 17 254 144 253 10

0 215 215 194 119 201 205 205 1 45 205 56 41 32 218 221 18 239 0 221 5 254 5 148 205 50 0 40 193 66 17 195 254 1 58 105 61 21 254 175 167 33 125 62 192 35 53 184 200 193 0

254 148 1 45 13 92 247 183 22 6 247 19 221 227 163 116 221 1 25 126 215 63 215 30 183 18 215 12 5 218 53 74 32 50 14 253 50 254 60 50 62 42 188 32 17 114 253 48 48 72

35 30 22 6 221 205 23 5 221 221 23 40 119 219 17 12 126 104 205 68 32 40 148 61 5 254 130 62 62 47 253 11 56 19 254 175 14 167 184 22 1 14 48 205 221 201 205 5 5 24

194 245 221 221 126 132 195 215 42 203 33 14 13 239 221 221 13 1 120 221 0 54 30 50 215 175 28 3 3 5 115 254 85 254 184 50 254 196 32 254 50 254 167 53 253 50 210 253 237 179

240 215 42 203 25 11 193 148 81 24 255 221 24 230 110 203 221 205 24 119 254 195 167 15 148 50 205 253 215 205 35 165 32 241 48 19 58 90 8 201 19 58 144 253 42 15 253 54 68 0

1 148 81 24 205 175 5 30 92 70 0 203 3 1 69 24 190 250 62 13 33 240 202 254 30 22 183 203 30 19 114 210 4 205 5 254 20 12 58 254 254 16 40 193 81 92 58 0 79 80

1472 1758 1197 1467 1502 1414 1571 1406 1259 1445 1340 1267 964 1667 1529 1539 1685 1621 1207 1933 1577 1271 1520 1591 1543 1283 1655 1226 1230 1037 874 1541 1130 1732 1033 1640 1044 1517 1077 1692 941 1127 1107 1565 1575 729 1809 1049 1356 1519 116

Assembler Listings (For Interface 1s equipped with the edition 1 Shadow ROM.) The following is a complete, fully commented assembler listing of the machine code programs which make the new BASIC commands possible. These consist of a main program which sets up the new system variables needed by the Interface 1, and a number of small programs, one of which is used by each new command. The equate statements used by these routines are included in a seperate table at the end of the listings. To add the new BASIC commands to your Spectrum, enter the assembler program on the following pages, as well as the table of EQU statements that follows it, into your assembler, assemble it, and save the resulting machine code on tape or microdrive. When you wish to use the new BASIC commands, load the machine code file into your Spectrum at 63744 (F900 Hex.), and do RANDOMIZE USR 63744 to make the new commands available. All readers of this book should at least glance through this section, as interspersed with the listings of the new commands are examples of their use, and explanations of their functions. A brief explanation of the manner in which new commands are added to BASIC is given later in the book, and it may prove useful in understanding what is being done in this section.

Main program The program starts at the address F900 hex (63744 decimal), and is located above RAMTOP. When it is used for the first time, you must use a command RANDOMIZE USR 63744 to initialise the system variable VECTOR. You must do this whenever the initial VECTOR contents are defaulted back to ERR_6 (01F0). This happens when you use a NEW command to delete the BASIC program from the memory. ORG ; MAINPG ; RST DEFB LD LD RET ERROR_1 HOOK31 HL,STSYN (VECTOR),HL ;Create new system variables if ;nonexistent. ;Store start address of new routine ;into VECTOR. EQU $ 0F900H

Note that initially 'hook code' +31 is used to create the new system variables if they do not already exist. Failure to do so in your programs will cause a probable crash when you try to POKE something into the location where the 'new' system variables should be. The address of the syntax module routine STSYN is then stored into VECTOR. This causes the routine STSYN to be used whenever a command fails the syntax of the main ROM interpreter, and then that of the shadow ROM interpreter. The system variable CH_ADD will point to the first character of the command that produces the error. The routine STSYN is listed below. STSYN RST DEFW CP JP CP JP CP JP CP JP CP JP CP JP JP CALBAS GET_CHAR POKE Z,PGM1 "*" Z,PGM3 BEEP Z,PGM5 READ Z,PGM7 RESTORE Z,PGM8 LPRINT Z,PGM9 ERR_6 ;Fetch command code. ;Jump to the appropriate routine, ;depending on the command code.

;Give an error if none of these.

Note that the 'main' ROM restart GET_CHAR is used to fetch the command code into the A register; then a jump is made to the required syntax module routine. If the command code is still wrong, the error is confirmed by jumping to ERR_6.

A simple 'double POKE' routine: Poke * x,y The first implemented 'new' command is a 'double POKE' command. That is, a POKE command that uses word operands (16 bits) instead of byte operands (8 bits). The syntax is 'POKE *x,y, where 'a' is the address in the range 0..65535, and 'y' is the value to be poked at location 'a', again in the range 0..65535. Note that the standard Intel format is used, i.e. the low byte goes before the high byte in memory. You may use the new POKE command, for example, to store values into twobyte system variables: POKE *23675,32000 is a rather straightforward way of changing to '32000' the contents of the UDG system variable, as compared with the usual: POKE 23675,32000~256*INT (32000/256) POKE 23676,INT (32000/256) The machine code program used to do this is very simple: PGM1 RST DEFW CP JP RST DEFW CALL RST DEFW PUSH RST DEFW PUSH POP POP LD INC LD JP CALBAS NEXT_CHAR "*" NZ,PGM2 CALBAS NEXT_2NUM ST_END CALBAS FIND_INT2 BC CALBAS FIND_INT2 BC HL BC (HL),C HL (HL),B END1 ;Advance CH_ADD. ;Jump if it is not a POKE *x,y command. ;Evaluate the two parameters. ;Confirm end of statement. ;Fetch number to be POKEd. ;Save it. ;Fetch address. ;Move it into HL. ;Restore value. ;POKE the low byte. ;POKE the high byte. ;Finished.

Initially the syntax of the new command is checked; CH_ADD is advanced to the character that follows the token 'POKE', and if the character '*' is not present, a jump is made to the command for pokeing strings into memory (because the current command is not POKE *x,y). Assuming that our current command is POKE *x,y, the 'main' ROM routine NEXT_2NUM is then used to evaluate two numeric expressions (separated by a comma), following the character pointed by CH_ADD. An error will occur if the expressions have incorrect syntax. Otherwise the syntax is correct and the call to ST_END returns to the 'main' ROM during syntax checking. During runtime the execution continues, and the main ROM routine FIND_INT2 is used to fetch the values from the calculator stack. After the POKE * has been done, control is returned to the main ROM interpreter by jumping to END1.

Pokeing strings into memory: POKE n,"s" This command makes it possible to POKE a string in memory, starting from the address 'n'. For example, the statement: POKE 30000, "FRED BLOGGS" Will POKE the ASCII values of the characters in the string "FRED BLOGGS", starting from the address 30000. You may check this by reading back the values with PEEK: FOR A=30000 TO 30010: PRINT CHR$ PEEK A;: NEXT A Note that the routine fails the syntax of the interpreter by using a different parameter type (i.e. a string instead of a number), while the previous routine used a '*' character following the command code. PGM2 RST DEFW CP JP RST DEFW RST DEFW CALL RST DEFW PUSH PUSH RST DEFW LD LD POP POP LD OR JP LDIR JP CALBAS EXPT_1NUM COMMA NZ,ERR_6 CALBAS NEXT_CHAR CALBAS EXPT_EXP ST_END CALBAS STK_FETCH BC DE CALBAS FIND_INT2 D,B E,C HL BC A,B C Z,END1 END1 ;Evaluate the address. ;A separator must be present. ;Report the error if it is not present. ;Advance CH_ADD. ;Evaluate string expression. ;Confirm end of statement, ;Fetch string parameters. ;Save 'length' and 'start'. ;Fetch address. ;Move address into DE. ;Restore string 'start' and 'length'. ;Exit with null string. ;The actual POKE. ;Finished.

The routine is entered from the POKE *x,y routine with CH_ADD pointing to the character following the 'POKE' token. The main ROM routines EXPT_ 1NUM and EXPT_EXP are then used, respectively to evaluate a numeric and a string expression. A comma must be placed between them as separator. During runtime, the main ROM routines STK_FETCH and FIND_INT2 are used to fetch the string parameters, and the value of the numeric expression. No action is taken iF the string has null length.

Memorydump: *L n This routine creates a new command '*L n' that lists the memory contents, starting from the address n, in both hexadecimal and ASCII. Unprintable ASCII codes ate printed as spaces. For example, the following statement: *L 31920 Will list the contents of the memory starting dumping memory at addresses lower than 16384, Try for example to dump from the address 695, ROM report messages table. The result will be 02B7 02BD 02C3 02C9 02CF 02D5 00 61 69 41 73 42 50 6D 73 6F 65 41 72 6F 20 66 68 65 6E 73 20 69 53 49 .... .... 67 69 64 65 6E 43 72 6E 01 6E 20 02 Progr am fin ished Nonsen se in BASIC from address 31920. Note that when the 'shadow' ROM code is shown. that is the start of the 'shadow as follows:

The routine follows: PGM3 RST DEFW CP JP OR CP JP RST DEFW RST DEFW CALL RST DEFW LD LD LD CALL LD CALL LD CALL PUSH LD PUSH LD CALL INC DJNZ POP POP LD AND CP JR CP JR JR LD CALBAS NEXT_CHAR CAT Z,PGM6 20H "l" NZ,PGM4 CALBAS NEXT_CHAR CALBAS EXPT_1NUM ST_END CALBAS FIND_INT2 H,B L,C A,H DISP_HEX A,L DISP_HEX A,SPACE DISP_CH HL B,6 BC A,(HL) DISPHEX2 HL L_LOOP BC HL A,(HL) 7FH SPACE C,REJECT 80H NC,REJECT ACCEPT A,SPACE ;Advance CH_ADD. ;Jump if the command is CAT. ;Make the letter lower case. ;Jump if this is not an "*L" command. ;Advance CH_ADD. ;Evaluate start address. ;Confirm end of statement. ;Fetch start address. ;Move it into HL. ;Display high byte of address in hex. ;Display low byte. ;Print a space. ;Save current address. ;Display 6 bytes. ;Save counter for later, ;Fetch a byte. ;Display it in hex. ;Examine each byte in turn. ;Restore counter and address. ;Fetch a byte. ;Clear bit 7. ;Reject codes lower than SPACE. ;Reject codes greater than 7FH. ;Replace invalid codes with 'space'

AGAIN

L_LOOP

C_LOOP

REJECT

ACCEPT

CALL INC DJNZ LD CALL JR

DISP_CH HL C_LOOP A,CR DISP_CH AGAIN

;code. ;Print the character. ;Advance pointer. ;Continue the toop. ;Finally print a carriage return. ;Jump back.

This time, the interpreter syntax is failed by starting the command with a non standard character, namely, a '*'. The letter 'L' (for 'L'ist) maybe either upper or lower case. Note that the shadow ROM routines DISP_HEX, DISP_HEX2 and DISP_CH are used to print characters onto stream 2 (the screen). Note also that the command will never end during runtime. This, however, will not result in an endless loop, as you may simply press BREAK when the 'scroll' message is displayed on the screen, to stop the listing. The output may be directed to a device other than the screen by first using an OPEN command. i.e. OPEN #2,"T" will direct the output to the RS232 "T" channel. The main ROM routines EXPT_1NUM and FIND_INT2 are used to evaluate and fetch the given parameter 'n'.

Modified EDIT function: *E n The normal 'EDIT' function is not very well implemented; if you wish, for example, to EDIT line 4385 of your program, you must first make the line 'current' by using something like LIST 4385, and then (after having pressed BREAK to stop the listing) press the EDIT key. By using this routine, you may type simply '*E 4385' to do the same thing. The listing of the routine follows: PGM4 CP JP RST DEFW RST DEFW CALL RST DEFW BIT JP LD AND JP LD LD LD PUSH LD PUSH LD PUSH LD LD RST DEFW LD PUSH RST "e" NZ,ERR_6 CALBAS NEXT_CHAR CALBAS EXPT_1NUM ST_END CALBAS FIND_INT2 7,(IY+PPC_HI) Z,ERR_6 A,B 0C0H NZ,ERR_6 (E_PPC),BC SP,(ERR SP) HL,ENDED HL HL,(ERR_SP) HL HL,ED_ERROR HL (ERR_SP),SP (IY+ERR_NR),0FFH CALBAS ED_EDIT HL,ED_LOOP HL MAIN_ROM ;Error if it is not the '*E' command. ;Advance CH_ADD. ;Evaluate line number. ;Confirm end of statement. ;Fetch line number. ;Accept only as 'direct' command. ;Error with line number out of range. ;Make this line 'current'. ;Clear machine stack. ;Final return address. ;Save current error address. ;The new error address. ;Clear the error. ;Copies the line into the editing area. ;Return to 'EDitor LOOP', and to ENDED ;below when finished.

; ;NOTE: Main ROM in use. ; ENDED POP HL JP 12B4H

;Remove +l303 from the stack. ;Jump back to the main execution loop.

Note that the command is rejected at runtime (BIT 7,PPC_HI) if it is not a 'direct' one: this is because an EDIT command used within a program does not make sense. The command is also rejected if you specify wrong line numbers. If you use a nonexistent line number, the next existent number is used. The routine enters the main ROM 'EDITOR' subroutine, after having copied the line into the editing area, and returns to the main execution loop when finished.

An improved sound command: BEEP *a,b,c,d The standard 'BEEP x,y' command is very limited: it will only make sounds of a constant pitch. This routine creates a new 'BEEP' command that accepts four parameters, giving an endless range of possible sound effects. The syntax of the new command is 'BEEP * a,b,c,d', where 'a' and 'b' are the values that the main ROM 'BEEPER' subroutine accepts as 'pitch' and 'length', 'c' is a 'step' value that will be subtracted from 'a' until it reaches zero, and 'd' is a 'repeat' value that defines how many times the whole sound must be made. Try the following statement: BEEP * 100,255,10,1 The following BASIC program will generate a wide range of sounds: 10 20 30 40 50 60 LET a=RND*1000+10 LET b=RND*6+1 LET c=RND*50+1 LET d=RND*3+1 BEEP *a,b,c,d GO TO 10

The routine is listed below: PGM5 RST DEFW CP JP RST DEFW CP JP RST DEFW CALL RST DEFW PUSH RST DEFW AND JR LD LD PUSH RST DEFW AND JR LD LD PUSH RST DEFW PUSH CALL POP POP POP POP PUSH CALBAS NEXT_CHAR "*" NZ,ERR_6 CALBAS NEXT_2NUM COMMA NZ,ERR_6 CALBAS NEXT_2NUM ST_END CALBAS FND_INTl AF CALBAS FND_INTl A Z,RANGE C,A B,0 BC CALBAS FND_INT1 A Z,RANGE C,A B,0 BC CALBAS FIND_INT2 BC UNPAGE HL DE BC AF HL ;Advance CH_ADD. ;The '*' must be present. ;Evaluste 'a','b' ;A separator must be found after 'b'. ;Evaluate 'c','d'. ;Confirm end of statement. ;Fetch 'd'. ;Save it. ;Fetch 'c'. ;Give an error if c=0. ;Otherwise move it into BC. ;Save it. ;Fetch 'b'. ;Give an error if b=0. ;Move it into BC. ;Save 'b' briefly. ;Fetch 'a'. ;Save it temporarily. ;Use main ROM. ;Restore 'a', 'b', 'c' and 'd' values.

LOOP2

;Save 'a' for next pass.

LOOP1

PUSH PUSH PUSH PUSH CALL POP POP POP POP AND SBC JR DEC POP JR LD LD RST DEFB LD RST

AF BC HL DE BEEPER DE HL BC AF A HL,BC NC,LOOP1 A HL NZ,L00P2 HL,END1 (HD_11),HL ERROR_1 HOOK32 (IY+ERR_NR),0 ROMERR

;Save values.

;Emit a sound. ;Restore values.

;Subtract 'step' from 'a'. ;Continue while subtraction possible. ;Decrease 'd' ;Restore 'a' for repetition. ;Repeat the sound until d=0. ;Note how the return is made to the ;shadow ROM.

; RANGE

;Call the error handling routine.

Note that 'b', 'c' and 'd' must be in the range l..255, ('d' may be set to 8, but this will give the same effect that you would expect from a value of 256.) Be careful that you choose the right length for your sound, as the the BREAK key will not work in the middle of a sound. If you have some difficulty in choosing the correct 'a' and 'b' values, you may calculate them as shown below: a = 437500/frequency-30.l25 b = frequency * time (frequency=Hz, time=sec.) You may also use the following BASIC program which calculates the values for A and B, starting from the 'x', 'y' values used in a normal BEEP command. For example, the sound obtained with BEEP 0.01,10 requires '908' and '4' as A and B values. 10 DATA 261.63, 277.18, 293.66, 311.13, 329.63, 349.23, 369.99, 392, 415.3, 440, 466.16, 493.88 20 INPUT "TIME (SEC.) ? ";t 30 INPUT "PITCH ? ";p 40 LET i=INT p: LET pi=p-i 50 LET pk=(pi*.0577622606)+1 60 LET a=i+60: LET b=-6 70 LET b=b+1: LET a~a-12 80 IF SGN a()-1 THEN GO TO 70 90 LET a=a+l2 100 RESTORE: FOR x=0 TO a: READ c: NEXT x 110 LET f=(pk*c)*2-b: IF f*t=0 THEN PRINT "ERROR": STOP 120 PRINT "A=";INT (437500/f-30.125)'"B=";INT (f*t)

A faster and more complete catalogue:* CAT n After some 'general purpose' commands come some more complicated routines that use the new Interface's devices. This routine adds a new command '* CAT n' that gives a full catalogue of a cartridge held in the microdrive 'n'. The command runs about 23 seconds faster than the standard 'CAT n' command, does not limit the output to 59 names, and provides information on the nature of the file, its length, etc., that is not given when the standard 'CAT' command is used. Since the names are printed as they are found on the cartridge, the list is not alphabetically ordered. Along with each filename the following information will appear: 'PRINT' 'CODE sssss,nnnnn' 'DATA x' 'DATA x$' 'LINE nnnnn' If it is a PRINTtype file. If it is a 'bytes' file; 'sss' and 'nnn' are respectively the start address and the length of the file. If it is a numeric array named 'x'. If it is a string array named 'x$' If it is a BASIC program with autostart at line 'nnn'.

If nothing is printed after the filename, then the file is a normal BASIC program saved without the autostart. The routine is listed below. PGM6 RST DEFW CALL CALL CALL LD RST DEFW CALL LD CALL CALL LD LD CALL CALL JR LD BIT JR LD LD OR AND JP CALL JR LD OR JR LD OR JR LD CP JR CALBAS NEXT_CHAR EXPT_NUM CHECK_M_2 ST_END A,2 CALBAS CHAN_OPEN SET_T_MCH A,(IX+CHDRIV) SEL_DRIVE GET_M_HD02 HL,RDESC DE,18H GETD CHKS_HD R NZ,CATLOOP HL,RDESC 0,(HL) NZ,CATLOOP A,(RDESC) HL,RDESC+3 (HL) 2 NZ,PRI_NA RES_B_MAP CATLOOP A,(RDESC+4) A Z,CATLOOP A,(RDESC+1) A NZ,CATLOOP A,(IX+HDNUMB) (IX+CHREC) Z,ENDCAT ;Advance CH_ADD. ;Evaluate drive number. ;Check range of drive number. ;Confirm end of statement. ;Use stream 2 (screen). ;Create a temporary "m" channel. ;Turn on drive motor. ;Get a header. ;And a record descriptor from the address ;RDESC. ;Repeat until the checksum is correct. ;Repeat also if loaded wrong block type ;(header). ;Jump forward with 'not free' sectors.

CATLOOP

;Mark 'free' sectors. ;Ignore names starting with CHR$ 0. ;Ignore other than first records. ;Jump forward when the whole tape has ;been examined.

; PRI_NA

CALL CALL LD OR JP LD LD JP ; ENDCAT PUSH XOR CALL CALL PUSH POP LD ADD CALL CALL CALL LD SRL RST DEFW RST DEFW CALL POP CALL JP LD JP

OUTNAM PRCR A,(IX+CHREC) A NZ,CATLOOP A,(IX+HDNUMB) (IX+CHREC),A CATLOOP IX A SEL_DRIVE PRCR IX HL DE,2CH HL,DE PRNANE PRCR FREESECT A,E A CALBAS STACK_A CALBAS PRINT_FP PRCR IX DEL_M_BUF END1 A,CR PRCHAR

;Examine current record. ;Print a carriage return. ;Jump unless first time (CHREC=0). ;Store current sector number (to ldetect when the whole tape has been ;examined). ;Save channel start address. ;Turn off drive motors. ;Leave a blank line. ;Make HL point to HDNANE.

;Print cartridge name. ;Print a carriage return. ;Fetch number of 'free' sectors. ;A holds the number of Kbytes left. ;Store this number on calculator stack. ;Print the number on the screen. ;Final carriage return. ;Restore channel start address. ;Reclaim the channel. ;Finished. ;Print a carriage return. ;Save start address. ;Fetch descriptor and header information. ;Space to store record descriptor. ;Suppresa scrolling. ;Print record name. ;Follow it with a space. ;This is 'RECFLG'. ;Jump if this is not a ;'PRINTtype' file. ;Otherwise print the keyword 'PRINT'.

; PRCR ; GETD ; RDESC ; OUTNAM

PUSH HL JP GT_M_BLK DEFB l8H LD LD LD CALL LD CALL LD BIT JP LD JP LD OR JP CP JP DEC PUSH LD CALL LD AND ADD HL,SCR_CT (HL),0FFH HL,RDESC+4 PRNAME A,SPACE PRCHAR A,(RDESC) 2,A NZ,NOTPRINT A,PRINT PRCHAR A,(HL) A Z,PROG 3 Z,BYTES A AF A,DATA PRCHAR A,(RDESC+20) 1FH A,60H

; ;Jump with type=0 (program files). ;Jump with type=3 (bytes). ;Save zero flag (set with numeric ;arrays). ;Print the keyword DATA. ;Fetch array name. ;Clear bit 5,6,7. ;Obtain an ASCII code.

CALL POP RET LD JP ; PROG LD LD AND RET DEC LD CALL LD INC LD EX LD CALL LD CALL LD CALL LD CALL LD LD INC OR SBC JR ADD OR JP LD CALL INC PUSH INC INC CALL LD CALL POP JP LD LD CALL INC DJNZ RET

PRCHAR AF Z A,"$" PRCHAR HL,RDESC+23 A,(HL) 0C0H NZ HL A,LINE PRCHAR E,(HL) HL D,(HL) DE,HL DE,10000 PRDIG DE,1000 PRDIG DE,100 PRDIG DE,10 PRDIG DE,1 A,-1 A A HL,DE NC,OUTD HL,DE 30H PRCHAR A,CODE PRCHAR HL HL HL HL FPRINT A,COMMA PRCHAR HL FPRINT B,10 A,(HL) PRCHAR HL PRLOOP

;Print array name. ;Return with numeric arrays. ;But print '$' with string arrays. ;Fetch high byte of autostart line no. ;Return if no autostart was specified. ;Point to the low byte. ;Print the keyword LINE, and then ;the line number: ;Fetch entry indirectly. ;Move value into DE. ;Print first digit. ;Print second digit. ;Print third digit. ;Print fourth digit. ;Print last digit. ;Counter. ;Increment the counter. ;Clear carry flag. ;'Trial subtraction'. ;Continue until borrowing found. ;Balance last SBC. ;Make the counter an ASCII character ;and print it. ;Print the keyword CODE. ;Save pointer to 'length'.

; FPRINT

; PRNUM

PRDIG OUTD

; BYTES

;Print the 'start'. ;Print a comma. ;Finally print the 'length'. ;Counts ten characters. ;Fetch character, ;Print it. ;Advance the pointer. ;Loop for the whole name.

; PRNAME PRLOOP

The 1. 2. 3. 4.

routine can be divided into eleven different sections: A 'temporary "m" channel' and map is created in the CHANS area. The required Microdrive is turned on. The current sector number is stored into CHREC. A 'header' is loaded from the cartridge into the "m" channel header area.

5. The 'record descriptor' and the 'header informations' are loaded into the RDESC area. 6. If the 'header' indicates 'free' sector, the appropriate map bit is reset and the program loops back to step (4) 7. The filename and the various information are taken from the RDESC area and printed on the screen. 8. The program continues looping back to (4) until the whole tape has been examined; this is done by comparing the current sector number against CHREC one. 9. The cartridge name is taken from HDNAME and printed. 10. The number of 'free sectors' is used to print the number of Kbytes left in the cartridge. 11. The Microdrive motor is turned off, and the channel is reclaimed. Names starting with CHR$ 18 are not printed; records other than 'first' ones are not examined, so as to avoid the printing of a filename more than once. Note that the form *CAT #S;N is not supported, because there is not sufficient time to send the information fetched to a channel other than the screen, before the next tape sector is due to be examminined. If you wish to direct the catalogue to a printer, you must use first a command such as: OPEN #2;"m";d;"CAT" and then use the *CAT n command. Finally 'CLOSE #2' will send the catalogue to the Microdrive. It can then be printed with either: MOVE "m";d;"CAT" TO #3 MOVE "m";d;"CAT" TO "b" (with the ZX printer) (with RS232 printers)

The catalogue can then be erased with ERASE "m";n;"CAT" (where 'n' is the drive number). In this case, you must take care that the output of the catalogue will not require more than 512 characters to be printed (otherwise unpredictable results may occur when the Spectrum tries to send the record to the Microdrive while the CAT command is operative). This can be done by making sure that there are fewer than 20 files on the cartridge. Note that to fail the syntax of the interpreter, the command begins with a '*' character; the command may not begin with a 'new' command token, such as CAT, SAVE, ERASE, CLS, etc.

Pseudo-random file handling: READ #S,N This command will permit a 'pseudorandom' handling of Microdrive files. When READ #S,N is executed, the 'read' Microdrive channel attached to the stream 'S' is used, and record 'n' of that file is loaded into the channel buffer. Since the Microdtive system splits the main 'PRINTtype' files into records 512 bytes long, you may write items with 'length=512', thereby having each record contain a single item, and then read back that item with the READ #S,N command. Such an application is shown by using the following BASIC program. Each item is made by 8 elements (length=63 characters + carriage return) of an array, exactly 512 bytes. Without the READ #S,N command you have to read sequentially all items before reaching the desired one. 10 DIM a$(24,63) 20 REM Some data for the file 30 DATA "THE MICRODRIVES", "give you fast access to a large memory.", "", "Each Microdrive can hold up to 100 Kbytes on a single", "removable cartridge.", "Note that the Microdrive nearest the computer is always known as", "Microdrive 1, and the next along is Microdrive 2, and so on.", "There is also a light on the front of each Microdrive." 40 DATA "THE CARTRIDGES", "come in a protective box; and should always be kept in their", "box when not in use.", "But remember...", "Never take the cartridge out of the Microdrive while the light", "is on.", "Never switch the power on or of while a cartridge is in the", "Microdrive." 50 DATA "THE NETWORK", "enables you and your friends to play computer games together", "and to send each other programs and data.", "This means that only one of you need ever type in a program.", "Using the lead supplied with each Interface you can link up", "as few as two and as many as 64 Spectrum computers." 60 OPEN #4;"M";1;"FILE" 70 FOR a=l TO 24: READ a$(a): NEXT a 80 CLOSE #4 90 CLEAR: REM The file has been cleared. 100 OPEN #4;"M";1;"FILE" 110 CLS: PRINT "Press a key for your choice: "''"0. MICRODRIVES"''"l. CARTRIDGES"''"2. NETWORK"''"3. STOP" 120 PAUSE 0: LET a$=INKEY$; IF a$<"0" OR a$>"3" THEN GO TO 110 130 IF a$="3" THEN CLOSE #4: STOP 140 READ #4,VAL a$ 150 CLS: FOR a=1 TO 8: INPUT #4;a$: PRINT a$: NEXT a 160 PAUSE 0: GO TO 110 If the stream(s) specified in the command is not opened, the error "invalid stream" will occur. "Invalid device expression" will occur if the stream is not attached to an "m" channel; "Reading a 'write' file" will occur if the channel is opened for writing. The cartridge is simply searched for the 'n'th record of the file 'n' being in range 0..255), then it is loaded and CHBYTE cleared so as to direct INKEY$ and INPUT commands to the first byte loaded. If the record is not found, or if any reading error occurs, the error 'File not found' will be reported. The listing of the routine follows. PGM7 RST DEFW CP JP RST DEFW CALL RST DEFW CALBAS NEXT_CHAR "#" NZ,ERR_6 CALBAS NEXT_2NUM ST_END CALBAS FND_INT1 ;Advance CH_ADD. ;A hash sign must be present ;after READ. ;Evaluate 's','n' ;Confirm end of statement. ;Fetch 'n'.

PUSH RST DEFW CP JP RST DEFW LD LD CP JP BIT JP POP LD LD CALL LD LD CALL XOR LD LD CALL JP

AF CALBAS FND_INT1 10H NC,NREPORT-2 CALBAS CHAN_OPEN IX,(CURCHL) A,(IX+4) "M" NZ,NREPORT3 0,(IX+CHFLAG) NZ,RWFERR AF (IX+CHREC),A A,(IX+CHDRIV) SEL_DRIVE HL,0FFH (SECTOR),HL GET_R_LP A (IX,CHBYTE),A (IX+CHBYTE+1),A SEL_DRIVE END1

;Save it for later. ;Fetch 's'. ;Give an error if 's' is greater than 15 ;Use this stream. ;Fetch channel start address. ;Fetch channel specifier. ;Error if not an "M" channel. ;Error if the file is opened for ;writlng. ;Store record number into CHREC. ;The drive motor is turned on. ;Counts 255 sectors. ;Fetch record. ;C1ear CHBYTE. ;Switch off drive motor. ;Finished.

Adding data to a file: RESTORE #S Normally, if you try to write data onto a Microdrive file that has previously been CLOSEd, the error 'Writing to a 'read' file' will occur. This makes the operation of extending a file with more data difficult, as one has to create a new file, copy into it the 'old' one and the 'new' data, and then erase the 'old' file. This is not easy, and is a very slow process. This command will convert the 'read' file attached to the stream 's' back to a 'write' file, thus permiting the addition of more data. When you have finished adding data, you may CLOSE the stream that returns a 'read' file. You may use RESTORE #S again to convert it into a 'write' file if you wish. The following BASIC program will demonstrate this. 5 10 20 30 40 50 60 70 80 90 100 110 120 130 140 150 160 170 180 190 200 210 220 230 240 250 260 PRINT "CREATING THE FILE" OPEN #4;"M";1;"NUMBERS" FOR N=1 TO 10 PRINT #4;N'N*N NEXT N CLOSE #4 PRINT '"READING THE FILE" OPEN #4;"M";l;'NUMBERS" PRINT FOR B=1 TO 10 INPUT #4;M;N PRINT "THE SQUARE OF ";M;" IS ";N NEXT B PRINT '"EXTENDING THE FILE" RESTORE #4 FOR N=11 TO 20 PRINT #4;N'N*N NEXT N CLOSE #4 PRINT '"READING EXTENDED FILE" OPEN #4;"M";1;"NUMBERS" PRINT FOR B=l TO 20 INPUT #4;M;N PRINT "THE SQUARE OF ";M;" IS ";N NEXT B CLOSE #4

The routine is as folllows: PGM8 RST DEFW RST DEFW CALL RST DEFW CP JP RST DEFW LD LD CP JP BIT JP LD CALBAS NEXT CHAR CALBAS EXPT_1NUM ST_END CALBAS FND_INT1 10H NC,NREPORT-2 CALBAS CHAN_OPEN IX,(CURCHL) A,(IX+4) "M" NZ,NREPORT3 0,(IX+CHFLAG) NZ,NREPORTN A,(IX+CHDRIV) ;Advance CH_ADD. ;Evaluate stream number. ;Confirm end of statement. ;Fetch stream number. ;Reject stream number > 15. ;Use this stream. ;Fetch channel start address. ;Fetch channel specifier. ;Error if not an "m" channel. ;Error also with 'write' files. ;Turn on drive motor.

LOOP

RESBIT NXTS

SLOOP

CALL LD LD CALL JR JR BIT JR LD LD JR CALL CALL JR IN AND JP CALL JP LD LD LD LD SET CALL CALL LD CP JR LD OUT LD CALL PUSH POP LD ADD CALL LD OUT CALL LD LD XOR CALL JP

SEL_DRIVE HL,0FFH (SECTOR),HL G_HD_R C,NXTS Z,RESBIT 1,(IX+RECFLG) Z,NXTS A,(IX+HDNUMB) (IX+CHREC),A NXTS RES_B_MAP DEC_SECT NZ,LOOP A,(0EFH) 1 Z,RSSH RD_SECTOR C,RSSH2 L,(IX+RECLEN) H,(IX+RECLEN+l) (IX+CHBYTE),L (IX+CHBYTE+1),H 0,(IX+CHFLAG) IN_CHK GET_M_HD02 A,(IX+CHREC) (IX+HDNUMB) NZ,SLOOP A,0E6H (0EFH),A BC,0168H DELAY_BC IX HL DE,37H HL,DE OUT_M_BUF A,0EEH (0EFH),A RES_B_MAP A,(IX+RECNUM) (IX+CHREC),A A SEL_DRIVE END1

;Counts through 255 sectors. ;Fetch header and data block. ;Jump with any error. ;Jump with 'free' sectors. ;Jump if this is not the last record in ;the file. ;Otherwise fetch sector number to CHREC. ;Mark 'free' sector. ;Continue until the whole tape has been ;examined. ;Error with writeprotected cartridge. ;Fetch the EOF record. ;Error with wrong checksum. ;Use RECLEN as current position.

;Make this a 'write' file. ;Set 'free sector' descriptor. ;Fetch a header block. ;Continue if this is not the one. ;Start writing. ;Wait to create a first gap. ;Make HL point to data block preamble.

;Erase this sector. ;'End of writing' signal. ;Mark 'free' this sector, ;Copy RECNUM into CHREC. Switch off drive motor. ;Finished.

The steps involved in doing the RESTORE #S command are as follows: 1. A 'Microdrive map' is setup, for use when the channel is made a 'write' channel. 2. The last record of the file is loaded into the channel area. 3. That record is erased from the cartridge. 4. The channel is marked 'write channel'. As with the READ #S,N command, errors are reported if the stream 'S' is closed, or if it is opened with other than "m" channel, or with "m" channels opened for writing.

Extending the RS-232 channel This routine creates three new commmands for time user who has an RS232 printer connected to their Spectrum. Time first command is LPRINT ? n. If n=1, a new RS232 channel is opened, so subsequent LPRINT and LLIST commands will use this for their outputs. If n=0, the "p" channel is defaulted for the LPRINT and LLIST Commands (the ZX Printer). The channel opened has more features than the normal "t" channel. The TAB function and the comma are supported on the printer, the 'leading space bug' present with the "t" channel has been corrected, the CR and LF codes are re definable, an auto CR feature is supported, and finally a redefinable formfeed code is sent after a certain amount of lines printed, so as to advance to the next page before the printing head reaches the end of the sheet. The second command is LPRINT ! x,y. The parameter 'x' will define the width of the line. If, for example, you specify a width equal to 32, the standard Spectrum screen format is used. A width of 40, 80, 132 columns, or other, is suitable depending on your choice, and on the maximum width that your printer allows. The parameter 'y' will define the number of columns used as step when printing the 'comma' control code. The default value for 'x' and 'y' are 80 and 20. The last command is LPRINT / x,y. In this case, 'x' will define the number of lines that will be printed before the form feed code (which advances to the next page) is sent to the printer (default=60, suitable for 66line sheets). The parameter 'y' is the formfeed code that your printer accepts; the standard value is 12. You may store codes other than formfeed, such as linefeed, etc., to obtain different results when the 'x' lines have been printed. The CR code is sent as 'CR' followed by 'LF' (Line Feed). If your printer does not need the LF code, you may end up with a double spacing between lines; the problem may be eliminated with POKE 65045,0. The assembly code for our final extension to BASIC is listed below. PGM9 RST DEFW CP JR CP JR CP JR JP RST DEFW CALL RST DEFW AND JP LD RST DEFW DEC LD JP RST DEFW CALL RST DEFW LD CALBAS NEXT CHAR "!" Z,OUT1 "/" Z,OUT2 "?" Z,OUT3 ERR_6 CALBAS NEXT_2NUM ST_END CALBAS FND_INT1 A Z,OTRNG (CMMSP),A CALBAS FND_INT1 A (WIDTH),A END1 CALBAS NEXT_2NUM ST_END CALBAS FND_INT1 (FFC),A ;Advance CH_ADD. ;Jump with LPRINT !x,y ;Jump with LPRINT /x,y ;jump with LPRINT ?n ;Error if none of these. ;Evaluate 'x,y'. ;Confirm end of statement. ;Fetch 'y'. ;Reject spacing=0. ;Store this value. ;Fetch 'x'. ;Range of 'x' is from 0 onwards. ;Store the value. ;Finished. ;Evaluate 'x,y' ;Confirm end of statement. ;Fetch 'y' ;Store it.

; OUT1

; OUT2

RST DEFW LD XOR LD JP ; OUT3 RST DEFW RST DEFW CALL RST DEFW CP JR LD RES CALL JP LD RST DEFW LD AND SBC JP CALL PUSH LD ADD LD LD INC LD LD LD POP JP CP JP RES CP JR JR SET PUSH LD LD POP CALL LD LD LD CP JR INC LD RET

CALBAS FND_ NT1 (LPAGE),A A (CLINE),A END1 CALBAS NEXT_CHAR CALBAS EXPT_1NUM ST_END CALBAS FND_INT1 1 Z,NEWOUT A,3 1,(IY+FLAGS3) CLOSE END1 A,3 CALBAS STR_DATA1 HL,11H A HL,BC C,NREPORTC OP_RS_CH DE HL,5 HL,DE DE,NRSOUT (HL),E HL (HL),D A,3 (5CD8H),A DE OP_STREAM 0A5H NC, TCHANOUT+4 0,(IY+FLAGS) SPACE C,CTRL NZ,NOSPC 0,(IY+FLAGS) AF A,1 (CLRCR),A AF TCHAN_OUT A,(WIDTH) B,A A,(POSN) B NC,NLIN A (POSN),A

;Fetch 'x'. ;Store it. ;Clear line counter. ;Finished. ;Advance CH_ADD. ;Evaluate 'n' ;Confirm end of statement. ;Fetch 'n' ;Open new channel if n=1. ;Otherwise stream 3 reverts to "p" ;channel. ;Finished.

; NEWOUT

;Fetch current stream data.

;Error if stream is already opened. ;Open a "t" channel. ;Save stream data. ;Make HL point to 'address of output ;routine'. ;Change this address to NRSOUT below.

;Use stream 3. ;Restore stream data. ;Attach channel to a stream. ;Use shadow ROM routine with token codes. ;Clear 'leading space' flag. ;Jump forward with control codes lower ;than 20H. ;Jump if not a space. ;Otherwise set 'leading space' flag. ;Save the code temporarily. ;Accept next CR code. ;Restore the code. ;Use "t" channel to print it,

; NRSOUT

NOSPC

;Jump if POSN>WIDTH. ;Otherwise increment POSN.

; NLIN

CALL ENDLI

;Advance to next line.

XOR LD RET ; ENDLI LD LD DEC LD LD AND CALL LD AND CALL LD LD LD INC CP JR LD AND CALL XOR LD RET CP JR LD AND LD LD RET JR CP JR LD LD LD LD INC INC LD CP JR SUB JR JR JR PUSH LD CALL POP DEC JR RET CP RET LD

A (CLRCR),A A,1 (CLRCR),A A (POSN),A A,(CRCD) A NZ,BCHAN_OUT A,(LFEED) A NZ,BCHAN_OUT A,(LPAGE) B,A A,(CLINE) A B NZ,NOFEED A,(FFC) A NZ,BCHAN_OUT A (CLINE),A CR NZ,NOCR A,(CLRCR) A A,1 (CLRCR),A Z ENDLI PRCOMMA NZ,NOCOMMA HL,(POSN) A,(CMMSP) B,A C,0 C L A,L H NC,NLIN B Z,SPLOOP NC,CHKPOS ONESPC BC A,SPACE NRSOUT BC C NZ,SPLOOP TAB NZ DE,PAR1

;Disable next CR code.

;Accept next CR code. ;Clear POSN. ;Fetch CR code. ;Send it unless zero. ;Fetch LF code. ;Send it unless zero.

;Jump if not the end of the page. ;Fetch form feed code. ;Send it unless zero. ;Store new line number. ;Jump if the code is not CR. ;Check current state of CR flag. ;Reset flag to 1 for later. ;Do not do a CR if CLRCR=0. ;Otherwise loop back. ;Jump if not a comma. ;L= POSN H= WIDTH

NOFEED ; CTRL

; NOCR

ONESPC

;Increment counters.

CHKPOS

;Jump if POSN>=WIDTH. ;Jump if reached the correct place, or ;continue if not.

; SPLOOP

;Print 'C' spaces by calling recursively ;this routine.

; NOCOMMA

;Return if not the TAB code. ;Alter output address.

; ALTOP

LD LD ADD LD INC LD RET LD LD JR LD CALL LD LD LD CP JR LD RST LD SUB RET JR NEG LD JR PUSH CALL POP LD JR DEFB DEFB DEFB DEFB DEFB DEFB DEFB DEFB DEFB END

HL,(CURCHL) B,5 HL,BC (HL),E HL (HL),D (TVDATA+1),A DE,PAR2 ALTOP DE,NRSOUT ALTOP A,(TVDATA+1) B,A A,(WIDTH) B NC,GOOD (IY+ERR_NR),0AH ROMERR A,(POSN) B Z NC,GRT C,A SPLOOP BC ENDLI BC C,B SPLOOP 0 80 20 12 60 1 CR 0AH 0

;Store new output address.

; PAR1 ; PAR2

;Store 'n'. ;Output address for next byte. ;Restore initial output address. ;Fetch column 'n'.

OTRNG GOOD

;Error if 'n' is greater than WIDTH.

;Return if already in place. ;Jump if POSN>'n'. ;A=number of spaces required. ;Jump back. ;First advance to next line. ;Jump back to insert the spaces.

GRT

; POSN WIDTH CMMSP FFC LPAGE CLRCR CRCD LFEED CLINE ;

The EQU statements given here define all the labels referred to in the preceding program: ; ;Shadow ROM addresses ; MAIN_ROM EQU 00H CALBAS EQU 10H SH_ERR EQU 20H ROMERR EQU 08H NEWVAR EQU 30H ERR_6 EQU 01F0H NREPORTC EQU 052FH ST_END EQU 05B7H END1 EQU 05C1H EXPT_NUM EQU 061EH NREPORT2 EQU 0663H CHECK_M_2 EQU 066DH NREPORT3 EQU 062DH UNPAGE EQU 0700H NREPORTN EQU 0902H OP_RS_CH EQU 0B13H OP_STREAM EQU 0B4AH TCHAN_ OUT EQU 0C3CH BCHAN_OUT EQU 0C5AH SET_T_MCH EQU 0FE8H DEL_M_BUF EQU 10C4H RWFERR EQU 1132H GET_R_LP EQU 1184H RSSH2 EQU 11A3H G_HD_RC EQU 11A5H GET_M_HD2 EQU 12C4H RES_B_MAP EQU 12FEH DEC_SECT EQU 1312H CHKS_HD_R EQU 1341H CLOSE EQU 1718H SEL_DRIVE EQU 17F7H OUT_M_BUF EQU 1878H GT_M_BLK EQU 18ADH DELAY_BC EQU 18FAH RD_SECTOR EQU 1A4BH RSSH EQU 1AC5H FREESECT EQU 1D38H PRCHAR EQU 1D66H IN_CHK EQU 1E3EH DISP_HEX EQU 1E87H DISPHEX2 EQU IE9EH DISP_CH EQU 1EA9H ; ;Main ROM addresses ; ERROR_1 EQU 08H GET_CHAR EQU 18H NEXT_CHAR EQU 20H BEEPER EQU 03B5H ED_LOOP EQU 0F38H ED_EDIT EQU 0FA9H ED_ERROR EQU 107FH CHAN_OPEN EQU 1601H STR_DATA1 EQU 1727H NEXT_2NUM EQU 1C79H EXPT_1NUM EQU 1C82H EXPT_EXP EQU 1C8CH

FND_INT1 EQU 1E94H FIND_INT2 EQU 1E99H STK_FETCH EQU 2BF1H STACK_A EQU 2D28H PRINT_FP EQU 2DE3H ; ;System variables ; TVDATA EQU 5C0EH ERR_SP EQU 5C3DH E_PPC EQU 5C49H CHURCHL EQU 5C51H SCR_CT EQU 5C8CH VECTOR EQU 5CB7H SECTOR EQU 5CC9H HD_11 EQU 5CEDH ; ;Offsets ; ERR_NR EQU 00H FLAGS EQU 01H CHBYTE EQU 0BH PPC_HI EQU 0CH CHREC EQU 0DH CHFLAG EQU 18H CHDRIV EQU 19H HDNUMB EQU 29H RECFLG EQU 43H RECNUM EQU 44H RECLEN EQU 45H FLAGS3 EQU 7CH ; ;Hook codes ; HOOK31 EQU 31H HOOK32 EQU 32H ; ;Keywords ; BEEP EQU 0D7H CAT EQU 0CFH CODE EQU 0AFH DATA EQU 0E4H LINE EQU 0CAH LPRINT EQU 0E0H POKE EQU 0F4H PRINT EQU 0F5H READ EQU 0E3H RESTORE EQU 0E5H ; ;ASCII ; PRCOMMA EQU 06H CR EQU 0DH TAB EQU 17H SPACE EQU 28H COMMA EQU 2CH

Extending Spectrum BASIC The information given in this section, together with the examples given earlier should be sufficient to allow experienced machine code programmers to write their own new BASIC commands, and incorporate them into the Spectrum's BASIC. Nonprogrammers may still be interested in the details of the Spectrum's BASIC interpreter, and of the ingenious mechanism used to extend it. Please note that this information is primarily designed to give a general explanation of how we have implemented our extended BASIC commands. It is not intended to be a step by step guide to writing new BASIC commands. A fuller explanation of this material is given in The Spectrum Microdrive Book by Dr. Ian Logan, also published by Melbourne House. We should first consider the workings of the Spectrums syntax checking routine. When the syntax checker detects an error in a statement, either as it is entered, or at runtime, a jump is made to the error handling routine. This is done by a RST 8 instruction, followed by a byte which defines the error type. Note the similarities between this and the hook codes discussed earlier. As with the normal hook codes, the RST 8 causes a jump to the Shadow ROM where there is a second examination of the current BASIC statement, to see if it really is an error. It is this 'second chance' that makes it possible for the new BASIC commands provided by the Interface 1 to exist. These new commands are perceived as errors by the Spectrums ROM, so the error routine is called. This passes control to the Interface 1, where the Shadow ROM does its own syntax checking, recognizes the new commands, and performs the appropriate action for each new command, before passing control back to the main Spectrum ROM. Code is provided in the Shadow ROM for checking commands starting with the following keywords: CAT, FORMAT, MOVE, ERASE, OPEN, SAVE, LOAD, VERIFY, MERGE, CLS, and CLEAR. More interesting to us, however, is what happens when a statement 'fails the syntax' (that is, is found to be syntactically incorrect) of both the main ROM and the Shadow ROM. When this happens, the Shadow ROM decides that it really has found an error, and jumps to the address which is stored in a system variable called VECTOR. This address is normally that of a main ROM error handling routine, which will print out an error message and cease execution. However, since the variable VECTOR is in RAM, we can change the value it holds to that of our own syntax checking routine, which will check the statement yet again, to see if it is one of the new commands that we have defined. If the statement is one of our new statements, our syntax checker will call a machine language subroutine which will perform the appropriate actions before returning control to the main ROM, otherwise it will call a routine to print an error message. So, the control routine that must be added to handle a new statement must have two parts: Firstly, there must be a syntax routine which will identify the new command, any parameters that go with it, and the end of the statement it is in. Secondly, there must be a routine which will do the actual work of the command. Finally, of course, the system variable VECTOR must have been redirected to point to the syntax routine, so that 'errors' detected by the Shadow ROM will be redirected to your routine. The syntax of your new command can be anything which will be rejected by the syntax checkers in both the main ROM and the Shadow ROM. Note however, that your command must not start with any of the eleven keywords which are recognised by the Shadow ROM (see above). The execution of the new commands then, is a fairly complex procedure, comprised of the following steps: (i) Command is rejected as being syntactically incorrect by the syntax checking routine in main ROM. Accordingly, an error routine is called, using a RST 8 command. (ii) This causes control to be transfered to the syntax checking routine in the Shadow ROM. (iii) The Shadow ROM syntax checker rejects your statement, and calls the routine whose address is stored in the variable VECTOR. This would normally be the main ROMs "report an error" routine, but since you have previously changed

the contents of VECTOR to point to your own syntax checking routine, it will be your routine that is called. (iv) Your syntax routine checks to see that you have given what it regards as a correct command. Once it decides that you have, it calls a routine which you have written to do whatever task is required of the new BASIC command. (v) Your routine returns control to the BASIC program which is currently running. Although the task of extending the Spectrum's BASIC is quite complex a close study of the examples given in this book should be quite rewarding.

THE SHADOW ROM DISASSEMBLY The restart routines THE 'RETURN TO MAIN ROM' RESTART This restart is used to return to the "main" ROM, starting from the address held in the stack before calling this routine. 0000 0001 0005 MAIN-ROM POP LD JP HL (FLAGS3),+00 0700,UNPAGE Remove return address from the machine stack. Clear FLAGS3. Return to 'main' ROM.

THE 'START' This is the main entry point to the 'shadow' ROM; it is paged in when the Program Counter reaches the address +0008 that is, the address of the main ROM 'ERROR' routine. (The other time in which the "shadow" ROM is paged in is when the Program Counter reaches the address +1708 i.e., the middle of the CLOSE command routine, which is not able to deal with Interface's channels.) 0008 000B 000C 000D ST-SHADOW LD POP PUSH JP HL,(CH-ADD) HL HL 009A,START-2 The first instruction is common to both ROMs. Get the return address (usually points to the error code) Jump forward.

THE 'CALL A MAIN ROM ROUTINE' RESTART This routine allows for a subroutine in the 'main' ROM to be called from the 'shadow' ROM, and can be called by using a RST 10 instruction, followed by the address of the 'main' ROM subroutine. 0010 0013 0014 0015 0017 CALBAS LD POP PUSH JR DEFB (H-L),HL HL DE 0081,CALBAS-2 +FF Save HL into SBRT. Get the return address (points to the address following the RST 10). Save DE temporarily. Jump forward. Unused location.

THE 'TEST IF SYNTAX IS BEING CHECKED' RESTART This corresponds to the 'main' ROM 'SYNTAX-Z' subroutine. A test of bit 7 of FLAGS wHL give the Zero flag set during syntax checking, and reset during execution. 0018 001C 001D CHKSYNTAX BIT 7,(FLAGS) RET DEFB +FF,+FF,+FF Test bit 7 of FLAGS. Finished. Unused locations.

THE 'SHADOW ERROR' RESTART Jumps to the required routine to deal with 'new' report messages. This routine can be called by using a RST 20 instruction followed by the error code (in the range +FF...+16). 0020 0021 0023 0025 SH-ERR RST JR JR DEFB 18,CHKSYNTAX Z,0068,ST-ERROR 003A,TEST-SP +FF,+FF,+FF Jump to ST-ERROR if checking syntax, or to TEST-SP during runtime. Unused locations.

THE 'MAIN ROM ERROR' RESTART Before calling this routine, (ERR-NR) must contain the error code. 0028 002C ROMERR RES JR 3,(TVFLAG) 0040,RMERR-2 Signal 'the mode is to be considered unchanged' jump forward.

002E

DEFB

+FF,+FF

Unused locations.

THE 'CREATE NEW SYSTEM VARIABLES' RESTART The routine is entered at 01F7. 0030 0033 0036 NEWVARS JP DEFB DEFB 01F7,CRT-VARS +FF,+FF,+FF +FF,+FF Jump forward immediately. Unused locations.

THE 'MASKABLE INTERRUPT' ROUTINE While the 'shadow' ROM is paged-in, the keyboard is not scanned. 0038 0039 INT-SERV EI RET Enable interrupts. Return immediately.

THE 'TEST-SP' ROUTINE Check if it is necessary to print the required report message. 003A 003D TEST-SP CALL JP 0077,CHECK-SP 0258,REP-MSG Use 'main' ROM error handler if required. Print the report message.

THE 'MAIN ROM ERROR' ROUTtNE The pressing of BREAK during the loading of 'autorun' programs wHL reset the system; otherwise the error routine continues. 0040 0041 0043 0046 0049 004D 004F 0053 0055 0058 005A 005C 005F 0060 0061 0064 RMERR-2 RST JR CALL CALL BIT JR BIT JR LD CP JR LD PUSH RST DEFB DEFB 18,CHKSYNTAX Z,0068,ST-ERROR 0077,CHECK-SP 17B9,RCL-T-CH 1,(FLAGS3) Z,0068,ST-ERROR 4,(FLAGS3) Z,0068,ST-ERROR A,(ERR-NR) +14 NZ,0068,ST-ERROR HL,+0000 HL 0,MAIN-ROM +FF,+FF,+FF +FF,+FF Jump forward if checking syntax. Use 'main' ROM error handler if required. Reclaim all temporary channels & switch off drive motors. Jump forward if not during the loading of an 'autorun' program. Fetch the error code. Check if attempting to BREAK into the loading of an autorun program. Jump if not. Otherwise reset the system by jumping to the address +0000 in the 'main' ROM. Unused locations.

THE 'NON-MASKABLE INTERRUPT' ROUTINE As with maskable interrupts, there are no service routines. 0066 NMINT-SRV RETN Return immediately.

THE 'ST-ERROR' ROUTINE This routine must be entered with the error code in (ERR-NR), and has the same effect as the 'main' ROM 'ERROR' restart. 0068 006B 006E 0072 0075 0076 STERROR LD LD LD LD PUSH RST HL,(CHADD) (X-PTR),HL SP,(ERR-SP) HL,+16C5 HL 0,MAIN-ROM The address of the character reached by the interpreter is copied into the error pointer. Clear machine stack. Return via 'main' ROM 'SET-STK' routine to the error handler routine.

THE 'CHECK-SP' ROUTINE Use the 'main' ROM error handler only if bit 2 of FLAGS3 is set. 0077 007B 007C 0080 CHECK-SP BIT RET LD RST 2,(FLAGS3) Z SP,(ERR-SP) 0,MAIN-ROM Return. (Normally bit 2 is always reset) Make error handler routine the return addrcs0 & exit.

THE 'CALBAS-2' ROUTINE This routine uses the SBRT area to call the required routine in the 'main' ROM. 0081 0082 0083 0084 0088 0089 008A 008B 008E 008F 0092 0093 0096 0097 CALBAS-2 LD INC LD LD INC EX EX LD PUSh LD PUSH LD PUSH JP E,(HL) HL D,(HL) (5CBD),DE HL (SP),HL DE,HL HL,+0000 HL HL,+0008 HL HL,+5CB9 HL 0700,UNPAGE Fetch the address of the subroutine to be called Into the DE register pair. Use the address with the CALL in SBRT area. Points to the return address. Exchange with initial value of DE register pair (see 0014). Restore initial value of DE. Signal "a 'main' ROM routine has been called". Return address to the shadow ROM is +0008. Call indirectly the SBRT subroutine after having paged-out the 'shadow' ROM.

The control routine This routine is called from ST-SHADOW at 0008 when the shadow ROM is paged-in. It has 3 main tasks: - If the paging of the shadow ROM is the return after a call to a 'main' ROM subroutine, then it returns to the calling routine. - If an Interface's channel has been requested, it jumps to the required 'input' or 'output' routine. - If an error has occurred in the 'main' ROM, a check is made to see if the error code is a 'hook code' (and calls the required routine if it is). If it is not a 'hook code', then it checks to determine whether the error is "Nonsense in BASIC", "Invalid filename", or "Invalid stream". If it is any of these, probably a 'new' command has been used. If so, the routine corresponding to the command is called, otherwise the error is produced by jumping to the address 01F0 (held in the VECTOR system variable - thus altering this address to point a routine in RAM wHL give the possLDility of adding more 'new' commands). In all other cases, the main ROM error handler is used. 009A 009B 009C 009D 009F 00A0 00A1 00A4 START-2 PUSH LD OR JR POP POP LD RET AF A,H L NZ,00A5,START-3 AF HL HL,(H-L) Save A register. Check if the return address is zero. Jump forward if it is not. Otherwise a 'main' ROM routine has been called; clear stack. Restore HL and return to the calling routine.

Now see if an Interface's channel has been requested. 00A5 START-3 PUSH DE Save DE temporarily.

00A6

LD

DE,+15FE

00A9 00AB 00AC 00AE 00AF 00B2 00B3 00B6 00B7 00B8 00B9 00BA 00BB

SBC POP JR POP LD PUSH LD ADD LD INC LD EX JP

HL,DE DE NZ,00BC,START-4 AF HL,+0700 HL HL,+0004 HL,DE E,(HL) HL D,(HL) DE,HL (HL)

If a channel has been requested, this is the return address stored by the CALL 162C inside the 'main' ROM 'CALL-SUB' subroutine. Jump forward if no channels have been requested. Restore A register (character to be transmitted if during 'output'). Make return address the UNPAGE routine. DE now holds (address of routine pointer - 4). HL holds the address of the routine pointer. Fetch the low byte. Fetch the high byte. Move the address to HL. Jump to the appropriate 'input' or 'output' routine.

At this point, the shadow ROM has surely been paged-in by an error in the 'main' ROM. 00BC 00BD 00BF 00C1 00C3 00C5 00C6 00C7 00C8 00C9 00CB 00CE 00D0 00D2 00D6 00D8 00DC 00DE 00E1 00E2 00E4 START-4 RST LD OUT LD OUT POP POP PUSH RST DEFW LD CP JR BIT JR BIT JR LD LD CP JP 30,NEWVARS A,+01 (+F7),A A,+EE (+EF),A AF HL AF 10,CALBAS +007B (ERR-NR),A +FF NZ,00E9,TESTCODE 1,(FLAGS3) Z,00E7,NREPORT-0 7,(PPC-hi) Z,00E7,NREPORT-0 HL,(E-LINE) A,(HL) +F7 Z,0A95,LOAD-RUN Create interface variables if non-existent, then send some signals to the Interface 1. Remove A temporarily. Return address (points to the error code after a RST 8). Save A again. This calls a single 'LD A,(HL)' instruction, so the error code is fetched from the main ROM code. Store the error code. Check if the error is 'OK'. Jump if it is not. This is set when using the 'shadow' ROM for the first time (i.e. after a NEW command). Give an error if not the first time. Give an error also if the line is not in the editing area. Otherwise fetch the command code from the editing area. Check if the command is 'RUN'. Load the 'run' program from Microdrive if so.

'Program finished' 00E7 00E8 NREPORT-0 RST DEFB 20,SH-ERR +FF Call the error handling routine.

The error code in A determines the task to be executed. 00E9 00EB 00EE TEST-CODE SUB JP CP +1B NC,1981,HOOK-CODE +F0 Reduce the range. Jump if it's a hook code, or an invalid code (greater than 0-32). Jump if the error is

00F0 00F2 00F4 00F6 00F8 00FB 00FE 0101 0102 0106 0109 010D 0110 0114 0115 0117

JR CP JR CP JP COPYCHADD LD LD POP BIT JP BIT JP SET RST JR LD

Z,00FB,COPYCHADD +F3 Z,00FB,C0PYCHADD +FC NZ,0028,ROMERR HL,(CH-ADD) (CHADD-),HL AF 5,(FLAGX) NZ,0028,ROMERR 0,(FLAGS3) NZ,0028,ROMERR 0,(FLAGS3) 18,CHKSYNTAX NZ,011B,RUNTIME (PPC-hi),+FF

'Nonsense in BASIC'. Also if 'Invalid filename'. Or 'Invalid stream'. If none of these, use 'main' ROM error handler to signal the error. The character pointer is saved. Clear the stack. Use 'main' ROM error handler also if in INPUT mode, or during execution of a 'new' command. Signal "execution of a 'new' command" Jump if during runtime. Signal "syntax time".

Now a loop is entered to find the line that has produced the error. 011B 011E 0120 0124 0126 0127 0128 012A 012B 012C 012E 0130 0133 0136 0137 RUNTIME LD LD BIT JR PUSH RST DEFW POP RST DEFW JR PROG-LINE LD SC-L-LOOP LD CP JR B,(SUBPPC) C,+00 7,(PPC-hi) Z,0130,PROG-LINE BC 10,CALBAS +19FB BC l0,CALBAS +0018 016F,S-STAT HL,(PROG) A,(PPC-hi) (HL) NC,013B,TEST-LOW Statement counter. Counter of ' " ' characters. Jump forward if the line is in the program area. Save counters. Call main ROM 'E-LINE-NO' (it fetches the number of the line in the editing area, but is actually used to update CH-ADD to the 1st char. in the line). Restore counters. Call GET-CHAR in the main ROM to update HL to the 1st character in the line. Jump forward. Fetch start of program area. Compare the number of the tine to be searched with that of the 'current' line. Jump if the 'current' line no. is less than or equal to that of the line to be searched for.

Nonsense in BASIC. 0139 013A 013B 013C 013E 0141 0142 0144 0145 0146 0147 0148 0149 014B 014C 014E LINE-LEN NREPORT-1 RST DEFB TEST-LOW INC JR LD CP JR INC LD INC LD INC JR ADD JR LD 20,SH-ERR +00 HL NZ,0144,LINE-LEN A,(PPC-lo) (HL) C,0139,NREPORT-1 HL E,(HL) HL D,(HL) HL Z,0l6F,S-STAT HL,DE 0l33,SC-L-LOOP DE,+0006 Call the error handling routine. Points to low byte of line no. Jump if the 'current' line is not the expected one. Compare also the high byte of the line numbers. Give an error if the line dDEs not exist. Increment the pointer. Fetch low byte of the length. Fetch high byte. Points to start of the line. Jump if the line is found, otherwise points to next line. Continue until found. Length of a floating point number.

SKIP-NUN

0151

ADD

HL,DE

Skip the floating point representat.

This loop advances the pointer 'HP until it reaches the start of the statement that has produced the error. 0152 0153 0155 0157 0158 015A 015C 015D 015F 0161 0163 0165 0167 0169 016B 016D 016F 0171 0172 0175 0176 0178 017C EACH-ST LD CP JR INC CP JR DEC CP JR CP JR BIT JR CP JR JR DJNZ DEC LD RST JR BIT JP A,(HL) +0E Z,0l4E,SKIP-NUM HL +22 NZ,015D,CHKEND C +3A Z,0165,CHKEVEN +CB NZ,0l69,CHKEND-L 0,C Z,016F,S-STAT +80 NZ,0152,EACH-ST 0139,NREPORT-1 0152,EACH-ST HL (CH-ADD),HL 18,CHKSYNTAX NZ,01AA,CL-WORK 7,(PPC-hi) Z,01F0,ERR-6 Get a character from line. Is it the 'number' marker ? If so, advance the pointer after the 'number'. Points to next character. Is the character a '"'? Jump it it is not. Decrement counter for each '"' found Is the character a colon? Jump if it is. Jump unless the character is 'THEN'. Check whether the number of quotes found is even (i.e. colon or THEN are out of a string). Jump if the statement is finished. Check whether the line is finished. Continue the loop if not. Give an error (because a wrong number of quotes have been found). Continue with next statement. Now HL holds the start address of the required statement. Update CH-ADD to this address. Jump forward if during runtime. Give an error report if the line is not in the editing area.

CHKEND

CHKEVEN CHKEND-L

S-STAT

The final loop is made during syntax checking, for removing all 6-byte floating point numbers inserted in the line by the 'main' ROM interpreter. 017F 0180 0182 0183 0184 0186 0188 0189 018C 018D 018F 0190 0194 0195 0197 0199 019A 019D 019E 01A0 01A3 01A4 01A5 01A6 01A8 DEC LD INC LD CP JR PUSH LD RST DEFW PUSH LD AND SBC JR EX LD AND SBC LD POP POP LD CP JR HL C,+00 HL A,(HL) +0E NZ,01A5,NEXTNUM BC BC,+0006 10,CALBAS +19E8 HL DE,(CHADD-) A HL,DE NC,01A3,NXT-1 DE,HL BC,+0006 A HL,BC (CHADD-),HL HL BC A,(HL) +0D NZ,0182,RCLM-NUM This balances the INC below. Clear C register. Points to next character. Fetch the character. Jump if the character is not the start of a 'number'. Save the counter. '6' bytes have to be reclaimed. Call RECLAIM-2 in the 'main' ROM to reclaim the 'number'. Save HL (points after the reclaimed 'number' ). Jump forward if the '6' bytes reclaimed were after the character pointed by CHADD-. Otherwise CHADD- needs to be updated. First move it into HL. The character pointed by CHADD- has been moved '6' bytes down. Store the new value. Restore pointer and counter. Jump back into the loop until the line is finished.

RCLN-NUM

NXT-1 NEXTNUM

Now the working areas and the new system variables are cleared. 01AA 01AB 01AD CL-WORK RST DEFW CALL 10,CALBAS +16BF 024D,RES-VARS Clear work areas by calling main ROM 'SET-WORK' routine. Reset some 'new' variables to +FF.

Finally, the command code is fetched from the line, and if it is a 'new' command, the appropriate routine is called. 01B0 01B1 01B3 01B5 01B7 01BA 01BC 01BF 01C1 01C4 01C6 01C9 01CB 01CE 01D0 01D3 01D5 01D8 01DA 01DD 01DF 01E2 01E4 01E7 01E9 RST DEFW SUB CP JP CP JP CP JP CP JP CP JP CP JP CP JP CP JP CP JP CP JP CP JP 10,CALBAS +0020 +CE +01 Z,0486,CAT-SYN +02 Z,04B4,FRMT-SYN +03 Z,053D,MOVE-SYN +04 Z,053l,ERASE-SYN +05 Z,04ED,OPEN-SYN +2A Z,082F,SAVE-SYN +21 Z,0894,LOAD-SYN +08 Z,089E,VERIF-SYN +07 Z,08A8,MRG-SYN +2D Z,0559,CLS#-SYN +2F Z,057F,CLR#-SYN Call NEXT-CHAR in the main ROM to fetch the command code. Reduce range of the code. Ia the command a 'CAT' Check CAT syntax if so. Also for 'FORMAT',... ...'MOVE',... ...'ERASE',... ...'OPEN',... ...'SAVE',... ...'LOAD',... ...'VERIFY',... ...'MERGE',... ...'CLS#',... ...and 'CLEAR#' command.

If the command that has produced the error was none of these, a jump is made to the address held in VECTOR system variable. 01EC 01EF ERR-V LD JP HL,(VECTOR) (HL) Jump to the address held in VECTOR (normally ERR-6 below).

Now the error produced by the main ROM is confirmed. 01F0 01F3 01F6 ERR-6 LD LD RST HL,(CHADD-) (CH-ADD),HL 28,ROMERR Restore initial CH-ADD contents. Give the appropriate error.

THE 'CREATE NEW SYSTEMS VARIABLES' ROUTINE This routine is used to create the 'new' system variables if nonexistent and it is called from the restart 0030. Many variables are initialised to its default value. 01F7 01FA 01FD 01FE 0200 0203 CRT-VARS LD LD ADD JR LD PUSH HL,(CHANS) DE,+A349 HL,DE C,0235,VAR-EXIST HL,+0224 HL Fetch start of channel area. This is FFFF-5CB6. The carry flag is now set it the CHANS area starts after the address +5CB6. Jump if the 'new' variables already exist. Pre-load machine-stack with the address DEFAHLT below.

0204 0207 020A 020D 0210 0213 0216 0219 021A 021C 021D 0220 0221

LD LD LD LD LD LD LD PUSH LD PUSH LD PUSH JP

HL,(STKBOT) (STKEND),HL HL,+5C92 (MEM),HL HL,+5CB5 BC,+003A DE,+0000 DE E,+08 DE DE,+1655 DE 0700,UNPAGE

Clear the calculator stack. Set MEM with the address of MEMBOT area. One location before the new space is needed. Length of space needed. Signal 'a main ROM routine has been called'. Store return address to the shadow ROM. Return address to main ROM is MAKE-ROOM.

After the 'insertion' of the new space has been made, the program continues here with the initialisation of some variables. 0224 0227 022A 022D 022F 0231 0234 DEFAULT LD LD LD LDIR LD LD RET HL,+023A BC,+0013 DE,+5CB6 A,+01 (COPIES),A Base address of 'default values' table. Length of table. Start of 'new variables' area. Store default values. Set COPIES to +01. Finished.

If the new variables already exist, bit 1 of FLAGS3 is reset. 0235 0239 VAR-EXIST RES 1,(FLAGS3) RET 'New variables already exist'. Finished.

THE 'SYSTEM VARIABLES DEFAHLT VALDES' TABLE This table contains the default values of all the 'new' system variables from FLAGS3 to SER-FL. 023A DEFB +02 Default value for FLAGS3 (bit 1 is set to signal that the aheadow ROM has been paged for the first time (see 00D2). Default for VECTOR is ERR-6 address. This short subroutine is the SBRT 'variable', used to call main ROM routines from the shadow ROM. default for BAUD is +000C (i.e. 9600 baud). Default for NTSTAT. Default for IOBORD colour (black). Default for SER-FL.

023B 023D 0240 0243 0246 0247 0249 024A 024B

DEFW LD CALL LD RET DEFW DEFB DEFB DEFW

+01F0 HL,+0000 +0000 (H-L),HL +000C +01 +00 +0000

THE 'RESET NEW SYSTEM VARIABLES' SUBROUTINE Before using the 'new' system variables from NTRESP to HD-11, their values are reset to +FF. 024D 0250 0252 0254 0255 0257 RES-VARS EACH-VAR LD LD LD INC DJNZ RET HL,NTRESP B,+22 (HL),+FF HL 0252,EACH-VAR Points to the 1st variable. The block is made by '34' bytes. Store +FF in all the bytes in the block. Finished.

THE 'SHADOW REPORT PRINTING' ROUTINE

This routine is very similar to 'MAIN-3' (+1303) in the 'main' ROM, but the report message printed is one of the 'shadow' report. 0258 025C 025D 025E 0261 0265 0269 026B 026C 026E 026F 0270 0273 0274 0275 0278 027B 027E 0281 0282 0285 0286 0288 028C 028D 028F 0293 0297 0298 029B 029D 029F 02A0 02A2 02A4 02A5 02A6 02A8 02A9 02AA 02AC 02B0 02B1 02B2 02B5 02B6 REP-MSG LD (FLAGS3),+00 EI HALT CALL 17B9,RCL-T-CH 5,(FLAGS) 1,(FLAGS2) Z,026E,FETCH-ERR 10,CALBAS +0ECD HL A,(HL) (ERR-NR),A A AF HL,+0000 (FLAGX),H (X-PTR-hi),H (DEFADD),HL L (+5C16),HL 10,CALBAS +16B0 5,(FLAGX) 10,CALBAS +0D6E 5,(TVFLAG) 3,(TVFLAG) AF HL,+02B7 B,+04 First clear FLAGS3. Enable interrupts. Accept one interrupt. Reclaim temporary channels and switch off drive motors. Signal 'ready for a new key'. Jump if the printer buffer has not been used, otherwise call COPY-BUFF in the main ROM to empty the buffer. This address points to the error code (after a RST 20). Fetch error code. Store it into ERR-NR. Increment error number. Save the new value. The system variables FLAGX, X-PTR-hi and DEFADD are all set to zero. Now HL holds +0001. Displacement for stream 0 is made +0001 (i.e. reset stream 0 to the "K" channel). Clear work areas, calculator stack by calling SET-MIN. Signal 'editing mode'. Clear lower screen by calling CLS-LOWER. Signal 'the lower screen is to be cleared'. Signal 'the mode is to be considered unchanged'. Restore error number. Base address of 'report messages' table. Hake BC hold a sufficiently high number. Advance HL to the required report message in the table. Fetch character of message. Print it unless reached the 'marker' of next message. Save pointer. Call main ROM 'PRINT-A' restart to print the character. Restore pointer. Point to next character. Continue with next character. Clear machine stack. Ignore also the address +1303 pointed by ERR-SP (that is replaced with the +1349 below). Return to the main ROM in the middle of the 'print report message routine.

RES BIT JR RST DEFW FETCH-ERR POP LD LD INC PUSH LD LD LD LD INC LD RST DEFW RES RST DEFW SET RES POP LD LD CPIR PR-REP-LP LD CP JR PUSH RST DEFW POP INC JR END-PR-MS LD INC INC LD PUSH RST

A,(HL) +20 C,02AC,END-PR-MS HL 10,CALBAS +0010 HL HL 029F,PR-REP-LP SP,(ERR-SP) SP SP HL,+1349 HL 0,MAIN-ROM

THE 'SHADOW' REPORT MESSAGES Before each report there is the correspondent error code, incremented by 1.

02B7 02B8 02C8 02C9 02DA 02DB 02F0 02F1 030A 030B 0317 0318 032C 032D 0343 0344 0350 0351 0367 0368 037C 037D 038E 038F 03A4 03A5 03B8 03B9 03D1 03D2 03E8 03E9 0400 0401 0410 0411 0427 0428 0436 0437 0446 0447 0451 0452 045D 045E 0475 0476 0485

DEFB DEFM DEFB DEFM DEFB DEFM DEFB DEFM DEFB DEFM DEFB DEFM DEFB DEFM DEFB DEFM DEFB DEFM DEFB DEFM DEFB DEFM DEFB DEFM DEFB DEFM DEFB DEFM DEFB DEFM DEFB DEFM DEFB DEFM DEFB DEFM DEFB DEFM DEFB DEFM DEFB DEFM DEFB DEFM DEFB DEFM DEFB DEFM DEFB

+00 "Program finished" +01 "Nonsense in BASIC" +02 "Invalid stream number" +03 "Invalid device expression" +04 "Invalid name" +05 "Invalid drive number" +06 "Invalid station number" +07 "Missing name" +08 "Missing station number" +09 "Missing drive number" +0A "Missing baud rate" +0B "Header mismatch error" +0C "Stream already open" +0D "Writing to a 'read' file" +0E "Reading a 'write' file" +0F "Drive 'write' protected" +10 "Microdrive full" +11 "Microdrive not present" +12 "File not found" +13 "Hook code error" +14 "CODE error" +15 "MERGE error" +16 "Verification has failed" +17 "Wrong file type" +18

The syntax checking routines The routines in this section of the shadow ROM check the syntax of the 'new' commands and call the command routines during runtime. THE 'CAT' COMMAND SYNTAX ROUTINE This routine checks that the command is in the form CAT n, or CAT #s,n then sets S-STR1 and D-STR1 system variables before exiting in syntax time or executing the CAT during runtime. 0486 0489 CAT-SYN LD LD HL,S-STR1 (HL),+02 First make the screen 'current' stream.

048B 048C 048E 0490 0492 0494 0497 0499 049B 049E 04A1 04A3 04A4 04A6 04A9 04AC 04AF

RST DEFW CP JR CP MISSING-D JP CP JR CALL CALL JR RST DEFW CAT-SCRN CALL CALL

10,CALBAS +0020 +0D Z,0494,MISSING-D +3A Z,0683,NREPORT-9 +23 NZ,04A6,CAT-SCRN 064E,EXPT-STRM 05B1,SEPARATOR NZ,04B2,OREPORT-1 10,CALBAS +0020 061E,EXPT-NUM 05B7,ST-END

CALL 066D,CHECK-M-2 JP 1E70,CAT-RUN 20,SH-ERR +00

Advance CH-ADD with a call to 'NEXT-CHAR' in the main ROM. If the line ends here, jump to produce an error. Give an error also if the statement ends with a colon. Jump if after the keyword there is not the 'hash' character. Otherwise evaluate stream number. Give an error if after the stream number there is no separator. Advance CH-ADD to next character. Evaluate drive number. Confirm end of statement and exit during syntax time. Checks that drive number held in D-STR1 is in range. Do the CATalogue. Call the error handling routine.

'Nonsense, in BASIC' 04B2 OREPORT-1 RST 04B3 DEFB

THE 'FORMAT' COMMAND SYNTAX ROUTINE A FORMAT command may have one of the following forms: FORMAT "m";n;"name" -FORMAT "b";n - FORMAT "t";n - FORMAT "n";n. This routine handles all forms and sets N-STR1, L-STR1 and D-STR1 as required before exiting during syntax checking, or executing the command during runtime. 04B4 04B7 04BA 04BC 04BF 04C2 04C5 04C7 04C9 04CB 04CD 04D0 04D3 04D5 04D7 04DA 04DD 04DE 04E1 04E4 04E7 04EA FOR-M NO-FOR-M FRMT-SYN CALL CALL JR CALL CALL LD CP JR CP JR CALL JP NOT-FOR-B CP JR CALL LD AND JP LD JP CALL JP 05F2,EXPT-SPEC 05Bl,SEPARATOR NZ,04BF,NO-FOR-M 062F,EXPT-NAME 05B7,ST-END A,(L-STR1) +54 Z,04CD,FOR-B-T +42 NZ,04D3,NOT-FOR-B 06B0,TEST-BAUD 0AC9,SET-BAUD +4E NZ,04E7,FOR-M 068F,TEST-STAT A,(D-STR1) A Z,069F,NREPORT-6 (NTSTAT),A 05C1,END1 0685,TEST-MNAM 1E75,FOR-RUN Evaluate (string)(separator)(number) and set L-STR1, D-STR1. If there is not a further separator, no filename is to be expected, so jump. Evaluate "name" and set N-STR1. Confirm end of statement and exit during syntax checking. Fetch channel specifier. Jump with FORMAT "T" Test for FORMAT "B" - note that the routine is the same as that of FORMAT "T". Jump with "N" and "M" channels. Check that D-STR1 holds a valid baud rate. Set BAUD variable from D-STR1 value (the actual FORMAT with RS232 link). Jump if not a FORMAT "N" command. Check that D-STR1 holds a valid station number. Give an error if attempting to FORMAT "n"';0 (i.e. with the 'broadcast' specifier). This is the actual FORMAT command with the network. Finished. Check that the various parameters are correct. Do the FORMAT "M" command.

FOR-B-T

ThE 'OPEN' COMMAND SYNTAX ROUTINE This routine deals with OPEN #s;"m";n;"name" - OPEN #s;"b" - OPEN #s;"t" OPEN #s;"n";n commands. All parameters are stored into S-STR1, N-STR1, D-STR1 and L-STR1 variables before exiting during syntax checking or executing the command during runtime. 04ED 04F0 04F3 04F5 04F8 04FB 04FD 0500 0503 0506 0507 0509 050C 050D 050F 0511 0514 0516 0518 051A 051C 051F 0521 0523 0526 0529 052C OPEN-SYN CALL CALL JR CALL CALL JR CALL CALL LD RST DEFW LD AND SBC JR LD CP JR CP JR JP CP JR CALL JP CALL JP 064E,EXPT-STRM 05B1,SEPARATOR NZ,04B2,OREPORT-1 05F2,EXPT-SPEC 05B1,SEPARATOR NZ,0500,NOT-OP-M 062F,EXPT-NAME 05B7,ST-END A,(S-STR1) 10,CALBAS +1727 HL,+0011 A HL,BC C,052F,NREPORT-C A,(L-STR1) +54 Z,051C,OPEN-RS +42 NZ,051F,NOT-OP-B 0B47,OP-RSCHAN +4E NZ,0520,OP-M-C 068F,TEST-STAT 0EA3,OPEN-N-ST 0685,TEST-MNAM 1E7A,OP-RUN Evaluate stream number. Give an error report if the separator has been missed after 'stream no.' Evaluate channel specifier, (separator numeric expression). Jump if no further separator is present. Otherwise evaluate "name". Confirm end of statement and exit if syntax is being checked. Fetch stream number. Call main ROM 'STR-DATA1' routine; on exit, BC holds 'stream data'. In fact, jump if the current stream is already opened with a 'new' channel. Fetch channel specifier. Jump if opening a 't' channel. Jump if not a 'b' channel. Do the OPEN referred to RS232 link. Jump if not a 'n' channel (i.e. with 'm' channel). Check that D-STR1 holds a valid station number. Do the OPEN referred to the network. Check that all parameters are valid. Do the OPEN "M" command. Call the error handling routine.

NOT-OP-M

OPEN-RS NOT-OP-B

OP-M-C

'Stream already open' 052F NREPORT-C RST 20,SH-ERR 0530 DEFB +0B

THE 'ERASE' COMMAND SYNTAX ROUTINE This command has only one form and, thus, this routine is more straightforward than the preceding ones. 0531 0534 0537 053A ERASE-SYN CALL CALL CALL JP 06A3,EXPT-EXPR 05B7,ST-END 0685,TEST-MNAM 1E66,ERASE-RUN Evaluate ("m";n;"name") Confirm end of statement and exit during syntax checking. Check that all parameters are valid. Do the ERASE command.

THE 'MOVE' COMMAND SYNTAX ROUTINE A 'MOVE' command requires two sets of parameters, for the 'input' channel and for the 'output' channel. These parameters are stored respectively in the two areas D-STR1 and D-STR2. 053D 0540 0543 0544 0546 MOVE-SYN CALL CALL RST DEFW CP 06B9,EXPT-EXP1 059F,EX-D-STR 10,CALBAS +0018 +CC Evaluate stream number, or channel expression. Exchange D-STR1 and D-STR2 contents. Call GET-CHAR in the main ROM. The keyword 'TO' must be present

0548 054A 054D 0550 0551 0553 0556

JR CALL CALL RST DEFW CALL JP

NZ,0584,NONSENSE 06B9,EXPT-EXP1 059F,EX-D-STR 10,CALBAS +0018 05B7,ST-END 1E6B,MOVE-RUN

between the two expressions. Evaluate 2nd stream number, or channel expression. Exchange again D-STR areas. Call GET-CHAR in the main ROM. Confirm end of statement and exit during syntax checking. Do the MOVE command,

THE 'CLS#' COMMAND ROUTINE This routine has the tasks of both 'syntax checking' and 'execution'. During runtime, ATTR-P, ATTR-T, MASK-P, MASK-T, P-FLAG and BORDCR system variables are reset to the 'initiaP value (as after a NEW command). 0559 055A 055C 055E 0560 0561 0563 0566 0569 056C 056F 0572 0575 0577 0579 057A 057C CLS#-SYN RST DEFW CP JR RST DEFW CALL LD LD LD LD LD LD OUT RST DEFW JP 10,CALBAS +0020 +23 NZ,0584,NONSENSE 10,CALBAS +0020 05B7,ST-END HL,+0038 (ATTR-P),HL (ATTR-T),HL (BORDCR),L (P-FLAG),H A,+07 (+FE),A 10,CALBAS +0D6B 05Cl,END1 Advance CH-ADD after the keyword CLS. The character must be a '#'. Advance CH-ADD again. Confirm end of statement and exii during syntax checking. +38 is the attribute byte. Store +38 into ATTR-P, clear MASK-P. Store +38 into ATTR-T, clear MASK-T. Store +38 also for lower screen attribute. Clear P-FLAG. Set white border. Call main ROM 'CLS' routine. Finished.

THE 'CLEAR#' COMMAND ROUTINE As in the previous routine, this routine both 'checks' and 'executes' the command. All streams are closed in turn, with bit 1 of FLAGS3 set to signal that the remaining buffer contents are to be erased (no data is sent, as opposed to the case of a CLOSE# command). 057F 0580 0582 0584 0587 0588 058A 058D 058E 058F 0593 0596 0597 0598 059A 059C CLR#-SYN NONSENSE RST DEFW CP JP RST DEFW CALL 10,CALBAS +0020 +23 NZ,04B2,OREPORT-1 10,CALBAS +0020 05B7,ST-END A AF 1,(FLAGS3) 1718,CLOSE AF A +10 C,058E,ALL-STRMS 05C1,END1 Advance CH-ADD. The character must be a '#'. Advance CH-ADD again. Confirm end of statement and exit during syntax checking. Start with stream 0. Save stream number. Signal 'CLEAR# command'. Close the current stream. Restore stream number. Each stream in turn is examined. Continue until all streams 0..15 have been closed. Finished.

XOR ALL-STRMS PUSH SET CALL POP INC CP JR JP

THE 'EXCHANGE FILE SPECIFIERS' SUBROUTINE This subroutine exchanges the contents of D-STR1 area with those of the D-STR2 area and vice-versa.

059F 05A2 05A5 05A7 05A8 05A9 05AA 05AB 05AC 05AD 05AE 05B0

EX-D-STR

LD LD LD ALL-BYTES LD LD LD LD LD INC INC DJNZ RET

HL,+5CD6 DE,+5CDE B,+08 A,(DE) C,(HL) (HL),A A,C (DE),A HL DE 05A7,ALL-BYTES

Start of first area. Start of 2nd area. Both areas are 8 bytes in length. Fetch a byte from D-STR2. Fetch a byte from D-STR1. Store into D-STR1 the byte coming from D-STR2. Byte from D-STR1. Store it into D-STRZ. Advance the pointers. Continue until the areas have been exchanged. Finished.

THE 'SEPARATOR' SUBROUTINE This short subroutine is called several times to see if the character held in the accumulator is a valid separator (i.e. a comma or a semicolon). A return with the Zero flag reset is made if the character is not a separator. 05B1 05B3 05B4 05B6 SEPARATOR CP RET CP RET +2C Z +3B Is the Return Is the Return character with zero character with zero a comma? flag set if so. a semicolon? flag set if so.

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 report is given if there are some characters left in the line. A return is made to the calling routine only during runtime, otherwise the control returns to the 'main' ROM interpreter. 05B7 05B9 05BB 05BD 05BF 05C0 ST-END CP JR CP JR +0D Z,05BF,TEST-RET +3A NZ,0584,NONSENSE Jump if the statement ends with ENTER. Give an error if character is not a colon (i.e. the statement is not finished). Return only during runtime, otherwise continue below.

TEST-RET

RST 18,CHKSYNTAX RET NZ

THE 'RETURN TO THE MAIN INTERPRETER' ROUTINE The control returns to the main interpreter, when a 'new' command has been checked or executed, for the interpretation of the next statement. 05C1 05C5 05C9 05CC 05CD 05CF 05D1 05D3 05D4 05D6 05D8 05DA 05DB 05DD 05E0 05E1 END1 LD LD LD RST JR LD IN RRA JR LD IN RRA JR RETAD-RUN LD RETAD-SYN PUSH RST SP,(ERR-SP) Clear machine stack. (ERR-NR),+FF Clear error code. HL,+1BF4 Return address to main ROM is 18,CHKSYNTAX STMT-NEXT if syntax being checked. Z,05E0,RETAD-RUN-SYN A,+7F Give, an error if BREAK is pressed A,(+FE) during runtime. C,05DD,RETAD-RUN A,+FE A,(+FE) NC,05E2,BREAK-PGM HL,+1B7D HL 0,MAIN-ROM (ERR-NR),+14 Return address during runtime is STMT-R-1. Save the return address. Return to the main ROM interpreter. Store the error code and call

'BREAK into program' 05E2 BREAK-PGM LD

05E6

RST

28,ROMERR

the error handling routine.

THE 'EVALUATE STRING EXPRESSION' SUBROUTINE A call to the main ROM 'EXPT-EXP' (class-9A) subroutine is made, to evaluate a string expression. During runtime the parameters of the string (start and length) are returned in the DE and BC register pairs. 05E7 05E8 05EA 05EB 05EC 05ED 05EE 05F0 05F1 EXPT-STR RST 10,CALBAS DEFW +1C8C RST 18,CHKSYNTAX RET Z PUSH AF RST DEFW POP RET 10,CALBAS +2BF1 AF Call 'EXPT-EXP' in the main ROM. Return if syntax is being checked Save the zero flag and the character following the string. Call 'STK-FETCH' in the main ROM to fetch the parameters. Zero flag reset to signal 'runtime' Finished.

THE 'EVALUATE CHANNEL SPECIFIER' SUBROUTINE The subroutine is entered at EXPT-SPEC or at EXP-SPEC2 depending upon whether or not the character pointer is to be updated to the next character. A single character string is evaluated, and its upper case ASCII value is stored into L-STR1 during runtime. If a separator is present after the single character string, then the routine continues into EXPT-NUM. 05F2 05F3 05F5 05F8 05FA 05FB 05FC 05FD 05FE 0600 0601 0602 0604 0606 0608 060B 060C 060E 060F 0611 0612 0614 0615 0618 061B 061C EXPT-SPEC RST 10,CALBAS DEFW +0020 EXP-SPEC2 CALL 95E7,EXPT-STR JR Z,060C,TEST-NEXT PUSH AF LD DEC OR JR LD RST DEFW JR AND LD POP TEST-NEXT CP RET CP RET CP RET CALL JP RST DEFW A,C A B NZ,062D,NREPORT-3 A,(DE) 10,CALBAS +2C8D NC,062D,NREPORT-3 +DF (L-STR1),A AF +0D Z +3A Z +A5 NC 05B1,SEPARATOR NZ,04B2,OREPORT-1 10,CALBAS +0020 Advance CH-ADD. Evaluate string expression. Jump if syntax is being checked. Save the character following the string. 'A' holds the low byte of the string length. Jump if there is more than one character in the string (also if the string is null). Fetch the channel specifier. Call 'ALPHA' to see whethe nt isi valid letter. Jump if it is not a valid letter Make the letter upper case. Store the channel specifier. The 'next character' is restored. Return if it is ENTER, or a colon,

Return also with a keyword. Otherwise a separator must be found. Give an error if not found. A numeric expression (i.e. drive no.,station no., baud rate) is expected. But first advance CH-ADD past the separator.

THE 'EVALUATE NUMERIC EXPRESSION' SUBROUTINE This subroutine is used whenever a single numeric expression is to be evaluated. The result is returned during runtime into the BC register pair and into the D-STR1 system variable. 061E EXPT-NUM RST 10,CALBAS Call EXPT-NUM in the main ROM to

061F 0621 0622 0623 0624 0625 0627 062B 062C

DEFW RST RET PUSH RST DEFW LD POP RET

+1C82 18,CHKSYNTAX Z AF 10,CALBAS +1E99 (D-STR1),BC AF

evaluate the expression. Return if syntax is being checked. Save character following the expression and zero flag. Call FIND-INT2 to fetch the value from calculator stack. Store the value. Restore character and zero flag. Finished.

'Invalid device expression' 062D 062E NREPORT-3 RST DEFB 20,SH-ERR +02 Call the error handling routine.

THE 'EVALUATE FILENAME' SUBROUTiNE A string expression is evaluated and, provided that the length is within the range 1..10 characters, the 'length' and the 'start' of that string are stored into N-STR1 and (N-STR1 + 2). 062F 0630 0632 0635 0636 0637 0638 0639 063B 063E 0640 0642 0646 064A 064B EXPT-NAME RST DEFW CALL RET PUSH LD OR JR LD SBC JR LD LD POP RET 10,CALBAS +0020 05E7,EXPT-STR Z AF A,C B Z,064C,NREPORT-4 HL,+000A HL,BC C,064C,NREPORT-4 (N-STR1),BC (N-STR1+2),DE AF Advance CH-ADD. Evaluate the string. Return if syntax is being checked. Save zero flag and 'next character'. Give an error with null string. Give the error also if 'length' exceeds ten characters. Store the 'length'. Store the 'start'. Restore A and zero flag. Finished.

'Invalid name' 064C 064D NREPORT-4 RST DEFB 20,SH-ERR +03 Call the error handling routine.

THE 'EVALUATE STREAM NUMBER' SUBROUTINE A single numeric expression is evaluated and the result, in the range 0..15, is stored into S-STR1 variable during runtime. 064E 064F 0651 0652 0654 0655 0656 0657 0658 065A 065C 065E 0661 0662 EXPT-STRM RST DEFW RST DEFW RST RET PUSH RST DEFW CP JR LD POP RET 10,CALBAS +0020 10,CALBAS +1C82 18,CHKSYNTAX Z AF 10,CALBAS +1E99 +10 NC,0663,NREPORT-2 (S-STR1),A AF Advance CH-ADD. Call EXPT-1NUM to evaluate a numeric expression. Return if syntax is being checked. Save 'next character' and zero flag. Call FIND-INT1 to fetch the value. Test for numbers greater than 15. Store stream number. Restore A and zero flag. Finished.

'Invalid stream number'

0663 0664

NREPORT-2 RST DEFB

20,SH-ERR +01

Call the error handling routine.

THE 'CHECK "M" PARAMETERS' SUBROUTINE A return to the calling routine is made only if L-STR1 denotes the Microdrive device being used, and if D-STR1 holds a valid drive number. 0665 0668 066A 066D 0671 0672 0673 0675 0676 0677 0678 067A 067B 067E 0680 CHECK-M LD CP JP CHECK-M-2 LD LD OR JR INC LD OR JR DEC LD SBC RET A,(L-STR1) +4D NZ,062D,NREPORT-3 DE,(D-STR1) A,E D Z,0681,NREPORT-5 DE A,E D Z,0683,NREPORT-9 DE HL,+0008 HL,DE NC Fetch channel specifier. Is it "M"? Report the error if it is not. Fetch drive number. Report an error if zero. Test against +FFFF. Report an error if no drive number has been evaluated. Balance the 'INC' above. Is drive number within range 1..8? Return if in range.

'Invalid drive number' 0681 0682 NREPORT-5 RST DEFB 20,SH-ERR +04 Call the error handling routine.

'Missing drive number' 0683 0684 NREPORT-9 RST DEFB 20,SH-ERR +08 Call the error handling routine.

THE 'CHECK "M" PARAMETERS AND FILENAME' SUBROUTINE This subroutine adds to the tests performed by the previous one, also a check to N-STR1-hi, that holds +FF when no filename has been evaluated. 0685 0688 068B 068C TEST-MNAM CALL LD AND RET 0665,CHECK-M A,(N-STR1-hi) A Z Check "m" parameters. Fetch high byte of name length. Is it 0? Return if so.

'Missing name' 068D 068E RST DEFB 20,SH-ERR +06 Call the error handling routine.

THE 'CHECK STATION NUMBER' SUBROUTINE A return to the calling routine is made only if D-STR1 holds a valid station number in the range 0..64. 068F 0693 0694 0695 0696 0698 0699 069C 069E TEST-STAT LD INC LD OR JR DEC LD SBC RET DE,(D-STR1) DE A,E D Z,06A1,NREPORT-8 DE HL,+0040 HL,DE NC Fetch station number. Test against +FFFF. No station number has been evaluated, so give an error Balance the 'INC' above. Return only if the value is no greater than 64 decimal.

'Invalid station number'

069F 06A0

RST DEFB

20,SH-ERR +05

Call the error handling routine.

'Missing station number' 06A1 06A2 NREPORT-8 RST DEFB 20,SH-ERR +07 Call the error handling routine.

THE 'EVALUATE "X";N;"NAME"' SUBROUTINE A call to the subroutine EXPT-SPEC will evaluate the '"X";N', while the subroutine EXPT-NAME is used to evaluate the filename. A separator must be found between them. 06A3 06A6 06A9 06AC 06AF EXPT-EXPR CALL CALL JP CALL RET 05F2,EXPT-SPEC 05B1,SEPARATOR NZ,04B2,OREPORT-1 062F,EXPT-NAME Evaluate channel specifier and drive number. A separator must be present. Evaluate the filename. Finished.

THE 'CHECK BAUD RATE' SUBROUTINE This routine simply checks that D-STR1 holds a valid baud rate (i.e. it is not set to +FFFF). Any value below +FFFF is accepted (but later rounded to the nearest 'standard' value). 06B0 06B3 06B4 06B5 06B6 TEST-BAUD LD INC LD OR RET HL,(D-STR1) HL A,L H NZ Fetch baud rate. Accept any value except +FFFF.

'Missing baud rate' 06B7 06B8 RST DEFB 20,SH-ERR +09 Call the error handling routine.

THE 'EVALUATE STREAM OR EXPRESSION' SUBROUTINE This subroutine is used to deck the syntax of the MOVE Command. If the 'current' character is a hash sign (#), then a stream number is evaluated and stored into S-STR1 during runtime. Otherwise a channel expression like '"x";n ["name"]' is evaluated, and N-STR1, D-STR1 and L-STR1 are set as required and, if the channel specifier is "M" or "N", the parameters are checked to be in range. 06B9 06BA 06BC 06BE 06C1 06C4 06C7 06C9 06CC 06CD 06CE 06D1 06D3 06D4 06D6 06D7 06D9 06DC EXPT-EXP1 RST DEFW CP JP CALL CALL JR CALL ENDHERE RST RET LD CP RET CP RET CP JP JP 10,CALBAS +0020 +23 Z,064E,EXPT-STRM 05F5,EXP-SPEC2 05B1,SEPARATOR NZ,06CC,ENDHERE 062F,EXPT-NAME 18,CHKSYNTAX Z A,(LSTR-1) +54 Z +42 Z +4E Z,068F,TEST-STAT 0685,TEST-MNAM Advance CH-ADD. Is the present code an hash sign? Evaluate stream number if so, otherwise evaluate "x";n. Jump if there is no further separator. Otherwise deal with "filename". Return if syntax is being checked. Fetch channel specifier. Return if It is "T". Return if it is "B". But check station no. if it is "N". Otherwise check "m" parameters.

06DF...06FF

Unused locations (all set to .FF).

THE 'UNPAGE' SUBROUTINE This subroutine is actually made by a single RET instruction, but the hardware detects that the Program Counter reaches the address +0700 and pages-in the 'main' ROM. 0700 UNPAGE RET RETurn to 'main' ROM.

THE 'EVALUATE PARAMETERS' SUBROUTINE This very important subroutine is called to evaluate the syntax of the SAVE, LOAD, VERIFY and MERGE commands referred to the 'new' channels. The subroutine is entered with CH-ADD pointing to the command code; on exit during runtime the variables D-STR1, L-STR1, N-STR1, HD-00, HD-0B, HD-0D, HD-0F, HD-11 are properly set. 0701 0702 0704 0706 0708 0709 070B 070E 0711 0713 0716 0717 071A 071C 071E 0722 0723 0725 0727 0729 072B 072D 072F 0731 0733 0735 0737 0739 EXPT-PRMS RST DEFW CP JR RST DEFW CALL CALL JR CALL NO-NAME PUSH LD CP JR SET NOT-NET POP CP JR CP JR CP JR CP JR CP JR CP JP 10,CALBAS +0020 +2A NZ,073C,OREP-1-2 10,CALBAS +0020 05F5,EXP-SPEC2 05B1,SEPARATOR NZ,0716,NO-NAME 062F,EXPT-NAME AF A,(L-STR1) +4E NZ,0122,NOT-NET 3,(FLAGS3) AF +0D Z,0750,END-EXPT +3A Z,0750,END-EXPT +AA Z,0771,SCREEN$ +AF Z,0789,CODE +CA Z,973E,LINE +E4 Z,07D2,DATA The next character is fetched from the line. It must be a '*'. Give an error if not a '*'. Advance CH-ADD past the '*'. Evaluate "x";n. Check that the separator dDEs exist. Jump if no name is to be expected. Evaluate "filename". The next character is saved. Fetch channel specifier. Jump if the channel is not "N", otherwise signal "networking". Restore 'next character'. Jump if the statement ends with ENTER, or with a colon. Jump if the statement continues with SCREEN$. Jump with CODE. Jump with LINE. Jump with DATA, otherwise give an error report.

'Nonsense in BASIC' 073C 073D OREP-1-2 RST DEFB 20,SH-ERR +00 Call the error handling routine.

Now deal with LINE. 073E 073F 0741 0742 0744 0747 0748 074A 074E LINE RST DEFW RST DEFW CALL RST DEFW LD JR 10,CALBAS +0020 10,CALBAS +1C82 05B7,ST-END 10,CALBAS +1E99 (HD-11),BC 0753,PROG Advance CH-ADD. Call EXPT-1NUM to evaluate the autostart line number. Confirm end of statement, and exit during syntax checking. Fetch the autostart line number from the calculator stack. Store autostart line number. Jump forward.

If there are no parameters (i.e. BASIC program), the syntax checking ends here.

0750 0753 0754 0757 075A 075E 0762 0763 0765 0768 076B 076D 0770

END-EXPT PROG

CALL XOR LD LD LD LD SCF SBC LD LD SBC LD RET

05B7,ST-END A (HD-00),A HL,(E-LINE) DE,(PROG) (HD-0D),DE HL,DE (HD-0B),HL HL,(VARS) HL,DE (HD-0F),HL

Confirm end of statement and exit during syntax checking. Store 0 into HD-00 (signalling 'program' file type). Address past the last location of variables area. The 'start' of data is fetched from PROG, and stored into HD-0D. This calculates ((E-LINE)-(PROG))-1, i.e. the length of the program and its variables. The 'length' is stored. Now calculate (VARS)-(PROG), i.e. the length of the program only. Store it into HD-0F. Finished.

If the token is SCREEN$, the parameters are entered directly into the system variables. 0771 0772 0774 0777 077A 077D 0780 0783 0785 0788 SCREEN$ RST DEFW CALL LD LD LD LD LD LD RET 10,CALBAS +0020 05B7,ST-END HL,+1B00 (HD-0B),HL HL,+4000 (HD-0D),HL A,+03 (HD-00),A Advance CH-ADD. Confirm end of statement and exit during syntax checking. The 'length' of the display file and attributes is stored. The start address of the display file. Signal 'bytes' file type. Finished.

Now deal with CODE. 0789 078A 078C 078E 0790 0792 0794 0798 079A 079B 079D 079F 07A0 07A2 07A5 07A7 07AB 07AD 07AE 07B0 07B2 07B3 07B5 07B6 07B8 07B9 CODE RST DEFW CP JR CP JR BIT JR RST DEFW 10,CALBAS +0020 +0D Z,079A,DEFLT-0 +3A NZ,PAR-1 5,(FLAGS3) NZ,OREP-1-2 10,CALBAS +1CE6 07A7,TEST-SAVE 10,CALBAS +1C82 0581,SEPARATOR Z,0782,PAR-2 5,(FLAGS3) NZ,073C,OREP-l-2 10,CALBAS +1CE6 07B8,END-CODE 10,CALBAS +0020 10,CALBAS +1CB2 10,CALBAS +0018 Advance CH-ADD. If there ate no further parameters, jump to use '0' as default value. Jump it there are parameters to be evaluated (i.e. the next character is not a colon). Give an error if SAVE "name"CODE is encountered by itself A call to main ROM routine 'USE-ZERO' is made to use a value of zero as default Jump forward. Otherwise call EXPT-1NUM to use the specified value. Jump if a separator is present. Give an error if a parameter has been missed in a SAVE name CODE command. Use zero also for the 2nd parameter, Jump forward. Advance CH-ADD. Evaluate the 2nd parameter by calling EXPT-1NUM. Call GET-CHAR to fetch the 'last' character in the statement.

DEFLT-0

JR RST DEFW CALL JR TEST-SAVE BIT JR RST DEFW JR PAR-2 RST DEFW RST DEFW END-CODE RST DEFW PAR-1

07BB 07BE 07BF 07C1 07C5 07C6 07C8 07CC 07CE 07D1

CALL RST DEFW LD RST DEFW LD LD LD RET

05B7,ST-END 10,CALBAS +1E99 (HD-0B),BC 10,CALBAS +1E99 (HD-0D),BC A,+03 (HD-00),A

Confirm end of statement and exit if syntax is being checked. Fetch the "length" from the calculator stack and store it. Fetch the "start" from the calculator stack and store it. Signal 'bytes' file type. Finished.

Finally the routine to evaluate DATA parameters. 07D2 07D6 DATA BIT JR 6,(FLAGS3) Z,07DA,NO-M-ARR Jump unless attempting to MERGE an array.

'MERGE error' 07D8 07D9 07DA 07DB 07DD 07DE 07E0 07E2 07E4 07E7 07EB 07ED 07F1 07F2 NO-M-ARR RST DEFB RST DEFW RST DEFW SET JR LD BIT JR LD RST JR 20,SH-ERR +14 10,CALBAS +0020 10,CALBAS +28B2 7,C NC,07F2,EXISTING HL,+0000 4,(FLAGS3) NZ,080E,LD-DATA (ERR-NR),+01 28,ROMERR Z,07F6,G-TYPE Call the error handling routine. Advance CH-ADD to point to the array name. Call LOOK-VARS to look for the array name. Set bit 7 of array name. Jump if handling an existing array. Signal 'using a new array'. Jump forward if LOADing the array. Give the error report 'Variable not found' if trying to SAVE a nonexistent array. Continue only when handling a numeric or alphanumeric array,

EXISTING

NOTE: This test fails to exclude simple strings, but the 'bug' (present in the main ROM) is corrected at 07FF. 07F4 07F5 07F6 07F7 07F9 07FD 07FF 0801 0803 0804 0805 0808 0809 080A 080D 080E 080F 0812 0814 0816 0818 NONS-BSC G-TYPE RST DEFB RST JR BIT JR BIT JR INC LD LD INC LD LD INC LD LD LD BIT JR INC 20,SH-ERR +00 18,CHKSYNTAX Z,081C,END-DATA 5,(FLAGS3) Z,0803,VR-DATA 7,(HL) Z,07F4,NONS-BSC HL A,(HL) (HD-0B-lo),A HL A,(HL) (HD-0B-hi),A HL A,C (HD-0F-lo),A A,+01 6,C Z,0819,NUM-ARR A 'Nonsense in BASIC' Jump forward if syntax is being checked. Jump forward if not during a SAVE command. Give an error if trying to SAVE a simple string. Point to the 'length' of the array. Fetch low byte. Store it. Point to high byte of 'length'. Fetch high byte. Store it. Advance to the start of the array. Store array name into HD-0F. Signal 'numeric array'. Jump if really a numeric array. Otherwise A=2 to signal

VR-DATA

LD-DATA

0819 081C 081D 081E 0820 0822 0824 0825 0827 082A 082E

NUM-ARR END-DATA

LD EX RST DEFW CP JR RST DEFW CALL LD RET

(HD-00),A DE,HL 10,CALBAS +0020 +29 NZ,07F4,NONS-BSC 10,CALBAS +0020 05B7,ST-END (HD-0D),DE

'alphanumeric array'. Store file type. DE holds the 'start' of the array (or +0000 with 'NEW' array to be loaded). Advance CH-ADD. Check that the ')' dDEs exist. Report an error if not. Advance CH-ADD. Confirm end of statement and exit during syntax checking. Store "start" of the array. Finished.

THE 'SAVE' COMMAND SYNTAX ROUTINE The actual saving is handled directly with "B" and "N" channels, or by SAVE-RUN if the 'N" channel is being used. 082F 0833 0836 0839 083B 083D 083F 0841 0844 0847 0849 SAVE-SYN SET CALL LD CP JR CP JR CALL CALL JR CALL JP 5,(FLAGS3) 0701,EXPT-PRMS A,(L-STR1) +42 Z,084F,SA-HEADER +4E NZ,0849,SAVE-M 068F,TEST-STAT 0EA9,OP-TEMP-N 084F,SA-HEADER 0685,TEST-MNAM 1E7F,SAVE-RUN Signal "Saving". Check syntax and set variables. Fetch channel specifier. Jump with "B" channel being used. Jump with other than "N" channel being used (i.e. with "M" channel). Check station number. Open a temporary "N" channel. Jump forward. Check "M" parameters. Jump forward.

SAVE-M

Now a loop is entered to SAVE to the "N" or "B" devices the 'header', i.e. the nine bytes taken from the system variables HD-00 to HD-11. 084F 0851 0854 0857 0858 085A 085D 0861 0863 0866 0868 086A 086D 086E 0872 0873 0874 0876 0879 087A 087B 087D SA-HEADER LD LD HD-LOOP CALL INC DJNZ LD BIT JR LD CP JR LD ADD SABLOCK LD SA-BLK-LP LD OR JR CALL DEC INC JR S-BLK-END JP B,+09 HL,+5CE6 0880,SA-BYTE HL 0854,HD-LOOP HL,(HD-0D) 3,(FLAGS3) Z,086E,SA-BLOCK A,(HD-00) +03 NC,086E,SA-BLOCK DE,+0014 HL,DE BC,(HD-0B) A,C B Z,087D,S-BLK-END 0880,SA-BYTE BC HL 0872,SA-BLK-LP 0988,TST-MR-M Nine bytes are to be saved. Start of HD variables. Save the byte pointed by HL. Each header byte in turn is saved. Fetch 'start of data block'. Jump if "B" channel is being used. Jump also if saving a block of memory (i.e. SAVE..CODE). Otherwise the data to be saved have been moved up by '276' bytes after the insertion of the "N" channel. Fetch the 'length' of the block. Jump forward when the counter has reached zero. Send the byte pointed by HL. Decrement 'length'. Point to next byte. Jump back until the whole block has been saved. Jump forward to send the 'end of file' block (only with Network).

THE 'SAVE A BYTE TO NETWORK OR RS232 LINK' SUBROUTINE The byte pointed by the HL register pair is fetched and sent by using the "B" or the "N" channel output routing, depending upon the state of bit 3 of the FLAGS3 variable. 0880 0881 0882 0886 0887 0889 088C 088E 0891 0892 0893 SA-BYTE PUSH PUSH BIT LD JR CALL JR CALL POP POP RET HL BC 3,(FLAGS3) A,(HL) NZ,088E,SA-NET 0C5A,BCHAN-OUT 0891,SA-B-END 0D6C,NCHAN-OUT BC HL Save HL and BC register pairs. Test 'networking' bit. Fetch the byte to be saved. Jump if network is being used. Otherwise send the byte through the RS232 link. Exit. Use "N" channel output routine to send the byte. Restore registers and return.

SA-NET SA-B-END

TUE 'LOAD' COMMAND SYNTAX ROUTINE The syntax is checked with a single call to the EXPT-PRMS subroutine. 0894 0898 089B LOAD-SYN SET CALL JP 4,(FLAGS3) 0701,EXPT-PRMS 08AF,LD-VF-MR Signal 'doing a LOAD command'. Check syntax and set variables. Do the LOAD.

THE 'VERIFY' COMMAND SYNTAX ROUTINE Again the syntax checking is handled by EXPT-PRMS. 089E VERIF-SYN 08A2 08A5 SET CALL JP 7,(FLAGS3) 0701,EXPT-PRMS O8AF,LD-VF-MR Signal 'doing a VERIFY command'. Check syntax and set variables. Do the VERIFY.

THE 'MERGE' COMMAND SYNTAX ROUTINE The syntax is handled by EXFT-PRMS the routine continues into the LOAD-VERIFYMERGE routine below. 08A8 08AC MRG-SYN SET CALL 6,(FLAGS3) 0701,EXPT-PRMS Signal 'doing a MERGE command'. Check syntax and set variables.

THE "LOAD-VERIFY-MERGE' COMMANDS ROUTINE The action to be performed depends on the state of bits 4,6,7 of FLAGS3. The 'old' header Is expected to be in the HD area, and is immediately transferred into the D-STR2 area, while the 'new' header is loaded into the HD area. 08AF 08B2 08B5 08B8 08BA 08BD 08BF 08C1 08C3 08C5 08C8 08CB 08CD 08D0 08D3 TS-L-NET TS-L-RS LD-VF-MR LD LD LD LDIR LD CP JR CP JR CALL CALL JR CALL CALL LD A,(L-STR1) +4E Z,08CD,TS-L-NET +42 Z,08D3,TS-L-RS 0685,TEST-MNAM 1580,F-M-HEAD 08F2,TEST-TYPE 068F,TEST-STAT 0EA9,OP-TEMP-N HL,+5CE6 HL,+5CE6 DE,+5CDE BC,+0007 Start of HD area. Start of D-STR2 area. There are '7' bytes to be copied (HD-11 is not affected). Copy the 'old' header into the D-STR2 area. Fetch channel specifier. Jump if network is being used. Jump if RS232 link is being used. Check "M" parameters. Fetch first nine bytes from cartridge and store into HD area. Jump forward. Check station number. Open a temporary "N" channel. Start of HD area.

08D6 08D8 08D9 08DA 08DD 08E0 08E3 08E5 08E7 08EA 08EC 08ED 08EE 08EF 08F0 08F2 08F5 08F6 08F9 08FA 08FC 08FE 0900

LD LD-HEADER PUSH PUSH BIT JR LD-HD-NET CALL JR JR LD-HD-RS CALL JR LD-HDR-2 POP POP LD INC DJNZ TEST-TYPE LD LD LD CP JR CP JR JR

B,+09 HL BC 3,(FLAGS3) Z,08E7,LD-HD-RS 0D12,NCHAN-IN NC,08E0,LD-HD-NET 08EC,LD-HDR-2 0B81,BCHAN-IN NC,08E7,LD-HD-RS BC HL (HL),A HL 08D8,LD-HEADER A,(5CDE) B,A A,(HD-00) B NZ,0902,NREPORT-N +03 Z,0911,T-H-CODE C,0904,TST-MERGE

Nine bytes are now expected. Save HL and BC register pairs. Jump if using RS232 link. Fetch a byte from "N" channel. Repeat until the byte is acceptable. Jump forward. Fetch a byte from RS232 link. Repeat until the byte is acceptable. Restore registers. Store the byte into the HO area. Advance the pointer. Go around the loop again. Fetch 'old' type from D-STR2 area and store into B register. Fetch 'new' file type. Compare with the 'old' one. Give an error if it does not match. Jump if handling a block of bytes. Jump with other types (but refuse types greater than 3).

'Wrong file type' 0902 0903 0904 0908 090A 090E NREPORT-N RST DEFB TST-MERGE BIT JR BIT JP 20,SH-ERR +16 6,(FLAGS3) NZ,0967,MERGE-BLK 7,(FLAGS3) Z,09A3,LD-PR-AR Call the error handling routine. Jump if doing a MERGE. Jump if not doing a VERIFY (i.e. doing a LOAD).

Now deal with loading of files of type 3 (i.e. CODE and SCREENS), or verifying of all file types. 0911 0915 T-M-CODE BIT JR 6,(FLAGS3) Z,0919,LD-BLOCK Allow for the loading or verifying, but not for the mergeing of a 'CODE' block. Call the error handling routine. Get 'old' length from D-STR2. Get 'new' length. Jump forward if 'old' length is 0 (i.e. not specified in the command). Accept the 'old' length if it is greater than or equal to the 'new' one. But give an error if attempting to LOAD or VERIFY a larger block than has been requested. Call the error handling routine.

'MERGE error' 0917 0918 0919 091C 0920 0921 0922 0924 0926 0928 092C 'CODE error' 092E 092F RST DEFB 20,SH-ERR +13 LD-BLOCK RST DEFB LD LD LD OR JR SBC JR BIT JR 20,SH-ERR +14 HL,(+5CDF) DE,(HD-0B) A,H L Z,0932,LD-BLK-2 HL,DE NC,0932,LD-BLK-2 4,(FLAGS3) Z,0930,NREPORT-L

'Verification has failed'

0930 0931 0932 0935 0938 093A 093C 093F 0941 0945 0947 094A 094C 094E 0951 0952 0953 0954 0956 0959 095C 095D 095F 0962 0965

NREPORT-L RST DEFB LD-BLK-2 LD LD CP JR LD JR BIT JR LD CP JR LD ADD LD OR JR LD LD AND JR LD

20,SH-ERR +15 HL,(5CE1) A,(IX+4) +CD NZ,0941,LD-BLK1 HL,(5CE4) 0952,LD-BLK4 3,(FLAGS3) Z,0952,LD-BLK-4 A,(HD-00) +03 Z,0952,LD-BLK-4 BC,+01l4 HL,BC A,H L NZ,0959,LD-BLK-5 HL,(HD-0D) A,(HD-00) A NZ,0962,LD~NO-PGM HL,(PROG) 0A5C,LV-ANY 0988,TST-MR-M

Call the error handling routine. Get 'old' start from D-STR2. Fetch specifier from the channel area. Jump if not "M"+80, i.e. not a temporary "M" channel. But if the "M" channel is being used, the 'start' has been stored into +5CE4 (see +1583). Jump forward. Jump if not using the network. Jump if it is a block of bytes. Otherwise the data has been moved up by '276' bytes after the insertion of the "N" channel. Use 'old' start if it has been specified in the command, otherwise use 'new' start. Use the 'start' in HL for types other than 'program'. But with 'program' type, the 'start' is pointed by PROG. Do the actual LOADing or VERIFYing. Jump forward to close the channel used.

LD-BLK-3

LD-BLK-4

LD-BLK-5

LD-NO-PGM CALL JR

Now consider the MERGEing of a program. 0967 096A 096C 096E 0971 0972 0973 0977 0978 0979 097A 097C 097E 097F 0980 0981 0984 0985 0986 0988 098B 098D 098F 0992 TST-MR-M MERGE-BLK LD AND JR CALL RST DEFB NO-AUTOST LD PUSH INC RST DEFW LD EX POP PUSH CALL POP RST DEFW LD CP JR CALL JR A,(HD-11-hi) +C0 NZ,0973,NO-AUTOST 17B9,RCL-T-CH 20,SH-ERR +14 BC,(HD-0B) BC BC 10,CALBAS +0030 (HL),+80 DE,HL DE HL 0A5C,LV-ANY HL 10,CALBAS +08CE A,(IX+4) +CD NZ,0994,TST-MR-N 12A9,CLOSE-M2 09A0,MERGE-END Continue only if the content of HD-11 is sufficiently high, i.e. if the program was not saved with 'autostart' Otherwise reclaim channels and give a 'MERGE error'. Fetch 'length' of program to be merged and save it. Allows for a further location to insert the 'end marker'. Call BC-SPACES to make the required room in workspace. Mark the end of the space. HL now points to the start of the space created. Restore length of program. Save 'start' pointer. Load the program in the workspace. Restore 'start' pointer. Do the mergeing with the existing program by entering into the main ROM 'MERGE' control routine. Jump if the specifier is not "M"+80 i.e. if other than "M" channel has been used. Close the "M" channel. Jump forward.

0994 TST-MR-N 0998 099A 099D 09A0 MERGE-END

BIT JR CALL CALL JP

3,(FLAGS3) Z,09A0,MERGE-END 0EF5,SEND-NEOF 17B9,RCL-T-CH 05C1,END1

Exit immediately if the "B" output has been used. Why? Reclaim the channel. Finished.

The final branch of the routine deals with the LOADIng of a program or an array. 09A3 09A7 09AA 09AB 09AC 09AD 09AF 09B0 09B1 09B2 09B4 09B5 09B8 09B9 09BA 09BC 09BE 09C1 09C2 09C3 09C4 09C5 09C7 09C8 09CB 09CC 09CE 09CF 09D0 09D2 09D5 09D7 09D9 09DC 09DE 09E2 09E4 09E7 09E8 09E9 09EA 09EB 09EC 09ED 09EE 09EF 09F0 T-LD-NET LD-PR-AR LD LD PUSH LD OR JR INC INC INC EX JR LD DE,(HD-0B) HL,(5CE1) HL A,H L NZ,09B5,LD-PROG DE DE DE DE,HL 09BE,TST-SPACE HL,(5CDF) DE,HL HL,DE C,09C7,TST-TYPE DE,+0005 HL,DE B,H C,L 10,CALBAS +1F05 HL A,(HD-00) A Z,0A15,SET-PROG A,H L Z,09F3,CRT-NEW A,(IX+4) +CD NZ,09DE,T-LD-NET HL,(5CE4) 09E8,RCLM-OLD 3,(FLAGS3) Z,09E8,RCLM-OLD DE,+0114 HL,DE HL B,(HL) HL C,(HL) HL BC BC BC 10,CALBAS Fetch 'new' length. Fetch 'old' start (set to 0 when loading a 'new' array). Save it temporarily. Jump if not a 'new' array. Increment 'length' by 3, i.e. allows for the insertion of array name and two-byte length. Move 'length' into HL. Jump forward. Fetch 'old' length (i.e. length of existing program or array) from D-STR2 area. HL holds the 'new' length. Jump forward if the program (or array) being loaded is no longer than the existing one. Otherwise a check must be made to ensure that there is sufficient space in memory for the program (or array) to be loaded, with a call to main ROM 'TEST-ROOM' subroutine. Restore 'old' start (+0000 when handling a 'new' array). Jump if 'type' indicates a BASIC program (+00). Jump unless an 'old' array is to be erased before loading the 'new' one. If the channel specifier denotes other than temporary "M" channels (i.e. "M"+80H), the 'start' is already held in HL. Otherwise the start address of the array is fetched from D-STR2 area. Jump if using the 515232 link. Otherwise the array has been moved '276' bytes up after the insertion of "N" channel. Points to the high byte of 'array length'. Fetch high byte. Points to the low byte. Fetch low byte. Points to the array name. Include 'length' and 'name' in the array length. Call RECLAIM-2 to delete the array.

LD-PROG

EX SCF SBC JR TST-SPACE LD ADD LD LD RST DEFW TST-TYPE POP LD AND JR LD OR JR LD CP JR LD JR BIT JR LD ADD DEC LD DEC LD DEC INC INC INC RST

RCLM-OLD

09F1 09F3 09F6 09F7 09FB 09FC 09FD 09FE 09FF 0A02 0A03 0A04 0A06 0A07 0A08 0A09 0A0A 0A0B 0A0C 0A0D 0A0E 0A0F 0A12

CRT-NEW

DEFW LD DEC LD PUSH INC INC INC LD PUSH RST DEFW INC POP LD POP INC LD INC LD INC

+19E8 HL,(E-LINE) HL BC,(HD-0B) BC BC BC BC A,(5CE3) AF 10,CALBAS +1655 HL AF (HL),A DE HL (HL),E HL (HL),D HL 0A5C,LV-ANY 0988,TST-MR-M

Points to the end of variables area. Fetch the length of the array to be loaded. Save it. Include in the length one byte for array name and two for 'length of array Fetch array name from D-STR2 area. Save it briefly. Call MAKE-ROOM to create the space for the array. Points to the first 'new' location inserted. Restore array name and store into the 1st location. Fetch 'array length'. Store it into the following two locations. The array will be loaded from this location. Load array or BASIC program. Jump back to close the channel.

END-LD-PR CALL JP

If the file loaded is a program, the space required is to be crested and some actions to be performed. 0A15 0A19 0A1D 0A20 0A21 0A22 0A24 0A28 0A2B 0A2C 0A2E 0A2F 0A33 0A34 0A37 0A3A 0A3B 0A3D 0A3F 0A43 0A46 0A47 0A4A 0A4E 0A51 0A55 0A56 0A59 0A5A SET-PROG RES LD LD DEC RST DEFW LD LD RST DEFW INC LD ADD LD LD LD AND JR SET LD LD LD LD LD LD DEC LD INC JR 1,(FLAGS3) DE,(PROG) HL,(E-LINE) HL 10,CALBAS +19E5 BC,(HD-0B) HL,(PROG) 10,CALBAS +1655 HL BC,(HD-0F) HL,BC (VARS),HL A,(HD-11-hi) H,A +C0 NZ,0A4E,NO-AUTO 1,(FLAGS3) A,(HD-11-lo) L,A (NEWPPC),HL (NSPPC),+00 HL,(PROG) DE,(HD-0B) HL (DATADD),HL HL 0A0F,END-LD-PR Signal 'no autostart' Fetch start of existing program. Fetch end of existing program. Call RECLAIM-1 in the main ROM to delete the program. Fetch length of program and variables. Fetch start address of program. Call MAKE-ROOM to create the required space. Points to the first location. Fetch program length. Calculate and store the start of variables area. This is set to +FF when no autostart is required. Jump with 'no autostart'. Signal 'autostart' (see +0049). Fetch low byte of autostart line number. Store autostart line no. Clear NSPPC to signal 'jump'. Fetch start address of program area. Fetch program length. Make DATADD point to the last byte of the CHANS area. Balance the DEC above. Jump back to LOAD the new program.

NO-AUTO

THE 'LOAD OR VERIFY' SUBROUTINE This subroutine is used to LOAD or VERIFY (depending upon the state of bit 7 of FLAGS3 system variable) a block of bytes. It must be entered with the 'start'

and the 'length' in the HL and DE register pairs, and with all system variables properly set. 0A5C 0A5D 0A5E 0A5F 0A62 0A64 0A66 0A69 0A6A 0A6B 0A6C 0A70 0A72 0A75 0A77 0A79 0A7C 0A7E 0A7F 0A80 0A81 0A85 0A87 LV-ANY LD OR RET LD CP JR CALL RET PUSH PUSH BIT JR CALL JR JR CALL JR POP DEC POP BIT JR LD JR CP JR A,D E Z A,(IX+4) +CD NZ,0A6A,LV-BN 15A9,LV-MCH HL DE 3,(FLAGS3) Z,0A79,LV-B 0D12,NCHAN-IN NC,0A72,LV-N 0A7E,LV-BN-E 0B81,BCHAN-IN NC,0A79,LV-B DE DE HL 7,(FLAGS3) NZ,0A8A,VR-BN (HL),A 0A8F,LVBN-END (HL) Z,0A8F,LVBN-END Return if 'length' is zero. Fetch channel specifier and jump if handling with RS232 or network. LOAD or VERIFY from Microdrive. Finished. Save 'start' and 'length'. Jump if RS232 link is being used. Fetch a byte by using the "N" channel Input routine. Jump forward. Fetch a byte by using the "B" channel input routine. Restore 'length' and decrease it. Restore 'start'. Jump when VERIFYING. The actual LOAD, i.e. store the received byte. Jump forward. The actual VERIFY, i.e. compare the received byte with that held in memory. Continue only if the bytes are equal.

LV-BN

LV-N LV-B LV-BN-E

0A88 0A8A VR-BN 0A8B

'Verification has failed' 0A8D 0A8E 0A8F 0A90 0A91 0A92 0A94 LVBN-END RST DEFB INC LD OR JR RET 20,SH-ERR +15 HL A,E D NZ,0A6A,LV-BN Call the error handling routine. Move 'start' on to address the next location. Repeat until 'length' has reached 0. Finished.

THE 'LOAD "RUN" PROGRAM' ROUTINE First the various system variables are properly set, then a nine-byte header is fetched from the first record of the program called 'run'. The routine continues into the middle of the LOAD~VERIFY-MERGE commands routine. 0A95 0A98 0A9C 0A9F 0AA3 0AA6 0AAA 0AAE 0AB1 0AB4 0AB7 0ABA LOAD-RUN LD LD LD LD LD LD SET CALL LD LD LD LDIR BC,+0001 (D-STR1),BC BC,+0003 (N-STR1),BC BC,+0AC6 (N-STR1+2),BC 4,(FLAGS3) 0753,PROG HL,+5CE6 DE,+5CDE BC,+0009 Load from drive 1. The filename 'run' is three characters in length. The filename is stored from this location. Signal 'loading'. Set HD variablea as required. Copy 'old' header into D-STR2 area. Clear the 'jump' signal set by using

0ABC 0AC0 0AC3 0AC6

SET CALL JP DEFM

7,(NSPPC) 1580,F-M-HEAD 08F2,TEST-TYPE "run"

the RUN command. Load the 'new' 9-byte header. Jump back in the middle of the LOAD-VERIFY-MERGE commands routine. The 'run' filename.

The RS-232 link routines THE 'SET "BAUD" SYSTEM VARIABLE' ROUTINE This routine is entered with a baud rate in the range 0. .65534 in the system variable D-STR1. It rounds the value to the nearest 'standard' value and sets the BAUD system variable with the appropriate timing constant. It is used by the 'FORMAT' command routine. 0AC9 0ACD 0AD0 0AD1 0AD2 0AD3 0AD4 0AD5 0AD6 0AD8 0ADA 0ADB 0ADD 0ADF 0AE0 0AE1 0AE2 0AE4 0AE5 0AE6 0AE7 0AE8 0AEC SET-BAUD LD LD NXT-ENTRY LD INC LD INC EX LD CP JR AND SBC JR EX INC INC JR END-SET EX LD INC LD LD JP BC,(D-STR1) HL,+0AEF E,(HL) HL D,(HL) HL DE,HL A,H +48 NC,0AE4,END-SET A HL,BC NC,0AE4,END-SET DE,HL HL HL 0AD0,NXT-ENTRY DE,HL E,(HL) HL D,(HL) (BAUD),DE 05C1,END1 Fetch baud rate. Start of RS232 timing constants table. Fetch an entry in the DE register pair. Pass the value to HL. Fetch high byte. Jump if the end of the table has been reached. Jump also if this value is greater than or equal to the supplied one. Restore the 'pointer' in HL. Skip the constant and jump back to examine the next table entry. Restore the pointer in HL. Fetch the timing constant. Store the constant. Finished.

THE 'RS232 TIMING CONSTANTS' TABLE The '9' entries in this table are the 'standard' baud rate values (from 75 to 19200), each one followed by the 'timing constant' to be stored in the BAUD system variable. 0AEF 0AF1 0AF3 0AF5 0AF7 0AF9 0AFB 0AFD 0AFF 0B01 0B03 0B05 0B07 0B09 0B0B 0B0D 0B0F 0B11 DEFW DEFW DEFW DEFW DEFW DEFW DEFW DEFW DEFW DEFW DEFW DEFW DEFW DEFW DEFW DEFW DEFW DEFW +0032 +0A82 +006E +04C5 +012C +01BE +0258 +00DE +04B0 +006E +0960 +0036 +12C0 +001A +2580 +000C +4B00 +0005 First baud rate=50 (but the constant is for 75 baud). 2nd baud rate=110. Constant for 110 baud. 3rd baud rate=300. Constant for 300 baud. 4th baud rate=600. Constant. 5th baud rate=1200. Constant. 6th baud rate=2400. Constant. 7th baud rate=4800. Constant. 8th baud rate=9600. Constant. Last baud rate=l9200. Constant.

THE 'OPEN RS232 CHANNEL IN CHANS AREA' SUBROUTINE This subroutine opens a permanent "B" or "T" channel (depending upon the specifier held in L-STR1) at the end of the CHANS area. On return, the DE register pair will hold the start of the channel. 0B13 0B16 0B17 0B1A 0B1B 0B1C 0B1E 0B1F 0B20 0B23 0B24 0B27 0B2A 0B2C 0B2D 0B30 0B32 0B33 0B34 0B37 0B38 0B3B 0B3C 0B3D 0B3E 0B3F 0B42 0B43 0B44 0B45 0B46 OP-RS-CH LD DEC LD PUSH RST DEFW POP PUSH CALL POP LD LD LDDR INC LD CP RET PUSH LD ADD LD LD INC LD INC LD LD INC LD POP RET HL,(PROG) HL BC,+000B BC 10,CALBAS +1655 BC DE 1691,REST-N-AD DE HL,+0B6E BC,+000B DE A,(L-STR1) +42 NZ DE HL,+0005 HL,DE DE,+0C5A (HL),E HL (HL),D HL DE,+0B75 (HL),E HL (HL),D DE The start address of the channel. The channel is '11' bytes in length. Call MAKE-ROOM to create the required space. Restore 'length'. Save address of last byte in the inserted area. Restore start address of 'filename' if it has been moved up after the insertion of the new space. Restore 'end of channel' address. Last byte in the "T" channel data table. Length of the table. Copy the 'T' channel data. Point to the 1st location of the channel. Fetch channel specifier. Return if the requested channel was not the 'B' channel. Save channel start address. Point to the 'shadow ROM output routine' pointer. the address of the BCHANOUT routine. Store the low byte. Store the high byte. Point to the 'shadow ROM input routine' pointer. The address of the BINPUT routine, Store the low byte. Store the high byte. Restore channel start address. Finished.

THE 'ATTACH CHANNEL TO A STREAM' ROUTINE The entry point is OP-RSCHAN if the channel is "B" or "T", otherwise the routine is entered at OP-STREAM with "N" channel (from 0EA6), with DE holding the channel start address. The correct displacement is calculated for the stream whose number is held in S-STR1, and stored in the STRMS area. 0B47 0B4A 0B4D 0B4E 0B4F 0B50 0B52 0B53 0B56 0B59 0B5A 0B5B 0B5D OP-RSCHAN CALL OP-STREAM LD DEC EX AND SBC EX LD LD RLCA LD LD ADD 0Bl3,OP-RS-CH HL,(CHANS) HL DE,HL A HL,DE DE,HL HL,+5C16 A,(S-STR1) C,A B,+00 HL,BC Create the channel area. Calculate the displacement to be stored in STRMS.

Pass the displacement to DE. Location in STRMS for stream 0. Fetch stream number and double it. Move the offset to the BC register pair. Index into the STRMS area.

0B5E 0B5F 0B60 0B61

LD INC LD JP

(HL),E HL (HL),D 05C1,END1

Store the displacement. Finished.

THE '"T" CHANNEL DATA' TABLE The '11' bytes that composes a "T" RS232 channel are as follows: 0B64 0B66 0B68 0B69 0B6B 0B6D DEFW DEFW DEFB DEFW DEFW DEFW +0008 +0008 +54 +0C3C +0B6F +000B Main ROM 'output' routine. Main ROM 'input' routine. "T" (channel specifier). Shadow ROM 'output' routine. Shadow ROM 'input' routine. Length of this channel.

THE '"T" CHANNEL INPUT' ROUTINE The actual 'input' is handled by the CALL-INP routine; the service routine is TCHAN-IN below. 0B6F 0B72 T-INPUT LD JP HL,+0B7B 0CBD,CALL-INP Address of TCHAN-IN routine. Jump forward.

THE '"B" CHANNEL INPUT' ROUTINE As with the "T" channel, the 'input' is handled by CALL-INP; however the service routine is BCHAN-IN. 0B75 0B78 B-INPUT LD JP HL,+0B81 0CBD,CALL-INP Address of BCHAN-IN routine. Jump forward.

THE '"T" CHANNEL INPUT' SERVICE ROUTINE The only difference in respect to the "B" input is that the most significant bit of the received character is always cleared. 0B7B 0B7E 0B80 TCHAN-IN CALL 0B81,BCHAN-IN RES 7,A RET Use the "B" channel input service routine. Accept only codes in the range +00... +7F (ASCII 7 bit). Finished.

THE '"B" CHANNEL INPUT' SERVICE ROUTINE This subroutine is also called by using the hook code +1D; it always returns carry set if an acceptable byte has been read in the A register from the RS232 link, or zero flag set when no byte has been read. 0B81 0B84 0B85 0B86 0B88 0B8A 0B8B 0B8C 0B8D 0B8E 0B90 0B92 0B93 0B95 0B99 0B9A 0B9B 0B9E 0BA0 BCHAN-IN LD LD AND JR LD INC LD SCF RET LD IN RRCA JR LD RST DI LD OUT LD HL,+5CC7 A,(HL) A Z,0B8E,REC-BYTE (HL),+00 HL A,(HL) A,+7F A,(+FE) C,0B9A,REC-PROC (ERR-NR),+14 28,ROMERR A,(IOBORD) (+FE),A DE,(BAUD) Fetch contents of SER-FL-lo system variable. Proceed in receiving the byte if it holds zero. Otherwise clear SER-FL-lo and fetch the byte from SER-FL-hi. Signal 'acceptable code'. Read port +7FFE. Rotate bit 0 into carry. Jump if SPACE not being pressed. Otherwise give the 'Break into program' error report. Disable interrupts. Fetch new border colour. Change border colour. Fetch timing constant.

REC-BYTE

REC-PROC

0BA4 0BA7 0BA8 0BA9 0BAB 0BAD 0BAF

LD LD LD SRL RR LD OUT

HL,+0320 B,D C,E B C A,+FE (+EF),A

Repeat '800' times the test. Copy timing constant into the BC register pair. Make BC hold (constant/2). Make high the CTS line.

Now a loop is entered to check if a 'start bit' is found from the TXdata line. A 'start bit' is detected if the TXdata line is high for at least '77' T cycles. If after about 25 ms no 'start bit' is found, the routine continues forward. 0BB1 0BB3 0BB4 0BB6 0BB8 0BB9 0BBB 0BBD 0BBE 0BC0 0BC2 0BC3 0BC5 0BC6 0BC7 0BC8 0BCA 0BCB 0BCD 0BCF READ-RS IN RLCA JR IN RLCA JR IN RLCA JR IN RLCA JR A,(+F7) NC,0BC5,TST-AGAIN A,(+F7) NC,0BC5,TST-AGAIN A,(+F7) NC,0BC5,TST-AGAIN A,(+F7) C,0BD1,START-BIT HL A,H L NZ,0BB1,READ-RS AF A,+EE (+EF),A 0BF0,WAIT-1 Jump if the beginning of a 'start bit' is found. Decrease the counter. Repeat the test until the counter reaches zero. Save zero flag set. Make the CTS line low again. Jump forward to repeat the test. Read TXdata line. Shift state into carry. Jump if the line has a low level. Repeat the test three times, i.e. make sure that TXdata is still high after 77 T cycles,

TST-AGAIN DEC LD OR JR PUSH LD OUT JR

This loop is used to read the eight data bits that follows the 'start' bit. First, a delay equal to ((BAUD)*1.5)*26 T cycles is entered, so the first reading is made at the middle of the first data bit. The seven subsequent readings are separated by a delay of (47+26*(BAUD)) T cycles, that is the length of each bit. 0BD1 0BD2 0BD3 0BD5 0BD6 0BD7 0BD8 0BD9 0BDA 0BDB 0BDC 0BDD 0BDF 0BE1 0BE3 0BE4 0BE6 0BE8 0BEA 0BEC 0BED START-BIT LD LD LD DEC DEC DEC SERIAL-IN ADD NOP BD-DELAY DEC LD OR JR ADD IN RLCA RR JR LD OUT LD CPL H,B L,C B,+80 HL HL HL HL,DE HL A,H C NZ,0BDA,BD-DELAY A,+00 A,(+F7) B NC,0BD8,SERIAL-IN A,+EE (+EF),A A,B Get (BAUD/2) into HL. Set a marker into bit 7. Decrease HL three times, i.e. short the loop by 78 T cycles, balancing the time spent before. Add (BAUD) to HL. Wait 4 T cycles. Insert a delay (26*BAUD) T cycles in length. Wait 7 T cycles. Read a bit. Shift it into carry flag, then into the B register. Repeat until the 'marker' is found (i.e., 8 bits have been read), Make the CTS line having a low level. Fetch the received byte. Complement it (the bits are sent inverted through the RS232 link).

0BEE 0BEF 0BF0 0BF1 0BF2 0BF3 0BF4 0BF6 0BF7 0BF8

WAIT-1 WAIT-2

SCF PUSH ADD DEC LD OR JR ADD ADD ADD

AF HL,DE HL A,H L NZ,0BF1,WAIT-2 HL,DE HL,DE HL,DE

Signal 'acceptable codes' when exit. Save the byte and carry flag. Make HL hold (BAUD). Wait until the first stop bit is finished.

HL now holds (BAUD)*3.

Now a loop similar to that at 0BB1 is entered. If the TXdata line is found high for at least 77 T cycles, the 'sending' device is sending the 'start' bit for a second byte, even if the CTS line has a low signal level. This byte is to be read and stored into SER-FL. 0BF9 0BFA 0BFB 0BFC 0BFE 0C08 0C01 0C03 0C05 0C06 0C08 0C0A 0C0B 0C0D 0C0E 0C10 0C12 0C13 0C14 0C16 T-FURTHER DEC LD OR JR IN RLCA JR IN RLCA JR IN RLCA JR IN RLCA JR LD LD SRL RR HL A,L H Z,0C36,END-RS-IN A,(+F7) NC,0BF9,T-FURTHER A,(+F7) NC,0BF9,T-FURTHER A,(+F7) NC,0BF9,T-FURTMER A,(+F7) NC,0BF9,T-FURTHER H,D L,E H L Fetch timing constant into HL. Calculate (BAUD)/2 into HL. Decrease counter. Repeat test until it has reached 0. Exit if there is no further byte to be received. Read TXdata line. Shift received bit into carry. Jump back with TXdats low. Otherwise repeat the test three times to ensure that TXdata is still high after 77 T cycles,

The next instructions are the same as those stored from 0BD3 above, so they are not commented. 0C18 0C1A 0C1B 0C1C 0C1D 0C1E 0C1F 0C20 0C21 0C22 0C24 0C26 0C28 0C29 0C2B LD DEC DEC DEC SER-IN-2 ADD NOP BD-DELAY2 DEC LD OR JR ADD IN RLCA RR JR B,+00 HL HL HL HL,DE HL A,H L NZ,0C1F,BD-DELAY2 A,+00 A,(+F7) B NC,0C1D,SER-IN-2

Finally, the last byte received is stored into SER-FL, and the first one returned into the A register. 0C2D 0C30 LD LD HL,+5CC7 (HL),+01 Points to SER-FL-lo Set it to +01, indicating that SER-FL-hi holds a valid byte.

0C32 0C33 0C34 0C35 0C36 0C39 0C3A 0C3B

INC LD CPL LD END-RS-IN CALL POP EI RET

HL A,B (HL),A 0CA9,BORDREST AF

Points to SER-FL-hi. Fetch the received byte. Complement it and store into SER-FL-hi. Restore border colour. Restore first byte and flags. Enable interrupts. Finished.

THE '"T" CHANNEL OUTPUT' ROUTINE The "t" channel output involves the 'sending' over the RS232 link of the character held into the A register, provided that it is within the range +20...+7F. Characters from +80 to +A4 are sent as '?', while tokens are detokenised. Codes lower than +20 are ignored, except for +0D that sends both CR and LF codes. The routine unfortunately contains a mistake as it does not suppress the 'leading space' flag (bit 0 of FLAGS) as required. The result is that when sending two tokens (i.e., THEN GOTO), two spaces are inserted between them, instead of one. The correct routine should include the following two instructions after the final 'RET C': 'JR NZ,BCHAN-OUT' and 'SET 0,(FLAGS)', and a 'RES 0,(FLAGS)' before the 'CP +7F'. 0C3C 0C3E 0C40 0C42 0C43 0C45 0C46 0C48 0C4A 0C4C 0C4E 0C50 0C53 0C55 0C57 0C59 TCHAN-OUT CF JR SUB RST DEFW RET NOT-TOKEN CP JR LD NOT-GRAPH CP JR CALL LD CALL NOT-CR CP RET +A5 C,0C46,NOT-TOKEN +A5 10,CALBAS +0C10 +7F C,0C4C,NOT-GRAPH A,+3F +0D NZ,0C57,NOT-CR 0C5A,BCHAN-OUT A,+0A 0C5A,BCHAN-OUT +20 C Jump if the code is not a token code. Reduce range of token, and detokenise it by calling recursively this routine via main ROM 'PO-TOKENS' routine. Jump if the character is not a graphic character. Otherwise send a '?'. Is the code 'ENTER'? Jump if not. Otherwise send the CR code. Follow it with a linefeed code. Ignore all codes lower than +20, otherwise use BCHAN-OUT below to send the character.

THE '"B" CHANNEL OUTPUT' ROUTINE The 8 bits that forms the byte held in the A register are sent inverted through the RS232 link, after an initial 'start' bit (high), and followed by two 'stop' bits (or rather a 'double length' stop bit, with low signal level). As with 'b' channel input, the bits have a fixed length, depending upon the selected baud rate. This subroutine is also called by the 'hook code' +1E. 0C5A 0C5C 0C5D 0C5E 0C61 0C63 0C65 0C67 0C68 0C6A 0C6D 0C6E 0C6F 0C70 0C7l 0C72 BCHAN-OUT LD CPL LD LD OUT LD OUT CPL OUT LD LD LD BD-DEL-1 DEC LD OR JR B,+0B C,A A,(IOBORD) (+FE),A A,+EF (+EF),A (+F7),A HL,(BAUD) D,H E,L DE A,D E NZ,0C6F,BD-DEL-1 Counts '11' bits. Invert the eight data bits. C holds the byte to be sent. Fetch new border colour. Change border colour. Reset CTS and select RS232. Make RXdata have a low signal level. Fetch timing constant. Copy into DE register pair. Firstly wait (26*(BAUD)) T cycles.

0C74 0C76 0C78 0C7A 0C7C 0C7D 0C80 0C82 0C84 0C86 0C87 0C88 0C8A 0C8C 0C8D 0C8E 0C8F 0C90 0C9l 0C93 0C94 0C95 0C97 0C99 0C9A 0C9C 0C9E 0CA0 0CA2 0CA4 0CA5 0CA6 0CA7

TEST-DTR

LD IN OR IN RRA JP IN AND JR SCF DI SER-OUT-L ADC OUT LD LD DEC LD OR JR DEC XOR SRL DJNZ DI LD LD LD OUT OUT DEC LD OR JR

A,+7F A,(+FE) +FE A,(+FE) NC,0CB4,BRK-INOUT A,(+EF) +08 Z,0C74,TEST-DTR A+00 (+F7),A D,H E,L DE A,D E NZ,0C8E,BD-DEL-2 DE A C 0C88,SER-OUT-L A,+01 C,+EF B,+EE (+F7),A (C),B HL A,L H NZ,0CA4,BD-DEL-3

Read port +7FFE (SPACE key). Read port +FEFE (CAPS SHIFT key) only if SPACE is being pressed. Test bit 0. Give an error if BREAK is pressed. Read DTR line. Only bit 3. Jump back until DTR is found high. Set carry flag (start bit). Disable interrupts. In fact, shift carry into bit 0 of accumulator. Send this bit over RS232 link. Copy timing constant into DE. Wait 26*(BAUD) T cycles after having sent each bit. Wait 6 T cycles. Clear A for next pass. Shift the bit to be sent into carry. Loop for all 11 bits. Enable interrupts.

BD-DEL-2

BD-DEL-3

Make RXdata having a high level. Turn off RS232. Insert a final delay before returning via the border-restore routine below.

THE 'BORDER COLOUR RESTORE' SUBROUTINE This subroutine is used whenever the border colour has been changed to IOBORD during an I/O operation, and needs to be restored to its original state. 0CA9 BORD-REST 0CAC 0CAE 0CAF 0CB0 0CB1 0CB3 LD AND RRCA RRCA RRCA OUT RET A,(BORDCR) +38 Fetch lower screen attribute. Only bits 3,4,5, i.e. BORDER. Rotate colour into bits 0,l,2. Restore colour. Finished.

(+FE),A

THE 'BREAK INTO I/O OPERATION' ROUTINE Whenever BREAK is pressed during an I/O operation, the interrupts have to be enabled, the border colour to be restored and the error "BREAK into program" to be given. 0CB4 0CB5 0CB8 0CBC BRK-INOUT EI CALL LD RST 0CA9,BORD-REST (ERR-NR),+l4 28,ROMERR Enable interrupts. Restore border colour. Store error code and give the error.

THE 'CALL-INP' ROUTINE All inputs from "B", "T", "N" and "M" channels are handled by loading HL with the address of the service 'input' routine for the channel, and then jumping to this routine. The routine handles both INPUT and INKEY$ commands referred to the 'new' channels.

0CBD 0CC1 0CC2 0CC5 0CC6 0CC7 0CC8 0CC9 0CCC 0CCE

CALL-INP

RES PUSH LD LD INC LD AND LD SBC JR

3,(TVFLAG) HL HL,(ERR-SP) E,(HL) HL D,(HL) A HL,+107F HL,DE NZ,0CFB,INKEY$

'The mode is to be considered as being unchanged' Save address of the service routine. Points to the error address. Fetch low byte of error addres Fetch high byte. Prepare for a true subtraction. This is ED-ERROR. Jump forward if not during an INPUT command.

Now deal with 'INPUT #' command referred to an Interface channel. 0CD0 0CD1 0CD5 0CD6 0CD7 0CDB 0CDC 0CDF 0CE0 POP LD POP POP LD PUSH LD PUSH JP HL SP,(ERR-SP) DE DE (ERR-SP),DE HL DE,+0CE1 DE (HL) Restore address of service routine. Clear machine stack. Remove ED-ERROR. The old value of ERR-SP is restored. Save address of the service routine. Return address is INPUT-END below. Jump to the service routine.

IN-AGAIN

When the character has been read from the required channel, a return is made here to add the character to the INPUT line, or to return if the character is ENTER. 0CE1 0CE3 0CE5 0CE9 0CEA 0CEB 0CED 0CEF 0CF1 0CF2 0CF4 0CF5 0CF7 0CF8 INPUT-END JR JR OREPORT-8 LD RST NO-READ POP JR ACC-CODE CP JR RST DEFW POP JR END-INPUT POP JP C,0CED,ACC-CODE Z,0CEA,NO-READ (ERR-NR),+07 28,ROMERR HL 0CDB,IN-AGAIN +0D Z,0CF7,END-INPUT 10,CALBAS +0F85 HL 0CDB,IN-AGAIN HL 0700,UNPAGE Jump with acceptable codes. Jump with no data read. Otherwise report the 'End of file' error. Restore address of service routine and try again. Jump if the code is ENTER. Otherwise the character is to be added to the INPUT line - so enter into the main ROM 'ADD-CHAR' subroutine. Restore address of service routine and read the next byte. Remove address of service routine. Return to the main ROM calling routine.

Enter here to deal with the INKEY$ function (a single character is returned). 0CFB 0CFC 0CFF 0D00 INKEY$ POP LD PUSH JP HL DE,+0D01 DE (HL) Address of service routine. Return address is INK$-END below. Jump to the service routine.

After having read the byte, a return is made here. 0D01 0D02 0D03 0D07 0D09 0D0B INK$-END RET RET BIT JR OR RET C Z 4,(FLAGS3) Z,0CE5,OREPORT-8 +01 Return with acceptable codes, or no character read. Give the 'end of file' error if bit 4 of FLAGS3 is reset. Otherwise the MOVE command has been used - so return with carry and zero flags both reset.

The Network routines THE '"N" CHANNEL INPUT' ROUTINE The actual 'input' is handled via the CALL-INP routine above. The service routine is NCHAN-IN. 0D0C 0D0F N-INPUT LD JP HL,+0D12 0CBD,CALL-INP Address of NCHAN-IN routine. Jump to the control routine.

THE '"N" CHANNEL INPUT' SERVICE 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. 0D12 0D16 0D19 0D1A NCHAN-IN LD LD AND JR IX,(CURCHL) A,(NCOBL) A Z,0D1E,TEST-BUFF Fetch start of "n" channel. This holds 0 while 'receiving'. Jump if it is a "read" file.

'Reading a 'write' file' 0D1C 0D1D 0D1E 0D21 0D22 0D24 0D27 0D28 0D29 0D2B 0D2D 0D2E 0D31 0D33 0D36 0D37 RST DEFB TEST-BUFF LD AND JR LD DEC SUB JR LD INC LD ADD LD SCF RET 20,SH-ERR +0D A,(NCIBL) A Z,0D38,TST-N-EOF E,(NCCUR) A E C,0D38,TST-N-EOF D,+00 E (NCCUR),E IX,DE A,(IX+20) Call the error handling routine. Fetch number of bytes to be read from the buffer. If NCIBL holds zero, then the buffer is empty. Otherwise fetch the current position. Decrease number of bytes to be read from the buffer. Subtract position from the result. Jump if all bytes have been read. Clear D register. Update the character position. Points to 'byte to be read - 20'. Read the byte. Carry is set to signal 'acceptable byte'. Finished.

If no data is found in the buffer, consider whether this data block is the last one. 0D38 0D3B 0D3C 0D3E TST-N-EOF LD AND JR RET A,(NCTYPE) A Z,0D3F,GET-N-BUF Fetch packet type. Jump if packet type = 0. Indicating a normal packet. Otherwise return with both zero and carry flags reset to signal 'end of file'.

A further data block is to be received if the current data block is not the 'EOF' one. 0D3F 0D42 0D44 0D45 0D48 0D4A 0D4D GET-N-BUF LD OUT DI TRY-AGAIN CALL JR CALL JR A,(IOBORD) (+FE),A 0F1E,WT-SCOUT NC,0D5F,TIME-OUT 0E18,GET-NBLK NZ,0D5F,TIME-OUT Fetch new border colour. Change border colour. Disable interrupts. Wait for a 'scout' leader. Jump if 'time out' occurs. Wait for header and data block. Jump with any error.

0D4F 0D51 0D53 0D57 0D5A 0D5D 0D5F TIME-OUT 0D62 0D63 0D65 0D66 0D69 0D6B

EI CALL LD LD LD JR LD AND JR DI CALL AND RET

0CA9,BORD-REST (NCCUR),+00 A,(NTTYPE) (NCTYPE),A 0D1E,TEST-BUFF A,(NCIRIS) A Z,0D45,TRY-AGAIN 0CA9,BORD-REST +00

Enable interrupts. Restore border colour. The position is 0. Copy this byte to signal the packet type. Try now to fetch the byte. Fetch destination station no. There is no 'time-out' when broadcasting (NCIRIS=0). Enable interrupts. Restore border colour. Return with zero flag set and carry reset, signalling that no data has been read.

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. 0D6C NCHAN-OUT 0D70 0D71 0D74 0D75 0D76 LD LD LD AND LD JR IX,(CURCHL) B,A A,(NCIBL) A A,B Z,0D7A,TEST-OUT Point to start of channel. Save temporarily into B the byte to be sent. This holds 0 for 'write' channel. Set zero flag as required. Restore the byte to be sent. Continue only if using a 'write' channel (NCIBL=0). Call the error handling routine. Fetch length of output buffer. Include the current byte in the length count. Jump unless the buffer is filled. Save the byte to be sent. Signal normal packet type. Send the packet. Restore the byte to be sent. 'Only 1 byte in the buffer'. Store new buffer length. Make IX point to 'first free byte in the buffer - 20). Store byte in the buffer. Finished.

'Writing to a 'read' file' 0D78 0D79 0D7A 0D7D 0D7E 0D80 0DB1 0D82 0D85 0D86 0D88 0D8B 0D8D 0D8F 0D92 RST DEFB LD INC 20,SH-ERR +0C E,(NCOBL) E NZ,0D88,ST-BF-LEN AF A 0DAB,S-PACK-1 AF E,+01 (NCOBL),E D,+00 IX,DE (IX+20),A

TEST-OUT

JR PUSH XOR CALL POP LD ST-BF-LEN LD LD ADD LD 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 into the E register. Then a 'response byte' is to be received into the NTRESP system variable (provided that you are not using the broadcast). 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. 0D93 0D96 0D99 0D9A 0D93 0D9E 0DA0 OUT-BLK-N CALL LD AND RET LD LD LD 0FC5,OUTPAK A,(NCIRIS) A Z HL,+5CCD (HL),+00 E,+01 Send the block. This holds zero when broadcasting. Return if broadcasting. Address of NTRESP system variable. First clear NTRESP. A single byte is to be received

0DA2 0DA5 0DA6 0DA9 0DAA

CALL RET LD DEC RET

0F92,INPAK NZ A,(NTRESP) A

into NTRESF. Get response byte (+01). Return with zero flag reset if the network was inactive. Now fetch the response byte. It must be +01. Return with zero flag reset if it is not +01.

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. 0DAB 0DAE 0DAF S-PACK-1 CALL RET JP 0DB2,SEND-PACK NZ 0E0F,BR-DELAY Send the packet. Return if not broadcasting. Otherwise exit through the delay routine.

THE 'SEND-PACK' SUBROUTINE This subroutine is also called by using 'hook code' +30. It sends over the network a SCOUT leader, followed by the header and the data block for the "n" channel pointed by the IX register. On entry, the A register may hold +01 or +00 depending upon whether the block is the 'end of file' one or not. NCNUMB is incremented before returning. The zero flag is returned set if the destination station number denotes that the 'broadcasting' is being used. 0DB2 SEND-PACK 0DB5 0DB8 0DBB 0DBD 0DBF 0DC0 0DC3 LD LD LD OUT PUSH POP LD ADD (NCTYPE),A B,(NCOBL) A,(IOBORD) (+FE),A IX DE HL,+0015 HL,DE Store packet type, i.e. +00 for normal, +01 for end of file. Fetch block length. Fetch new border colour. Make DE point to the start of the "n" channel. Add this offset to point to the start of the 255-byte buffer.

Now the checksum of the data block is calculated. 0DC4 0DC5 0DC6 0DC7 0DC9 0DCC 0DCF 0DD0 CHKS1 XOR ADD INC DJNZ LD LD ADD PUSH A A,(HL) HL 0DC5,CHKS1 (NCDCS),A HL,+000B HL,DE HL First clear A register. Add this byte. Point to next location. Repeat for all bytes in the block. Store the checksum obtained. Now points to NCIRIS. Save the pointer.

The checksum of 0DD1 0DD3 0DD4 0DD5 0DD6 0DD8

the header is calculated. LD XOR ADD INC DJNZ LD B,+07 A A,(HL) HL 0DD4,CHKS2 (HL),A Checksum is for 7 bytes. Clear A register. Add this byte. Point to next location. Repeat for all bytes. Store checksum into NCHCS.

CHKS2

Now the SCOUT leader, followed by the 8-byte header, is sent, and a response code is received (if not broadcasting). 0DD9 0DDk 0DD0 DI SENDSCOUT CALL 9Fb1,SEND-SC POP HL Disable interrupts. Send the SCOUT (i.e. a leader followed by station number). Restore pointer to NCIEIS,

0DD1 0DD1 0DE1 0DE4 0DE6 0DE8 0DE9 0DEC 0DED 0DF0 0DF1 0DF2 0DF4 0DF6

PUSH LD CALL JR PUSH POP LD ADD LD LD AND JR LD DJNZ

HL 1,48 9II93,OUT-BLK-N NZ,0DDA,SENDSCOUT IX HL DE,+0015 HL,DE E,(NCOBL) A,E A Z,0DFD,INC-BLKN B,+20 0DF6,SP-DL-1

i.e. Start of header block. The header Is made by '8' bytes. The header is sent, and the response code received. Repeat until successfully. Make HL point to the start of the "n" channel. Now points to the start of the data block. Get block length. If NCOBL is zero (i.e. if the buffer is empty); no data needs to be sent. Wait 418 T cycles before proceeding.

SP-DL-1

Now the data block is sent, and NCNUMB updated. 0DF8 0DF8 0DF0 0E00 0E02 0E05 0E06 0E09 0E0k 0E00 0E01 INC_BLKN CALL JR INC JR INC POP CALL EI LD AND RET 0D93,OUT-BLK-N NZ,0DDA,SENDSCOUT (NCNUMB-lo) NZ,0E05,SP-N-END (NCNUMB-hi) HL 0CA9,BORD-REST A,(NCIRIS) A Send the data block and get the response code. Repeat until succesfully. Increment low byte of block number. Increment also high byte when low byte reaches zero. Restore pointer to NCIRIS. Restore border colour. Enable interrupts. Get destination station number. Set zero flag if NCIRIS is 0 (i.e. broadcasting). Finished.

SP-N-END

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 'broadcasts' transmissions. 0E01 0E12 0E13 0E14 0E15 0E17 BR-DELAY DL-LOOP LD DEC LD OR JR RET DE,+1500 DE A,E D NZ,0E12,DL-LOOP Set a counter. Decrease it. Check whether the counter has reached zero. Repeat if not. Finished.

THE 'HEADER AND DATA BLOCK RECEIVING' 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 point to the start of the "n" channel. The zero flag is returned reset with any error. 0E18 0E1B 0E1D 0E20 0E21 GET-NBLK LD LD CALL RET LD HL,+5CCE E,+08 0F92,INPAK NZ HL,+5CCE Point to NTDEST, i.e. first byte of header block. Length of the block. Receive the header. Return with network inactive (i.e. no header has been found). Points again to NTDEST.

Now the header checksum is calculated, and compared with NTCHS checksum. 0E24 0E25 0E27 0E28 XOR LD ADD INC A B,+07 A,(HL) HL First clear A. Checksum for next 7 bytes. Add this byte. Point to next location.

CHKS3

0E29 0E2B 0E2C

DJNZ CP RET

0E27,CHKS3 (HL) NZ

Repeat for all bytes. Compare with NTCHS checksum. Return if they do not match.

Some other tests are now made. 0E2D 0E30 0E31 0E33 0E36 0E37 0E3A 0E3D 0E3E 0E40 0E43 0E44 0E45 0E48 0E4B 0E4E 0E4F 0E51 0E53 0E54 0E55 0E56 0E57 0E5A 0E5D 0E5F 0E62 0E64 BRCAST LD AND JR CP RET LD CP RET JR LD OR RET A,(NTDEST) A Z,0E40,BRCAST (NCSELF) NZ A,(NTSRCE) (NCIRIS) NZ 0E45,TEST-BLKN A,(NCIRIS) A NZ HL,(NTNUMB) E,(MCNUMB-lo) D,(NCNUMB-hi) A HL,DE Z,0E65,GET-NBUFF HL A,H C NZ 0E65,GET-NBUFF (NCNUMB-lo) NC,0E62,GETNB-END (NCNUMB-hi) +01 Fetch destination station number. Jump if broadcasting. Otherwise compare with NCSELF. Return if this data block is for another Spectrum. Source station number. Compare against NCIRIS. Return if the transmitting Spectrum is not the required one. Jump forward. Make sure that it is broadcasting (i.e. NTDEST and NCIRIS both 0). Return if you are not waiting for a broadcast. Fetch number of block being transmitted. Fetch number of expected block. Jump if the block received is the expected one. Accept also the previous block (already received, but 'response' lost). But refuse other blocks. Receive the block. Decrease NCNUMB, i.e. 'ignore' this block. Return with zero flag reset, so that the procedure is repeated.

TEST-BLKN LD LD LD AND SBC JR DEC LD OR RET CALL DEC JR DEC GETNB-END OR RET

Now follows the second routine, used to get the data block from the network. 0E65 GET-NBUFF 0E68 0E69 0E6C 0EbF 0E73 0E72 0E74 0E75 0E78 0E79 0E?A 0E7B 0E7E 0E7F 0E83 0E83 0E84 0E87 0E88 LD AND CALL LD AND JR PUSH POP LD ADD PUSH LD CALL POP RET LD LD LD SUB INC A,(NTDEST) A NZ,0FBE,SEND-RESP A,(NTLEN) A Z,0E93,STORE-LEN IX HL DE,+0015 HL,DE HL E,A 0F92,INPAK HL NZ A,(NTLEN) B,A A,(NTDCS) (HL) HL This holds zero when broadcasting. Send the 'response' code (for the header) unless broadcasting. Fetch data block length. Jump if the data block is empty. Make HL point to the start of the channel. Point to the start of data buffer. Save start address. Length of the block to be goes into E. Receive the data block. Restore start address. Return if the network was Pass length of data block B register. Start with the data block Subtract current byte. Point to next location. received

inactive. to the checksum.

CHKS4

0E89 0E8B 0E8C 0E8F 0E93 0E93 0E96 0E99 0E9C 0E9E 0EA1 0EA2

DJNZ RET LD AND CALL STORE-LEN LD LD INC JR INC GETBF-END CP RET

0E87,CHKS4 NZ A,(NTDEST) A NZ,0FBE,SEND-SCOUT A,(NTLEN) (NCIBL),A (NCNUMB-lo) NZ,0EA1,GETBF-END (NCNUMB-hi) A

Repeat for all bytes in the block. Return if the checksum is wrong. This holds 0 when broadcasting. Send response code for the data block (unless broadcasting). Fetch data block length. Copy it into NCIBL. Finally increment block number. Return with zero flag set to signal 'block successfully received'.

THE 'OPEN "N" CHANNEL' COMMAND ROUTINE This routine is the actual OPEN command referred to the network. It is called from the 'OPEN' command syntax routine to attach a permanent "n" channel to a stream. 0EA3 0EA6 OPEN-N-ST CALL JP 0EB5.OP-PERM-N 0B4A,OP-STREAM Open a permanent "n" channel. Attach it to a stream.

THE 'OPEN TEMPORARY "N" CHANNEL' SUBROUTINE This subroutine is also called by using the 'hook code' +2D. It simply opens a permanent 'n' channel, and then makes it "temporary" by setting bit 7 of the channel specifier. The variables are to be set as for the OP-PERM-N subroutine below. The start of the channel area is returned in the IX index register. 0EA9 OP-TEMP-N 0EAC 0EB1 0EB4 CALL LD SET RET 0EB5,OP-PERM-N IX,(CURCHL) 7,(IX+4) Open a permanent "n" channel. Fetch start of channel area. Make the channel temporary. Finished.

THE 'OPEN PERMANENT "N" CHANNEL" SUBROUTINE On entry, D-STR1 must hold the destination station number to be copied into NCIRIS, while NTSTAT must hold the own station number to be copied into NCSELF. A permanent "n" channel is created into the CHANS area, and it is made, the 'current' channel. The routine returns the channel base address into the DE register pair. 0EB5 0EB8 0EB9 0EBC 0EBD 0EBE 0EC3 0EC1 0EC2 0EC5 0EC8 0EC9 0ECC 0ECF 0ED1 0ED4 0ED5 0ED6 0ED9 0EDA 0EDB OP-PERN-N LD HL,(PROG) DEC HL LD BC,+0114 PUSH BC RST 10,CALBAS DEFW +1655 INC HL POP BC CALL I691,RESThN.,.AD LD EX LD LD LDIR LD LD INC LD LD INC XOR (CHURCHL),HL DE,HL HL,+0EEA BC,+000B A,(DSTR-1) (DE),A DE A,(NTSTAT) (DE),A DE A Fetch start address of the channel created. Length of the channel. Save it briefly. Call main ROM 'MAKE-ROOM' subroutine to make the required space. Point to the first new location. Restore 'length' of space inserted. Restore start address of 'filename' possibly moved up after the insert ion. Make the Channel 'current' Pass 'start' to DE. Start of "n" Channel data. Length of data. Copy the data into the channel area. Fetch destination station number. Copy it into NCIRIS. Points to NCSELF. Fetch own station number. Copy it into NCSELF. Points to NCNUMB. Clear it.

0EDC 0EDD 0EDE 0EDF 0EE0 0EE3 0EE5 0EE9

LD LD LD INC LD LDIR LD RET

(DE),A H,D L,E DE BC,+0106 DE,(CURCHL)

Fill the remaining bytes with zeros.

Fetch start address of the channel. Finished.

THE '"N" CHANNEL DATA' TABLE The '11' bytes that composes the initial part of a "N" channel are as follows: 0EEA 0EEC 0EEE 0EEF 0EF1 0EF3 DEFW DEFW DEFB DEFW DEFW DEFW +0008 +0008 +43 +0D6C +0D0C +0114 Main ROM 'output' routine. Main ROM 'input' routine. "N" (channel specifier) Shadow ROM 'output' routine. Shadow ROM 'input' routine. Length of "n" channel.

THE 'SEND EOF BLOCK TO NETWORK' SUBROUTINE This subroutine is used whenever the remaining buffer contents of the current "n" channel have to be sent as the "end of file" block. 0EF5 0EF9 0EFC 0EFD 0EFE 0F00 SEND-NEOF LD LD AND RET LD JP IX,(CURCHL) A,(NCOBL) A Z A,+01 0DAB,S-PACK-1 Fetch start address of channel. Fetch data block length. Return if this is a 'read' channel (NCOBL=0). Signal 'EOF' packet. 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' as to prevent Spectrums from claiming the network at the same time. 0F03 0F05 0F07 0F08 0F0B 0F0D NET-STATE LD CP LD CALL JR RET A,R +C0 B,A 0F8E,CHK-REST C,0F03,NET-STATE Get a random value. Allow only the range 192..255. Pass the value to B. Check network state. Repeat until the network is resting, or BREAK pressed. 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 to the B register ((B * 54) - 22) T cycles). 0F0E 0F10 0F12 0F13 0F15 0F16 0Fl7 0F19 0F1A 0F1B 0F1D CHK-REST LD A,+7F IN A,(+FE) RRCA JR NC,0F4D,E-READ-N PUSH BC POP BC IN A,(+F7) RRCA RET C DJNZ 0F15,MAKESURE RET First check SPACE key. Give an error if it is pressed. Wait 21 T cycles. Check network state. Only bit 0. Return if the network is already claimed by another Spectrum. Repeat the test. Return with carry reset when network is resting.

MAKESURE

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 the carry flag reset if 'time-out' has occurred and no SCOUT has been identified. Remember that there are no 'time-outs' when broadcasting. 0F1E 0F21 0F23 0F26 0F28 0F29 0F2A 0F2B 0F2C 0F2E 0F31 0F32 0F34 WT-SCOUT CLAIMED LD LD CALL JR DEC DEC LD OR JR LD AND JR RET HL,+01C2 B,+80 0F9E,CHK-REST NC,0F35,WT-SYNC HL HL A,H C NZ,0F21,CLAIMED A,(NCIRIS) A Z,0F21,CLAIMED Set a counter. This constant allows the network to be tested for 6890 T cycles. Check network state. Jump for waiting the SCOUT. Loop again if the network is active, until the counter reaches zero.

There is no 'time-out' when broadcasting. Return with carry reset to signal 'time-out'.

Now the SCOUT pulse is waited for. 0F35 0F37 0F38 0F3A 0F3C 0F3E 0F3F 0F41 0F42 0F43 0F44 0F46 0F49 0F4A 0F4C 0F4D 0F4E 0F51 0F55 WT-SYNC IN A,(+F7) RRCA JR C,0F56,SCOUT-END CD A,+7F IN A,(+FE) RRCA JR NC,0F4O,E-READ-N DEC HL LD A,H OR C JR NZ,0F35,WT-SYNC CD A,(NCIRIS) AND A JR Z,0F35,WT-SYNC RET EI CALL 0CA9,BORD-REST LD (ERR-NR),+14 RST 28,ROMERR Read the network line. Only bit 0. Jump if the SCOUT is identified. Check SPACE key. Give an error if it is pressed. decrease counter. Repeat until it reaches zero. There is no 'time-out' when broadcasting. Carry reset signals 'time-out', Enable interrupts. Restore border colour. Give the error 'BREAK into program'.

E-READ-N

NOTE: The above routine is a 'duplicate' of BRK-INOUT at 0CB4. When the SCOUT has been identifIed, the routine waits until it is finished. 0F56 0F58 0F59 0F5A 0F5B 0F5D 0F5F SCOUT-END LD LP-SCOUT DEC SCF RET DELAY-SC LD DJNZ JR L,+09 L Z B,+0E 0F5D,DELAY-SC 0F58,LP-SCOUT Set a counter. Decrease it. Return with carry set (to signal 'scout identified') when the counter reaches zero. Wait a while. 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.

0F61 0F64 0F66 0F69 0F6C 0F6D 0F6F 0F70 0F72 0F74 0F75 0F77 0F79 0F7B 0F7D 0F7F 0F81 0F83 0F84 0F86 0F87 0F89 0F8B 0F8D 0F8F 0F91

SEND-SC

CALL LD LD LD LD IN RRCA JR OUT LD LD RLC RL LD DJNZ IN AND CP JR

0F03,NET-STATE C,+F7 HL,+0009 A,(NTSTAT) E,A A,(+F7) C,0F61,SEND-SC (C),H D,H H,+00 E H B,+08 0F7D,S-SC-DEL A,(+F7) +01 D Z,0F61,SEND-SC L NZ,0F72,ALL-BITS A,+01 (+F7),A B,+0E 0F8F,END-S-DEL

Wait until the network Is resting. Output port. H=0 (leader bit value) L=9 (bit counter). Fetch global station number, Pass it to the E register. Start again if some data is found into the network line. Send a bit. Save the bit into D. Clear H. Rotate 'station no.' left. Bit 7 of station no. goes into bit 0 of H. Wait 196 T cycles. Read the network. Only bit 0. Repeat again the procedure if the network does not have the right state from the current bit value. Decrease bit counter. Send all bits. Make network inactive. Wait 184 T cycles. Finished.

ALL-BITS

S-SC-DEL

DEC JR LD OUT LD END-S-DEL DJNZ 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. 0F92 0F94 0F96 0F97 0F99 0F9B 0F9C INPAK N-ACTIVE LD IN RRA JR BJNZ INC RET B,+FF A,(+F7) C,0F9D,INPAK-2 0F94,N-ACTIVE B Set a counter. Read the network. Only bit 3. Jump if found active. Otherwise try again. Return with zero flag reset to indicate 'network inactive'.

Now the block is received. 0F9D 0F9E 0FAI 0FA2 0FA4 0FA5 0FA6 0FA8 0FAA 0FAC 0FAE 0FB0 0FB2 0FB3 0FB5 INPAK-2 INPAK-L LD LD LD OUT NOP NOP INC DEC INC DEC LD IN RRA RR JP B,E E,+80 A,+CE (+EF),A IX IX IX IX A,+00 A,(+F7) E NC,0FAE,UNTIL-MK B holds the length of the block. Set a marker into bit 7. Make Wait and CTS having a low level, enable network comm. Wait 48 T cycles at the start of each byte.

UNTIL-MK

Wait 7 T cycles. Get a bit into carry flag. Shift the bit into E. Repeat for 8 bits.

0FB8 0FB9 0FBA 0FBC 0FBD

LD INC DJNZ CP RET

(HL),E HL 0F9E,INPAK-L A

Store the received byte. Point to next location. Get next byte. Return with zero flag set to signal 'successfully read'.

THE 'SEND RESPONSE BYTE' SUBROUTINE A 'response byte' is simply a byte +01 that is sent over the network to confirm that some data has been successfully received. 0FBE 0FC0 0FC3 0FC4 SEND-RESP LD LD LD LD A,+0l HL,+5CCD (HL),A E,A This is the response byte. Store it into NTRESP. A 'single byte' is to be sent.

The subroutine continues into OUTPAK below. THE 'OUTPAK' SUBROUTINE This is the opposite to the INPAK subroutine network a block of bytes. Again, on entry HL block is stored, and E must hold its length. cycles) is sent, followed by each byte, with the 8 bits (each 40 T cycles). 0FC5 0FC6 0FC8 0FCA 0FCC 0FCD 0FCE 0FCF 0FD0 0FD2 0FD4 0FD5 0FD6 0FD7 0FD9 0FDC 0FDD 0FDE 0FDF 0FE0 0FE3 0FE5 0FE7 OUTPAK DEL_O-1 OUTPAK-L XOR OUT LD DJNZ LD CPL SCF RLA LD OUT RRA AND DEC LD JP INC DEC PUSH POP JP LD OUT RET A (+F7),A B,+04 0FCA,DEL_O-1 A,(HL) and thus, is used to send over the must hold the address fro which the Initially a leader (length 98 T an initial 'start' period, and then

Begin with a 'start' leader. Insert the required delay. Get the byte to be sent, Send the bits inverted. The start bit is high. Rotate bit into A. '10' bits to be sent (start, data, stop). Send the bit. Next bit. But clear carry. becrease bit counter. Wait 7 T cycles. Loop for all bits. Point to next location in the block. Decrease 'block length'. Wait 22 T cycles. Loop until all bytes have been sent. Finally make the network inactive. Finished.

B,+0A (+F7),A A B D,+00 NZ,0FB2,UNT-MARK HL E HL HL NZ,0FCC,OUTPAK-L A,+01 (+F7),A

UNT-MARK

The Microdrive routines THE 'SET A TEMPORARY "M" CHANNEL' SUBROUTINE This very important subroutine sets a temporary "M" channel in the CHANS area; the Microdrive map is created unless it already exists (if another channel refers to the same drive). The subroutine returns with the IX register pointing to the start of the channel, and with the HL register holding a suitable displacement to be eventually inserted in the STRHS area to attach the channel to a stream. Note that the HL' register is corrupted if you call this routine from your own program. 0FE8 0FE9 SET-T-MCH EXX LD HL,+0000 Swap registers. Signal 'map to be created'.

0FEC 0FED 0FF1 0FF4 0FF6 0FF9 0FFB 0FFD 1000 1002 1004 1006 1009 l00C 100E 100F 1012 1015 1016 101A 101D 1020 1022 1026 1028 1029 102A 102D 1030 1032

CHK-LOOP

EXX LD LD ADD LD CP JR LD AND CP JR LD CP JR EXX LD LD LXX LD LD CALL JR BIT JR RST DEFB

IX,(CHANS) DE,+0014 IX,DE A,(IX+0) +80 Z,1034,CHAN-SPC A,(IX+4) +7F +4D NZ,102A,NEXT-CHAN A,(D-STR1) (CHDRIV) NZ,102A,NEXT-CHAN L,(CHMAP-lo) H,(CHMAP-hi) BC,(N-STR1) HL.(N-STR1+2) 131E,CHK-NAME NZ,102A,NEXT-CHAN 3,(IX+24) Z,102A,NEXT-CHAN 20,SH-ERR +0D E,(IX+9) D,(lX+10) IX,DE 0FF6,CHK-LOOP

Swap registers again. Start of channel area. Points to the start of the channels Other than the 'standard' ones. Jump forward it the CHANS area is finished. Fetch channel specifier. Clear bit 7. Jump if this is not an "m" channel. Fetch drive number. Jump if this channel uses a different drive. Swap registers. Move address of map area into H'L' Swap registers again. Length of filename. Start address of filename. Check name against 'CHNAME' of this channel. Jump if not the same file. This is set with 'write' files. Continue with 'read' files. Give the 'Reading a 'write' file' error if the file is already opened for writing. Fetch length of channel into the DE register pair. Point to the next channel. Back again.

NEXT-CHAN LD LD ADD JR

Now the space for the new channel is created at the end of the CHANS area. 1034 1037 1038 1039 103C 103D 103E 1040 1041 1044 1047 1049 104C 104F 1052 1054 1055 1058 1059 105D 105F CHAN-SPC LD HL,(PROG) DEC HL PUSH HL LD BC,+0253 RST 10,CALBAS DEFW +1655 POP DE PUSH DE LD HL,+13CC LD BC,+0019 LDIR LD A,(D-STR1) LD (CHDRIV),A LD BC,+0253 PUSH IX POP HL CALL 1691,REST-N-AD EX LD BIT JR DE,HL BC,(N-STR1) 7,B NZ,106F,TEST-MAP Fetch the start address of the channel, Save it. Length is '595' bytes. Call MAKE-ROOM to create the required space. Restore start address of the channel. Start of "M" channel data. Length of data. Store data into the channel. Fetch drive number. Initialise CHDRIV. Length of channel. Fetch start of channel into HL. Restore start of 'filename' possibly moved after the 'insertion' of the channel. The start address of the filename goes to HL. Fetch length of filename. Jump if the name does not exist (N-STR1 = +FFFF).

The channel name is transferred into CHNAME. 1061 T-CH-NAME LD A,B The loop continues until 'length'

1062 1063 1065 1066 1069 l06A 106C 106D

OR JR LD LD INC INC DEC JR

C Z,106F,TEST-MAP A,(HL) (IX+14),A HL IX BC 106l,T-CH-NAME

reaches zero. Fetch character of name. Store it into CHNAME. Point to next locations. Decrease 'length'. Continue with next character.

Now the 'map' is created unless it already exists. 106F 1071 1072 1073 1074 1076 1079 107A 107B 107E 107F 1081 1082 1085 1087 108A 108E TEST-MAP POP EXX LD OR JR LD PUSH DEC LD RST DEFW POP LD ADD CALL ST-MAP-AD LD LD IX A,H L NZ,108A,ST-MAP-AD HL,(CHANS) HL HL BC,+0020 10,CALBAS +1655 HL BC,+0020 IX,BC 1691,REST-N-AD (CHMAP-lo),L (CHMAP-hi),H Restore start address of channel. Use alternate registers. Address of map, or +0000 if the map does not exist. Jump if the map already exists. Otherwise the map is to be created. Length of the map is 32 bytes. Call MAKE-ROOM to make the space. Restore start of map. Restore 'start' of channel. Restore 'start' of filename. Lastly store the start address of the Microdrive map.

All bits in the map are made high; then the 'preambles' are collected from the table at +13E5 and stored in the channel. 1090 1092 l094 1095 1096 1098 109A 109B 109E 109F 10A0 l0A3 10A6 10A8 10AA 10AB 10AE 10B1 10B2 10B3 10B6 10B8 10BA 10BB 10BF 10C0 10C2 10C3 LD LD LD INC DJNZ PUSH POP LD ADD EX LD LD LDIR PUSH POP LD LD ADD EX LD LDIR PUSH POP LD OR SBC INC RET A,+FF B,+20 (HL),A HL 1094,FILL-MAP IX HL DE,+001C HL,DE DE,HL HL,+13E5 BC,+000C IX HL DE,+0037 BC,+000C HL,DE DE,HL HL,+13E5 IX HL DE,(CHANS) A HL,DE HL All bits high. Length of map. Store +FF into this location. Advance the pointer. Continue for 32 bytes. Pass start of channel to HL. Make DE register pair point to the header block preamble area. Start of 'preamble' data. Preamble is 12 bytes in length. Set-up header preamble. Pass again 'start' to HL. Offset for data block preamble. Preamble is again 12 bytes. Make DE register pair point to the data block preamble. Start of 'preamble data'. Set-up the data block preamble. Move 'start' of channel into HL register pair. Calculate the required 'stream offset' into HL. Finished.

FILL-MAP

THE 'RECLAIM "M" CHANNEL' SUBROUTINE

This subroutine (also called by using 'hook code' +2C) is used to reclaim the "m" channel pointed by the IX index register. Any stream attached to this channel will be closed, and other 'dynamic' areas updated. The map is also reclaimed, unless it is used by another channel. 10C4 10C7 10CA 10CB 10CE 10CD 10D1 10D2 10D5 10D6 10D8 10DA 10DB 10DF 10E0 10E2 l0E3 10E6 DEL-M-BUF LD LD PUSH LD PUSH PUSH POP LD RST DEFW PUSH POP LD OR SBC INC LD CALL L,(CHMAP-lo) H,(CHMAP-hi) HL A,(CHDRIV) AF IX HL BC,+0253 10,CALBAS +19E8 IX HL DE,(CHANS) A HL,DE HL BC,+0253 l35F,REST-STRM Fetch address of map into HL. Save it for later. Fetch drive number. Save it for later. Make HL point to the start of the channel. Length of the channel. RECLAIM-2 is called to delete the channel area. Make HL point again to the start of the (reclaimed) channel. Calculate 'offset' used into STRMS area for the deleted channel, possibly attached to a stream. Amount of bytes moved down. Close required streams and update data for other streams.

Look for any channel that uses the same map as that of the channel deleted. 10E9 10EA 10EB 10EC 10F0 10F3 10F5 10F8 10FA 10FC 10FF 1101 1103 1105 1108 1109 110A 110D 1110 1112 NXTCHAN POP POP LD LD LD ADD TEST-MCHL LD CP JR LD AND CP JR LD CP RET LD LD ADD JR AF HL B,A IX,(CHANS) DE,+0014 IX,DE A,(IX+0) +80 Z,1114,RCLM-MAP A,(IX+4) +7F +4D NZ,113A,NXTCHAN A,(CHDRIV) B Z E,(IX+9) B,(IX+10) IX,DE 10F5,TEST-MCHL Restore drive number, and start address of map. Drive number goes into B. Start of channel area. Skip initial 'standard' channels. Jump if the channel area is finished. Fetch channel specifier. Clear bit 7 (no distinction is made between 'temporary' and 'permanent' channels). Jump if this is not an "m" channel. Fetch drive number of this channel. If it is the same as that of the deleted channel, the map is not to be reclaimed. Fetch length of this channel. Points to next channel. Back again for next channel.

If no channel uses the map used by the deleted one, it is to be deleted too. 1114 1117 1118 1119 111A 111C 111D RCLM-HAP LD PUSH PUSH RST DEFW POP POP BC,+0020 HL BC 10,CALBAS +19E8 BC HL Length of the map. Save start of map. Save length of map. Note: this is not necessary. Call RECLAIM-2 to delete the map. Restore length and start of the deleted map.

111E 1121

CALL RET

1391,REST-MAP

Update addresses of other maps. Finished.

THE '"M" CHANNEL INPUT' ROUTINE The actual 'input' is handled via CALL-INP. The service routine is MCHAN-IN below. 1122 1126 1129 M-INPUT LD LD JP IX,(CURCHL) HL,+112C 0CBD,CALL-INP First make IX point to start of channel. Address of MCHAN-IN routine. Jump to the control routine.

THE '"M" CHANNEL INPUT' SERVICE ROUTINE Similarly to the "n" input, the "m" input involves the reading of a byte from the data block. When it is empty, a further block is to be received from Microdrive (Provided that the 'current' data block is not the 'end of file' one) before reading the byte. 112C MCHAN-IN 1130 BIT JR 0,(CHFLAG) Z,1134,TEST-M-BF This is reset to indicate a 'read' file. Jump with 'read' file.

'Reading a write file' 1132 1133 1134 1137 113A 113D 1140 1141 1143 1145 1146 1149 114C 114D 114F 1151 1154 1156 1157 RWF-ERR RST DEFB 20,SH-ERR +0D E,(CHBYTE-lo) D,(CHBYTE-hi) L,(RECLEN-lo) H,(RECLEN-hi) HL,DE C,1158,CHK-M-EOF DE (CHBYTE-lo),E (CHBYTE-hi),D DE IX IX,DE A,(IX+82) IX Call the error handling routine. Fetch current byte counter. Fetch record length. Include byte to be read. Jump if all bytes have been read. Include byte to be read in the byte counter. Store pew byte Counter. Position of character to be read. Save start address of channel. IX now points to 'byte to be read - 82'. Fetch the byte. Restore start of channel. Signal 'acceptable code'. Finished.

TEST-M-BF LD LD LD LD SCF SEC JR INC LD LD DEC PUSH Abb LD POP SCF RET

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. 1158 115C 115E 115F 1161 CHK-M-EOF BIT JR XOR ADD RET 1,(RECFLG) Z,1162,NEW-BUFF A A,+0D This is set to signal 'EOF'. Jump if not the last block. Otherwise zero and carry flags are returned reset to signal EOF. Returned byte is +0D. Finished.

A new data block is now read from the Microdrive unit. 1162 1165 1168 116B NEW-BUFF LD LD LD INC DE,+0000 (CHBYTE-lo),E (CHBYTE-hi),D (CHREC) The byte counter is cleared. Increment record number.

116E 1171 1172 1175

CALL XOR CALL JR

1177,GET-RECD A 17F7,SEL-DRIVE 1134,TEST-M-BF

Fetch a new data block. Turn off drive motor. Now read the byte.

THE 'GET A RECORD' SUBROUTINE This subroutine is used to load a record of a 'PRINT-type file. The number of the wanted record must be stored into CHREC, the drive number into CHDRIV and the filename into CHNAME. If after five passes of the tape the record is not found, or if a checksum error occurs, the error 'File not found' is given. 1177 117A 117D 1180 1184 1187 1189 118B 118E 1191 1193 1195 1196 1199 119A 119D 119E 11A1 GET-RECD GET-R-2 GET-R-LP LD CALL LD LD CALL JR JR CD CP JR PUSH POP LD ADD CALL RET CALL JR A,(CHDRIV) 17F7,SEL-DRIVE BC,+04FB (SECTOR),BC 11A5,G-HD-RC C,119E,NXT-SCT Z,119E,NXT-SCT A,(RECNUM) (CHREC) NZ,119E,NXT-SCT IX HL DE,+0052 HL,DE 1346,CHKS-BUFF Z 1312,DEC-SECT NZ,1184,GET-R-LP Fetch drive number. Start drive motor. Initialise SECTOR to 1275 (i.e., at least five passes of the tape). Get header and data block. Consider next sector with errors, or if it is unused. Fetch record number. Test against CHREC. Next sector also if this record is not the expected one. Fetch base of "m" channel into HL. Points to the start of the buffer. Return if the checksum is correct. Decrease SECTOR. Continue until five passes of the tape have been made. Call the error handling routine.

NXT-SCT

'File not found' 11A3 11A4 RS-SH2 RST DEFB 20,SH-ERE +11

THE 'GET HEADER AND DATA BLOCK' SUBROUTINE This subroutine is used to collect from the selected Hicrodrive unit a header followed by its data block. Note that the drive motor is to be turned on before calling G-HD-RC. The flags returned denote the following states: - zero set, carry reset - zero reset, carry reset - carry set The sector is unused. Successful loading. With any error.

The 'errors' include wrong checksums, and no matching between the wanted filename (CHNAME) and the loaded one (RECNAM). 11A5 11A8 11AB 11AC 11AF 11B2 11B4 11B8 11BA 11BD 11C0 11C2 11C3 11C5 G-HD-RC CALL LD ADD CALL CALL JR BIT JR LD OR AND RET PUSH POP 12C4,GET-M-HD2 DE,+001B HL,DE 18A9,GET-M-BF 1341,CHKS-HD-R NZ,11D6,G-REC-ERR 0,(RECFLG) NZ,11D6,G-REC-ERR A,(RECFLG) (RECLEN-hi) +02 Z IX HL Fetch the header. Make HL point to RECLCG, i.e., start of record area. Fetch the record. Calculate record checksum. Exit if the checksum is wrong. This is set to signal a header block. Exit if a header instead of a data block has been fetched. Bit 1 of RECFLG and of REGLEN-hi are both reset with 'unused sector'. Take only bit 1. Return with unused sectors. Make HL point to the start of the channel.

11C6 11C9 11CA 11CD 11D0 11D2 11D4 11D5 11D6 11D7

LD ADD LD CALL JR LD OR RET G-REC-ERR SCF RET

DE,+0047 HL,DE BC,+000A 131E,CHK-NAME NZ,11D6,G-REC-ERR A,+FF A

Points to RECNAM. Name is ten characters in length. Compare it against CHNAME. Exit if they do not match. Exit with zero and carry flags reset when successful. Exit with carry set if any error has been detected.

THE '"M" CHANNEL OUTPUT' ROUTINE The routine is very similar to that for the "N" channel output. The byte passed to the accumulator is stored into the 512-byte buffer. When it is filled, the record is written onto the Microdrive cartridge. 11D8 11DC 11E1 11E2 MCHAN-OUT LD ADD BIT JR IX,+FFFA IX,DE 0,(CHFLAG) NZ,11E6,NOREAD Point to the start of the channel (i.e. DE-6, see 00BA). Continue only if this is a 'write' file.

'Writing to a 'read' file' 11E4 11E5 11E6 11E9 11EC 11EE 11F0 11F3 11F5 11F6 11F9 11FC 11FE NOREAD RST DEFB LD LD PUSH ADD LD POP INC LD LD BIT RET 20,SH-ERR +0C E,(CHBYTE-lo) D,(CHBYTE-hi) IX IX,DE (IX+82),A IX DE (CHBYTE-lo),E (CHBYTE-hi),D 1,D Z Call the error handling routine. Fetch byte counter. Save start address of channel. Now IX points to 'first free byte in the buffer'-82. Store a byte in the buffer. Restore start address of channel. Points to first 'free byte' in the buffer. Update byte counter, Return if the buffer is not filled (position 512 has not been reached).

If the buffer is filled, the routine continues into WR-RECD below. THE 'WRITE A RECORD ONTO MICRODRIVE' SUBROUTINE This subroutine is also called by using hook code +26. The record held in the "m" channel pointed by the IX register (with name CHNAME and number CHREC), is written into the first unused sector in the cartridge inserted in the drive CHDRIV. Then the appropriate map bit is set and CHREC is automatically incremented. An error is produced if no more space is available on the cartridge to write the record. 11FF 1202 1205 1208 1209 120C 120D 1210 1212 1215 1216 WR-RECD LD CALL CALL XOR CALL RET WRITE-PRC CALL JR CALL XOR CALL A,(CHDRIV) 17F7,SEL-DRIVE 120D,WRITE-PRC A 17F7,SEL-DRIVE 1264,CHK-FULL NZ,121B,NOFULL 10C4,DEL-M-BUF A 17F7,SEL_DRIVE Fetch drive number. Turn on the motor. Write the record. Turn off the motor. Finished. Check if the cartridge is full, and jump if it is not. Otherwise reclaim the channel. Turn off drive motors.

'Microdrive full'

1219 121A 121B 121D 121F 1222 1225 1227 1229 122B 122E 1231 1234 1237 l23A 123D 123F 1240 1243 1244 1247 124A 124B 124E 1250 1251 1254 1257 125A 125D 1260 1263 NOFULL CP-NAME

RST DEFB PUSH LD LD LD INC DJNZ POP LD CD LD LD LD LD PUSH POP LD ADD CALL LD ADD CALL PUSH POP LD CALL LD LD LD INC RET

20,SH-ERR +0F IX B,+0A A,(IX+14) (IX+71),A IX 121F,CP-NAME IX C,(CHBYTE-lo) (RECLEN-lo),C A,(CHBYTE-hi) (RECLEN-hi),A A,(CHREC) (RECNUM),A IX HL DE,+0043 HL,DE 1341,CHKS-HD-R DE,+000F HL,DE 1346,CHKS-BUFF IX HL DE,+0047 1275,SEND-BLK DE,+0000 (CHBYTE-lo),E (CHBYTE-hi),D (CHREC)

Call the error handling routine. Save base address of channel. Counts ten characters. Copy CHNAME into RECNAM.

Restore start of channel. Copy CHBYTE into RECLEN.

Copy CHREC into RECNUM. Make HL point to the start of the data block workspace, i.e. RECFLG. Calculate DESCHK checksum. Hake HL point to the start of the 512-byte buffer. Calculate DCHK checksum. Three useless instructions. Send the record to Microdrive. Clear CHBYTE. Finally increment record number. Finished.

THE 'CHK-FULL' SUBROUTINE This subroutine is used to check whether the currently used Microdrive unit contains a full cartridge, with no 'free tot use' sectors. The zero flag is returned set if the cartridge is full (i.e. if all map bits are set). 1264 1267 i26A 126C 126D 126F 1273 1271 1273 1274 CHK-FULL LD LD LD NXT-B-MAP LD CP RET INC DJNZ XOR RET L,(CHMAP-lo) H,(CHMAP-hi) B,+20 A,(HL) +FF NZ HL 126C,NXT-B-MAP A Fetch address of the map. Length of the map. Fetch a byte. Exit with zero flag reset if not all bits are set. Point to the next byte, Loop for all bytes. Return with zero flag set to signal that the cartridge is full.

TIlE 'SEND-BLK' SUBROUTINE The following subroutine fetches the first 'free' header and then writes the buffer into the sector, provided that the cartridge is not write-protected. Finally the map bit is set. 1275 1277 1278 127B l27C 127D 1280 SEND-BLK PUSH POP LD ADD PUSH CALL CALL IX HL DE,+0037 HL,BE HL 12C4,GET-M-HD2 12DF,CHECK-MAP Make HL point to the data block preamble. Save this address. Fetch the first header. Check map bit for this header.

FAILED

1283 1285 1286 1287 1289 128B

JR EX

NZ,127D,FAILED (SP),HL

PUSH BC IN A,(+EF) AND +01 JR NZ,128F,NO-PRT

Continue if the sector is not 'free for use'. Now (SP) holds the map bit address; and HL the start of the data block preamble. Save map bit position. Jump if the write-protect tab is present.

'Drive 'write' protected' 128D 128E 128F 1291 1293 1296 1299 129C 129E 12A0 12A1 12A2 12A3 12A4 12A5 NO-PRT RST DEFB LD OUT LD CALL CALL LD OUT POP POP LD OR LD RET 20,SH-ERR +0E A,+E6 (+EF),A BC,+0168 18FA,DELAY-BC 1878,OUT-M-BUF A,+EE (+EF),A BC HL A,B (HL) (HL),A Call the error handling routine. Start the writing process. Wait until the first gap is finished. Write preamble and data block. Send a signal when finished. Restore map bit position. Restore map bit address. Set the required map bit. Finished.

THE 'CLOSE FILE' SUBROUTINE This subroutine CLOSEs a 'PRINT-type' "m" channel. 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 onto the Microdrive cartridge before reclaiming the channel. The entry point CLOSE-M is used when the start address of the channel is held in the HL register pair, while CLOSE-M2 (also used by 'hook code' +23) is used when that address is held in the IX index register. 12A6 12A7 12A9 12AD 12AF 12B3 1236 1237 12BA 12BD CLOSE-M CLOSE-M2 PUSH POP BIT JR SET CALL XOR CALL CALL RET HL IX 0,(CHFLAG) Z,12B6,NOEMP 1,(RECFLG) 11FF,WR-RECD A 17F7,SEL-DRIVE 10C4,DEL-M-BUF Make IX register point to the start of the channel. Jump forward when 'reading'. Otherwise signal 'EOF' record, and write it onto drive. Switch off motor. Reclaim the channel. Finished.

NOEMP

THE 'MAIN ERROR RESTART' EMULATION ROUTINE This routine, called in the format 'CALL ERR-RS / DEFB nn' emulates the sequence 'RST 8 / DEFB nn' that is used (when the main ROM is paged in) to give an error report. ERR-RS is never called from the shadow ROM code. 12BE 12BF 12C0 12C3 ERR-RS POP LD LD RST HL A,(HL) (ERR-NR),A 28,ROMERR Fetch return address (points to the error code). Fetch error code. Store it. Give the error report.

THE 'FETCH HEADER FROM HICRODRIVE' SUBROUTINE This subroutine is used to fetch a header from the current Microdrive unit (whose motor must be turned on). The header is loaded into HDFLG...HDCHK. The procedure is repeated unless the checksum is correct.

12C4 12C6 12C7 12CA 12CB 12CE 12D1 12D3 12D7 12D9

GET-M-HD2 PUSH POP LD ADD CALL CALL JR BIT JR RET

IX HL DE,+0028 HL,DE 18A3,GET-M-HD 1341,CHKS-HD-R NZ,12C4,GET-M-HD2 0,(HDFLAG) Z,12C4,GET-M-HD2

Make HL point to MDFLAG, i.e. location from which the header will be loaded. Fetch the header. Calculate checksum. Repeat if it does not match with the 'old' checksum. Repeat if a record descriptor has been loaded instead of a header. Finished.

THE 'CHECK MAP BIT STATE' SUBROUTINE The bit correspondent to a given sector in the microdrive map is checked. The zero flag is returned set if the bit was reset, and vice-versa. Also the B register will hold, on return, a bit set in the position of the map bit; HL will hold the address of that map bit. The entry point is CHK-MAP-2 when the sector number has to be collected from RECNUM, or CHECK-MAP if from HDNUMB. 12DA 12DD 12E1 12E2 12E5 12E8 12E9 12EA 12EB 12ED 12EF 12F1 12F3 12F4 12F5 12F6 12F7 12F8 12F9 12FB 12FC 12FD CHK-KAP-2 CD JR CHECK-MAP LD ENTRY LD LD ENTRY-2 XOR LD LD AND SRL SRL SRL ADD LD INC XOR SCF RLA DJNZ LD AND RET E,(RECNUM) 12E2,ENTRY E,(HDNUMB) L,(CHMAP-lo) H,(CHMAP-hi) A D,A A,E +07 E E E HL,DE B,A B A 12F8,ROTATE B,A (HL) Fetch sector number. Jump forward. Fetch sector number. Fetch map start address. Clear D register. Move sector number to A. Only 3 less significant bits, (i.e. 'bit position'). Shift out the 'position' from E. Calculate map bit address. Pass 'position' to B. Range is now 1..8. Clear A register. Set carry flag. Set a bit in the correct position. Exit with B holding that bit. Set zero flag as required. Finished.

ROTATE

THE 'RESET BIT IN MAP AREA' SUBROUTINE This subroutine is used to reset the bit in the map area corresponding to the sector HDNUMB. 12FE 1301 1302 1303 1304 1305 RES-B-MAP CALL LD CPL AND LD RET 12DF,CHECK-MAP A,B (HL) (HL),A Set B and HL registers with position and map address. Pass bit to be reset to A. Reset only that bit and leave other bits unchanged. Store new map byte. Finished.

THE 'CHECK "PSEUDO-MAP" BIT STATE' SUBROUTINE This subroutine is apparently equal to the CHECK-MAP subroutine at 12DA, however it does not refer to the standard map, but to a 'pseudo-map' set up in the 512byte buffer of the "m" channel. This map is used from the ERASE command to mark the sectors to be erased, 1306 TEST-PMAP PUSH IX Make HL point to the start of the

1308 1309 130C 130D 1310

POP LD ADD LD JR

HL DE,+0052 HL,DE E,(HDNUMB) 12E8,ENTRY-2

buffer. I.e. to the start of the 'pseudo-map' area. Fetch sector number. Continue into CHECK-MAP.

THE 'DECREASE SECTOR NUMBER' SUBROUTINE This short subroutine is frequently called to decrease the content of the system variable SECTOR, used to count a given number of sectors during microdrive operations. The zero flag is returned set when SECTOR reaches zero. 1312 1316 1317 131B 131C 131D DEC-SECT LD DEC LD LD OR RET BC,(SECTOR) BC (SECTOR),BC A,B C Decrease (SECTOR). Set zero flag if SECTOR has reached zero. 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 comparison is successful, the zero flag is returned set. 131E 1320 1322 1323 1326 1328 1329 132B 132C 132D 132F 1330 1331 1333 1336 1338 133A 133C 133E 1340 CHK-NAME PUSH LD ALL-CHARS LD CP JR INC INC DEC DEC JR LD OR JR ALLCHR-2 LD CP JR INC DJNZ CKNAM-END POP RET IX B,+0A A,(HL) (IX+14) NZ,133E,CKNAM-END HL IX B C NZ,1322,ALL-CHARS A,B A Z,133E,CKNAM-END A,(IX+14) +20 NZ,133E,CKNAM-END IX 1333,ALCCHR-2 IX Save start of channel. Length of CHNAME. Fetch a byte from name. Compare it against CHNAME. Jump if it does not match. Point to next character. Decrease 'lengths'. Continue until the length of the name reaches zero. CHNAME remaining length. Exit if successful (CHNAME length has reached zero). Otherwise check that the following characters of CHNAME are spaces. Signal 'unsuccessful' if not spaces. Continue until the whole CHNAME has been examined. Restore channel start address. Finished.

THE 'CALCULATE/COMPARE CHECKSUMS' ROUTINE This routine is used to calculate HDCHK, 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 CHKS-HD-R for HDCHK or DESCHK, or CHKS-BUFF for DCHK checksum. In both cases HL must contain on entry the start address of the block for which the checksum is to be obtained. 1341 1344 1346 1349 134A 134C 134D 134E 134F 1351 CHKS-HD-R LD JR CHKS-BUFF LD CHKS-ALL PUSH LD NXT-BYTE LD ADD INC ADC JR BC,+000E 1349,CHKS-ALL BC,+0200 HL E,+00 A,E A,(HL) HL A,+01 Z,1354,STCHK The block length. Skip next instruction. The block length. Save the start address. Start with E cleared. Add the current byte to the previous sum. Point to next location. Include also the carry. Jump when A reaches zero.

1353 1354 1355 1356 1357 1358 135A 135B 135C 135D 135E

STCHK

DEC LD DEC LD OR JR LD CP LD POP RET

A E,A BC A,B C NZ,134C,NXT-BYTE A,E (HL) (HL),A HL

Otherwise balance the ADD above. Update sum. Decrement counter. Repeat until the counter reaches 0. Move checksum into A. Compare with previous checksum. Store new checksum. Restore start address. Finished.

THE 'RESTORE STREAM DATA' SUBROUTINE This subroutine is entered with BC holding the length of a reclaimed channel, and HL holding the 'stream displacement' for that channel, The stream that refers to this displacement (i.e. the stream attached to the reclaimed channel) is closed. The other stream displacements are updated if they refer to channels moved down after the reclaiming. 135F 1360 1362 1365 1368 1369 136A 136B 136C 136D 136E 1370 1372 1375 1377 1379 137A 137B 137D 137E 1381 1382 1383 1384 1387 1388 1389 138A 138C 138F 1390 REST-STRM PUSH LD LD NXT-STRM LD LD INC LD POP PUSH OR SBC JR LD JR NOTRIGHT JR EX OR SBC EX STO-DISP LD LD INC LD UPD-POINT LD INC INC DEC JR LD POP RET HL A,+10 HL,+5C16 (X-PTR),HL E,(HL) HL D,(HL) HL HL A HL,DE NZ,1377,NOTRIGHT DE,+0000 137E,STO-DISP NC,1384,UPD-POINT DE,HL A HL,BC DE,HL HL,(X-PTR) (HL),E HL (HL),D HL,(X-PTR) HL HL A NZ,1365,NXT-STRM (X-PTR-lo),A HL Save stream displacement. Counts 16 streams. Start with STRMS-0 address. Save current address into X-PTR. Fetch stream displacement for the current stream. Restore displacement of stream to be closed. Clear carry. Jump if this is not the stream to be closed. Close the stream by storing a displacement = 0. Jump if the stream data does not need to be updated. HL holds the current displacement. Clear carry. Obtain new displacement. Move it into DE. Store the new displacement for the current stream. Fetch pointer of current stream data. Advance to data for next stream. Jump back until all 16 streams have been examined. Clear X-PTR. Restore displacement. Finished.

THE 'RESTORE MAP ADDRESSES' SUBROUTINE When a map has been deleted, the addresses of the other 'higher' maps have to be updated. The 'REST-MAP' subroutine does this. HL should hold on entry the address of the deleted map. 1391 1394 1398 139B 139D 13A0 REST-MAP LD LD LD ADD LD CP BC,+0020 IX,(CHANS) DE,+0014 IX,DE A,(IX+0) +80 Length of map. Point to the first channel other than 'standard' one. Return if the CHANS area is finished.

LCHAN

13A2 13A3 13A4 13A7 13A9 13AB 13AD 13B0 13B3 13B5 13B7 13B8 13B9 13BB 13BE 13C1 13C2 13C5 13C8 13CA

RET PUSH LD AND CP JR LD LD SBC JR EX OR SBC LD LD POP LD LD ADD JR

Z HL A,(IX+4) +7F +4D NZ,13C1,LPEND E,(CHMAP-lo) D,(CHMAP-hi) HL,DE NC,13C1,LPEND DE,HL A HL,BC (CHMAP-lo),L (CMMAP-hi),H HL E,(IX+9) D,(IX+10) IX,DE 139D,LCHAN

Save 'start' of map. Fetch channel specifier. No distinction between 'temporary' and 'permanent' channels is made. Jump if this is not an "m" channel. Fetch address of map. Jump if this map has not been moved. HL = old map address. Clear carry. Calculate actual start address and store it. Restore 'start' of deleted map. Fetch channel length. Point to next channel. Loop again.

LPEND

THE '"M" CHANNEL DATA' TABLE The '25' bytes that compose the initial part of an "M" channel are as follows: 13CC 13CE 13D0 13D1 13D3 13D5 13D7 13D9 13DA 13E4 DEFW DEFW DEFB DEFW DEFW DEFW DEFW DEFB DEFM DEFB +0008 +0008 +CD +11D8 +1122 +0253 +0000 +00 " (10 spc) " +FF Main ROM 'output' routine. Main ROM 'input' routine. "M"+80H (channel specifier). Shadow ROM 'output' routine. Shadow ROM 'input' routine. Channel length. Default for CHBYTE. Default for CHREC. Default for CHNAME. Default for CHFLAG ('write' channel).

THE 'PREAMBLE DATA' TABLE The header and the data block preambles are made by the following bytes: 13E5 13E8 13EB 13EE DEFB DEFB DEFB DEFB +00,+00,+00 +00,+00,+00 +00,+00,+00 +00,+FF,+FF

Each preamble is used to fetch the start of a block of data when reading from the Microdrive unit. THE 'MOVE' COMMAND SUBROUTINE The actual MOVE command involves the 'reading' of a byte from the required stream or channel, and then the 'writing' of that byte onto the 2nd stream or channel. The operation is repeated until the 'end of file' condition occurs. Note that bit 4 of FLAGS3 is set to signal to the CALL-INP subroutine (see 0D03) that the 'end of file' error is not to be reported. 13F1 13F5 13F8 13FB 13FC 13FF 1402 1405 MOVE SET CALL LD PUSH CALL CALL CALL POP 4,(FLAGS3) 1455,OP-STRM HL,(CHANS) BC 14C7,EX-DSTR2 1455,OP-STRM 14C7,EX-DSTR2 DE See comment above. Open the first channel. Store current value 0f CHANS. Exchange D-STR areas. Open second channel. Exchange D-STR areas again. Initial start of CHANS.

1406 1409 140A 140C 1410 1411 1414 1417 141A 141B 141D 141F 1421 1423 1426 1429 142A 142C 142E 1432 1435 1436 1439 143C 143F 1440 1443 1444 1446 144A 144B 144E 1451 1454

LD OR SBC LD ADD LD LD LD RST DEFW JR JR JR LD LD RST DEFW JR RES LD PUSH CALL CALL CALL POP LD OR SBC LD ADD LD CALL CALL RET

HL,(CHANS) A HL,DE DE,(N-STR1) HL,DE (N-STR1),HL HL,(N-STR1) (CURCHL),HL 10,CAL8AS +15E6 C,1423,MOVE-OUT Z,141A,I-AGAIN 142E,MOVE-EOF HL,(N-STR2) (CURCHL),HL 10,CALBAS +0010 1414,M-AGAIN 4,(FLAGS3) HL,(CHANS) HL 14C7,EX-DSTR2 14A4,CL-CHAN 14C7,EX-DSTR2 DE HL,(CHANS) A HL,DE DE,(N-STR1) HL,DE (N-STR1),HL 14A4,CL-CHAN 17B9,RCL-T-CH

M-AGAIN I-AGAIN

Current start of CHANS. Clear carry for a true subtraction. HL holds the length of the space inserted after the 2nd opening (possible maps inserted). Fetch start of first channel. Calculate 'current' start. Store it. Make 'current' the 1st channel Call INPUT-A in the main ROM to read a byte. Jump with acceptable codes. Repeat if no data has been read. Jump when EOF has been reached. Make 'current' the 2nd channel. Use main ROM 'PRINT-A' restart to send the byte through the 2nd channel Repeat the whole procedure. Signal that the MOVE command is finished. Store current CHANS address. Exchange D-STR areas, Close the second channel. Exchange D-STR areas again. Restore initial address of CHANS. Fetch current CHANS address. Calculate the amount of bytes reclaimed after the deletion of the second channel. Calculate the new start address of first channel. Close the first channel. Reclaim temporary channels and switch off drive motors. Finished.

MOVE-OUT

MOVE-EOF

THE 'USE STREAM OR TEMPORARY CHANNEL' SUBROUTINE This subroutine is used from the MOVE command routine above to fetch the start address of the channel attached to a stream (if the command is of the type 'MOVE #N TO ...' ), or to open a temporary channel and fetch its start address if the command is in the form 'MOVE "S";N (;"NAME") TO ...'. In both cases the start address of the channel is returned into N-STR1. 1455 1458 1459 145B 145C 145D 145F 1462 1465 1466 1469 146B 146D 1470 1471 OP-STRM LD INC JR DEC RST DEFW LD LD RET LB CP JR CALL XOR CALL A,(S-STR1) A Z,1466,OP-CHAN A 10,CAL8AS +1601 HL,(CURCHL) (N-STR1),HL A,(L-STR1) +4D NZ,147F,CHECK-N 1B29,OP-TEMP-M A 17F7,SEL-DRIVE Fetch stream number. If stream no. is +FF (i.e. nonexistent), jump to open a temporary channel. A holds the stream number. Call 'CHAN-OPEN' to select the stream. Fetch channel start address. Store it. Finished. Fetch channel specifier Jump if not "m". Open a temporary "m" channel. Switch off drive motor.

OP-CHAN

1474 1478 147C

LD BIT RET

(N-STR1),IX 2,(RECFLG) Z

Store channel start address. Allow only PRINT-type files with the MOVE command.

'Wrong file type' 147D 147E 147F 1481 1483 1486 148A 148B 148D 148F 1491 CHECK-N RST DEFB CP JR CALL LD RET CP JR CP JR 28,SH-ERR +16 +4E NZ,148B,CHECK-R 0EA9,OP-TEMP-N (N-STR1),IX +54 Z,1495,USE-R +42 Z,1495,USE-R Call the error handling routine. Jump if not an "n" channel. Open a temporary "n" channel. Store channel start address, Finished. Jump with "t" channel. Jump with "b" channel.

CHECK-R

'Nonsense in BASIC' 1493 1494 1495 1498 149C 149D 149F 14A3 USE-R RST DEFB CALL LD PUSH POP SET RET 28,SH-ERR +00 0B13,OP-RS-CH (N-STR1),DE DE IX 7,(IX+4) Call the error handling routine. Open a permanent "b" or "t" channel. Store channel start address. Make IX point to start of channel. Make the channel 'temporary'. Finished.

THE 'CLOSE "MOVE" CHANNEL' SUBROUTINE This is the opposite subroutine of the preceding one, and is used to CLOSE the channel used by the MOVE command routine. If S-STR1 denotes that a stream was used, no action is made. 14A4 14A7 14A8 14A9 14AC 14AE 14B0 14B4 14B7 14B8 14BA 14BB 14BF 14C3 14C6 CL-CHAN LD INC RET LD CP JR LD CALL RET CP RET LD LD CALL RET A,(S-STR1) A NZ A,(L-STR1) +4D NZ,14B8,CL-CHK-N IX,(N-STR1) 12A9,CLOSE-M2 +4E NZ IX,(N-STR1) (CURCHL),IX 0EF5,SEND-NEOF Fetch stream number. Return if a stream has been used (i.e. S-STR1<>+FF). Fetch channel specifier. Jump if not "m" channel. Fetch channel start address. Close the "m" channel. Finished. Return with "b" and "t" channels. Fetch channel start addreus. Make the "n" channel the 'current' one Close the "n" channel. Finished.

CL-CHK-N

THE 'EXCHANGE DSTR1 AND DSTR2 CONTENTS' SUBROUTINE This subroutine performs exactly the same task as the EX-D-STR subroutine at 059F, even if it is slightly different. The D-STR1 area is copied into the D-STR2 one, and vice-versa. 14C7 14CA 14CD EX-DSTR2 LD LD LD DE,+5CD6 HL,+5CDE B,+08 Start of 1st area. Start of 2nd area. Length of both areas.

14CF 14D0 14D1 14D2 14D3 14D4 14D5 14D6 14D7 14D9

ALL-BYT-2 LD LD EX LD LD EX INC INC DJNZ RET

A,(DE) C,(HL) DE,HL (HL),C (DE),A DE,HL HL DE 14CE,ALL-BYT-2

Fetch byte from D-STR1. Fetch byte from D-STR2. Exchange pointers. Store into D-STR1. Store into D-STR2. Exchange pointers again. Advance pointers. Continue for all bytes in the D-STR areas. Finished.

THE 'SAVE DATA BLOCK INTO MICRODRIVE' SUBROUTINE This is the actual SAVE command referred to the Microdrive (see 082F). The '9' bytes that form the 'header information' are collected from the HD variables and passed to the channel data block; then the memory block, whose 'start' and 'length' are held in the system variables HD-0D and HD-0B, is written onto the Microdrive (provided that there is sufficient space available on cartridge). 14DA 14DD 14E0 14E2 14E4 SA-DRIVE LD CALL IN AND JR A,(D-STR1) 17F7,SEL-DRIVE A,(+EF) +01 NZ,14E8,START-SA Fetch drive number. Turn on drive motor. Continue only if the write-protect tab was not removed.

'Drive 'write' protected' 14E6 14E7 14E8 14EB 14EE 14F1 14F5 l4F7 START-SA RST DEFB LD LD CALL BIT JR CALL 20,SH-ERR +0E HL,(HD-0D) (5CE4),HL 1B29,OP-TEMP-M 0,(CHFLAG) NZ,14FC,NEW-NAME 12A9,CLOSE-M2 Call the error handling routine. Fetch 'start' of data. Store it into (N-STR2+2). Open a temporary "m" channel. Continue only if this file does not already exist in the cartridge. Close the channel and report the error

'Writing to a 'read' file' 14FA 14FB 14FC 1500 1503 1506 1508 1509 150C 150D 150E 1511 1514 1517 1519 151A 151D 1521 1522 1524 NEW-NAME RST DEFB SET LD CALL PUSH POP LD ADD EX LD LD LD LDIR PUSH LD LD ADD SRL INC 20,SH-ERR +0C 2,(RECFLG) A,(CHDRIV) 17E7,SEL-DRIVE IX HL DE,+0052 HL,DE DE,HL HL,+5CE6 BC,+0009 (CHBYTE-lo),C DbE HL,+0009 BC,(HD-0B) HL,BC H H Call the error handling routine. Signal 'not a PRINT file'. Fetch drive number. Start motor. Make HL point to the buffer.

Address of HD-00. Length of HD variables. Current position is '9'. Store header informations. Save address of 'first free byte'. Add 9 to 'data length'. H holds INT(length/5l2), i.e. number of sectors required. Allow for a further EOF sector.

1525 1526 1529 152A 152B 152D

PUSH CALL POP LD CP JR

HL 1D38,FREESECT HL A,E H NC,1530,SA-DRI-2

Save H register. Calculate into E the number of 'free' sectors. Restore H. Jump if there are sufficient 'free' sectors.

'Microdrive full' 152E 152F 1530 1531 1534 1538 1539 153A 153D 151E 1541 1543 1544 1545 1548 1549 154B 154C 154E 1550 1551 1552 1554 1557 1559 155C 155E 1562 1565 1568 1569 156B 156E 1572 1574 1577 1579 157A 157D SA-DRI-3 SA-BRI-2 RST DEFB POP LD LD LD OR JR LD CP JR PUSH PUSH CALL POP PUSH POP LD ADD EX POP SA-DRI-WR LDI INC JR INC JR SA-DRI-4 SET CALL LD DEC JR LD RES LD LD JR END-SA-DR XOR CALL JP 20,SH-ERR +0F DE HL,(5CE4) BC,(HD-0B) A,B C Z,155E,SA-DRI-4 A,(CHBYTE-hi) +02 NZ,1552,SA-DRI-WR HL BC 123D,WRITE-PRC BC IX HL DE,+0052 HL,DE DE,HL HL (CHBYTE-lo) NZ,SA-DRI-3 (CHBYTE-hi) 1538,SA-DRI-3 1,(RECFLG) 120D,WRITE-PRC A,(COPIES) A Z,1579,END-SA-DR (COPIES),A 1,(RECFLG) A,+00 (CHREC),A 14FC,NEW-NAME A 17F7,SEL-DRIVE 10C4,DEL-M-BUF Call the error handling routine. Restore address of 'first free byte' in the buffer. Fetch 'start' of block from (N-STR2+2) (see 14EB). Fetch 'length' of data. Jump when it reaches zero. Jump until the buffer space has been filled. Save registers. Write this data block onto cartridge. Restore counter. Make DE point to the start of the buffer.

Restore pointer. Move a byte to the buffer. Increment CHBYTE and go back into the loop. Mark as 'EOF' record. Write the last data block. Decrease COPIES and exit if it has reached zero. Store new value. Signal 'not EOF'. Clear CHREC. Make another copy. Turn off Microdrive motor. Exit via the delete-channel subroutine.

THE 'GET HEADER INFORMATION FROM MICRODRIVE' SUBROUTINE This subroutine is used to collect the first nine bytes of the file into the "m" channel buffer, i.e. the 'header information' when handling files which have been written using the SAVE command (see 08C8). These bytes are copied into the HD system variables. 1580 1583 1586 F-M-HEAD LD LD CALL HL,(5CE1) (5CE4),HL 1B29,OP-TEMP-M Move 'start' of data into +5CE4. Open a temporary "m" channel.

1589 158D 'File not found' 158F 1590 1591 1595 F-HD-2

BIT JR

0,(CHFLAG) Z,1591,F-HD-2

Continue only if the file is found.

RST DEFB BIT JR

20,SH-ERR +11 2,(RECFLG) NZ,1599,F-HD-3

Call the error handling routine. Continue only if it is not a 'PRINT-type' tile.

'Wrong file type' 1597 1598 1599 159B 159C 159F 15A0 15A3 15A6 15A8 F-HD-3 RST DEFB PUSH POP LD ADD LD LD LDIR RET 20,SH-ERR +16 IX HL DE,+0052 HL,DE DE,+5CE6 BC,+0009 Call the error handling routine. Point to the start of the 512-byte buffer. Address of HD-00 variable. Length of 'header information'. Copy header into HD variables. Finished.

THE 'LOAD OR VERIFY BLOCK FROM MICRODRIVE' SUBROUTINE This subroutine is called from the 'LOAD OR VERIFY' subroutine (see 0A66) to load or verify (depending upon the state of bit 7 of FLAGS3) a block of bytes in memory, starting from the address held in the HL register pair. The subroutine initially calculates the number of records that composes the file; then starts to collect records and to LOAD or VERIFY the data coming in the 512-Byte buffer. When each record has been loaded or verified, the relevant map bit is set, so as to prevent the record to be used again. The map contents are restored to their initial values before returning. Note that the records may be collected from the Microdrive in a random order. 15A9 15AC 15AF 15B2 15B5 15B6 15B8 15B9 15BA 15BD 15C0 15C3 15C6 15C9 15CA 15CC 15CF 15D2 15D4 15D5 15D8 15D9 15DD 15DF 15E2 LV-MCH LD LD LD LD ADD SRL INC LD LD CALL LD LD LD OR SBC LD LD PUSH POP LD ADD LD JR CALL LD (HD-0D),HL E,(IX+83) D,(IX+84) HL,+0008 HL,DE H H A,H (HD-0B),A 1613,SA-MAP DE,+0009 L,(RECLEN-lo) H,(RECLEN-hi) A HL,DE (RECLEN-lo),L (RECLEN-hi),H IX HL DE,+005B HL,DE DE,(HD-0D) 15F9,LOOK-MAP 166C,F-REC2 A,(RECNUM) Store 'start'. Get 'new' length directly from the 'header information' held in the buffer. Increase 'length' by 8. The number of records that composes the file is computed (INT(length/5l2)) Include the 'EOF' record. Save this value into HD-0B. Save the map into the stack. Subtract the 'header length' from the 'block length'.

Store actual data block length. Make HL point after the nine bytes of 'header information'. Fetch 'start' saved at 15A9. Jump forward. Fetch a record. Repeat if RECNUM is still zero.

USE-REC

15E5 15E6

OR JR

A Z,15DF,USE-REC

Now some calculation is performed to obtain into DE the address from which the data coming from the current record, will be loaded or verified. HL will point to the 512-byte buffer. 15E8 15E9 15EA 15EB 15ED 15F0 15F1 15F2 15F4 15F5 15F8 15F9 15FA 15FD 15FF 1600 1601 1602 1603 1606 1609 160A 160D 160F 1612 RLA DEC LD LD LD ADD EX PUSH POP LD ADD EXX CALL JR LD OR LD EXX CALL LD DEC LD JR CALL RET A D,A E,+F7 HL,(HD-0D) HL,DE DE,HL IX HL BC,+0052 HL,BC 12DA,CHK-MAP-2 NZ,15DF,USE-REC A,(HL) B (HL),A 1648,LD-VE-M A,(HD-0B) A (ED-0B),A NZ,15DF,USE-REC 162D,RE-MAP Now A = RECNUM*2. A=RECNUM*2-1. Use it as high byte. Exclude nine bytes of initial header. Fetch 'start'. Calculate 'start' to use with this record. Move it to DE. Make HL point to the start of the 512-byte buffer. Use alternate registers. Check bit state for the current record. Repeat if this record has already been fetched. Set map bit to prevent the record from being fetched again. Restore initial register values. Load or verify this record. Fetch number of records stored at 15BA above. Decrease it and repeat until all records have been collected. Restore map contents. Finished.

LOOK-MAP

THE 'SAVE MICRODRIVE MAP CONTENTS' SUBROUTINE This subroutine, called from LV-MCH above, simply copies the '32' bytes that form the Microdrive map into the machine stack. 1613 1614 1617 161A 161D 1620 1621 1622 1623 1624 1625 1626 1627 1629 162C SA-MAP POP LD LD LD LD SA-MAP-LP LD LD INC LD LD INC PUSH DJNZ LD JP HL (SECTOR),HL L,(CHMAP-lo) H,(CHMAP-hi) BC,+1000 E,(HL) (HL),C HL D,(HL) (HL),C HL DE 1620,SA-MAP-LP HL,(SECTOR) (HL) Store return address in a currently unused variable. Fetch map start address. B counts 16 passes (map length/2) Fetch a byte from the map. Then clear map byte. Point to next location. Fetch another map byte. Then clear map byte. Point to next location. Save the two collected bytes into the stack. Continue the loop. Fetch return address. Make an indirect return.

THE 'RESTORE MICRODRIVE MAP CONTENTS' SUBROUTINE Exactly the opposite of the subroutine above: the 32 bytes at the top of the stack are collected and copied into the Microdrive map. 162D RE-MAP POP HL Fetch return address.

162E 1631 1634 1637 l63A 163B 163D 163E 163F 1640 1641 1642 1644 1647

LD LD LD LD ADD LD RE-MAP-LP POP LD DEC LD DEC DJNZ LD JP

(SECTOR),HL L,(CHMAP-lo) H,(CHMAP-hi) DE,+001F HL,DE B,+10 DE (HL),D HL (HL),E HL 163D,RE-MAP-LP HL,(SECTOR) (HL)

Store it in a currently unused variable. Fetch map address. Advance to the last map location. Counts 'map/2' bytes. Fetch two bytes. Store first byte in the map. Previous location. Store second byte. Previous location. Continue the loop. Fetch return address. Make an indirect return.

THE 'LD-VE-M' SUBROUTINE This subroutine performs the actual LOAD or VERIFY operation. It is entered with HL holding the data start address, and with DE holding the address from which the data have to be loaded or verified. 1648 164B 164E 1651 1653 1655 1657 1658 1659 165A 165C 165D 165E 165F 1660 1661 1663 LD-VE-M LD LD LD BIT JR LDIR RET LD CP JR INC INC DEC LD OR JR RET C,(RECLEN-lo) B,(RECLEN-hi) A,(FLAGS3) 7,A NZ,1658,VE-M-E A,(DE) (HL) NZ,1664,VE-FAIL HL DE BC A,B C NZ,1658,VE-M-E Fetch record length. Jump if VERIFYing. LOAD the data. Finished. Fetch an existing byte. Compare against loaded one. Jump if they do not match. Point to next locations. Repeat until the block has been verified. Finished.

VE-M-E

'Verification has failed' 1664 1665 VE-FAIL RST DEFB 20,SH-ERR +15 Call the error handling routine.

THE 'FETCH A RECORD FROM MICRODRIVE' SUBROUTINE This subroutine is used to read from the Microdrive unit, a record of the current file (saved with a SAVE command). An error occurs if no record is found after five passes of the cartridge tape. The entry point is F-REC2 when the Microdrive motor is already turned on. 1666 1669 166C 166F 1673 1676 1678 167A 167D 167F 1681 1682 F-REC1 LD CALL E-REC2 LD LD UNTILFIVE CALL JR JR CALL JR PUSH POP LD A,(CHDRIV) 17E7,SEL-DRIVE BC,+04FB (SECTOR),BC 11A5,G-HD-RC C,168A,F-ERROR Z,168A,F-ERROR 12DA,CHK-MAP-2 NZ,168A,F-ERROR IX HL DE,+0052 Fetch drive number. Start drive motor. Count five passes of the tape. Fetch header and record. Jump with any error, or with unused sectors. Check map bit. Jump also with already fetched records. Make HL point to the start of the 512-byte buffer.

1685 1686 1689 168A 168D

F-ERROR

ADD CALL RET CALL JR

HL,DE 1346,CHKS-BUFF Z 1312,DEC-SECT NZ,1673,UNTILFIVE

Return only with correct checksum. Decrease SECTOR. And continue until five passes of the tape have been made. Call the error handling routine.

'File not found' 168F 1690 RST DEER 20,SH-ERR +11

THE 'RESTORE ADDRESS OF "FILENAME"' ROUTINE After the 'insertion' of some space, the 'filenames' 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). 1691 1692 1693 1697 169A 169E 169F 16A3 16A6 l6AA 16AB REST-N-AD PUSH PUSH LD CALL LD POP LD CALL LD POP RET HL HL DE,(N-STR2+2) 16AC,TST-PLACE (N-STR2+2),DE HL DE,(N-STR1+2) 16AC,TST-PLACE (N-STR1+2),DE HL Save 'start of channel' twice. Restore start address of the second filename. Restore channel start address. Restore start address of the first filename. Restore channel start address. Finished.

The subroutine which calculates the new filename address is the following: 16AC 16AD 16AF 16B0 16B3 16B5 16B6 16B7 16B8 16B9 TST-PLACE SCF SBC RET LD SBC RET EX ADD EX RET HL,DE NC HL,(STKEND) HL,DE C DE,HL HL,BC DE,HL Allow for a further byte. No action is made if the filename is before the channel, or if it is after STKEND. Add to DE the number of 'inserted' bytes, so returning the new filename address. Finished.

16BA...1707

Unused locations (all set to +FF).

THE 'CLOSE-STREAM' ROUTINE The main ROM 'CLOSE' routine at +16E5 is rather inadequate to deal with Interface's channels: First, it has a bug that may crash the system when attempting to CLOSE an unopened stream. This is because the CLOSE-2 routine at +1701 does not check whether the displacement data (found by STR-DATA, +171E) is +0000 (signalling a CLOSEd stream). Thus a channel specifier is loaded from a wrong location (CHANS+3), and finally a call to the INDEXER routine is made to search for the (wrong) specifier into the 'stream look-up table', this will result in a system crash. Secondly, that routine will only clear the stream data in STRMS area and is not able to manipulate and reclaim the interface's channels as required. So the shadow ROM is paged-in by an instruction fetch at the address +1708 (i.e., the middle of the CLOSE-2 routine in the main ROM). The following routine is then used.

1708 1709 170A 170C 170E 1712 1715

CLOSE-CL

INC RST SRL SUB RES CALL JP

HL 30,NEWVARS A +03 1,(FLAGS3) 1718,CLOSE 05C1,END1

The 'INC HL' at +1708 in main ROM. Create new variables if required. Range of stream number is +03..+12. And now +00..+0F. Signal 'unsent bytes in the buffer have to be sent'. Close the stream. Finished.

THE 'CLOSE' COMMAND SUBROUTINE Any stream +00 to +0F may be CLOSEd by loading the stream number into A and then calling this subroutine. The unsent bytes in 'write'-type files are sent or lost depending upon whether bit 1 of FLAGS3 is reset or set. First a call to STR-DATA1 in the main ROM is made to fetch into BC the 'stream data' for the given stream, and to make HL point to the first of the two data bytes. 1718 1719 171B 171C 171D 171E 171F 1720 1723 1724 1725 1726 1727 1729 l72C 172F 1730 1731 1732 1734 1735 1736 1737 1738 1739 173C 173D 173E 173F 1740 1741 1744 1745 1746 1747 1748 1749 174B 174D 174F CLOSE RST DEFW LD OR RET PUSH PUSH LD DEC ADD EX RST DEFW LD LD ADD POP SCF SBC POP RET PUSH PUSH EX LD INC INC INC INC LD LD ADD LD INC LD PUSH CP JR CP JR 10,CALBAS +1727 A,C B Z BC HL HL,(CHANS) HL HL,BC (SP),HL 20,CALBAS +16EB HL,(CHANS) DE,+0014 HL,DE DE HL,DE BC NC BC DE DE,HL (CURCHL),HL HL HL HL HL A,(HL) DE,+0005 HL,DE E,(HL) HL D,(HL) DE +42 Z,1751,CL-RS-CH +54 NZ,175E,CL-N-CH Call STR-DATA1. Return if the stream is already CLOSEd (i.e. stream data = 0).

Make HL point to the start of the channel attached to the stream to be closed. HL now holds the address of the stream data. A call in the middle of the main ROM 'CLOSE' routine is made to update STRMS contents. Make HL point to the first of the 'new' channels, Restore channel start address. Return if the channel is not a 'new' one. Save stream data, and channel start address. Move start of channel to HL. Make the channel 'current'. Advance HL to the channel specifier.

Fetch channel specifier. Points to channel length. Fetch channel length. Save the 'length'. Jump with "b" channel. Jump with "n" and "m" channels.

Now follows the CLOSE routine for the "t" and "b" channels. 1751 1755 CL-RS-CH BIT JR 1,(FLAGS3) NZ,177F,RCLM-CH Jump if doing a CLEAR#. (i.e. do not send any data).

1757 1759 175C 175E 1760

CL-N-CH

LD CALL JR CP JR

A,+0D 0C5A,BCHAN-OUT 177F,RCLM-CH +4E NZ,176D,CL-M-CH

Send a carriage return over RS232. Jump to reclaim the channel. Jump with "m" channel.

This is the CLOSE routine for the "n" channel, 1762 1766 1768 176B BIT JR CALL JR 1,(FLAGS3) NZ,177F,RCLM-CH 0EF5,SEND-NEOF 177F,RCLM-CH Jump if doing a CLEAR#. Send remaining contents of "n" buffer.

Finally the CLOSE routine for the "m" channel. 176D 176F 1771 1772 1774 1775 1779 l77C 177F 1780 1781 1782 1783 CL-M-CH CP JR POP POP POP BIT JP JP POP POP PUSH RST DEFW +4D NZ,177F,RCLM-CH DE IX DE 1,(FLAGS3) Z,12A9,CLOSE-M2 10C4,DEL-M-BUF BC HL BC 10,CALBAS +19E8 Jump if not "m" channel. Remove 'channel length'. Start of channel. Stream data. Send the EOF record on microdrive if not using CLEAR#. Reclaim "m" channel. Channel length. Channel start address. Save 'length' again. Call RECLAIM-2 to delete the channel.

RCLM-CH

NOTE: If the BREAK key is pressed while the buffer is sent as EOF block, the routine does not reach RCLM-CH or DEL-M-BUF, and it is impossible to delete the channel from BASIC unless using the NEW command. The bug should be eliminated by making the channel 'temporary', by setting bit 7 of channel specifier (at 1740..1741), so the channel will be deleted when any error occurs. Now all data referring to the stream attached to the channels moved down are updated. 1785 1786 1789 178A 178B 178C 178D 1790 1791 1792 1793 1794 1795 1797 1799 179A 179B 179D 179E 17A1 17A2 17A3 17A4 XOR LD UPD-STRM LD INC LD DEC LD POP POP PUSH PUSH AND SBC JR EX AND SBC EX LD LD INC LD UPD-NXT-S LD A HL,+5C16 E,(HL) HL D,(HL) HL (X-PTR),HL BC HL HL BC A HL,DE NC,17A4,UPD-NXT-S DE,HL A HL,BC DE,HL HL,(X-PTR) (HL),E HL (HL),D HL,(X-PTR) Start with stream 0. Address of data for stream 0. Fetch stream data. Point to the first byte. Store address into X-PTR. Length of channel. Stream data for closed channel. Jump if the stream data found is lower than that of the closed stream (i.e. channel has not been moved). HL holds the fetched Stream data. Calculate into DE the new stream data to be stored. Restore stream data address. Store new stream data. Make HL point to next stream data.

17A7 17A8 17A9 17AA 17AC 17AE 17B2 17B3 17B4 17B8

INC INC INC CP JR LD POP POP RES RET

HL HL A +10 C,1789,UPD-STRM (X-PTR-hi),+00 HL HL 1,(FLAGS3)

Increment stream number. Loop for all 16 streams. Clear X-PTR. Remove 'channel length' and 'stream data'. Clear 'CLEAR/CLOSE' flag. Finished.

THE 'RECLAIM TEMPORARY CHANNELS' SUBROUTINE This subroutine is called to reclaim from the CHANS area all 'temporary' channels (i.e. with bit 7 of channel specifier set). Also the drive motors are turned off. The routine is always called when any error report occurs, and on some other occasions. 17B9 17BD 17C0 17C2 17C5 17C7 17C9 17CB 17CD 17CE 17D1 17D2 17D5 17D7 17D9 17DC 17DE 17E0 17E2 17E5 17E7 17E8 17E9 17EB 17ED l7F0 17F3 17F5 RCL-T-CH EX-CHANS LD LD ADD LD CP JR LD OUT XOR JP RET IX,(CHANS) DE,+0014 IX,DE A,(IX+3) +80 NZ,17D2,CHK-TEMPM A,+EE (+EF),A A 17F7,SEL-DRIVE A,(IX+4) +CD NZ,17DE,CHK-TEMPN 10C4,DEL-M-BUF 17B9,RCL-T-CH +CE NZ,17ED,PT-N-CHAN BC,+0114 IX HL 10,CALBAS +19E8 17B9,RCL-T-CH E,(IX+9) D,(IX+10) IX,DE 17C2,EX-CHANS Make IX point to first 'non-standard' channel. Jump if the CHANS area is not finished. Send a signal to the interface. Return via SEL_DRIVE to turn off drive motors. Never executed. Fetch channel specifier. Jump if not a temporary "m" channel. Reclaim "m" channel. Loop again. Jump if not a temporary "n" channel. Length of "n" channel. Make HL point to start of channel. Call RECLAIM-2 to reclaim channel. Loop again. Fetch channel length. Point to next channel. Loop for the whole CHANS area.

CHK-TEMPM LD CP JR CALL JR CHK-TEMPN CP JR LD PUSH POP RST DEFW JR PT-N-CHAN LD LD ADD JR

THE 'SELECT DRIVE MOTOR' SUBROUTINE This subroutine is also called by using 'hook code' +21. On entry, A must hold a drive number in the range 1. .8; the appropriate drive motor is turned on, and an error occurs if the specified drive is not present (or if it contains no cartridge, or contains an unformatted cartridge). If A holds zero, all motors are turned off. Note that this subroutine returns with interrupts disabled if a motor has been switched on. 17F7 17F8 17FA 17FC 17FF 1800 1801 SEL-DRIVE PUSH CP JR CALL EI POP RET HL +00 NZ,1802,TURN-ON 182A,SW-MOTOR HL Save HL register pair. Jump if a drive motor is to be turned on. Otherwise switch off all motors. Enable interrupts. Restore HL. Finished.

1802 1803 1806 1809 180A 180B 180C 180E 1811 1813 1816 1818 181A 181C 181E 181F 1820 1821 1822 1823 1825

TURN-ON

DI CALL LD TON-DELAY DEC LD OR JR LD REPTEST LD CMK-PRES CALL IN AND JR DJNZ POP RET NOPRES DEC LD OR JR CALL

182A,SW-MOTOR HL,+1388 HL A,H L NZ,1809,TON-DELAY HL,+1388 B,+06 18E9,TEST-BRK A,(+EF) +04 NZ,1820,NOPRES 1813,CHK-PRES HL HL A,H L NZ,1811,REPTEST 17F7,SEL-DRIVE

Disable interrupts. Switch motors as required. First wait about 40 ms. Decrease counter. Has the counter reached zero? Repeat if not. Repeat 5000 times the following test: Set a counter. Give an error if BREAK is pressed. Repeat until 'GAP' signal is found low Repeat 6 times. Restore HL. Finished. Becrease counter. Has the counter reached zero? Repeat if not. Send 'switch off' signals and enable interrupts. Call the error handling routine.

'Microdrive not present' 1828 1829 RST DEFB 20,SH-ERR +10

The following subroutine does the actual switching' of the motors, and the selection of the required drive. It is entered with drive number 1...8 (or 0 to switch all motors off) in the A register. The required drive motor is selected, its motor started, while others are disabled. 182A 182B 182E 1830 1832 1833 1835 1836 1838 1839 183B 183D 183F 1842 1844 1846 1849 184B 184D 184F 1850 1852 1855 1857 1859 185C 185E 185F 1861 1863 1865 SW-MOTOR PUSH LB NEG ADD LD LD ALL-NOTRS DEC JR LD OUT LD OUT CALL LD OUT CALL JR OFF-MOTOR LD OUT LD OUT CALL LD OUT CALL NXT-NOTOR DJNZ LD OUT LD OUT POP DE DE,+0100 A,+09 C,A B,+08 C NZ,184B,OFF-MOTOR A,D (+F7),A A,+EE (+EF),A 1867,DEL-S-1 A,+EC (+EF),A 1867,DEL-S-1 185C,NXT-MOTOR A,+EF (+EF),A A,E (+F7),A 1867,DEL-S-1 A,+ED (+EF),A 1867,DEL-S-1 1835,ALL-MOTRS A,D (+F7),A A,+EE (+EF),A DE Save DE register pair. A = 0 - drive number. A = 9 - drive number. Move counter to C. Loop for 8 microdrives. Switch off this microdrive if it is not the required one. Otherwise switch on the motor.

Wait about 1 ms. Wait about 1 ms. Continue with next microdrive. The drive motor Is turned off.

Wait about 1 ms. Wait about 1 ms, Loop for 8 microdrives. End of drive selection.

Restore DE register pair.

1866

RET

Finished.

THE '1 MILLISECOND DELAY' SUBROUTINE This subroutine inserts a delay of about 3600 T cycles. It is called from SW-MOTOR above. 1867 1868 1869 186C 186F 1870 1871 DEL-S-1 PUSH PUSH LD CALL POP POP RET BC AF BG,+0087 18FA,DELAY-BC AF BC Save BC register pair. Save accumulator. Inserts a delay of 3553 T cycles. Restore registers. Finished.

THE 'SEND DATA BLOCK TO MICRODRIVE HEAD' SUBROUTINE This subroutine is used for writing a block of bytes onto a Microdrive cartridge. On entry, HL must hold the start of the block to be written. The block is then sent to the Interface (provided that the write-protect tab is present) in a parallel form (the bytes are converted in a serial form by the hardware). The entry points OUT-M-HD and OUT-M-BUF are used respectively to write headers or data blocks (including preambles). 1872 1873 1876 1878 1879 187C 187E 1880 OUT-M-HD PUSH LD JR OUT-M-BUF PUSH LD OUT-N-ELK IN AND JR HL DE,+001E 187C,0UT-M-BLK HL DE,+021F A,(+EF) +01 NZ,1884,NOT-PROT Save block start address. Block length. Jump forward. Save block start address. Block length. Check write-protect tab. Only bit 0. Jump if tab is present.

'Drive 'write' protected' 1882 1883 1884 1887 1889 188B 188D 188E 188F 1890 1892 1893 1894 1895 1897 1898 189A 189C 189E 18A1 18A2 NOT-PROT RST DEFB LD OUT LD OUT INC LD LD LD NOP NOP NOP OUT-M-BYT OTIR DEC JR LD OUT CALL POP RET 20,SH-ERR +0E A,(IOBORD) (+FE),A A,+E2 (+EF),A D A,D B,E C,+E7 Call the error handling routine. Fetch border colour. Change border colour. Start writing. Increment high counter and copy it into A. Low counter. Output port. Wait 12 T cycles. Write first block. Repeat until the whole block has been written. Stop writing. Restore border colour. Restore block start address. Finished.

A NZ,1895,OUT-M-BYT A,+E6 (+EF),A 0CA9,BORD-REST HL

THE 'RECEIVE BLOCK FROM MICRODRIVE HEAD' SUBROUTINE This is the opposite routine to the preceding one. On entry, HL must hold the start address of the header block or of the data block (AFTER the preamble). The bytes are collected from Microdrive head and stored In the appropriate block. The entry points are GET-M-HD or GET-M-BUF, depending upon whether the block to be received is a header or a data block.

18A3 18A4 18A7 18A9 18AA 18AD 18AE 18AF 18B0 18B1 18B3 18B6 18B8 18BA 18BC 18BE 18C0 18C3 18C5 18C7 18C9 18CB 18CC 18CE 18D0 18D1 18D2 18D4 18D6 18D8 18DB 18DC 18DE 18E0 18E1 18E3 18E5 18E7 18E8

GET-M-HD

PUSH LD JR GET-M-BUF PUSH LD GET-M-BLK LD LD INC PUSH CHK-AGAIN LD CHKLOOP CALL IN AND JR DJNZ CHK-AG-2 LD CHK-LP-2 CALL IN AND JR DJNZ POP LD OUT POP PUSH DR-READY IN AND JR CALL LD LD IN-M-BLK INIR DEC JR LD OUT POP RET

HL DE,+000F 18AD,GET-M-BLK HL DE,+0210 B,E C,D C BC B,+08 18E9,TEST-BRK A,(+EF) +04 Z,18B1,CHK-AGAIN 18B3,CHKLOOP B,+06 18E9,TEST-BRK A,(+EF) +04 NZ,18BE,CHK-AG-2 18C0,CHK-LP-2 BC A,+EE (+EF),A HL HL A,(+EF) +02 NZ,18D2,DR-READY 18E9,TEST-BRK A,C C,+E7 A NZ,18DE,IN-M-BLK A,+EE (+EF),A HL

Save start address. Block length. Jump forward. Save start address. Block length. Copy 'length' into BC in a reversed form. Increment high byte of 'length'. Loop 8 times. Give an error if BREAK pressed. The GAP line is read repeatedly; it must be found 'low' after a 'high' period.

Restore BC. Start reading. Restore 'start address'. Read SYNC line to synchronize the reading with the start of the block. Repeat until SYNC is found low. Give an error if BREAK pressed. A holds the high counter. Input port. Read first block. Repeat until the whole block has been read. Finished. Restore block start address.

TIlE 'TEST-BRK' SUBROUTINE The BREAK key is checked and the error 'BREAK into program' is made if it is pressed. 18E9 18EB 18ED 18EE 18EF 18F1 18F3 18F4 18F5 18F9 TEST-BRK LD IN RRA RET LD IN RRA RET LD RST A,+7F A,(+FE) C A,+FE A,(+FE) C (ERR-NR),+14 28,ROMERR Read port +7FFE. Only bit 0. Return if SPACE not being pressed. Read port +FEFE. Only bit 0. Return if CAPS SHIFT not being pressed Store error code. Report the error.

THE 'DELAY-BC' SUBROUTINE This subroutine is called to insert delays in the program execution, depending upon the value of BC. The exact delay is (BC * 26 - 43) T cycles. 18FA 18FB 1BFC 18FD DELAY-BC PUSH DELAY-BC1 DEC LD OR AF BC A,B C Save accumulator. Decrease counter. Repeat until the counter reaches 0.

18FE 1900 1901

JR POP RET

NZ,18FB,DELAY-BC1 AF

Restore accumulator. Finished.

The following are two subroutines that are never called from the Shadow ROM code. These subroutines operate evidently on the buffer's contents of a Microdrive channel. 1902 1903 1905 1906 1909 190A 190B 190C 190F 1912 1913 1916 1919 191C 191D 191E 191F 1920 1921 1923 1924 1928 1929 192A 192B 192D 192E 192F 1930 1932 1933 1934 1935 1937 1939 193A 193B 193D 193F 1940 1941 1942 1944 1945 1946 1947 1948 1949 194A 194C 194D 194E 1950 1951 UNKN-1 PUSH PUSH POP LD ADD LD LD LD LD EXX LD LD LD EXX LD INC ADD LD JR INC JR EXX ADD EXX ADC DEC LD OR JR LD EXX LD LD SLA EXX LD RL RL EXX ADD EXX ADC PUSH EXX PUSH PUSH POP POP LD LD CP JR INC LD HL IX HL BC,+0052 HL,BC B,H C,L HL,+0000 DE,+0000 BC,+0200 HL,+0000 DE,+0000 A,(BC) BC A,E E,A NC,1929,UNKN-3 D NZ,1929,UNKN-3 HL,DE HL,DE BC A,B C NZ,191C,UNKN-2 D,E A,D E,+00 D E,A E D HL,DE HL,DE HL HL BC HL BC E,+00 A,C (HL) Z,1952,UNKN-4 E (HL),A

UNKN-2

UNKN-3

1952 1953 1954 1955 1957 1958 1959 195A 195B 195C 195D 195F 1960 1961 1962 1963 1964 1966 1967 1968 1969 196A 196B l96C 196E 196F 1972 1973 1976 1977 1979 197A 197B 197C 197D 197E 1980

UNKN-4

UNKN-5

UNKN-6

UNKN-7

UNKN-8

UNKN-9

INC LD CP JR INC LD INC POP LD CP JR INC LD INC LD CP JR INC LD LD OR POP RET PUSH POP LD ADD LD LD XOR LD INC DEC LD OR JR RET

HL A,B (HL) Z,1959,UNKN-5 E (HL),A HL BC A,C (HL) Z,1961,UNKN-6 E (HL),A HL A,B (HL) Z,1968,UNKN-7 E (HL),A A,E A HL IX HL DE,+0052 HL,DE BC,+0200 A,(HL) +55 (HL),A HL BC A,B C NZ,1976,UNKN-9

The 'Hook code' routines THE 'HOOK-CODE' ROUTINE This routine is entered from 00EB with the A register holding a 'hook code', or an invalid error code. The routine calls a set of subroutines in the shadow ROM, and is intended to help the machine-code user. Only the value held in the accumulator may be passed to the called subroutine. 1981 1983 HOOK-CODE CP JR +18 C,1987,CLR-ERR Continue with 'hook' codes.

'Hook code error' 1985 1986 1987 198B 198F 1990 1991 1992 1993 CLR-ERR RST DEFB LD SET INC EX PUSH ADD LD 20,SH-ERR +12 (ERR-NR),+FF 2,(FLAGS) HL (SP),HL HL A,A D,+00 Call the error handling routine. The 'error' is cleared. Advance return address past the error code. Store new return address; the initial value of A goes into H. Save this value. Multiply code by two. Pass offset into DE.

1995 1996 1999 199A 199B 199C 199D 199E 19Al 19A2 l9A3

LD LD ADD LD INC LD POP LD PUSH EX JP

E,A HL,+19A9 HL,DE E,(HL) HL D,(HL) AF HL,+0700 HL DE,HL (HL)

Start of 'hook code addresses' table. Index into this table. Fetch low byte of the address. Fetch high byte. Restore initial value of A. Return address is UNPAGE. Move address to HL. Jump to the 'hook code' routine.

THE 'HOOK CODE +32' ROUTINE This 'hook code' is designed to call (when the main ROM is paged in) any subroutine held in the shadow ROM. The address of the subroutine to be called is taken from HD-11. 19A4 19A7 HOOK-32 LD JP HL,(HD-11) (HL) Fetch address of the routine. Jump to the routine.

THE 'HOOK CODE +31' ROUTINE This 'hook code' has the task of creating the new system variables if nonexistent. Note that the routine is made by a single RET instruction, because the variables have been created on entry to the shadow RON. 19A8 HOOK-3l RET Jump indirectly to UNPAGE.

THE 'HOOK CODE ADDRESSES' TABLE This jump table is made by the 24 addresses of the routines called by using the various 'hook codes'. Note that hook code +2B jumps incorrectly to the same routine as hook code +22. The correct address seems to be +1AF0. 19A9 19AB 19AD 19AF 1981 1983 1985 1987 1989 198B 198D 198F 19C1 19C3 19C5 19C7 19C9 19CB 19CD 19CF 19D1 19D3 19D5 19D7 DEFW DEFW DEFW DEFW DEFW DEFW DEFW DEFW DEFW DEFW DEFW DEFW DEFW DEFW DEFW DEFW DEFW DEFW DEFW DEFW DEFW DEFW DEFW DEFW +19D9,CONS-IN +19EC,CONS-OUT +0B81,BCHAN-IN +0C5A,BCHAN-OUT +19FC,PRT-OUT +1A01,KBD-TEST +17F7,SEL-DRIVE +1B29,OP-TEMP-M +12A9,CLOSE-M2 +1D6E,ERASE +1A09,READ-SEQ +11FF,WR-RECD +1A17,RD-RANDOM +1A4B,RD-SECTOR +1A86,RD-NEXT +1A91,WR-SECTOR +1B29,OP-TEMP-M +10C4,DEL-M-BUF +0EA9,OP-TEMP-N +1A24,CLOSE-NET +1A31,GET-PACK +0DB2,SEND-PACK +19A8,HOOK-31 +19A4,HOOK-32 Hook Hook Hook Hook Hook Hook Hook Hook Hook Hook Hook Hook Hook Hook Hook Hook Hook Hook Hook Hook Hook Hook Hook Hook code code code code code code code code code code code code code code code code code code code code code code code code +1B. +1C. +1D. +1E. +1F. +20. +21. +22. +23. +24. +25. +26. +27. +28. +29. +2A. +2B. +2C. +2D. +2E. +2F. +30. +31. +32.

THE 'CONSOLE INPUT' SUBROUTINE This subroutine is called by using 'hook code' +1B. It simply waits until a key is pressed and returns the character code in the A register. This 'hook code' has been included because the keyboard is not scanned when the main ROM is paged-in.

19D9 19DA 19DE 19DF 19E0 19E2 19E6 19E8 19EB

CONS-IN WTKEY

EI RES HALT RST DEFW BIT JR LD RET

5,(FLAGS) 10,CALBAS +02BF 5,(FLAGS) Z,19DE,WTKEY A,(LAST-K)

Enable interrupts. Signal 'ready for a new key'. Wait 1/50th of second. Call the keboard scan routine in the main ROM. Repeat the scan if no key has been pressed. Fetch character code. Finished.

THE 'CONSOLE OUTPUT' SUBROUTINE This subroutine is called by using 'hook code' +1C. The character held in the A register is printed on the screen, with scroll suppressed. 19EC 19ED 19EF 19F2 19F4 19F5 1SF7 19F8 19F9 19FB CONS-OUT OUT-CODE PUSH LD LU LD RST DEFW POP RST DEFW RET AF A,+FE HL,+5C8C (HL),+FF 10,CALBAS +1601 AF 10,CALBAS +0010 Save character to be printed. Use stream '-2' (Screen). This is SCR-CT. Set scroll counter. Call CHAN-OPEN in the main ROM to select the stream. Restore character. Call 'PRINT-A' restart to print the character. Finished.

THE 'PRINTER OUTPUT' SUBROUTINE This subroutine is called by using hook code +1F. This is identical to the preceding one, but the output is directed to the stream +03 (normally the ZX Printer). 19FC 19FD 19FF PRT-OUT PUSH LD JR AF A,+03 196F,OUT-CODE Save character to be printed. Select stream 3. Jump back.

THE 'KEYBOARD TEST' SUBROUTINE This is called by using 'hook code' +20. The keyboard is scanned and the zero flag returned reset if any key has been pressed. 1A01 1A02 1A04 1A06 1A08 KBD-TEST XOR IN AND SUB RET A A,(+FE) +1F +1F Clear A, allowing for the whole keyboard to be examined. Read the keyboard. Only 5 less significant bits. Return with sign negative and zero flag reset if any key has been pressed.

THE 'READ SEQUENTIAL' SUBROUTINE This is called by using 'hook code' +25. The subroutine reads into the datablock 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 file name. 1A09 1A0D 1A0F 1A13 1A14 READ-SEQ BIT JR LD RST INC 1,(RECFLG) Z,1A14,INCREC (ERR-NR),+07 28,ROMERR (CHREC) Jump if the current record is not the EOF one. Otherwise signal 'end of file'. Report the error. Increment record number and continue into RD-RANDOM.

INCREC

THE 'READ RANDOM' SUBROUTINE

This subroutine is called by using 'hook code' +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. 1A17 1A1A 1A1E 1A1F RD-RANDOM CALL BIT RET CALL 1177,GET-RECD 2,(RECFLG) Z 10C4,DEL-M-BUF Load CHREC record. Return only if this is a PRINT-type file. Otherwise reclaim the channel, and report the error. Call the error handling routine.

'Wrong file type' 1A22 1A23 RST DEFB 20,SH-ERR +16

THE 'CLOSE NETWORK CHANNEL' SUBROUTINE This is called by using 'hook code' +2E. First, the remaining bytes in the "n" channel buffer whose base address is held in the CURCHL system variable, are sent as EOF block (if the channel is opened for writing). The channel is then reclaimed. 1A24 1A27 1A29 1A2A 1A2D 1A2E 1A30 CLOSE-NET CALL PUSH POP LD RST DEFW RET 0EF5,SEND-NEOF IX HL BC,+0114 10,CALBAS +19E8 Send the EOF block if required. Move channel start address to HL. Length of "n" channel. Call RECLAIM-2 to delete the channel. Finished.

THE 'GET PACKET FROM NETWORK' SUBROUTINE This is called by using 'hook code' +2F. Unfortunately the subroutine is unusable, because the carry flag (that signals if an error has occurred) is corrupted by exit via the BORD-REST subroutine. You may use GET-N-BUF at 03DF to read a packet. 1A31 1A34 1A36 1A37 1A3A 1A3C 1A3F 1A41 1A42 1A43 1A46 1A47 1A48 GET-PACK LD OUT DI CALL JR CALL JR EI AND JP SCF EI JP A,(IOBORD) (+FE),A 0F1E,WT-SCOUT NC,1A46,GP-ERROR 0E18,GET-NBLK NZ,1A46,GP-ERROR A 0CA9,BORD-REST 0CA9,BORD-REST Fetch border colour. Change border colour. Disable interrupts. Wait for a 'scout' leader. Jump if 'time-out' occurs. Wait for header and data block. Jump with any error. Enable interrupts. Reset carry to signal 'successful'. But it is corrupted into BORD-REST. Signal 'error' Enable interrupts. Again the carry flag will be corrupted.

CF-ERROR

THE 'READ SECTOR' SUBROUTINE This is called by using 'hook code' +28. Before using this, you must start the required drive motor, and store a sector number into CHREC. The data block stored into the given sector is then read in the channel area (pointed by IX), and used if it is a PRINT-type record. The carry flag is returned reset if the reading is successful. 1A4B 1A4E 1A51 1A54 1A57 1A5A 1A5C RD-SECTOR LD LD NO-GOOD CALL LD CP JR CALL HL,+00F0 (SECTOR),HL 12C4,GET-M-HD2 A,(HDNUM) (CHREC) Z,1A63,USE-C-RC 1312,DEC-SECT Counts through 240 sectors. Get a header. Fetch current sector number. Compare with given sector number. Jump if found the required sector. Otherwise decrease SECTOR.

1A5E 'File not found' 1A61 1A62 1A63 1A65 1A66 1A69 1A6A 1A6D 1A70 1A72 1A75 1A76 1A79 1A7B 1A7C 1A80 1A81 1A84 1A85 USE-C-RC

JR

NZ,1A51,NO-G00D

Jump until 240 sectors have been examined. Call the error handling routine. Make HL point to RECFLG (i.e. sTart of data block. Read data block. Calculate current checksum. Jump if it does not match with old checksum. Make HL point to start of buffer. Calculate checksum of buffer. Jump if it does not match with old checksum. Return with carry flag reset if this is a PRINT-type file. Otherwise clear buffer contents. Return with carry flag set to signal the error.

RST DEFB PUSH POP LD ADD CALL CALL JR LD ADD CALL JR OR BIT RET CALL SCF RET

20,SH-ERR +11 IX HL DE,+0043 HL,DE 18A9,GET-M-BUF 1341,CHKS-HD-R NZ,1A81,DEL-B-CT DE,+000F HL,DE 1346,CHKS-BUFF NZ,1A81,DEL-B-CT A 2,(RECFLG) Z 1AE0,CLR-BUFF

DEL-B-CT

THE 'READ NEXT SECTOR' SUBROUTINE This is used by using 'hook code' +29. The first header and data block that pass through the Microdrive head are copied into the channel area pointed by IX. As with the previous subroutine, the drive motor is to be started before calling it. 1A86 1A89 1A8C 1A8F RD-NEXT LD LD CALL JR HL,+00F0 (SECTOR),HL 12C4,GET-M-HD2 1A63,USE-C-RC Initialise SECTOR to 240. Fetch the first header. Continue back.

THE 'WRITE SECTOR' SUBROUTINE This is used by using 'hook code' +2A. It is the opposite routine of 'RD-SECTOR' above. The Microdrive unit is to be started before calling the routine. The current data block in the "m" channel pointed by the IX register is written onto the sector whose number is specified by CHREC. Other channel variables, such as CHNAME, are to be set as required. 1A91 1A94 1A97 1A99 1A9A 1A9D 1A9E 1A9F 1AA2 1AA3 1AA6 1AAA 1AAD 1AB0 1AB3 1AB6 1AB8 1ABB WR-SECTOR LD CD PUSH POP LD ADD PUSH LD ADD CALL LD CALL WR-S-l CALL LD CP JR CALL JR HL,+00F0 (SECTOR),HL IX HL DE,+0037 HL,DE HL DE,+000C HL,DE 1341,CHKS-HD-R DE,+000F 1346,CHKS-BUFF 12C4,GET-M-HD2 A,(HDNUMB) (CHREC) Z,1ABF,WR-S-2 1312,DEC-SECT NZ,1AAD,WR-S-l Pass through 240 sectors. Make HL point to the data block preamble. Save this address. Now point to RECFLG. Calculate DESCHK checksum. Now point to the buffer. Calculate DCHK checksum. Get a header. Fetch current sector number. See whether it is the expected one. Jump if so. Otherwise decrease SECTOR. Loop until 240 sectors have been

examined. 'File not found' 1ABD 1ABE 1ABF 1AC1 1AC3 WR-S-2 RST DEFB IN AND JR 20,SH-ERR +11 A,(+EF) +01 NZ,1AC7,WR-S-3 Call the error handling routine. Continue if the write-protect tab is present.

'Drive write protected' 1AC5 1AC6 1AC7 1AC9 1ACB 1ACE 1AD1 1AD2 1AD5 1AD7 1AD9 1ADC 1ADD 1ADE 1ADF RS-SH WR-S-3 RST DEFB LD OUT LD CALL POP CALL LD OUT CALL LD OR LD RET 20,SH-ERR +0E A,+E6 (+EF),A BC,+0168 18FA,DELAY-BC HL 1878,OUT-M-BUF A,+EE (+EF),A 12DF,CHECK-MAP A,B (HL) (HL),A Call the error handling routine. Start writing. Wait until the first gap is finished. Restore address of data block preamble Write data block onto cartridge. Stop writing. Finally set the appropriate map bit.

Finished.

THE 'CLEAR BUFFER CONTENTS' SUBROUTINE This subroutine is called from RD-SECTOR and RD-NEXT subroutines to clear the data received into the "m" buffer, if they are not part of a PRINT-type file. 1AE0 1AE2 1AE3 1AE6 1AE7 1AE8 1AE9 1AEA 1AED 1AEF CLR-BUFF PUSH POP LD ADD LD LD INC LD LDIR RET IX HL DE,+0052 HL,DE D,H E,L DE BC,+01FF Make HL point to the start of the "m" buffer. Copy this address into DE. 'Destination' is next byte. Buffer length - 1. Clear the buffer. Finished.

THE 'OPEN A PERMANENT "M" CHANNEL' SUBROUTINE This is the actual OPEN corinnand referred to the "m" channel. A permanent "m" channel is opened, and it is attached to the stream held into S-STR1 (provided that the file is a PRINT-type file). 1AF0 1AF3 1AF4 1AF7 1AF8 1AFA 1AFB 1AFC 1AFF 1B03 1B05 1B07 1B09 OP-M-STRM LD ADD LD LD LD ADD PUSH CALL BIT JR IN AND JR A,(S-STR1) A,A HL,+5Cl6 E,A D,+00 HL,DE HL 1B29,OP-TEMP-M 0,(CHFLAG) Z,1B0D,MAKE-PERM A,(+EF) +01 NZ,1B0D,MAKE-PERM Fetch stream number. Multiply by two. Address of data for stream 0. Use (stream*2) as offset. Index into STRMS area. Save address of data for the required stream. Open a temporary "m" channel. Jump if this is a 'read' file. Jump if the write-protect tab is present.

'Drive 'write' protected' 1B0B 1B0C 1B0D 1B11 1B12 1B15 1B19 1B1B 1B1F RST DEFB MAKE-PERN RES XOR CALL BIT JR BIT JR 20,SH-ERR +0E 7,(IX+4) A 17F7,SEL-DRIVE 0,(CHFLAG) NZ,1B23,STORE-DSP 2,(RECFLG) Z,1B23,STORE-DSP Call the error handling routine. Make the channel permanent. Switch off drive motors. Jump with 'write' files. Jump with PRINT-type 'read' files.

'Wrong file type' 1B21 1B22 1B23 1B24 1B25 1B26 1B27 1B28 RST DEFB STORE-DSP EX POP LD INC LD RET 20,SH-ERR +16 DE,HL HL (HL),E HL (HL),D Call the error handling routine. DE holds new stream data. Restore stream address. Store stream data into STRMS area. Finished.

THE 'OPEN TEMPORARY "M" CHANNEL' SUBROUTINE This fundamental subroutine is used to open a temporary "m" channel in the CHANS area. First a temporary "m" channel is created. Then the drive whose number is held into D-STR1 is started, and searched for a file whose name is held into N-STR1. A map area is created (unless it already exists), and its contents are setup according to the cartridge contents (the bits reset indicate 'free for use' sectors). 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. The drive motor will not be switched off. Note that H'L' is corrupted and the user must preserve its value when using this routine from BASIC. 1B29 1B2C 1B2D 1B30 1B33 1B36 1B3A 1B3D 1B3F 1B41 1B45 1B48 1B49 1B4B 1B4D 1B4E 1B51 1B52 1B55 1B57 OP-TEMP-M CALL 0FE8,SET-T-MCH PUSH HL LD A,(CHDRIV) CALL 17F7,SEL-DRIVE LD BC,+00FF LD (SECTOR),BC OP-F-1 CALL 11A5,G-HD-RC JR C,1B5F,OP-F-4 JR Z,1B5C,OP-F-3 RES 0,(CHFLAG) LD OR JR PUSH POP LD ADD CALL JR CALL A,(RECNUM) A NZ,1B57,OP-F-2 IX HL DE,+0052 HL,DE 1346,CHKS-BUFF Z,1B6C,OF-F-5 117D,GET-R-2 Create a temporary "m" channel. Save 'stream displacement' Fetch drive number. Turn on drive motor. Count 255 sectors. Get header and data block. Jump with any error. Jump with 'free' sectors. Signal 'read file' if the file is already present on cartridge. Jump if this is not the 1st record. Make HL point to the data buffer.

OP-F-2

Calculate new DCHK checksum. Jump it it is equal to the old one. Fetch first record of file.

1B5A 1B5C 1B5F 1B62 1B64 1B68 1B6C 1B6D

OP-F-3 OP-F-4

OP-F-5

JR CALL CALL JR RES RES POP RET

1B6C,OP-F-5 12FE,RES-B-MAP 1312,DEC-SECT NZ,1B3A,OP-F-1 1,(RECFLG) 2,(RECFLG) HL

Jump forward. Reset map bit to signal 'free sector' Decrease SECTOR. Consider all 255 sectors. 'No EOF'. 'PRINT-type file' (opened for writing) Restore stream 'stream data'. Finished.

The Microdrive command routines THE 'FORMAT' COMMAND ROUTINE The action of FORMATting a new cartridge is performed by this subroutine. It is entered with the drive number into D-STR1, the cartridge name address and length into N-STR1. 1B6E 1B71 1B74 1B77 1B7A 1B7D 1B7E 1B80 1B82 FORMAT CALL LD CALL LD CALL DI IN AND JR 0FE8,SET-T-MCH A,(CHDRIV) 182A,SW-MOTOR BC,+32C8 18FA,DELAY-BC A,(+EF) +01 NZ,1B86,FORMAT-1 Create channel and map. Fetch drive number. Turn on the motor. Wait before checking the writeprotect tab. Disable interrupts. Continue only if the tab is present.

'Drive 'write' protected' 1B84 1B85 1B86 1B88 1B8A 1B8D 1B91 1B93 1B94 1B97 1B98 1B99 1B9C 1B9D 1BA0 1BA2 1BA3 1BA6 1BAA 1BAE 1BB2 1BB4 1BB5 1BB8 FORMAT-1 RST DEFB LD OUT LD LD PUSH POP LD ADD EX LD ADD LD LDIR XOR LD SET RES SET PUSH POP LD ADD 20,SH-ERR +0E A,+E6 (+EF),A BC,+00FF (SECTOR),BC IX HL DE,+002C HL,DE DE,HL HL,+FFE2 HL,DE BC,+000A A (RECNAM),A 0,(HDFLAG) 0,(RECFLG) 1,(RECFLG) IX HL DE,+0052 HL,DE Call the error handling routine. Start writing. Pass through 255 sectors. Make DE point to HDNAHE.

Now make HL point to CHNAME (i.e. cartridge name). Name length. Copy cartridge name into HDNAME. Use 'invisible' name by storing zero as first character of the record name. Mark the header block. Mark the data block. Mark as 'EOF' block. Make HL point to the start of the data buffer.

Now the data buffer is filled with bytes +FC; and the checksums are calculated. 1BB9 1BBB 1BBD 1BBE 1BBF 1BC1 1BC2 LD LD FILL-B-F LD INC BJNZ FILL-B-F2 LD INC B,+00 A,+FC (HL),A HL 1BBD,FILL-B-F (HL),A HL Counts 256 bytes. The byte to be stored. Fill 256 bytes. Fill next 256 bytes.

1BC3 1BC5 1BC7 1BC8 1BCB 1BCC 1BCF 1BD2 1BD3

DJNZ PUSH POP LD ADD CALL LD ADD CALL

1BC1,FILL-B-F2 IX DE HL,+0043 HL,DE 1341,CHKS-HD-R DE,+000F HL,DE 1346,CHKS-BUFF

Point to start of data block workspace (i.e. RECFLG). Calculate DESCHK checksum. Make HL point to the data buffer.

Now this data block is written in all sectors with HDNUMB numbered from 254 to 1 1BD6 WR-F-TEST 1BD9 1BDB 1BDE 1BE0 1BE1 1BE4 1BE5 1BE8 1BEB 1BEC 1BEF 1BF2 1BF5 1BF7 1BF8 1BFB 1BFC 1BFF 1C02 1C05 1C08 1C0A 1C0C 1C0E 1C11 TEST-SCT CALL 1312,DEC-SECT JR Z,1C0A,TEST-SCT LD (HDNUMB),C PUSH IX POP HL LD DE,+0028 ADD HL,DE CALL 1341,CHKS-HD-R LD DE,+FFF4 ADD HL,DE CALL 1872,OUT-M-HD LD BC,+01B2 CALL 18FA,DELAY-BC PUSH IX POP HL LD DE,+0037 ADD HL,DE CALL 1878,OUT-M-BUF LD BC,+033F CALL 1BFA,DELAY-BC CALL 18E9,TEST-BRK JR LD OUT LD CALL 18D6,WR-F-TEST A,+EE (+EF),A A,(CHDRIV) 17F7,SEL-DRIVE Decrease SECTOR. Jump when SECTOR has reached zero. Take HDNUMB from SECTOR. Make HL point to the start of the header workspace, i.e. HDFLAG. Calculate HDCHK checksum. Make HL point to the header block preamble. Write the header onto the cartridge. Wait to create part of the first gap. Make HL point to data block preamble.

Write the data block. Part of the second gap is created. Give an error if BREAK has been pressed. Continue with next sector. Stop writing. Fetch drive number. Start motor.

Now the sectors contain 'test data', and are to be read back to see if they are usable. If the checksums are correct, the appropriate map bit is reset to signal 'free for use' sectors. 1C14 1C17 1C1B 1C1E 1C21 1C23 1C25 1C26 1C29 1C2A 1C2D 1C30 1C32 1C35 1C36 1C39 1C3B 1C3E 1C41 LD LD CHK-SCT CALL CALL JR PUSH POP LD ADD CALL CALL JR LD ADD CALL JR CALL CHK-NSECT CALL JR BC,+00FF (SECTOR),BC 12C4,GET-H-HD2 12DF,CHECK-MAP Z,1C3E,CHK-NSECT IX HL DE,+0043 HL,DE 18A9,GET-M-BF 1341,CHKS-HD-R NZ,1C3E,CHK-NSECT DE,+000F HL,DE 1346,CHKS-BUFF NZ,1C3E,CHK-NSECT 12FE,RES-B-MAP 1312,DEC-SECT NZ,1C1B,CHK-SCT Pass through 255 sectors, Fetch a header. Check map bit and jump with examined sectors. Make HL point to start of data block workspace (i.e. RECFLG). Fetch data block. Calculate DESCHK checksum. Jump with faulty sectors. Make HL point to the data buffer. Start of data buffer. Calculate DCHK checksum. Jump with faulty sectors. Reset map bit with usable sectorS. Decrease SECTOR. Check all sectors.

1C43

CALL

1E3E,IN-CHK

Initialise RECFLG, BECLEN, and DESCHK.

At this point all sectors that have been found 'usable' (marked with a reset bit in the map) are written with RECFLG and RECLEN zeroed, as to mark them 'working' for future use. 1C46 1C49 1C48 1C4C 1C4F 1C52 1C53 1C56 MARK-FREE CALL JR XOR CALL CALL RET MK-BLK CALL JR 1264,CHK-FULL NZ,1C53,MK-BLK A 17F7,SEL-DRIVE 10C4,DEL-M-BUF 1275,SEND-BLK 1C46,MARK-FREE Jump until all bits in the map are made set. Turn off drive motors. Reclaim "m" channel and map. Finished. Write data block in the next 'free' sector. Jump back.

THE 'CAT' COMMAND ROUTINE This subroutine makes a CATalogue of the cartridge inserted in the drive whose number must be specified by D-STR1. S-STR1 must hold the stream to which the catalogue is to be directed. 1C58 1C5B 1C5C 1C5E 1C61 1C64 1C67 1C6A 1C6E 1C71 1C74 1C76 1C79 1C7C 1C7E 1C80 1C83 CAT LD RST DEFW CALL LD CALL LD LD CALL CALL JR LD OR AND JR CALL JR A,(S-STR1) 10,CALBAS +1601 0FE8,SET-T-MCH A,(CHDRIV) 17F7,SEL-DRIVE BC,+00FF (SECTOR),BC 12C4,GET-M-HD2 1E53,G-RDES NZ,1C6E,CAT-LP A,(RECFLG) (RECLEN-hi) +02 NZ,1C85,IN-NAME 12FE,RES-B-MAP 1CEE,F-N-SCT Fetch stream number. Call CHAN-OPEN to select the specified stream. Set a temporary "m" channel. Fetch drive number. Turn on drive motor. Pass through 255 sectors. Fetch a header. Fetch record descriptor. Repeat until data is correct. The sector is 'free for use' when bit 1 of both RECFLG and RECLEN-hi are reset (see comment after 1C43). Jump if not a 'free' sector. Reset map bit if it is a 'free' sector. Continue with next sector.

CAT-LP

If the current sector is not 'free', then the name of the file held in it is to be collected and inserted in alphabetical order into the data buffer of the "m" channel. Names starting with CHR$ 0 are refused. 1C85 1C88 1C89 1C8B 1C8D 1C8E 1C91 1C92 1C95 1C97 IN-NAME LD OR JR PUSH POP LD ADD LD LD LD A,(RECNAM) A Z,1CEE,F-N-SCT IX HL DE,+0052 HL,DE DE,+000A B,+00 C,(CHREC) Fetch first character of file name. Continue with next sector if this is CHR$ 0. Make HL point to the data buffer.

Length of filename. B is initiallly cleared. Start with 'file count' cleared.

The following loop tests to make sure that the name of the current record is not already stored in the buffer. 1C9A 1C9B 1C9C 1C9E 1C9F SE-NAME LD OR JR PUSH PUSH A,C A Z,1CD4,INS-NAME HL IX Jump forward when all existing names have been examined. Save registers.

1CA1 1CA2 1CA4 1CA5 1CA8 1CAA 1CAB 1CAD 1CAF 1CB1 1CB2 1CB3 1CB5 1CB7 1CB8 1CB9

T-NA-1

PUSH LD LD CF JR INC INC DJNZ POP POP POP JR JR ADD DEC JR

BC B,+0A A,(HL) (RECNAM) NZ,1CAF,T-NA-2 HL IX 1CA4,T-NA-1 BC IX HL Z,1CEE,F-N-SCT NC,1CBB,ORD-NAM HL,DE C 1C9A,SE-NAME

Counts characters in a name. Fetch a character from buffer. Compare against RECNAM. Jump if they do not match. Point to next character, Loop until all the names have been compared. Restore registers. Continue with next sector if the name is already stored. Jump if the name is surely not in the buffer (i.e. 'lower' than the current one). Otherwise point to next name. Decrease number of names to be examined. Loop back.

T-NA-2

The address at which the name is to be inserted has been calculated; the following names are moved down to create the space for storing the new name. 1CBB 1CBC 1CBD 1CBE 1CBF 1CC1 1CC2 1CC3 1CC4 1CC5 1CC6 1CC7 1CC8 1CC9 1CCA 1CCB 1CCC 1CCD 1CCE 1CCF 1CD1 1CD2 1CD3 1CD4 1CD6 1CD8 1CDB 1CDC 1CDE 1CDF 1CE1 1CE3 1CE6 1CE7 ORD-NAM PUSH PUSH PUSH PUSH SLA LD LD ADD ADD ADD ADD LD LD POP DEC ADD EX ADD EX LDIR POP POP POP PUSH LD LD LD INC INC DJNZ POP LD INC LD HL DE BC HL C H,B L,C HL,BC HL,BC HL,BC HL,BC B,H C,L HL HL HL,BC DE,HL HL,DE DE,HL BC DE HL IX B,+0A A,(IX+RECNAM) (HL),A IX HL 1CB8,MOVE-NA IX A,(CHREC) A (CHREC),A Save registers.

Multiply by two the number of names to be moved down. Move result into HL (B holds 0). Multiply this by 5, to get in HL the number of bytes to be moved down (i.e. number of names * 10). Move into BC the length of the block to be moved. Restore address where the current name is to be inserted. Make HL point to the last character of the last name. Calculate into DE the address 'HL+10'. Move down the required names (leaving 10 bytes for the current name). Restore registers. Save channel start address. Ten characters in a name. Fetch a character from the name. Store in the buffer. Point to next location. Loop until the whole name has been transferred. Restore channel start address. Fetch number of names in the buffer. Include current name in the count. Store new number.

INS-NAME MOVE-NA

1CEA 1CEC 1CEE 1CF1

F-N-SCT

CP JR CALL JP

+32 Z,1CF4,BF-FILLED 1312,BEC-SECT NZ,1C6E,CAT-LP

Jump if 50 names have been collected Decrease SECTOR. Repeat for all 255 sectors.

Now the file names are in the data buffer. CHREC holds the number of names stored. First the cartridge name is printed onto the selected stream. 1CF4 1CF6 1CF7 1CFA 1CFC 1CFD 1D00 1D01 1D04 1D06 1D09 1D0B 1D0C 1D0F 1D11 1D13 1D14 1D15 1D17 1D1A 1D1C 1D1F 1D20 1D22 1D23 1D25 1D27 1D2A 1D2B 1D2D 1D2F 1D32 1D34 1D37 BF-FILLED PUSH XOR CALL PUSH POP LD ADD CALL LD CALL PUSH POP LD ADD LD LD OR JR OT-NAMS CALL DJNZ NONAMES CALL LD SRL RST DEFW LD CALL RST DEFW LD CALL POP CALL RET IX A 17F7,SEL-DRIVE IX HL DE,+002C HL,DE 1D50,PRNAME A,+0D 1D66,PRCHAR IX HL DE,+0052 HL,DE B,(CHREC) A,B A Z,1D1C,NONAMES 1D53,PRNAME 1D17,OT-NAMS 1D38,FREESECT A,E A 10,CALBAS +2D28 A,+0D 1D66,PRCHAR 10,CALBAS +2DE3 A,+0D 1D66,PRCHAR IX 10C4,DEL-M-BUF Save channel base address. Switch off drive motor. Make HL point to HDNAME.

Print cartridge name. Print a carriage return. Make HL point to the data buffer.

Fetch number of stored names. Jump forward if no names have been stored. Print all filenames. Calculate number of 'free' sectors into A. Divide by two, giving the kilobytes left. Call STACK-A to store this value on the calculator stack. Print a carriage return. Call PRINT-FP to print the number of kilobytes left. Print the final carriage return. Restore channel start address. Reclaim the channel. Finished.

THE 'FREESECT' SUBROUTINE This subroutine is called to calculate the number of 'free sectors' (I...> bits reset in the map). The number is returned in the I register. 1D38 1D3B 1D3E 1D40 1D42 1D43 1D44 1D46 1D47 1D49 1D4A 1D4C 1D4D 1D4F FREESECT LD LD LD LD LD INC LD RRA JR INC DJNZ DEC JR RET L,(CHMAP-lo) H,(CHMAP-hi) E,+00 C,+20 A,(HL) HL B,+08 C,14DA,FR-S-RES E 1D46,FR-S-LPB C NZ,1D42,FR-SC-LP Fetch address of map. Start with E cleared. Length of map. Fetch a byte from the map. Advance the pointer. Loop for 8 bits. Jump if this bit is set, Otherwise increment counter. Repeat for all 8 bits. Repeat for all bytes in the map. Finished.

FR-SC-LP FR-S-LPB FR-S-RES

THE 'PRNAME' SUBROUTINE This is used from the CAT command routine to print a file-name starting from the address held into the HL register. 1D50 1D51 1D53 1D54 1D57 1D58 1D5A 1D5C 1D5F lD60 1D61 1D63 1D64 1D65 PRNAME PRNM-LP PUSH LD LD CALL INC DJNZ LD CALL PUSH RST DEFW POP POP RET BC B,+0A A,(HL) 1D66,PRCHAR HL 1D53,PRNM-LP A,+0D 1D66,PRCHAR HL 10,CALBAS +0D4D HL BC Save BC register. Counts 10 characters. Fetch a character. Print it. Increment pointer. Loop until the whole name has been printed. Print a carriage return. Save the pointer. Call main ROM 'TEMPS' subroutine. Restore registers.

THE 'PRCHAR' SUBROUTINE The character whose code is held in the A register is sent over the currently selected stream. 1D66 1D68 1D69 1D6B 1D6D PRCHAR PUSH RST DEFW POP RET IX 10,CALBAS +0010 IX Save channel base address. Call restart 'PRINT-A' to print the character. Restore channel base address. Finished.

THE 'ERASE' COMMAND SUBROUTINE This is also called by using 'hook code' +24. The subroutine is entered with D-STR1 holding the drive number and N-STR1 holding the length and the start of the filename, Note that H'L' is corrupted. 1D6E 1D71 1D74 1D77 1D79 1D7B ERASE CALL LD CALL IN AND JR 0FE8,SET-T-MCH A,(CHDRIV) 17F7,SEL-DRIVE A,(+EF) +01 NZ,107F,ERASE-1 Set a temporary "m" channel. Fetch drive number. Turn on drive motor. Continue only if the write-nrotect tab is present.

'Drive write protected' 1D7D 1D7E 1D7F 1D81 1D82 1D85 1D86 1D87 ERASE-1 RST DEFB PUSH POP LD ADD PUSH POP 20,SH-ERR +0E IX HL DE,+0052 HL,DE HL DE Call the error handling routine. Point to the data buffer.

Copy this address into DE.

Now the first 32 locations in the data buffer are cleared. These locations act as a 'pseudo-map', used to mark the sectors to be erased, 1D88 1D89 1D8C 1D8D 1D8E lD90 INC LD XOR LD LDIR LD DE BC,+001F A (HL),A A,+FF Point to next location. Gap length - 1. Clear the first location. Clear all locations. CHREC is made holding 255.

1D92 1D95 1D98

CD LD LD

(CHREC),A BC,+04FB (SECTOR),BC

SECTOR is initialised to 'five passes of the tape'.

The cartridge is searched for the sectors to be erased, and when any such sector is found, the relevant 'pseudo-map' bit is set. A 'normal' map is also set-up. 1D9C 1D9F 1DA1 1DA4 1DA7 1DA9 1DAC lDAF 1DB1 1DB3 1DB6 ERASE-LP CALL JR CALL CALL JR LD OR AND JR CALL JR 1312,DEC-SECT Z,1DF8,ERASE-MK 12C4,GET-M-HD2 1E53,G-RDES NZ,1DDA,TST-NUM A,(RECFLG) (RECLEN-hi) +02 NZ,1DB8,ERASE-2 12FE,RES-B-MAP 1DDA,TST-NUM Decrease SECTOR. Make the ERASE when it reaches 0. Fetch a header. Fetch record descriptor. Jump with any error. Jump it this record is not 'tree'.

Otherwise reset map bit to indicate 'free sector'. Jump forward.

The name of the current record is compared against that of the file to be erased. If the comparison is successful, the 'pseudo-map' bit is set to mark the sector. 1DB8 1DBA 1DBB 1DBE 1DBF 1DC2 1DC5 1DC7 1DCA 1DCB 1DCC 1DCD 1DD1 1DD3 1DD6 1DD7 1DDA 1DDC 1DDD 1DE0 1DE1 1DE3 IDE5 1DE6 1DE7 1DE9 1DEA 1DEC 1DED 1DEF 1DF0 1DF2 1DF5 1DF6 ERASE-2 PUSH POP LD ADD LD CALL JR CALL LD OR LD BIT JR LD INC LD PUSH POP LD ADD LD LD LD INC LD RRA JR INC DJNZ DEC JR LD CP JR IX HL DE,+0047 HL,DE BC,+000A 131E,CHK-NAME NZ,1DDA,TST-NUM 1306,TEST-PMAP A,B (HL) (HL),A 1,(RECFLG) Z,1DDA,TST-NUN A,(RECNUM) A (CHREC),A IX HL DE,+0052 HL,DE E,+00 C,+20 A,(HL) HL B,+08 NC,1DED,NOINC-C E 1DE9,LP-B-MAP C NZ,1DE5,LP-P-MAP A,(CHREC) E NZ,1D9C,ERASE-LP MAke HL point to RECNAM.

Counts ten characters in a name. Compare against CHNAME. Jump if it does not equal. Get position of pseudo-map bit. Fetch bit position. Set the pseudo-map bit. Jump if this is not the EOF block. Otherwise increment record number and store it into CHMREC, giving the number of sectors that composes the file. Make HL point to the 'pseudo-map'.

TST-NUM

LP-P-MAP LP-B-MAP NOINC-C

Clear E register. Length of the pseudo-map. Fetch pseudo-map byte. Advance the pointer. Counts 8 bits. Jump if this bit is reset. Otherwise increment counter. Loop for all 8 bits. Decrement map length. Loop until the whole map has been examined. Fetch number of records that composes the file. Compare with number of records found. Continue until all records have been found (or five passes of the tape have been made).

A 'free' sector descriptor is written in all records of the file to be erased. 1DF8 1DFB 1DFE 1E01 1E03 1E04 1E05 1E07 1E09 1E0C 1E0F 1E11 1E12 1E15 1E16 1E19 1E1B 1E1D 1E20 1E21 1E22 1E23 1E24 1E25 ERASE-MK CALL ERASE-MK2 CALL CALL JR PUSH PUSH LD OUT LD CALL PUSH POP LD ADD CALL LD OUT CALL POP POP LD CPL AND LD 1E3E,IN-CHK 12C4,GET-M-HD2 1306,TEST-PMAP Z,1E26,T-OTHER HL BC A,+E6 (+EF),A BC,0168 18FA,DELAY-BC IX HL DE,+0037 HL,DE 1878,OUT-M-BUF A,+EE (+EF),A 12FE,RES-B-MAP BC HL A,B (HL) (HL),A Set 'free record' attributes. Fetch a header. Jump if this is not the header of a record to be erased. Save map bit address and position. Start writing. Wait to insert part of the first gap. Make HL point to the data block preamble. Write the 'free record' descriptor. Stop writing. Reset map bit. Restore pseudo-map bit position and address. Reset the appropriate bit.

If the pseudo-map contains at least one bit set, there are other records to be erased. 1E26 1E28 1E29 1E2C 1E2D 1E2F 1E30 1E31 1E33 1E34 1E36 1E37 1E3A 1E3D T-OTHER PUSH POP LD ADD LD CHK-W-MAP LD OR JR INC DJNZ XOR CALL CALL RET IX HL DE,+0052 HL,DE B,+20 A,(HL) A NZ,1DFB,ERASE-MK2 HL 1E2F,CHK-W-MAP A 17F7,SEL-DRIVE 10C4,DEL-M-BUF Make HL point to the pseudo-map.

Length of pseudo-map. Fetch a byte. Jump back if the byte is not zero (i.e. there are other records to be erased). Next location. Loop for the whole map. Switch off drive motor. Reclaim channel and map. Finished.

THE 'SIGNAL "FREE SECTOR"' SUBROUTINE This subroutine is called from the FORMAT and ERASE command routines to mark the current record descriptor as 'free sector' identifier. 1E3E 1E3F 1E42 1E45 1E48 1E4A 1E4B 1E4E 1E4F 1E52 IN-CHK XOR LD LD LD PUSH POP LD ADD CALL RET A (RECFLG),A (RECLEN-lo),A (RECLEN-hi),A IX HL DE,+0043 HL,DE 1341,CMKS-HD-R Clear RECFLG and RECLEN.

Make HL point to RECFLG.

Restore DESCHK checksum. Finished.

THE 'OBTAIN A RECORD DESCRIPTOR' SUBROUTINE

This subroutine is used from the FORMAT and ERASE coamnand routines to fetch from the current Microdrive unit the record descriptor held in the current sector (i.e. RECFLG...DESCHK). The zero flag is returned reset with any error. 1E53 1E55 1E56 1E59 1E5A 1E5D 1E60 1E61 1E65 G-RDES PUSH POP LD ADD CALL CALL RET BIT RET IX HL DE,+0043 HL,DE 18A3,GET-M-HD 1341,CHKS-MD-R NZ 0,(RECFLG) Make HL point to RECFLG.

Fetch record descriptor. Calculate new checksum. Return if it is wrong. Return with zero flag reset if this is a header.

THE 'CALLS TO THE COMMAND ROUTINES' The six following calls are entered from the appropriate command syntax routine; on return from the command routine, the control returns to END1. 1E66 1E69 1E6B 1E6E 1E70 1E73 1E75 1E78 1E7A 1E7D 1E7F 1E82 1E84 ERASE-RUN CALL JR MOVE-RUN CALL JR CAT-RUN CALL JR FOR-RUN CALL JR OP-RUN CALL JR SAVE-RUN CALL JR ENDC JP 1D6E,ERASE 1E84,ENDC 13F1,MOVE 1E84,ENDC 1C58,CAT 1E84,ENDC 1B6E,FORMAT 1E84,ENDC 1AF0,OP-M-STRM 1E84,ENDC 14DA,SA-DRIVE 1E84,ENDC 05Cl,END1

The 'not used' routines The following are four subroutines that are never called from the shadow ROM code; these routines are however described below as they may be useful to the programmer. THE 'DISP-HEX' SUBROUTINE The contents of the A register are displayed on the screen in hexadecimal. All registers are preserved, excluding A. 1E87 1E88 1E89 1E8A 1E8B 1E8C 1E8F 1E90 1E92 1E94 1E96 1E98 1E9A 1E9D DISP-HEX PUSH RRA RRA RRA RRA CALL POP AND CP JR ADD ADD CALL RET AF Save A register. Shift left nibble to right.

DISP-NIB

1E90,DISP-NIB AF +0F +0A C,1E98,CONV-1 A,+07 A,+30 1EA9,DISP-CH

Print the first digit. Restore value. Clear left nibble. Jump if the value is lower than 9. Otherwise add +07 to reach the code of A..F. Add offset for ASCII code. Print the digit. Finished.

CONV-1

THE 'DISP-HEX2' SUBROUTINE This performs the same task as the preceding one, but the hex number is followed by a space. All registers are preserved. 1E9E DISP-HEX2 PUSH AF Save A register.

1E9F 1EA2 1EA4 1EA7 1EA8

CALL LD CALL POP RET

1E87,DISP-HEX A,+20 1EA9,DISP-CH AF

Print the hex number. Follow with a space. Restore A. Finished.

THE 'DISP-CH' SUBROUTINE This subroutine prints on the screen the character held in the accumulator. All registers are preserved. 1EA9 1EAA 1EAB 1EAC 1EAD 1EAE 1EAF 1EB0 1EB1 1EB2 1EB5 1EB6 1EB7 1EB9 1EBA 1EBC 1EBD 1EBE 1EC0 1EC1 1EC4 1EC5 1EC6 1EC7 1EC8 1EC9 1ECA 1ECB 1ECC 1ECD DISP-CH PUSH PUSH PUSH PUSH EXX PUSH PUSH PUSH PUSH LD PUSH PUSH LD RST DEFW POP RST DEFW POP LD POP POP POP POP EXX POP POP POP POP RET HL DE BC AF HL DE BC AF HL,(CURCHL) HL AF A,+02 10,CALBAS +1601 AF 10,CALBAS +0010 HL (CURCHL),A AF BC DE HL AF BC DE HL Save registers.

Save alternate registers.

Save also current channel address. Save character to be printed. Select stream 2 (screen). Call CHAN-OPEN to select the stream. Restore character. Call PRINT-A restart to print the character. Restore old channel address. Restore alternate registers.

Restore normal registers.

THE 'HEX-LINE' SUBROUTINE This subroutine displays the values of the ten bytes from the location passed in the HL register. Again all registers are preserved. 1ECE 1ECF 1ED0 1ED1 1ED3 1ED4 1ED7 1ED8 1EDA 1EDC 1EDF 1EE0 1EEl 1EE2 1EE3...1FFF HEX-LINE PUSH PUSH PUSH LD HEX-LINE2 LD CALL INC DJNZ LD CALL POP POP POP RET HL BC AF B,+0A A,(HL) 1E9E,DISP-HEX2 HL 1ED3,HEX-LINE2 A,+0D 1EA9,DISP-CH AF BC HL Save registers. Counts 10 bytes. Fetch a byte. Display it in hex and follow with a space. Point to next byte. Loop for all l0 bytes. Finally print a carriage return. Restore registers. Finished.

Unused locations (all set to +FF).

APPENDIX 1 Labels sorted by address value (For edition 1 Shadow ROM) 0000 0020 003A 0077 00BC 011B 013B 015D 0182 01EC 0235 026E 0494 04BF 04ED 0529 0559 059F 05BF 05E2 060C 064C 066D 068F 06B9 0716 0750 079A 07B8 07F4 0819 084F 087D 0894 08CD 08E7 0904 0932 0962 0994 09BE 09F3 0A5C 0A7E 0AC9 0B47 0B7B 0BB1 0BDA 0C1D 0C46 0C6F 0CA4 0CDB 0CED 0D0C 0D3F 0D7A MAIN-ROM SH-ERR TEST-SP CHECK-SP START-4 RUNTIME TEST-LOW CHKEND RCLM-NUM ERR-V VAR-EXIST FETCH-ERR MISSING-D NO-FOR-M OPEN-SYN OP-M-C CLS#-SYN EX-D-STR TEST-RET BREAK-PGM TEST-NEXT NREPORT-4 CHECK-M-2 TEST-STAT EXPT-EXP1 NO-NAME END-EXPT DEFLT-0 END-CODE NONS-BSC NUM-ARR SA-HEADER S-BLK-END LOAD-SYN TS-L-NET LD-HD-RS TST-MERGE LD-BLK-2 LD-NO-PGM TST-MR-N TST-SPACE CRT-NEW LV-ANY LV-BN-E SET-BAUD OP-RSCHAN TCHAN-IN READ-RS BD-DELAY SER-IN-2 NOT-TOKEN BD-DEL-1 BD-DEL-3 IN-AGAIN ACC-CODE N-INPUT GET-N-BUF TEST-OUT 0008 0028 0040 0081 00E7 0130 0144 0165 01A3 01F0 024D 029F 04A6 04CD 0500 052F 057F 05A7 05C1 05E7 061E 064E 0681 06A1 06CC 0722 0753 079F 07D2 07F6 081C 0854 0880 089E 08D3 08EC 0911 0941 0967 09A0 09C7 0A0F 0A6A 0A8A 0AD0 0B4A 0B81 0BC5 0BF0 0C1F 0C4C 0C74 0CA9 0CE1 0CF7 0D12 0D45 0D88 ST-SHADOW ROMERR RMERR-2 CALBAS-2 NREPORT-0 PROG-LINE LINE-LEN CHKEVEN NXT-1 ERR-6 RES-VARS PR-REP-LP CAT-SCRN FOR-B-T NOT-OP-M NREPORT-C CLR#-SYN ALL-BYTES END1 EXPT-STR EXPT-NUM EXPT-STRM NREPORT-5 NREPORT-8 ENDHERE NOT-NET PROC PAR-1 DATA G-TYPE END-DATA HD-LOOP SA-BYTE VERIF-SYN TS-L-RS LD-HDR-2 T-M-CODE LD-BLK-3 MERGE-BLK MERGE-END TST-TYPE END-LD-PR LV-BN VR-BN NXT-ENTRY OP-STREAM BCHAN-IN TST-AGAIN WAIT-1 BD-DELAY2 NOT-GRAPH TEST-DTR BORD-REST INPUT-END END-INPUT NCHAN-IN TRY-AGAIN ST-BF-LEN 0010 0030 0066 009A 00E9 0133 014E 0169 01A5 01F7 0252 02AC 04B2 04D3 051C 0531 0584 05B1 05DD 05F2 062D 0663 0683 06A3 0700 073C 0771 07A7 07DA 0803 082F 086E 088E 08A8 08D8 08F2 0919 0952 0973 09A3 09DE 0A15 0A72 0A8F 0AE4 0B6F 0B8E 0BD1 0BF1 0C36 0C57 0C88 0CB4 0CE5 0CFB 0D1E 0D5F 0D93 CALBAS NEWVARS NMINT-SRV START-2 TEST-CODE SC-L-LOOP SKIP-NUN CHKEND-L NEXTNUM CRT-VARS EACH-VAR END-PR-MS OREPORT-1 NOT-FOR-B OPEN-RS ERASE-SYN NONSENSE SEPARATOR RETAD-RUN EXPT-SPEC NREPORT-3 NREPORT-2 NREPORT-9 EXPT-EXPR UNPAGE OREP-1-2 SCREEN$ TEST-SAVE NO-M-ARR VR-DATA SAVE-SYN SA-BLOCK SA-NET MRG-SYN LD-HEADER TEST-TYPE LD-BLOCK LD-BLK-4 NO-AUTOST LD-PR-AR T-LD-NET SET-PROG LV-N LVBN-END END-SET T-INPUT REC-BYTE START-BIT WAIT-2 END-RS-IN NOT-CR SER-OUT-L BRK-INOUT OREPORT-8 INKEY$ TEST-BUFF TIME-OUT OUT-BLK-N 0018 0038 0068 00A5 00FB 0139 0152 016F 01AA 0224 0258 0486 04B4 04E7 051F 053D 058E 05B7 05E0 05F5 062F 0665 0685 06B0 0701 073E 0789 07B2 07F2 080E 0849 0872 0891 08AF 08E0 0902 0930 0959 0988 09B5 09E8 0A4E 0A79 0A95 0813 0B75 0B9A 0BD8 0BF9 0C3C 0C5A 0C8E 0CBD 0CEA 0D01 0D38 0D6C 0DAB CHKSYNTAX INT-SERV ST-ERROR START-3 COPYCHADD NREPORT-1 EACH-ST S-STAT CL-WORK DEFAULT REP-MSG CAT-SYN FRTM-SYN FOR-M NOT-OP-B MOVE-SYN ALL-STRMS ST-END RETAD-SYN EXP-SPEC2 EXPT-NAME CHECK-H TEST-MNAM TEST-BAUD EXPT-PRMS LINE CODE PAR-2 EXISTING LD-DATA SAVE-M SA-BLK-LP SA-B-END LD-VF-MR LD-HD-NET NREPORT-H NREPORT-L LD-BLK-5 TST-MR-M LD-PROG RCLM-OLD NO-AUTO LV-B LOAD-RUN OP-RS-CH B-INPUT REC-PROC SERIAL-IN T-FURTHER TCHAN-OUT BCHAN-OUT BD-DEL-2 CALL-INP NO-READ INK$-END TST-N-EOF NCHAN-OUT S-PACK-1

0DB2 0DF6 0E12 0E45 0E93 0EB5 0F15 0F4D 0F61 0F92 0FAE 0FCC 102A 108A 110A 1132 1177 11A3 11E6 121F 127D 12B6 12DF 12FE 1322 1346 135F 1384 13F1 142E 148B 14C7 14FC 155E 1599 1613 1648 166C 16AC 175E 17A4 17DE 1809 182A 1867 1884 18AD 18C0 1BFA 1929 1968 1987 19DE 1A01 1A24 1A51 1A91 1AC7 1B23 1B5C 1B86 1C0A 1C53

SEND-PACK SP-DL-1 DL-LOOP TEST-BLKN STORE-LEN OP-PERN-N MAKESURE E-READ-N SEND-SC INPAK UNTIL-MK OUTPAK-L NEXT-CHAN ST-MAP-AD NXTCHAN RWF-ERR GET-RECD RS-SH2 NOREAD CP-NAME FAILED NOEMP CHECK-MAP RES-B-MAP ALL-CHARS CHKS-BUFF REST-STRM UPD-POINT MOVE MOVE-EOF CHECK-R EX-DSTR2 NEW-NAME SA-DRI-4 F-HD-3 SA-MAP LD-VE-M F-REC2 TST-PLACE CL-N-CH UPD-NXT-S CHK-TEMPN TON-DELAY SW-MOTOR DEL-S-1 NOT-PROT GET-M-BLK CHK-LP-2 DELAY-BC UNKN-3 UNKN-7 CLR-ERR WTKEY KBD-TEST CLOSE-NET NO-GOOD WR-SECTOR WR-S-3 STORE-DSP OP-F-3 FORMAT-1 TEST-SCT MK-BLK

0DC5 0DFD 0E18 0E62 0EA1 0EF5 0F1E 0F56 0F72 0F94 0FBE 0FD2 1034 1094 1114 1134 117D 11A5 11FF 1264 128F 12BE 12E2 1306 1333 1349 1365 1391 1414 1455 1495 14CF 1530 1579 15A9 1620 1658 1673 1708 176D 17B9 17ED 1811 1835 1872 1895 18B1 18D2 18FB 1952 196C 19A4 19EC 1A09 1A31 1A63 1AAD 1AE0 1B29 1B5F 1BBD 1C1B 1C58

CHKS1 INC-BLKN GET-NBLK GETNB-END GETNBF-END SEND-NEOF WT-SCOUT SCOUT-END ALL-BITS N-ACTIVE SEND-RESP UNT-MARK CHAN-SPC FILL-MAP RCLM-MAP TEST-M-BF GET-R-2 G-HD-RC WR-RECD CHK-FULL NO-PRT ERR-RS ENTRY TEST-PMAP ALLCHR-2 CHKS-ALL NXT-STRM REST-MAP M-AGAIN OP-STRM USE-R ALL-BYT-2 SA-DRI-2 END-SA-DR LV-MCH SA-MAP-LP VE-M-E UNTILFIVE CLOSE-CH CL-M-CN RCL-T-CH PT-N-CHAN REPTEST ALL-MOTRS OUT-M-HD OUT-M-BYT CHK-AGAIN DR-READY DELAY-BC1 UNKN-4 UNKN-8 HOOK-32 CONS-OUT READ-SEQ GET-PACK USE-C-RC WR-S-1 CLR-BUFF OP-TEMP-M OP-F-4 FILL-B-F CHK-SCT CAT

0DD4 0E05 0E27 0E65 0EA3 0F03 0F21 0F58 0F7D 0F9D 0FC5 0FE8 1061 10C4 1122 1158 1184 11D6 120D 126C 12A6 12C4 12E8 1312 133E 134C 1377 139D 141A 1466 14A4 14DA 1538 1580 15DF 162D 1664 168A 1718 177F 17C2 17F7 1813 184B 1878 18A3 18B3 18DE 1902 1959 1976 19A8 19EF 1A14 1A46 1A81 1ABF 1AF0 1B3A 1B6C 1BC1 1C3E 1C6E

CHKS2 SP-N-END CHKS3 GET-NBUFF OPEN-N-ST NET-STATE CLAIMED LP-SCOUT S-SC-DEL INPAK-2 OUTPAK SET-T-MCH T-CH-NAME DEL-M-BUF M-INPUT CHK-M-EOF GET-R-LP G-REC-ERR WRITE-PRC NXT-B-MAP CLOSE-M GET-M-HD2 ENTRY-2 DEC-SECT CHKNAM-END NXT-BYTE NOTRIGHT LCHAN I-AGAIN OP-CHAN CL-CHAN SA-DRIVE SA-DRI-3 F-M-HEAD USE-REC RE-MAP VE-FAIL F-ERROR CLOSE RCLM-CH EX-CHANS SEL-DRIVE CHK-PRES OFF-MOTOR OUT-M-BUF GET-M-HD CHKLOOP IN-M-BLK UNKN-1 UNKN-5 UNKN-9 HOOK-31 OUT-CODE INCREC GP-ERROR DEL-B-CT WR-S-2 OP-M-STRM OP-F-1 OP-F-5 FILL-B-F2 CHK-NSECT CAT-LP

0DDA 0E0F 0E40 0E87 0EA9 0F0E 0F35 0F5D 0F8F 0F9E 0FCA 0FF6 106F 10F5 112C 1162 119E 11D8 121B 1275 12A9 12DA 12F8 131E 1341 1354 137E 13C1 1423 147F 14B8 14E8 1552 1591 15F9 163D 1666 1691 1751 1789 17D2 1802 1820 185C 187C 18A9 18BE 18E9 191C 1961 1981 19D9 19FC 1A17 1A4B 1A86 1AC5 1B0D 1B57 1B6E 1BD6 1C46 1C85

SENDSCOUT BR-DELAY BRCAST CHKS4 OP-TEMP-N CHK-REST WT-SYNC DELAY-SC END-S-DEL INPAK-L DEL-D-1 CHK-LOOP TEST-MAP TEST-MCHL MCHAN-IN NEW-BUFF NXT-SCT MCHAN-OUT NOFULL SEND-BLK CLOSE-M2 CHK-MAP-2 ROTATE CHK-NAME CHKS-HD-R STCHK STO-DISP LPEND MOVE-OUT CHECK-N CL-CHK-N START-SA SA-DRI-WR F-HD-2 LOOK-MAP RE-MAP-LP F-REC1 REST-N-AD CL-RS-CH UPD-STRM CHK-TEMPM TURN-ON NOPRES NXT-MOTOR OUT-M-BLK GET-M-BUF CHK-AG-2 TEST-ERR UNKN-2 UNKN-6 HOOK-CODE CONS-IN PRT-OUT RD-RANDOM RD-SECTOR RD-NEXT RS-SH MAKE-PERM OP-F-2 FORMAT WR-F-TEST MARK-FREE IN-NAME

1C9A 1CD4 1D17 1D46 1D66 1DB8 1DED 1E2F 1E6B 1E7F 1E98 1ED3

SE-NAME INS-NAME OT-NAMS FR-S-LPB PRCHAR ERASE-2 NOINC-C CHK-W-MAP MOVE-RUN SAVE-RUN CONV-1 HEX-LINE2

1CA4 1CD8 1D1C 1D4A 1D6E 1DDA 1DF8 1E3E 1E70 1E84 1E9E

T-NA-1 MOVE-NA NONAMES FR-S-RES ERASE TST-NUM ERASE-MK IN-CHK CAT-RUN ENDC DISP-HEX2

1CAF 1CEE 1D38 1D50 1D7F 1DE5 1DFB 1E53 1E75 1E87 1EA9

T-NA-2 F-N-SCT FREESECT PRNAME ERASE-1 LP-P-MAP ERASE-MK2 G-RDES FOR-RUN DISP-HEX DISP-CH

1CBB 1CF4 1D42 1D53 1D9C 1DE9 1E26 1E66 1E7A 1E90 1ECE

ORD-NAM BF-FILLED FR-SC-LP PRNM-LP ERASE-LP LP-B-MAP T-OTHER ERASE-RUN OP-RUN DISP-NIB HEX-LINE

APPENDIX 2 Labels sorted alphabetically (For edition 1 Shadow ROM) ACC-CODE ALL-CHARS B-INPUT BD-DEL-2 BF-FILLED BREAK-PGM CALL-INP CAT-SCRN CHECK-M-2 CHECK-SP CHK-LOOP CHK-NAME CHK-SCT CHKEND CHKNAM-END CHKS1 CHKSYNTAX CL-N-CH CLOSE CLOSE-NET CLS#-SYN CONV-1 CRT-VARS DEFLT-0 DEL-S-1 DISP-CH DL-LOOP EACH-VAR END-INPUT END-S-DEL ENDC ERASE ERASE-MK ERR-6 EX-D-STR EXPT-EXP1 EXPT-PRMS F-ERROR F-N-SCT FETCH-ERR FOR-B-T FORMAT-1 FREESECT G-REC-ERR GET-M-HD GET-NBUFF GET-RECD HD-LOOP HOOK-32 IN-CHK INCREC INPAK-2 INT-SERV LD-BLK-3 LD-DATA LD-HEADER LD-VE-M LOAD-RUN 0CED 1322 0B75 0C8E 1CF4 05E2 0CBD 04A6 066D 0077 0FF6 131E 1C1B 015D 133E 0DC5 0018 175E 1718 1A24 0559 1E98 01F7 079A 1867 1EA9 0E12 0252 0CF7 0F8F 1E84 1D6E 1DF8 01F0 059F 06B9 0701 168A 1CEE 026E 04CD 1B86 1D38 11D6 18A3 0E65 1177 0854 19A4 1E3E 1A14 0F9D 0038 0941 080E 08D8 1648 0A95 ALL-BITS ALL-MOTRS BCHAN-IN BD-DEL-3 BORD-REST BRK-INOUT CAT CAT-SYN CHECK-MAP CHK-AG-2 CHK-LP-2 CHK-NSECT CHK-TEMPM CHKEND-L CHKS-ALL CHKS2 CL-CHAN CL-RS-CH CLOSE-CH CLR#-SYN CODE COPYCHADD DATA DEL-B-CT DELAY-BC DISP-HEX DR-READY END-CODE END-LD-PR END-SA-DR ENDHERE ERASE-1 ERASE-MK2 ERR-RS EX-DSTR2 EXPT-EXPR EXPT-SPEC F-HD-2 F-REC1 FILL-B-F FOR-M FR-S-LPB FRTM-SYN G-TYPE GET-M-HD2 GET-PACK GETNB-END HEX-LINE HOOK-CODE IN-M-BLK INK$-END INPAK-L KBD-TEST LD-BLK-4 LD-HD-NET LD-NO-PGM LD-VF-MR LOAD-SYN 0F72 1835 0B81 0CA4 0CA9 0CB4 1C58 0486 12DF 18BE 18C0 1C3E 17D2 0169 1349 0DD4 14A4 1751 1708 057F 0789 00FB 07D2 1A81 1BFA 1E87 18D2 07B8 0A0F 1579 06CC 1D7F 1DFB 12BE 14C7 06A3 05F2 1591 1666 1BBD 04E7 1D46 04B4 07F6 12C4 1A31 0E62 1ECE 1981 18DE 0D01 0F9E 1A01 0952 08E0 0962 08AF 0894 ALL-BYT-2 ALL-STRMS BCHAN-OUT BD-DELAY BR-DELAY CALBAS CAT-LP CHAN-SPC CHECK-N CHK-AGAIN CHK-M-EOF CHK-PRES CHK-TEMPN CHKEVEN CHKS-BUFF CHKS3 CL-CHK-N CL-WORK CLOSE-M CLR-BUFF CONS-IN CP-NAME DEC-SECT DEL-D-1 DELAY-BC1 DISP-HEX2 E-READ-N END-DATA END-PR-MS END-SET ENTRY ERASE-2 ERASE-RUN ERR-V EXISTING EXPT-NAME EXPT-STR F-HD-3 F-REC2 FILL-B-F2 FOR-RUN FR-S-RES G-HD-RC GET-M-BLK GET-N-BUF GET-R-2 GETNBF-END HEX-LINE2 I-AGAIN IN-NAME INKEY$ INPUT-END LCHAN LD-BLK-5 LD-HD-RS LD-PR-AR LINE LOOK-MAP 14CF 058E 0C5A 0BDA 0E0F 0010 1C6E 1034 147F 18B1 1158 1813 17DE 0165 1346 0E27 14B8 01AA 12A6 1AE0 19D9 121F 1312 0FCA 18FB 1E9E 0F4D 081C 02AC 0AE4 12E2 1DB8 1E66 01EC 07F2 062F 05E7 1599 166C 1BC1 1E75 1D4A 11A5 18AD 0D3F 117D 0EA1 1ED3 141A 1C85 0CFB 0CE1 139D 0959 08E7 09A3 073E 15F9 ALL-BYTES ALLCHR-2 BD-DEL-1 BD-DELAY2 BRCAST CALBAS-2 CAT-RUN CHECK-H CHECK-R CHK-FULL CHK-MAP-2 CHK-REST CHK-W-MAP CHKLOOP CHKS-HD-R CHKS4 CL-M-CN CLAIMED CLOSE-M2 CLR-ERR CONS-OUT CRT-NEW DEFAULT DEL-M-BUF DELAY-SC DISP-NIB EACH-ST END-EXPT END-RS-IN END1 ENTRY-2 ERASE-LP ERASE-SYN EX-CHANS EXP-SPEC2 EXPT-NUM EXPT-STRM F-M-HEAD FAILED FILL-MAP FORMAT FR-SC-LP G-RDES GET-M-BUF GET-NBLK GET-R-LP GP-ERROR HOOK-31 IN-AGAIN INC-BLKN INPAK INS-NAME LD-BLK-2 LD-BLOCK LD-HDR-2 LD-PROG LINE-LEN LP-B-MAP 05A7 1333 0C6F 0C1F 0E40 0081 1E70 0665 148B 1264 12DA 0F0E 1E2F 18B3 1341 0E87 176D 0F21 12A9 1987 19EC 09F3 0224 10C4 0F5D 1E90 0152 0750 0C36 05C1 12E8 1D9C 0531 17C2 05F5 061E 064E 1580 127D 1094 1B6E 1D42 1E53 18A9 0E18 1184 1A46 19A8 0CDB 0DFD 0F92 1CD4 0932 0919 08EC 09B5 0144 1DE9

LP-P-MAP LV-B LV-N MAIN-ROM MCHAN-IN MISSING-D MOVE-NA MRG-SYN NCHAN-OUT NEWVARS NO-AUTO NO-M-ARR NOEMP NONS-BSC NOT-CR NOT-OP-B NOTRIGHT NREPORT-3 NREPORT-9 NUM-ARR NXT-ENTRY NXTCHAN OP-F-2 OP-M-C OP-RSCHAN OP-TEMP-M OPEN-SYN OREPORT-8 OUT-M-BLK OUTPAK PR-REP-LP PROC RCL-T-CH RCLM-OLD RE-MAP REC-BYTE RES-B-MAP REST-STRM ROMERR RUNTIME S-SC-DEL SA-BLOCK SA-DRI-4 SA-MAP SAVE-RUN SCREEN$ SEND-NEOF SENDSCOUT SERIAL-IN SH-ERR ST-BF-LEN ST-SHADOW START-BIT STORE-DSP T-FURTHER T-NA-1 TCHAN-OUT TEST-CODE TEST-M-BF TEST-NEXT TEST-SAVE TEST-TYPE TS-L-NET

1DE5 0A79 0A72 0000 112C 0494 1CD8 08A8 0D6C 0030 0A4E 07DA 12B6 07F4 0C57 051F 1377 062D 0683 0819 0AD0 110A 1B57 0529 0B47 1B29 04ED 0CE5 187C 0FC5 029F 0753 17B9 09E8 162D 0B8E 12FE 135F 0028 011B 0F7D 086E 155E 1613 1E7F 0771 0EF5 0DDA 0BD8 0020 0D88 0008 0BD1 1B23 0BF9 1CA4 0C3C 00E9 1134 060C 07A7 08F2 08CD

LP-SCOUT LV-BN LVBN-END MAKE-PERM MCHAN-OUT MK-BLK MOVE-OUT N-ACTIVE NET-STATE NEXT-CHAN NO-AUTOST NO-NAME NOFULL NONSENSE NOT-FOR-B NOT-OP-M NREPORT-0 NREPORT-4 NREPORT-C NXT-1 NXT-MOTOR OFF-MOTOR OP-F-3 OP-M-STRM OP-RUN OP-TEMP-N ORD-NAM OT-NAMS OUT-M-BUF OUTPAK-L PRCHAR PROG-LINE RCLM-CH RD-NEXT RE-MAP-LP REC-PROC RES-VARS RETAD-RUN ROTATE RWF-ERR S-STAT SA-BYTE SA-DRI-WR SA-MAP-LP SAVE-SYN SE-NAME SEND-PACK SEPARATOR SET-BAUD SKIP-NUN ST-END START-2 START-SA STORE-LEN T-INPUT T-NA-2 TEST-BAUD TEST-DTR TEST-MAP TEST-OUT TEST-SCT TIME-OUT TS-L-RS

0F58 0A6A 0A8F 1B0D 11D8 1C53 1423 0F94 0F03 102A 0973 0716 121B 0584 04D3 0500 00E7 064C 052F 01A3 185C 184B 1B5C 1AF0 1E7A 0EA9 1CBB 1D17 1878 0FCC 1D66 0130 177F 1A86 163D 0B9A 024D 05DD 12F8 1132 016F 0880 1552 1620 082F 1C9A 0DB2 05B1 0AC9 014E 05B7 009A 14E8 0E93 0B6F 1CAF 06B0 0C74 106F 0D7A 1C0A 0D5F 08D3

LPEND LV-BN-E M-AGAIN MAKESURE MERGE-BLK MOVE MOVE-RUN N-INPUT NEW-BUFF NEXTNUM NO-FOR-M NO-PRT NOINC-C NOPRES NOT-GRAPH NOT-PROT NREPORT-1 NREPORT-5 NREPORT-H NXT-B-MAP NXT-SCT OP-CHAM OP-F-4 OP-PERN-N OP-STREAM OPEN-N-ST OREP-1-2 OUT-BLK-N OUT-M-BYT PAR-1 PRNAME PRT-OUT RCLM-MAP RD-RANDOM READ-RS REP-MSG REST-MAP RETAD-SYN RS-SH S-BLK-END SA-B-END SA-DRI-2 SA-DRIVE SA-NET SC-L-LOOP SEL-DRIVE SEND-RESP SER-IN-2 SET-PROG SP-DL-1 ST-ERROR START-3 STCHK SW-MOTOR T-LD-NET T-OTHER TEST-BLKN TEST-ERR TEST-MCHL TEST-PMAP TEST-SP TON-DELAY TST-AGAIN

13C1 0A7E 1414 0F15 0967 13F1 1E6B 0D0C 1162 01A5 04BF 128F 1DED 1820 0C4C 1884 0139 0681 0902 126C 119E 1466 1B5F 0EB5 0B4A 0EA3 073C 0D93 1895 079F 1D50 19FC 1114 1A17 0BB1 0258 1391 05E0 1AC5 087D 0891 1530 14DA 088E 0133 17F7 0FBE 0C1D 0A15 0DF6 0068 00A5 1354 182A 09DE 1E26 0E45 18E9 10F5 1306 003A 1809 0BC5

LV-ANY LV-MCH M-INPUT MARK-FREE MERGE-END MOVE-EOF MOVE-SYN NCHAN-IN NEW-NAME NMINT-SRV NO-GOOD NO-READ NONAMES NOREAD NOT-NET NOT-TOKEN NREPORT-2 NREPORT-8 NREPORT-L NXT-BYTE NXT-STRM OP-F-1 OP-F-5 OP-RS-CH OP-STRM OPEN-RS OREPORT-1 OUT-CODE OUT-M-HD PAR-2 PRNM-LP PT-N-CHAN RCLM-NUM RD-SECTOR READ-SEQ REPTEST REST-N-AD RMERR-2 RS-SH2 S-PACK-1 SA-BLK-LP SA-DRI-3 SA-HEADER SAVE-M SCOUT-END SEND-BLK SEND-SC SER-OUT-L SET-T-MCH SP-N-END ST-MAP-AD START-4 STO-DISP T-CH-NAME T-M-CODE TCHAN-IN TEST-BUFF TEST-LOW TEST-MNAM TEST-RET TEST-STAT TRY-AGAIN TST-MERGE

0A5C 15A9 1122 1C46 09A0 142E 053D 0D12 14FC 0066 1A51 0CEA 1D1C 11E6 0722 0C46 0663 06A1 0930 134C 1365 1B3A 1B6C 0813 1455 051C 04B2 19EF 1872 07B2 1D53 17ED 0182 1A4B 1A09 1811 1691 0040 11A3 0DAB 0872 1538 084F 0849 0F56 1275 0F61 0C88 0FE8 0E05 108A 00BC 137E 1061 0911 0B7B 0D1E 013B 0685 05BF 068F 0D45 0904

TST-MR-M TST-PLACE UNKN-1 UNKN-5 UNKN-9 UNTILFIVE USE-C-RC VE-FAIL VR-DATA WR-RECD WR-SECTOR WTKEY

0988 16AC 1902 1959 1976 1673 1A63 1664 0803 11FF 1A91 19DE

TST-MR-N TST-SPACE UNKN-2 UNKN-6 UNPAGE UPD-NXT-S USE-R VE-M-E WAIT-1 WR-S-1 WRITE-PRC

0994 09BE 191C 1961 0700 17A4 1495 1658 0BF0 1AAD 120D

TST-N-EOF TST-TYPE UNKN-3 UNKN-7 UNT-MARK UPD-POINT USE-REC VERIF-SYN WAIT-2 WR-S-2 WT-SCOUT

0D38 09C7 1929 1968 0FD2 1384 15DF 089E 0BF1 1ABF 0F1E

TST-NUM TURN-ON UNKN-4 UNKN-8 UNTIL-MK UPD-STRM VAR-EXIST VR-BN WR-F-TEST WR-S-3 WT-SYNC

1DDA 1802 1952 196C 0FAE 1789 0235 0A8A 1BD6 1AC7 0F35

APPENDIX 3 'Shadow' system variables Notes Address X1 23734 5CB6 Name FLAGS3 Contents Various flags: bit 0 - set during execution of 'new' commands. bit 1 - set during execution of CLEAR# command. On entry to the shadow ROM, it is set at the first 'paging' operation. bit 2 - set if the main ROM error handler is to be used. (Normally it is always reset). bit 3 - set if using the network. bit 4 - set during execution of LOAD and MOVE. bit 5 - set during execution of SAVE conwsand. bit 6 - set during execution of MERGE conmiand. bit 7 - set during execution of VERIFY cormnand. FLAGS3 may be addressed with (IY+124). Normally points to +01F0 (ERR-6). You may modify this address to point to a RAM routine. ROM paging subroutine as follows: 5CB9 5CBC 5CBF 5CC2 LD HL,nnnnn CALL nnnnn LD (H-L),HL RET

X2 X10

23735 5CB7 23737 5CB9

VECTOR SBRT

H-L EQU +5CBA Used to save the value of HL while calling 'main' ROM routines. 2 23747 5CC3 BAUD Timing constant used during RS232 i/o. May be obtained with: (3500000/(26*baud rate))-2 Network own station number 1..64. Border colour used during I/O; normally 0 (black) Low byte may be 00 or 01; high byte holds a received byte if low byte is 01. Counter of sectors examined during Microdrive operations. Temporary store for CH-ADD. Store for network response code +01. Start of network buffer; destination station number for the current packet (0..64). Station number of 'sending' Spectrum for the current packet. Current packet block number (0..65535). Packet type (00 for normal packets, 01 for EOF). Length of the data block being received, 1..255.

1 1 N2 N2 N2 1 1 1 X2 N1 X1

23749 5CC5 23759 5CC6 23751 5CC7 23753 5CC9 23755 5CCB 23757 5CCD 23758 5CCE 23759 5CCF 23768 5CD0 23762 5CD2 23763 5CD3

NTSTAT IOBORD SER-FL SECTOR CHADDNTRESP NTDEST NTSRCE NTNUMB NTTYPE NTLEN

N1 N1 N2

23764 5CD4 23765 5CD5 23766 5CD6

NTDCS NTCHS D-STR1

Current data block checksum. Current header block checksum. Start of first 8-byte file specifier; drive number 1..8, destination station number 0..64 or baud rate 75..19200. Stream number 0..15. Device specifier "m", "n", "t" or "b". Filename length Filename start address. Start of 2nd 8-byte file specifier, used by MOVE and LOAD commands. See S-STR1. See L-STR1. See N-STR1. Start of workspace used by LOAD, SAVE, VERIFY and MERGE commands. File type, may be: 00 - program 01 - numeric array 02 - string array 03 - bytes Data blocklength. Data block start address. Program length (without variables), or array name. Autostart line number (+FFFF if no autostart), or address of the routine called by using 'hook code' +32. Number of copies made by SAVE. Reset to +01 after the SAVE.

N1 N1 N2 N2 N2 N1 N1 N2 N2 N1

23768 5CD8 23769 5CD9 23770 5CDA 23772 5CDC 23774 5CDE 23776 5CE0 23777 5CE1 23778 5CE2 23788 5CE4 23782 5CE6

S-STR1 L-STR1 N-STR1 D-STR2 S-STR2 L-STR2 N-STR2 HD-00

N2 N2 N2 N2 1

23783 5CE7 23785 5CE9 23787 5CEB 23789 5CED 23791 5CEF

HD-0B HD-0D HD-0F HD-11 COPIES

APPENDIX 4 Channels 1. MICRODRIVE CHANNEL This area is used to communicate with the Microdrive device; it is created in the CHANS area. The start address is pointed by the IX index register in the shadow ROM program. 0 2 4 5 7 9 11 13 14 24 25 26 28 40 41 42 44 54 55 67 +0008 +0008 'M' +11D8 +1122 +0253 CHBYTE CHREC CHNAME CHFLAG CHDRIV CHMAP HDFLAG HDNUMB HDNAME HDCHK RECFLG Main ROM 'output' routine. Main ROM 'input' routine. Channel specifier ('M'+80H denotes a 'temporary' channel, used by SAVE, MOVE, etc.). Shadow ROM 'output' routine. Shadow ROM 'input' routine. Channel length. Position of the next byte to be received or stored in the buffer (0..512). Record number 0..255. Also used as temporary store of sector number. 10-byte filename with trailing spaces. Bit 0 set indicates a 'write' channel. Bit 0 reset indicates a 'read' channel. Drive number 1..8. Address of microdrive map. 12 bytes of header preamble (ten zeros and two +FF). Used to mark the start of the header block. Bit 0 set indicates that the received block is a header. Sector number from which the header comes. Unused 10-byte cartridge name with trailing spaces. Checksum from HDFLAG to HDCHK-1. 12 bytes of data preamble, as for the header; used to mark the start of the record descriptor. Bit 0 reset indicates that the received block is a record descriptor. Bit 1 is set if the record is the EOF one. Bit 2 is reset if the record is part of a PRINT-type file. Record number 0..255. Number of bytes in the record (0..512). 10-byte record name with trailing spaces. Checksum from RECFLG to DESCHK-l. Start of 512-byte buffer. Buffer checksum.

68 69 71 81 82 594

RECNUM RECLEN RECNAM DESCHK CHDATA DCHK

Bytes 0..27 are used as 'channel descriptor' and are never transmitted; bytes 28..54 are the 'header block'; bytes 55..594 are the 'data block'. Bytes 55..91 may be collected (or written) as 'record descriptor', without affecting the data buffer starting from CHDATA. 2. NETWORK CHANNEL As with the Microdrive channel, this area is addressed by using the IX index register it is used to communicate through the Local Area Network. 0 2 4 5 7 9 11 +0008 +0008 'N' +0D6C +0D9C +0114 NCIRIS Main ROM 'Output' routine, Main ROM 'Input' routine. Channel specifier ('N'+80H denotes a 'temporary' channel). Shadow ROM 'Output' routine. Shadow ROM 'Input' routine. Length of the channel. Destination station number 0..64.

12 13 15 16 17 18 19 20 21

NCSELF NCNUMB NCTYPE NCOBL NCDCS NCHCS NCCUR NCIBL NCB

Own station number 1..64. Current block bumber 0..65535. Packet type: 00 normal packet, 01 'end of file' packet. Number of bytes held in the buffer during 'output' (holds 0 if the channel is used for reading). Checksum of the 255-byte buffer. Checksum of block NCIRIS...NCDCS. Position of the currently received byte (in the buffer). Number of bytes in the buffer during 'output' (holds zero if the channel is used for writing). 255-byte data buffer.

While 'sending', the bytes 11.18 form the 'header block' (to be stored into the system variables NTDEST...NTCHS of the receiving Spectrum). Bytes 21..275 form the 'data block'. 3. RS232 "T" CHANNEL

This channel is created only when the RS232 link is to be attached to a stream. 'Temporary' RS232 channels are never created, because the RS232 I/O does not require a buffer to store the data to be sent or received (the only 'workspace' is made by the SER-FL system variable). 0 2 4 5 7 9 4. 0 2 4 5 7 9 +0008 +0008 'T' +0C3C +0B6F +000B Main ROM 'output' routine. Main ROM 'input' routine. Channel specifier. Shadow ROM 'output' routine. Shadow ROM 'input' routine. Channel length.

RS232 "B" CIANNEL +0008 +0008 'B' +0C5A +0B75 +000B Main ROM 'output' routine. Main ROM 'input' routine. Channel specifier. Shadow ROM 'output' routine. Shadow ROM 'input' routine. Channel length.

APPENDIX 5 Bibliography S. Vickers, ZX SPECTRUM BASIC PROGRAMMING (Sinclair Research Ltd.) MICRODRIVE AND INTERFACE I MANUAL (Sinclair Research Ltd.) I. Logan, F. O'Hara, THE COMPLETE SPECTRUM ROM DISASSEMBLY (Melbourne House) I. Logan, SPECTRUM MICRODRIVE BOOK (Melbourne House) R. Zaks, PROGRAMMING THE Z80 (Sybex)

APPENDIX 6 Index to routines (For edition 1 Shadow ROM) 0000 0008 0010 0018 0020 0028 0030 0038 003A 0040 0066 0068 0077 0081 009A 01F7 023A 024D 0258 0287 0486 04B4 04ED 0531 053D 0559 057F 059F 05B1 05B7 05Cl 05E7 05F2 061E 062F 064E 0665 0685 068F 06A3 06B0 06B9 0700 0701 082F 0880 0894 089E 08A8 08AF 0A5C 0A95 0AC9 0AEF 0B13 0B47 0B64 0B6F Return to main ROM Start Call a main ROM routine Test if syntax is being checked Shadow error Main ROM error restart Create new system variables restart Maskable interrupt TEST-SP Main ROM error routine Non-maskable interrupt ST-ERROR CHECK-SP CALBAS-2 Control routine Create new system variables routine System variables default values Reset new system variables Shadow report printing Shadow report messages CAT command syntax routine FORMAT command syntax routine OPEN command syntax routine ERASE command syntax routine MOVE command syntax routine CLS# command routine CLEAR# command routine Exchange file specifiers SEPARATOR End of statement Return to the main interpreter Evaluate string expression Evaluate channel expression Evaluate numeric expression Evaluate filename Evaluate stream number Check "m" parameters Check "m" parameters and filename Check station number Evaluate "x";n;"name" Check baud rate Evaluate stream or expression UNPAGE Evaluate parameters SAVE command syntax routine Save a byte to network or RS232 link LOAD command syntax routine VERIFY command syntax routine MERGE command syntax routine LOAD-VERIFY-MERGE commands routine LOAD or VERIFY Load "run" program Set "BAUD" system variable RS232 timing constants Open RS232 channel in CHANS area Attach channel to a stream "T" channel data "T" channel input

0B75 0B7B 0B81 0C3C 0C5A 0CA9 0CB4 0CBD 0D0C 0D12 0D6C 0D93 0DAB 0DB2 0E0F 0E18 0EA3 0EA9 0EB5 0EEA 0EF5 0F03 0F0E 0F1E 0F61 0F92 0FBE 0FC5 0FE8 10C4 1122 112C 1177 11A5 11D8 11FF 1264 1275 12A6 12BE 12C4 12DA 12FF 1306 1312 131E 1341 135F 1391 13CC 13E5 13F1 1455 14A4 14C7 14DA 1580 15A9 1613 162D 1648 1666 1691

"B" channel input "T" channel input service routine "B" channel input service routine "T" channel output "B" channel output Border colour restore Break into I/O operation CALL-INP "N" channel input "N" channel input service routine "N" channel output OUT-BLK-N S-PACK-1 SEND-PACK BR-DELAY Header and data block receiving OPEN "N" channel command routine Open temporary "n" channel Open permanent "n" channel "N" channel data Send EOF block to network Network state Check-resting Wait-scout Send-scout INPAK Send response byte OUTPAK Set a temporary "m" channel Reclaim "m" channel "M" channel input "M" channel input service routine Get a record Get header and data block "M" channel output Write record onto Microdrive CHK-FULL SEND-BLK Close file ERR-RS Fetch header from Microdrive Check map bit state Reset bit in map area Check 'pseudo-map' bit state Decrease sector counter CHECK-NAME Calculate/compare checksums Restore stream data Restore map addresses "M" channel data Preamble data MOVE command Use stream or temporary channel Close 'MOVE' channel Exchange DSTR1 and STR2 contents Save data block into Mlcrodrive Get header information from Microdrive Load or verify block from Microdrive Save Microdrive Map contents Restore Microdrive Map contents LD-VE-M Fetch record from Microdrive. Restore address of filename

1708 1718 17B9 17F7 1867 1872 18A3 18E9 18FA 1902 196C 1981 19A4 19A8 19A9 19D9 19EC 19FC 1A01 1A09 1A17 1A24 1A31 1A4B 1A86 1A91 1AE0 1AF0 1B29 1B6E 1C58 1D38 1D59 1D66 1D6E 1E3E 1E53 1E66 1E87 1E9E 1EA9 1ECE

CLOSE STREAM CLOSE command Reclaim temporary channels Select drive motor 1 millisecond delay Send data block to Microdrive Head Receive block from Microdrive Head TEST-BRK DELAY-BC UNKN-1 UNKN-8 HOOK-CODE Hook code +32 Hook code +31 Hook code addresses Console input Console output Printer output Keyboard test Read sequential Read random Close network channel Get packet from network Read sector Read next sector Write sector Clear buffer contents Open a permanent "m" channel Open a temporary "m" channel FORMAT "m" command CAT command FREESECT PRNAME PRCHAR ERASE command Signal 'free sector' Obtain record descriptor Calls to the command routines DISP-HEX DISP-HEX2 DISP-CH HEX-LINE

APPENDIX 7 Shadow ROM issue 2 The new ZX Interface 1's with serial number greater than 87315 have been provided with a new shadow ROM, with some general improvements. The Spectrums fitted with the 'new' interface will print '80' in response to the command 'PRINT PEEK 23729'. The main changes may be summed up as follows: The TCHAN-OUT subroutine ("t" channel output routine) has been remarkably improved; the TAB function now is supported, as well as the 'comma' control code, and the 'leading space' bug has been corrected. The whole subroutine is listed below. 0C3A 0C3C 0C3E 0C40 0C41 0C43 0C44 0C47 0C49 0C4B 0C4D 0C4F 0C51 0C53 0C55 0C57 0C59 0C5A 0C5D 0C60 0C63 0C65 0C68 0C6C 0C6D 0C70 0C72 0C74 0C78 0C7A 0C7D 0C7F 0C82 0C84 0C86 0C8A 0C8C 0C8D 0C8E 0C8F 0C90 0C92 0C94 0C96 TCHAN-OUT CP JR SUB RST DEFW RET NOT-TOKEN LD RES CP JR SET NOT-LEAD CP JR LD NOT-GRAPH CP JR PUSH INC LD CP JR CALL LD ENIT-CH POP JP CTRL-CD CP JR LD LD CALL LD JP NOT-CR CP JR LD LD SPC-COUNT INC INC LD CP JR CMM-LOOP SUB JR JR +A5 C,0C44,NOT-TOKEN +A5 10,CALBAS +0C18 HL,+5C3B 0,(HL) +20 NZ,0C4F,NOT-LEAD 0,(HL) +7F C,0C55,NOT-GRAPH A,+3F +20 C,0C78,CTRL-CD AF (IY+118) A,(5CB1) (IY+118) NC,0C6C,EMIT-CH 0C74,NEWLINE (IY+118),+01 AF 0D07,BCHAN-OUT +0D NZ,0C82,NOT-CR (IY+118),+00 A,+0D 0D07,BCHAN-OUT A,+0A 0D07,BCHAN-OUT +06 NZ,0CA5,NOT-CMM BC,(5CB0) E,+00 E C A,C B Z,0C9A,PRINT-SPC +08 Z,0C9A,PRINT-SPC NC,0C92,CMMLOOP Jump if the code is not a token code. Reduce range of token. And detokenise it by calling recursively this routine via main ROM 'PO-TOKENS' routine. This is FLAGS. Reset 'leading space' flag. Is the character a space? Jump if it is not. Otherwise set 'leading space' flag. Jump if the character is not a 'graphic' character. Otherwise print a '?' Jump with codes lower than +20. Save the character code. Increment current print position. Fetch line width. Jump if the position is lower than or equal to the value of WIDTH. Send CR and LF codes when pos>width. Reset 'print position' to +01. Restore character to be printed. Print it. Jump if the character is not 'CR'. Clear print position. Print a CR code. Print a LF code. Jump if the character is not the 'comma' control code. Fetch width into B, and print position into C. Increment space counter. Increment print position. Jump if 'position' reaches the right margin. Tabulate every 8 columns. Jump when reached the right column. Subtract again...

0C98 0C9A 0C9B 0C9D 0CA0 0CA1 0CA2 0CA3 0CA5 0CA7 0CA9 0CAB 0CAD 0CAF 0CB0 0CB3 0CB5 0CB8 0CBB 0CBE 0CBF 0CC2 0CC3 0CC4 0CC5 0CC6 0CC? 0CC8 0CCB 0CCE 0CD0 0CD3 0CD6 0CD7 0CDA 0CDC 0CDE 0CE0 0CE1 0CE2 0CE5 0CE6 0CE9 0CEA 0CFC 0CEE 0CEF 0CF0 0CF1 0CF2 0CF4 0CF5 0CF6 0CF9 0CFC 0CFD 0CFE 0CFF 0D01 0D04 0D05

JR 0C8C,SPC-COUNT PRINT-SPC PUSH DE LD A,+20 CALL 0C3A,TCHAN-OUT POP DE DEC E RET Z JR 0C9A,PRINT-SPC NOT-CNN CP +16 JR Z,0CB5,TAB-PROC CP +17 JR Z,0C5B,TAB-PROC CP +19 RET C LD DE,+0CD0 JR 0CB8,STORE-COD TAB-PROC LD DE,+0CC8 STORE-COD LD (TVDATA-lo),A ALTER-OUT LD HL,(CURCHL) PUSH DE LD DE,+0005 ADD HL,DE POP DE LD (HL),E INC HL LD (HL),D RET TAB-SERV LD DE,+0CD0 LD (TVDATA-hi),A JR 0CBB,ALTER-OUT LD DE,+0C3A CALL 0CBB,ALTER-OUT LD D,A LD A,(TVDATA-lo) CP +16 JR Z,0CE6,TST-WIDTH CP +17 CCF RET NZ LD A,(TVDATA-hi) LD D,A TST-WIDTH LD A,(5CB1) CP D JR Z,0CEE,TAB-MOD JR NC,0CF4,TABZERO TAB-MOD LD B,A LD A,D SUB B LD D,A JR 0CE6,TST-WIDTH TABZERO LD A,D OR A JP Z,0C74,NEWLINE TABLOOP LD CP RET PUSH LD CALL POP JR A,(5CB0) D Z DE A,+20 0C3A,TCHAN-OUT DE 0CF9,TABLOOP

Column not reached, jump back. Save space counter. Print the required number of spaces by calling TCHAN-OUT recursively. Restore space counter.

Jump with AT control code. Jump with TAB control code. Return with codes lower than 16d Service routine for INK, PAPER ...control codes. Service routine for AT and TAB Store first operand. Fetch current channel address. Point to 'output address' pointer. Store new 'output' address.

The new 'output' address. Store second operand. Jump to change the 'output' address. Restore the normal 'output' address. Pass 'second operand' to D. Fetch first operand (code type). Jump with AT. Return unless the code is TAB. Fetch TAB column. Move it into D. Fetch line width. Jump if TAB is at last column. Jump if TAB is within range. Fetch column width. Fetch TAB column. A=TAB-WIDTH The new position. Take (TAB pos.) MOD (width). Fetch TAB column. New line with TAB 0.

Fetch current print position. Return if already at the.. ..TAB position. Print the TAB spaces.

You should note that variable line width is allowed by POKEing the required value into the location 23729; the default value is 80. Other important alterations concern with Microdrive reading operations (i.e. channel opening, CAT command); the number of sectors examined is not fixed to 255, but is given by 'maximum sector number + 3'; this prevents the routine from reading 'nonexistent' sectors, and reduces the time required in the command execution. The checking operation done by the FORMAT command routine is now made directly with OUT instructions, without using the Microdrive channel to send/receive the blocks containing 'test data'. Other minor changes are: - The OP-RS-CH routine stores the "B" specifier into (IX+4) when opening a RS232 "b" channel. - Most of the tests done to the BREAK key have been replaced by calls to the TEST-BRK subroutine at +163E. - The carry flag is preserved when calling the BORD-REST subroutine at +0D4D; thus, the 'hook code' +2F works correctly. - When a "m" or "n" channel is created in the CHANS area (OP-PERM-N and SET-TMCH), a check is made to see if there is sufficient memory to insert the new space. - The GET-M-HD subroutine, returns (with bit 0 of HDFLAG or RECFLG inverted) when 'time-out' occurs during the reading operation. - An instruction 'SET 7,(HL)' has been inserted at location +1741 (i.e. the middle of the CLOSE routine), to correct the bug present in the 'old' shadow ROM (see comment to the CLOSE routine). - No CR code is sent when a RS232 'b" channel is closed. - The 'hook code' +2B calls the SET-T-MCH subroutine. - Two new 'hook codes', +33 and +34, are now available. Hook code +33 may be used to fetch a 'record descriptor' from the next sector; the microdrive motor must be turned on before calling the routine. The record descriptor is stored into the current microdrive channel (whose start address must be held into the IX index register), and the carry flag is returned set if any error occurs, or if the 'record descriptor' holds a filename starting wIth CHR$ 0. Hook code +34 opens a RS232 "b" channel by calling the OP-RS-CH subroutine. The channel base address is returned into the DE register pair.

APPENDIX 8 How to tell which edition Interface 1 you have There are two different versions of the ZX Interface, each having a slightly different program in its ROM. It is important that you know which version of the ROM you have, and you can find this out in the fo5lowing manner: Run the following line of BASIC: CLOSE # 0: PRINT PEEK 23729 If this prints out a '0', then you have an edition 1 Interface 1. if it prints out '80', you have an edition 2 Interface 1. There is the possibility that Sinclair Research may release a third edition of the Shadow ROM, with a few more changes. If you should get one of these, the extended BASIC commands may not work properly with it. The 'PRINT PEEK 23729' test probably wont distinguish between the edition 2 and edition 3 Shadow ROMs, You can, however, upload the Shadow ROM into a higher area of RAM, disassemble it there, and compare the code to the listings given in this book. The following program will load the Shadow ROM into RAM from 32768 to 48968: ORG LD LD RST DEFB POP POP LD LD LD LDIR RST RET 34000 HL,LABEL (23789),HL 08 32H HL HL HL,0 DE,32768 BC,8192 0

LABEL

To do the same thing from BASIC, use the following program: 10 20 30 40 50 60 70 80 CLEAR 26000 FOR X=54000 TO 54022 READ Y POKE X,Y NEXT X RAND USR 54000 DATA 33,248,210,34,237,92,207,50,225,225 DATA 33,0,0,17,0,128,1,0,32,237,176,199,201

For information on how to make the extended BASIC commands work with edition 3 or later Shadow ROMs, refer to Appendix 11, page 165.

APPENDIX 9 Basic loader program for edition 2 Shadow ROM (For owners of Interface 1s equipped with the edition 2 Shadow ROM.) Here is the new BASIC loader program, changed to run with the new shadow ROM. To enter the new BASIC commands into your Spectrum without using an assembler, enter the following BASIC loader program. 1 10 20 30 40 50 60 70 80 PRINT 90 100 110 120 CLEAR 63743 FOR A=63744 TO 65951 STEP 12 PRINT "ADDRESS:";A' LET C=0 FOR B=1 TO 12 LET Z=A+B-1: IF Z<=65051 THEN INPUT X: PRINT X: POKE Z,X: LET C=C+X NEXT B PRINT '"CHECKSUM=";C INPUT "THIS IS WRONG ? (Y/N) "; LINE Y$: IF CODE Y$=80 OR CODE Y$=121 THEN "Retype from address ";A: PAUSE 100: CLS : GO TO 20 IF CODE Y$<>78 AND CODE Y$<>110 THEN GO TO 80 CLS: NEXT A PRINT "Saving the program" SAVE *"M";l;"SHADP" CODE 63744,1308

When you have finished typing this program into your Spectrum, you should 'RUN' it. You should then type from the listing on the next page, the 12 bytes from the address shown on the screen. When you have typed the first 12 bytes, a 'checksum' should be displayed on the screen; if it matches the one printed on the listing, at the right hand side of the line, then you have made no mistakes in typing the numbers, and you may enter 'N' or 'n' to continue with the next line. If the checksums do not match, you must enter 'Y' or 'y', and then retype the whole line. When all numbers have been entered, the program will automatically be saved on Microdrive cartridge (there must be a cartridge with at least 2K free in Microdrive 1). If you wish to save the program on tape, line 120 of the listing should be modified appropriately. When at some 1ater time you wish to use the routines, you have simply to place the cartridge in Microdrive 1, and then to enter the following direct commands: CLEAR 63743: LOAD *"M";1;"SHADP" CODE: RANDOMIZE USR 63744 and the new comnands should be available. Note that if you use the NEW command, you should then give the direct command 'RANDOMISE USR 63744' to reinitialise the VECTOR system variable.

Here is the data for the new Shadow ROM: 63744: 63756: 63768: 63780: 63792: 63804: 63816: 63828: 63840: 63852: 63864: 63876: 63888: 63900: 63912: 63924: 63936: 63948: 63960: 63972: 63984: 63996: 64008: 64020: 64032: 64044: 64056: 64068: 64080: 64092: 64104: 64116: 64128: 64140: 64152: 64164: 64176: 64188: 64200: 64212: 64224: 64236: 64248: 64260: 64272: 64284: 64296: 64308: 64320: 64332: 64344: 64356: 64368: 64380: 64392: 64404: 64416: 64428: 64440: 64452: 64464: 207 254 202 252 254 153 195 32 213 5 102 215 205 229 225 24 205 0 12 67 61 54 195 121 5 6 197 229 167 34 0 215 205 205 58 205 10 22 128 229 44 123 221 29 0 0 251 203 251 245 96 32 94 3 0 82 113 113 35 215 30 49 244 11 254 42 30 193 0 215 237 250 130 214 6 126 2 248 215 126 73 92 0 180 28 215 0 215 245 237 237 205 1 169 38 9 227 251 205 250 175 0 203 225 229 0 0 205 87 126 62 205 251 35 205 205 48 29 29 16 121 254 33 202 250 224 194 197 5 215 153 176 246 28 20 6 230 62 20 130 202 92 229 255 18 254 148 197 153 197 66 92 30 22 19 20 251 19 183 33 221 205 25 63 205 195 0 0 178 194 183 228 113 126 86 143 143 250 35 225 249 28 16 9 45 254 202 75 215 215 140 30 195 32 205 125 197 127 32 24 28 240 237 33 215 215 44 30 215 30 229 48 207 6 205 33 32 33 24 32 251 126 50 205 215 159 242 0 0 251 62 202 205 29 230 235 251 251 25 229 195 201 205 210 249 249 227 175 249 153 130 28 80 193 254 183 205 126 254 205 202 205 1 123 127 169 32 194 245 148 197 213 240 50 205 165 9 239 12 215 203 205 41 21 178 40 17 21 0 0 62 251 99 113 241 192 17 17 17 246 35 112 215 183 99 34 254 202 252 215 30 28 205 89 5 108 5 214 205 32 248 254 183 120 61 16 15 0 240 215 30 205 205 61 253 183 16 251 33 251 58 221 0 221 205 251 45 195 0 0 33 32 62 251 29 200 192 16 100 1 48 35 251 32 5 6 183 42 188 195 121 197 254 183 223 215 194 215 20 237 56 20 101 5 230 92 229 33 254 1 148 167 0 181 225 54 5 221 17 9 182 13 126 251 119 0 205 215 193 0 0 140 205 245 254 58 62 43 39 0 0 195 205 6 8 215 215 92 202 251 240 28 225 44 5 193 32 197 153 62 20 6 35 194 215 192 33 237 56 42 215 30 40 7 3 32 0 205 126 24 251 230 251 41 221 13 251 0 227 5 0 0 92 113 195 3 29 36 62 205 205 62 113 112 10 254 148 1 201 114 254 1 205 193 194 215 120 0 249 30 32 35 254 16 240 153 194 7 115 15 194 121 167 44 225 209 235 0 109 25 0 203 2 183 221 126 195 221 251 45 62 0 0 54 29 113 202 251 195 202 143 143 255 29 251 126 35 30 22 215 249 229 215 183 113 240 241 177 254 215 96 205 16 128 235 1 30 240 250 61 229 240 28 40 79 209 225 33 239 6 205 205 70 194 40 190 13 128 229 205 205 13 0 0 255 58 29 157 230 113 205 251 251 60 62 62 205 194 245 221 24 254 202 32 5 35 1 43 202 207 32 105 248 249 48 62 215 253 1 229 92 199 1 205 54 6 193 193 193 215 62 50 5 32 169 209 13 183 250 225 67 0 195 0 0 33 9 33 251 31 29 113 17 17 183 175 44 113 240 215 42 0 215 12 0 215 112 215 197 193 202 0 124 20 193 2 13 32 203 237 42 253 225 215 183 79 0 241 241 5 32 2 21 251 232 250 58 40 194 221 17 29 251 113 0 0 13 251 24 61 198 33 29 232 10 237 205 205 29 1 148 81 1296 2384 2282 2042 1786 1653 1714 1504 1890 1721 1879 1489 1570 1519 1488 954 1736 1620 1795 1306 1545 1465 1586 1634 1376 932 1872 2370 1542 1413 1018 1272 1188 1364 1641 1502 1531 1704 1913 1828 1460 1632 1603 716 0 620 1644 1498 2040 1679 1352 1677 1370 1345 1414 1478 1355 1414 1472 1758 1197

64476: 64488: 64500: 64512: 64524: 64536: 64548: 64560: 64572: 64584: 64596: 64608: 64620: 64632: 64644: 64656: 64668: 64680: 64692: 64704: 64716: 64728: 64740: 64752: 64764: 64776: 64788: 64800: 64812: 64824: 64836: 64848: 64860: 64872: 64884: 64896: 64908: 64920: 64932: 64944: 64956: 64968: 64980: 64992: 65004: 65016: 65028: 65040:

92 70 50 221 215 254 221 194 34 67 205 202 221 198 41 22 238 175 40 1 250 195 50 254 5 124 23 11 62 62 253 127 58 111 61 26 254 175 167 33 125 62 192 114 253 48 48 72

221 194 21 119 32 16 126 6 201 78 227 185 102 205 32 221 211 205 11 215 253 193 22 195 215 142 33 213 3 12 203 56 19 253 50 254 60 50 62 42 188 32 17 201 205 5 5 24

126 13 33 11 0 210 4 9 92 40 19 31 70 73 245 229 239 50 254 121 50 5 254 193 148 205 17 33 50 253 1 2 254 175 19 167 184 27 1 19 48 205 226 50 216 253 237 180

4 18 255 221 215 99 254 221 205 11 205 205 221 30 62 225 205 21 47 28 21 215 215 5 30 24 0 5 216 203 198 62 184 50 254 196 32 254 50 254 167 53 253 15 253 54 68 0

254 241 0 119 130 6 77 126 128 221 247 63 117 205 230 17 227 195 40 205 254 121 148 215 254 23 167 0 92 1 245 63 48 24 58 7 0 201 24 58 144 253 42 92 58 0 79 80

77 221 34 12 28 215 194 25 18 126 19 31 11 169 211 55 19 193 33 183 215 28 30 32 1 195 237 25 209 134 62 205 5 254 25 13 58 254 254 21 40 193 81 17 15 10 24 20

194 119 201 205 205 1 45 205 56 41 32 218 221 19 239 0 221 5 254 5 148 205 50 0 40 193 66 17 195 254 1 7 60 201 254 58 22 13 200 254 4 13 92 234 92 239 188 12

45 13 92 50 183 22 6 50 19 221 227 126 116 221 1 25 126 215 63 215 30 183 23 215 12 5 218 53 81 32 50 13 50 62 167 23 254 32 24 71 48 32 6 253 71 58 197 60

6 221 205 21 5 221 221 21 40 119 219 18 12 126 104 205 68 32 40 148 61 5 254 130 62 62 47 253 11 56 24 58 19 1 196 254 167 12 194 14 251 246 5 24 58 19 205 1

221 126 95 195 215 42 203 33 14 13 239 221 221 13 1 179 221 0 54 30 50 215 175 28 3 3 5 115 254 91 254 20 254 50 7 71 196 58 254 0 24 201 9 238 20 254 111 13

203 25 18 193 148 81 24 255 221 24 230 110 203 221 205 21 119 254 195 167 20 148 50 205 253 215 205 35 165 32 241 254 201 24 13 58 7 24 6 12 243 254 115 17 254 144 253 10

24 205 175 5 30 92 70 0 203 3 1 69 24 190 82 62 13 33 240 202 254 30 27 183 203 39 23 114 211 4 254 71 205 254 58 27 13 254 32 44 197 23 35 53 184 200 193 0

1467 1466 1179 1372 1406 1259 1445 1145 1231 964 1870 1479 1539 1670 1453 1261 1907 1378 1271 1520 1606 1543 1298 1655 1226 1230 1041 874 1548 1134 1786 938 1357 1459 1162 1154 1255 1354 1268 822 1479 1567 1073 1308 1679 1284 1608 472

APPENDIX 10 Extended BASIC commands from assembler for edition 2 Shadow ROM (For owners of Interface 1s equipped with the edition 2 Shadow ROM.) To add the extended BASIC commands to your Spectrum, if you own an edition 2 Shadow ROM and an assembler, first enter the assembly program on pages 12 to 32 into your Spectrum. Do not enter the table of EQU statements that follows on page 33. These labels all refer to the edition 1 Shadow ROM, and are not relevant to edition 2. Instead, for each label given on page 33, enter the value given in the alphabetical list of Shadow ROM 2 labels which follow. When you have entered this program and assembled it, save it onto Microdrive or tape. To use the new BASIC commands, load the machine code file you have made into your Spectrum at 63744 (F900 Hex), and give the direct command 'RANDOMISE USR 63744'. The new commands should then be available. Below is an alphabetical list of the labels related to the edition 2 shadow ROM. ACC-CODE ALL-MOTRS B-INPUT BD-DEL-2 BF-FILLED BREAK-PGM CAT CAT-SCRN CHECK-M-2 CHECK-SP CHK-LOOP CHK-NAME CHK-SCT CHK-W-MAP CHKLOOP CHKS1 CHKSYNTAX CL-M-CH CLOSE CLOSE-NET CLS#-SYN CONS-OUT CRT-NEW DEC-SECT DEL-M-BUF DELAY-BC1 DISP-HEX2 E-READ-N END-CODE END-LD-PR END-SA-DR ENDHERE ERASE ERASE-MK ERR-6 EX-D-STR EXPT-NAME EXPT-STR F-HD-3 F-REC2 FOR-B-T FORMAT-1 FREESECT G-REC-ERR GET-M-BUF GET-NBLK 0D8A 1570 0B7C 0D32 1CFF 05E2 1C52 04A6 066D 0077 10B3 1403 1BF6 1E3A 15FD 0E62 0018 176B 1718 1F18 0559 1EE0 09F7 13F7 119F 1653 14ED 100A 07B8 0A13 196A 06CC 1D79 1E03 01F0 059F 062F 05E7 198A 1A5D 04CD 1B75 1D43 12B1 15EB 0EB5 ALL-BITS ALL-STRMS BCHAN-IN BD-DEL-3 BORD-REST CALBAS CAT-LP CAT-SYN CHECK-MAP CHK-AG-2 CHK-LP-2 CHK-NSECT CHK-SCT2 CHKEND CHKS-ALL CHKS2 CKNAM-END CL-N-CH CLOSE-CH CLR#-SYN CMM-LOOP CONV-1 CRT-VARS DEFAULT DEL-O-1 DELAY-SC DISP-NIB EACH-ST END-DATA END-PR-MS END-SET ENDRD ERASE-1 ERASE-MK2 ERR-RS EXISTING EXPT-NUM EXPT-STRM F-M-HEAD FAILED FOR-M FR-S-LPB FRMT-SYN G-TYPE GET-M-HD GET-NBUFF 102F 058E 0B88 0D48 0D4D 0010 1C68 0486 13C4 1608 160F 1C1E 1C05 015D 142E 0E71 1423 175C 1708 057F 0C92 14E7 01F7 0224 1087 101A 14DF 0152 081C 02B4 0AE8 16AD 1D8A 1E06 13A3 07F2 061E 064E 1971 1362 04E7 1D51 04B4 07F6 15E2 0F02 ALL-BYTES ALLCHR-2 BCHAN-OUT BD-DELAY BR-DELAY CALBAS-2 CAT-LP-E CHAN-SPC CHECK-N CHK-AGAIN CHK-M-EOF CHK-PRES CHK-TEMPM CHKEND-L CHKS-BUFF CHKS3 CL-CHAN CL-WORK CLOSE-M CLR-BUFF CODE COPYCHADD CTRL-CD DEFLT-0 DEL-S-1 DISP-CH DL-LOOP EACH-VAR END-EXPT END-RS-IN END1 ENTRY ERASE-2 ERASE-RUN ERR-V EXPT-EXP1 EXPT-PRMS F-ERROR F-N-SCT FETCH-ERR FOR-RUN FR-S-RES G-HD-RC GET-DESC GET-M-HD2 GET-PACK 05A7 1418 0D07 0BD8 0EAC 0081 1C77 10F1 1883 15F6 1233 154E 17D0 0169 142B 0EC4 18A8 01AA 138B 1FD4 0789 00FB 0C70 079A 15A2 14F8 0EAF 025A 0750 0C34 05C1 13C7 1DC3 1AAB 01EC 06B9 0701 1A7B 1CF4 0276 1ABA 1D55 1280 1FE4 13A9 1F25 ALL-CHARS 1407 ALTER-OUT 0CBB BD-DEL-1 0D1C BD-DELAY2 0C1D BRCAST 0EDD CALL-INP 0D5A CAT-RUN 1AB5 CHECK-M 0665 CHECK-R 188F CHK-FULL 1349 CHK-MAP-2 13BF CHK-REST 0FC7 CHK-TEMPN 17DC CHKEVEN 0165 CHKS-HD-R 1426 CHKS4 0F24 CL-CHK-N 18BC CLAIMED 0FD9 CLOSE-M2 138E CLR-ERR 1E77 CONS-IN 1ECD CP-NAME 1300 DATA 07D2 DEL-B-CT 1F75 DELAY-BC 1652 DISP-HEX 14D6 DR-READY 1620 EMIT-CH 0C6C END-INPUT 0D94 END-S-DEL 104C ENDC 1AC9 ENTRY-2 17C0 ERASE-LP 1DA7 ERASE-SYN 0531 EX-CHANS 17C0 EXPT-EXPR 06A3 EXPT-SPEC 05F2 F-HD-2 1982 F-REC1 1A57 FILL-MAP 1163 FORMAT 185D FR-SC-LP 1D4D G-RDES 1E5E GET-M-BLK 15F2 GET-N-BUF 0DDC GET-R-2 1258

GET-R-LP GP-ERROR HOOK-31 IN-AGAIN INC-BLKN INPAK-2 KBD-TEST LD-BLK-4 LD-HD-NET LD-NO-PGM LD-VF-MR LOAD-SYN LP-SCOUT LV-BN LVBN-END MAKE-PERM MCHAN-OUT MK-BLK MOVE-OUT N-ACTIVE NET-STATE NEXT-CHAN NO-AUTOST NO-NAME NOFULL NONSENSE NOT-CR NOT-NET NOT-RECV NREPORT-2 NREPORT-8 NUM-ARR NXT-ENTRY NXTCHAN OP-F-1 OP-F-5 OP-PERM-N OP-STREAM OPEN-N-ST OREP-1-2 OUT-BLK-N OUT-M-BYT OUTPAK PR-REP-LP PRNAME PRT-OUT RCLM-MAP RD-BYT-2 RD-SECTOR RDLOOP4 READ-RS REC-BYTE RES-B-MAP REST-STRM ROMERR RUNTIME S-SC-DEL SA-BLOCK SA-DRI-4 SA-MAP SAVE-RUN SCREEN$ SEND-NEOF

125F 1F3A 1E98 0D78 0E9A 105A 1EF5 0956 08E4 0966 08B3 0898 1015 0A6E 0A93 1AE9 12B3 1C4D 1827 1051 0FBC 10E7 0977 0716 12FC 0584 0C82 0722 1FF1 0663 06A1 0819 0AD4 11E5 1B16 1B5B 0F52 0B51 0F40 073C 0E30 15D0 1082 02A7 1D5B 1EF0 11EF 169D 1F3F 166B 0BAF 0B95 13E3 1444 0028 011B 103A 086E 194F 1A04 1AC4 0771 0FAE

GET-RECD HD-LOOP HOOK-32 IN-CHK INCREC INPAK-L LCHAN LD-BLK-5 LD-HD-RS LD-PR-AR LINE LOOK-MAP LPEND LV-BN-E M-AGAIN MAKESURE MERGE-BLK MOVE MOVE-RUN N-INPUT NEW-BUFF NEXTNUM NO-FOR-M NO-PRT NOINC-C NOPRES NOT-FOR-B NOT-OP-B NOT-TOKEN NREPORT-3 NREPORT-9 NXT-1 NXT-MOTOR OFF-MOTOR OP-F-2 OP-F-X OP-RS-CH OP-STRM OPEN-RS OREPORT-1 OUT-CODE OUT-M-HD OUTPAK-L PRCHAR PRNM-LP PT-N-CHAN RCLM-NUM RD-BYT-3 RDLOOP1 RE-MAP READ-SEQ REC-PROC RES-VARS RETAD-RUN ROTATE RWF-ERR S-STAT SA-BYTE SA-DRI-WR SA-MAP-LP SAVE-SYN SE-NAME SEND-PACK

1252 0854 1E94 1E49 1F08 105B 1482 095D 08EB 09A7 073E 19EA 14A6 0A82 1818 0FCA 096B 17F5 1AB0 0DA9 123D 01A5 04BF 1374 1DF8 155B 04D3 051F 0C44 062D 0683 01A3 1597 1586 1B41 1B26 0B17 1859 051C 04B2 1EE3 15AD 1089 1D71 1D5E 17EB 0182 16A5 165C 1A1E 1EFD 0B98 0255 05DD 13DD 1132 016F 0884 1943 1A11 082F 1CA0 0E4F

GETBF-END HEX-LINE HOOK-CODE IN-M-BLK INKEY$ INS-NAME LD-BLK-2 LD-BLOCK LD-HDR-2 LD-PROG LINE-LEN LP-B-MAP LV-ANY LV-MCH M-INPUT MARK-FREE MERGE-END MOVE-EOF MOVE-SYN NCHAN-IN NEW-NAME NMINT-SRV NO-GOOD NO-READ NONAMES NOREAD NOT-GRAPH NOT-OP-M NOTRIGHT NREPORT-4 NREPORT-L NXT-B-MAP NXT-SCT OP-B-CHAN OP-F-3 OP-M-C OP-RSCHAN OP-TEMP-M OPEN-SYN OREPORT-8 OUT-M-BLK OUTMEM PAR-1 PREP-MARK PROG RCL-T-CH RCLM-OLD RD-NEXT RDLOOP2 RE-MAP-LP READY-R2 REP-MSG REST-MAP RETAD-SYN RS-SH S-BLK-END SA-B-END SA-DRI-2 SA-DRIVE SA-NET SC-L-LOOP SEL-DRIVE SEND-RESP

0F3E 151D 1E71 1633 0D98 1CDA 0936 091D 08F0 09B9 0144 1DF4 0A60 199A 11FD 1C40 09A4 1832 053D 0DAF 18ED 0660 1F45 0D87 1D27 12C1 0C55 0500 145C 064C 0934 1351 1279 1FF6 1B46 0529 0B4E 1B05 04ED 0D82 15B7 0F9E 079F 1C35 0753 17B7 09EC 1F7A 165E 1A2E 1686 0260 1476 05E0 1AC5 0881 0895 1921 18CB 0892 0133 1532 107B

GETNB-END 0EFF HEX-LINE2 1522 I-AGAIN 181E IN-NAME 1C8B INPAK 104F INT-SERV 0038 LD-BLK-3 0945 LD-DATA 080E LD-HEADER 08DC LD-VE-M 1A39 LOAD-RUN 0A99 LP-P-MAP 1DF0 LV-B 0A7D LV-N 0A76 MAIN-ROM 0000 MCHAN-IN 1207 MISSING-D 0494 MOVE-NA 1CDE MRG-SYN 08AC NCHAN-OUT 0E09 NEWVARS 0030 NO-AUTO 0A52 NO-M-ARR 07DA NOEMP 139B NONS-BSC 07F4 NOT-CMM 0CA5 NOT-LEAD 0C4F NOT-PROT 15BF NREPORT-1 0139 NREPORT-5 0681 NREPORT-N 0906 NXT-BYTE 1431 NXT-STRM 144A OP-CHAN 186A OP-F-4 1B49 OP-M-STRM 1ACC OP-RUN 1ABF OP-TEMP-N 0F46 ORD-NAM 1CC1 OT-NAMS 1D22 OUT-M-BUF 15B3 OUTMEM2 119A PAR-2 07B2 PRINT-SPC 0C9A PROG-LINE 0130 RCLM-CH 177D RD-BYT-1 1696 RD-RANDOM 1F0B RDLOOP3 1669 READ-BLK 165A READY-RE 162A REPTEST 154C REST-N-AD 1A82 RMERR-2 0040 RS-SH2 11A3 S-PACK-1 0E48 SA-BLK-LP 0872 SA-DRI-3 1929 SA-HEADER 084F SAVE-M 0849 SCOUT-END 1013 SEND-BLK 135A SEND-SC 101E

SENDSCOUT SERIAL-IN SH-ERR SP-N-END ST-ERROR START-3 STCHK STORE-LEN T-FURTHER T-NA-1 TAB-PROC TCHAN-IN TEST-BRK TEST-LOW TEST-MNAM TEST-RET TEST-STAT TRY-AGAIN TST-MERGE TST-NUM TST-WIDTH UNTIL-MK UPD-STRM VAR~EXIST VR-BN WR-BLK WR-F-TEST WR-S-3 WT-SCOUT

0E77 0BD6 0020 0EA2 0068 00A5 1439 0F30 0BF7 1CAA 0CB5 0B82 163E 013B 0685 05BF 068F 0DE2 0908 1DE5 0CE6 106B 1787 023D 0A8E 16AF 1BAB 1FBB 0FD6

SEPARATOR SET-BAUD SIGN-ERR SPC-COUNT ST-MAP-AD START-4 STO-DISP SW-MOTOR T-INPUT T-NA-2 TAB-SERV TCHAN-OUT TEST-BUFF TEST-N-BF TEST-NEXT TEST-SAVE TEST-TYPE TS-L-NET TST-MR-M TST-PLACE TURN-ON UNTILFIVE USE-C-RC VE-FAIL VR-DATA WR-BYT-1 WR-RECD WR-SECTOR WT-SYNC

05B1 0ACD 15DE 0C8C 1168 00BC 1463 1565 0B76 1CB5 0CC8 0C3A 0DBB 120F 060C 07A7 08F6 08D1 098C 1A9D 153D 1A64 1F57 1A55 0803 16C4 12DA 1F85 0FED

SER-IN2 SET-PROG SKIP-NUM ST-BF-LEN ST-SHADOW START-BIT STORE-COD SYNC-RD T-LD-NET T-OTHER TABLOOP TEST-BAUD TEST-CODE TEST-MAP TEST-OUT TEST-SCT TIME-OUT TS-L-RS TST-MR-N TST-SPACE UNPAGE UPD-NXT-S USE-R VE-M-E WAIT-1 WR-BYT-2 WR-S-1 WRITE-PRC WTKEY

0C1B 0A19 014E 0E25 0008 0BCF 0CB8 167C 09E2 1E31 0CF9 06B0 00E9 1143 0E17 1BDF 0DFC 08D7 0998 09C2 0700 17A2 1899 1A49 0BEE 16C8 1FA1 12EE 1ED2

SER-OUT-L 0D2C SET-T-MCH 10A5 SP-DL-1 0E93 ST-END 05B7 START-2 009A STAR-SA 18D9 STORE-DSP 1AFF T-CH-NAME 1135 T-M-COOE 0915 TAB-MOD 0CEE TABZERO 0CF4 TEST-BLKN 0EE2 TEST-DTR 0D21 TEST-MCHL 11D0 TEST-PMAP 13E8 TEST-SP 003A TON-DELAY 1344 TST-AGAIN 0BC3 TST-N-EOF 0DD5 TST-TYPE 09CB UNT-MARK 108F UPD-POINT 1469 USE-REC 19D0 VERIFSYN 08A2 WAIT-2 0BEF WR-BYT-3 16CD WR-S-2 1FB3 WT-SC-E 0FD3

APPENDIX 11 What to do if you have an unknown ROM If you should find that your Interface 1 contains a ROM which differs from both the edition 1 and the edition 2 Shadow ROM, then the code to implement the extended BASIC commands that is given in this book will not work. This is because the code that we have given contains a number of calls to routines in the Shadow ROM, which perform a number of useful tasks, such as selecting Microdrives, reading a sector of tape, displaying hexadecimal numbers on the screen, etc. Many of these routines are at different locations in the first two editions of the ROM, and presumably they would be different again in any future ROMs. This does not, however mean that it is not possible for you to add the expanded BASIC commands to your machine. The code that we have given is still quite correct, but the calls it makes to the ROM are no longer accurate. The solution, then is to add to the extended BASIC commands routines of your own which have the same names as the ROM routines, and will perform the same functions. This doesn't mean that you'll have to write all these routines - all the code you need is contained in the Shadow ROM disassembly given earlier in this book. Simply find the routine you need in the Shadow ROM disassembly, and copy it into your program. Consider the following example: The extended BASIC command '*L n' includes the following code: LD LD LD CALL LD H,B L,C A,H DISP-HEX A,L ;move it into HL ;display high byte of address in hex ;display low byte

AGAIN

This wont work as it stands, as it includes a call to the Shadow ROM routine DISP-HEX, and the address given for DISP-HEX in the table of EQU statements will not be correct. What you should do to make this work is to remove the entry for DISP-HEX from the EQU table, and then look up the entry for DISP-HEX in the alphabetical list of labels (Appendix 2.). According to this, the DISP-HEX routine is at 1E87H. Look this up in the disassembly, and add the routine you find (shown below) to the end of your program. DISP-HEX PUSH RRA RRA RRA RRA CALL POP AND CP JR ADD ADD CALL RET AF

DISP-NIB

CONV-1

DISP-NIB AF 0FH 0AH C,CONV-1 A,7 A,30H DISP-CH

Note, however, that this routine makes its own call to another Shadow ROM routine, DISP-CH, which you will also have to add to your code. DISP-CH is located at 1EA9 in the disassembly, and is as follows: DISP-CH PUSH PUSH PUSH PUSH EXX HL DE BC AF

PUSH PUSH PUSH PUSH LD PUSH PUSH LD RST DEFW POP RST DEFW POP LD POP POP POP POP EXX POP POP POP POP RET

HL DE BC AF HL,(CURCHL) HL AF A,2 CALBAS 1601H AF CALBAS 10H HL (CURCHL),A AF BC DE HL AF BC DE HL

Doing the same thing with all the other Shadow ROM calls should make it possible for you to run all the extended BASIC commands. Note that the program, as it stands, fits into the very highest part of memory, with no room above it to spare. The code you will be adding will make the extended commands somewhat longer. Because of this, you will have to ORG your program to an earlier location, in order for it to fit into memory. Remember that when you initialise the new commands from BASIC, you should no longer use RANDOMISE USR 63744 but instead use RANDOMISE USR x where x is the location at which your assembler program is ORGed.