Page 1 of 1

File Loader Tutorial

Posted: Sun Jul 19, 2020 10:38 am
by VincentF



File Loader Tutorial




View File






Introduction

Here is a little demo of how to dynamically load files in RAM bank in assembly.

It's very simple to do but I think that it can be helpful for someone who don't know how to do this right away. In fact I personally would love to see more of this kind of programs in the download section ?

How this loader works ?

First thing to do is to tell the Kernal that we want to use a logical file. For this we need to use the SETLFS subroutine.

From the "C64 Programmer Reference Guide" :


Quote




This routine sets the logical file number, device address, and secondary address (command number) for other KERNAL routines.



The logical file number is used by the system as a key to the file table created by the OPEN file routine. Device addresses can range from 0 to 31. The following codes are used by the Commodore 64 to stand for the CBM devices listed below:




































ADDRESS

DEVICE

0

Keyboard

1

Datassette™

2

RS-232C device

3

CRT display

4

Serial bus printer

8

CBM serial bus disk drive



Device numbers 4 or greater automatically refer to devices on the serial bus.



A command to the device is sent as a secondary address on the serial bus after the device number is sent during the serial attention handshaking sequence. If no secondary address is to be sent, the Y index register should be set to 255.



How to Use :




  1. Load the accumulator with the logical file number.


  2. Load the X index register with the device number.


  3. Load the Y index register with the command.




Since we want to load from the disk (or filesystem) we'll need to use the device number 8. The logical file is not important so we use the file 0.

Also, as we want to relocate the program into a bank at $A000, we'll set the Y register to #0 to activate secondary address mode.

Next step is telling the Kernal which file we want to load. For this we'll use the SETNAME subroutine.

From the "C64 Programmer Reference Guide" :


Quote




This routine is used to set up the file name for the OPEN, SAVE, or LOAD routines. The accumulator must be loaded with the length of the file name. The X and Y registers must be loaded with the address of the file name, in standard 6502 low-byte/high-byte format. The address can be any valid memory address in the system where a string of characters for the file name is stored. If no file name is desired, the accumulator must be set to 0, representing a zero file length. The X and Y registers can be set to any memory address in that case.



How to use :




  1. Load the accumulator with the length of the file name.


  2. Load the X index register with the low order address of the file name.


  3. Load the Y index register with the high order address.




For this we'll need to store our file names somewhere in our program so we write at the bottom of our file the names as a string of petscii characters. We then prepare our registers with the size of the filename to load, and then the address.

Our final step to load a file is obviously the LOAD subroutine.

From the "C64 Programmer Reference Guide" :


Quote




This routine LOADs data bytes from any input device directly into the memory of the Commodore 64. It can also be used for a verify operation, comparing data from a device with the data already in memory, while leaving the data stored in RAM unchanged.



The accumulator (.A) must be set to 0 for a LOAD operation, or 1 for a verify, If the input device is OPENed with a secondary address (SA) of 0 the header information from the device is ignored. In this case, the X and Y registers must contain the starting address for the load. If the device is addressed with a secondary address of 1, then the data is loaded into memory starting at the location specified by the header. This routine returns the address of the highest RAM location loaded.



Before this routine can be called, the KERNAL SETLFS, and SETNAM routines must be called.














NOTE: You can NOT LOAD from the keyboard (0), RS-232 (2), or the screen (3).


How to use :





  1. Call the SETLFS, and SETNAM routines. If a relocated load is desired, use the SETLFS routine to send a secondary address of 0.


  2. Set the A register to 0 for load, 1 for verify.


  3. If a relocated load is desired, the X and Y registers must be set to the start address for the load.




As the Reference guide said, we want to load a file so we set our A register to 0 and since we want to load to banked RAM ($A000) we enter the address in our X and Y registers.

One last thing that we need to do just after each of our LOAD calls, is to verify that our file has been successfully loaded. For this, we'll need to use the READST subroutine.

From the "C64 Programmer Reference Guide" :


Quote




This routine returns the current status of the I/O devices in the accumulator. The routine is usually called after new communication to an I/O device. The routine gives you information about device status, or errors that have occurred during the I/O operation.



The bits returned in the accumulator contain the following information: (see table below)







































































ST Bit Position

ST Numeric Value

Cassette Read

Serial Bus R/W

Tape Verify + Load

0

1

 

time out write

 

1

2

 

time out read

 

2

4

short block

 

short block

3

8

long block

 

long block

4

16

unrecoverable read error

 

any mismatch

5

32

checksum error

 

checksum error

6

64

end of file

EOI line

 

7

-128

end of tape

device not present    

end of tape



How to use :




  1. Call this routine.


  2. Decode the information in the A register as it refers to your pro- gram.




As usual, following the Reference guide, all we need to do is call this subroutine just after our LOAD call, and check the content of the Accumulator. If zero, the load was successful.

And that's all ! You can chain file loading as much as you need, and even you just need to call SETLFS once at the start of the chain.

Note that you'll need to switch the bank between file loads to prevent overwriting previously added program. And since Bank 0 is also reserved you'll need to first switch to bank 1 and start from here.

At the end you can also load a file in place of the loader, just avoid overwriting memory where the code is currently being executed. You can for example leave this kind of code in your first bank and at last run it to load a program from $0800 to $9EFF.

Kernal Subroutines full documentation : https://www.pagetable.com/c64ref/kernal/

Post Scriptum

If you have any suggestions for the code or even want to change things in this description, don't hesitate to tell me !






 

File Loader Tutorial

Posted: Sat Aug 15, 2020 11:46 pm
by CX16UserSteveC

Does this program actually work? When I load and execute loader.prg it immediately returns to the Basic READY prompt. If it worked I was expecting it to execute b1.prg and b2.prg after loading these two into RAM which I believe should have printed "hello, bank 1!" and "hello, bank 2!" to the screen. My reason for investigating this program is that I've read Revision R37 of the Commander X16 emulator doesn't support file IO to disk (except perhaps loading VRAM via the VLOAD function). I tried some file IO to disk with some of my own C applications and couldn't seem to get disk file IO to work, so I'd like to determine if it really is a problem with the Commander X16 emulator or a problem with cc65 or my C applications.


File Loader Tutorial

Posted: Sun Aug 16, 2020 1:28 am
by SlithyMatt

Bytewise file I/O is not working with R37, but you can LOAD and SAVE whole files. The only thing that doesn't appear to work is saving data from banked RAM.


File Loader Tutorial

Posted: Sun Aug 16, 2020 8:04 am
by VincentF


8 hours ago, CX16UserSteveC said:




Does this program actually work? When I load and execute loader.prg it immediately returns to the Basic READY prompt.



Can you tell me what were your process for testing the prg, please ?

Here's mine : unzip the files into your x16emu folder, launch the emulator and load the file directly via the prompt. I just tested it right now and it worked.

I think I already had this same kind of issue because the files b1 and b2 were not found by the emulator, and when the program launched the content of the banks (which were empty) the emulator reset back to an empty basic prompt because of the opcode $00.


File Loader Tutorial

Posted: Sun Aug 16, 2020 3:31 pm
by CX16UserSteveC

I originally put the three files in my program file directory which is one layer deeper than the emulator executable and then executed the following command from the terminal: ../x16emu -echo -prg loader.prg.

Today I tried changing the names of all three files from lower case to upper case and now it seems to be working. I believe the original problem was the b1.prg and b2.prg files were not found by the emulator, but changing their names to B1.PRG and B2.PRG seems to have fixed the problem.

Perhaps you could clarify in your tutorial the names of the b1.prg and b2.prg files need to be changed from lower case to upper case, or re-post the files with upper case names instead of lower case names.

You could also enhance your program which currently has no error checking on the LOAD operation. This would allow you to print a message informing the user of the error returned by READST on the LOAD operation and avoid attempting to execute code that hasn't been loaded.


File Loader Tutorial

Posted: Sun Aug 16, 2020 4:55 pm
by SlithyMatt

My guess is whoever created the demo was running Windows, which is case-insensitive when it comes to filenames. A good note to everyone is to make all X16 filenames all caps with only PETSCII-compatible special characters (which excludes, for instance, underscores). This will ensure compatibility with all platforms, including the actual X16 hardware.


File Loader Tutorial

Posted: Mon Aug 17, 2020 10:06 am
by VincentF


17 hours ago, SlithyMatt said:




My guess is whoever created the demo was running Windows



Yep good guess, it was Windows all this time !

I've renamed the files in uppercase. I assumed that the emulator won't mind the filename's case but it turned out I was wrong. I'll keep this in mind for my futures programs.


18 hours ago, CX16UserSteveC said:




You could also enhance your program which currently has no error checking on the LOAD operation. This would allow you to print a message informing the user of the error returned by READST on the LOAD operation and avoid attempting to execute code that hasn't been loaded.



I also added a small error checking stub at the end of each LOAD operation, and I edited the tutorial to add the READST section. It's just a somewhat lazy addition that just print "LOAD ERROR!" if the accumulator is not zero.

Thank you @CX16UserSteveC and @SlithyMatt for your suggestions !


File Loader Tutorial

Posted: Wed Sep 15, 2021 6:37 am
by kliepatsch

Thank you for the nice tutorial! Very clear and well explained.

I have one question: does the tutorial also work when the load address and the first two bytes of the "B1.PRG" file mismatch? According to the C64 KERNAL documentation, the address in the file should be ignored. I have a case where I want to load a file at a specific address, but the file itself does not "know" where it's gonna be loaded. So I set the first two bytes in that file to zero. Thing is: the file appears to always be loaded at address zero, and the address I put into .X .Y before calling LOAD seems to have no effect whatsoever. I have messed around a while and couldn't get it to work as intended. Could this be a KERNAL bug?

Edit: ah well ... it was THAT bug. Sorry for the spam


 


File Loader Tutorial

Posted: Wed Sep 15, 2021 11:06 am
by VincentF


4 hours ago, kliepatsch said:




Edit: ah well ... it was THAT bug. Sorry for the spam



Don't worry, it's not spam if you're pointing out a bug,  your message might help people find out why that don't work as intended ?