helpline |
Andrew Hewson examines headerless files and block line deletion
IT SOMETIMES happens that an item in this column rings a bell with many readers and triggers a flood of letters on the subject. My piece in Sinclair User November 1984 had that effect because it included a short section on SAVEing and LOADing headerless files on the Spectrum which interested a number of correspondents. I shall expand on the theme this month.
First, though, I must correct an error that crept in and was brought to my attention by Martin Carte, amongst others. It occurred in a table included in the piece which listed two machine code routines for SAVEing and LOADing the screen in a headerless file. The routines had been written as if the Spectrum display file and attributes file were together 6192 bytes long whereas they are in fact 6912 bytes. As a result the routines omitted to SAVE or LOAD the latter part of the attributes file. Apologies to all those who were led astray. The correct version of the routines is given in Table 1.
Repeating the routines gives the opportunity to answer Warren Milburn who asks: "Please could you tell me how to alter the start address and bytes length in your routines for saving and loading headerless files?"
The start address is passed to the routines in the ix register pair and the length is passed in the de register pair and so to adapt the routines to your own purposes you must alter the values passed. Assuming you know the start address and number of bytes that you wish to SAVE or LOAD and that the routines are stored consecutively in the printer buffer, then the procedure for LOADing is as follows:
POKE 23301, START - 256*INT (START / 256) POKE 23302, INT (START / 256) POKE 23304, LENGTH - 256*INT (LENGTH / 256) POKE 23305, INT (LENGTH / 256)
To modify the SAVE routine POKE the same values into 23311, 23312, 23314 and 23315 respectively.
|
The routine will be of use to Mike Hughes who writes: "I have written a cataloguing program which requires me to SAVE three character arrays but it is a nuisance waiting for each one to SAVE in turn and then pressing a key before the next SAVE. Is there any way of by-passing the 'start tape then press any key' message?"
The best way of tackling the problem would seem at first sight to be to SAVE the contents of the variables area as a CODE file. It is quite easy to calculate the starting address and length parameters required for the SAVE "" CODE command - the starting address would be the value held in the VARS system variable and the length would be the difference between VARS and the E_LINE system variable.
The method would work well for SAVEing all variables but problems would occur on reLOADing because the starting address, length or arrangement of the variables area might have changed in between the two events. The starting address would have changed, for example, if a microdrive had been brought into use so that the microdrive maps occupied more space lower down in memory thereby causing the Basic program, variables and other items to be shuffled further up in RAM.
The length of the variables area would change if a new variable were brought into use or if a pre-existing array were reDIMensioned. The arrangement of the variables area would have changed if new data were written into a pre-existing string because the Spectrum handles this task by creating the string afresh at the top of the variables area before deleting the old version, which is generally lower down in memory.
Thus the reLOADed CODE file would often end up incorrectly placed in the Spectrum memory or in the correct place overwriting the wrong things.
The safest solution, which although not ideal ensures that the data that is reLOADed does not corrupt the program, is to SAVE the variables area using a short machine code routine as a headerless file. On reLOADing g use another machine code routine to execute the following steps: 1 - delete all the current Basic variables using the ROM routine for recovering redundant memory; 2 - create a new variables area large enough to hold the incoming data using the ROM routine for creating space in memory; 3 - reLOAD the variables using the routine for LOADing headerless files.
Two routines to perform the tasks are listed in Tables 2 and 3 and as usual the decimal codes are listed so that readers without an assembler to hand can load the routines into the printer buffer using the decimal loader in Table 4.
|
The first routine is an adaptation of the SAVE routine in Table 1. Notice that the length of the variables area is saved in the printer buffer at address 23296 so that the load routine can reference the value when it is required. The user can also PRINT the value by invoking the routine using the PRINT USR command - because it is left in the be register pair at the completion of the routine - so that the value can be noted for future reference.
The second routine makes two ROM calls. The first recovers the space lying between the addresses pointed to by the dep and hl register pairs thereby deleting all current variables. The second routine creates a space of length be at the address pointed to by hl thereby creating room for the new variables. Those two ROM routines are very useful because they look after all the relevant system variable pointers no matter whereabouts in memory that the space is to be deleted or created.
|
The delete routine can be used to deal with a problem raised by Jeff Sims. He writes: "I sometimes wish to delete large chunks of an existing program in order to create a new version which shares some of the original subroutines. Is there a way of doing so which is more convenient than deleting each line?"
I have described a technique previously in this column for deleting large chunks of a Basic program by manipulating the hidden pointer which the machine places after each line number to tell it the length of the line. It is comparatively straightforward to POKE a new value into the pointer in the first line of the chunk to be deleted so that the machine thinks it is dealing with one monster line. The monster line can then be deleted in the conventional way by entering the line number.
The routine listed in Table 5 achieves the same end in a rather more elegant fashion. The user POKES the first and last line numbers of the section he wishes to delete into the first four bytes of the printer buffer as follows:
POKE 23296, LINE1 - 256*INT (LINE1 / 256) POKE 23297, INT (LINE1 / 256) POKE 23298, LINE2 - 256*INT (LINE2 / 256) POKE 23299, INT (LINE2 / 256)
The routine checks each number in turn to ensure that it is non-zero and then calls the ROM routine at 6510 which returns, in the hl register, the address of the first of the two lines in RAM. It calls the same routine a second time to obtain the address of the byte following the end of the second line. The difference between the two addresses is checked to make sure that it is positive and if so the ROM routine at 6629 is called to recover the space thereby deleting the lines.
Finally I have been taken to task by Alex King who writes: Why do you persist in using decimal in your machine code listings when almost all other sources use hexadecimal?It is true that hexadecimal is the most common means of identifying numbers in assembly language programs but I feel that the majority of readers are not familiar with hex. Those who prefer hex are probably adept at conversions whereas the converse is not true those who dislike hex probably find conversion confusing. Decimal is a compromise.
|