Dragon 32 Homecomputer Kassetten in ASCII umwandeln...

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Hab mal eine Analyse Funktion eingebaut. Damit wird eine Statistik erstellt, die so aussieht:

Code: Alles auswählen

~$ python PyDC_cli.py --analyze FooBar.wav

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.
Wie man sieht, haben die Zeiten einigen Abstand zu den Spezifizierten 2400Hz / 1200Hz ...
Deswegen hab ich auch einen "Offset" Wert eingebaut.

Eigentlich könnte man auch hingehen und erst Analysieren und dann mit den gemessenen Timings den Trigger starten...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Brauche nochmal Hilfe...

Hab nun mit cas2bas angefangen:
https://github.com/jedie/PyDragon32/com ... 33e#L0R421

Dabei ist .CAS nichts anderes als der Nackte byte-stream des WAVE daten.

Doch so allgemein, einen "unbekannen" Byte-Stream zu parsen... Wie geht man da am besten vor?
ich denke struct ist keine gute Idee: Ich möchte lieber Stück für Stück die Daten durch gehen und prüfen und evtl. Fehler Anzeigen.
z.B. das die Checksum des Blocks falsch ist usw.

Also es ist im Prinzip immer der gleiche Aufbau, aber ich will nicht voraussetzten das es z.B. genau 256 LeadIn-bytes sind. Kann irgendeine Anzahl sein.

EDIT: Achso, eigentlich ist bas2wav fertig. Doch es funktioniert nicht so wie erwartet. Ich vermute das am Ende der Wave-Datei einfach eine Menge Samples fehlen.
Zum Debugging, will ich hat die Zwischenstufe .CAS hin und her konvertieren können um zu sehen, ob die Grundlagen zur WAVE Datei auch die richtigen sind.

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Hab das ganze nochmal neu gemacht und somit die meine Frage von vorhin fast überflüssig.

Mir ist nämlich aufgefallen, das ich bei cas2bas im Prinzip das selbe machen muß, wie bei wav2bas... Denn im Grunde ist es fast so: .wav -> .cas -> .bas

Somit brauche ich fast nichs neu zu machen... Sieht z.Z. dann so aus:
https://github.com/jedie/PyDragon32/blo ... handler.py

Ich brauchte quasi nur den bit-sync Teil anders machen. Denn eigentlich brauche ich nur zu Überprüfen ob LeadIn und Sync-Byte vorhanden sind und das sieht nun so aus:

Code: Alles auswählen

def count_the_same(iterable, sentinel):
    """
>>> count_the_same([0x55,0x55,0x55,0x55,0x3C,"foo","bar"],0x55)
(4, 60)
>>> 0x3C == 60
True
"""
    count = 0
    x = None
    for count, x in enumerate(iterable):
        if x != sentinel:
            break
    return count, x

...

        leadin_bytes_count, sync_byte = count_the_same(bitstream, self.cfg.LEAD_BYTE_CODEPOINT)
        if leadin_bytes_count == 0:
            log.error("Leadin byte not found in file!")
            sys.exit(-1)
        log.info("%s x leadin bytes (%s) found." % (leadin_bytes_count, hex(self.cfg.LEAD_BYTE_CODEPOINT)))

        if sync_byte != self.cfg.SYNC_BYTE_CODEPOINT:
            log.error("Sync byte wrong. Get %s but excepted %s" % (
                hex(sync_byte), hex(self.cfg.SYNC_BYTE_CODEPOINT)
            ))
        else:
            log.debug("Sync %s byte, ok." % hex(self.cfg.SYNC_BYTE_CODEPOINT))
Insgesammt muß der ganze code nun mal aufgeräumt werden. Mache ich allerdings erst, wenn es denn mal läuft.
Noch sind Bugs drin:

Die Ausgaben von .bas -> .cas sieht so aus:

Code: Alles auswählen

./PyDC_cli.py test_files/HelloWorld1.bas test.cas
sieht so aus:
Verbosity log level: DEBUG
logfile log level: INFO
filename 'HELLOWOR' from: ../test_files/HelloWorld1.bas
Create '../test.cas'...
filename block: ['HELLOWOR', '\x00', '\xff', '\x00', '\x0c', '\x00', '\x0c', '\x00']
yield 255x lead byte 0x55
yield sync byte 0x3c
yield block type 'filename block (0x00)'
yield block length 0xf (15Bytes)
content of 'filename block (0x00)':
-------------------------------------------------------------------------------
['HELLOWOR', '\x00', '\xff', '\x00', '\x0c', '\x00', '\x0c', '\x00']
-------------------------------------------------------------------------------
yield calculated checksum 0xf
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)
content of '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 0x3b
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
logger name: PyDC

Python dragon 32 converter 0.1.0.dev
-------------------------------------------------------------------------------

source file.......: ../test_files/HelloWorld1.bas
destination file..: ../test.cas
set log level to: 10
There exists 1 files:
Filename: 'HELLOWOR'
file type: BASIC programm (0x00)
is tokenized: False
code lines:
-------------------------------------------------------------------------------
10 FOR I = 1 TO 10
20 PRINT I;"HELLO WORLD!"
30 NEXT I
-------------------------------------------------------------------------------

File '../test.cas' saved.
und dann von .cas -> .bas so:
../PyDC_cli.py --verbosity=10 ../test.cas --dst=../test.bas
Verbosity log level: DEBUG
logfile log level: INFO
file sizes: 853 Bytes
255 x leadin bytes (0x55) found.
Sync 0x3c byte, ok.
raw block type: 0x0 (0)
Block length: 15Bytes, ok.
Block checksum 0xf is not equal with calculated checksum: 0x92
Magic Byte 0x55, ok.
filename data: ['HELLOWOR', '\x00', '\xff', '\x00', '\x0c', '\x00', '\x0c', '\x00']
file type: BASIC programm (0x00)
Raw ASCII flag is: 255
ASCII flag: ASCII BASIC (0xff)
Add file <BlockFile 'HELLOWOR'>
255 x leadin bytes (0x55) found.
Sync 0x3c byte, ok.
raw block type: 0x1 (1)
Block length: 58Bytes, ok.
Block checksum 0x3b is not equal with calculated checksum: 0x91
Magic Byte 0x55, ok.
255 x leadin bytes (0x55) found.
Sync 0x3c byte, ok.
raw block type: 0xff (255)
Block length: 0Bytes, ok.
Block checksum 0xff is ok.
Magic Byte 0x55, ok.
EOF-Block found
Create '../test_HELLOWOR.bas'...
logger name: PyDC

Python dragon 32 converter 0.1.0.dev
-------------------------------------------------------------------------------

source file.......: ../test.cas
destination file..: ../test.bas
set log level to: 10
_______________________________________________________________________________
*** block type: 0x0 (filename block (0x00))

Filename: 'HELLOWOR'
===============================================================================
_______________________________________________________________________________
*** block type: 0x1 (data block (0x01))

ERROR: Splitting linenumber in '\x00\x00': need more than 1 value to unpack
59 Bytes parsed

ERROR: Block length value 58 is not equal to parsed bytes!

*******************************************************************************
10 FOR I = 1 TO 10
20 PRINT I;"HELLO WORLD!"
30 NEXT I
*******************************************************************************
===============================================================================
_______________________________________________________________________________
*** block type: 0xff (end-of-file block (0xff))

File '../test_HELLOWOR.bas' saved.

Der eigentliche Code der rauskommt, ist ja schon mal der selbe. Allerdings die eine fehler aufgefallen... Evtl. nur Kleinigkeiten...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

So, alles gefixed. Checksum stimmen und block länge auch.

Mit https://github.com/jedie/PyDragon32/com ... 5cc#L0R147 habe ich einen relativ Aufwendigen Unittest geschrieben, so kann man gleich den Aufbau nachvollziehen:

Code: Alles auswählen

    def test_bas2cas01(self):
        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)

        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))
            )
Jetzt sollten auch wave dateien richtig erzeugt werden. Schließlich ist die Ausgangsbasis das selbe.

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

So, mit dem fix beim erzeugen der WAVE Dateien (siehe: http://www.python-forum.de/viewtopic.php?f=1&t=32241 ) kommt nun auch erstmal "brauchbare" Daten bei einer bas2wav konvertierung...

Der Emulator läd sie auch ein und das Listing sieht schon recht brauchbar aus. Aber es stimmt noch nicht.
Das BASIC Programm was ankommt, ist quasi zweimal hintereinander da:

Code: Alles auswählen

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
Richtig sollte es so aussehen:

Code: Alles auswählen

10 FOR I = 1 TO 10
20 PRINT I;"HELLO WORLD!"
30 NEXT I

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
sparrow
User
Beiträge: 4183
Registriert: Freitag 17. April 2009, 10:28

Ist das eine Originaldatei?
Möglicherweise ist der zweite Teil eine Sicherung, falls Checksummen für ersten ergeben, dass die Daten korrupt sind?
Ist aber geraten, ich kenne das Format nicht.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Nein, das kann nicht sein. So viel Rendundanz ist in den Daten nicht erhalten.

Zum Thema "Maschinen Code start/load Adresse": Auf der CoCo ML hat jemand die Antwort, siehe: http://five.pairlist.net/pipermail/coco ... 71210.html

Also:

Code: Alles auswählen

...
            ("HELLOWOR", "filename"),
..
            ("\x0c\x00", "machine code starting address"),
            ("\x0c\x00", "machine code loading address"),
In dem Fall muss die Start Adresse: 0x4845 und loading: 0x4c4c sein. Weil:

Code: Alles auswählen

print hex(ord("H")),hex(ord("E")) #-> 0x48 0x45
print hex(ord("L")),hex(ord("L")) # -> 0x4c 0x4c

Also "start/load Adresse" sind nur die ersten Buchstaben des Dateinamens, den man beim Speichern angegeben hat, in dem Fall: CSAVE "HELLOWOR"

Bloß, was soll das? Sind doch redundante Informationen...

Offensichtlich ist die Bezeichnung "Maschinen Code start/load Adresse" falsch, oder?

EDIT: Umgesetzt mit: https://github.com/jedie/PyDragon32/com ... d7be0bd7ad

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
BlackJack

@jens: Nein die Bezeichnung ist richtig. Bei Maschinencode-Programmen steht da die Lade- und die Ausführungsadresse drin. Bei BASIC-Programmen halt irgendwas, nur keine sinnvollen Informationen. Was das soll hatten wir schon mal: Man will nicht für jeden Dateityp unterschiedliche Datenlayouts haben, weil man dann jeden Dateityp mit deutlich unterschiedlicherem Code behandeln muss. Das sind vier Bytes auf Band „zu viel” bei BASIC-Programmen vs. deutlich mehr Bytes als Code im ROM.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Stimmt. Aber kann könnte man doch einfach 0x00 eintragen, anstatt ein Teil vom Dateinamen. Aber wahrscheinlich war das irgendwie einfacher zu machen?

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
BlackJack

@jens: Ich vermute mal ganz stark da wurde *gar nichts* eingetragen und das ist einfach nur zufällig der Speicherinhalt an der Stelle, der da noch von einem anderen Subroutinenaufruf herum liegt.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Jep, du hast recht. Siehe: http://archive.worldofdragon.org/phpBB3 ... t=60#p9110

Beim BASIC code kann man die Information auch einfach weg lassen ;)

Nun läuft das hin und her konvertieren auch schon recht gut. Der Code kommt nun auch richtig an. Es waren noch ein paar falsche "Terminator-Zeichen" drin, die ich in fremden Code gesehen und übernommen hatte. Was aber falsch war...

Was nun noch fehlt: Ich muss den Code in 255Bytes Häppchen verpacken. Dann fehlt noch das konvertieren vom ASCII code in torkens, damit das ganze effektiver wird.

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Antworten