2009-01-13 19:46:42 +01:00
|
|
|
/*
|
|
|
|
* 1541job.cpp - Emulation of 1541 GCR disk reading/writing
|
|
|
|
*
|
|
|
|
* Frodo (C) 1994-1997,2002-2005 Christian Bauer
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Notes:
|
|
|
|
* ------
|
|
|
|
*
|
|
|
|
* - This is only used for processor-level 1541 emulation.
|
|
|
|
* It simulates the 1541 disk controller hardware (R/W head,
|
|
|
|
* GCR reading/writing).
|
|
|
|
* - The preferences settings for drive 8 are used to
|
|
|
|
* specify the .d64 file
|
|
|
|
*
|
|
|
|
* Incompatibilities:
|
|
|
|
* ------------------
|
|
|
|
*
|
|
|
|
* - No GCR writing possible (WriteSector is a ROM patch)
|
|
|
|
* - Programs depending on the exact timing of head movement/disk
|
|
|
|
* rotation don't work
|
|
|
|
* - The .d64 error info is unused
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "sysdeps.h"
|
|
|
|
|
|
|
|
#include "1541job.h"
|
|
|
|
#include "CPU1541.h"
|
|
|
|
#include "Prefs.h"
|
|
|
|
|
2010-02-09 07:16:07 +01:00
|
|
|
#include "utils.hh"
|
2009-01-13 19:46:42 +01:00
|
|
|
|
|
|
|
// Number of tracks/sectors
|
|
|
|
const int NUM_TRACKS = 35;
|
|
|
|
const int NUM_SECTORS = 683;
|
|
|
|
|
|
|
|
// Size of GCR encoded data
|
|
|
|
const int GCR_SECTOR_SIZE = 1+10+9+1+325+8; // SYNC Header Gap SYNC Data Gap (should be 5 SYNC bytes each)
|
|
|
|
const int GCR_TRACK_SIZE = GCR_SECTOR_SIZE * 21; // Each track in gcr_data has 21 sectors
|
|
|
|
const int GCR_DISK_SIZE = GCR_TRACK_SIZE * NUM_TRACKS;
|
|
|
|
|
|
|
|
// Job return codes
|
|
|
|
const int RET_OK = 1; // No error
|
|
|
|
const int RET_NOT_FOUND = 2; // Block not found
|
|
|
|
const int RET_NOT_READY = 15; // Drive not ready
|
|
|
|
|
|
|
|
|
|
|
|
// Number of sectors of each track
|
|
|
|
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
|
|
|
|
};
|
|
|
|
|
|
|
|
// Sector offset of start of track in .d64 file
|
|
|
|
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
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Constructor: Open .d64 file if processor-level 1541
|
|
|
|
* emulation is enabled
|
|
|
|
*/
|
|
|
|
|
|
|
|
Job1541::Job1541(uint8 *ram1541) : ram(ram1541)
|
|
|
|
{
|
|
|
|
the_file = NULL;
|
|
|
|
|
|
|
|
gcr_data = gcr_ptr = gcr_track_start = new uint8[GCR_DISK_SIZE];
|
|
|
|
gcr_track_end = gcr_track_start + GCR_TRACK_SIZE;
|
|
|
|
current_halftrack = 2;
|
|
|
|
|
|
|
|
disk_changed = true;
|
|
|
|
|
|
|
|
if (ThePrefs.Emul1541Proc)
|
|
|
|
open_d64_file(ThePrefs.DrivePath[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Destructor: Close .d64 file
|
|
|
|
*/
|
|
|
|
|
|
|
|
Job1541::~Job1541()
|
|
|
|
{
|
|
|
|
close_d64_file();
|
|
|
|
delete[] gcr_data;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Preferences may have changed
|
|
|
|
*/
|
|
|
|
|
|
|
|
void Job1541::NewPrefs(Prefs *prefs)
|
|
|
|
{
|
|
|
|
// 1541 emulation turned off?
|
|
|
|
if (!prefs->Emul1541Proc)
|
|
|
|
close_d64_file();
|
|
|
|
|
|
|
|
// 1541 emulation turned on?
|
|
|
|
else if (!ThePrefs.Emul1541Proc && prefs->Emul1541Proc)
|
|
|
|
open_d64_file(prefs->DrivePath[0]);
|
|
|
|
|
|
|
|
// .d64 file name changed?
|
|
|
|
else if (strcmp(ThePrefs.DrivePath[0], prefs->DrivePath[0])) {
|
|
|
|
close_d64_file();
|
|
|
|
open_d64_file(prefs->DrivePath[0]);
|
|
|
|
disk_changed = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Open .d64 file
|
|
|
|
*/
|
|
|
|
|
|
|
|
void Job1541::open_d64_file(char *filepath)
|
|
|
|
{
|
|
|
|
long size;
|
|
|
|
uint8 magic[4];
|
|
|
|
uint8 bam[256];
|
|
|
|
|
|
|
|
// Clear GCR buffer
|
|
|
|
memset(gcr_data, 0x55, GCR_DISK_SIZE);
|
|
|
|
|
|
|
|
// Try opening the file for reading/writing first, then for reading only
|
|
|
|
write_protected = false;
|
|
|
|
the_file = fopen(filepath, "rb+");
|
|
|
|
if (the_file == NULL) {
|
|
|
|
write_protected = true;
|
|
|
|
the_file = fopen(filepath, "rb");
|
|
|
|
}
|
|
|
|
if (the_file != 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?
|
|
|
|
fseek(the_file, 0, SEEK_SET);
|
2010-02-09 07:16:07 +01:00
|
|
|
if (fread(&magic, 4, 1, the_file) != 1)
|
|
|
|
warning("Can't read all bytes\n");
|
2009-01-13 19:46:42 +01:00
|
|
|
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);
|
2010-02-09 07:16:07 +01:00
|
|
|
if (fread(&error_info, NUM_SECTORS, 1, the_file) != 1)
|
|
|
|
warning("Can't read all bytes\n");
|
2009-01-13 19:46:42 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
// Read BAM and get ID
|
|
|
|
read_sector(18, 0, bam);
|
|
|
|
id1 = bam[162];
|
|
|
|
id2 = bam[163];
|
|
|
|
|
|
|
|
// Create GCR encoded disk data from image
|
|
|
|
disk2gcr();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Close .d64 file
|
|
|
|
*/
|
|
|
|
|
|
|
|
void Job1541::close_d64_file(void)
|
|
|
|
{
|
|
|
|
if (the_file != NULL) {
|
|
|
|
fclose(the_file);
|
|
|
|
the_file = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Write sector to disk (1541 ROM patch)
|
|
|
|
*/
|
|
|
|
|
|
|
|
void Job1541::WriteSector(void)
|
|
|
|
{
|
|
|
|
int track = ram[0x18];
|
|
|
|
int sector = ram[0x19];
|
|
|
|
uint16 buf = ram[0x30] | (ram[0x31] << 8);
|
|
|
|
|
|
|
|
if (buf <= 0x0700)
|
|
|
|
if (write_sector(track, sector, ram + buf))
|
|
|
|
sector2gcr(track, sector);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Format one track (1541 ROM patch)
|
|
|
|
*/
|
|
|
|
|
|
|
|
void Job1541::FormatTrack(void)
|
|
|
|
{
|
|
|
|
int track = ram[0x51];
|
|
|
|
|
|
|
|
// Get new ID
|
|
|
|
uint8 bufnum = ram[0x3d];
|
|
|
|
id1 = ram[0x12 + bufnum];
|
|
|
|
id2 = ram[0x13 + bufnum];
|
|
|
|
|
|
|
|
// Create empty block
|
|
|
|
uint8 buf[256];
|
|
|
|
memset(buf, 1, 256);
|
|
|
|
buf[0] = 0x4b;
|
|
|
|
|
|
|
|
// Write block to all sectors on track
|
|
|
|
for(int sector=0; sector<num_sectors[track]; sector++) {
|
|
|
|
write_sector(track, sector, buf);
|
|
|
|
sector2gcr(track, sector);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clear error info (all sectors no error)
|
|
|
|
if (track == 35)
|
|
|
|
memset(error_info, 1, NUM_SECTORS);
|
|
|
|
// Write error_info to disk?
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read sector (256 bytes)
|
|
|
|
* true: success, false: error
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool Job1541::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)
|
|
|
|
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
|
2010-02-09 07:16:07 +01:00
|
|
|
if (fread(buffer, 256, 1, the_file) != 1)
|
|
|
|
warning("Can't read all bytes\n");
|
2009-01-13 19:46:42 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Write sector (256 bytes) !! -> GCR
|
|
|
|
* true: success, false: error
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool Job1541::write_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)
|
|
|
|
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
|
|
|
|
fwrite(buffer, 256, 1, the_file);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Convert track/sector to offset
|
|
|
|
*/
|
|
|
|
|
|
|
|
int Job1541::secnum_from_ts(int track, int sector)
|
|
|
|
{
|
|
|
|
return sector_offset[track] + sector;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Job1541::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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Convert 4 bytes to 5 GCR encoded bytes
|
|
|
|
*/
|
|
|
|
|
|
|
|
const uint16 gcr_table[16] = {
|
|
|
|
0x0a, 0x0b, 0x12, 0x13, 0x0e, 0x0f, 0x16, 0x17,
|
|
|
|
0x09, 0x19, 0x1a, 0x1b, 0x0d, 0x1d, 0x1e, 0x15
|
|
|
|
};
|
|
|
|
|
|
|
|
void Job1541::gcr_conv4(uint8 *from, uint8 *to)
|
|
|
|
{
|
|
|
|
uint16 g;
|
|
|
|
|
|
|
|
g = (gcr_table[*from >> 4] << 5) | gcr_table[*from & 15];
|
|
|
|
*to++ = g >> 2;
|
|
|
|
*to = (g << 6) & 0xc0;
|
|
|
|
from++;
|
|
|
|
|
|
|
|
g = (gcr_table[*from >> 4] << 5) | gcr_table[*from & 15];
|
|
|
|
*to++ |= (g >> 4) & 0x3f;
|
|
|
|
*to = (g << 4) & 0xf0;
|
|
|
|
from++;
|
|
|
|
|
|
|
|
g = (gcr_table[*from >> 4] << 5) | gcr_table[*from & 15];
|
|
|
|
*to++ |= (g >> 6) & 0x0f;
|
|
|
|
*to = (g << 2) & 0xfc;
|
|
|
|
from++;
|
|
|
|
|
|
|
|
g = (gcr_table[*from >> 4] << 5) | gcr_table[*from & 15];
|
|
|
|
*to++ |= (g >> 8) & 0x03;
|
|
|
|
*to = g;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create GCR encoded disk data from image
|
|
|
|
*/
|
|
|
|
|
|
|
|
void Job1541::sector2gcr(int track, int sector)
|
|
|
|
{
|
|
|
|
uint8 block[256];
|
|
|
|
uint8 buf[4];
|
|
|
|
uint8 *p = gcr_data + (track-1) * GCR_TRACK_SIZE + sector * GCR_SECTOR_SIZE;
|
|
|
|
|
|
|
|
read_sector(track, sector, block);
|
|
|
|
|
|
|
|
// Create GCR header
|
|
|
|
*p++ = 0xff; // SYNC
|
|
|
|
buf[0] = 0x08; // Header mark
|
|
|
|
buf[1] = sector ^ track ^ id2 ^ id1; // Checksum
|
|
|
|
buf[2] = sector;
|
|
|
|
buf[3] = track;
|
|
|
|
gcr_conv4(buf, p);
|
|
|
|
buf[0] = id2;
|
|
|
|
buf[1] = id1;
|
|
|
|
buf[2] = 0x0f;
|
|
|
|
buf[3] = 0x0f;
|
|
|
|
gcr_conv4(buf, p+5);
|
|
|
|
p += 10;
|
|
|
|
memset(p, 0x55, 9); // Gap
|
|
|
|
p += 9;
|
|
|
|
|
|
|
|
// Create GCR data
|
|
|
|
uint8 sum;
|
|
|
|
*p++ = 0xff; // SYNC
|
|
|
|
buf[0] = 0x07; // Data mark
|
|
|
|
sum = buf[1] = block[0];
|
|
|
|
sum ^= buf[2] = block[1];
|
|
|
|
sum ^= buf[3] = block[2];
|
|
|
|
gcr_conv4(buf, p);
|
|
|
|
p += 5;
|
|
|
|
for (int i=3; i<255; i+=4) {
|
|
|
|
sum ^= buf[0] = block[i];
|
|
|
|
sum ^= buf[1] = block[i+1];
|
|
|
|
sum ^= buf[2] = block[i+2];
|
|
|
|
sum ^= buf[3] = block[i+3];
|
|
|
|
gcr_conv4(buf, p);
|
|
|
|
p += 5;
|
|
|
|
}
|
|
|
|
sum ^= buf[0] = block[255];
|
|
|
|
buf[1] = sum; // Checksum
|
|
|
|
buf[2] = 0;
|
|
|
|
buf[3] = 0;
|
|
|
|
gcr_conv4(buf, p);
|
|
|
|
p += 5;
|
|
|
|
memset(p, 0x55, 8); // Gap
|
|
|
|
}
|
|
|
|
|
|
|
|
void Job1541::disk2gcr(void)
|
|
|
|
{
|
|
|
|
// Convert all tracks and sectors
|
|
|
|
for (int track=1; track<=NUM_TRACKS; track++)
|
|
|
|
for(int sector=0; sector<num_sectors[track]; sector++)
|
|
|
|
sector2gcr(track, sector);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Move R/W head out (lower track numbers)
|
|
|
|
*/
|
|
|
|
|
|
|
|
void Job1541::MoveHeadOut(void)
|
|
|
|
{
|
|
|
|
if (current_halftrack == 2)
|
|
|
|
return;
|
|
|
|
current_halftrack--;
|
2009-01-15 18:28:44 +01:00
|
|
|
#if !defined(__riscos__) && !defined(GEKKO)
|
2009-01-13 19:46:42 +01:00
|
|
|
printf("Head move %d\n", current_halftrack);
|
|
|
|
#endif
|
|
|
|
gcr_ptr = gcr_track_start = gcr_data + ((current_halftrack >> 1) - 1) * GCR_TRACK_SIZE;
|
|
|
|
gcr_track_end = gcr_track_start + num_sectors[current_halftrack >> 1] * GCR_SECTOR_SIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Move R/W head in (higher track numbers)
|
|
|
|
*/
|
|
|
|
|
|
|
|
void Job1541::MoveHeadIn(void)
|
|
|
|
{
|
|
|
|
if (current_halftrack == NUM_TRACKS*2)
|
|
|
|
return;
|
|
|
|
current_halftrack++;
|
2009-01-15 18:28:44 +01:00
|
|
|
#if !defined(__riscos__) && !defined(GEKKO)
|
2009-01-13 19:46:42 +01:00
|
|
|
printf("Head move %d\n", current_halftrack);
|
|
|
|
#endif
|
|
|
|
gcr_ptr = gcr_track_start = gcr_data + ((current_halftrack >> 1) - 1) * GCR_TRACK_SIZE;
|
|
|
|
gcr_track_end = gcr_track_start + num_sectors[current_halftrack >> 1] * GCR_SECTOR_SIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get state
|
|
|
|
*/
|
|
|
|
|
|
|
|
void Job1541::GetState(Job1541State *state)
|
|
|
|
{
|
|
|
|
state->current_halftrack = current_halftrack;
|
|
|
|
state->gcr_ptr = gcr_ptr - gcr_data;
|
|
|
|
state->write_protected = write_protected;
|
|
|
|
state->disk_changed = disk_changed;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set state
|
|
|
|
*/
|
|
|
|
|
|
|
|
void Job1541::SetState(Job1541State *state)
|
|
|
|
{
|
|
|
|
current_halftrack = state->current_halftrack;
|
|
|
|
gcr_ptr = gcr_data + state->gcr_ptr;
|
|
|
|
gcr_track_start = gcr_data + ((current_halftrack >> 1) - 1) * GCR_TRACK_SIZE;
|
|
|
|
gcr_track_end = gcr_track_start + num_sectors[current_halftrack >> 1] * GCR_SECTOR_SIZE;
|
|
|
|
write_protected = state->write_protected;
|
|
|
|
disk_changed = state->disk_changed;
|
|
|
|
}
|