Post by henrik51 on Nov 26, 2005 23:46:40 GMT -5
Ok, I dug through the guts of DSC, the resulting half-assed comments are on my website at www.phin.com/qlink/dsc.txt
Basically, DSC is loaded at $9FF8, then copies itself up to $D000 (under I/O), then executes THAT copy to move itself back down where it came from. Basically, the $D000 copy is always resident and is copied down to $A000 each time a load occurs. When executed, it reads a three letter filename from $0335. This is the fork list.
The fork list is a PRG file with a three letter name. Let us take ASK as an example. This is the file loaded when you LOAD"*",8,1. The file is NOT a valid program file. The first two bytes are not a load address. Basically, the fork list is just a list of byte pairs representing the track and sector each fork starts at, terminted by $00/$00. Any bytes after this are ignored. In the case of ASK, the bytes are $10/$01/$10/$00/$00/$00, indicating that the loader should load a fork from Track 16, Sector 1, then a new fork from Track 16, Sector 0, and then it should end loading and try and run the resulting program.
Each fork is given a four digit name in the directory on the main disk, but this name is never used. RabbitJacks Casino does NOT create names for it's forks, only for it's forklists. SuperQ seems to do both. A fork may be an initial fork, or a subsequent fork. An initial fork may ONLY appear as the first item of a forklist, and a subsequent fork may only appear as a second or higher fork in a forklist. An initial fork has a 11 byte header that is MOSTLY discarded, and seems to be really irrelavent to extracting data. After the possible 11 byte header, come the hunks.
All hunks are created equal, and a fork may contain any number of hunks. Valid hunk types are $01, $03, $05, $80.
Hunk $80 is easiest. It means all done with this fork.
Hunk $01 is followed by it's load address as a two byte quantity, then it's load size as a two byte quantity, then the data. This is just a simple 'stored' hunk.
Hunk $03 is RLE compression. Again, it is followed by it's load address, then it's load size. Then there is a one byte 'escape' code. After that comes RLE compressed data. Decompressing is easy. Read a byte. If it is not the escape byte, write it to memory. If it is the escape byte, read another byte. If it is also the escape byte, write the escape byte to memory. If it is NOT the escape byte, then it is your data value. Read one more byte, and that is the count. Write the data value to memory count times, then carry on with the next byte of the file. The load size is the UNCOMPRESSED size, so it is easy to know when you are done.
Hunk $05 is huffman compression. Eek. It starts with it's two byte load size (yes, different order). Then a 20 byte 'bucket table'. Now, add up entries in the bucket table, and that is the size of the dictionary that follows. Then the load address, then the compressed data.
To decompress, read in one bit. See if there are enough entries in bucket 1 to hold the value you got (there never are, bucket size is always zero). If no, read in one more bit, and check bucket 2. So on and so forth. The trick is that each bucket has an offset.
Bucket 1 offset = Items in bucket 1
Bucket 2 offset = Items in bucket 2 + (2 * bucket 1 offset)
Bucket 3 offset = Items in bucket 3 + (2 * bucket 2 offset)
So on and so forth. This lends itself to a very simple table lookup on a PC with more roomy memory.
Subtract the 'offset' from the code you have read from the file so far, and if it's less than the bucket size, you can go ahead and look it up in the dictionary, and get the decompressed code.
I can send code that is more clear on this point, but that is the basic idea. The 6502 implementation is near the end of dsc.txt.
The last hunk (not the last fork, but the last HUNK of the last fork) loaded must be a BASIC program that is ready to take control of the computer. Even People Connection has a stub of basic that sits there and kicks off the ML side. This BASIC program need not reside at $0801. Most of them reside at $1301.
I have successfully extracted the contents of MMM, and have all the hunks as normal c64 load images. I need to enhance the program I am using to save off the login info from 18/15, and I ought to be able to load all the files in and start things off with a simple RUN command.
Note. One hunk can overwrite any portion of another hunk. This is used in two ways. One is that the state machine text is loaded in two hunks. The first is the state machine text itself. The second hunk overwrites some of the state machine parser by loading at F46C and gives it pointers to the state machine text.
My favorite 'overwrite' trick is the 'loading screen'. Each load screen is three hunks. The first at $0400 for screen ram, the next at $D800 for color ram, and the last at $D000, where it overwrites the VIC registers needed to turn the screen back on (loader turns it off). Take a loook at "0007". It's an initial fork, so don't forget to skip the extra 11 byte header.
Basically, DSC is loaded at $9FF8, then copies itself up to $D000 (under I/O), then executes THAT copy to move itself back down where it came from. Basically, the $D000 copy is always resident and is copied down to $A000 each time a load occurs. When executed, it reads a three letter filename from $0335. This is the fork list.
The fork list is a PRG file with a three letter name. Let us take ASK as an example. This is the file loaded when you LOAD"*",8,1. The file is NOT a valid program file. The first two bytes are not a load address. Basically, the fork list is just a list of byte pairs representing the track and sector each fork starts at, terminted by $00/$00. Any bytes after this are ignored. In the case of ASK, the bytes are $10/$01/$10/$00/$00/$00, indicating that the loader should load a fork from Track 16, Sector 1, then a new fork from Track 16, Sector 0, and then it should end loading and try and run the resulting program.
Each fork is given a four digit name in the directory on the main disk, but this name is never used. RabbitJacks Casino does NOT create names for it's forks, only for it's forklists. SuperQ seems to do both. A fork may be an initial fork, or a subsequent fork. An initial fork may ONLY appear as the first item of a forklist, and a subsequent fork may only appear as a second or higher fork in a forklist. An initial fork has a 11 byte header that is MOSTLY discarded, and seems to be really irrelavent to extracting data. After the possible 11 byte header, come the hunks.
All hunks are created equal, and a fork may contain any number of hunks. Valid hunk types are $01, $03, $05, $80.
Hunk $80 is easiest. It means all done with this fork.
Hunk $01 is followed by it's load address as a two byte quantity, then it's load size as a two byte quantity, then the data. This is just a simple 'stored' hunk.
Hunk $03 is RLE compression. Again, it is followed by it's load address, then it's load size. Then there is a one byte 'escape' code. After that comes RLE compressed data. Decompressing is easy. Read a byte. If it is not the escape byte, write it to memory. If it is the escape byte, read another byte. If it is also the escape byte, write the escape byte to memory. If it is NOT the escape byte, then it is your data value. Read one more byte, and that is the count. Write the data value to memory count times, then carry on with the next byte of the file. The load size is the UNCOMPRESSED size, so it is easy to know when you are done.
Hunk $05 is huffman compression. Eek. It starts with it's two byte load size (yes, different order). Then a 20 byte 'bucket table'. Now, add up entries in the bucket table, and that is the size of the dictionary that follows. Then the load address, then the compressed data.
To decompress, read in one bit. See if there are enough entries in bucket 1 to hold the value you got (there never are, bucket size is always zero). If no, read in one more bit, and check bucket 2. So on and so forth. The trick is that each bucket has an offset.
Bucket 1 offset = Items in bucket 1
Bucket 2 offset = Items in bucket 2 + (2 * bucket 1 offset)
Bucket 3 offset = Items in bucket 3 + (2 * bucket 2 offset)
So on and so forth. This lends itself to a very simple table lookup on a PC with more roomy memory.
Subtract the 'offset' from the code you have read from the file so far, and if it's less than the bucket size, you can go ahead and look it up in the dictionary, and get the decompressed code.
I can send code that is more clear on this point, but that is the basic idea. The 6502 implementation is near the end of dsc.txt.
The last hunk (not the last fork, but the last HUNK of the last fork) loaded must be a BASIC program that is ready to take control of the computer. Even People Connection has a stub of basic that sits there and kicks off the ML side. This BASIC program need not reside at $0801. Most of them reside at $1301.
I have successfully extracted the contents of MMM, and have all the hunks as normal c64 load images. I need to enhance the program I am using to save off the login info from 18/15, and I ought to be able to load all the files in and start things off with a simple RUN command.
Note. One hunk can overwrite any portion of another hunk. This is used in two ways. One is that the state machine text is loaded in two hunks. The first is the state machine text itself. The second hunk overwrites some of the state machine parser by loading at F46C and gives it pointers to the state machine text.
My favorite 'overwrite' trick is the 'loading screen'. Each load screen is three hunks. The first at $0400 for screen ram, the next at $D800 for color ram, and the last at $D000, where it overwrites the VIC registers needed to turn the screen back on (loader turns it off). Take a loook at "0007". It's an initial fork, so don't forget to skip the extra 11 byte header.