Jump to content
IGNORED

BAS file with machine language


Recommended Posts

There's methods to embed short routines but generally you want it to be something relocatable or to be copied to it's intended run address.

 

For a routine that's just called once you can embed it within a USR function, like:

 

1000 R=USR(ADR("hSJHWRUY*&")

(note, meaningless "assembly" routine there)

A potential problem with ML embedded in strings is if you have values 34 or 155 which are quote and RETURN.

A string within a DATA statement can contain quote marks but not the RETURN character.  The added downside to reading from DATA is you then have 2 copies of the same thing in memory which is wasteful.

 

Another alternate could be to just read the routine from a seperate file but that's only practical for disk based programs.

Link to comment
Share on other sites

I always wondered how this worked on the C64. If you loaded a game there, you could (sometimes) list it as a basic program, though the only statement was a SYS instruction starting the game. Though I wonder how the machine language was actually loaded, how it comes that the basic interpreter does not puke on the machine language file, and how it comes that you can SYS to an absolute address.

Link to comment
Share on other sites

On 7/23/2019 at 6:19 AM, thorfdbg said:

I always wondered how this worked on the C64. If you loaded a game there, you could (sometimes) list it as a basic program, though the only statement was a SYS instruction starting the game. Though I wonder how the machine language was actually loaded, how it comes that the basic interpreter does not puke on the machine language file, and how it comes that you can SYS to an absolute address.

 

I was never much of a "Commie" so I mayy be all washed up on this but... for some reason I'm thinking that on the C64 the "DOS" and "BASIC" were part of the same over-all program.  I think CBM called it the "kernal" (yes with an A L).

Link to comment
Share on other sites

On 7/24/2019 at 10:57 PM, fujidude said:

 

I was never much of a "Commie" so I mayy be all washed up on this but... for some reason I'm thinking that on the C64 the "DOS" and "BASIC" were part of the same over-all program.  I think CBM called it the "kernal" (yes with an A L).

Having spent the past month or so immersing myself into C64 hardware from scratch in order to troubleshoot and repair my "Deadbin" model, I can tell you this: the C64 OS - such as it is - is pretty dreadfully primitive. There are actually 3 system ROMs, the BASIC ROM, the Kernal ROM, and the Character ROM. The Kernal ROM is essentially the "DOS" part of the system. That's a hell of a lot of board space and custom silicon for such primitive functionality in terms of user-friendliness. 

 

IMG_1697.thumb.png.393d1af8026322c20d40a6de361b571e.png

  • Like 1
Link to comment
Share on other sites

The C64 BASIC tokenized format may lend itself to embedding machine language files.  Other BASICs, not so much.  MS PC, for one, will go through the program after loading it and look for the line number separations while it rebuilds the linked list of lines.  When it encounters what would be considered a line number of zero, it declares that to be the end of the program and marks that as the start of the scalar variable heap.  I believe it also sanity-checks the line numbers to make sure they're in strictly ascending order.  If you wanted to embed machine language, you'd likely have to make most of the code friendly to the line number counting on load, and have a bunch of data statements with patch data to make the code runnable as soon as you first run the program.

Edited by ChildOfCv
Link to comment
Share on other sites

Hi!

On 7/23/2019 at 7:19 AM, thorfdbg said:

 

I always wondered how this worked on the C64. If you loaded a game there, you could (sometimes) list it as a basic program, though the only statement was a SYS instruction starting the game. Though I wonder how the machine language was actually loaded, how it comes that the basic interpreter does not puke on the machine language file, and how it comes that you can SYS to an absolute address.

 

This is also possible in Atari BASIC, see my example here: 

 

The reason that most C64 games use a BASIC stub is that the OS is not capable of loading binary files, so this is the only option to load data. Even the disk directory is loaded as a BASIC listing.

 

On the other hand, the Atari OS has support for booting from directly from disk and cassette, so it was not necessary to rely on BASIC loading.

 

Have Fun!

 

Link to comment
Share on other sites

On 7/23/2019 at 1:19 PM, thorfdbg said:

I always wondered how this worked on the C64. If you loaded a game there, you could (sometimes) list it as a basic program, though the only statement was a SYS instruction starting the game. Though I wonder how the machine language was actually loaded, how it comes that the basic interpreter does not puke on the machine language file, and how it comes that you can SYS to an absolute address.

 

My CBM-fu is rusty, but I think it worked like this:

 

A PRG file contains the load address in its first two bytes. DOS knows the length of the file from the file system. So  'LOAD"PROG",8' loads the complete contents of the file to the load adress. The first part of the file contains a small BASIC program which just calls SYS. The rest of the file is the machine language program. The parameter to SYS is the entry point into the machine language program loaded beyond the BASIC stub.

Link to comment
Share on other sites

3 hours ago, sanny said:

 

My CBM-fu is rusty, but I think it worked like this:

 

A PRG file contains the load address in its first two bytes. DOS knows the length of the file from the file system. So  'LOAD"PROG",8' loads the complete contents of the file to the load adress. The first part of the file contains a small BASIC program which just calls SYS. The rest of the file is the machine language program. The parameter to SYS is the entry point into the machine language program loaded beyond the BASIC stub.

Hmm. Two questions: Why is there an absolute address in basic programs? Wouldn't that mean if some part of the lower memory is used for other purposes that loading would fail? Or put differently, why would one ever want to load a basic program to some other place if the low memory start is always at the same place?

 

Second, why doesn't the basic interpreter puke on the "binary junk" behind the main program?

Link to comment
Share on other sites

Old BASIC implementations always loaded programs into the same address, so it was possible to use absolute addresses.

 

Even if you want to make the code relocatable, you'll still have to start with an absolute address that you trust for looking up pointers to whatever.

 

Why the interpreter doesn't puke is because it doesn't try to interpret it.  There is probably an end of program marker in the BASIC portion.  On PC, a line begins with two bytes that will be filled in with the location of the next line, followed by 2 bytes that contain the line number, followed by the tokenized data for that line, ending with a 0 byte and then followed by the next line.  The last line is simply two zeroes.

 

But BASIC doesn't know where those two zeroes are.  It knows the file size.  So it loads file size data bytes.  If you immediately run the program upon loading it, you should be okay.  But if you define variables first, it will probably crash since immediately following those two zeroes it sets the location of the variable space, which will begin overwriting the machine code.  But in neither circumstance does BASIC have to even consider the data the following the end of the BASIC tokenized program data.  So of course it doesn't choke.

Link to comment
Share on other sites

On 7/30/2019 at 10:55 PM, thorfdbg said:

Hmm. Two questions: Why is there an absolute address in basic programs? Wouldn't that mean if some part of the lower memory is used for other purposes that loading would fail? Or put differently, why would one ever want to load a basic program to some other place if the low memory start is always at the same place?

Ok. In CBM world at least (I just have some C64 knowledge, not much of the other machines).

 

On C64, the load address of a BASIC program is always $0801. In this regard it wouldn't make much sense to store the load address also in the PRG file.

 

But, one can also load files at arbitrary locations (as specified in their PRG header). 'LOAD"file",8' will (on the C64) always load the file to $0801. But 'LOAD"file",8,1' will load the file to the address specified in the PRG header. On the C64 the area $C000..$CFFF is free, beyond the BASIC ROM and not used by BASIC. So you typically loaded utilities (like machine language monitors or such) to this address and started them with SYS49152. My fingers still remember to type this address... ?

  • Like 2
Link to comment
Share on other sites

  • 2 years later...
On 7/23/2019 at 4:19 AM, thorfdbg said:

I always wondered how this worked on the C64. If you loaded a game there, you could (sometimes) list it as a basic program, though the only statement was a SYS instruction starting the game. Though I wonder how the machine language was actually loaded, how it comes that the basic interpreter does not puke on the machine language file, and how it comes that you can SYS to an absolute address.

BASIC on the C64 has two ideas of where the end of the program is and where variable storage should start.  When the program is loaded, the end address of the load is saved, marking the beginning of variables--this protects everything that is loaded from being overwritten by any commands executed in direct mode, including the creation of variables.  Within the BASIC program, during LISTing and RUNning, the end of the program is marked by three consecutive zero values.  Normally, a pure BASIC program would have these three zero bytes as the last that are loaded, making the end of the BASIC code and beginning of variable memory space coincide.  However, any additional bytes that come after the three zeros is loaded and protected, but never LISTed.  BASIC doesn't check for any of this--frankly, it doesn't care (unless it's LISTing or RUNning, whereupon it would stop when reaching three zeros), and that's how machine language can be loaded along with a BASIC stub, which is usually a SYS command that runs the machine language code (it's not limited to this, however--you can combine as much BASIC with ML as you want, memory permitting).

 

As for why the SYS is to an absolute address, it's because BASIC is always loaded at the same address in the C64.  Resident ML code that needs to be protected from BASIC can reserve memory at the top of BASIC or reside in the free 4K block at $C000-$CFFF.  This doesn't work on the Atari because Atari BASIC utilizes the top of BASIC RAM for the various graphics modes, but with CBM BASIC V2 this is not an issue because there is no graphics support (and even support that is added through BASIC extensions can potentially use the RAM "under" the ROMs instead).

 

By the way, such a system of loading ML wouldn't work so easily on the Atari because BASIC processes a bunch of vectors upon loading, since BASIC is relocatable on that platform, and there may or may not be a way to get a block of ML in there safely.  I could look into this if anyone is interested, but even if there is a way, it would take more work to implement than on the C64, where all you have to do is append the ML to the end of the BASIC file.

 

On 7/24/2019 at 8:57 PM, fujidude said:

I was never much of a "Commie" so I mayy be all washed up on this but... for some reason I'm thinking that on the C64 the "DOS" and "BASIC" were part of the same over-all program.  I think CBM called it the "kernal" (yes with an A L).

Someone at Commodore misspelled kernel at some point, and it stuck--it's no big deal, and has become a "colorful" part of Commodore history.  Anyway, CBM DOS on Commodore 8-bit computers resides in the 8-16K ROM chips in the drives themselves, rather than in RAM on the computers.  The Kernal (sic), which I suppose could be thought of as the front end for DOS, interfaces with CBM DOS in the most abstract way.  The Kernal "understands" tape drives and has code that operates those, but it doesn't have a clue about what a disk drive is.  All it "knows" is that there are potentially four I/O mass storage devices numbered 8-11 that it communicates with through the IEC serial port, and that it can load, save, and send commands as well as data (even ML code!) to these devices.  This is why I can plug a 1581 3.5" floppy drive (which came along later) into a C64, and it just works, without having to update DOS--the new DOS for this type of drive is in the 1581, not the C64.  The C64 tells the drive what it needs to do at a very high level, and the 1581 takes care of all of the lower-level details on its own.

 

The Atari is different in that the OS "knows" what a floppy drive is, although it only supports low-level DOS functions, while a DOS is loaded into the computer's RAM (below BASIC) to take care of the higher-level functions.  On the C64, in contrast, the Kernal only handles the very highest-level functions, and the ROM code in the drives handles the actual DOS functionality, both low- and high-level.  The only low-level thing the C64 Kernal does with drives is the serial communication, but that's not DOS, that's just the interface.  This, by the way, is the super-slow part of the C64's drive data transfer process, and is what so-called "fastloaders" or "turbo" utilities replace.  This is possible because the CBM DOS on every Commodore floppy drive accepts commands to load custom ML code into the drive's memory and execute it there, enabling much faster data transfer over the IEC bus with no hardware modifications.  Each Commodore floppy drive comes with a minimum of 2K of RAM (compared to 256 bytes in Atari-made drives), which is more than sufficient for this purpose, in addition to buffers.

 

BASIC plays no part in this except for providing general LOAD and SAVE commands that call the abstract Kernal routines.  

 

On 7/27/2019 at 10:34 AM, dmsc said:

This is also possible in Atari BASIC, see my example here: 

I figured it would be, based on some work I've done recently with loading Atari BASIC programs and relocating it with my own ML code.  It just takes more care and work than with the C64, and with less reason to do it.

 

On 7/27/2019 at 10:34 AM, dmsc said:

The reason that most C64 games use a BASIC stub is that the OS is not capable of loading binary files,

You state this as a fact, but it is incorrect, and should seem extremely unlikely based on common sense alone, if you think about it.  The true fact is that the C64 Kernal can load binary files, and furthermore this is all it can do in terms of loading.  It doesn't know about BASIC or BASIC programs, it just loads them as binary files, like everything else.  Additionally, adding a ",1" after the device number (usually ",8" for the first drive) loads binary files to the address they're supposed to be loaded to (which is stored in the file on the disk).  All of this can be overridden by code that calls the Kernal directly, but when BASIC calls the Kernal, you can either load to the normal BASIC address (",0" or nothing) or load to the address specified by the file itself (",1").

 

On 7/27/2019 at 10:34 AM, dmsc said:

so this is the only option to load data. Even the disk directory is loaded as a BASIC listing.

There are a number of different ways to load data from disk, including from any track and sector you want.  The key is to send the right DOS commands to the drives, and they will give you any data you ask for.  While it is true that the ROM-based DOS in the drives do generate directory listings that are binary-compatible with BASIC on the computers, that is merely a hack that makes it convenient for users to retrieve and list a directory in computers that aren't "aware" they're dealing with floppy drives specifically (in a way, this is actually kind of advanced in being a type of hardware abstraction).  Contrary to popular belief, however, this does not necessarily mean that BASIC programs in memory have to be overwritten.  It is very easy to read the listing and display it on the fly instead, and many DOS wedges, including those that come with fastloaders on cartridge, do exactly this (as does the more feature-filled BASIC on the C128).  Meanwhile, typing "DOS" on the Atari and getting a listing from there wipes out any BASIC program in memory.  Yes, I know there are ways around this, but it doesn't come with the computer, either.

 

On 7/27/2019 at 10:34 AM, dmsc said:

On the other hand, the Atari OS has support for booting from directly from disk and cassette, so it was not necessary to rely on BASIC loading.

Autoboot is a convenience, but not exactly a huge one.  Programs that took up entire disks on the C64 typically ran themselves once you entered LOAD"*",8,1 (didn't have to type RUN in many cases, but that depends on the title and its programmer).

 

On 7/30/2019 at 10:28 AM, sanny said:

A PRG file contains the load address in its first two bytes.

True, but that can be overridden, either by BASIC or by your own ML code calling the Kernal with whatever address you want the load to start at.

 

On 7/30/2019 at 10:28 AM, sanny said:

DOS knows the length of the file from the file system.

Sort of.  The first two bytes of each 256-byte sector specify the next track and sector, and the last sector has 0 for the track and the index of the last byte of the file within the sector.  A more sophisticated type of file in CBM DOS called "relative files" (REL) has "side sectors" that allow for fast random access to data records (like for a database), and the size of those are known from the start, but every other type of file is a sequential file that is loaded until there isn't anything more to load.  By the way, I suppose REL files are the rough equivalent of Atari DOS' NOTE-POINT functionality.

 

On 7/30/2019 at 10:28 AM, sanny said:

So  'LOAD"PROG",8' loads the complete contents of the file to the load adress. The first part of the file contains a small BASIC program which just calls SYS. The rest of the file is the machine language program. The parameter to SYS is the entry point into the machine language program loaded beyond the BASIC stub.

LOAD"PROG",8 actually overrides the specified load address of the file with BASIC's address of $0801.  The two should be and are almost certainly a match anyway if there is a BASIC stub, but if they differ, then the program is loaded at $0801 regardless.  On the other hand, LOAD"PROG",8,1 forces the use of the load address specified by the file.  Concerning ML programs that are supposed to be run with a BASIC RUN command, this usually has exactly the same effect, although sometimes people goof.  A couple of examples are the Ultima III and Ultima IV loaders.  Try loading those with ,8,1 and they'll be all messed up because their specified addresses are wrong (off by a single byte, if I remember correctly).  Looks like "Chuckles" (Chuck Bueche at Origin Systems), who ported these games to the C64, made a slight mistake when creating these files.  As a result, you must omit the ,1 in order to RUN these games, so that BASIC will override the incorrect load address.

 

Fortunately, this mistake is exceedingly rare, as these are the only examples I know of.  To launch major programs on the C64, you simply enter LOAD"*",8,1.  If it autoruns, like most major programs/games do, then you're all set, and if it doesn't, then simply enter RUN.  It's not quite as convenient as autobooting, but it's not a big deal in the vast majority of cases.

 

By the way, ML programs do not need to have a BASIC stub at all.  They can be loaded at any address specified by the file, and then run from BASIC with a SYS command in direct mode.  Some (especially major programs, but some utilities as well) autorun when loaded, as mentioned earlier.  And when making one's own calls to the Kernal to load programs (which, by the way, is way simpler on the C64 than calling DOS on the Atari), you can load at any address you want (or tell the Kernal to use the address specified by the file itself).

 

On 7/30/2019 at 1:55 PM, thorfdbg said:

Hmm. Two questions: Why is there an absolute address in basic programs? Wouldn't that mean if some part of the lower memory is used for other purposes that loading would fail? Or put differently, why would one ever want to load a basic program to some other place if the low memory start is always at the same place?

Resident ML programs have other places to hide on the C64.

 

On 7/30/2019 at 1:55 PM, thorfdbg said:

Second, why doesn't the basic interpreter puke on the "binary junk" behind the main program?

It's because aside from setting the beginning of the variable area to the end address of the Kernal load, BASIC doesn't do anything with the program.  It's expected to be at a predetermined address (no relocation necessary), and anything BASIC ever does with it is constrained by the three zeros that mark the end of BASIC code and the beginning of the variable memory (equal to the end of the load).  Anything in between, including all the ML you can fit in RAM in the BASIC area, is conveniently ignored by BASIC.

 

On 7/30/2019 at 3:06 PM, ChildOfCv said:

Old BASIC implementations always loaded programs into the same address, so it was possible to use absolute addresses.

 

Even if you want to make the code relocatable, you'll still have to start with an absolute address that you trust for looking up pointers to whatever.

 

Why the interpreter doesn't puke is because it doesn't try to interpret it.  There is probably an end of program marker in the BASIC portion.  On PC, a line begins with two bytes that will be filled in with the location of the next line, followed by 2 bytes that contain the line number, followed by the tokenized data for that line, ending with a 0 byte and then followed by the next line.  The last line is simply two zeroes.

 

But BASIC doesn't know where those two zeroes are.  It knows the file size.  So it loads file size data bytes.  If you immediately run the program upon loading it, you should be okay.  But if you define variables first, it will probably crash since immediately following those two zeroes it sets the location of the variable space, which will begin overwriting the machine code.  But in neither circumstance does BASIC have to even consider the data the following the end of the BASIC tokenized program data.  So of course it doesn't choke.

Pretty close.  On the C64, you can define variables after loading because the start of the variable memory has been set to the end address of the load.  You can also do string operations since the scratch area for that is at the top of BASIC memory.  What you can't do is modify the BASIC stub, especially add to it, of course, which would overwrite the ML code.

 

On 8/1/2019 at 2:26 PM, sanny said:

On C64, the load address of a BASIC program is always $0801. In this regard it wouldn't make much sense to store the load address also in the PRG file.

It does make sense because it keeps everything consistent and therefore simpler, and all program files have a specified load address whether it will be overridden or not.

 

On 8/1/2019 at 2:26 PM, sanny said:

But, one can also load files at arbitrary locations (as specified in their PRG header). 'LOAD"file",8' will (on the C64) always load the file to $0801. But 'LOAD"file",8,1' will load the file to the address specified in the PRG header.

It's just the first two bytes in the file, but yes.  All PRG files that can be loaded by the Kernal have this, while SEQ files used for data can contain anything you want.  Nothing's stopping anyone from using PRG files for data, though, if you want to leave the loading to the Kernal instead of writing your own routine.

 

On 8/1/2019 at 2:26 PM, sanny said:

On the C64 the area $C000..$CFFF is free, beyond the BASIC ROM and not used by BASIC. So you typically loaded utilities (like machine language monitors or such) to this address and started them with SYS49152. My fingers still remember to type this address... ?

That's right, and you can move down the top of BASIC memory to reserve space there, as well.  To take another example, back in the day I made a software fastloader that attempted to be as broadly compatible as possible for a program that is not on a cartridge.  It hid in the RAM "under" the I/O and character ROM at $D000-DFFF.  While the use of this RAM was not uncommon for programs/games that needed all the RAM they could get, typically it's the last block of RAM that is used in the C64.  The Kernal LOAD vector was redirected to a small bit of stub ML code hiding near the bottom of the system stack at $0100 (above whatever BASIC uses).  What it did was save a 5-page area of RAM to the block at $D000 and copy the fastloader to this RAM so that it could access the memory-mapped I/O, and then run the fastloader, which would divert any bytes that would have overwritten it to the saved data at $D000.  Then the rest of the stub would restore the bytes from the $D000 block, and return to the calling program.  It actually worked pretty well, being compatible with a lot more software than any other purely software-based fastloader, but of course it was no replacement for a cartridge-based fastloader, which had more options to make it broadly compatible.  This was just for fun, so no big deal, but my point is that there are various options besides $C000.

  • Like 2
Link to comment
Share on other sites

On 7/26/2019 at 5:18 PM, DrVenkman said:

Having spent the past month or so immersing myself into C64 hardware from scratch in order to troubleshoot and repair my "Deadbin" model, I can tell you this: the C64 OS - such as it is - is pretty dreadfully primitive. There are actually 3 system ROMs, the BASIC ROM, the Kernal ROM, and the Character ROM. The Kernal ROM is essentially the "DOS" part of the system. That's a hell of a lot of board space and custom silicon for such primitive functionality in terms of user-friendliness.

What's so unfriendly about it?  As a programmer, I think the Kernal (sic) is very friendly to use, and the BASIC is also easy to "wedge" into in order to extend its functionality.  I don't program much in BASIC (mostly assembly and some compiled high-level languages), so maybe the lack of graphics commands in BASIC is one of the things you're talking about?  There is always Simons' BASIC, which came out in 1983, and some other alternatives/extensions.  And the PETSCII character set with its graphics characters is really useful and quite powerful from the programmer's perspective.  It can be replaced with any set of glyphs you want, like on the Atari, but it's a nice default set that is readily available even to beginners.

 

As a user, I don't see what difference anything you're saying makes, aside from the C64 Kernal not having a disk autoboot function.  That sort of thing could have been added with very little code required, so it's not a matter of the amount of silicon used, just the history of Commodore computers not being "aware" of what a disk drive is.  What else is lacking from the user perspective?

 

As for the Kernal as DOS, it is nothing of the sort.  All it does is implement the highest-level functions for mass storage devices on the IEC serial port, such as load, save, get byte, put byte, open, close, command, etc.  On the Atari, the OS implements the lowest-level DOS functions, such as writing sectors, while a "DOS" is loaded in low RAM to provide higher-level DOS functions such as managing space and files.  In an 8-bit Commodore system, this is all handled by CBM DOS in ROM in the drives themselves.  The Kernal only knows there are files, not how to manage them, which is done by the drives.  The PET didn't originally have disk drives, so this hardware abstraction was done and DOS functionality was the responsibility of the drives.  The Kernal does contain low-level code for communication, of course, and data structures for the notion of files, but that is all--no actual DOS processing happens in the computer, and the devices don't even have to be disk drives, they can be any mass storage device that responds to the CBM DOS commands.

 

I've become fairly familiar with the Atari OS from making an Atari 800 "emulator" for the C64 (just for fun--I'll share it soon for a chuckle, as sort of an "answer" to Atari 64, which is a C64 "emulator" for the Atari XL/XE), and yeah, it's way more complex and does everything in the most convoluted way possible, but I don't see the point, including how this is better than doing things in a simple way.  I learned how to program the C64 as a child and wrote a fastloader as a teen, and found this whole process way easier and more straightforward than learning how to program the Atari as an adult a few years later.  I love the Atari's hardware, which is powerful yet easy enough to quickly grasp, but even doing something simple with the system is more involved and takes more knowledge than the same things on the C64.  Detailed information (as in gory details) about the C64 was and even still is much easier to track down, as well.

Edited by Robert Cook
  • Like 1
Link to comment
Share on other sites

On 2/22/2022 at 4:11 PM, Robert Cook said:

BASIC on the C64 has two ideas of where the end of the program is and where variable storage should start.  When the program is loaded, the end address of the load is saved, marking the beginning of variables--this protects everything that is loaded from being overwritten by any commands executed in direct mode, including the creation of variables.  Within the BASIC program, during LISTing and RUNning, the end of the program is marked by three consecutive zero values.  Normally, a pure BASIC program would have these three zero bytes as the last that are loaded, making the end of the BASIC code and beginning of variable memory space coincide.  However, any additional bytes that come after the three zeros is loaded and protected, but never LISTed.  BASIC doesn't check for any of this--frankly, it doesn't care (unless it's LISTing or RUNning, whereupon it would stop when reaching three zeros), and that's how machine language can be loaded along with a BASIC stub, which is usually a SYS command that runs the machine language code (it's not limited to this, however--you can combine as much BASIC with ML as you want, memory permitting).

 

As for why the SYS is to an absolute address, it's because BASIC is always loaded at the same address in the C64.  Resident ML code that needs to be protected from BASIC can reserve memory at the top of BASIC or reside in the free 4K block at $C000-$CFFF.  This doesn't work on the Atari because Atari BASIC utilizes the top of BASIC RAM for the various graphics modes, but with CBM BASIC V2 this is not an issue because there is no graphics support (and even support that is added through BASIC extensions can potentially use the RAM "under" the ROMs instead).

 

By the way, such a system of loading ML wouldn't work so easily on the Atari because BASIC processes a bunch of vectors upon loading, since BASIC is relocatable on that platform, and there may or may not be a way to get a block of ML in there safely.  I could look into this if anyone is interested, but even if there is a way, it would take more work to implement than on the C64, where all you have to do is append the ML to the end of the BASIC file.

 

Someone at Commodore misspelled kernel at some point, and it stuck--it's no big deal, and has become a "colorful" part of Commodore history.  Anyway, CBM DOS on Commodore 8-bit computers resides in the 8-16K ROM chips in the drives themselves, rather than in RAM on the computers.  The Kernal (sic), which I suppose could be thought of as the front end for DOS, interfaces with CBM DOS in the most abstract way.  The Kernal "understands" tape drives and has code that operates those, but it doesn't have a clue about what a disk drive is.  All it "knows" is that there are potentially four I/O mass storage devices numbered 8-11 that it communicates with through the IEC serial port, and that it can load, save, and send commands as well as data (even ML code!) to these devices.  This is why I can plug a 1581 3.5" floppy drive (which came along later) into a C64, and it just works, without having to update DOS--the new DOS for this type of drive is in the 1581, not the C64.  The C64 tells the drive what it needs to do at a very high level, and the 1581 takes care of all of the lower-level details on its own.

 

The Atari is different in that the OS "knows" what a floppy drive is, although it only supports low-level DOS functions, while a DOS is loaded into the computer's RAM (below BASIC) to take care of the higher-level functions.  On the C64, in contrast, the Kernal only handles the very highest-level functions, and the ROM code in the drives handles the actual DOS functionality, both low- and high-level.  The only low-level thing the C64 Kernal does with drives is the serial communication, but that's not DOS, that's just the interface.  This, by the way, is the super-slow part of the C64's drive data transfer process, and is what so-called "fastloaders" or "turbo" utilities replace.  This is possible because the CBM DOS on every Commodore floppy drive accepts commands to load custom ML code into the drive's memory and execute it there, enabling much faster data transfer over the IEC bus with no hardware modifications.  Each Commodore floppy drive comes with a minimum of 2K of RAM (compared to 256 bytes in Atari-made drives), which is more than sufficient for this purpose, in addition to buffers.

 

BASIC plays no part in this except for providing general LOAD and SAVE commands that call the abstract Kernal routines.  

 

I figured it would be, based on some work I've done recently with loading Atari BASIC programs and relocating it with my own ML code.  It just takes more care and work than with the C64, and with less reason to do it.

 

You state this as a fact, but it is incorrect, and should seem extremely unlikely based on common sense alone, if you think about it.  The true fact is that the C64 Kernal can load binary files, and furthermore this is all it can do in terms of loading.  It doesn't know about BASIC or BASIC programs, it just loads them as binary files, like everything else.  Additionally, adding a ",1" after the device number (usually ",8" for the first drive) loads binary files to the address they're supposed to be loaded to (which is stored in the file on the disk).  All of this can be overridden by code that calls the Kernal directly, but when BASIC calls the Kernal, you can either load to the normal BASIC address (",0" or nothing) or load to the address specified by the file itself (",1").

 

There are a number of different ways to load data from disk, including from any track and sector you want.  The key is to send the right DOS commands to the drives, and they will give you any data you ask for.  While it is true that the ROM-based DOS in the drives do generate directory listings that are binary-compatible with BASIC on the computers, that is merely a hack that makes it convenient for users to retrieve and list a directory in computers that aren't "aware" they're dealing with floppy drives specifically (in a way, this is actually kind of advanced in being a type of hardware abstraction).  Contrary to popular belief, however, this does not necessarily mean that BASIC programs in memory have to be overwritten.  It is very easy to read the listing and display it on the fly instead, and many DOS wedges, including those that come with fastloaders on cartridge, do exactly this (as does the more feature-filled BASIC on the C128).  Meanwhile, typing "DOS" on the Atari and getting a listing from there wipes out any BASIC program in memory.  Yes, I know there are ways around this, but it doesn't come with the computer, either.

 

Autoboot is a convenience, but not exactly a huge one.  Programs that took up entire disks on the C64 typically ran themselves once you entered LOAD"*",8,1 (didn't have to type RUN in many cases, but that depends on the title and its programmer).

 

True, but that can be overridden, either by BASIC or by your own ML code calling the Kernal with whatever address you want the load to start at.

 

Sort of.  The first two bytes of each 256-byte sector specify the next track and sector, and the last sector has 0 for the track and the index of the last byte of the file within the sector.  A more sophisticated type of file in CBM DOS called "relative files" (REL) has "side sectors" that allow for fast random access to data records (like for a database), and the size of those are known from the start, but every other type of file is a sequential file that is loaded until there isn't anything more to load.  By the way, I suppose REL files are the rough equivalent of Atari DOS' NOTE-POINT functionality.

 

LOAD"PROG",8 actually overrides the specified load address of the file with BASIC's address of $0801.  The two should be and are almost certainly a match anyway if there is a BASIC stub, but if they differ, then the program is loaded at $0801 regardless.  On the other hand, LOAD"PROG",8,1 forces the use of the load address specified by the file.  Concerning ML programs that are supposed to be run with a BASIC RUN command, this usually has exactly the same effect, although sometimes people goof.  A couple of examples are the Ultima III and Ultima IV loaders.  Try loading those with ,8,1 and they'll be all messed up because their specified addresses are wrong (off by a single byte, if I remember correctly).  Looks like "Chuckles" (Chuck Bueche at Origin Systems), who ported these games to the C64, made a slight mistake when creating these files.  As a result, you must omit the ,1 in order to RUN these games, so that BASIC will override the incorrect load address.

 

Fortunately, this mistake is exceedingly rare, as these are the only examples I know of.  To launch major programs on the C64, you simply enter LOAD"*",8,1.  If it autoruns, like most major programs/games do, then you're all set, and if it doesn't, then simply enter RUN.  It's not quite as convenient as autobooting, but it's not a big deal in the vast majority of cases.

 

By the way, ML programs do not need to have a BASIC stub at all.  They can be loaded at any address specified by the file, and then run from BASIC with a SYS command in direct mode.  Some (especially major programs, but some utilities as well) autorun when loaded, as mentioned earlier.  And when making one's own calls to the Kernal to load programs (which, by the way, is way simpler on the C64 than calling DOS on the Atari), you can load at any address you want (or tell the Kernal to use the address specified by the file itself).

 

Resident ML programs have other places to hide on the C64.

 

It's because aside from setting the beginning of the variable area to the end address of the Kernal load, BASIC doesn't do anything with the program.  It's expected to be at a predetermined address (no relocation necessary), and anything BASIC ever does with it is constrained by the three zeros that mark the end of BASIC code and the beginning of the variable memory (equal to the end of the load).  Anything in between, including all the ML you can fit in RAM in the BASIC area, is conveniently ignored by BASIC.

 

Pretty close.  On the C64, you can define variables after loading because the start of the variable memory has been set to the end address of the load.  You can also do string operations since the scratch area for that is at the top of BASIC memory.  What you can't do is modify the BASIC stub, especially add to it, of course, which would overwrite the ML code.

 

It does make sense because it keeps everything consistent and therefore simpler, and all program files have a specified load address whether it will be overridden or not.

 

It's just the first two bytes in the file, but yes.  All PRG files that can be loaded by the Kernal have this, while SEQ files used for data can contain anything you want.  Nothing's stopping anyone from using PRG files for data, though, if you want to leave the loading to the Kernal instead of writing your own routine.

 

That's right, and you can move down the top of BASIC memory to reserve space there, as well.  To take another example, back in the day I made a software fastloader that attempted to be as broadly compatible as possible for a program that is not on a cartridge.  It hid in the RAM "under" the I/O and character ROM at $D000-DFFF.  While the use of this RAM was not uncommon for programs/games that needed all the RAM they could get, typically it's the last block of RAM that is used in the C64.  The Kernal LOAD vector was redirected to a small bit of stub ML code hiding near the bottom of the system stack at $0100 (above whatever BASIC uses).  What it did was save a 5-page area of RAM to the block at $D000 and copy the fastloader to this RAM so that it could access the memory-mapped I/O, and then run the fastloader, which would divert any bytes that would have overwritten it to the saved data at $D000.  Then the rest of the stub would restore the bytes from the $D000 block, and return to the calling program.  It actually worked pretty well, being compatible with a lot more software than any other purely software-based fastloader, but of course it was no replacement for a cartridge-based fastloader, which had more options to make it broadly compatible.  This was just for fun, so no big deal, but my point is that there are various options besides $C000.

 

On 2/22/2022 at 4:44 PM, Robert Cook said:

What's so unfriendly about it?  As a programmer, I think the Kernal (sic) is very friendly to use, and the BASIC is also easy to "wedge" into in order to extend its functionality.  I don't program much in BASIC (mostly assembly and some compiled high-level languages), so maybe the lack of graphics commands in BASIC is one of the things you're talking about?  There is always Simons' BASIC, which came out in 1983, and some other alternatives/extensions.  And the PETSCII character set with its graphics characters is really useful and quite powerful from the programmer's perspective.  It can be replaced with any set of glyphs you want, like on the Atari, but it's a nice default set that is readily available even to beginners.

 

As a user, I don't see what difference anything you're saying makes, aside from the C64 Kernal not having a disk autoboot function.  That sort of thing could have been added with very little code required, so it's not a matter of the amount of silicon used, just the history of Commodore computers not being "aware" of what a disk drive is.  What else is lacking from the user perspective?

 

As for the Kernal as DOS, it is nothing of the sort.  All it does is implement the highest-level functions for mass storage devices on the IEC serial port, such as load, save, get byte, put byte, open, close, command, etc.  On the Atari, the OS implements the lowest-level DOS functions, such as writing sectors, while a "DOS" is loaded in low RAM to provide higher-level DOS functions such as managing space and files.  In an 8-bit Commodore system, this is all handled by CBM DOS in ROM in the drives themselves.  The Kernal only knows there are files, not how to manage them, which is done by the drives.  The PET didn't originally have disk drives, so this hardware abstraction was done and DOS functionality was the responsibility of the drives.  The Kernal does contain low-level code for communication, of course, and data structures for the notion of files, but that is all--no actual DOS processing happens in the computer, and the devices don't even have to be disk drives, they can be any mass storage device that responds to the CBM DOS commands.

 

I've become fairly familiar with the Atari OS from making an Atari 800 "emulator" for the C64 (just for fun--I'll share it soon for a chuckle, as sort of an "answer" to Atari 64, which is a C64 "emulator" for the Atari XL/XE), and yeah, it's way more complex and does everything in the most convoluted way possible, but I don't see the point, including how this is better than doing things in a simple way.  I learned how to program the C64 as a child and wrote a fastloader as a teen, and found this whole process way easier and more straightforward than learning how to program the Atari as an adult a few years later.  I love the Atari's hardware, which is powerful yet easy enough to quickly grasp, but even doing something simple with the system is more involved and takes more knowledge than the same things on the C64.  Detailed information (as in gory details) about the C64 was and even still is much easier to track down, as well.

TL;DR.

  • Thanks 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...