Jump to content
IGNORED

Cassette loader bypasing C: device - looking for sample source


Recommended Posts

Hello,

 

I am looking for a sample source code that demonstrates loading from cassette without using the C: device, just by calling SIO.

I would like to write a binary loader that uses 512 B - 1024 B blocks.

 

I could disassemble some existing code, but perhaps there is something ready that I can just take and customize a bit.

 

Thank you.

Link to comment
Share on other sites

It's been a while, but... going mostly from memory, in a cassette boot situation:

 

Motor control should already be on so no need to worry about it. Due to a bug in the OS, you should turn the motor off via PACTL once the tape boot is complete.

A call to SIOV $E459 should be made for each block to be read.

Prerequisite setup of the DCB:

DDEVIC $300 should be set to $60 for cassette, this only needs to be done once, in the case of a tape boot/continuation code it should already have been set.

DUNIT $301 unit number for cassette is ignored so don't worry about this one.

DCOMND $302 command, we want "R" for Read operation, set to $52

DSTATS $303 this is important. On return this has the status code but before calling SIOV the top 2 bits are used to indicate whether an input or output operation is taking place. So, we need $40 to indicate input, if we were writing a block it'd be $80.

DBUF $304-5 Points to our buffer address.

DTIMLO $306 and DUNUSE $307 Not used for tape.

DBYT $308-9 Byte count - this is the number of bytes for the operation - data payload only, this doesn't include sync leader bytes or checksum, SIO takes care of those things.

DAUX1/2 $30A-B DAUX1 ignored (?) DAUX2 should = $80 to force short inter-record gaps (in a boot situation should already be set as such)

 

For a multistage boot, a few things to note:

You can use a short leader between the EOF block of the booter and the remainder of the program, 4-5 seconds should be plenty.

Probably best to have a delay of about 1 second before attempting to start read operations, allows for pause between recording of the 1st/2nd stage of the boot and any unwanted noise that might be on that section of tape.

Note that if you have intro screen or any possible extra delay beyond a couple of seconds it's probably a good idea to stop the tape. Note that when you restart the tape you should allow about 1.5-2 seconds before calling SIO, allow the tape to get up to speed.

 

Most important - a binary loader by tape can potentially be too slow such that the next block is played before the SIO call has a chance to start reading it. Efficient programming becomes a must. In most cases it'd probably never happen but e.g. a section of binary file with a lot of 1-2 byte segments could potentially cause overrun.

 

And of course, INIT sections where title screens or user intervention is needed to continue loading - you'd need to cater for this by motor off, then extra long leader before the next block.

Edited by Rybags
  • Like 2
Link to comment
Share on other sites

It's been a while, but... going mostly from memory, in a cassette boot situation:

 

Motor control should already be on so no need to worry about it. Due to a bug in the OS, you should turn the motor off via PACTL once the tape boot is complete.

A call to SIOV $E459 should be made for each block to be read.

Prerequisite setup of the DCB:

DDEVIC $300 should be set to $60 for cassette, this only needs to be done once, in the case of a tape boot/continuation code it should already have been set.

DUNIT $301 unit number for cassette is ignored so don't worry about this one.

DCOMND $302 command, we want "R" for Read operation, set to $52

DSTATS $303 this is important. On return this has the status code but before calling SIOV the top 2 bits are used to indicate whether an input or output operation is taking place. So, we need $40 to indicate input, if we were writing a block it'd be $80.

DBUF $304-5 Points to our buffer address.

DTIMLO $306 and DUNUSE $307 Not used for tape.

DBYT $308-9 Byte count - this is the number of bytes for the operation - data payload only, this doesn't include sync leader bytes or checksum, SIO takes care of those things.

DAUX1/2 $30A-B DAUX1 ignored (?) DAUX2 should = $80 to force short inter-record gaps (in a boot situation should already be set as such)

 

Thank you, Rybags.

 

This is a very good start. I cannot, however, presume cassette boot situation. The loader could be loaded from a disk, so I have to set everything from scratch - switch on the motor, baud rate, wait for leader tone.

Another question - How do I set the expected baud rate? - POKEY registers?

Link to comment
Share on other sites

Your best source for information is the Atari OS source itself. You have everything there.

 

If you call SIO, you don't mess directly with Pokey. Unless of course, you want to bypass SIO as well, and not just CIO. Anyway, again, check the OS source.

Link to comment
Share on other sites

Your best source for information is the Atari OS source itself. You have everything there.

 

If you call SIO, you don't mess directly with Pokey. Unless of course, you want to bypass SIO as well, and not just CIO. Anyway, again, check the OS source.

Yes, now I understand. I will have to bypass also SIO, as I intend to use constant (and not measured) baud rate.

Link to comment
Share on other sites

The bitrate for tape read is determined automatically by the SIO code counting how long it takes to read the timing bytes, each record.

This can compensate for tape stretch that's present or not in various parts of the tape... a standard 132 byte block + short IRG occupies about 5 inches of tape.

I once had an AsmEd cartridge copy as 2-stage tape load, it was recorded as booter + a large 8K single block of data recorded at higher bitrate, never had a problem with it.

 

For tape I/O straight after disk I think the only thing you'd need worry about is turning on the motor.

There's not really any logic that takes care of finding leader tone preceding a file, it's up to the user to position the tape properly.

Generally that means about 8 seconds worth of leeway in total.

 

For recording to tape, the bitrate isn't user selectable but I used a workaround method - have a VBlank Immediate routine which you enable just before calling SIOV - the VBI should plug your alternate bitrate values into the AUDF3/4 channels. In doing so you can perform fast tape writes although most drives won't handle much over 800 bps.

  • Like 2
Link to comment
Share on other sites

Yes, now I understand. I will have to bypass also SIO, as I intend to use constant (and not measured) baud rate.

 

Again, I recommend to check the OS source. You can use the information there to code your own loader. Or, as sometimes was done back at the day, copy the OS to RAM under the ROM (assuming you can live without computers with less than 64k), and implement some patches. Some patches are rather trivial.

 

Not that I am lazy to answer questions. Just that I think this is the best way. And you can always come back if you still have questions after checking the source.

Link to comment
Share on other sites

 

Again, I recommend to check the OS source. You can use the information there to code your own loader. Or, as sometimes was done back at the day, copy the OS to RAM under the ROM (assuming you can live without computers with less than 64k), and implement some patches. Some patches are rather trivial.

 

Not that I am lazy to answer questions. Just that I think this is the best way. And you can always come back if you still have questions after checking the source.

 

This is fair enough. The OS listing is a valuable resource. I am not going to patch OS, because the binary loader would fail to load games using RAM under ROM.

 

My goal is to write an alternative to the STDBLOAD 2a binary loader skeleton. The alternative is to be slightly faster and more reliable. Fixed baud rate ranging from 600-820 bd (utility that will generate the .CAS or .WAV files will patch the loader to set the baud rate), 512 B blocks, XOR checksum, program name displayed, adjustable text and background colors, optional silent I/O, stopping the motor when INIT segments are executed.

Edited by baktra
Link to comment
Share on other sites

 

My goal is to write an alternative to the STDBLOAD 2a binary loader skeleton. The alternative is to be slightly faster and more reliable. Fixed baud rate ranging from 600-820 bd (utility that will generate the .CAS or .WAV files will patch the loader to set the baud rate), 512 B blocks, XOR checksum, program name displayed, adjustable text and background colors, optional silent I/O, stopping the motor when INIT segments are executed.

 

Just out of curiosity. Why you want a fixed bit rate? And in which way it will be more reliable?

  • Like 1
Link to comment
Share on other sites

Besides the clock sampling bugs, the other funny thing about the standard OS baud rate routine is that it has a lot of code to do a table-based piecewise linear interpolation to compute the POKEY divisor from 10 bit cell times. All they had to do was measure 19 bits instead and the calculation would have simply been multiply the line pair count by six and subtract seven.

Link to comment
Share on other sites

Besides the clock sampling bugs, the other funny thing about the standard OS baud rate routine is that it has a lot of code to do a table-based piecewise linear interpolation to compute the POKEY divisor from 10 bit cell times. All they had to do was measure 19 bits instead and the calculation would have simply been multiply the line pair count by six and subtract seven.

 

Interesting, and possibly they were not aware about that optimization. But if I had to choose, and being so slow, I still prefer to save one additional byte per block.

Link to comment
Share on other sites

Possibly you could just do the baud rate determination once every 8th frame or so, then just use that value as a forced bitrate for the following ones.

But really, it'd probably be a break-even odds tradeoff where you sacrifice reliability to reduce the random chance of the bug popping up.

  • Like 1
Link to comment
Share on other sites

I am also considering disabling DMA and all unnecessary interrupts when reading a block.

That way, i can try to measure baud rate by counting loop passes when polling the DATA IN pin for just one $55 byte.

 

Anyway, I will have to do some experimentation to determine what works the best.

Edited by baktra
Link to comment
Share on other sites

I think the existing method is probably more than sufficient so long as it's done using bug-free code.

One timing byte or two... really it's less than 1% difference but I guess in terms of a long program load that can be several seconds.

 

Really, without going crazy the biggest savings to be had are by a moderate bitrate increase combined with a larger record size.

  • Like 1
Link to comment
Share on other sites

Larger block sizes can perform a significant saving. But the chances of checksum collision (good checksum with bad data) also increase. And unlike smart devices that compute the checksum, here checksum errors are more frequent because they come directly from the media.

  • Like 1
Link to comment
Share on other sites

Such a system has been implemented by Jakub Husak in his games, he also did it for my Bank! Bang!. The goal was to be able to rewind tape and retry reading a single record.

 

With regards to baud rate - I had quite a success in reading 1200baud tapes, but 900 was the reasonable and standard in my primary school for keeping data :))))))))

Link to comment
Share on other sites

Such a system has been implemented by Jakub Husak in his games, he also did it for my Bank! Bang!. The goal was to be able to rewind tape and retry reading a single record.

 

There are several custom loaders. But I think we implemented tape retry logic earlier than your friend. At least we did it a few years before those games.

 

Link to comment
Share on other sites

  • 2 weeks later...

All things considered, I will create a binary loader that works with 512 B blocks and will call SIOV. Bypassing SIO doesn't appear to be a brilliant idea anymore.
I will try to make the processing of the segment headers fast, so the IRGs won't have to be elongated by much or at all.

Edited by baktra
Link to comment
Share on other sites

It will be hard to load 512KB blocks on a 64k / 6502 machine. ;-)

512byte blocks might be better, even though they are non-standard...

(I still prefer the standard 128byte blocks that can be easily copied to disk/bootdisk.)

 

You are right, but with U1MB, it might be possible :-)

For 128byte blocks, there is the C: device.

Link to comment
Share on other sites

You might get away with using slightly longer gaps when you know the record just written has lots of segments.

But really, it's not common to have more than several in a 512 byte section.

 

Perhaps switching off screen DMA will give me some cycles. As the loader will be embedded in Turgen System, some preprocessing of the binary file can help - e.g. ensuring that no segment except the first one has optional $FF, $FF header. And yes, I will know how many INIT vectors are in a block, so the IRG can be elongated accordingly.

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...