Load data or code from disk
Reading code from disk can be useful when you want to develop a project of a certain size. Often you find yourself not having all the memory space necessary for the project so it is necessary to break it up. At the right time, a routine can be called to read the code from disk and load it into memory. Subsequently the new code will be called and the program execution can continue. It is necessary to design the program structure and the memory areas involved well, preserving any portions of code or data that must always be present during execution.
It all revolves around the Kernal JLOAD
call that does the actual loading. Before calling it, however, you need to make some preliminary calls and set several parameters.
The first call to make is to the JSETNAM
routine that sets the name of the file on disk to load.
The routine expects parameters in the three registers .A, .X, .Y.
In .A you must enter the length of the file name to load. In .X and .Y you must enter the memory address that contains the name of the file to load (in .X goes the low-byte while in .Y goes the hi-byte).
lda #$ff
ldx #$be
ldy #$ef
jsr $FFBD
Next, you need to call the JSETBNK
routine that sets the memory bank used to provide the parameters and to receive the read code.
In .A, you need to insert the number of the bank for the parameters, while in .X, you need to insert the number of the bank where the data will be written.
lda #00
tax
jsr $FF68
The last preparatory routine to call is JSETLFS
which is used to specify the logical file number, the device number and the secondary address for I/O operations.
In .A you enter the logical file number, in .X you enter the device number and in .Y you enter the secondary address for I/O operations.
lda #00
ldx #08
ldy #01
jsr $FFBA
Finally, it is possible to call the JLOAD
routine that performs the disk reading.
The routine can perform a read or a check and this option must be specified via the .A register
In .A the value 0 must be inserted, any other value would activate the check function.
lda #00
jsr $FFD5
The routine will now read the specified file and write it to memory. But where will it be written? First, the first two bytes of the file will be read, which contain the absolute address to load. After that, the reading will continue with the rest of the file, and all the contents will be positioned starting from the address read before.
Note that JLOAD
returns the result of the reading in the Carry bit: if it is set, it means that an error occurred during the reading or that RUN/STOP
was pressed to stop the operation. In this case, the .A register contains the error code.
Below is the entire routine and a macro I use to set parameters more conveniently and to make the code reusable. The self-modifying code technique is widely used, which explains the abundance of labels within the routine and the use of the $be and $ef values (for debugging enthusiasts who use $beef to understand whether the self-modifying code has been activated or not).
.macro LoadFile(Level1FileNameLength, Level1FileName) {
lda #Level1FileNameLength
sta LoadFile.FilenameSize + 1
lda #<Level1FileName
sta LoadFile.FilenameLowByte + 1
lda #>Level1FileName
sta LoadFile.FilenameHighByte + 1
jsr LoadFile
}
LoadFile: {
FilenameSize:
lda #$ff
FilenameLowByte:
ldx #$be
FilenameHighByte:
ldy #$ef
jsr $FFBD
lda #00
tax
jsr $FF68
lda #00
ldx #08
ldy #01
jsr $FFBA
lda #00
jsr $FFD5
}
As a final note, remember to transfer control to the loaded code. A simple JMP
will do.
LoadFile(Level1FileNameLength, Level1FileName)
JMP $4000 // Supposing that $4000 is the absolute address of the code loaded
Do you remember when you loaded a program and the screen started showing colored flashes that ended when the loading was complete?
They are called loading bars and they were used to let the user know that the loading was proceeding without problems.
The JLOAD
routine does not provide the possibility to verify what is happening and returns its results in case of error or when loading is finished. We can still understand what it is doing using a little trick.
JLOAD
can be interrupted by pressing the RUN/STOP button, so in the Kernal it is periodically checked if this button is pressed. The check is done with a JSR $FFE1
.
At the address $FFE1
there is the JSTOP
routine which is a simple JMP ($0328)
. It means that the address (16 bits) present at the location $0328
(Indirect vector for the STOP
routine) is loaded and a JMP
is done on it. The standard routine checks if the STOP
button is pressed. It can be modified to add a loading bar before checking the state of the button.
By adding this code before calling JLOAD
, JSTOP call can be redirected to a custom routine called LoadingBars
:
lda #<LoadingBars
sta $0328
lda #>LoadingBars
sta $0329
LoadingBars routine it’s quite simple, it changes the border color to create a flashing bar effect, then a jmp to original routine is done.
LoadingBars: {
inc $d020
nop
jmp $f66e
}
Coupling
A coupling problem is highlighted that requires knowing in advance the absolute address on which the code will be read and inserted. This address is read by the JLOAD but is not exposed so it is necessary to follow a different approach.
Instead of the JLOAD, it is necessary to read the first two bytes to build the absolute address. Then, one byte at a time must be read until the end of the file, writing the read code in the address read before each time. It is a longer procedure to write and subject to errors but it allows us to obtain the absolute address and make it available externally.