|
|
|
Dieser Anhang beschreibt die verbreiteten Dateiformate im Bereich der Commodore VC-20 Emulation. Er richtet sich an interessierte Programmierer, die sich für die interne Funktionsweise der Emulation interessieren. Wenn Sie mit Power20 nur Ihre alten Programme nutzen wollen, brauchen Sie sich um diese Details nicht sorgen.
Power20 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 VC-20 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 VC-20 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 VC-20 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 VC-20 Daten auf Diskette oder Band speichert.
typedef struct { Byte LoadAddrLow, LoadAddrHigh; /* $00 - $01 */ Byte PrgData[n]; /* $02 - ... */ } C64File;
LoadAddr | - Jene Adresse im VC-20 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 *.ROM, *.CRT, *.x000, *.x0 ROM Module |
Es existieren zwei Dateiformate für ROM Module, die nur durch ihre Dateilänge unterschieden werden können.
Wenn die Dateilänge ein Vielfaches von 256 Byte (üblicherweise ein Vielfaches on 1 KByte) ist, dann handelt es sich um eine einfaches Abbild der ROMs. Die richtige Ladeadresse muß sich in diesem Fall aus dem Dateinamen und seiner Extension ergeben (z.B. muß PacMan.A000 an die Adresse A000 geladen werden).
Andernfalls ist das Dateiformat ähnlich wie bei dem *.PRG Dateien. Zwei Bytes Ladeadresse plus ROM Abbild (normalerweise ein Vielfaches von 1 KByte). Dieses Dateiformat ist eindeutig das bessere von diesen beiden, da die Datei nicht dadurch zerstört werden kann, das sie umbenannt (oder auf die 8+3 Namenskonvention von DOS beschnitten) wurde.
E.4 *.X64 Floppy Disk Image Format |
Das *.X64 Format ist ein sehr flexibles Format für Disk Image Dateien, das eine Vielzahl von Diskettenlaufwerken unterstützt. Die Implementation in Power20 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 Power20 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.5 *.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.6 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, die zur VC-20 Emulation verwendet werden haben kein Problem eine 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 Änderung: 25. April 2002
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 Power20 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) beschrieben, 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.7 *.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 (C64-) 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 C64-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'.
Power20 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, 0x32, 0x30 /* POWER20 */ $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.8 *.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 VC-20 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. Power20 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 VC-20 Dateien öffentlich (etwa im Web oder auf CD-ROM) zur Verfügung zu stellen.
E.9 *.TAP (*.RAW) Tape Image Format |
Das *.TAP (oder (seltener) *.RAW) Dateiformat stellt ein sehr genaues Abbild einer VC-20 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 VC-20 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 VC-20 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 VC-20 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.10 S20-RAM Snapshot Format |
Das S20-Format für RAM Snapshots stammt von einem Vorschlag ab, den Arne Bockholdt für Pfau Zeh auf DOS Systemen entwickelt hat. Die Original Dokumentation, die auch die Bedeutung der einzelnen Felder im Detail erklärt, ist unter folgender Adresse verfügbar: http://www.classicgaming.com/pfauzeh/s20spec.htm
S20 ist zur Zeit das einzige RAM Snapshot Format, daß von Power20 unterstützt wird. Wenn Sie ernsthaft an VC-20 Snapshots interessiert sind, sollten Sie wissen, daß es zur Zeit eine Diskussion über ein einheitliches VC-20 Snapshot Format gibt, daß dann (hoffentlich) von allen Emulatoren unterstützt wird. Das wird NICHT S20 sein.
Quelle: http://www.salto.at/Power20/Documentation/Power20-LiesMich/AE-Dateiformate.html Power20 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 |