mirror of
https://github.com/Oibaf66/frodo-wii.git
synced 2024-11-29 15:04:23 +01:00
1026 lines
18 KiB
C++
1026 lines
18 KiB
C++
|
/*
|
||
|
* 1541d64.cpp - 1541 emulation in .d64 file
|
||
|
*
|
||
|
* Frodo (C) 1994-1997,2002 Christian Bauer
|
||
|
*
|
||
|
|
||
|
*
|
||
|
* Incompatibilities:
|
||
|
* ------------------
|
||
|
*
|
||
|
* - Only read accesses possible
|
||
|
* - Not all commands implemented
|
||
|
* - The .d64 error info is read, but unused
|
||
|
*/
|
||
|
|
||
|
#include "sysdeps.h"
|
||
|
|
||
|
#include "1541d64.h"
|
||
|
#include "IEC.h"
|
||
|
#include "Prefs.h"
|
||
|
|
||
|
|
||
|
// Channel modes (IRC users listen up :-)
|
||
|
enum {
|
||
|
CHMOD_FREE, // Channel free
|
||
|
CHMOD_COMMAND, // Command/error channel
|
||
|
CHMOD_DIRECTORY, // Reading directory
|
||
|
CHMOD_FILE, // Sequential file open
|
||
|
CHMOD_DIRECT // Direct buffer access ('#')
|
||
|
};
|
||
|
|
||
|
// Access modes
|
||
|
enum {
|
||
|
FMODE_READ, FMODE_WRITE, FMODE_APPEND
|
||
|
};
|
||
|
|
||
|
// File types
|
||
|
enum {
|
||
|
FTYPE_PRG, FTYPE_SEQ, FTYPE_USR, FTYPE_REL
|
||
|
};
|
||
|
|
||
|
// Number of tracks/sectors
|
||
|
const int NUM_TRACKS = 35;
|
||
|
const int NUM_SECTORS = 683;
|
||
|
|
||
|
// Prototypes
|
||
|
static bool match(uint8 *p, uint8 *n);
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Constructor: Prepare emulation, open .d64 file
|
||
|
*/
|
||
|
|
||
|
D64Drive::D64Drive(IEC *iec, char *filepath) : Drive(iec)
|
||
|
{
|
||
|
the_file = NULL;
|
||
|
ram = NULL;
|
||
|
|
||
|
Ready = false;
|
||
|
strcpy(orig_d64_name, filepath);
|
||
|
for (int i=0; i<=14; i++) {
|
||
|
chan_mode[i] = CHMOD_FREE;
|
||
|
chan_buf[i] = NULL;
|
||
|
}
|
||
|
chan_mode[15] = CHMOD_COMMAND;
|
||
|
|
||
|
// Open .d64 file
|
||
|
open_close_d64_file(filepath);
|
||
|
if (the_file != NULL) {
|
||
|
|
||
|
// Allocate 1541 RAM
|
||
|
ram = new uint8[0x800];
|
||
|
bam = (BAM *)(ram + 0x700);
|
||
|
|
||
|
Reset();
|
||
|
Ready = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Destructor
|
||
|
*/
|
||
|
|
||
|
D64Drive::~D64Drive()
|
||
|
{
|
||
|
// Close .d64 file
|
||
|
open_close_d64_file("");
|
||
|
|
||
|
delete[] ram;
|
||
|
Ready = false;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Open/close the .d64 file
|
||
|
*/
|
||
|
|
||
|
void D64Drive::open_close_d64_file(char *d64name)
|
||
|
{
|
||
|
long size;
|
||
|
uint8 magic[4];
|
||
|
|
||
|
// Close old .d64, if open
|
||
|
if (the_file != NULL) {
|
||
|
close_all_channels();
|
||
|
fclose(the_file);
|
||
|
the_file = NULL;
|
||
|
}
|
||
|
|
||
|
// Open new .d64 file
|
||
|
if (d64name[0]) {
|
||
|
if ((the_file = fopen(d64name, "rb")) != NULL) {
|
||
|
|
||
|
// Check length
|
||
|
fseek(the_file, 0, SEEK_END);
|
||
|
if ((size = ftell(the_file)) < NUM_SECTORS * 256) {
|
||
|
fclose(the_file);
|
||
|
the_file = NULL;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// x64 image?
|
||
|
rewind(the_file);
|
||
|
fread(&magic, 4, 1, the_file);
|
||
|
if (magic[0] == 0x43 && magic[1] == 0x15 && magic[2] == 0x41 && magic[3] == 0x64)
|
||
|
image_header = 64;
|
||
|
else
|
||
|
image_header = 0;
|
||
|
|
||
|
// Preset error info (all sectors no error)
|
||
|
memset(error_info, 1, NUM_SECTORS);
|
||
|
|
||
|
// Load sector error info from .d64 file, if present
|
||
|
if (!image_header && size == NUM_SECTORS * 257) {
|
||
|
fseek(the_file, NUM_SECTORS * 256, SEEK_SET);
|
||
|
fread(&error_info, NUM_SECTORS, 1, the_file);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Open channel
|
||
|
*/
|
||
|
|
||
|
uint8 D64Drive::Open(int channel, char *filename)
|
||
|
{
|
||
|
set_error(ERR_OK);
|
||
|
|
||
|
// Channel 15: execute file name as command
|
||
|
if (channel == 15) {
|
||
|
execute_command(filename);
|
||
|
return ST_OK;
|
||
|
}
|
||
|
|
||
|
if (chan_mode[channel] != CHMOD_FREE) {
|
||
|
set_error(ERR_NOCHANNEL);
|
||
|
return ST_OK;
|
||
|
}
|
||
|
|
||
|
if (filename[0] == '$')
|
||
|
if (channel)
|
||
|
return open_file_ts(channel, 18, 0);
|
||
|
else
|
||
|
return open_directory(filename+1);
|
||
|
|
||
|
if (filename[0] == '#')
|
||
|
return open_direct(channel, filename);
|
||
|
|
||
|
return open_file(channel, filename);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Open file
|
||
|
*/
|
||
|
|
||
|
uint8 D64Drive::open_file(int channel, char *filename)
|
||
|
{
|
||
|
char plainname[256];
|
||
|
int filemode = FMODE_READ;
|
||
|
int filetype = FTYPE_PRG;
|
||
|
int track, sector;
|
||
|
|
||
|
convert_filename(filename, plainname, &filemode, &filetype);
|
||
|
|
||
|
// Channel 0 is READ PRG, channel 1 is WRITE PRG
|
||
|
if (!channel) {
|
||
|
filemode = FMODE_READ;
|
||
|
filetype = FTYPE_PRG;
|
||
|
}
|
||
|
if (channel == 1) {
|
||
|
filemode = FMODE_WRITE;
|
||
|
filetype = FTYPE_PRG;
|
||
|
}
|
||
|
|
||
|
// Allow only read accesses
|
||
|
if (filemode != FMODE_READ) {
|
||
|
set_error(ERR_WRITEPROTECT);
|
||
|
return ST_OK;
|
||
|
}
|
||
|
|
||
|
// Find file in directory and open it
|
||
|
if (find_file(plainname, &track, §or))
|
||
|
return open_file_ts(channel, track, sector);
|
||
|
else
|
||
|
set_error(ERR_FILENOTFOUND);
|
||
|
|
||
|
return ST_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Analyze file name, get access mode and type
|
||
|
*/
|
||
|
|
||
|
void D64Drive::convert_filename(char *srcname, char *destname, int *filemode, int *filetype)
|
||
|
{
|
||
|
char *p;
|
||
|
|
||
|
// Search for ':', p points to first character after ':'
|
||
|
if ((p = strchr(srcname, ':')) != NULL)
|
||
|
p++;
|
||
|
else
|
||
|
p = srcname;
|
||
|
|
||
|
// Remaining string -> destname
|
||
|
strncpy(destname, p, NAMEBUF_LENGTH);
|
||
|
|
||
|
// Search for ','
|
||
|
p = destname;
|
||
|
while (*p && (*p != ',')) p++;
|
||
|
|
||
|
// Look for mode parameters seperated by ','
|
||
|
p = destname;
|
||
|
while ((p = strchr(p, ',')) != NULL) {
|
||
|
|
||
|
// Cut string after the first ','
|
||
|
*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;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Search file in directory, find first track and sector
|
||
|
* false: not found, true: found
|
||
|
*/
|
||
|
|
||
|
bool D64Drive::find_file(char *filename, int *track, int *sector)
|
||
|
{
|
||
|
int i, j;
|
||
|
uint8 *p, *q;
|
||
|
DirEntry *de;
|
||
|
|
||
|
// Scan all directory blocks
|
||
|
dir.next_track = bam->dir_track;
|
||
|
dir.next_sector = bam->dir_sector;
|
||
|
|
||
|
while (dir.next_track) {
|
||
|
if (!read_sector(dir.next_track, dir.next_sector, (uint8 *) &dir.next_track))
|
||
|
return false;
|
||
|
|
||
|
// Scan all 8 entries of a block
|
||
|
for (j=0; j<8; j++) {
|
||
|
de = &dir.entry[j];
|
||
|
*track = de->track;
|
||
|
*sector = de->sector;
|
||
|
|
||
|
if (de->type) {
|
||
|
p = (uint8 *)filename;
|
||
|
q = de->name;
|
||
|
for (i=0; i<16 && *p; i++, p++, q++) {
|
||
|
if (*p == '*') // Wildcard '*' matches all following characters
|
||
|
return true;
|
||
|
if (*p != *q) {
|
||
|
if (*p != '?') goto next_entry; // Wildcard '?' matches single character
|
||
|
if (*q == 0xa0) goto next_entry;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (i == 16 || *q == 0xa0)
|
||
|
return true;
|
||
|
}
|
||
|
next_entry: ;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Open file given track/sector of first block
|
||
|
*/
|
||
|
|
||
|
uint8 D64Drive::open_file_ts(int channel, int track, int sector)
|
||
|
{
|
||
|
chan_buf[channel] = new uint8[256];
|
||
|
chan_mode[channel] = CHMOD_FILE;
|
||
|
|
||
|
// On the next call to Read, the first block will be read
|
||
|
chan_buf[channel][0] = track;
|
||
|
chan_buf[channel][1] = sector;
|
||
|
buf_len[channel] = 0;
|
||
|
|
||
|
return ST_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Prepare directory as BASIC program (channel 0)
|
||
|
*/
|
||
|
|
||
|
const char type_char_1[] = "DSPUREERSELQGRL?";
|
||
|
const char type_char_2[] = "EERSELQGRL??????";
|
||
|
const char type_char_3[] = "LQGRL???????????";
|
||
|
|
||
|
// Return true if name 'n' matches pattern 'p'
|
||
|
static bool match(uint8 *p, uint8 *n)
|
||
|
{
|
||
|
if (!*p) // Null pattern matches everything
|
||
|
return true;
|
||
|
|
||
|
do {
|
||
|
if (*p == '*') // Wildcard '*' matches all following characters
|
||
|
return true;
|
||
|
if ((*p != *n) && (*p != '?')) // Wildcard '?' matches single character
|
||
|
return false;
|
||
|
p++; n++;
|
||
|
} while (*p);
|
||
|
|
||
|
return *n == 0xa0;
|
||
|
}
|
||
|
|
||
|
uint8 D64Drive::open_directory(char *pattern)
|
||
|
{
|
||
|
int i, j, n, m;
|
||
|
uint8 *p, *q;
|
||
|
DirEntry *de;
|
||
|
uint8 c;
|
||
|
char *tmppat;
|
||
|
|
||
|
// Special treatment for "$0"
|
||
|
if (pattern[0] == '0' && pattern[1] == 0)
|
||
|
pattern += 1;
|
||
|
|
||
|
// Skip everything before the ':' in the pattern
|
||
|
if ((tmppat = strchr(pattern, ':')) != NULL)
|
||
|
pattern = tmppat + 1;
|
||
|
|
||
|
p = buf_ptr[0] = chan_buf[0] = new uint8[8192];
|
||
|
chan_mode[0] = CHMOD_DIRECTORY;
|
||
|
|
||
|
// Create directory title
|
||
|
*p++ = 0x01; // Load address $0401 (from PET days :-)
|
||
|
*p++ = 0x04;
|
||
|
*p++ = 0x01; // Dummy line link
|
||
|
*p++ = 0x01;
|
||
|
*p++ = 0; // Drive number (0) as line number
|
||
|
*p++ = 0;
|
||
|
*p++ = 0x12; // RVS ON
|
||
|
*p++ = '\"';
|
||
|
|
||
|
q = bam->disk_name;
|
||
|
for (i=0; i<23; i++) {
|
||
|
if ((c = *q++) == 0xa0)
|
||
|
*p++ = ' '; // Replace 0xa0 by space
|
||
|
else
|
||
|
*p++ = c;
|
||
|
}
|
||
|
*(p-7) = '\"';
|
||
|
*p++ = 0;
|
||
|
|
||
|
// Scan all directory blocks
|
||
|
dir.next_track = bam->dir_track;
|
||
|
dir.next_sector = bam->dir_sector;
|
||
|
|
||
|
while (dir.next_track) {
|
||
|
if (!read_sector(dir.next_track, dir.next_sector, (uint8 *) &dir.next_track))
|
||
|
return ST_OK;
|
||
|
|
||
|
// Scan all 8 entries of a block
|
||
|
for (j=0; j<8; j++) {
|
||
|
de = &dir.entry[j];
|
||
|
|
||
|
if (de->type && match((uint8 *)pattern, de->name)) {
|
||
|
*p++ = 0x01; // Dummy line link
|
||
|
*p++ = 0x01;
|
||
|
|
||
|
*p++ = de->num_blocks_l; // Line number
|
||
|
*p++ = de->num_blocks_h;
|
||
|
|
||
|
*p++ = ' ';
|
||
|
n = (de->num_blocks_h << 8) + de->num_blocks_l;
|
||
|
if (n<10) *p++ = ' ';
|
||
|
if (n<100) *p++ = ' ';
|
||
|
|
||
|
*p++ = '\"';
|
||
|
q = de->name;
|
||
|
m = 0;
|
||
|
for (i=0; i<16; i++) {
|
||
|
if ((c = *q++) == 0xa0) {
|
||
|
if (m)
|
||
|
*p++ = ' '; // Replace all 0xa0 by spaces
|
||
|
else
|
||
|
m = *p++ = '\"'; // But the first by a '"'
|
||
|
} else
|
||
|
*p++ = c;
|
||
|
}
|
||
|
if (m)
|
||
|
*p++ = ' ';
|
||
|
else
|
||
|
*p++ = '\"'; // No 0xa0, then append a space
|
||
|
|
||
|
if (de->type & 0x80)
|
||
|
*p++ = ' ';
|
||
|
else
|
||
|
*p++ = '*';
|
||
|
|
||
|
*p++ = type_char_1[de->type & 0x0f];
|
||
|
*p++ = type_char_2[de->type & 0x0f];
|
||
|
*p++ = type_char_3[de->type & 0x0f];
|
||
|
|
||
|
if (de->type & 0x40)
|
||
|
*p++ = '<';
|
||
|
else
|
||
|
*p++ = ' ';
|
||
|
|
||
|
*p++ = ' ';
|
||
|
if (n >= 10) *p++ = ' ';
|
||
|
if (n >= 100) *p++ = ' ';
|
||
|
*p++ = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Final line
|
||
|
q = p;
|
||
|
for (i=0; i<29; i++)
|
||
|
*q++ = ' ';
|
||
|
|
||
|
n = 0;
|
||
|
for (i=0; i<35; i++)
|
||
|
n += bam->bitmap[i*4];
|
||
|
|
||
|
*p++ = 0x01; // Dummy line link
|
||
|
*p++ = 0x01;
|
||
|
*p++ = n & 0xff; // Number of free blocks as line number
|
||
|
*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;
|
||
|
|
||
|
buf_len[0] = p - chan_buf[0];
|
||
|
|
||
|
return ST_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Open channel for direct buffer access
|
||
|
*/
|
||
|
|
||
|
uint8 D64Drive::open_direct(int channel, char *filename)
|
||
|
{
|
||
|
int buf = -1;
|
||
|
|
||
|
if (filename[1] == 0)
|
||
|
buf = alloc_buffer(-1);
|
||
|
else
|
||
|
if ((filename[1] >= '0') && (filename[1] <= '3') && (filename[2] == 0))
|
||
|
buf = alloc_buffer(filename[1] - '0');
|
||
|
|
||
|
if (buf == -1) {
|
||
|
set_error(ERR_NOCHANNEL);
|
||
|
return ST_OK;
|
||
|
}
|
||
|
|
||
|
// The buffers are in the 1541 RAM at $300 and are 256 bytes each
|
||
|
chan_buf[channel] = buf_ptr[channel] = ram + 0x300 + (buf << 8);
|
||
|
chan_mode[channel] = CHMOD_DIRECT;
|
||
|
chan_buf_num[channel] = buf;
|
||
|
|
||
|
// Store actual buffer number in buffer
|
||
|
*chan_buf[channel] = buf + '0';
|
||
|
buf_len[channel] = 1;
|
||
|
|
||
|
return ST_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Close channel
|
||
|
*/
|
||
|
|
||
|
uint8 D64Drive::Close(int channel)
|
||
|
{
|
||
|
if (channel == 15) {
|
||
|
close_all_channels();
|
||
|
return ST_OK;
|
||
|
}
|
||
|
|
||
|
switch (chan_mode[channel]) {
|
||
|
case CHMOD_FREE:
|
||
|
break;
|
||
|
|
||
|
case CHMOD_DIRECT:
|
||
|
free_buffer(chan_buf_num[channel]);
|
||
|
chan_buf[channel] = NULL;
|
||
|
chan_mode[channel] = CHMOD_FREE;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
delete[] chan_buf[channel];
|
||
|
chan_buf[channel] = NULL;
|
||
|
chan_mode[channel] = CHMOD_FREE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return ST_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Close all channels
|
||
|
*/
|
||
|
|
||
|
void D64Drive::close_all_channels()
|
||
|
{
|
||
|
for (int i=0; i<15; i++)
|
||
|
Close(i);
|
||
|
|
||
|
cmd_len = 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Read from channel
|
||
|
*/
|
||
|
|
||
|
uint8 D64Drive::Read(int channel, uint8 *byte)
|
||
|
{
|
||
|
switch (chan_mode[channel]) {
|
||
|
case CHMOD_COMMAND:
|
||
|
*byte = *error_ptr++;
|
||
|
if (--error_len)
|
||
|
return ST_OK;
|
||
|
else {
|
||
|
set_error(ERR_OK);
|
||
|
return ST_EOF;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case CHMOD_FILE:
|
||
|
// Read next block if necessary
|
||
|
if (chan_buf[channel][0] && !buf_len[channel]) {
|
||
|
if (!read_sector(chan_buf[channel][0], chan_buf[channel][1], chan_buf[channel]))
|
||
|
return ST_READ_TIMEOUT;
|
||
|
buf_ptr[channel] = chan_buf[channel] + 2;
|
||
|
|
||
|
// Determine block length
|
||
|
buf_len[channel] = chan_buf[channel][0] ? 254 : (uint8)chan_buf[channel][1]-1;
|
||
|
}
|
||
|
|
||
|
if (buf_len[channel] > 0) {
|
||
|
*byte = *buf_ptr[channel]++;
|
||
|
if (!--buf_len[channel] && !chan_buf[channel][0])
|
||
|
return ST_EOF;
|
||
|
else
|
||
|
return ST_OK;
|
||
|
} else
|
||
|
return ST_READ_TIMEOUT;
|
||
|
break;
|
||
|
|
||
|
case CHMOD_DIRECTORY:
|
||
|
case CHMOD_DIRECT:
|
||
|
if (buf_len[channel] > 0) {
|
||
|
*byte = *buf_ptr[channel]++;
|
||
|
if (--buf_len[channel])
|
||
|
return ST_OK;
|
||
|
else
|
||
|
return ST_EOF;
|
||
|
} else
|
||
|
return ST_READ_TIMEOUT;
|
||
|
break;
|
||
|
}
|
||
|
return ST_READ_TIMEOUT;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Write byte to channel
|
||
|
*/
|
||
|
|
||
|
uint8 D64Drive::Write(int channel, uint8 byte, bool eoi)
|
||
|
{
|
||
|
switch (chan_mode[channel]) {
|
||
|
case CHMOD_FREE:
|
||
|
set_error(ERR_FILENOTOPEN);
|
||
|
break;
|
||
|
|
||
|
case CHMOD_COMMAND:
|
||
|
// Collect characters and execute command on EOI
|
||
|
if (cmd_len >= 40)
|
||
|
return ST_TIMEOUT;
|
||
|
|
||
|
cmd_buffer[cmd_len++] = byte;
|
||
|
|
||
|
if (eoi) {
|
||
|
cmd_buffer[cmd_len++] = 0;
|
||
|
cmd_len = 0;
|
||
|
execute_command(cmd_buffer);
|
||
|
}
|
||
|
return ST_OK;
|
||
|
|
||
|
case CHMOD_DIRECTORY:
|
||
|
set_error(ERR_WRITEFILEOPEN);
|
||
|
break;
|
||
|
}
|
||
|
return ST_TIMEOUT;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Execute command string
|
||
|
*/
|
||
|
|
||
|
void D64Drive::execute_command(char *command)
|
||
|
{
|
||
|
uint16 adr;
|
||
|
int len;
|
||
|
|
||
|
switch (command[0]) {
|
||
|
case 'B':
|
||
|
if (command[1] != '-')
|
||
|
set_error(ERR_SYNTAX30);
|
||
|
else
|
||
|
switch (command[2]) {
|
||
|
case 'R':
|
||
|
block_read_cmd(&command[3]);
|
||
|
break;
|
||
|
|
||
|
case 'P':
|
||
|
buffer_ptr_cmd(&command[3]);
|
||
|
break;
|
||
|
|
||
|
case 'A':
|
||
|
case 'F':
|
||
|
case 'W':
|
||
|
set_error(ERR_WRITEPROTECT);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
set_error(ERR_SYNTAX30);
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 'M':
|
||
|
if (command[1] != '-')
|
||
|
set_error(ERR_SYNTAX30);
|
||
|
else
|
||
|
switch (command[2]) {
|
||
|
case 'R':
|
||
|
adr = ((uint8)command[4] << 8) | ((uint8)command[3]);
|
||
|
error_ptr = (char *)(ram + (adr & 0x07ff));
|
||
|
if (!(error_len = (uint8)command[5]))
|
||
|
error_len = 1;
|
||
|
break;
|
||
|
|
||
|
case 'W':
|
||
|
adr = ((uint8)command[4] << 8) | ((uint8)command[3]);
|
||
|
len = (uint8)command[5];
|
||
|
for (int i=0; i<len; i++)
|
||
|
ram[adr+i] = (uint8)command[i+6];
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
set_error(ERR_SYNTAX30);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 'I':
|
||
|
close_all_channels();
|
||
|
read_sector(18, 0, (uint8 *)bam);
|
||
|
set_error(ERR_OK);
|
||
|
break;
|
||
|
|
||
|
case 'U':
|
||
|
switch (command[1] & 0x0f) {
|
||
|
case 1: // U1/UA: Block-Read
|
||
|
block_read_cmd(&command[2]);
|
||
|
break;
|
||
|
|
||
|
case 2: // U2/UB: Block-Write
|
||
|
set_error(ERR_WRITEPROTECT);
|
||
|
break;
|
||
|
|
||
|
case 10: // U:/UJ: Reset
|
||
|
Reset();
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
set_error(ERR_SYNTAX30);
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 'G':
|
||
|
if (command[1] != ':')
|
||
|
set_error(ERR_SYNTAX30);
|
||
|
else
|
||
|
chd64_cmd(&command[2]);
|
||
|
break;
|
||
|
|
||
|
case 'C':
|
||
|
case 'N':
|
||
|
case 'R':
|
||
|
case 'S':
|
||
|
case 'V':
|
||
|
set_error(ERR_WRITEPROTECT);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
set_error(ERR_SYNTAX30);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Execute B-R command
|
||
|
*/
|
||
|
|
||
|
void D64Drive::block_read_cmd(char *command)
|
||
|
{
|
||
|
int channel, drvnum, track, sector;
|
||
|
|
||
|
if (parse_bcmd(command, &channel, &drvnum, &track, §or)) {
|
||
|
if (chan_mode[channel] == CHMOD_DIRECT) {
|
||
|
read_sector(track, sector, buf_ptr[channel] = chan_buf[channel]);
|
||
|
buf_len[channel] = 256;
|
||
|
set_error(ERR_OK);
|
||
|
} else
|
||
|
set_error(ERR_NOCHANNEL);
|
||
|
} else
|
||
|
set_error(ERR_SYNTAX30);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Execute B-P command
|
||
|
*/
|
||
|
|
||
|
void D64Drive::buffer_ptr_cmd(char *command)
|
||
|
{
|
||
|
int channel, pointer, i;
|
||
|
|
||
|
if (parse_bcmd(command, &channel, &pointer, &i, &i)) {
|
||
|
if (chan_mode[channel] == CHMOD_DIRECT) {
|
||
|
buf_ptr[channel] = chan_buf[channel] + pointer;
|
||
|
buf_len[channel] = 256 - pointer;
|
||
|
set_error(ERR_OK);
|
||
|
} else
|
||
|
set_error(ERR_NOCHANNEL);
|
||
|
} else
|
||
|
set_error(ERR_SYNTAX30);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Parse block command parameters
|
||
|
* true: OK, false: error
|
||
|
*/
|
||
|
|
||
|
bool D64Drive::parse_bcmd(char *cmd, int *arg1, int *arg2, int *arg3, int *arg4)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
if (*cmd == ':') cmd++;
|
||
|
|
||
|
// Read four parameters separated by space, cursor right or comma
|
||
|
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 false;
|
||
|
|
||
|
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 false;
|
||
|
|
||
|
i = 0;
|
||
|
while (*cmd >= 0x30 && *cmd < 0x40) {
|
||
|
i *= 10;
|
||
|
i += *cmd++ & 0x0f;
|
||
|
}
|
||
|
*arg3 = i & 0xff;
|
||
|
|
||
|
while (*cmd == ' ' || *cmd == 0x1d || *cmd == 0x2c) cmd++;
|
||
|
if (!*cmd) return false;
|
||
|
|
||
|
i = 0;
|
||
|
while (*cmd >= 0x30 && *cmd < 0x40) {
|
||
|
i *= 10;
|
||
|
i += *cmd++ & 0x0f;
|
||
|
}
|
||
|
*arg4 = i & 0xff;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Execute 'G' command
|
||
|
*/
|
||
|
|
||
|
void D64Drive::chd64_cmd(char *d64name)
|
||
|
{
|
||
|
char str[NAMEBUF_LENGTH];
|
||
|
char *p = str;
|
||
|
|
||
|
// Convert .d64 file name
|
||
|
for (int i=0; i<NAMEBUF_LENGTH && (*p++ = conv_from_64(*d64name++, false)); i++) ;
|
||
|
|
||
|
close_all_channels();
|
||
|
|
||
|
// G:. resets the .d64 file name to its original setting
|
||
|
if (str[0] == '.' && str[1] == 0)
|
||
|
open_close_d64_file(orig_d64_name);
|
||
|
else
|
||
|
open_close_d64_file(str);
|
||
|
|
||
|
// Read BAM
|
||
|
read_sector(18, 0, (uint8 *)bam);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Reset drive
|
||
|
*/
|
||
|
|
||
|
void D64Drive::Reset(void)
|
||
|
{
|
||
|
close_all_channels();
|
||
|
|
||
|
read_sector(18, 0, (uint8 *)bam);
|
||
|
|
||
|
cmd_len = 0;
|
||
|
for (int i=0; i<4; i++)
|
||
|
buf_free[i] = true;
|
||
|
|
||
|
set_error(ERR_STARTUP);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Allocate floppy buffer
|
||
|
* -> Desired buffer number or -1
|
||
|
* <- Allocated buffer number or -1
|
||
|
*/
|
||
|
|
||
|
int D64Drive::alloc_buffer(int want)
|
||
|
{
|
||
|
if (want == -1) {
|
||
|
for (want=3; want>=0; want--)
|
||
|
if (buf_free[want]) {
|
||
|
buf_free[want] = false;
|
||
|
return want;
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (want < 4)
|
||
|
if (buf_free[want]) {
|
||
|
buf_free[want] = false;
|
||
|
return want;
|
||
|
} else
|
||
|
return -1;
|
||
|
else
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Free floppy buffer
|
||
|
*/
|
||
|
|
||
|
void D64Drive::free_buffer(int buf)
|
||
|
{
|
||
|
buf_free[buf] = true;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Read sector (256 bytes)
|
||
|
* true: success, false: error
|
||
|
*/
|
||
|
|
||
|
bool D64Drive::read_sector(int track, int sector, uint8 *buffer)
|
||
|
{
|
||
|
int offset;
|
||
|
|
||
|
// Convert track/sector to byte offset in file
|
||
|
if ((offset = offset_from_ts(track, sector)) < 0) {
|
||
|
set_error(ERR_ILLEGALTS);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (the_file == NULL) {
|
||
|
set_error(ERR_NOTREADY);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
#ifdef AMIGA
|
||
|
if (offset != ftell(the_file))
|
||
|
fseek(the_file, offset + image_header, SEEK_SET);
|
||
|
#else
|
||
|
fseek(the_file, offset + image_header, SEEK_SET);
|
||
|
#endif
|
||
|
fread(buffer, 256, 1, the_file);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Convert track/sector to offset
|
||
|
*/
|
||
|
|
||
|
const int num_sectors[41] = {
|
||
|
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,
|
||
|
17,17,17,17,17 // Tracks 36..40
|
||
|
};
|
||
|
|
||
|
const int sector_offset[41] = {
|
||
|
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,
|
||
|
683,700,717,734,751 // Tracks 36..40
|
||
|
};
|
||
|
|
||
|
int D64Drive::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;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Conversion PETSCII->ASCII
|
||
|
*/
|
||
|
|
||
|
uint8 D64Drive::conv_from_64(uint8 c, bool map_slash)
|
||
|
{
|
||
|
if ((c >= 'A') && (c <= 'Z') || (c >= 'a') && (c <= 'z'))
|
||
|
return c ^ 0x20;
|
||
|
if ((c >= 0xc1) && (c <= 0xda))
|
||
|
return c ^ 0x80;
|
||
|
if ((c == '/') && map_slash && ThePrefs.MapSlash)
|
||
|
return '\\';
|
||
|
return c;
|
||
|
}
|