Many games have custom loaders which don't require headers for the code blocks, while still using some of the ROM tape loading routines. I have noticed that these loaders often bypass the LOAD A DATA BLOCK ROM routine at 0x0802 which the Controller class monitors (see Flash loader - Part 1), so for headerless loaders I choose to monitor 0x059F instead, which is a bit into the routine, just before the data block is actually loaded. When there is no header, the target address for the data is fetched from the IX register instead. Here is the Controller class code for handling headerless blocks: Most of the time, the regular loading process takes over when flash loading fails, but sometimes a file won't load correctly when flash loading is enabled. To make it easy to control this I added checkboxes for enabling flash load as well as speed load in the tape player window.
0 Comments
Having implemented flash load for code blocks (see Flash loader - part 1) I went ahead and looked into how to flash load a basic program. This is a bit more complex, since there are a number of system variables that need to be set correctly for the program to work. Aside from that, the process is the same as for code blocks, i.e. the Controller checks if the LD_BLOCK ROM routine is reached while a tape is playing. If the currently loading block is a program header, it will be flash loaded by a method in the Tape Flash Loader class, which looks like this: The method loads the data block to RAM, starting at the address found in the system variable PROG. It returns the end position of the program in RAM, which is used by the calling method to update the IX register. I'm not sure if all system variables are handled correctly but this seems to work. The best source of information about the system variables and how a program is mapped in memory that I could find was the Spectrum 128 Rom bank 0 disassembly by Matthew Wilson and others (full credits in the file). Here is a list of the system variables that I have identified (partly through trial-and-error) as important: Normally, a Basic program is loaded by the "Load a data block" ROM routine (at 0x0802). This routine loads the header and then makes room for the Basic program before loading it into RAM.
The header gives the ROM routine information about the following:
The ROM routine sets the E_LINE variable to the first address after the end of the program space. The two bytes stored at this address needs to be preserved, so before E_LINE is updated, the bytes are copied from the previous address. Following the two bytes are a 0x0D byte and a 0x80 byte. Normally, the WORKSP, STKBOT and STKTOP variables seem to be set to the value of E_LINE + 2, so this is what I implemented (I tested this by breaking into the emulation at the end of the loading routine and checking the variables for a number of tape files). The VARS variable is set based on the header parameter and the PROG variable is preserved as it was before loading, as is the K_CUR variable. After the VARS area, a 0x80 byte is inserted. The ROM routine triggers autorun from the line number in the NEWPPC variable if it has a value < 32768. The autorun routine also needs a parameter for which statement number within the line it should start from. This is stored in NSPPC which must be set to 0. Update 2019-08-10: A few updates to the code and text descriptions were made. Later, this method was replaced by a more robust method which is described in this post. |
Archives
November 2020
Categories
All
|