Files in BASIC I - Loading and Saving files and high scores

I never owned a Commodore computer and therefore I will be the first to admit that file management on C64 always seemed confusing to me. I am slowly trying to figure it out as my projects require more and more interactions with external files. Even in BASIC programs we need to occasionally load external assets and save game state or high scores.

So my goal is to tackle very basic functionality and as Emulator evolves I will update this tutorial. Of course when the physical system reaches our desks I will review and update this post even more thoroughly.

What File system

First of all Emulator runs on a host system which can be either Windows, Linux or Mac which are all officially supported and several other platforms that were built by members of our community.

It is very important to remember that we can run an emulator with or without a mounted SD card image. It is expected that when the actual hardware ships it will come with an SD card reader which will be used as primary storage. For that reason the emulator comes with SD card image support which provides full functionality.

Current version Emulator ROM R.43 also does not support reading both the host file system and SD Card image at the same time.

Empty SD Card Image can be download from official Commander X16 Repository:

sdcard.img.zip

I am using Windows and I prefer to use VHD format because it allows me to mount and use on Windows 10 directly without any 3rd party tools. Detailed instructions on how to create were written by user TomXP411:

https://www.commanderx16.com/forum/viewtopic.php?p=6929&hilit=create+vhd#p6929

To start Emulator with a mounted SD Drive you need to use -sdcard option, of course depending on whatever you name the SD image card and where on your system you save it.

X16emu -sdcard D:\TestDrive.vhd

Or

X16emu -sdcard sdcard.img

I personally like to adjust the size and look of the screen so I start Emulator in several ways depending on what I am doing:

X16emu -scale 2 -quality nearest -debug
X16emu -scale 2 -quality nearest -echo
X16emu -scale 2 -quality nearest -debug -sdcard D:\TestDrive.vhd
X16emu -scale 2 -quality nearest -echo -sdcard D:\TestDrive.vhd

Typically I use -debug option when working on Assembly programs and occasionally turn on -echo when working on BASIC programs so I can save them in text format.

Loading and Saving programs

Let’s start with the part that should be fairly straightforward. Loading and saving BASIC programs.

LOAD ”PROGRAMNAME”

SAVE “PROGRAMNAME”

Note that Commander X16 will not allow you to overwrite the existing file so to save newer version with the same name you need to use following syntax:

SAVE “@:PROGRAMNAME”

These two commands load and save BASIC programs to the current drive. Note that Emulator assumes current drive to be 8 and therefore above commands are the same as typing

LOAD ”PROGRAMNAME”,8
SAVE “PROGRAMNAME”,8

We will see later why this is important to remember. But what does the 8 mean? It is the result of the Commodore heritage, because Commodore line of home computers used device numbers to determine on which device the command should operate on. It is not clear at this time which of these devices will be available on Commander X16 and if they will stay the same but at the time of writing this post the default disk drive on Commodore 64 would have Device number 8 and that is mapped to either host files system if Emulator is started with SD card or SD Card if sdcard option was used.

I hope that in the future Emulator will be able to use both e.g. 8 for host file system and 9 for SD drive.

Loading and Saving BASIC programs as text

BASIC programs are tokenized immediately after each line of BASIC code is finished by pressing Enter command. This means that in memory BASIC commands only use one byte which is a significant saving in space and also during running the program the commands don’t need to be parsed again.

For example, the command “PRINT” uses 5 characters and would use 5 bytes and a BASIC interpreter would need to read them all and scan all the BASIC commands to determine which one it is and that would slow down BASIC programs to the crawl. Instead the PRINT command is stored as 159 or $99 in hex so the interpreter knows right away that the next characters that follow need to be printed to screen.

That causes a problem that we can’t open BASIC programs with standard text editors on the host system, write and change programs and test them in the Emulator. Ideally we would have an option with the SAVE and LOAD commands to save and load text versions of BASIC programs however that does not exist but there is a workaround.

If we start the Emulator with -echo options everything that is displayed in the Emulator using standard commands like PRINT will be copied to the terminal from which we started the Emulator. So if we write a BASIC program in the Emulator and list it out to the screen using command LIST the complete listing will be printed into the terminal/command window as text. From there we can easily copy it and paste it into our favorite text editor.

After we are done writing or editing it on a host system or even copying the source code from this and other articles and Github repositories we can simply copy the text and paste it directly into Emulator. On Windows we can use Ctrl C and Ctrl V shortcuts.

Loading assets and other data

We already established that BASIC, being interpreted language is not the fastest. So whenever we need to move large amounts of data it will show its weakness. Even reading a few hundred bytes in DATA statements will take a long time and create uncomfortable delays.

On the other hand Commander X16 has very powerful graphics with many colors, layers, sprites, even high resolution bitmap graphics in many color modes and 16K of video memory. Then there are other types of data like short assembly subroutines that can help increase the speed of mostly BASIC programs or add features that are very hard or impossible to do in BASIC like playing music in the background etc.

How do we solve these two seemingly incompatible situations?

One possibility would be to use Assembly language but loading files in Assembly is not trivial and is not something even an intermediate coder can do easily.

The easiest way to do it is to load game assets directly into memory using LOAD and VLOAD commands. The names already hint at what the difference is. Load is used to load files to CPU memory and VLOAD to load files directly in Video Memory or VRAM.

Before we try some examples we have to understand that files that will be loaded might or might not have two extra bytes at the beginning. Those two bytes are reserved for starting addresses of memory where the file will be loaded. Of course we can override them but some commands on the Commander X16 expects these two bytes regardless if they are used or not. This is again legacy of Commodore computers where these two extra bytes were mandatory but on Commander X16 we have an option to load files without those two extra bytes.

Another important note to make is that when the Emulator is started directly from the host file system and receives the Load command from either BASIC or Assembly it directly injects the loaded file into Commander X16 memory which is pretty much instantaneous. That is not the case when using an SD card and in the future on an actual system so loading time still has to be taken into consideration.

Loading to Video Memory (VRAM)

If our files contain two extra bytes at the beginning we can load them directly into VRAM using the following command:

VLOAD “FILENAME”,Drive,Bank,Memory Address

  • Drive has to be 8 for the host file system or SD card depending on how the Emulator was started.
  • Because VRAM is 128K bytes in size we have to choose if our data will go into first 64K (Bank 0) or second 64K (Bank1).
  • Memory Address is the starting address with the chosen bank.

In concrete example to load new font to VRAM starting at address $06000 we would use:

VLOAD”LIGHTFONT.BIN”,8,0,$6000

To load tile sheet to address $12000 following command can be used:

VLOAD”TILESHEET.BIN”,8,1,$2000

If we prefer to use files without those extra bytes we have to use command BVLOAD in a similar way:

BVLOAD”LIGHTFONT.BIN”,8,0,$6000

and

BVLOAD”TILESHEET.BIN”,8,1,$2000


Loading to CPU Memory

LOAD command is similar to VLOAD with differentiation that it operates in CPU RAM.

Typical files that we would load to CPU memory would be music and to a certain extent level data for games however that comes with a potentially slow reading of data from memory into some kind of BASIC data structure like arrays which is not much faster than reading from DATA statements.

Another very important type of files that we would load into CPU memory are machine code routines and libraries like my Simplest Sounds Effect Library and Music Player Library for BASIC programs. Syntax is similar to the VLOAD command:

LOAD”FILENAME”,Drive,Secondary,Memory Address

  • Drive again has to be 8 for default drive either on host system or SD drive
  • Secondary number does not have any effect so I just put 0 or 1
  • Memory address is starting address to where we want to load our file

The above syntax works on both host file systems as well as SD drive.

So to load Simplest Sound Effect Library we can use

LOAD”EFFECTS.PRG”

When we want to override the address we need to use:

LOAD”EFFECTS.PRG”,8,0,$0400

I we decide to use files without leading two bytes we use similar syntax:

BLOAD”EFFECTS.PRG”,8,0,$0400

Reading and Writing to files

It is time to switch gears a little, it is time to start programming and accessing files directly from BASIC. We will not go very deep for now but will create a rudimentary set of functions so our BASIC games will be able to load and save High scores. The same technique can be used to save the status of the game etc.

Checking for Drive availability

Because file access is not guaranteed it is good practice to check first and then enable or disable certain functionality programmatically so the program doesn’t crash. We can check that by opening and closing the drive channel 8:

10 OPEN 1,8,2:CLOSE 1
20 IF ST=0 THEN PRINT "DRIVE IS PRESENT!"
30 IF ST<>0 THEN PRINT "DRIVE IS NOT PRESENT!"

First line returns zero in system variable ST if the Drive opening and Closing was successful or some non zero error value if it was not.

ST stands for STATUS - remember Commander X16 BASIC only distinguishes two characters in variable names so ST and STATUS is exactly the same and is also a reason why we can’t use custom variables START for example.

Reading from file

Reading from a file is very straight forward after we understand all the parameters. We first have to open the file using command OPEN and then read using command INPUT#

OPEN Logical file number, Device, Secondary number, “Filename,Type,Mode”

It looks pretty complicated but it is really not, let’s first check what parameters mean.

  • Logical file number - is a number that will identify the “handle” to this file after it is opened for consequent reads or writes.
  • Device number is now already familiar number 8 for the default drive
  • Secondary number still does not have special meaning for our purposes
  • Filename is in parenthesis and contains more than just a name:
    • File name itself
    • File type. Several different file types are possible but we can just use type P which stands for Program but is commonly used for data files too
    • Mode determines if the file will be opened for Reading or Writing so it can have value R or W

Let’s look at example:

10 OPEN 1,8,2,"HIGHSCORES.TXT,P,R"

Logical file number is 1 wo we will use handle 1 for all consequent operations of the file. We open it from the default drive 8. Number 2 doesn’t have special meaning for our purposes here. File name is HIGHSCORES.TXT and is of type Program and will be opened for Reading.

To read from the file the command to use is INPUT#

20 INPUT#1,A$

The first parameter 1 is of course reference to handle 1 for the file we opened above and the second one is directing it to read a string from the file and store it to variable A$.

We can use system variable ST (STATUS) again to check if reading was successful. If everything was OK then ST will be 0 after the read.

Any other value represents some error. One of the most important ones is 64 which means that the end of the file was reached so we can use it to control the flow of our program if the number of reads can be variable.

At the end it is important to close the file using

30 CLOSE 1

Writing to File

Writing to file is very similar and is also done by opening the file, performing writes and then closing the file like this:

10 OPEN 1,8,2,"@:HIGHSCORES.TXT,P,W"
20 PRINT#1,A$
30 CLOSE 1

As most of the code above is exactly the same as for reading let’s just highlight one obvious change. We see the “@:” in the beginning of the File name. That symbol tells the BASIC interpreter and in turn to the Kernal that we want to rewrite the file if the file with the same name already exists. Without that the opening would fail.

High Score Demo

Let’s put all of the above in a short demo that will allow us to initialize, load and save 10 high scores. I also included a simple function for adding new scores to the sorted array.

And a short video of the demo in action. Again, remember this only works if the Emulator was started with an SD card attached.




Comments

Popular posts from this blog

Commander X16 Premium Keyboard