More Save Protection

By Matthew Ford

Originally published in EUG #21

In EUG #20, Ross Little presented a new version of his Save Protection program, that intercepted most filing system operations and checked, using an OSFIND call, to see if a file existed before overwriting it. I wrote a similar program several months ago when Gus suggested the idea but didn't get around to sending it until now. The program works in a similar manner to Ross' version, except that it uses a slightly different method to test for the existence of a file. It calls the OSFILE routine with the accumulator set to 5 - the file type is returned in the accumulator (see page 97 of the Advanced User Guide or page 84 of the Acorn Plus 3 User Guide). An advantage of this approach is that the program can tell whether the file in question is locked or if it is a directory. In these cases, the ADFS will not allow another file to be saved with the same name - it returns a "Locked" or "Already exists" error message. Hence it would only waste time in these cases for a Save Protection program to ask whether or not to overwrite the file.

Another feature is my program intercepts the OSFIND vector, as well as the OSFILE vector. This means it can protect against OPENOUT commands overwriting existing files, as well as SAVE and *SAVE. I thought this would make it impossible to unwittingly overwrite a file but I was wrong - it seems the ADFS commands *COPY and *RENAME do not ue OSFILE or OSFIND so they will still overwrite existing files.

After I read Ross' program, I copied his idea to include a facility disabling the program and incorporated this into mine by intercepting the OSCLI vector and testing all * commands. When the command *DISABLE is entered, the vectors are all reset to their original values, so files can be overwritten as usual. It's advisable to do this before examining any machine code program unless you are sure it won't corrupt the Save Protection one.

Remember also that page &B will be corrupted by any *KEY commands and that page &C will be corrupted by any VDU 23 character definition commands. This area of memory is also likely to be used by any ADT commands used. and page 9 is used by the E00 ADFS. To avoid all these pitfalls, is it possibly better to reduce the value of HIMEM to (say) &5C00 and store the code between HIMEM and screen memory. This will keep the code safe until the Mode is next changed - this is not often if you are typing listings in, etc. To store the code here, you must remember to change HIMEM before assembling the source code AND before running the code each time you use it.

Even better would be to store the code permanently in battery-backed RAM as a service ROM. This would be set up automatically every time you switch on the machine and would be almost immune to corruption by other programs. It would need to utilise the extended vector system (see Advanced User Guide, pgs 171 and 189). I have to admit that despite writing the program, I have no intention of using it on a regular basis. I have already got into the habit of locking all programs that are of any value when I am not working on them and, in any case, if I was in a sufficiently absent-minded mood to overwrite a file by mistake, I would probably have forgotton to load the protection program as well! However, if the program could be made to work permanently in Sideways RAM, it would be far more useful. I will investigate this further...

Note that when the program asks if a file should be overwritten, you must answer with the word "YES" (either case) in fall - other keys will give the error message "Aborted". This is the standard procedure used by ADFS when asking for confirmation for the *DESTROY command. Remember the "Aborted" error may cause a BASIC program to stop if it has inadequate error trapping. However, it is always unwise to use the protection program from inside another program in case of corrupting the code, so this shouldn't be a major problem.

As with Ross' program, I have included a check to make sure the setup routine is not executed twice, though you will still run into trouble if you run a second copy which was assembled at a different address. If the code becomes corrupted for some reason, press CTRL-BREAK, save your work (if still intact) then re-install the protection program. Ross mentioned in EUG #20 that this feature did not work properly in his DFS version of the program. The problem was that the contents of the OSFILE vector were copied into the "return" vector (used to carry out the OSFILE call at the end of the routine) BEFORE the check was made to find out if the program has already been installed. This means that after the program is run the second time, both the real vector and the return vector point to the start of the Protection program. This explains why it enters an endless loop when run. The solution of course is to swap around the first few lines of the code so the check is made before any changes are made:

               160.init  LDAfilev+1
               170       CMP#newv DIV 256
               180       BEQend  
               190       STAretv+1
               200       LDAfilev
               210       STAretv
However, this is not the end of the story, because if the SAFE program program has been run a second time using *SAFE (as opposed to CALL&900), the return vectors will have been overwritten with zeros, and the original contents of the vectors lost. They cannot be restored using the default vector table in the OS ROM, since this will point to the tape filing system. The solution here is to put the return vectors at the end of the program, and not to include them in the saved file. This means that they will not be overwritten when the SAFE program is re-loaded. This is how I avoided the problem in my program.

My program is designed to work on both ADFS and DFS, since it only uses 'legal' operating system calls, which are common to both. However, I do not have a DFS ROM to test this, so I would be interested to hear if it is not compatible, or if there are any bugs.

Matthew Ford, EUG #21