Voici un petit utilitaire pour convertir un fichier basic en texte.
Vous trouverez la liste de tokens ici : https://www.bento8.fr/?p=54
package fr.bento8.to8.basic;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
/**
* @author Benoît Rousseau
* @version 1.0
*
*/
public class BasicConverter {
byte[] basBytes;
int fileSize;
public static byte[][] keywords = {
{0x45 ,0x4e ,0x44}, // 0x80 END
{0x46 ,0x4f ,0x52}, // 0x81 FOR
{0x4e ,0x45 ,0x58 ,0x54}, // 0x82 NEXT
{0x44 ,0x41 ,0x54 ,0x41}, // 0x83 DATA
{0x44 ,0x49 ,0x4d}, // 0x84 DIM
{0x52 ,0x45 ,0x41 ,0x44}, // 0x85 READ
{0x4c ,0x45 ,0x54}, // 0x86 LET
{0x47 ,0x4f}, // 0x87 GO
{0x52 ,0x55 ,0x4e}, // 0x88 RUN
{0x49 ,0x46}, // 0x89 IF
{0x52 ,0x45 ,0x53 ,0x54 ,0x4f ,0x52 ,0x45}, // 0x8A RESTORE
{0x52 ,0x45 ,0x54 ,0x55 ,0x52 ,0x4e}, // 0x8B RETURN
{0x52 ,0x45 ,0x4d}, // 0x8C REM
{0x27}, // 0x8D '
{0x53 ,0x54 ,0x4f ,0x50}, // 0x8E STOP
{0x45 ,0x4c ,0x53 ,0x45}, // 0x8F ELSE
{0x54 ,0x52 ,0x4f ,0x4e}, // 0x90 TRON
{0x54 ,0x52 ,0x4f ,0x46 ,0x46}, // 0x91 TROFF
{0x44 ,0x45 ,0x46 ,0x53 ,0x54 ,0x52}, // 0x92 DEFSTR
{0x44 ,0x45 ,0x46 ,0x49 ,0x4e ,0x54}, // 0x93 DEFINT
{0x44 ,0x45 ,0x46 ,0x53 ,0x4e ,0x47}, // 0x94 DEFSNG
{0x44 ,0x45 ,0x46 ,0x44 ,0x42 ,0x4c}, // 0x95 DEFDBL
{0x4f ,0x4e}, // 0x96 ON
{0x57 ,0x41 ,0x49 ,0x54}, // 0x97 WAIT
{0x45 ,0x52 ,0x52 ,0x4f ,0x52}, // 0x98 ERROR
{0x52 ,0x45 ,0x53 ,0x55 ,0x4d ,0x45}, // 0x99 RESUME
{0x41 ,0x55 ,0x54 ,0x4f}, // 0x9A AUTO
{0x44 ,0x45 ,0x4c ,0x45 ,0x54 ,0x45}, // 0x9B DELETE
{0x4c ,0x4f ,0x43 ,0x41 ,0x54 ,0x45}, // 0x9C LOCATE
{0x43 ,0x4c ,0x53}, // 0x9D CLS
{0x43 ,0x4f ,0x4e ,0x53 ,0x4f ,0x4c ,0x45}, // 0x9E CONSOLE
{0x50 ,0x53 ,0x45 ,0x54}, // 0x9F PSET
{0x4d ,0x4f ,0x54 ,0x4f ,0x52}, // 0xA0 MOTOR
{0x53 ,0x4b ,0x49 ,0x50 ,0x46}, // 0xA1 SKIPF
{0x45 ,0x58 ,0x45 ,0x43}, // 0xA2 EXEC
{0x42 ,0x45 ,0x45 ,0x50}, // 0xA3 BEEP
{0x43 ,0x4f ,0x4c ,0x4f ,0x52}, // 0xA4 COLOR
{0x4c ,0x49 ,0x4e ,0x45}, // 0xA5 LINE
{0x42 ,0x4f ,0x58}, // 0xA6 BOX
{0x55 ,0x4e ,0x4d ,0x41 ,0x53 ,0x4b}, // 0xA7 UNMASK
{0x41 ,0x54 ,0x54 ,0x52 ,0x42}, // 0xA8 ATTRB
{0x44 ,0x45 ,0x46}, // 0xA9 DEF
{0x50 ,0x4f ,0x4b ,0x45}, // 0xAA POKE
{0x50 ,0x52 ,0x49 ,0x4e ,0x54}, // 0xAB PRINT
{0x43 ,0x4f ,0x4e ,0x54}, // 0xAC CONT
{0x4c ,0x49 ,0x53 ,0x54}, // 0xAD LIST
{0x43 ,0x4c ,0x45 ,0x41 ,0x52}, // 0xAE CLEAR
{0x49 ,0x4e ,0x54 ,0x45 ,0x52 ,0x56 ,0x41 ,0x4c}, // 0xAF INTERVAL
{0x4b ,0x45 ,0x59}, // 0xB0 KEY
{0x4e ,0x45 ,0x57}, // 0xB1 NEW
{0x53 ,0x41 ,0x56 ,0x45}, // 0xB2 SAVE
{0x4c ,0x4f ,0x41 ,0x44}, // 0xB3 LOAD
{0x4d ,0x45 ,0x52 ,0x47 ,0x45}, // 0xB4 MERGE
{0x4f ,0x50 ,0x45 ,0x4e}, // 0xB5 OPEN
{0x43 ,0x4c ,0x4f ,0x53 ,0x45}, // 0xB6 CLOSE
{0x49 ,0x4e ,0x50 ,0x45 ,0x4e}, // 0xB7 INPEN
{0x50 ,0x45 ,0x4e}, // 0xB8 PEN
{0x50 ,0x4c ,0x41 ,0x59}, // 0xB9 PLAY
{0x54 ,0x41 ,0x42 ,0x28}, // 0xBA TAB(
{0x54 ,0x4f}, // 0xBB TO
{0x53 ,0x55 ,0x42}, // 0xBC SUB
{0x46 ,0x4e}, // 0xBD FN
{0x53 ,0x50 ,0x43 ,0x28}, // 0xBE SPC(
{0x55 ,0x53 ,0x49 ,0x4e ,0x47}, // 0xBF USING
{0x55 ,0x53 ,0x52}, // 0xC0 USR
{0x45 ,0x52 ,0x4c}, // 0xC1 ERL
{0x45 ,0x52 ,0x52}, // 0xC2 ERR
{0x4f ,0x46 ,0x46}, // 0xC3 OFF
{0x54 ,0x48 ,0x45 ,0x4e}, // 0xC4 THEN
{0x4e ,0x4f ,0x54}, // 0xC5 NOT
{0x53 ,0x54 ,0x45 ,0x50}, // 0xC6 STEP
{0x2b}, // 0xC7 +
{0x2d}, // 0xC8 -
{0x2a}, // 0xC9 *
{0x2f}, // 0xCA /
{0x5e}, // 0xCB ^
{0x41 ,0x4e ,0x44}, // 0xCC AND
{0x4f ,0x52}, // 0xCD OR
{0x58 ,0x4f ,0x52}, // 0xCE XOR
{0x45 ,0x51 ,0x56}, // 0xCF EQV
{0x49 ,0x4d ,0x50}, // 0xD0 IMP
{0x4d ,0x4f ,0x44}, // 0xD1 MOD
{0x40}, // 0xD2 @
{0x3e}, // 0xD3 >
{0x3d}, // 0xD4 =
{0x3c}, // 0xD5 <
{0x44 ,0x53 ,0x4b ,0x49 ,0x4e ,0x49}, // 0xD6 DSKINI
{0x44 ,0x53 ,0x4b ,0x4f ,0x24}, // 0xD7 DSKO$
{0x4b ,0x49 ,0x4c ,0x4c}, // 0xD8 KILL
{0x4e ,0x41 ,0x4d ,0x45}, // 0xD9 NAME
{0x46 ,0x49 ,0x45 ,0x4c ,0x44}, // 0xDA FIELD
{0x4c ,0x53 ,0x45 ,0x54}, // 0xDB LSET
{0x52 ,0x53 ,0x45 ,0x54}, // 0xDC RSET
{0x50 ,0x55 ,0x54}, // 0xDD PUT
{0x47 ,0x45 ,0x54}, // 0xDE GET
{0x56 ,0x45 ,0x52 ,0x49 ,0x46 ,0x59}, // 0xDF VERIFY
{0x44 ,0x45 ,0x56 ,0x49 ,0x43 ,0x45}, // 0xE0 DEVICE
{0x44 ,0x49 ,0x52}, // 0xE1 DIR
{0x46 ,0x49 ,0x4c ,0x45 ,0x53}, // 0xE2 FILES
{0x57 ,0x52 ,0x49 ,0x54 ,0x45}, // 0xE3 WRITE
{0x55 ,0x4e ,0x4c ,0x4f ,0x41 ,0x44}, // 0xE4 UNLOAD
{0x42 ,0x41 ,0x43 ,0x4b ,0x55 ,0x50}, // 0xE5 BACKUP
{0x43 ,0x4f ,0x50 ,0x59}, // 0xE6 COPY
{0x43 ,0x49 ,0x52 ,0x43 ,0x4c ,0x45}, // 0xE7 CIRCLE
{0x50 ,0x41 ,0x49 ,0x4e ,0x54}, // 0xE8 PAINT
{0x52 ,0x45 ,0x53 ,0x45 ,0x54}, // 0xE9 RESET
{0x52 ,0x45 ,0x4e ,0x55 ,0x4d}, // 0xEA RENUM
{0x53 ,0x57 ,0x41 ,0x50}, // 0xEB SWAP
{0x2a}, // 0xEC *
{0x57 ,0x49 ,0x4e ,0x44 ,0x4f ,0x57}, // 0xED WINDOW
{0x50 ,0x41 ,0x54 ,0x54 ,0x45 ,0x52 ,0x4e}, // 0xEE PATTERN
{0x44 ,0x4f}, // 0xEF DO
{0x4c ,0x4f ,0x4f ,0x50}, // 0xF0 LOOP
{0x45 ,0x58 ,0x49 ,0x54}, // 0xF1 EXIT
{0x49 ,0x4e ,0x4d ,0x4f ,0x55 ,0x53 ,0x45}, // 0xF2 INMOUSE
{0x4d ,0x4f ,0x55 ,0x53 ,0x45}, // 0xF3 MOUSE
{0x43 ,0x48 ,0x41 ,0x49 ,0x4e}, // 0xF4 CHAIN
{0x43 ,0x4f ,0x4d ,0x4d ,0x4f ,0x4e}, // 0xF5 COMMON
{0x53 ,0x45 ,0x41 ,0x52 ,0x43 ,0x48}, // 0xF6 SEARCH
{0x46 ,0x57 ,0x44}, // 0xF7 FWD
{0x54 ,0x55 ,0x52 ,0x54 ,0x4c ,0x45} // 0xF8 TURTLE
};
public static byte[][] functions = {
{0x53 ,0x47 ,0x4e}, // 0xFF 0x80 SGN
{0x49 ,0x4e ,0x54}, // 0xFF 0x81 INT
{0x41 ,0x42 ,0x53}, // 0xFF 0x82 ABS
{0x46 ,0x52 ,0x45}, // 0xFF 0x83 FRE
{0x53 ,0x51 ,0x52}, // 0xFF 0x84 SQR
{0x4c ,0x4f ,0x47}, // 0xFF 0x85 LOG
{0x45 ,0x58 ,0x50}, // 0xFF 0x86 EXP
{0x43 ,0x4f ,0x53}, // 0xFF 0x87 COS
{0x53 ,0x49 ,0x4e}, // 0xFF 0x88 SIN
{0x54 ,0x41 ,0x4e}, // 0xFF 0x89 TAN
{0x50 ,0x45 ,0x45 ,0x4b}, // 0xFF 0x8A PEEK
{0x4c ,0x45 ,0x4e}, // 0xFF 0x8B LEN
{0x53 ,0x54 ,0x52 ,0x24}, // 0xFF 0x8C STR$
{0x56 ,0x41 ,0x4c}, // 0xFF 0x8D VAL
{0x41 ,0x53 ,0x43}, // 0xFF 0x8E ASC
{0x43 ,0x48 ,0x52 ,0x24}, // 0xFF 0x8F CHR$
{0x45 ,0x4f ,0x46}, // 0xFF 0x90 EOF
{0x43 ,0x49 ,0x4e ,0x54}, // 0xFF 0x91 CINT
{0x43 ,0x53 ,0x4e ,0x47}, // 0xFF 0x92 CSNG
{0x43 ,0x44 ,0x42 ,0x4c}, // 0xFF 0x93 CDBL
{0x46 ,0x49 ,0x58}, // 0xFF 0x94 FIX
{0x48 ,0x45 ,0x58 ,0x24}, // 0xFF 0x95 HEX$
{0x4f ,0x43 ,0x54 ,0x24}, // 0xFF 0x96 OCT$
{0x53 ,0x54 ,0x49 ,0x43 ,0x4b}, // 0xFF 0x97 STICK
{0x53 ,0x54 ,0x52 ,0x49 ,0x47}, // 0xFF 0x98 STRIG
{0x47 ,0x52 ,0x24}, // 0xFF 0x99 GR$
{0x4c ,0x45 ,0x46 ,0x54 ,0x24}, // 0xFF 0x9A LEFT$
{0x52 ,0x49 ,0x47 ,0x48 ,0x54 ,0x24}, // 0xFF 0x9B RIGHT$
{0x4d ,0x49 ,0x44 ,0x24}, // 0xFF 0x9C MID$
{0x49 ,0x4e ,0x53 ,0x54 ,0x52}, // 0xFF 0x9D INSTR
{0x56 ,0x41 ,0x52 ,0x50 ,0x54 ,0x52}, // 0xFF 0x9E VARPTR
{0x52 ,0x4e ,0x44}, // 0xFF 0x9F RND
{0x49 ,0x4e ,0x4b ,0x45 ,0x59 ,0x24}, // 0xFF 0xA0 INKEY$
{0x49 ,0x4e ,0x50 ,0x55 ,0x54}, // 0xFF 0xA1 INPUT
{0x43 ,0x53 ,0x52 ,0x4c ,0x49 ,0x4e}, // 0xFF 0xA2 CSRLIN
{0x50 ,0x4f ,0x49 ,0x4e ,0x54}, // 0xFF 0xA3 POINT
{0x53 ,0x43 ,0x52 ,0x45 ,0x45 ,0x4e}, // 0xFF 0xA4 SCREEN
{0x50 ,0x4f ,0x53}, // 0xFF 0xA5 POS
{0x50 ,0x54 ,0x52 ,0x49 ,0x47}, // 0xFF 0xA6 PTRIG
{0x44 ,0x53 ,0x4b ,0x46}, // 0xFF 0xA7 DSKF
{0x43 ,0x56 ,0x49}, // 0xFF 0xA8 CVI
{0x43 ,0x56 ,0x53}, // 0xFF 0xA9 CVS
{0x43 ,0x56 ,0x44}, // 0xFF 0xAA CVD
{0x4d ,0x4b ,0x49 ,0x24}, // 0xFF 0xAB MKI$
{0x4d ,0x4b ,0x53 ,0x24}, // 0xFF 0xAC MKS$
{0x4d ,0x4b ,0x44 ,0x24}, // 0xFF 0xAD MKD$
{0x4c ,0x4f ,0x43}, // 0xFF 0xAE LOC
{0x4c ,0x4f ,0x46}, // 0xFF 0xAF LOF
{0x53 ,0x50 ,0x41 ,0x43 ,0x45 ,0x24}, // 0xFF 0xB0 SPACE$
{0x53 ,0x54 ,0x52 ,0x49 ,0x4e ,0x47 ,0x24}, // 0xFF 0xB1 STRING$
{0x44 ,0x53 ,0x4b ,0x49 ,0x24}, // 0xFF 0xB2 DSKI$
{0x46 ,0x4b ,0x45 ,0x59 ,0x24}, // 0xFF 0xB3 FKEY$
{0x4d ,0x49 ,0x4e ,0x28}, // 0xFF 0xB4 MIN(
{0x4d ,0x41 ,0x58 ,0x28}, // 0xFF 0xB5 MAX(
{0x41 ,0x54 ,0x4e}, // 0xFF 0xB6 ATN
{0x43 ,0x52 ,0x55 ,0x4e ,0x43 ,0x48 ,0x24}, // 0xFF 0xB7 CRUNCH$
{0x4d ,0x54 ,0x52 ,0x49 ,0x47}, // 0xFF 0xB8 MTRIG
{0x45 ,0x56 ,0x41 ,0x4c}, // 0xFF 0xB9 EVAL
{0x50 ,0x41 ,0x4c ,0x45 ,0x54 ,0x54 ,0x45}, // 0xFF 0xBA PALETTE
{0x42 ,0x41 ,0x4e ,0x4b}, // 0xFF 0xBB BANK
{0x48 ,0x45 ,0x41 ,0x44}, // 0xFF 0xBC HEAD
{0x52 ,0x4f ,0x54}, // 0xFF 0xBD ROT
{0x53 ,0x48 ,0x4f ,0x57}, // 0xFF 0xBE SHOW
{0x5a ,0x4f ,0x4f ,0x4d}, // 0xFF 0xBF ZOOM
{0x54 ,0x52 ,0x41 ,0x43 ,0x45} // 0xFF 0xC0 TRACE
};
public BasicConverter() {
}
public void load(String fileName) {
try {
basBytes = Files.readAllBytes(Paths.get(fileName)); // Lecture de l'en-tête du fichier .bas
byte fileType = basBytes[0];
if (fileType != (byte) 0xff) { // Octet 0 : 0xff (Type du fichier)
throw new IllegalStateException("Type de fichier non reconnu: Le premier octet "+
fileType+" n'est pas 0xff");
}
fileSize = basBytes[1] << 8 & 0xff00 | basBytes[2] & 0xff; // Octet 1-2 : Longueur du fichier
System.out.println("Taille du fichier "+fileName+": "+fileSize+" octets.");
} catch (IOException e) {
e.printStackTrace();
}
}
public byte[] convertToAscii() {
if (basBytes == null) {
return null;
}
byte[] txtBytes = new byte[basBytes.length*8]; // On réserve de l'espace supplémentaire pour le remplacement des codes par les mots clés
int i=3, j=0, k, l; // La première ligne commence a l'index 3
int linelength, lineNumber;
String lineNumberStr;
while (i<basBytes.length-2) { // Lecture ligne à ligne du fichier basic (on ne lit pas les deux octets 0x0000 en fin de fichier)
linelength = basBytes[i++] << 8 & 0xFF00 | basBytes[i++] & 0xFF; // La longueur de la ligne comprend line length et line number
lineNumber = basBytes[i++] << 8 & 0xFF00 | basBytes[i++] & 0xFF;
lineNumberStr = String.valueOf(lineNumber);
System.out.println("Lecture ligne: "+lineNumberStr+" longueur: "+linelength);
for (l = 0; l < lineNumberStr.length(); l++) { // ajout du numéro de ligne
txtBytes[j++] = (byte) lineNumberStr.charAt(l);
}
txtBytes[j++] = (byte) 0x20; // ajout d'un espace
while (basBytes[i] != (byte) 0x00) { // une fin de ligne se termine par la valeur 0x00
if (basBytes[i] == (byte) 0x16) { // caractères accentués
if (basBytes[i+1] == (byte) 0x41) { // - accent grave
if (basBytes[i+2] == (byte) 0x61) {txtBytes[j++] = (byte) 0xe0; i+=2;} // à
else if (basBytes[i+2] == (byte) 0x65) {txtBytes[j++] = (byte) 0xe8; i+=2;} // è
else if (basBytes[i+2] == (byte) 0x75) {txtBytes[j++] = (byte) 0xf9; i+=2;} // ù
} else if (basBytes[i+1] == (byte) 0x42) { // - accent aigu
if (basBytes[i+2] == (byte) 0x65) {txtBytes[j++] = (byte) 0xe9; i+=2;} // é
} else if (basBytes[i+1] == (byte) 0x43) { // - accent circonflexe
if (basBytes[i+2] == (byte) 0x61) {txtBytes[j++] = (byte) 0xe2; i+=2;} // â
else if (basBytes[i+2] == (byte) 0x65) {txtBytes[j++] = (byte) 0xea; i+=2;} // ê
else if (basBytes[i+2] == (byte) 0x69) {txtBytes[j++] = (byte) 0xee; i+=2;} // î
else if (basBytes[i+2] == (byte) 0x6f) {txtBytes[j++] = (byte) 0xf4; i+=2;} // ô
else if (basBytes[i+2] == (byte) 0x75) {txtBytes[j++] = (byte) 0xfb; i+=2;} // û
} else if (basBytes[i+1] == (byte) 0x48) { // - tréma
if (basBytes[i+2] == (byte) 0x65) {txtBytes[j++] = (byte) 0xeb; i+=2;} // ë
else if (basBytes[i+2] == (byte) 0x69) {txtBytes[j++] = (byte) 0xef; i+=2;} // ï
else if (basBytes[i+2] == (byte) 0x6f) {txtBytes[j++] = (byte) 0xf6; i+=2;} // ö
else if (basBytes[i+2] == (byte) 0x75) {txtBytes[j++] = (byte) 0xfc; i+=2;} // ü
} else if (basBytes[i+1] == (byte) 0x4b) { // - cédille
if (basBytes[i+2] == (byte) 0x63) {txtBytes[j++] = (byte) 0xe7; i+=2;} // ç
}
else {
System.out.println("Caractère accentué "+
String.format("0x%02X", basBytes[i])+" "+
String.format("0x%02X", basBytes[i+1])+" "+
String.format("0x%02X", basBytes[i+2])+
" non reconnu.");
i+=3;}
} else if (basBytes[i] == (byte) 0xff) { // Gestion des fonctions Basic
i++; // on se positione après la balise 0xff
if (basBytes[i] <= (byte) 0xc0 && functions[basBytes[i]+128].length > 0) { // test de la valeur la plus haute du tableau de correspondance et vérification de la présence d'une valeur
for (k = 0; k < functions[basBytes[i]+128].length; k++) { // +128 positionne la valeur 0x80 à l'index 0 du tableau
txtBytes[j++] = functions[basBytes[i]+128][k]; // écriture lettre à lettre du nom de fonction
}
} else {
System.out.println("Fonction 0xFF "+
String.format("0x%02X", basBytes[i])+
" non reconnue.");
txtBytes[j++] = basBytes[i];
}
} else if (basBytes[i] < (byte) 0xff) { // Gestion des mots clés Basic
if (basBytes[i] <= (byte) 0xf8 && keywords[basBytes[i]+128].length > 0) { // test de la valeur la plus haute du tableau de correspondance et vérification de la présence d'une valeur
for (k = 0; k < keywords[basBytes[i]+128].length; k++) { // +128 positionne la valeur 0x80 à l'index 0 du tableau
txtBytes[j++] = keywords[basBytes[i]+128][k]; // écriture lettre à lettre du nom de mot clé
}
} else {
System.out.println("Mot clé "+
String.format("0x%02X", basBytes[i])+
" non reconnu.");
txtBytes[j++] = basBytes[i];
}
} else {
txtBytes[j++] = basBytes[i]; // Caractère sans transcodage
}
i++;
}
i++;
txtBytes[j++] = (byte) 0x0d; // Ajout d'un retour à la ligne
txtBytes[j++] = (byte) 0x0a;
}
System.out.println("Conversion terminée.");
return txtBytes;
}
}
package fr.bento8.to8.samples;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import fr.bento8.to8.basic.BasicConverter;
/**
* @author Benoît Rousseau
* @version 1.0
*
*/
public class BasToTxt
{
/**
* Effectue la conversion d'un fichier BASIC (.BAS) TO8 Thomson en fichier Texte
*
* @param args nom du fichier .BAS a convertir
*/
public static void main(String[] args)
{
try {
String basFileName = args[0];
BasicConverter bc = new BasicConverter();
bc.load(basFileName);
byte[] txtBytes = bc.convertToAscii();
Path outputFile = Paths.get(basFileName.substring(0, basFileName.lastIndexOf('.'))+".txt");
Files.deleteIfExists(outputFile);
Files.createFile(outputFile);
Files.write(outputFile, txtBytes);
System.out.println("Ecriture du fichier "+outputFile.getFileName()+" terminée.");
} catch (Exception e) {
e.printStackTrace();
System.out.println(e);
}
}
}