/* * 1541fs.c - 1541-Emulation im Amiga-Dateisystem * * Copyright (C) 1994-1996 by Christian Bauer */ /* * Anmerkungen: * ------------ * * Routinen: * - Die Schnittstelle zu den IEC-Routinen besteht in den Routinen * FS_Init, FS_Exit, FS_Open, FS_Close, FS_Read und FS_Write: * FS_Init bereitet die Emulation vor * FS_Exit beendet die Emulation * FS_Open öffnet einen Kanal * FS_Close schließt einen Kanal * FS_Read liest aus einem Kanal * FS_Write schreibt in einen Kanal * * DriveData: * - lock enthält den Lock des Verzeichnisses, in dem die Emulation * ablaufen soll * * Directory-Emulation: * - Wird das Directory geöffnet (Dateiname "$"), wird in T: eine * temporäre Datei angelegt, die vom Aufbau genau einem 1541-Directory * entspricht und diese Datei geöffnet. Sie kann dann mit den ganz * normalen Lesebefehlen verarbeitet werden. * * Inkompatibilitäten/Verbesserungen: * - Keine Wildcards beim Directory-Laden * - Kein "rohes" Directory-Lesen * - Keine relativen Dateien * - Nur 'I'- und 'UJ'-Befehle implementiert */ #include #include #include #include #include #include "IEC.h" #include "1541fs.h" #include "Display.h" #define CATCOMP_NUMBERS 1 #include "LocStrings.h" // Aus Main.asm extern void ResetC64(void); // Prototypes int open_file(DriveData *drive, int channel, const char *filename); void convert_filename(const char *filename, char *plainname, int *filemode, int *filetype, int *wildflag); void find_first_file(char *name); int open_directory(DriveData *drive, int channel, const char *filename); void close_all_channels(DriveData *drive); void execute_command(DriveData *drive, const char *command); char conv_from_64(char c); char conv_to_64(char c); /** ** Emulation vorbereiten, Lock auf Verzeichnis holen, prefs zeigt auf den ** Preferences-String **/ void FS_Init(DriveData *drive, char *prefs) { int i; if (drive->lock = Lock(prefs,ACCESS_READ)) { for (i=0; i<16; i++) drive->handle[i] = NULL; drive->cmd_length = 0; SetError(drive, ERR_STARTUP); } } /** ** Emulation beenden, Lock auf Verzeichnis freigeben **/ void FS_Exit(DriveData *drive) { if (drive->lock) { close_all_channels(drive); UnLock(drive->lock); drive->lock = NULL; } } /** ** Kanal öffnen, filename ist Null-terminiert **/ int FS_Open(DriveData *drive, int channel, char *filename) { SetError(drive, ERR_OK); // Kanal 15: Dateiname als Befehl ausführen if (channel == 15) { execute_command(drive, filename); return ST_OK; } // Vorige Datei schließen, wenn noch offen if (drive->handle[channel]) { Close(drive->handle[channel]); drive->handle[channel] = NULL; } if (filename[0] == '$') return open_directory(drive, channel, filename); if (filename[0] == '#') { SetError(drive, ERR_NOCHANNEL); return ST_OK; } return open_file(drive, channel, filename); } /* * Datei wird geöffnet */ // Zugriffsmodi enum { FMODE_READ, FMODE_WRITE, FMODE_APPEND }; // Dateitypen enum { FTYPE_PRG, FTYPE_SEQ }; int open_file(DriveData *drive, int channel, const char *filename) { BPTR olddir; BPTR fh = NULL; char plainname[256]; int filemode = FMODE_READ; int filetype = FTYPE_PRG; int wildflag = 0; olddir = CurrentDir(drive->lock); convert_filename(filename, plainname, &filemode, &filetype, &wildflag); // Bei Kanal 0 immer PRG lesen, bei Kanal 1 immer PRG schreiben if (!channel) { filemode = FMODE_READ; filetype = FTYPE_PRG; } if (channel == 1) { filemode = FMODE_WRITE; filetype = FTYPE_PRG; } // Wildcards sind nur beim Lesen erlaubt if (wildflag) { if (filemode != FMODE_READ) { SetError(drive, ERR_SYNTAX33); CurrentDir(olddir); return ST_OK; } find_first_file(plainname); } if (fh = Open(plainname, filemode == FMODE_READ ? MODE_OLDFILE : MODE_NEWFILE)) { switch (filemode) { // Erstes Zeichen lesen, wenn zum Lesen geöffnet case FMODE_READ: drive->read_char = FGetC(fh); break; // Neue Datei anlegen: E-Bit bei sequentieller Datei löschen case FMODE_WRITE: if (filetype == FTYPE_SEQ) { Close(fh); SetProtection(plainname, FIBF_EXECUTE); fh = Open(plainname, filemode == FMODE_READ ? MODE_OLDFILE : MODE_NEWFILE); } else { Close(fh); SetProtection(plainname, 0); fh = Open(plainname, filemode == FMODE_READ ? MODE_OLDFILE : MODE_NEWFILE); } break; // Anhängen: Ans Ende der Datei gehen case FMODE_APPEND: Seek(fh, 0, OFFSET_END); break; } } if (!(drive->handle[channel] = fh)) SetError(drive, ERR_FILENOTFOUND); CurrentDir(olddir); return ST_OK; } /* * Dateibezeichnung analysieren, Dateimodus und -typ ermitteln */ void convert_filename(const char *srcname, char *destname, int *filemode, int *filetype, int *wildflag) { char *p, *q; int i; // Nach ':' suchen, p zeigt auf erstes Zeichen hinter dem ':' if (p = strchr(srcname, ':')) p++; else p = srcname; // Zeichensatz des Reststrings wandeln -> destname q = destname; for (i=0; iap_Info.fib_FileName, NAMEBUF_LENGTH); } MatchEnd(a); FreeMem(a, sizeof(struct AnchorPath)); } } /* * Directory wird geöffnet, temporäre Datei erstellen */ struct FileInfoBlock fib; int open_directory(DriveData *drive, int channel, const char *filename) { BPTR fh; char buf[] = "\001\004\001\001\0\0\022\042 \042 00 2A"; char *p, *q; int i; if (!Examine(drive->lock, &fib)) return ST_OK; if (!(fh = Open("T:Frodo$File", MODE_NEWFILE))) return ST_OK; // Directory-Titel erzeugen und schreiben p = &buf[8]; for (i=0; i<16 && fib.fib_FileName[i]; i++) *p++ = conv_to_64(fib.fib_FileName[i]); Write(fh, buf, 32); // Für jeden Verzeichniseintrag eine Zeile erzeugen und schreiben while (ExNext(drive->lock, &fib)) { // Zeile mit Leerzeichen löschen und mit Nullbyte abschließen memset(buf, ' ', 31); buf[31] = 0; p = buf; *p++ = 0x01; // Dummy-Verkettung *p++ = 0x01; // Größe in Blocks berechnen und eintragen i = (fib.fib_Size + 254) / 254; *p++ = i & 0xff; *p++ = (i >> 8) & 0xff; p++; if (i < 10) p++; // Kleiner als 10: Ein Leerzeichen dazunehmen if (i < 100) p++; // Kleiner als 100: Noch ein Leerzeichen dazunehmen // Dateiname wandeln und eintragen *p++ = '\"'; q = p; for (i=0; i<16 && fib.fib_FileName[i]; i++) if (fib.fib_FileName[i]) *q++ = conv_to_64(fib.fib_FileName[i]); *q++ = '\"'; p += 18; // Typ ermitteln und eintragen if (fib.fib_DirEntryType >= 0) { *p++ = 'D'; *p++ = 'I'; *p++ = 'R'; } else if (fib.fib_Protection & FIBF_EXECUTE) { *p++ = 'S'; *p++ = 'E'; *p++ = 'Q'; } else { *p++ = 'P'; *p++ = 'R'; *p++ = 'G'; } // Datei geschützt? if (fib.fib_Protection & FIBF_DELETE) *p++ = '<'; // Zeile schreiben Write(fh, buf, 32); } // Abschlußzeile Write(fh, "\001\001\0\0BLOCKS FREE. \0\0", 32); Close(fh); // Datei zum Lesen öffnen und das erste Zeichen lesen if (drive->handle[channel] = Open("T:Frodo$File", MODE_OLDFILE)) drive->read_char = FGetC(drive->handle[channel]); return ST_OK; } /** ** Kanal schließen **/ int FS_Close(DriveData *drive, int channel) { if (channel==15) { close_all_channels(drive); return ST_OK; } if (drive->handle[channel]) { Close(drive->handle[channel]); drive->handle[channel] = NULL; } return ST_OK; } /* * Alle Kanäle schließen */ void close_all_channels(DriveData *drive) { int i; for (i=0; i<15; i++) FS_Close(drive, i); drive->cmd_length = 0; } /** ** Ein Byte aus Kanal lesen **/ int FS_Read(DriveData *drive, int channel, char *data) { LONG c; // Kanal 15: Fehlerkanal if (channel == 15) { *data = *drive->error_ptr++; if (*data != '\r') return ST_OK; else { SetError(drive, ERR_OK); return ST_EOF; } } if (!drive->handle[channel]) return ST_READ_TIMEOUT; // Zeichen aus dem Puffer holen und nächstes Zeichen lesen *data = drive->read_char; c = FGetC(drive->handle[channel]); drive->read_char = c; if (c == -1) return ST_EOF; else return ST_OK; } /** ** Ein Byte in Kanal schreiben **/ int FS_Write(DriveData *drive, int channel, char data, char eof) { // Kanal 15: Zeichen sammeln und bei EOF Befehl ausführen if (channel == 15) { if (drive->cmd_length >= 40) return ST_TIMEOUT; drive->cmd_buffer[drive->cmd_length++] = data; if (eof < 0) { drive->cmd_buffer[drive->cmd_length++] = 0; drive->cmd_length = 0; execute_command(drive, drive->cmd_buffer); } return ST_OK; } if (!drive->handle[channel]) { SetError(drive, ERR_FILENOTOPEN); return ST_TIMEOUT; } if (FPutC(drive->handle[channel], (unsigned char)data) < 0) { SetError(drive, ERR_WRITEERROR); return ST_TIMEOUT; } return ST_OK; } /* * Befehlsstring ausführen */ void execute_command(DriveData *drive, const char *command) { APTR args; switch (command[0]) { case 'B': args = "B-?"; if (ShowRequester(MSG_UNSUPFLOPPYCMD, MSG_REQGADS3, &args)) ResetC64(); SetError(drive, ERR_SYNTAX30); break; case 'M': args = "M-?"; if (ShowRequester(MSG_UNSUPFLOPPYCMD, MSG_REQGADS3, &args)) ResetC64(); SetError(drive, ERR_SYNTAX30); break; case 'I': close_all_channels(drive); SetError(drive, ERR_OK); break; case 'U': if ((command[1] & 0x0f) == 0x0a) { close_all_channels(drive); SetError(drive, ERR_STARTUP); } else SetError(drive, ERR_SYNTAX30); break; default: SetError(drive, ERR_SYNTAX30); break; } } /* * Umwandlung PETSCII->ASCII */ char conv_from_64(char c) { if ((c >= 'A') && (c <= 'Z') || (c >= 'a') && (c <= 'z')) return c ^ 0x20; if ((c == '/') && MapSlash) return '\\'; return c; } /* * Umwandlung ASCII->PETSCII */ char conv_to_64(char c) { if ((c >= 'A') && (c <= 'Z') || (c >= 'a') && (c <= 'z')) return c ^ 0x20; if ((c == '\\') && MapSlash) return '/'; return c; }