|A L L . C H A N G E !|
|Ever wanted to rename a Microdrive file? Providing the code to create the necessary new command, Andrew Pennell brings Microdrives one step nearer the expensive disk systems they emulate.|
Although Sinclair Research's Interface 1 unit allows good use of the
Microdrives from Basic, there is one command noticeable only by its
absence - that of RENAME. It's provided by most disk operating systems
and associated Basics, but not for the dear old Speccy. Here, therefore,
is the machine code you've been waiting for - although, I'm afraid that
it's for 48K owners only.
Listing 1 shows the Basic loader to create the machine code; enter it with great care - the checksum should spot most typing errors, but some may slip through. After entering and executing, SAVE the program in case it's wrong. To test it, enter RANDOMIZE USR 28000, which enables it. Then, with an unimportant cartridge, try to rename a file; the syntax to use is
where 'TO' is the keyword (and 'r' is also accepted). Naturally, '1' in the example can be replaced with any drive number, up to '8' (should you be lucky enough to have that many!). Having entered the command, the file 'oldname' will be renamed to 'newname', although there are a couple of limitations. Firstly, data files (that is, those created with OPEN/PRINT/CLOSE) will not be renamed, and will produce the error 'Wrong file type'. A second limitation is that the file 'oldname' must be under about 37K in length - otherwise it'll be flagged 'Out of memory'.
The program has been written so that if any error occurs during the process (except perhaps a power cut!) then the file will remain intact, under one of the names. If you try to rename a non-existent file, you'll come across the 'File not found' message, and an attempt to define a new name that's already present on the cartridge will force 'Writing to a read file'.
The source code is shown in Listing 2, and uses the facility of the Interface 1 to add commands to the Basic interpreter. The remainder of this explanation assumes a basic knowledge of machine code with the Microdrives, and of how to add commands; for this I suggest you read chapters eight and nine respectively of Master your ZX Microdrive - and Toni Baker's Extending Basic article (see issue 3).
The code is placed at 28000 decimal, to give some room for small Basic programs, and over 37K to store the file. The routine SETUP is the one entered directly from Basic, and the first creates the Interface system variables, using
10 REM MICRODRIVE RENAME 20 REM (C) A. Pennell 1984 30 REM 100 RESTORE 1000: CLEAR 27999 105 LET s=0 110 FOR i=28000 TO 28254 120 READ a: POKE i,a: LET s=s+a 130 NEXT i 140 IF s<>27057 THEN PRINT "Data error": STOP 150 PRINT "Data OK. Code starts @28000, 255long." 1000 DATA 207,49,33,188,109,34 1010 DATA 183,92,33,116,109,34 1020 DATA 237,92,207,50,1,0,0 1030 DATA 201,33,145,109,58,218 1040 DATA 22,254,255,40,3,33 1050 DATA 165,109,6,5,94,35,86 1060 DATA 35,126,18,35,126,19 1070 DATA 18,35,16,243,201,238 1080 DATA 109,41,27,47,110,172 1090 DATA 21,50,110,169,18,73 1100 DATA 110,238,20,79,110,110 1110 DATA 29,238,109,5,27,47 1120 DATA 110,157,25,50,110,142 1130 DATA 19,73,110,223,24,79 1140 DATA 110,121,29,195,40,0
1150 DATA 198,206,254,42,194 1160 DATA 240,1,215,32,0,246 1170 DATA 32,254,114,32,237,215 1180 DATA 32,0,205,30,6,254,59 1190 DATA 32,227,205,47,6,254 1200 DATA 204,32,220,205,84,110 1210 DATA 205,47,6,205,183,5 1220 DATA 205,84,110,42,220,92 1230 DATA 229,205,5,27,225,34 1240 DATA 220,92,221,203,24,70 1250 DATA 40,2,231,17,219,239 1260 DATA 230,1,32,2,231,14,221 1270 DATA 203,67,86,32,2,231 1280 DATA 22,221,229,225,17,82 1290 DATA 0,25,17,99,110,1,9 1300 DATA 0,237,176,237,83,233 1310 DATA 92,33,108,110,237,91 1320 DATA 100,110,25,48,5,253 1330 DATA 54,0,3,239,205,157 1340 DATA 25,205,142,19,33,99 1350 DATA 110,17,230,92,1,9,0 1360 DATA 237,176,205,84,110 1370 DATA 33,108,110,34,228,92 1380 DATA 205,223,24,205,84,110 1390 DATA 205,121,29,195,193 1400 DATA 5,33,218,92,17,95,110 1410 DATA 6,4,195,167,5
|Listing 1 (above):
The Basic loader to create the RENAME machine code.
Listing 2 (below): The disassembled listing, roughly split into five columns: address, Hex code, line numbers (unique to the disassembler used), labels and mnemonics.
10 ; MICRODRIVE RENAME 20 ; (C) A.PENNELL 1984 30 ; 6D60 1000 ORG 28000 6D60 CF 1010 SETUP RST 8 6D61 31 1020 DEFB #31 ;make vars 6D62 21BC6D 1030 LD HL,NEWSYN 6D65 22B75C 1040 LD (VECTOR),HL ;change VECTOR 6D68 21746D 1050 LD HL,WATROM 6D6B 22ED5C 1060 LD (HD_11),HL 6D6E CF 1070 RST 8 6D6F 32 1080 DEFB #32 ;call WATROM to modify CALLs 6D70 010000 1090 LD BC,0 6D73 C9 1100 RET 1110 ; modify routine for different ROMs 6D74 21916D 1120 WATROM LD HL,OLDROM 6D77 3ADA16 1130 LD A,(#16DA) 6D7A FEFF 1140 CP #FF 6D7C 2803 1150 JR Z,YESOLD 6D7E 21A56D 1160 LD HL,NEWROM ;to suit new ROM 6D81 0605 1170 YESOLD LD B,5 ;no of CALLs to alter 6D83 5E 1180 REDOLP LD E,(HL) 6D84 23 1190 INC HL 6D85 56 1200 LD D,(HL) ;DE=CALL+1 6D86 23 1210 INC HL 6D87 7E 1220 LD A,(HL) 6D88 12 1230 LD (DE),A 6D89 23 1240 INC HL 6D8A 7E 1250 LD A,(HL) 6D8B 13 1260 INC DE 6D8C 12 1270 LD (DE),A ;alter CALL 6D8D 23 1280 INC HL 6D8E 10F3 1290 DJNZ REDOLP 6D90 C9 1300 RET 1309 ; data table for old ROM 6D91 EE6D291B 1310 OLDROM DEFW L1+1,#1B29 ;OPEN_M 6D95 2F6EAC15 1320 DEFW L2+1,#15AC ;LDBYTS 6D99 326EA912 1330 DEFW L3+1,#12A9 ;CLOSEM 6D9D 496EEE14 1340 DEFW L4+1,#14EE ;SVBYTS 6DA1 4F6E6E1D 1350 DEFW L5+1,#1D6E ;ERASEM 1359 ; data table for new ROM 6DA5 EE6D051B 1360 NEWROM DEFW L1+1,#1B05 ;OPEN_M 6DA9 2F6E9D19 1370 DEFW L2+1,#199D ;LDBYTS 6DAD 326E8E13 1380 DEFW L3+1,#138E ;CLOSEM 6DB1 496EDF18 1390 DEFW L4+1,#18DF ;SVBYTS
hook code #31, then alters VECTOR to point to the new
command routine. It then calls WATROM using hook code #32, before
RETurning to Basic. The routine WATROM actually modifies the program to
suit the ROM in the Interface 1. As you may be aware, Interface 1 will
shortly be sporting a new ROM which has most of its routines in
different places. To cater for this, the tables OLDROM and NEWROM allow
the program to modify itself to suit either, using location #16DA to
test the ROM's type. It had to be called indirectly via hook code #32
because the Shadow ROM must be in place for it to work.
Location SYNERR is just a jump to #28 in the Shadow ROM which produces a syntax error when in the line editor. NEWSYN is the new command handler, which first ensures that the command starts with *R or *r. That done, the drive number is scanned, a semi-colon checked for, then the old filename scanned. Next, the character following it (that is, the TO token) is checked, then the routine SWOP called. This is explained later, and only has significance during 'run-time'.
The new name is scanned, and a check made to ensure that there's no more to the statement. Control only passes to line 2200 during 'run-time', which again calls SWOP. This routine swaps the parameters (start address and length) for the old and new names, which are stored in N_STR1, T_STR1 and N_STR3, T_STR3. The Shadow ROM routines act on the parameters in the former two.
The next action taken is for OPEN_M to be called, while preserving T_STR1. As the Shadow ROM is in place, hook codes cannot be used, so routines vary according to ROM type. OPEN_M takes identical action to hook code #22 - that is, create an 'M' channel addressed by IX. The old file is then checked to see if it exists, whether it's a PRINT file, and whether the cartridge is write-protected or not. If it passes all the criterion the nine-byte file header is copied to FREE, and the length of the file is tested to see if there's room for it and if not an error is produced. The shadow routine LDBYTS is called which loads the rest of the file into locations FREE+9 onwards, and then the channel is closed.
Next, the header is copied back from FREE to HD-00 to HD-11, and the bytes SAVEd on to the required cartridge. Routine SVBYTS does a lot of the fiddly bits for you, like opening an 'M' channel, setting the PRINT file flag and testing to see if it's already there. Finally, the old file is erased - by calling ERASEM - and an exit made via #05C1 (which is the same in both ROMs, thankfully).
6DB5 4F6E791D 1400 DEFW L5+1,#1D79 ;ERASEM 6DB9 C32800 2000 SYNERR JP #28 ;syntax error 2005 ; new command routine 6DBC C6CE 2010 NEWSYN ADD A,206 6DBE FE2A 2020 CP "*" 6DC0 C2F001 2030 JP NZ,#01F0 ;check "*" 6DC3 D7 2040 RST #10 6DC4 2000 2050 DEFW #20 ;next-char 6DC6 F620 2060 OR #20 ;make it lower case 6DC8 FE72 2070 CP "r" 6DCA 20ED 2080 JR NZ,SYNERR 6DCC D7 2090 RST #10 6DCD 2000 2100 DEFW #20 6DCF CD1E06 2110 CALL #061E ;do drive no 6DD2 FE3B 2120 CP ";" 6DD4 20E3 2130 JR NZ,SYNERR 6DD6 CD2F06 2140 CALL #062F ;do old name 6DD9 FECC 2150 CP 204 ;"TO" 6DDB 20DC 2160 JR NZ,SYNERR 6DDD CD546E 2170 CALL SWOP 6DE0 CD2F06 2180 CALL #062F ;do new name 6DE3 CDB705 2190 CALL #05B7 ;check end 6DE6 CD546E 2200 CALL SWOP 6DE9 2ADC5C 2210 LD HL,(T_STR1) 6DEC E5 2220 PUSH HL ;save start on stack 6DED CD051B 2250 L1 CALL OPEN_M 6DF0 E1 2255 POP HL 6DF1 22DC5C 2260 LD (T_STR1),HL ;restore start 6DF4 DDCB1846 2265 BIT 0,(IX+24) 6DF8 2802 2270 JR Z,FOUND 6DFA E7 2280 RST #20 6DFB 11 2290 DEFB #11 ;"File not found" 6DFC DBEF 2300 FOUND IN A,(#EF) 6DFE E601 2310 AND 1 6E00 2002 2320 JR NZ,NPROT 6E02 E7 2330 RST #20 6E03 0E 2340 DEFB #0E ;"Write protected" 6E04 DDCB4356 2350 NPROT BIT 2,(IX+67) 6E08 2002 2360 JR NZ,NPRINT 6E0A E7 2370 RST #20 6E0B 16 2380 DEFB #16 ;"Wrong file type" 6E0C DDE5 2390 NPRINT PUSH IX 6E0E E1 2400 POP HL 6E0F 115200 2410 LD DE,82 6E12 19 2420 ADD HL,DE ;=1st byte in header 6E13 11636E 2430 LD DE,FREE 6E16 010900 2440 LD BC,9 6E19 EDB0 2450 LDIR ;copy header to FREE 6E1B ED53E95C 2460 LD (HD_0D),DE ;start=FREE+9 6E1F 216C6E 2461 LD HL,FREE+9 6E22 ED5B646E 2462 LD DE,(FREE+1) ;=file length 6E26 19 2463 ADD HL,DE 6E27 3005 2464 JR NC,L2 ;if it will fit 6E29 FD360003 2465 LD (IY+0),3 ;"Out of memory" 6E2D EF 2466 RST #28 ;do BASIC ROM error 6E2E CD9D19 2470 L2 CALL LDBYTS ;load the file 6E31 CD8E13 2480 L3 CALL CLOSEM ;close the channel 6E34 21636E 2490 LD HL,FREE 6E37 11E65C 2500 LD DE,HD_00 6E3A 010900 2510 LD BC,9 6E3D EDB0 2520 LDIR ;copy header to HD_00 etc 6E3F CD546E 2530 CALL SWOP 6E42 216C6E 2570 LD HL,FREE+9 6E45 22E45C 2580 LD (#5CE4),HL ;new start 6E48 CDDF18 2590 L4 CALL SVBYTS ;save data & close channel 6E4B CD546E 2600 CALL SWOP 6E4E CD791D 2610 L5 CALL ERASEM ;erase old file 6E51 C3C105 2620 JP #05C1 ;then exit 2700 ; 2710 ; Swop contents of N_STR1 & T_STR1 with 3 6E54 21DA5C 2720 SWOP LD HL,N_STR1 6E57 115F6E 2730 LD DE,N_STR3 6E5A 0604 2740 LD B,4 6E5C C3A705 2750 JP #05A7 6E5F 0000 9000 N_STR3 DEFW 0 6E61 0000 9010 T_STR3 DEFW 0 6E63 00 9020 FREE NOP 10000 ; CONSTANTS 5CB7 10010 VECTOR EQU #5CB7 5CDA 10020 N_STR1 EQU #5CDA 5CDC 10030 T_STR1 EQU #5CDC 5CE2 10040 N_STR2 EQU #5CE2 5CE4 10050 T_STR2 EQU #5CE4 5CE6 10060 HD_00 EQU #5CE6 5CE7 10065 HD_0B EQU #5CE7 5CE9 10070 HD_OD EQU #5CE9 5CED 10080 HD_11 EQU #5CED 12000 ; (NEW) SHADOW ROM ROUTINES 1D79 12080 ERASEM EQU #1D79 1B05 12090 OPEN_M EQU #1B05 199D 12100 LDBYTS EQU #199D 18DF 12110 SVBYTS EQU #18DF 138E 12120 CLOSEM EQU #138E 6E64 12130 END