PyDC converter (was: dragon 32 cassette format ?)

Hardware Hacking, Programming and Game Solutions/Cheats
jedie
Posts: 655
Joined: Wed Aug 14, 2013 12:23 pm
Location: germany
Contact:

Re: dragon 32 cassette format ?

Post by jedie »

Implemented cas2wav with: https://github.com/jedie/PyDragon32/com ... 0e8aaad21a

Debug output looks like this:
Verbosity log level: DEBUG
logfile log level: INFO
filename 'HELLOWOR' from: ../test_files/HelloWorld1.bas
Create 16-bit wave file with 22050Hz and 29491 max volumen (90%)
Real frequency: 1225.00
Real frequency: 2450.00
filename block: ['HELLOWOR', '\x00', '\xff', '\x00', '\x00', '\x00', '\x00', '\x00']
yield 255x lead byte 0x55
yield sync byte 0x3c
yield block type 'filename block (0x00)'
yield block length 0xf (15Bytes)
yield 'filename block (0x00)':
-------------------------------------------------------------------------------
['HELLOWOR', '\x00', '\xff', '\x00', '\x00', '\x00', '\x00', '\x00']
-------------------------------------------------------------------------------
yield calculated checksum 0x7a
yield magic byte 0x55
code: [13, 49, 48, 32, 70, 79, 82, 32, 73, 32, 61, 32, 49, 32, 84, 79, 32, 49, 48, 13, 50, 48, 32, 80, 82, 73, 78, 84, 32, 73, 59, 34, 72, 69, 76, 76, 79, 32, 87, 79, 82, 76, 68, 33, 34, 13, 51, 48, 32, 78, 69, 88, 84, 32, 73, 13, 0, 0]
yield 255x lead byte 0x55
yield sync byte 0x3c
yield block type 'data block (0x01)'
yield block length 0x3a (58Bytes)
yield 'data block (0x01)':
-------------------------------------------------------------------------------
['\r', '10 FOR I = 1 TO 10', '\r', '20 PRINT I;"HELLO WORLD!"', '\r', '30 NEXT I', '\r', '\x00', '\x00']
-------------------------------------------------------------------------------
yield calculated checksum 0x91
yield magic byte 0x55
yield 255x lead byte 0x55
yield sync byte 0x3c
yield block type 'end-of-file block (0xff)'
yield block length 0x0 (0Bytes)
yield calculated checksum 0xff
yield magic byte 0x55
The wave doesn't work in xroar yet :(

There must be exist a bug... Or i must use other starting/loading address instead of 0x00 ;)
See: https://github.com/jedie/PyDragon32/com ... 21a#L0R349

btw. current implementation does only create ASCII stream,yet ;)
... too many ideas and too little time ... Related stuff written in Python:
Dragon 32 emulator / PyDC - Python Dragon 32 converter: https://github.com/jedie/DragonPy
DWLOAD server / Dragon-Lib and other stuff: https://github.com/6809
jedie
Posts: 655
Joined: Wed Aug 14, 2013 12:23 pm
Location: germany
Contact:

Re: dragon 32 cassette format ?

Post by jedie »

I have added a analyze function, to display zeror crossing timings in wave files.

e.g.:

Code: Select all

~$ python PyDC_cli.py --analyze FooBar.wav
The Output will look like this:

Code: Select all

Found this zeror crossing timings in the wave file:

  394Hz (   28 Samples) exist:    1 
  613Hz (   18 Samples) exist:    1 
  788Hz (   14 Samples) exist:    1 
  919Hz (   12 Samples) exist:  329 *********
 1002Hz (   11 Samples) exist: 1704 **********************************************
 1103Hz (   10 Samples) exist: 1256 **********************************
 1225Hz (    9 Samples) exist: 1743 ***********************************************
 1378Hz (    8 Samples) exist:    1 
 1575Hz (    7 Samples) exist:  322 *********
 1838Hz (    6 Samples) exist: 1851 **************************************************
 2205Hz (    5 Samples) exist: 1397 **************************************
 2756Hz (    4 Samples) exist:  913 *************************

Notes:
 - Hz values are converted to full sinus cycle duration.
 - Sample cound is from half sinus cycle.
We can see, that the most timings are not at 1200Hz and 2400Hz. See also: http://five.pairlist.net/pipermail/coco ... 70879.html
The current implementation used static values:

Code: Select all

    BIT_NUL_HZ = 1200 # "0" is a single cycle at 1200 Hz
    BIT_ONE_HZ = 2400 # "1" is a single cycle at 2400 Hz
    # see: http://five.pairlist.net/pipermail/coco/2013-August/070879.html
    HZ_DRIFT = -200 # offset for ground signal
    HZ_VARIATION = 500 # How much Hz can signal scatter to match 1 or 0 bit ?
I added the HZ_DRIFT = -200 because i found that 2400/1200 + HZ_VARIATION doesn't match very good.

Maybe i should add a "automatic" modus: Analyse the zeror crossings and use the timing information in trigger.


EDIT: I will collect some WAVES here: viewtopic.php?f=8&t=4326
... too many ideas and too little time ... Related stuff written in Python:
Dragon 32 emulator / PyDC - Python Dragon 32 converter: https://github.com/jedie/DragonPy
DWLOAD server / Dragon-Lib and other stuff: https://github.com/6809
jedie
Posts: 655
Joined: Wed Aug 14, 2013 12:23 pm
Location: germany
Contact:

Re: dragon 32 cassette format ?

Post by jedie »

Small status update:

I split the git repro in branches. Because i've noticed that wav2bas is broken by some code changes.

I have create a simple unitest for wav2bas here: https://github.com/jedie/PyDragon32/blo ... C/tests.py
and rollback the 'master' branch to a working state. All changes are not in "dev" branch.
Then i cherry-pick commits separately and run the unittest.

So the tree looks a little bit confuse: https://github.com/jedie/PyDragon32/network

I have not the time to cleanup everything and to find the bug that breaks wav2bas.

For the future i will develope in the "dev" branch and merge in "master" only working states.

Currently the "--analyze" function and the "auto drift" is not in "master"...
... too many ideas and too little time ... Related stuff written in Python:
Dragon 32 emulator / PyDC - Python Dragon 32 converter: https://github.com/jedie/DragonPy
DWLOAD server / Dragon-Lib and other stuff: https://github.com/6809
jedie
Posts: 655
Joined: Wed Aug 14, 2013 12:23 pm
Location: germany
Contact:

Re: dragon 32 cassette format ?

Post by jedie »

cas2bas is on the way: https://github.com/jedie/PyDragon32/com ... s?expand=1

btw. later, if the basics (wav <-> cas <-> bas) works, i have the idea to insert a BASIC-Tokens converter. So thats possible to convert between CoCo and Dragon. Would this makes sence? (Before i must implement the BASIC parster ;) )
... too many ideas and too little time ... Related stuff written in Python:
Dragon 32 emulator / PyDC - Python Dragon 32 converter: https://github.com/jedie/DragonPy
DWLOAD server / Dragon-Lib and other stuff: https://github.com/6809
User avatar
robcfg
Posts: 1529
Joined: Sat Apr 04, 2009 10:16 pm
Location: Stockholm, Sweden
Contact:

Re: dragon 32 cassette format ?

Post by robcfg »

It would make sense if you have a tokenised CoCo basic cas file and you want to load it on a Dragon (or the other way round).

If you can save the basic file in ASCII mode, then you can load it right away on the other machine.
jedie
Posts: 655
Joined: Wed Aug 14, 2013 12:23 pm
Location: germany
Contact:

Re: dragon 32 cassette format ?

Post by jedie »

bas2cas lands in master with: https://github.com/jedie/PyDragon32/com ... 72832a45cc

I added a "step-by-step" unittest, looks like this:

Code: Select all

    def test_bas2cas01(self):
        # create cas
        source_filepath = self._src_file_path("HelloWorld1.bas")
        destination_filepath = self._dst_file_path("unittest_HelloWorld1.cas")

        cfg = configs.Dragon32Config()
        cfg.LEAD_BYTE_LEN = 35
        bas2cas(source_filepath, destination_filepath, cfg)

        dest_content = self._get_and_delete_dst(destination_filepath)
# print repr(dest_content)

        cas = (
            ("UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU", "35x Leadin bytes 0x55"),
            ("<", "Sync byte 0x3C"),

            ("\x00", "block type: filename block (0x00)"),
            ("\x0f", "block length (15Bytes)"),

            ("HELLOWOR", "filename"),
            ("\x00", "File type: BASIC programm (0x00)"),
            ("\xff", "format: ASCII BASIC (0xff)"),
            ("\x00", "gap flag (00=no gaps, FF=gaps)"),
            ("\x0c\x00", "machine code starting address"),
            ("\x0c\x00", "machine code loading address"),

            ("\x92", "block checksum"),
            ("U", "magic byte block terminator 0x3C"),

            ("UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU", "35x Leadin bytes 0x55"),
            ("<", "Sync byte 0x3C"),

            ("\x01", "block type: data block (0x01)"),
            (":", "block length 0x3a (58Bytes)"),
            (
                '\r10 FOR I = 1 TO 10\r20 PRINT I;"HELLO WORLD!"\r30 NEXT I\r',
                "Basic code in ASCII format"
            ),
            ("\x00\x00", "code end terminator"),
            ("\x91", "block checksum"),
            ("U", "magic byte block terminator 0x3C"),

            ("UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU", "35x Leadin bytes 0x55"),
            ("<", "Sync byte 0x3C"),

            ("\xff", "block type: end-of-file block (0xff)"),
            ("\x00", "block length (0Bytes)"),
            ("\xff", "block checksum"),
            ("U", "magic byte block terminator 0x3C"),
        )

        dest_content = iter(dest_content)
        for no, data in enumerate(cas):
            part, desc = data
            part_len = len(part)
            dest_part = "".join(tuple(itertools.islice(dest_content, part_len)))
            self.assertEqual(part, dest_part,
                msg="Error in part %i '%s': %s != %s" % (no, desc, repr(part), repr(dest_part))
            )
btw. the "\x00\x00", "code end terminator" is from my own research: viewtopic.php?f=8&t=4231&start=20#p8736

But it can not been found here:
http://www.onastick.clara.net/cosio.htm
http://www.cs.unc.edu/~yakowenk/coco/te ... ormat.html
http://archive.worldofdragon.org/index. ... le_Formats

Have i missed it?


btw. completely missed in the current implementation: Split data blocks with more than 255Bytes... But where should the cut be made exactly? On a line-ending? Or in the middle of the code?
... too many ideas and too little time ... Related stuff written in Python:
Dragon 32 emulator / PyDC - Python Dragon 32 converter: https://github.com/jedie/DragonPy
DWLOAD server / Dragon-Lib and other stuff: https://github.com/6809
User avatar
robcfg
Posts: 1529
Joined: Sat Apr 04, 2009 10:16 pm
Location: Stockholm, Sweden
Contact:

Re: dragon 32 cassette format ?

Post by robcfg »

Well, in some sense you missed the terminator 0x00's, because you are looking in the wrong place.

One thing is how the .cas format works, and other one is how the data you retrieve from the .cas file is actually formatted. For that info you should search for tokenized dragon basic format or something similar.

Regarding your second question, you can cut in the middle of the code, but I guess nothing will stop you from saving data blocks with less than 255 bytes...
jedie
Posts: 655
Joined: Wed Aug 14, 2013 12:23 pm
Location: germany
Contact:

Re: dragon 32 cassette format ?

Post by jedie »

I have bugfixed the wave generation... Now i can CLOAD in XRoar the first time without errors... But the loaded listing is wrong:

Code: Select all

12320 FOR I = 1 TO 10
20 PRINT I;"HELLO WORLD!"
30 NEXT I
0 FOR I = 1 TO 10
20 PRINT I;"HELLO WORLD!"
30 NEXT I
65535 SGN
It should be looks like this:

Code: Select all

10 FOR I = 1 TO 10
20 PRINT I;"HELLO WORLD!"
30 NEXT I
Maybe the gap flag / machine code starting/loading addresses are wrong?

I used this values:

Code: Select all

        codepoints.append(0x00) # one byte gap flag (00=no gaps, FF=gaps)

        codepoints += [0x0c, 0x00] # two bytes machine code starting address
        codepoints += [0x0c, 0x00] # two bytes machine code loading address
... too many ideas and too little time ... Related stuff written in Python:
Dragon 32 emulator / PyDC - Python Dragon 32 converter: https://github.com/jedie/DragonPy
DWLOAD server / Dragon-Lib and other stuff: https://github.com/6809
jedie
Posts: 655
Joined: Wed Aug 14, 2013 12:23 pm
Location: germany
Contact:

Re: dragon 32 cassette format ?

Post by jedie »

anybody a idea what's wrong here:

Code: Select all

'UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU' # 35x Leadin bytes 0x55
'<'                                   # Sync byte 0x3C
'\x00'                                # block type: filename block (0x00)
'\x0f'                                # block length (15Bytes)
'HELLOWOR'                            # filename
'\x00'                                # File type: BASIC programm (0x00)
'\xff'                                # format: ASCII BASIC (0xff)
'\x00'                                # gap flag (00=no gaps, FF=gaps)
'HE'                                  # machine code starting address
'LL'                                  # machine code loading address
'\x9f'                                # block checksum
'U'                                   # magic byte block terminator 0x3C
'UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU' # 35x Leadin bytes 0x55
'<'                                   # Sync byte 0x3C
'\x01'                                # block type: data block (0x01)
':'                                   # block length 0x3a (58Bytes)
'\r10 FOR I = 1 TO 10\r20 PRINT I;"HELLO WORLD!"\r30 NEXT I\r' # Basic code in ASCII format
'\x00\x00'                            # code end terminator
'\x91'                                # block checksum
'U'                                   # magic byte block terminator 0x3C
'UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU' # 35x Leadin bytes 0x55
'<'                                   # Sync byte 0x3C
'\xff'                                # block type: end-of-file block (0xff)
'\x00'                                # block length (0Bytes)
'\xff'                                # block checksum
'U'                                   # magic byte block terminator 0x3C
Note: any codepoint which is alphanumeric ASCII would be display as ASCII characters. e.g.: Sync byte would be inserted as 0x3c and not as '<' ;)
... too many ideas and too little time ... Related stuff written in Python:
Dragon 32 emulator / PyDC - Python Dragon 32 converter: https://github.com/jedie/DragonPy
DWLOAD server / Dragon-Lib and other stuff: https://github.com/6809
Sarah
Posts: 177
Joined: Wed Apr 13, 2011 3:36 pm
Contact:

Re: dragon 32 cassette format ?

Post by Sarah »

You need to set the gap flag ($FF) for ASCII BASIC. The correct checksum would then be $79 if you make both the load and exec addresses $0000 or omit them. Your sync byte trailing the header is commented incorrectly but that won't matter; it's not required (sync bytes come before blocks rather than after them).

Since this is an ASCII file the text should end with the final carriage return ($0D which I presume is what your '\r' character generates) rather than $0000. The length of the single data block then becomes $38 and the checksum $BF.

You have a couple more sync bytes commented incorrectly at the ends of blocks but the 'U' character will provide the right value.
Post Reply