home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 10
/
aminetcdnumber101996.iso
/
Aminet
/
misc
/
emu
/
Frodo.lha
/
Frodo
/
src
/
1541d64.c
next >
Wrap
C/C++ Source or Header
|
1995-12-03
|
21KB
|
965 lines
/*
* 1541d64.c - 1541-Emulation in .d64-Datei
*
* Copyright (C) 1994-1995 by Christian Bauer
*/
/*
* Anmerkungen:
* ------------
*
* Routinen:
* - Die Schnittstelle zu den IEC-Routinen besteht in den Routinen
* D64_Init, D64_Exit, D64_Open, D64_Close, D64_Read und D64_Write:
* D64_Init bereitet die Emulation vor
* D64_Exit beendet die Emulation
* D64_Open öffnet einen Kanal
* D64_Close schließt einen Kanal
* D64_Read liest aus einem Kanal
* D64_Write schreibt in einen Kanal
*
* DriveData:
* - lock enthält das FileHandle der .d64-Datei
*
* Inkompatibilitäten/Verbesserungen:
* - Nur Lesezugriffe möglich
* - Keine Wildcards beim Directory-Lesen
* - Nur 'I'-Befehl implementiert
*/
#include <exec/types.h>
#include <exec/memory.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <stdlib.h>
#include <string.h>
#include "IEC.h"
#include "1541d64.h"
#include "Display.h"
#define CATCOMP_NUMBERS 1
#include "LocStrings.h"
// Kanalmodi
enum {
CHMOD_FREE, // Kanal frei
CHMOD_COMMAND, // Kommando-/Fehlerkanal
CHMOD_DIRECTORY, // Directory wird gelesen
CHMOD_FILE, // Sequentielle Datei ist geöffnet
CHMOD_DIRECT // Direkter Pufferzugriff ('#')
};
// Anzahl Tracks
#define NUM_TRACKS 35
// Aus Main.asm
extern void ResetC64(void);
// Prototypes
int open_file(DriveData *drive, int channel, const char *filename);
void convert_filename(const char *srcname, char *destname, int *filemode, int *filetype);
BOOL find_file(DriveData *drive, const char *filename, int *track, int *sector);
int open_file_ts(DriveData *drive, int channel, int track, int sector);
int open_directory(DriveData *drive, const char *filename);
int open_direct(DriveData *drive, int channel, const char *filename);
void close_all_channels(DriveData *drive);
void execute_command(DriveData *drive, const char *command);
void block_read_cmd(DriveData *drive, char *command);
void buffer_ptr_cmd(DriveData *drive, char *command);
BOOL parse_bcmd(char *cmd, int *arg1, int *arg2, int *arg3, int *arg4);
int alloc_buffer(DriveData *drive, int want);
void free_buffer(DriveData *drive, int buf);
BOOL read_sector(DriveData *drive, int track, int sector, char *buffer);
int offset_from_ts(int track, int sector);
/**
** Emulation vorbereiten, .d64-Datei öffnen, prefs zeigt auf den
** Preferences-String
**/
struct FileInfoBlock fib;
void D64_Init(DriveData *drive, char *prefs)
{
BPTR lock;
int i;
ULONG magic;
// Dateilänge prüfen
if (lock = Lock(prefs, ACCESS_READ)) {
Examine(lock, &fib);
UnLock(lock);
if (fib.fib_Size < 174848)
return;
} else
return;
if (drive->ram = malloc(2048)) {
drive->BAM = drive->ram + 0x700;
if (drive->lock = Open(prefs, MODE_OLDFILE)) {
// x64 Image?
Read(drive->lock, &magic, 4);
drive->image_header = (magic == 0x43154164 ? 64 : 0);
// BAM lesen
read_sector(drive, 18, 0, (char *)drive->BAM);
for (i=0; i<=14; i++) {
drive->chan_mode[i] = CHMOD_FREE;
drive->chan_buf[i] = NULL;
}
drive->chan_mode[15] = CHMOD_COMMAND;
drive->cmd_length = 0;
for (i=0; i<4; i++)
drive->buf_free[i] = TRUE;
SetError(drive, ERR_STARTUP);
}
}
}
/**
** Emulation beenden, .d64-Datei schließen
**/
void D64_Exit(DriveData *drive)
{
if (drive->lock) {
close_all_channels(drive);
Close(drive->lock);
drive->lock = NULL;
}
if (drive->ram) {
free(drive->ram);
drive->ram = NULL;
}
}
/**
** Kanal öffnen, filename ist Null-terminiert
**/
int D64_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;
}
if (drive->chan_mode[channel] != CHMOD_FREE) {
SetError(drive, ERR_NOCHANNEL);
return ST_OK;
}
if (filename[0] == '$')
if (channel)
return open_file_ts(drive, channel, 18, 0);
else
return open_directory(drive, filename);
if (filename[0] == '#')
return open_direct(drive, channel, filename);
return open_file(drive, channel, filename);
}
/*
* Datei wird geöffnet
*/
// Zugriffsmodi
enum {
FMODE_READ, FMODE_WRITE, FMODE_APPEND
};
// Dateitypen
enum {
FTYPE_PRG, FTYPE_SEQ, FTYPE_USR, FTYPE_REL
};
int open_file(DriveData *drive, int channel, const char *filename)
{
char plainname[256];
int filemode = FMODE_READ;
int filetype = FTYPE_PRG;
int track, sector;
convert_filename(filename, plainname, &filemode, &filetype);
// 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;
}
// Nur Lesezugriffe erlaubt
if (filemode != FMODE_READ) {
SetError(drive, ERR_WRITEPROTECT);
return ST_OK;
}
// Datei im Directory suchen und öffnen
if (find_file(drive, plainname, &track, §or))
return open_file_ts(drive, channel, track, sector);
else
SetError(drive, ERR_FILENOTFOUND);
return ST_OK;
}
/*
* Dateibezeichnung analysieren, Dateimodus und -typ ermitteln
*/
void convert_filename(const char *srcname, char *destname, int *filemode, int *filetype)
{
char *p, *q;
int i;
// Nach ':' suchen, p zeigt auf erstes Zeichen hinter dem ':'
if (p = strchr(srcname, ':'))
p++;
else
p = srcname;
// Reststring -> destname
strncpy(destname, p, NAMEBUF_LENGTH);
// Nach ',' suchen
p = destname;
while (*p && (*p != ',')) p++;
// Nach Modusparametern, getrennt durch ',' suchen
p = destname;
while (p = strchr(p, ',')) {
// String hinter dem ersten ',' abschneiden
*p++ = 0;
switch (*p) {
case 'P':
*filetype = FTYPE_PRG;
break;
case 'S':
*filetype = FTYPE_SEQ;
break;
case 'U':
*filetype = FTYPE_USR;
break;
case 'L':
*filetype = FTYPE_REL;
break;
case 'R':
*filemode = FMODE_READ;
break;
case 'W':
*filemode = FMODE_WRITE;
break;
case 'A':
*filemode = FMODE_APPEND;
break;
}
}
}
/*
* Datei im Directory suchen, ersten Track und Sektor ermitteln
* FALSE=nicht gefunden, TRUE=gefunden
*/
BOOL find_file(DriveData *drive, const char *filename, int *track, int *sector)
{
int i, j;
UBYTE *p, *q;
DirEntry *dir;
// Alle Directory-Blöcke scannen
drive->dir.next_track = drive->BAM->dir_track;
drive->dir.next_sector = drive->BAM->dir_sector;
while (drive->dir.next_track) {
if (!read_sector(drive, drive->dir.next_track, drive->dir.next_sector,
(char *) &drive->dir))
return FALSE;
// Alle 8 Einträge eines Blocks scannen
for (j=0; j<8; j++) {
dir = &drive->dir.entry[j];
*track = dir->track;
*sector = dir->sector;
if (dir->type) {
p = filename;
q = dir->name;
for (i=0; i<16 && *p; i++, p++, q++) {
if (*p == '*')
return TRUE;
if (*p != *q) {
if (*p != '?') goto next;
if (*q == 0xa0) goto next;
}
}
if (i == 16 || *q == 0xa0)
return TRUE;
}
next:
}
}
return FALSE;
}
/*
* Datei öffnen, Track und Sektor des ersten Blocks gegeben
*/
int open_file_ts(DriveData *drive, int channel, int track, int sector)
{
if (drive->chan_buf[channel] = malloc(256)) {
drive->chan_mode[channel] = CHMOD_FILE;
// Beim nächsten D64_Read-Aufruf wird der erste Block gelesen
drive->chan_buf[channel][0] = track;
drive->chan_buf[channel][1] = sector;
drive->buf_len[channel] = 0;
}
return ST_OK;
}
/*
* Directory als Basic-Programm vorbereiten (Kanal 0)
*/
const char type_char_1[] = {'D', 'S', 'P', 'U', 'R'};
const char type_char_2[] = {'E', 'E', 'R', 'S', 'E'};
const char type_char_3[] = {'L', 'Q', 'G', 'R', 'L'};
int open_directory(DriveData *drive, const char *filename)
{
int i, j, n, m;
char *p, *q;
DirEntry *dir;
UBYTE c;
if (p = drive->buf_ptr[0] = drive->chan_buf[0] = malloc(8192)) {
drive->chan_mode[0] = CHMOD_DIRECTORY;
// Directory-Titel erzeugen
*p++ = 0x01; // Ladeadresse $0401 (aus PET-Zeiten :-)
*p++ = 0x04;
*p++ = 0x01; // Dummy-Verkettung
*p++ = 0x01;
*p++ = 0; // Laufwerksnummer (0) als Zeilennummer
*p++ = 0;
*p++ = 0x12; // RVS ON
*p++ = '\"';
q = drive->BAM->disk_name;
for (i=0; i<23; i++) {
if ((c = *q++) == 0xa0)
*p++ = ' '; // 0xa0 durch Leerzeichen ersetzen
else
*p++ = c;
}
*(p-7) = '\"';
*p++ = 0;
// Alle Directory-Blöcke scannen
drive->dir.next_track = drive->BAM->dir_track;
drive->dir.next_sector = drive->BAM->dir_sector;
while (drive->dir.next_track) {
if (!read_sector(drive, drive->dir.next_track, drive->dir.next_sector,
(char *) &drive->dir))
return ST_OK;
// Alle 8 Einträge eines Blocks scannen
for (j=0; j<8; j++) {
dir = &drive->dir.entry[j];
if (dir->type) {
*p++ = 0x01; // Dummy-Verkettung
*p++ = 0x01;
*p++ = dir->num_blocks_l; // Zeilennummer
*p++ = dir->num_blocks_h;
*p++ = ' ';
n = (dir->num_blocks_h << 8) + dir->num_blocks_l;
if (n<10) *p++ = ' ';
if (n<100) *p++ = ' ';
*p++ = '\"';
q = dir->name;
m = 0;
for (i=0; i<16; i++) {
if ((c = *q++) == 0xa0) {
if (m)
*p++ = ' '; // Alle 0xa0 durch Leerzeichen ersetzen
else
m = *p++ = '\"'; // Aber das erste durch einen '"'
} else
*p++ = c;
}
if (m)
*p++ = ' ';
else
*p++ = '\"'; // Kein 0xa0, dann ein Leerzeichen anhängen
if (dir->type & 0x80)
*p++ = ' ';
else
*p++ = '*';
*p++ = type_char_1[dir->type & 0x0f];
*p++ = type_char_2[dir->type & 0x0f];
*p++ = type_char_3[dir->type & 0x0f];
if (dir->type & 0x40)
*p++ = '<';
else
*p++ = ' ';
*p++ = ' ';
if (n >= 10) *p++ = ' ';
if (n >= 100) *p++ = ' ';
*p++ = 0;
}
}
}
// Abschlußzeile
q = p;
for (i=0; i<29; i++)
*q++ = ' ';
n = 0;
for (i=0; i<35; i++)
n += drive->BAM->bitmap[i*4];
*p++ = 0x01; // Dummy-Verkettung
*p++ = 0x01;
*p++ = n & 0xff; // Anzahl freier Blöcke als Zeilennummer
*p++ = (n >> 8) & 0xff;
*p++ = 'B';
*p++ = 'L';
*p++ = 'O';
*p++ = 'C';
*p++ = 'K';
*p++ = 'S';
*p++ = ' ';
*p++ = 'F';
*p++ = 'R';
*p++ = 'E';
*p++ = 'E';
*p++ = '.';
p = q;
*p++ = 0;
*p++ = 0;
*p++ = 0;
drive->buf_len[0] = p - drive->chan_buf[0];
}
return ST_OK;
}
/*
* Kanal für direkten Pufferzugriff öffnen
*/
int open_direct(DriveData *drive, int channel, const char *filename)
{
int buf = -1;
if (filename[1] == 0)
buf = alloc_buffer(drive, -1);
else
if ((filename[1] >= '0') && (filename[1] <= '3') && (filename[2] == 0))
buf = alloc_buffer(drive, filename[1] - '0');
if (buf == -1) {
SetError(drive, ERR_NOCHANNEL);
return ST_OK;
}
// Die Puffer liegen im 1541-RAM ab $300 und belegen je 256 Byte
drive->chan_buf[channel] = drive->buf_ptr[channel] = drive->ram + 0x300 + (buf << 8);
drive->chan_mode[channel] = CHMOD_DIRECT;
drive->chan_buf_num[channel] = buf;
// Tatsächliche Puffernummer im Puffer ablegen
*drive->chan_buf[channel] = buf + '0';
drive->buf_len[channel] = 1;
return ST_OK;
}
/**
** Kanal schließen
**/
int D64_Close(DriveData *drive, int channel)
{
if (channel==15) {
close_all_channels(drive);
return ST_OK;
}
switch (drive->chan_mode[channel]) {
case CHMOD_FREE:
break;
case CHMOD_DIRECT:
free_buffer(drive, drive->chan_buf_num[channel]);
drive->chan_buf[channel] = NULL;
drive->chan_mode[channel] = CHMOD_FREE;
break;
default:
free(drive->chan_buf[channel]);
drive->chan_buf[channel] = NULL;
drive->chan_mode[channel] = CHMOD_FREE;
break;
}
return ST_OK;
}
/*
* Alle Kanäle schließen
*/
void close_all_channels(DriveData *drive)
{
int i;
for (i=0; i<15; i++) D64_Close(drive, i);
drive->cmd_length = 0;
}
/**
** Ein Byte aus Kanal lesen
**/
int D64_Read(DriveData *drive, int channel, char *data)
{
switch (drive->chan_mode[channel]) {
case CHMOD_FREE:
return ST_READ_TIMEOUT;
break;
case CHMOD_COMMAND:
*data = *drive->error_ptr++;
if (--drive->error_length)
return ST_OK;
else {
SetError(drive, ERR_OK);
return ST_EOF;
}
break;
case CHMOD_FILE:
// Nächsten Block lesen, wenn notwendig
if (drive->chan_buf[channel][0] && !drive->buf_len[channel]) {
if (!read_sector(drive, drive->chan_buf[channel][0],
drive->chan_buf[channel][1], drive->chan_buf[channel]))
return ST_READ_TIMEOUT;
drive->buf_ptr[channel] = drive->chan_buf[channel] + 2;
// Blocklänge ermitteln
drive->buf_len[channel] = drive->chan_buf[channel][0] ? 254 : (UBYTE)drive->chan_buf[channel][1];
}
if (drive->buf_len[channel] > 0) {
*data = *drive->buf_ptr[channel]++;
if (!--drive->buf_len[channel] && !drive->chan_buf[channel][0])
return ST_EOF;
else
return ST_OK;
} else
return ST_READ_TIMEOUT;
break;
case CHMOD_DIRECTORY:
case CHMOD_DIRECT:
if (drive->buf_len[channel] > 0) {
*data = *drive->buf_ptr[channel]++;
if (--drive->buf_len[channel])
return ST_OK;
else
return ST_EOF;
} else
return ST_READ_TIMEOUT;
break;
}
}
/**
** Ein Byte in Kanal schreiben
**/
int D64_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->chan_mode[channel] == CHMOD_FREE)
SetError(drive, ERR_FILENOTOPEN);
if (drive->chan_mode[channel] == CHMOD_DIRECTORY)
SetError(drive, ERR_WRITEFILEOPEN);
return ST_TIMEOUT;
}
/*
* Befehlsstring ausführen
*/
void execute_command(DriveData *drive, const char *command)
{
UWORD adr;
APTR args;
switch (command[0]) {
case 'B':
if (command[1] != '-') {
SetError(drive, ERR_SYNTAX30);
} else {
switch (command[2]) {
case 'R':
block_read_cmd(drive, &command[3]);
break;
case 'P':
buffer_ptr_cmd(drive, &command[3]);
break;
case 'A':
case 'F':
case 'W':
SetError(drive, ERR_WRITEPROTECT);
break;
case 'E':
args = "B-E";
if (ShowRequester(MSG_UNSUPFLOPPYCMD, MSG_REQGADS3, &args))
ResetC64();
SetError(drive, ERR_SYNTAX30);
break;
default:
SetError(drive, ERR_SYNTAX30);
break;
}
}
break;
case 'M':
if (command[1] != '-') {
SetError(drive, ERR_SYNTAX30);
} else {
switch (command[2]) {
case 'R':
adr = ((UBYTE)command[4] << 8) | ((UBYTE)command[3]);
drive->error_ptr = drive->ram + (adr & 0x07ff);
if (!(drive->error_length = (UBYTE)command[5]))
drive->error_length = 1;
break;
case 'E':
args = "M-E";
if (ShowRequester(MSG_UNSUPFLOPPYCMD, MSG_REQGADS3, &args))
ResetC64();
SetError(drive, ERR_SYNTAX30);
break;
case 'W':
args = "M-W";
if (ShowRequester(MSG_UNSUPFLOPPYCMD, MSG_REQGADS3, &args))
ResetC64();
SetError(drive, ERR_SYNTAX30);
break;
default:
SetError(drive, ERR_SYNTAX30);
break;
}
}
break;
case 'I':
close_all_channels(drive);
read_sector(drive, 18, 0, (char *)drive->BAM);
SetError(drive, ERR_OK);
break;
case 'U':
switch (command[1] & 0x0f) {
case 1: // U1/UA: Block-Read
block_read_cmd(drive, &command[2]);
break;
case 2: // U2/UB: Block-Write
SetError(drive, ERR_WRITEPROTECT);
break;
case 10: // U:/UJ: Reset
close_all_channels(drive);
read_sector(drive, 18, 0, (char *)drive->BAM);
SetError(drive, ERR_STARTUP);
break;
default:
SetError(drive, ERR_SYNTAX30);
break;
}
break;
case 'C':
case 'N':
case 'R':
case 'S':
case 'V':
SetError(drive, ERR_WRITEPROTECT);
break;
default:
SetError(drive, ERR_SYNTAX30);
break;
}
}
/*
* B-R-Befehl ausführen
*/
void block_read_cmd(DriveData *drive, char *command)
{
int channel, drvnum, track, sector;
if (parse_bcmd(command, &channel, &drvnum, &track, §or)) {
if (drive->chan_mode[channel] == CHMOD_DIRECT) {
read_sector(drive, track, sector, drive->buf_ptr[channel] = drive->chan_buf[channel]);
drive->buf_len[channel] = 256;
SetError(drive, ERR_OK);
} else
SetError(drive, ERR_NOCHANNEL);
} else
SetError(drive, ERR_SYNTAX30);
}
/*
* B-P-Befehl ausführen
*/
void buffer_ptr_cmd(DriveData *drive, char *command)
{
int channel, pointer, i;
if (parse_bcmd(command, &channel, &pointer, &i, &i)) {
if (drive->chan_mode[channel] == CHMOD_DIRECT) {
drive->buf_ptr[channel] = drive->chan_buf[channel] + pointer;
drive->buf_len[channel] = 256 - pointer;
SetError(drive, ERR_OK);
} else
SetError(drive, ERR_NOCHANNEL);
} else
SetError(drive, ERR_SYNTAX30);
}
/*
* Parameter der Block-Befehle auswerten
* TRUE: OK, FALSE: Fehler
*/
BOOL parse_bcmd(char *cmd, int *arg1, int *arg2, int *arg3, int *arg4)
{
int i;
if (*cmd == ':') cmd++;
// Vier durch Leerzeichen, Cursor Right oder Komma getrennte Parameter lesen
while (*cmd == ' ' || *cmd == 0x1d || *cmd == 0x2c) cmd++;
if (!*cmd) return FALSE;
i = 0;
while (*cmd >= 0x30 && *cmd < 0x40) {
i *= 10;
i += *cmd++ & 0x0f;
}
*arg1 = i & 0xff;
while (*cmd == ' ' || *cmd == 0x1d || *cmd == 0x2c) cmd++;
if (!*cmd) return TRUE;
i = 0;
while (*cmd >= 0x30 && *cmd < 0x40) {
i *= 10;
i += *cmd++ & 0x0f;
}
*arg2 = i & 0xff;
while (*cmd == ' ' || *cmd == 0x1d || *cmd == 0x2c) cmd++;
if (!*cmd) return TRUE;
i = 0;
while (*cmd >= 0x30 && *cmd < 0x40) {
i *= 10;
i += *cmd++ & 0x0f;
}
*arg3 = i & 0xff;
while (*cmd == ' ' || *cmd == 0x1d || *cmd == 0x2c) cmd++;
i = 0;
while (*cmd >= 0x30 && *cmd < 0x40) {
i *= 10;
i += *cmd++ & 0x0f;
}
*arg4 = i & 0xff;
return TRUE;
}
/*
* Einen Floppy-Puffer belegen
* -> Gewünschte Puffernummer oder -1
* <- Belegte Puffernummer oder -1
*/
int alloc_buffer(DriveData *drive, int want)
{
if (want == -1) {
for (want=3; want>=0; want--)
if (drive->buf_free[want]) {
drive->buf_free[want] = FALSE;
return want;
}
return -1;
}
if (want < 4)
if (drive->buf_free[want]) {
drive->buf_free[want] = FALSE;
return want;
} else
return -1;
else
return -1;
}
/*
* Einen Floppy-Puffer freigeben
*/
void free_buffer(DriveData *drive, int buf)
{
drive->buf_free[buf] = TRUE;
}
/*
* Einen Sektor lesen (256 Bytes)
* TRUE: Gelungen, FALSE: Fehler
*/
BOOL read_sector(DriveData *drive, int track, int sector, char *buffer)
{
int offset;
// Track/Sektor-Angabe in Byteoffset in der Datei umwandeln
if ((offset = offset_from_ts(track, sector)) < 0) {
SetError(drive, ERR_ILLEGALTS);
return FALSE;
}
Seek(drive->lock, offset + drive->image_header, OFFSET_BEGINNING);
Read(drive->lock, buffer, 256);
return TRUE;
}
/*
* Track/Sektor in Offset umrechnen
*/
const int num_sectors[36] = {
0,
21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21
19,19,19,19,19,19,19,
18,18,18,18,18,18,
17,17,17,17,17
};
const int sector_offset[36] = {
0,
0,21,42,63,84,105,126,147,168,189,210,231,252,273,294,315,336,
357,376,395,414,433,452,471,
490,508,526,544,562,580,
598,615,632,649,666
};
int offset_from_ts(int track, int sector)
{
if ((track < 1) || (track > NUM_TRACKS) ||
(sector < 0) || (sector >= num_sectors[track]))
return -1;
return (sector_offset[track] + sector) << 8;
}