Date 1er jet: 14/11/2008.
Date de dernière mise à jour: 20/11/2008.
1) Introduction
Si vous êtes un fan d'apple II, vous aurez
nécessairement rencontré le cas de figure
suivant: après avoir passé du temps
à convertir votre collection de disquettes 5,25" (non
protégées) en images
disque au format .dsk grâce à FASTDSK,
vous avez
constaté avec effroi voire énervement
que lorsque vous lancez des programmes avec votre
émulateur préféré, ils se
plantent (que ce soit dès le boot ou
après une page de
présentation d'un cracker).
Pourtant, vous n'aviez rencontré aucun problème
lors de la création du .dsk et ces programmes se dupliquent
sans soucis depuis votre Apple II avec un utilitaire type Locksmith
Fast Copy, ce qui atteste
à priori que les disquettes sont formattées en 16
secteurs par piste (le format standard).
Alors d'où vient le problème?
Nous avons vu dans un autre article que quelques logiciels
peuvent avoir certains secteurs de données
encodés en 4-4 au lieu de 6-2. Mais ce cas de
figure est marginal. Par contre il y a un cas
d'incompatibilité beaucoup plus
fréquent: l'usage du numéro de volume.
En effet, lorsque vous formattez vous-même sur votre machine
physique Apple II une disquette sous
DOS 3.3, vous pouvez spécifier un numéro de
volume compris de 1 à 254 pour "identifier" votre disquette.
Par exemple, vous pouvez taper: INIT HELLO, D1, V55 pour formatter la
disquette présente dans le drive 1 avec un numéro
de volume à 55.
Cette information de numéro de volume est ensuite
rappelé à chaque fois que vous tapez la
commande CATALOG.
Il faut savoir plusieurs choses:
- Tout d'abord le volume par défaut a la valeur
254 (quand
vous tapez juste INIT HELLO, D1 par exemple).
- Le format d'image disk .dsk ne conserve pas cette
information de
volume. Ce qui veut dire que lorsque vous créez un .dsk, le
volume de vos disquettes n'est pas mémorisé et
lorsque vous mettez un .dsk dans votre émulateur, ce dernier
considère d'office qu'il s'agit d'une disquette avec un
volume = 254!
- Lorsque les éditeurs de logiciels ont sortis des
jeux sur
plusieurs disquettes (ce qui est souvent le cas pour les jeux
d'aventure, par exemple pour la série des
softs graphiques signés Sierra),
bien souvent ils
ont utilisé le
numéro de volume comme identifiant de face. Donc
à un moment, le soft recherche soit la face courante soit
vous demande d'insérer un certain disque et il ne trouvera
jamais la face attendue... d'où blocage sur cette face ou
même plantage des fois.
Alors que faut-il faire en 1er quand vous avez un crash avec un .dsk
pour savoir s'il s'agit de ce cas de figure?
Pour être sur que c'est bien ce problème de volume
qui fait planter le .dsk, vous devez contrôler la valeur du
numéro de volume sur chaque face du jeu (sur vos disquettes
physiques
Apple II évidemment).
Il existe plusieurs utilitaires capables d'inspecter les champs
d'adresses où sont stockées ces informations, par
exemple Disk Fixer, CIA, Trax
de Bag Of Tricks, etc... Il
suffit de lire une piste au hasard et de relever le volume (normalement
le volume est unique pour toute la face).
Voici par exemple 2 écrans avec Disk
Fixer (qui indique le numéro de volume en hexa
en haut après Track et Sector). J'ai lu le secteur $00 de la
piste $01.
Le 1er écran indique un volume $FE (c'est à dire
254 en décimal, soit un volume normal).
Le 2nd (la face Front du disk 6 du jeu Gold
Rush! de Sierra)
quant
à lui indique $0B. Il y a toutes les "chances" pour
que le .dsk ne soit pas reconnu par le jeu qui ne fonctionnera
pas correctement.
Pour résoudre le problème, certains se bornent
à répondre qu'il faut
utiliser le logiciel SST
pour créer des images disque au
format .nib depuis votre Apple II.
Le problème, c'est que créer un .nib avec SST
demande du temps; le process est long et puis cela signifie que tous
les .dsk que vous avez créés et qui ne "passent"
pas sont bons pour le poubelle.
Personnellement, je ne suis pas d'accord avec ce point de vue.
Je pense qu'il est plus simple de créer des .dsk PUIS
ENSUITE rajouter le numéro de volume sur votre machine
moderne.
Quand on fait un tour sur le forum comp.sys.apple2,
différentes solutions sont proposées pour
convertir un .dsk en .nib mais aucune ne me parait
idéale.
J'ai donc décidé de faire un petit programme pour
répondre à ce besoin spécifique.
2) Le programme dsk2nib
Pour que ce programme soit compatible avec le plus de configurations
possibles, il a été écrit en langage
java.
Afin de ne pas perdre de temps à ré-inventer la
roue, j'ai repiqué le coeur du programme, c'est à
dire la routine de nibblizing des datas (transformation des 256 octets
de données d'un secteur en nibbles disk). Je me suis servi
dans l'émulateur Applelet écrit par
Steven E.
Hugg (un émulateur d'Apple ][+ sous forme
d'applet java).
UTILISATION:
Placez le petit fichier dsk2nib.jar dans le répertoire
contenant les .dsk à traiter et tapez:
java -jar dsk2nib.jar <volume> <liste de vos .dsk
à traiter>
Le programme ne modifie pas les .dsk, il les utilise pour
créer des fichiers .nib (à raison d'un .nib par
.dsk).
Il faut bien entendu avoir le droit en écriture sur le
répertoire et en lecture sur les .dsk!!!
Quelques exemples:
Pour affecter le volume 1 aux disks a.dsk et b.dsk:
java -jar
dsk2nib.jar 1 a.dsk b.dsk
Pour affecter un volume normal aux .dsk du répertoire:
java
-jar dsk2nib.jar 254 *.dsk
Pour affecter un volume 55 aux disks dont le nom contient VOL55:
java
-jar dsk2nib.jar 55 *VOL55*.dsk
PRINCIPE:
Dans un .dsk, chaque piste ne contient que les données,
à savoir 16 secteurs * 256 octets = 4096 octets ($1000).
Une piste d'un .nib contient $1A00 octets.
Un .dsk et un .nib contiennent tous les 2 le même nombre de
piste: 35 (de la piste 0 à la piste 34).
On remplit chaque piste du .nib à partir d'une piste du .dsk.
Chaque piste du .nib est alimentée de la
façon suivante:
a) 16 blocs correspondant chacun à un secteur du
.dsk. Chaque bloc a la structure suivante:
- 40 octets de synchro (valeur = $FF)
- 3 octets pour le marker de début du champ adresse
( D5 AA 96)
- 8 octets pour l'encodage en 4+4 du champ adresse (3 informations:
piste/secteur/volume à raison de 2 octets par information)
- 3 octets pour le marker de fin du champ adresse ( DE AA EB)
- 6 octets de synchro (valeur = $FF)
- 3 octets pour le marker de début du champ data (D5 AA AD)
- 343 octets encodés en 6+2 pour les données du
secteur de la piste traitée du .dsk
- 3 octets pour le marker de fin du champ data (DE AA EB)
Soit un total de 409 octets par bloc.
Pour ces 16 blocs, la place occupée est de 16 * 409
octets = 6544 octets ($1990)
b) Puis on complète avec 112 octets à $FF pour
finir la piste.
TELECHARGEMENT / DOWNLOAD
|
java 1.5
|
Download dsk2nib.jar (class)
|
|
java 1.5
|
Download
dsk2nib_src.jar (sources)
|
Bon, et bien si avec tout ça votre soft ne fonctionne
toujours pas, il ne reste plus qu'à aller faire un tour
ailleurs... (sans fumer de préférence!!)
3) Tests sous Windows XP
Illustration sous XP pour créer des .nib avec le volume 1
à partir de 2 fichiers .dsk.
Utilisation de caractères * dans le nom
spécifié pour faciliter la sélection
sans tout taper. Ici on ne sélectionne que les faces "Front"
des "Disk1".
java -jar dsk2nib.jar 1 *Disk1_F*.dsk
On fait la même chose pour les autres faces en prenant bien
soin d'affecter les bons numéros de volume.
On vérifie que les.nib passent avec AppleWin
sous XP.
Pour Space Quest 1:
Pour Space Quest 2:
Autre test sous XP: on créé des .nib avec un
volume normal (254) pour tous les .dsk présents dans le
répertoire.
Cela peut par exemple être utile pour les utilisateurs de la
carte Pseudo-Disk ][ d'Alex
Freed qui exploite des .nib (mais je n'ai
pas essayé de mon côté):
Il n'y a que 2 .dsk dans le répertoire.
java -jar dsk2nib.jar 254 *.dsk
Sélection du ProDOS.nib
généré et exécution avec
Applewin:
Ecran de travail sous Eclipse
(version Ganymède) avec mon
eeepc
connecté à un vieil écran 15 pouces:
4) Tests sur mac intel OS X
Le programme étant écrit en java et sans
particularité, il doit tourner aussi sur mon imac.
Il faut faire attention à la version du runtime java
présent sur sa machine (controlez en tapant java
-version).
A la base, j'avais créé le programme avec comme
cible un jre 1.6.0_07 présent sur mon eeepc.
Mais sur mon mac, j'en étais encore à la version
1.5.0_16-133.
Du coup, j'ai du recréer un dsk2nib.jar pour jre 1.5.0_06
pour être tranquille (le programme tourne ainsi sur mes 2
machines).
Si vous avez une version antérieure, vous aurez droit
à un plantage similaire à celui-là:
Récupérez une version plus récente sur
le site de Sun ou
utilisez le source fourni pour faire votre propre jar.
Comme pour XP, test avec les faces Front des disks 1 de Police Quest et
King's Quest IV:
java -jar dsk2nib.jar 1 *Disk1_F*.dsk
Il y a bien création de 2 fichiers .nib.
On recommence pour les autres faces en faisant volume+1 à
chaque fois et tests des jeux avec Virtual
][.
Dans les vestiaires/chiottes de Police
Quest:
Au début de la quête de King's
Quest IV:
5) Tests avec les jeux d'aventure Sierra
Une fois n'est pas coutume, voici réunies dans cette section
des copies d'écran de la série
complète des jeux d'aventure produits par la
société américaine Sierra
On-Line Inc.
King's Quest : Quest for the Crown
King's Quest II: Romancing the Throne
King's Quest III: To Heir is Human
King's Quest IV: The Perils of Rosella
Police Quest: In Pursuit of the Death Angel
Space Quest: The Sarien Encounter
Space Quest II : Vohaul's Revenge
Leisure Suit Larry in the Land of the Loundge Lizards
Mixed-up Mother Goose
Gold Rush!
Manhunter: New York (yes, the 8 bits version, not the IIGS version!!!)
The Black Cauldron
The Dark Crystal
6) Compléments
PARAMETRES
N'ayant pas accès à ma collection de disquettes,
j'ai demandé à mon ami Thry2
de regarder les
numéros de volume de ses propres softs, ce qui l'a
obligé pendant une heure à ne pas jouer
à Warcraft II!!! Au
passage, si vous êtes adepte de ce jeu en
réseau, je vous DECONSEILLE de lancer une partie
contre le pseudo Ouin
ou
OuinOuin: il totalise plus de
13.000 parties (oui vous avez bien lu).
Aussi si vous ne voulez pas vous prendre une paté et/ou vous
faire traiter de "Gros Noob", vous voila prévenu ;-) Avec
tout ça, on n'est pas prêt de revoir le site
www.apple2forever.net de sitôt...
Ce contrôle a permis de mettre en évidence une
anomalie de face dans ses disquettes (pour Gold
Rush!), ce qui est
déjà ça...
Les parametres de volume (pour les quelques cracks
spécifiés):
King's Quest I: french crack (14/01/85)
by
Eric Irq & The Wildman (ABC = Association of Broadcasting
Crackers)
--------------
Side A = 1,
Side B = 2,
Side C = 3
Kings Quest II: french crack (01/02/86) by Lockbuster & Binary
Digit (LCB = Laser Crack Band) >>> Thry2
-------------- french crack (17/12/85) by Chris (Copyart)
>>> Deckard
Disk 1
Disk 2 Disk 3
Front
1
3 5
Back
2
4
Kings Quest III: french crack (27/06/88) by Godfather & Steff
---------------
Disk 1
Disk 2
Disk 3 Disk 4 Disk 5
Front
1
3 5
7
9
Back
2
4 6
8
10
King's Quest IV: french crack (14/05/1989) by Loockheed (TBT = The
Brain Trust)
---------------
Disk 1
Disk 2
Disk 3 Disk 4 Disk 5 Disk 6
Disk 7 Disk 8
Front
1
3 5
7
9 11
13 15
Back
2
4 6
8
10 12
14 16
Space Quest I: french crack (??/??/????) by Softpatch (avec les
explications de Lot)
>>> Thry2
------------- unsigned
>>> Deckard
Disk 1
Disk 2
Disk 3 Disk 4
Front
1
3 5
7
Back
2
4 6
8
Space Quest II: french crack (04/05/1988) by Gerard & Goldpom
--------------
Disk 1
Disk 2
Disk 3 Disk 4
Front
1
3 5
7
Back
2
4 6
8
Leisure Suit Larry: french crack (??/??/????) by HackerForce (HF)
>>>
Thry2
------------------ cracked (12/05/1987) by The Boy! (COD =
Circle Of Deneb / BBS: The Lost City) >>> Deckard
Disk 1 Disk 2
Disk 3
Front
1
3 5
Back
2
4
Police Quest: The game was unprotected (distributed by The Brain Trust)
------------
Disk 1
Disk 2 Disk 3 Disk 4
Front
1
3 5
7
Back
2
4 6
8
Mixed-up Mother Goose: The game was unprotected (distributed by The
Brain Trust)
---------------------
Disk
1 Disk 2
Front
1
3
Back
2
4
Gold Rush!: french crack (18/05/1990) by LoGo (TOD = The Thieves Of
Destiny) presentation #1 >>>
Thry2
---------- french crack (18/05/1990) by ??? (TOD =
The Thieves Of Destiny) presentation #2
>>> Deckard
Disk 1
Disk 2 Disk 3 Disk 4 Disk 5
Disk 6 Disk 7 Disk 8
Front
1
3 5
7
9 11
13 15
Back
2
4 6
8
10 12
14 16
Manhunter: New York: french crack (20/06/1990) by LoGo (TOD =
The Thieves Of Destiny)
-------------------
Disk
1 Disk 2 Disk 3
Disk 4 Disk 5 Disk 6
Front
1
3 5
7
9 11
Back
2
4 6
8
10 12
The Black Cauldron: unsigned
------------------
Disk
1 Disk 2
Disk 3
Front
1
3 5
Back
2
4
The Dark Crystal: french crack (13/03/1983) by Aldo Reset (CCB
= Clean Crack Band)
----------------
Disk 1
Disk 2
Front
254 4
Back
3
5
Je complèterai cette liste à fur et à
mesure pour tous les autres jeux ne fonctionnant pas en .dsk.
POUR RESTER EN DSK
Il n'y a pas eu que les jeux à adopter des
numéros de volume spéciaux.
Ce fut aussi le cas par exemple pour la série des disquettes
vendues avec le magazine Tremplin Micro.
Chaque disquette avait comme numéro de volume le
numéro du magazine (tout au moins la face en DOS 3.3).
Lorsqu'on lance un .dsk de ces disquettes, on obtient le beau plantage
suivant:
Quand le DOS est standard, il est plus intéressant
à mon sens de garder la disquette sous forme d'un .dsk que
de passer en un .nib.
Il faut alors modifier le DOS présent sur chaque disquette
pour qu'il accepte ce numéro de volume standard 254.
Pour les Tremplin Micro,
bien que le DOS ait été
trafiqué (il affiche un "MicroBasic" et reste
extrèmement lent), il utilise tout de
même une RWTS classique.
Plutôt que de rechercher toutes les tables de
paramètres utilisées lors des appels de RWTS, le
plus simple, c'est encore de modifier cette routine pour
forcer un bon résultat du contrôle du
numéro de volume.
Pour cela, je vous propose 2 patchs:
D'abord sur la routine de controle basique:
Au lieu de faire un LDA (IOBPL),Y avec Y=3 pour recherche le
numéro de volume demandé (ici le
numéro du magazine), on force un LDA #$0 pour avoir toujours
un volume 0.
Puis au cas où sur la routine de formattage:
Même modif.
Ca marche à ce que j'ai pu voir pour quelques
numéros testés. J'ai essayé
quelques points de menu aussi...
La seule différence "visible" par rapport à la
version initiale, c'est quand on fait un catalog.
On a volume 254 au lieu du numéro du magazine:
Le patch avec un éditeur de secteurs (seulement 4 octets
à changer ):
Track $00 Sector $08:
octets $12/$13 avant=B1 48. Le patch: mettre A9 00.
octets $B1/$B2 avant=B1 48. Le patch: mettre A9 00.
ASTUCE
Il y a un truc intéressant à savoir si par
exemple vous avez récupéré un .dsk sur
internet et qu'il ne fonctionne pas car il faut un numéro de
volume spécial.
Quand vous avez créé une disquette physique avec
ce .dsk, vous l'avez probablement formatté avec le volume
standard 254.
Et bien est il possible de changer le
numéro de volume sans devoir tout reformatter?
Oui, l'outil INIT de Bag Of Tricks permet de
modifier le
numéro de
volume sans altérer les données. Il suffit juste
de laisser l'option "PRESERVE DATA" à YES.
Dans les écrans ci-dessous, un exemple pour forcer un
numéro de volume = 55 (soit $37 en hexadécimal)
et le contrôle final avec Disk
Fixer:
7) Les sources de dsk2nib
Source dsk2nib.java:
import java.io.File;
/*
* dsk2nib
*
* Deckard 20081114
* Utilise la routine de nibblizing de l'applet Applelet
(émulateur Apple II java)
*
*/
public class dsk2nib {
/**
* @param args
*/
public static void main(String[] args) {
// Si pas
assez de paramètres, rappel de la syntaxe de la commande
if
(args.length < 2) {
System.err.println("Usage: java -jar
dsk2nib.jar volume[1-254] .dsk file(s)");
return;
}
//
Vérifie que le volume renseigné est
numérique
if(!isNumeric(args[0])) {
System.err.println("volume must be
numeric!");
return;
}
// Volume
à affecter
int vol =
Integer.valueOf(args[0]).intValue();
//
Vérifie fourchette de validité
if (vol
< 1 || vol > 254) {
System.err.println("Bad volume!");
return;
}
// Traite
les .dsk les 1 après les autres
// A
noter que si vous tapez *.dsk ou nom*.dsk, il y aura autant de parms
que de fichiers matchés
for (int
i = 1; i < args.length; i++) {
File f = new File(args[i]);
// Vérifie que le .dsk est
correct
if (isDSKOkay(f)) {
//
Construit le nom du .nib à créer
String
nameNIB = new String(f.getName().substring(0, f.getName().length()
-3).concat("nib") );
//
Initialise le .nib en mémoire
NIBImage
myNIB = new NIBImage();
//
Alimentation depuis le .dsk
myNIB.fillNIBWithDSK(f.getName(), vol);
//
Sauvegarde du .nib
myNIB.saveNIB(nameNIB);
}
}
System.out.println("FINISHED!");
}
/**
* Test la
numéricité d'une chaine
*
* @param String zoneTest (non
null)
* @return Boolean result
*/
public static boolean isNumeric(String
zoneTest) {
//
Décompose dans un tableau de char
final
char[] chars = zoneTest.toCharArray();
// Teste
chaque caractère
for (int
x = 0; x < chars.length; x++)
{
final char c = chars[x];
if ((c >= '0') && (c
<= '9')) continue; // numeric
return false;
}
return true;
}
/**
* Test un .dsk
*
* @param File f (non null)
* @return Boolean result
*/
public static boolean isDSKOkay(File f) {
//
Vérifie que c'est bien un .dsk
if
(!f.getName().toLowerCase().endsWith(".dsk")) {
System.err.println(f.getName() + " is
not a .dsk file!");
return false;
}
//
Vérifie que le nom de .dsk donné n'est pas bidon
try {
if (!f.exists()) {
System.err.println("Can't find " + f.getName() + "!");
return
false;
}
} catch
(SecurityException x) {
System.err.println("Security err
(exists)!");
}
//
Vérifie la taille du .dsk
try {
if (f.length() != 143360) {
System.err.println(f.getName() + " has a bad length!");
return
false;
}
} catch
(SecurityException x) {
System.err.println("Security err
(length)!");
}
// OK
return
true;
}
}
Source NIBImage.java:
import java.io.FileInputStream;
import java.io.DataInputStream;
import java.io.FileOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public class NIBImage
{
// Contenu du .nib
public byte data[][];
// Nombre de pistes par face de disquette
static int NUM_TRACKS = 35;
// Nombre de secteurs par piste d'un .dsk
static int NUM_SECTORS = 16;
// Taille d'une piste du .nib
static int TRACK_SIZE = 0x1a00;
// Nombre de nibbles correspondant à 1
secteur complet du .dsk
static int SECTOR_SIZE = 409;
public NIBImage()
{
// Init l'espace de
stockage
data = new
byte[NUM_TRACKS][TRACK_SIZE];
}
/*
* Alimentation du
contenu du .nib en mémoire à partir du .dsk
*/
void fillNIBWithDSK(String filename, int
volume) {
System.out.println("Loading disk image " + filename);
try {
FileInputStream imgFile = new
FileInputStream(filename);
DataInputStream s = new
DataInputStream(imgFile);
// Buffer contenant une piste du .dsk
// 1 piste = 16 secteurs * 256 octets =
4096 = $1000
byte buf[] = new byte[0x1000];
// Lecture 1 à 1 des pistes
du .dsk
for (int trk = 0; trk <
NUM_TRACKS; trk++) {
// Stocke
la piste lue dans le buffer buf
s.readFully(buf, 0, 0x1000);
//
Alimente la piste équivalente du .nib
this.data[trk] = NIBImage.nibblizeTrack(volume, trk, buf);
}
} catch
(IOException e) {
System.err.println("Could not load disk
image");
}
}
/*
* Sauvegarde du
contenu du .nib en mémoire
*/
void saveNIB(String filename) {
System.out.println("Saving new disk image " + filename);
try {
FileOutputStream imgFile = new
FileOutputStream(filename);
DataOutputStream s = new
DataOutputStream(imgFile);
// Ecriture 1 à 1 des pistes
du .nib
for (int trk = 0; trk <
NUM_TRACKS; trk++) {
s.write(this.data[trk], 0, TRACK_SIZE);
}
} catch
(IOException e) {
System.err.println("Could not save new
disk image");
}
}
/*
* Nibblize les 16 secteurs
d'une piste de .dsk
*/
public static byte[] nibblizeTrack(int
vol, int trk, byte in[]) {
// Buffer
1 piste du .nib
byte
out[] = new byte[TRACK_SIZE];
//
Pointeur dans le buffer de piste
int
out_pos = 0;
//
Remplit le buffer de piste avec les 16 secteurs nibblizés
for (int
sector = 0; sector < NUM_SECTORS; sector++) {
nibblizeSector(vol, trk, sector, in,
skewing_table[sector] << 8,
out, out_pos);
// Positionne après le
secteur traité
out_pos += SECTOR_SIZE;
}
//
Complète avec des $FF le restant de la piste
while
(out_pos < TRACK_SIZE)
out[out_pos++] = (byte) (0xff);
return
out;
}
/*
* Encode a 256-byte sector as
SECTOR_SIZE disk bytes as follows:
*
* 40 sync
bytes
: FF FF ...
* 3
address header bytes : D5 AA 96
* 8
address block bytes
* 3
address trailer bytes : DE AA EB
* 6 sync
bytes
: FF FF FF FF FF FF
* 3 data
header bytes : D5 AA
AD
* 343 data block bytes
* 3 data
trailer bytes : DE AA EB
* ===
* 409
bytes
(409 * 16 sectors = $1990)
*/
public static void nibblizeSector(int
vol, int trk, int sector, byte in[],
int in_ofs, byte out[], int i) {
int loop,
checksum, prev_value, value;
int
sector_buffer[] = new int[258];
value = 0;
/*
* Step 1: write 40 sync bytes (0xff's).
*/
for (loop
= 0; loop < 40; loop++)
out[i++] = (byte) 0xff;
/*
* Step 2: write the 3-byte address header (0xd5 0xaa 0x96).
*/
out[i++]
= (byte) 0xd5;
out[i++]
= (byte) 0xaa;
out[i++]
= (byte) 0x96;
/*
* Step 3: write the address block. Use 4-and-4 encoding to
convert the
* volume, track and sector and checksum into 2 disk bytes
each. The
* checksum is a simple exclusive OR of the first three values.
*/
out[i++]
= (byte) ((vol >> 1) | 0xaa);
out[i++]
= (byte) (vol | 0xaa);
checksum
= vol;
out[i++]
= (byte) ((trk >> 1) | 0xaa);
out[i++]
= (byte) (trk | 0xaa);
checksum
^= trk;
out[i++]
= (byte) ((sector >> 1) | 0xaa);
out[i++]
= (byte) (sector | 0xaa);
checksum
^= sector;
out[i++]
= (byte) ((checksum >> 1) | 0xaa);
out[i++]
= (byte) (checksum | 0xaa);
/*
* Step 4: write the 3-byte address trailer (0xde 0xaa 0xeb).
*/
out[i++]
= (byte) (0xde);
out[i++]
= (byte) (0xaa);
out[i++]
= (byte) (0xeb);
/*
* Step 5: write another 6 sync bytes.
*/
for (loop
= 0; loop < 6; loop++)
out[i++] = (byte) (0xff);
/*
* Step 6: write the 3-byte data header.
*/
out[i++]
= (byte) (0xd5);
out[i++]
= (byte) (0xaa);
out[i++]
= (byte) (0xad);
/*
* Step 7: read the next 256-byte sector from the old disk
image file,
* and add two zero bytes to bring the number of bytes up to a
multiple
* of 3.
*/
for (loop
= 0; loop < 256; loop++)
sector_buffer[loop] = in[loop + in_ofs]
& 0xff;
sector_buffer[256] = 0;
sector_buffer[257] = 0;
/*
* Step 8: write the first 86 disk bytes of the data block,
which
* encodes the bottom two bits of each sector byte into
six-bit values
* as follows:
*
* disk byte n, bit 0 = sector byte n, bit 1 disk byte n, bit
1 = sector
* byte n, bit 0 disk byte n, bit 2 = sector byte n + 86, bit
1 disk
* byte n, bit 3 = sector byte n + 86, bit 0 disk byte n, bit
4 = sector
* byte n + 172, bit 1 disk byte n, bit 5 = sector byte n +
172, bit 0
*
* The scheme allows each pair of bits to be shifted to the
right out of
* the disk byte, then shifted to the left into the sector
byte.
*
* Before the 6-bit value is translated to a disk byte, it is
exclusive
* ORed with the previous 6-bit value, hence the values
written are
* really a running checksum.
*/
prev_value = 0;
for (loop
= 0; loop < 86; loop++) {
value = (sector_buffer[loop] &
0x01) << 1;
value |= (sector_buffer[loop] &
0x02) >> 1;
value |= (sector_buffer[loop + 86]
& 0x01) << 3;
value |= (sector_buffer[loop + 86]
& 0x02) << 1;
value |= (sector_buffer[loop + 172]
& 0x01) << 5;
value |= (sector_buffer[loop + 172]
& 0x02) << 3;
out[i++] = (byte)
(byte_translation[value ^ prev_value]);
prev_value = value;
}
/*
* Step 9: write the last 256 disk bytes of the data block,
which
* encodes the top six bits of each sector byte. Again, each
value is
* exclusive ORed with the previous value to create a running
checksum
* (the first value is exclusive ORed with the last value of
the
* previous step).
*/
for (loop
= 0; loop < 256; loop++) {
value = (sector_buffer[loop]
>> 2);
out[i++] = (byte)
(byte_translation[value ^ prev_value]);
prev_value = value;
}
/*
* Step 10: write the last value as the checksum.
*/
out[i++]
= (byte) (byte_translation[value]);
/*
* Step 11: write the 3-byte data trailer.
*/
out[i++]
= (byte) (0xde);
out[i++]
= (byte) (0xaa);
out[i++]
= (byte) (0xeb);
}
// Normal byte (lower six bits only)
-> disk byte translation table.
static int byte_translation[] = {
0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e,
0x9f, 0xa6,
0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf,
0xb2, 0xb3,
0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba,
0xbb, 0xbc,
0xbd, 0xbe, 0xbf, 0xcb, 0xcd, 0xce,
0xcf, 0xd3,
0xd6, 0xd7, 0xd9, 0xda, 0xdb, 0xdc,
0xdd, 0xde,
0xdf, 0xe5, 0xe6, 0xe7, 0xe9, 0xea,
0xeb, 0xec,
0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4,
0xf5, 0xf6,
0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd,
0xfe, 0xff };
// Sector skewing table.
static int skewing_table[] = {
0, 7, 14,
6, 13, 5, 12, 4, 11, 3, 10, 2, 9, 1, 8, 15 };
}