|
|
|
Dieser Anhang beschreibt die verbreiteten Dateiformate im Bereich der Commodore64 Emulation. Er richtet sich an interessierte Programmierer, die sich für die interne Funktionsweise der Emulation interessieren. Wenn Sie mit Power64 nur Ihre alten Programme nutzen wollen, brauchen Sie sich um diese Details nicht sorgen.
Power64 verwendet für die verschiedenen Dateiformate verschiedene Icons. Zusätzlich wird in den Icons zwischen unkomprimierten, GZ- und ZIP-komprimierten Dateien unterschieden. GZ-komprimierte Dateien tragen stets eine gelbe Markierung. Die Markierung der ZIP-komprimierten Dateien ist dagegen rot. Da LHA Dateien sehr selten sind wird für LHA-komprimierte Dateien nur ein allgemeines Icon verwendet, ohne zwischen den möglichen Inhalten des Archivs zu unterscheiden.
E.1 *.P00 Programm Format |
Das *.P00 Format ist ein flexibles Dateiformat, das in der Lage ist, alle üblichen Arten von C64 Dateien zu unterstützen. Es verfügt über einen 'magischen' Header, durch den der Emulator erkennen kann, ob es sich um eine korrekte Datei handelt. Somit ist *.P00 das empfohlene Dateiformat für einzelne Dateien.
typedef struct { Byte P00Magic[8]; /* $00 - $07 */ Byte OrigFName[17]; /* $08 - $18 */ Byte RecordSize; /* $19 */ Byte Data[n]; /* $1A - ... */ } P00File;
P00Magic | - Magische Konstante 'C64File' = {$43, $36, $34, $46, $69, $6C, $65, $00} |
OrigFName | - Original C64 Dateiname (CBM Charset) |
RecordSize | - Record Größe für RELative Dateien |
Data | - Die eigentlichen Daten |
Dieses Dateiformat wurde zuerst von Wolfgang Lorenz in PC64 verwendet. Beachten Sie, daß die Extension *.P00 nicht fest ist. Vielmehr steht das 'P' für 'PRG' und kann sich in ein 'S' für 'SEQ', oder 'R' für 'REL' wandeln. Weiters kann das '00' genutzt werden um mit '01', '02', '03'… weiterzuzählen und so Konflikte aufzulösen, die durch die Beschneidung von 16 Zeichen langen C64 Dateinamen auf 8+3 DOS Dateinamen auftreten. Dieses Problem existiert natürlich nicht auf einem Macintosh - Es ist nur ein Hinweis, falls Sie jemals über eine *.P01 Datei stolpern sollten.
E.2 *.C64 Program Format |
Das *.C64 Format entspricht genau der Struktur in der der original Commodore 64 Daten auf Diskette oder Band speichert.
typedef struct { Byte LoadAddrLow, LoadAddrHigh; /* $00 - $01 */ Byte PrgData[n]; /* $02 - ... */ } C64File;
LoadAddr | - Jene Adresse im C64 RAM an die das Programm normalerweise geladen wird. |
PrgData | - Das eigentliche Programm |
Dieses Dateiformat wurde von Miha Peternel in C64S eingeführt. Wie man aus der obigen Beschreibung sehen kann, ist es ein recht einfaches Dateiformat. Trotzdem ist seine Verwendung nur eingeschränkt zu empfehlen, da es über keine Headerinformation anhand derer ein Emulator sicherstellen könnte, daß nur gültige Dateien verwendet werden.
E.3 *.X64 Floppy Disk Image Format |
Das *.X64 Format ist ein sehr flexibles Format für Disk Image Dateien, daß eine Vielzahl von Diskettenlaufwerken unterstützt. Die Implementation in Power64 unterstützt allerdings nur 1541 Disketten.
typedef struct { Byte X64Magic[4]; /* $00 - $03 */ Byte Version[2]; /* $04 - $05 */ Byte DiskType; /* $06 */ Byte TrackCnt; /* $07 */ Byte SecondSide; /* $08 */ Byte ErrorFlag; /* $09 */ Byte Reserved[22]; /* $0A - $1F */ Byte DiskInfo[31]; /* $20 - $3E */ Byte ConstZero; /* $3F */ Byte DiskImage[683 * 256]; } X64File;
X64Magic | - Magische Konstante 'C'1541/64 = {$43, $15, $41, $64} |
Version | - C1541 Version 2.6 = {$02, $06} |
DiskType | - Floppy disk type: 1541 = {$01} Sonstige definierte Werte: (Mit Power64 nicht nutzbar!) 0..1540, 1..1541, 2..1542, 3..1551, 4..1570, 5..1571, 6..1572, 8..1581, 16..2031&4031, 17..2040&3040, 18..2041 24..4040, 32..8050, 33..8060, 34..8061, 48..SFD 1001, 49..8250, 50..8280 |
TrackCnt | - Anzahl der Tracks auf der Disk (Side 0) = {35} |
SecondSide | - Ist es ein doppelseitige Diskette (0..Nein, 1..Ja) = {$00} |
ErrorFlag | - Flag für Fehler (genaue Bedeutung unbekannt) (ungenutzt) |
Reserved | - Muß $00 sein |
DiskInfo | - Beschreibung des Disk Image (in ASCII oder ISO Latin/1) |
ConstZero | - Muß $00 sein |
DiskImage | - 683 Disk Sektoren zu je 256 Bytes. |
(Für weitere Informationen zu DiskImage: siehe *.D64)
Das *.X64 Disk Image Format wurde von Teemu Ratanen für X64 erfunden. Da das *.X64 Dateiformat einen klaren Header definiert, der es dem Emulator möglich macht sicherzustellen, daß nur gültige Dateien verwendet werden, ist es das empfohlenen Dateiformat für Disk Images. Leider ist das *.D64 Dateiformat wesentlich weiter verbreitet.
E.4 *.D64 Floppy Disk Image Format |
Das *.D64 Dateiformat ist ein 1:1 Abbild der Sektoren, so wie sie auf der Diskette stehen. Jeder Sektor einer C1541 formatierten Diskette (und somit auch jeder Sektor in einer *.D64 Datei) besteht aus 256 Byte. Um zusätzliche Informationen über fehlerhaften Sektoren speichern zu können existiert im *.D64 Dateiformat ein optionales Byte pro Sektor.
Eine Diskette die auf einer normalen C1541 mit dem NEW Befehl formatiert wurde verfügt über 683 Sektoren in 35 Spuren. Mit Hilfe spezieller Software ist es möglich Disketten mit bis zu 768 Sektoren in 40 Spuren oder sogar 802 Sektoren in 42 Spuren zu beschreiben. Obwohl diese zusätzlichen Spuren nicht der Spezifikation des Laufwerks entsprechen, lassen sie sich doch mit den meisten C1541 Laufwerken lesen (Anmerkung: Zum Lesen ist keine spezielle Software erforderlich).
Es existieren sechs verschiedene Arten von *.D64 Dateien:
Beachten Sie, daß die Spuren am äußeren Rand der Diskette mehr Sektoren enthalten, als jene in der Mitte (Zone Bit Recording). Die Zahl der Sektoren pro Spur ist:
Spuren 1..17 - 21 Sektoren Spuren 18..24 - 19 Sektoren Spuren 25..30 - 18 Sektoren Spuren 31..35 - 17 Sektoren Spuren 36..42 - 17 Sektoren (nicht im 1541 StandardFormat!)
Die Block Allocation Map (BAM) ist in Spur 18 - Sektor 0 gespeichert. Das Directory folgt ab Spur 18 - Sektor 1.
In einer *.D64 Datei sind die Sektoren wie folgt angeordnet:
Spur 1 - Sektor 0: Offset 0 * 256 Spur 1 - Sektor 1: Offset 1 * 256 ..... Spur 1 - Sektor 20: Offset 20 * 256 Spur 2 - Sektor 0: Offset 21 * 256 Spur 2 - Sektor 1: Offset 22 * 256 ..... Spur 2 - Sektor 20: Offset 41 * 256 Spur 3 - Sektor 0: Offset 42 * 256 ..... Spur 18 - Sektor 0: Offset 357 * 256 Spur 18 - Sektor 1: Offset 358 * 256 ..... Spur 35 - Sektor 0: Offset 666 * 256 Spur 35 - Sektor 1: Offset 667 * 256 ..... Spur 35 - Sektor 16: Offset 682 * 256
Beachten Sie, daß die Nummerierung der Spuren bei 1 beginnt, jene der Sektoren aber bei 0.
typedef struct { Byte DiskImage[SektorCnt][256]; Byte ErrorInfo[SektorCnt]; /* Optional */ } D64File;
DiskImage | - 256 Bytes pro Sektor |
ErrorInfo | - 1 Byte pro Sektor |
Beachten Sie, daß die Fehlerinformationen für alle Sektoren am Ende der Datei zusammengefaßt sind. Es gibt keine Verflechtung von Daten und Fehlerinformation.
Die Bedeutung der Fehlerinformation ist in folgender Tabelle zusammengestellt:
Code Error Type 1541 error description ---- ----- ---- ------------------------------ 01 00 N/A No error, Sektor ok. 02 20 Read Header block not found 03 21 Seek No sync character 04 22 Read Data block not present 05 23 Read Checksum error in data block 06 24 Write Write verify (on format) 07 25 Write Write verify error 08 26 Write Write protect on 09 27 Seek Checksum error in header block 0A 28 Write Write error 0B 29 Seek Disk ID mismatch 0F 74 Read Disk Not Ready (no device 1)
E.5 ZipCode Disk Format (1!*, 2!*, etc.) |
Dies ist eine komprimierte Version des *.D64 Formats, die sich häufig auf Servern findet die schon auf eine lange Geschichte zurückblicken können und den C64 Freaks schon dienten als Emulation noch unbekannt war. Eine *.D64 Datei wird dabei in 4 (für Disketten mit 35 Spuren) oder 5 (40 Spuren) Teile zerlegt. Jedes Segment wird mit dem einfachen (und nur mäßig komprimierenden) Run Length Encoding gepackt.
Der Hauptgrund für die Existenz dieses Dateiformates besteht darin, daß jedes Segment eines solchen ZipCode Archives kleiner als 44 KByte ist und daher bequem mit einem realen C64 und seinem C1541 Diskettenlaufwerk handhabbar ist (etwa nach der Übermittlung von einem BBS via Modem). Außerdem ist das Komprimieren und Dekomprimieren auf einem C64 sehr schnell.
Moderne Computer, wie ein Power Macintosh, für verwendet werden um C64 Emulatoren haben kein Problem ein Datei von 171 KByte (wie etwa eine *.D64 Datei) zu benutzen, und ziehen somit keinen Nutzen daraus wenn eine 'große' Datei in mehrere 'kleine' zersplittert wird. Im Gegenteil: Durch die große Allokationsgrößen auf modernen Festplatten und CD-ROMs ist es oft so, daß der Platz der durch 4 halbgefüllte letzte Blöcke von 4 Dateien verschwendet wird mehr ausmacht als durch die Komprimierung einer einigermaßen gut gefüllten Floppy eingespart werden konnte. Außerdem bieten moderne Kompressionsverfahren wie Zip, GZ oder LHA deutlich bessere Kompression als das in ZipCode verwendete RLE.
Auch wenn ZipCode nicht die optimale Wahl eines Dateiformats auf einem Emulator darstellt, ist es doch immer noch ein gutes Format für einen echten C64 und daher bieten einige Sites ihre Dateien im ZipCode Format an um jene treuen C64-Freaks zu bedienen die weiterhin an ihrer echten Hardware hängen.
Anmerkung: Es gibt noch zwei weitere Zip Formate für den C64, die von Power64 nicht unterstützt werden. Sie verwenden die Dateinamen-Präfixe 1!!, 2!!, 3!!… bzw. A!, B!, C!. Sie sind in der Sammlung der Dateiformate von Peter Schepers, dem Autor von C64 COPY (schepers@dcs1.uwaterloo.ca) erwähnt, aber ich habe noch nie Dateien dieses Types irgendwo im Web gefunden.
Der Inhalt einer Diskette ist wie folgt auf die 4-5 Segmente verteilt:
Dateiname Spuren Sektoranzahl -------- ----------- ----------- 1!xxxxxx 1 - 8 168 Sektors 2!xxxxxx 9 - 16 168 Sektors 3!xxxxxx 17 - 25 172 Sektors 4!xxxxxx 26 - 35 175 Sektors 5!xxxxxx 36 - 40 85 Sektors (nur Disketten mit 40 Spuren)
Alle Segmente haben die gleiche grundsätzliche Struktur, die auf komprimierten Disk Sektoren basiert. Nur das erste Segment unterscheidet sich geringfügig vom Rest.
typedef struct { Byte LoadAddr[2]; /* Konst. $03FE = { 0xFE, 0x03}; */ Byte FloppyID[2]; ZipSektor Image[168]; } ZipSegment_1!;
typedef struct { Byte LoadAddr[2]; /* Konst. $0400 = { 0x00, 0x04}; */ ZipSektor Image[...]; } ZipSegment_n!; /* n = {2, 3, 4, 5} */
Die Kompression erfolgt sektorweise. Jeder Sektor weist die folgende Struktur auf:
typedef struct { Byte Spur; /* Spur Nummer und Kompressionsart */ Byte Sektor; /* Sektor Nummer */ union { { Byte NoCompression[256]; } Mode00; { Byte SingleByte; } Mode01; { Byte Length; Byte RepeatChar; Byte RLEData[Length]; } Mode10; } Data; } ZipSektor;
Spur | - Enthält die Spurnummer des komprimierten Sektors in Bits 5 bis Bit 0. Bits 7 & 6 enthalten die Kompressionsart. |
Sektor | - Enthält die Sektornummer des komprimierten Sektors. |
Data | - Die Bedeutung von Data hängt von der Kompressionsart ab. |
Kompressionsarten:
Die Sektoren einer Spur werden NICHT in linearer Abfolge gespeichert (1, 2, 3, 4…). Um das Packen und Entpacken auf einer echten 1541 schneller zu machen wird Interleaving eingesetzt. Die korrekte Reihenfolge der Sektoren hängt von der Anzahl der Sektoren in der Spur ab, und kann folgender Tabelle entnommen werden:
Spur 1-17: 0 11 1 12 2 13 3 14 4 15 5 16 6 17 7 18 8 19 9 20 10 Spur 18-24: 0 10 1 11 2 12 3 13 4 14 5 15 6 16 7 17 8 18 9 Spur 25-30: 0 9 1 10 2 11 3 12 4 13 5 14 6 15 7 16 8 17 Spur 31-40: 0 9 1 10 2 11 3 12 4 13 5 14 6 15 7 16 8
Als Beispiel die ersten Sektoren eines ersten Segmentes (1!*):
$0000: 0xFE, 0x03, /* Load Addr. Konst. $03FE */ $0002: 0x36, 0x34, /* Floppy ID */ $0004: 0x41, 0x00, 0x00, /* Spur 1, Sektor 0 gefüllt mit 0x00 */ $0007: 0x41, 0x0B, 0x00, /* Spur 1, Sektor 11 gefüllt mit 0x00 */ $000B: 0x41, 0x01, 0x00, /* Spur 1, Sektor 1 gefüllt mit 0x00 */ $0007: 0x01, 0x0C, /* Spur 1, Sektor 12 unkomprimierte Daten */ ... 256 Byte Data ... $0109: 0x81, 0x02, /* Spur 1 ,Sektor 2 RLE Kodiert */ 0x12, 0xEA, /* 0x12(18) Byte, RepeatChar: 0xEA */ 0x45, 0x22, 0x34, 0x08,/* Normale Daten */ 0xEA, 0xD0, 0x77, /* 0x77 wird 0xD0 mal wiederholt */ 0x12, 0xFF, 0x00, 0x00, 0x32, 0x11 /* Normale Daten */ 0xEA, 0x24, 0x55, /* 0x55 wird 0x24 wiederholt */ 0xEE, 0x98, /* Normale Daten */ $011F: 0x41, 0x0D, 0x11 /* Spur 1, Sektor 13 gefüllt mit 0x11 */ $0122: ...
E.6 *.D71 Floppy Disk Image Format |
Das *.D71 Dateiformat ist ein 1:1 Abbild der Sektoren, so wie sie auf der Diskette eines 1571 Diskettenlaufwerks stehen. Jeder Sektor einer C1571 formatierten Diskette (und somit auch jeder Sektor in einer *.D71 Datei) besteht aus 256 Byte. Um zusätzliche Informationen über fehlerhaften Sektoren speichern zu können existiert im *.D71 Dateiformat ein optionales Byte pro Sektor.
Das 1571 Diskettenlaufwerk verwendet doppelseitige 5.25" Disketten die mit 35 Spuren pro Seite (also 70 Spuren insgesamt) beschieben werden. Die Spuren am äußeren Rand der Diskette enthalten mehr Sektoren, als jene in der Mitte (Zone Bit Recording). Die Zahl der Sektoren pro Spur ist:
Spuren 1..17 - 21 Sektoren Spuren 18..24 - 19 Sektoren Spuren 25..30 - 18 Sektoren Spuren 31..35 - 17 Sektoren Spuren 36..52 - 21 Sektoren (entsprechend Spuren 1..17 - zweite Diskettenseite) Spuren 53..59 - 19 Sektoren (entsprechend Spuren 18..24 - zweite Diskettenseite) Spuren 60..65 - 18 Sektoren (entsprechend Spuren 25..30 - zweite Diskettenseite) Spuren 66..70 - 17 Sektoren (entsprechend Spuren 31..35 - zweite Diskettenseite)
Vergleicht man diese Anordnung mit der in Kapitel E.4 beschriebenen Aufteilung bei den *.D64 Floppy Disk Images so erkennt man, daß hier einfach die zwei Seiten einer 35 Spur - Diskette hintereinandergehängt wurden.
Die Block Allocation Map (BAM) ist in Spur 18 - Sektor 0 und Spur 53 - Sektor 0 gespeichert. Das Directory beginnt ab Spur 18 - Sektor 1.
In einer *.D71 Datei sind die Sektoren wie folgt angeordnet:
Spur 1 - Sektor 0: Offset 0 * 256 Spur 1 - Sektor 1: Offset 1 * 256 ..... Spur 1 - Sektor 20: Offset 20 * 256 Spur 2 - Sektor 0: Offset 21 * 256 Spur 2 - Sektor 1: Offset 22 * 256 ..... Spur 2 - Sektor 20: Offset 41 * 256 Spur 3 - Sektor 0: Offset 42 * 256 ..... Spur 18 - Sektor 0: Offset 357 * 256 Spur 18 - Sektor 1: Offset 358 * 256 ..... Spur 35 - Sektor 0: Offset 666 * 256 Spur 35 - Sektor 1: Offset 667 * 256 ..... Spur 35 - Sektor 16: Offset 682 * 256 ..... Spur 36 - Sektor 0: Offset 683 * 256 Spur 36 - Sektor 1: Offset 684 * 256 ..... Spur 36 - Sektor 20: Offset 703 * 256 ..... Spur 70 - Sektor 0: Offset 1349 * 256 Spur 70 - Sektor 1: Offset 1350 * 256 ..... Spur 70 - Sektor 16: Offset 1365 * 256
Beachten Sie, daß die Nummerierung der Spuren bei 1 beginnt, jene der Sektoren aber bei 0.
E.7 *.D81 Floppy Disk Image Format |
Das *.D81 Dateiformat ist ein 1:1 Abbild der Sektoren, so wie sie auf der Diskette eines 1581 Diskettenlaufwerks stehen. Jeder Sektor einer C1581 formatierten Diskette (und somit auch jeder Sektor in einer *.D81 Datei) besteht aus 256 Byte. Um zusätzliche Informationen über fehlerhaften Sektoren speichern zu können existiert im *.D81 Dateiformat ein optionales Byte pro Sektor.
Das 1581 Diskettenlaufwerk verwendet doppelseitige 3.5" Disketten die mit 40 Spuren pro Seite (also 80 Spuren insgesamt) beschieben werden. Bei der Nummerierung der Spuren wird immer zwischen den Diskettenseiten gewechselt, so daß Spur 40 in der Mitte der Diskette liegt. Jede Spur enthält 40 Sektoren, so daß insgesamt 40*80 = 3200 Sektoren zur Verfügung stehen.
Der Floppy Master Block ist in Spur 40 - Sektor 0 gespeichert. Es folgt die Block Allocation Map (BAM) ist in Spur 40 - Sektor 1 und Sektor 2 und danach beginnt das Directory ab Spur 40 - Sektor 3.
Der Offset eines Sektors in einer *.D81 berechnet sich daher wie folgt:
Offset = (40 * (Spur -1) + Sektor) * 256
Beachten Sie, daß die Nummerierung der Spuren bei 1 beginnt, jene der Sektoren aber bei 0.
E.8 *.Lynx File Format |
Das Lynx (oder LNX oder Ultimate Lynx) Dateiformat wurde von Will Corley für die Verwendung auf dem C64(!) entwickelt. Es basiert auf Blöcken von 254 Byte. Diese entsprechen den 256 Byte eines 1541 Disk Sektors abzüglich jener 2 Byte, die die Spur/ Sektor Information des Folgesektors enthalten. Dadurch ist es einfach Daten zwischen 1541 Disketten und Lynx Dateien zu kopieren.
Unglücklicher Weise ist der Lynx Header in einem Format geschrieben, das wesentlich schwieriger zu Handhaben ist als das einer T64 oder D64 Datei. Dadurch hat dieses Format nur wenig Aufmerksamkeit von Emulator Autoren erhalten.
typedef struct { Byte Directory[DirSektorCnt * 254]; Byte Data[DataSektorCnt * 254]; } LynxFile;
Das Directory beginnt mit einem kleinen BASIC programm, daß, wenn man es lädt und startet die Meldung "Use LYNX to dissolve this file" ausgibt. Die genaue Meldung und die Programmgröße kann variieren. Üblicherweise ist das Programm 94 Byte lang (von $0000 bis $005D). Unglücklicher Weise gibt es einige Emulatoren, die blind darauf vertrauen, daß das Programm exakt 94 Byte groß ist und, daß der Text 'LYNX' exakt bei Offset $003C bis $003F zu finden ist.
$0000: 0x01, 0x08, 0x5B, 0x08, 0x0A, 0x00, 0x97, 0x35, /* .......5 */ $0008: 0x33, 0x32, 0x38, 0x30, 0x2C, 0x30, 0x3A, 0x97, /* 3280,0:. */ $0010: 0x35, 0x33, 0x32, 0x38, 0x31, 0x2C, 0x30, 0x3A, /* 53281,0: */ $0018: 0x97, 0x36, 0x34, 0x36, 0x2C, 0xC2, 0x28, 0x31, /* .646,.(1 */ $0020: 0x36, 0x32, 0x29, 0x3A, 0x99, 0x22, 0x93, 0x11, /* 62):.".. */ $0028: 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x22, /* ......." */ $0030: 0x3A, 0x99, 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, /* :." */ $0038: 0x55, 0x53, 0x45, 0x20, 0x4C, 0x59, 0x4E, 0x58, /* USE LYNX */ $0040: 0x20, 0x54, 0x4F, 0x20, 0x44, 0x49, 0x53, 0x53, /* TO DISS */ $0048: 0x4F, 0x4C, 0x56, 0x45, 0x20, 0x54, 0x48, 0x49, /* OLVE THI */ $0050: 0x53, 0x20, 0x46, 0x49, 0x4C, 0x45, 0x22, 0x3A, /* S FILE": */ $0058: 0x89, 0x31, 0x30, 0x00, 0x00, 0x00, /* .10... */
Dies entspricht folgendem BASIC Programm:
10 POKE53280,0:POKE53281,0:POKE646,PEEK(162): PRINT"<CLS><DOWN><DOWN><DOWN><DOWN><DOWN><DOWN><DOWN><DOWN>": PRINT" USE LYNX TO DISSOLVE THIS FILE": GOTO10
Darauf folgt ein <CR> und die Anzahl der Blöcke im Directory im ASCII Format mit Leerzeichen auf beiden Seiten. Für ein Directory mit nur einem Block wäre das:
$005E: 0x0D, 0x20, 0x31, 0x20, 0x20 /* . 1 */
Dahinter befindet sich nun die "Signatur" des Archives, ein Text in CBM Kleinbuchstaben (ASCII Großbuchstaben) der mit <CR> abgeschlossen ist und das Lynx Archiv beschreibt. Üblicherweise enthält die Signatur den String 'LYNX'.
Power64 verwendet:
$0063: 0x2A, 0x4C, 0x59, 0x4E, 0x58, 0x20, 0x41, 0x52, /* *LYNX AR */ $006B: 0x43, 0x48, 0x49, 0x56, 0x45, 0x20, 0x42, 0x59, /* CHIVE BY */ $0073: 0x20, 0x50, 0x4F, 0x57, 0x45, 0x52, 0x36, 0x34 /* POWER64 */ $007B: 0x0D
Nun folgt die Anzahl der Dateien im Lynx Archiv. Auch hier wird die Zahl in ASCII mit Leerzeichen vorne und hinten und einem abschließenden <CR> angegeben. Für ein Directory mit 3 Dateien wäre das:
$007C: 0x20, 0x33, 0x20, 0x0D /* 3 . */
Nach diesen Headern folgt nun das eigentliche Directory. Jede Datei wird über ihren Dateinamen (in PETASCII, oft (aber nicht immer!) mit Shift-Space auf 16 Zeichen aufgefüllt), gefolgt von der Größe der Datei (plus 2 Byte für die Ladeadresse) in Blöcken zu 254 Byte, dem Dateityp (P, S, R oder U) und der Anzahl der benutzten Byte im letzten Block. Wenn es sich um eine relative Datei handelt folgt nun noch die Anzahl der Byte pro Record (Relative Dateien werden von Power64 nicht unterstützt). Jede Komponente wird mit <CR> abgeschlossen. Zahlen sind vorne und hinten von Leerzeichen umgeben.
Damit ist der Directoryeintrag für eine Datei abgeschlossen und es folgt der Eintrag für die nächste Datei.
Ein mögliches Beispiel wäre etwa:
Datei 1: "Block Out", PRG, 2+2519 Byte (2521 = (10-1) * 254 + 235) $0080: 0x42, 0x4C, 0x4F, 0x43, 0x4B, 0x20, 0x4F, 0x55, /* BLOCK OU */ $0088: 0x54, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, /* T */ $0090: 0x0D, 0x20, 0x31, 0x30, 0x20, 0x0D, 0x50, 0x0D, /* . 10 .P. */ $0098: 0x20, 0x32, 0x33, 0x35, 0x20, 0x0D, /* 235 . */ Datei 2: "Serpentine", PRG, 2+8703 Byte (8705 = (35-1) * 254 + 69) $009E: 0x53, 0x45, 0x42, 0x4C, 0x45, 0x4E, 0x54, 0x49, /* SERPENTI */ $00A6: 0x4E, 0x45, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, /* NE */ $00AE: 0x0D, 0x20, 0x33, 0x35, 0x20, 0x0D, 0x50, 0x0D, /* . 35 .P. */ $00B6: 0x20, 0x36, 0x39, 0x20, 0x0D, /* 69 . */ Datei 3: "Quadromania", PRG, 2+7056 Byte (7058 = (28-1) * 254 + 200) $00BB: 0x51, 0x55, 0x41, 0x44, 0x52, 0x4F, 0x4D, 0x41, /* QUADROMA */ $00C3: 0x4E, 0x49, 0x41, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, /* NIA */ $00CB: 0x0D, 0x20, 0x32, 0x38, 0x20, 0x0D, 0x50, 0x0D, /* . 28 .P. */ $00D3: 0x20, 0x32, 0x30, 0x30, 0x20, 0x0D, /* 200 . */
Jede Datei, und auch das Directory umfaßt ein Vielfaches von 254 Byte. Wenn die tatsächliche Information weniger Platz benötigt entsteht ein ungenutzter Bereich, der in Power64 mit 0x00 gefüllt ist (andere Implementierungen nutzen andere Werte).
Auf das Directory folgen die eigentlichen Dateien. Jede besteht aus 2 Byte Load-Adresse und dem Dateiinhalt.
Im obigen Beispiel besteht das Directory nur aus einem einzigen Block. Daher beginnt das erste Programm (Block Out) bei Offset 1*254. Das zweite Programm (Serpentine) beginnt bei Offset (1+10)*254 und Quadromania beginnt bei (1+10+35)*254.
$00FE: 0x01, 0x08, 0x26, 0x08, 0xC1, 0x07, 0x9E, 0x32, /* ..&.i..2 */ $0106: 0x30, 0x38, 0x38, 0x3A, 0x12, 0x42, 0x4C, 0x4F, /* 088:.BLO */ $011E: 0x43, 0x4B, 0xAB, 0x4F, 0x55, 0x54, ... /* CK-OUT.. */ ... /* (1+10) * 254 = $0AEA */ $0AEA: 0x01, 0x08, 0x26, 0x08, 0xC1, 0x07, 0x9E, 0x32, /* ..&.¡..2 */ $0AF2: 0x30, 0x38, 0x38, 0x3A, 0x12, 0x53, 0x45, 0x52, /* 088:.SER */ $0AFA: 0x50, 0x45, 0x4E, 0x54, 0x49, 0x4E, 0x45,... /* PENTINE */ ...
E.9 *.T64 Tape Image Format |
Das *.T64 Dateiformat ist ein sehr elegantes (aber früher leider sehr schlecht dokumentiertes) Dateiformat, daß von Miha Peternell für C64S entworfen wurde. Wie ein Disk Image kann es mehrere logische Dateien enthalten (ein großer Vorteil wenn es darum geht den Überblick über viele Dateien zu behalten). Gleichzeitig ist der Verwaltungs- aufwand, sehr gering. Da ein *.T64 auch einen magischen Header enthält ist es für einen Emulator leicht möglich ungültige Dateien zu erkennen.
typedef struct { TapeHeader Header; /* $0000 - $003F */ TapeEntry Entry[MaxFiles]; /* $0040 - ($03FF) */ Byte Data[n]; /* ($0400) - ... */ } T64File;
Header | - Allgemeine Informationen; Enthält einen magischen String um ein *.T64 zu erkennen, die Zahl der Dateien auf dem Band etc. (Details: siehe unten) |
Entry | - Tape Directory; Die Liste aller auf dem Band gespeicherten Dateien. Der Wert von MaxFiles ist im Header gespeichert, üblicherweise ist es 30. (Details: siehe unten) |
Data | - Die eigentlichen Daten der Programme |
typedef struct { Byte TapeDescr[32]; /* $00 - $1F */ Byte Version[2]; /* $20 - $21 */ Byte MaxFiles[2]; /* $22 - $23 */ Byte CurrFiles[2]; /* $24 - $25 */ Byte Reserved[2]; /* $26 - $27 */ Byte UserDescr[24]; /* $28 - $3F */ } TapeHeader;
TapeDescr | - Magischer String: "C64 tape image file"; mit $00 aufgefüllt Anmerkung: Der String muß nicht wortwörtlich so vorhanden sein. Man sollte nach den Substrings "C64" und "tape" suchen. Vorsicht: TAP Images verwenden den String: C64-TAPE-RAW der ebenfalls die Substrings "C64" und "TAPE" enthält. |
Version | - Tape Version 1.0 = {$00, $01} |
MaxFiles | - Anzahl der Plätze im Tape Directory. Es gibt leider einige Emulatoren, die sich darauf verlassen daß es exakt 30 Einträge gibt. Daher verwenden auch alle von Power64 erzeugten T64 Dateien (mindestens) diesen Wert. = {$1E, $00} |
CurrFiles | - Anzahl der Dateien auf dem Tape. Diese Zahl darf MaxFiles nicht übersteigen. Einige Emulatoren können nur das erste auf einem *.T64 gespeicherte Programm lesen (etwa VICE). |
Reserved | - Immer $00. |
UsedDescr | - Benutzerbeschreibung des Tape. (CBM Zeichensatz) |
typedef struct { Byte EntryUsed; /* $00 */ Byte FileType; /* $01 */ Byte StartAddr[2]; /* $02 - $03 */ Byte EndAddr[2]; /* $04 - $05 */ Byte ReservedA[2]; /* $06 - $07 */ Byte TapePos[4]; /* $08 - $0B */ Byte ReservedB[4]; /* $0C - $0F */ Byte FileName[16]; /* $10 - $1F */ } TapeEntry;
EntryUsed | - Ist dieser Eintrag im *.T64 Directory belegt? (0..Nein, 1..Ja) |
FileType | - Es gibt sehr wenig Dokumentation über die Verwendung dieses Feldes.
Power64 folgt der Konvention von Star Commander das sich an den Typ-Identifiern von Floppys orientiert: $82..Verschiebbares Prg, $81..Datenfile Erste Versionen von Power64 implementierten folgende Bedeutung: 0..Verschiebbares Prg., 1..Nicht verschiebbares Prg., 2..Daten File Um Fehlermeldungen zu vermeiden, werden alle anderen Werte ebenfalls als verschiebbare Programme behandelt. |
StartAddr | - Start des Speicherbereiches im C64 Speicher. (Low/High) |
EndAddr | - Ende des Speicherbereiches im C64 Speicher. (Low/High) |
ReservedA | - Immer $00 |
TapePos | - Offset des Dateiinhalts vom Anfang der Tape-Image Datei. |
ReservedB | - Immer $00 |
FileName | - Dateiname (CBM Zeichensatz) |
Alle Multi-Byte Werte in *.T64 Dateien sind im Little-Endian Format (Low/High) gespeichert, wie es allgemein vom C64 genutzt wird.
Es gibt sehr wenig offizielle Dokumentation zum *.T64 Dateiformat. Was wenige verfügbare Wissen wurde durch intensives Untersuchen bestehender *.T64 Dateien gewonnen. Leider wurde das oft nicht mit der erforderlichen Sorgfalt gemacht, so daß nun vile *.T64 Dateien im Internet kursieren, die sich nicht an das beschriebene Datenformat halten. So ist es beispielsweise häufig, daß die Differenz von StartAddr und EndAddr nicht der Länge des Datenbereichs der *.T64 Datei entspricht. Power64 versucht solche Inkonsistenzen automatisch zu erkennen und zu beheben.
Wie bereits oben erwähnt verlassen sich manche Emulatoren darauf, daß das Directory genau 30 Einträge (= 1 KByte inkl. Header) umfaßt. Gleichzeitig sind sie aber nur in der Lage die erste Datei zu laden :(. Bitte bedenken Sie diese Beschränkungen anderer Emulatoren wenn Sie planen Commodore 64 Dateien öffentlich (etwa im Web oder auf CD-ROM) zur Verfügung zu stellen.
E.10 *.TAP (*.RAW) Tape Image Format |
Das *.TAP (oder (seltener) *.RAW) Dateiformat stellt ein sehr genaues Abbild einer C64 Daten-Kasette dar. Durch die hohe Präzision und Detailtreue der Daten lassen sich auf diese Weise sehr viele Tricks, die mit der Datasette möglich waren, abbilden und auch Fastloader stellen kein Problem dar. Der Nachteil des *.TAP Formates liegt im großen Speicherbedarf (mindestens 8x, real 10-12x, maximal 45x im Vergleich zu einem T64 Image) und in der geringen Ladegeschwindigkeit (mit den Original C64 ROM Routinen max. 110 Byte/Sekunde Spitze, durch doppelte Aufzeichnung zur Fehlerkorrektur und Leerraum auf dem Band aber nur 50 Byte/Sekunde Schnitt; mit speziellen Turboloadern ca. 200-500 Byte/Sekunde).
Die Information auf einem C64 Band wird durch einzelne Impulse codiert, wobei der zeitliche Abstand zwischen zwei aufeinander folgenden Impulsen die eigentliche Nutzinformation trägt. Eben diese zeitlichen Abstände werden in einer *.TAP Datei gespeichert.
typedef struct { Byte TAPMagic[12]; /* $00 - $0B */ Byte Version; /* $0C */ Byte Reserved[3]; /* $0D - $0F */ Byte Size[4]; /* $10 - $13 */ Byte Data[Size]; /* $14 - ... */ } TAPFile;
TAPMagic | - Magische Konstante "C64-TAPE-RAW" = {$43, $36, $34, $2D, $54, $41, $50, $45, $2D, $52, $41, $57} |
Version | - $00 oder $01: Bedeutung siehe Data |
Reserved | - Immer $00 |
Size | - Größe der *.TAP Datei im Little-Endian-Format exklusive des Headers. |
Data | - Die zeitlichen Abstände zwischen zwei Impulsen auf dem Band. Die Werte $01-$FF geben dabei direkt die Dauer eines Abstandes an, gemessen als Vielfaches der Dauer von 8 CPU Zyklen. (z.B. der Wert $2E bedeutet, daß zwischen zwei Impulsen 8*$2E = $180 Taktzyklen der CPU verstreichen). Der Wert $00 hat eine Sonderstellung. In Version $00 des TAP Formates bedeutet er einfach eine beliebige Zeit von mehr als 8*$FF Taktzyklen. In Version $01 des TAP Formates geben die 3 folgenden Byte (im Little-Endian-Format) an wie viele Zyklen (ohne Faktor 8!) bis zum folgenden Impuls verstreichen. |
Die Original C64 ROM Routinen verwenden ein reichlich ineffizientes, aber gut fehlerkorrigierendes Verfahren um Daten zu kodieren. Für jedes Byte Nutzdaten wird ein Startbit, die 8 Nutzbits (LSB zuerst, MSB zuletzt) und ein Parity Bit (so, daß die Gesamtzahl der '1'-Bits (Nutzbits+Paritybit) ungerade wird) gespeichert. Das Startbit besteht aus einem extra langen Intervall (4T) gefolgt von einem langen Intervall (3T). '0' Datenbits bestehen aus einem kurzen Intervall (2T) gefolgt von einem langen Intervall (3T). '1' Bits bestehen aus einem langen Intervall (3T) gefolgt von einem kurzen Intervall (2T). Dabei ist 'T' Basis Zeitkonstante, die in etwa den Wert von 8*$16 = $B0 CPU Takten hat. Der konkrete Wert kann auch in kurzer Zeit sehr stark schwanken, da die Laufruhe der Datasette nie optimal ist. Die ROM Routinen versuchen über einen Bandgeschwindigkeitsparameter, der laufend aktulalisiert, wird eine eindeutige Erkennung der Intervalllänge sicher zu stellen. Ein extra langes Intervall (4T) gefolgt von einem kurzen Intervall (2T) kennzeichnet das Ende eines Datenblocks.
Effizienzabschätzung: Für ein Byte Nutzdaten benötigt man als (4+3)+9*(3+2) = 52T. Das sind 52 * 8 * $16 = 9152 CPU Zyklen, womit sich eine Performance der Datasette von 110 Byte/Sekunde ergibt. Da zur Fehlerkorrektur jeder Datenblock zweifach gespeichert wird und zwischen den Blöcken leere Bereiche sind, bleibt von diesem Wert aber nur die Hälfte als Nutzleistung übrig (ca. 50 Byte/Sekunde). Andererseits benötigt ein Nutzbyte also 2+9*2 Intervalle = 20 Intervalle die in je einem Byte codiert sind. Dadurch daß zur Fehlerkorrektur alle Daten doppelt gespeichert werden, sind also 40 Byte TAP File / Nutzbyte erforderlich. Zusätzlich enthalten viele TAP Files noch Lücken zwischen genutzen Bereichen, was die Dateigröße weiter nach oben treibt.
Die verschiedenen Turboloader speichern ihre Daten in jeweils proprietären Formaten. Im allgemeinen gewinnen sie Effizienz indem sie pro Nutzbit nur ein Intervall verwenden, die Intervalllänge reduzieren, auf Startbits und Parity verzichten und den gesamten Datensatz nur ein einziges mal lesen. So geht zwar viel Datensicherheit verloren, aber die Lesegeschwindigkeit erhöht sich auf 200-500 Byte/Sekunde. Gleichzeitig sinkt die Größe der TAP Datei auf 8 Byte *.TAP Datei pro Byte Nutzdaten.
Die Commodore 64 Tape Info Central bietet unter http://www.geocities.com/SiliconValley/Platform/8224/c64tape/index.html detailierte Informationen über eine Vielzahl von Tape Formaten.
Wenn Sie versuchen wollen Ihre alten Schätze von Kassette auf den Mac zu übertragen, sollten Sie die Commodore 64 Tape Transfer FAQ lesen die unter http://www.geocities.com/SiliconValley/Platform/8224/c64tape/faq.html verfügbar ist. Dort finden Sie auch Links zu (DOS/Windows-, leider nicht Mac-) Tools die WAV-Dateien in TAPs oder T64-Tape Images wandeln.
E.11 *.CRT ROM Modul Format |
Das *.CRT ROM Modul Format ist sehr flexibel und kann eine große Zahl verschiedener Modultypen kompakt beschreiben. Gleichzeitig ist es einfach zu lesen, verfügt über einen gut erkennbaren MagicString und benötigt es nur wenig Verwaltungsaufwand.
Grundsätzlich besteht eine CRT Datei aus einem Cartridge-Header, der das Modul allgemein beschreibt, gefolgt von einem oder mehreren Chip Paketen, die jeweils den Inhalt eines ROM Bausteines auf der Erweiterungsplatine wiedergeben.
Alle Multibyte-Werte sind im Big-Endian Format gespeichert.
typedef struct { Byte CartrigeMagic[16]; /* $00 - $0F */ Long HeaderSize; /* $10 - $13 */ Word Version; /* $14 - $15 */ Word HardwareType; /* $16 - $17 */ Byte ExROM_Line /* $18 */ Byte Game_Line /* $19 */ Byte Unused[6]; /* $1A - $1F */ Byte CartridgeName[32]; /* $20 - $3F */ } CartridgeHeader;
CartridgeMagic | - Magische Konstante 'C64 CARTRIDGE ' = {$43, $36, $34, $20, $43, $41, $52, $54, $52, $49, $44, $47, $45, $20, $20, $20} |
HeaderSize | - sizeof(CartridgeHeader) = 64 = {$00, $00, $00, $40} |
Version | - CRT Version 1.0 = {$01, $00} |
HardwareType | - Es gibt eine Vielzahl verschiedener Cartridgetypen, die zusätzlich zu dem (von den C64 Designer geplanten ROM) auch noch RAM und/oder andere log. Funktionen bieten. Oft wurden diese Designs nur für ein einziges Produkt verwendet, sodaß der Aufwand der Emulation nicht lohnt. Power64 unterstützt die Typen 0, 4, 5, 7, 8, 11 und 12 und deckt damit das gesamte Repertoire an Spielen ab. 0 - Normal cartridge 1 - Action Replay 2 - KCS Power Cartridge 3 - Final Cartridge III 4 - Simons Basic 5 - Ocean type 1 (256 und 128 KByte) 6 - Expert Cartridge 7 - Fun Play 8 - Super Games 9 - Atomic Power 10 - Epyx Fastload 11 - Westermann 12 - Rex 13 - Final Cartridge I 14 - Magic Formel |
ExROM_Line | - Pegel der ExROM Leitung zur Speicherkonfiguration |
Game_Line | - Pegel der Game Leitung zur Speicherkonfiguration |
CartridgeName | - Ausführlicher Name des Cartridges (Null-terminierter String) |
typedef struct { Byte ChipMagic[4]; /* 0x00 - 0x03 */ Long PacketLength; /* 0x04 - 0x07 */ Word ChipType; /* 0x08 - 0x09 */ Word Bank; /* 0x0A - 0x0B */ Word Address; /* 0x0C - 0x0D */ Word Length; /* 0x0E - 0x0F */ Byte Data[Length]; /* 0x10 - ... */ } ChipPacket;
ChipMagic | - Magische Konstante 'CHIP' = {$43, $48, $49, $50} |
PacketLength | - Größe des ChipPacket (= Length + 0x10) |
ChipType | - 0..ROM, 1..RAM (kein Data Feld), 2..EEPROM |
Bank | - für Multi-Bank Cartridges (für normale Cartridges: 0) |
Address | - 'Lade'-Adresse des ROM Bausteins (i.a. $8000 oder $A000) |
Length | - Größe des ROM Bausteins (i.a. $1000 oder $2000 oder $4000) |
Data | - Inhalt des ROM |
Beispiel: Simons Basic Modul
$0000: 0x43, 0x36, 0x34, 0x20, 0x43, 0x41, 0x52, 0x54, /* C64 CART */ $0008: 0x52, 0x49, 0x44, 0x47, 0x45, 0x20, 0x20, 0x20, /* RIDGE */ $0010: 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x04, /* ...@.... */ $0018: 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ........ */ $0020: 0x53, 0x69, 0x6D, 0x6F, 0x6E, 0x27, 0x73, 0x20, /* Simon's */ $0028: 0x42, 0x61, 0x73, 0x69, 0x63, 0x00, 0x00, 0x04, /* Basic... */ $0030: 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ........ */ $0038: 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ........ */ $0040: 0x43, 0x48, 0x49, 0x50, 0x00, 0x00, 0x20, 0x10, /* CHIP.. . */ $0048: 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x20, 0x00 /* ...... . */ $0050: .... $2000 Byte for Bank 0: $8000 - $9FFF .... $2050: 0x43, 0x48, 0x49, 0x50, 0x00, 0x00, 0x20, 0x10, /* CHIP.. . */ $2058: 0x00, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x20, 0x00 /* ...... . */ $2060: .... $2000 Byte for Bank 0: $A000 - $BFFF .... $4060: End of File
E.12 RAM Snapshot File |
Das Format für RAM Snapshots ist eine adaptierte Version eines Vorschlags den Jouko Valta für den VICE-Emulator unter Unix entworfen hat. Die ursprüngliche Beschreibung die auch die Bedeutung der einzelnen Felder in mehr Detail beschreibt, findet sich unter:http://stekt.oulu.fi:/~jopi/x64/RAM-format
typedef struct { Byte MagicCode[6]; /* $00 - $05 */ Byte ReservedA[2]; /* $06 - $07 */ Byte Version[2]; /* $08 - $09 */ Byte EmulatorID; /* $0A */ Byte SnapType; /* $0B */ Byte RAMStart[2]; /* $0C - $0D */ Byte RAMSizeKB[2]; /* $0E - $0F */ Byte REUSizeKB[2]; /* $10 - $11 */ Byte ReservedB[14]; /* $12 - $1F */ Byte Config[6]; /* $20 - $25 */ Byte IORAM; /* $26 */ Byte CPURestore; /* $27 */ Byte PCHigh, PCLow; /* $28 - $29 */ Byte RegA, RegX, RegY; /* $2A - 2C */ Byte RegFlags, RegSP; /* $2D - $2E */ Byte IOPort; /* $2F */ Byte Z80_CPU[16]; /* $30 - $3F */ Byte REC[16]; /* $40 - $4F */ Byte OSVersion; /* $50 */ Byte OSPatch; /* $51 */ Byte PatchID[2]; /* $52 - $53 */ Byte ReservedC[44]; /* $54 - $7F */ Byte Custom[128]; /* $80 - $FF */ Byte RAM[65536]; Byte IOArea[4096]; Byte REU[REUSize]; FloppyState Floppy1541[FloppyCnt]; } Snapshot;
Alle Multi-Byte Werte sind im Big-Endian Format (High/Low) gespeichert, und nicht in dem auf dem C64 verbreiteten Little-Endian Format (Low/High).
MagicCode | - Magische Konstante 'CBM'64 = {$43, $42, $4D, $64, $00, $00} |
ReservedA | - Immer {$00, $00} |
Version | - Haupt- und Neben-Versionsnummer 1.0 = {$01, $00} |
EmulatorID | - Power64 verwendet: '/' = $2F |
SnapType | - RAM Snapshot = $00 |
RAMStart | - Offset zum RAM Image in der Datei. Der Header belegt momentan 256 Byte. Somit ist RAMStart = {$01, $00}. |
RAMSizeKB | - Größe des RAM Images in KByte (nicht Byte!) = {$00, $40} |
REUSizeKB | - Größe der RAM Expansion Unit in KByte |
ReservedB | - Immer $00 |
Config | - Reserviert für die Konfigurations Bitmaske - zur Zeit: $00 |
IORAM | - Bit 2/1: VDC RAM verfügbar (00..Nein, 01..16 KB, 10..64 KB, 11..32KB) Bit 0: I/O Daten verfügbar (0..Nein, 1..Ja) Power64 verwendet: $01 |
CPURestore | - CPU(s) verfügbare: (Bit 0: M6502, Bit 1: Z80, Bit 2: REC) = $01 |
PCHigh, PCLow | - M6502 Program Counter |
RegA, RegX, RegY, RegFlags, RegSP | - M6502 Register |
IOPort | - 6502 CPU I/O Latch |
Z80_CPU | - Reserviert für den internen Zustand der Z80 (nicht genutzt) |
REC | - Reserviert für den internen Zustand der REC (nicht genutzt) |
OSVersion | - C64 Kernal ID Byte (nicht genutzt) |
OSPatch | - Kernal Patch ID für Erweiterungen (Fastloader etc.) (nicht genutzt) |
PatchID | - Patch Versionsinformation (nicht genutzt) |
ReservedC | - Kernal Versionsinformation (nicht genutzt) |
Custom | - Information über Peripheriegeräte (nicht genutzt) |
RAM | - Inhalt des RAM |
IOArea | - Inhalt des I/O-Bereiches (VIC, SID, ColorRAM, CIA1/2, REU) Jeder Chip wird nur einmal gespeichert, obwohl er im Adressraum des C64 mehrfach gespiegelt ist. Die Lücken sind mit Power64 interner Zustandsinformation und viel $00 gefüllt. |
REU | - Inhalt des RAM der REU, soweit vorhanden. 0, 128, 256 or 512 KByte - wie in REUSizeKB angegeben. |
Floppy1541 | - Der interne Zustand jeder vollständig emulierten C1541 Floppy.
typedef struct { Byte MagicCode[6]; /* $00 - $05 ('CBM',$15, $41, $00) */ Byte ReservedA[2]; /* $06 - $07 */ Byte Version[2]; /* $08 - $09 */ Byte EmulatorID; /* $0A */ Byte SnapType; /* $0B */ Byte RAMStart[2]; /* $0C - $0D */ Byte RAMSizeKB[2]; /* $0E - $0F */ Byte REUSizeKB[2]; /* $10 - $11 */ Byte ReservedB[14]; /* $12 - $1F */ Byte Config[6]; /* $20 - $25 */ Byte IORAM; /* $26 */ Byte CPURestore; /* $27 */ Byte PCHigh, PCLow; /* $28 - $29 */ Byte RegA, RegX, RegY; /* $2A - 2C */ Byte RegFlags, RegSP; /* $2D - $2E */ Byte ReservedC[81]; /* $2F - $7F */ Byte Custom[128]; /* $80 - $FF */ Byte RAM[2048]; Byte IOArea[512]; } FloppyState; |
E.13 Commodore64 Audio File (reSID Sound File) |
Das Commodore64 Audio File Format wurde von Roland Lieger für Power64 und reSID entwickelt. Es basiert auf einer Idee von Richard Bannister für Frodo, wurde aber im Sinne von Flexibilität und Platzersparnis verbessert.
Alle Multi-Byte Werte sind im Big-Endian Format (High/Low) gespeichert, und nicht in dem auf dem C64 verbreiteten Little-Endian Format (Low/High).
typedef struct { Byte MagicString[24]; /* $00 - $17 */ /* -> "Commodore64 Audio File\n\0" */ Word Version; /* $18 - $19 */ Word HeaderSize; /* $1A - $1B */ Byte EmulatorID; /* $1C */ Byte Reserved; /* $1D */ Word EmulatorVersion; /* $1E - $1F */ Word PrivHeaderSize; /* $20 - $21 */ } GlobalHeader; typedef struct { Long SystemClockSpeed; /* $22 - $26 */ Word ClockGrain; /* $27 - $28 */ } SystemHeader; typedef struct { GlobalHeader GlobalHdr; SystemHeader SystemHdr; Byte PrivateHdr[PrivHeaderSize]; Byte InitialSIDState[25]; Byte Reserved[7]; Byte SIDCommands[...]; Byte EOFMarker[3]; } C64_Audio_File;
MagicString | - "Commodore64 Audio File\n\0" |
Version | - Haupt- und Neben-Versionsnummer 1.0 = {$01, $00} |
HeaderSize | - Größe des SystemHeader (zur Zeit 6 Byte = {$00, $06}, zusätzliche Felder können in zukünftigen Versionen hinzugefügt werden) |
EmulatorID | - Power64 verwendet: '/' = $2F |
EmulatorVersion | - Haupt- und Nebenversionsnummer des erstellenden Emulators. |
PrivHeaderSize | - Größe des privaten Datenbereiches des Emulators. |
SystemClockSpeed | - Systemtaktrate: 982800Hz für PAL, 1021800Hz für NTSC |
ClockGrain | - Zeitliche Granularität des Tonaufzeichnung gemessen in Systemtaktschlägen. Power64 verwendet eine zeitliche Auflösung von einer Rasterzeile (63 Takte für PAL, 65 Takte für NTSC). Dies bietet hinreichende Genauigkeit und hält die Dateigröße gering. Verwenden Sie nicht unüberlegt eine Granularität von 1 Zyklus nur weil es technisch möglich ist. Sie werden keinen Unterschied hören! |
InitialSIDState | - Inhalt der 25 beschreibbaren SID Register zu Beginn der Aufzeichnung. | |
SIDCommands | -
Dies ist eine Abfolge von 2 und 3 Byte langen Befehlen. Jeder Befehl besteht aus einer Verzögerung (gemessen in Vielfachen von ClockGrain) und üblicherweise einem Schreibzugriff auf eines der SID Register. Das erste Byte eines solchen Befehls enthält die Dauer der Verzögerung (Bits 7..5) und die Wahl des SID Registers (Bits 4..0). Wenn die Dauer der Verzögerung im Bereich 0..5 liegt, so gibt sie die Anzahl der ClockGrains an die der SID ohne äußere Beeinflußung Musik erzeugt. Wenn die Verzögerung den Wert 6 annimmt, so enthält das nächste Byte eines ExtDelay Wert, und es kommt zu einer Verzögerung von 6+ExtDelay ClockGrains. Wenn die Verzögerung den Wert 7 annimmt, so enthält das nächste Byte eines ExtDelay Wert, und es kommt zu einer Verzögerung von 6+256+ExtDelay ClockGrains. ExtDelay muß dabei kleiner oder gleich 254 sein, so daß die maximale Verzögerung 516 Clockgrains entspricht (fast zwei ganze NTSC Frames, wenn ein ClockGrain einer Rasterzeile entspricht). Der ExtDelay Wert von 255 ist reserviert als Dateiendemarkierung. Wenn im ersten Befehlsbyte ein SID-Register aus dem Bereich 0x00..0x18 gewählt wurde, so enthält das nächste Byte (evt. nach dem ExtDelay Byte), den Wert der in das SID Register geschrieben werden soll. Der SID-Register Bereich 0x19..0x1E ist reserviert und darf nicht benutzt werden. Die Wahl des SID-Register 0x1F bedeutet das kein Schreibzugriff auf ein SID Register stattfindet. Das ist erforderlich wenn zwischen zwei Schreibzugriffen auf SID Register eine Verzögerung von mehr als 516 ClockGrains benötigt wird. | |
EOFMarker | - Dateiendemarkierung = {$FF, $FF, $FF} |
Quelle: http://www.salto.at/Power64/Documentation/Power64-LiesMich/AE-Dateiformate.html Power64 Homepage: http://www.infinite-loop.at und http://www.salto.at - EMail: © Roland Lieger, Goethegasse 39, A-2340 Mödling, Österreich Letzte Änderung: 29. Feb. 2008 |