mirror of
https://github.com/bradgrantham/apple2e.git
synced 2024-12-27 17:29:18 +00:00
read tracks from disk on demand
This commit is contained in:
parent
b99576c1e0
commit
8f04f04543
206
apple2e.cpp
206
apple2e.cpp
@ -270,30 +270,41 @@ namespace floppy
|
||||
{
|
||||
|
||||
/*
|
||||
5 1/2" floppy disk images are 35 tracks of 16 sectors of 256 bytes = 143360 bytes
|
||||
5 1/2" floppy disk images are 70 tracks of 16 sectors of 256 bytes
|
||||
But DOS and ProDOS only read even tracks, so floppy images saved on disk contain only even tracks,
|
||||
and a ".dsk" floppy image is only 143360 bytes.
|
||||
*/
|
||||
constexpr int sectorSize = 256;
|
||||
constexpr int sectorsPerTrack = 16;
|
||||
constexpr int tracksPerFloppy = 35;
|
||||
|
||||
const unsigned char sectorHeader[21] = {
|
||||
const unsigned char sectorHeader[21] =
|
||||
{
|
||||
0xD5, 0xAA, 0x96, 0xFF, 0xFE, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0xDE, 0xAA, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xD5, 0xAA, 0xAD };
|
||||
0xFF, 0xFF, 0xD5, 0xAA, 0xAD
|
||||
};
|
||||
|
||||
const unsigned char doSectorSkew[16] = {
|
||||
0x0, 0x7, 0xE, 0x6, 0xD, 0x5, 0xC, 0x4, 0xB, 0x3, 0xA, 0x2, 0x9, 0x1, 0x8, 0xF };
|
||||
const unsigned char poSectorSkew[16] = {
|
||||
0x0, 0x8, 0x1, 0x9, 0x2, 0xA, 0x3, 0xB, 0x4, 0xC, 0x5, 0xD, 0x6, 0xE, 0x7, 0xF };
|
||||
const int sectorSkewDOS[16] =
|
||||
{
|
||||
0x0, 0x7, 0xE, 0x6, 0xD, 0x5, 0xC, 0x4, 0xB, 0x3, 0xA, 0x2, 0x9, 0x1, 0x8, 0xF
|
||||
};
|
||||
const int sectorSkewProDOS[16] =
|
||||
{
|
||||
0x0, 0x8, 0x1, 0x9, 0x2, 0xA, 0x3, 0xB, 0x4, 0xC, 0x5, 0xD, 0x6, 0xE, 0x7, 0xF
|
||||
};
|
||||
|
||||
const unsigned char sectorFooter[48] = {
|
||||
const unsigned char sectorFooter[48] =
|
||||
{
|
||||
0xDE, 0xAA, 0xEB, 0xFF, 0xEB, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
|
||||
const unsigned char SixBitsToBytes[0x40] = {
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
||||
};
|
||||
const unsigned char SixBitsToBytes[0x40] =
|
||||
{
|
||||
0x96, 0x97, 0x9A, 0x9B, 0x9D, 0x9E, 0x9F, 0xA6,
|
||||
0xA7, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB2, 0xB3,
|
||||
0xB4, 0xB5, 0xB6, 0xB7, 0xB9, 0xBA, 0xBB, 0xBC,
|
||||
@ -301,7 +312,8 @@ const unsigned char SixBitsToBytes[0x40] = {
|
||||
0xD6, 0xD7, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE,
|
||||
0xDF, 0xE5, 0xE6, 0xE7, 0xE9, 0xEA, 0xEB, 0xEC,
|
||||
0xED, 0xEE, 0xEF, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6,
|
||||
0xF7, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF };
|
||||
0xF7, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
|
||||
};
|
||||
|
||||
constexpr size_t trackGapSize = 64;
|
||||
constexpr size_t nybblizedSectorSize = 21 + 343 + 48;
|
||||
@ -360,17 +372,8 @@ void nybblizeSector(int trackIndex, int sectorIndex, const uint8_t *sectorBytes,
|
||||
// p += 48;
|
||||
}
|
||||
|
||||
bool nybblizeImage(FILE *floppyImageFile, unsigned char *nybblizedImage, const unsigned char *skew)
|
||||
bool nybblizeTrackFromFile(FILE *floppyImageFile, int trackIndex, unsigned char *nybblizedTrack, const int *skew)
|
||||
{
|
||||
// Format of a sector is header (23) + nybbles (343) + footer (30) = 396
|
||||
// (short by 20 bytes of 416 [413 if 48 byte header is one time only])
|
||||
// hdr (21) + nybbles (343) + footer (48) = 412 bytes per sector
|
||||
// (not incl. 64 byte track marker)
|
||||
|
||||
for(unsigned char trackIndex = 0; trackIndex < 35; trackIndex++)
|
||||
{
|
||||
unsigned char *nybblizedTrack = nybblizedImage + trackIndex * nybblizedTrackSize;
|
||||
|
||||
memset(nybblizedTrack, 0xFF, trackGapSize); // Write gap 1, 64 bytes (self-sync)
|
||||
|
||||
for(unsigned char sectorIndex = 0; sectorIndex < 16; sectorIndex++)
|
||||
@ -386,11 +389,11 @@ bool nybblizeImage(FILE *floppyImageFile, unsigned char *nybblizedImage, const u
|
||||
size_t wasRead = fread(sectorBytes, 1, sectorSize, floppyImageFile);
|
||||
if(wasRead != sectorSize) {
|
||||
fprintf(stderr, "failed to read sector from floppy disk image\n");
|
||||
printf("track %d, sectorIndex %d, skew %d, offset %ld, read %zd\n", trackIndex, sectorIndex, skew[sectorIndex], sectorOffset, wasRead);
|
||||
return false;
|
||||
}
|
||||
|
||||
nybblizeSector(trackIndex, sectorIndex, sectorBytes, nybblizedTrack + sectorIndex * nybblizedSectorSize);
|
||||
}
|
||||
nybblizeSector(trackIndex, sectorIndex, sectorBytes, nybblizedTrack + trackGapSize + sectorIndex * nybblizedSectorSize);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -411,7 +414,7 @@ struct DISKIIboard : board_base
|
||||
static constexpr int Q7L = 0xC0EE; // IO strobe for clear
|
||||
static constexpr int Q7H = 0xC0EF; // IO strobe for shift
|
||||
|
||||
map<unsigned int, string> io = {
|
||||
const map<unsigned int, string> io = {
|
||||
{0xC0E0, "CA0OFF"},
|
||||
{0xC0E1, "CA0ON"},
|
||||
{0xC0E2, "CA1OFF"},
|
||||
@ -432,23 +435,28 @@ struct DISKIIboard : board_base
|
||||
|
||||
backed_region rom_C600 = {"rom_C600", 0xC600, 0x0100, ROM, nullptr, [&]{return true;}};
|
||||
|
||||
unsigned char *floppy_nybblized[2]; // [232960];
|
||||
const unsigned int bytes_per_nybblized_track = 6656;
|
||||
bool floppy_present[2];
|
||||
bool floppyPresent[2];
|
||||
std::string floppyImageNames[2];
|
||||
FILE *floppyImageFiles[2] = {nullptr, nullptr};
|
||||
const int *floppySectorSkew[2];
|
||||
|
||||
int drive_selected = 0;
|
||||
bool drive_motor_enabled[2];
|
||||
enum {READ, WRITE} head_mode = READ;
|
||||
unsigned char data_latch = 0x00;
|
||||
int head_stepper_phase[4] = {0, 0, 0, 0};
|
||||
int head_stepper_most_recent_phase = 0;
|
||||
int track_number = 0; // physical track number - DOS and ProDOS only use even tracks
|
||||
unsigned int track_byte = 0;
|
||||
// Floppy drive control
|
||||
int driveSelected = 0;
|
||||
bool driveMotorEnabled[2];
|
||||
enum {READ, WRITE} headMode = READ;
|
||||
unsigned char dataLatch = 0x00;
|
||||
int headStepperPhase[4] = {0, 0, 0, 0};
|
||||
int headStepperMostRecentPhase = 0;
|
||||
|
||||
// track data
|
||||
uint8_t trackBytes[floppy::nybblizedTrackSize];
|
||||
int trackNumber[2] = {0, 0}; // physical track number - DOS and ProDOS only use even tracks
|
||||
unsigned int trackByte = 0;
|
||||
|
||||
void set_floppy(int number, const char *name) // number 0 or 1; name = NULL to eject
|
||||
{
|
||||
floppy_present[number] = false;
|
||||
floppyPresent[number] = false;
|
||||
floppyImageNames[number] = "";
|
||||
|
||||
if(name) {
|
||||
if(floppyImageFiles[number]) {
|
||||
@ -463,21 +471,15 @@ struct DISKIIboard : board_base
|
||||
|
||||
} else {
|
||||
|
||||
const unsigned char *skew;
|
||||
if(strcmp(name + strlen(name) - 3, ".po") == 0) {
|
||||
printf("ProDOS floppy\n");
|
||||
skew = floppy::poSectorSkew;
|
||||
floppySectorSkew[number] = floppy::sectorSkewProDOS;
|
||||
} else {
|
||||
skew = floppy::doSectorSkew;
|
||||
floppySectorSkew[number] = floppy::sectorSkewDOS;
|
||||
}
|
||||
|
||||
bool success = floppy::nybblizeImage(floppyImageFiles[number], floppy_nybblized[number], skew);
|
||||
if(!success) {
|
||||
floppyImageFiles[number] = nullptr;
|
||||
fprintf(stderr, "unexpected failure reading sector from disk \"%s\"\n", name);
|
||||
}
|
||||
|
||||
floppy_present[number] = true;
|
||||
floppyPresent[number] = true;
|
||||
floppyImageNames[number] = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -490,57 +492,80 @@ struct DISKIIboard : board_base
|
||||
{
|
||||
std::copy(diskII_rom, diskII_rom + 0x100, rom_C600.memory.begin());
|
||||
if(floppy0_name) {
|
||||
floppy_nybblized[0] = new (std::nothrow) unsigned char[232960];
|
||||
if(!floppy_nybblized[0]) {
|
||||
throw "failed to allocate floppy_nybblized[0]";
|
||||
}
|
||||
set_floppy(0, floppy0_name);
|
||||
}
|
||||
if(floppy1_name) {
|
||||
floppy_nybblized[1] = new (std::nothrow) unsigned char[232960];
|
||||
if(!floppy_nybblized[1]) {
|
||||
throw "failed to allocate floppy_nybblized[1]";
|
||||
}
|
||||
set_floppy(1, floppy1_name);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char read_next_nybblized_byte()
|
||||
unsigned char readNextTrackByte()
|
||||
{
|
||||
if(head_mode != READ || !drive_motor_enabled[drive_selected] || !floppy_present[drive_selected])
|
||||
if(headMode != READ || !driveMotorEnabled[driveSelected] || !floppyPresent[driveSelected])
|
||||
return 0x00;
|
||||
int i = track_byte;
|
||||
track_byte = (track_byte + 1) % bytes_per_nybblized_track;
|
||||
return floppy_nybblized[drive_selected][(track_number / 2) * bytes_per_nybblized_track + i];
|
||||
|
||||
uint8_t data = trackBytes[trackByte];
|
||||
|
||||
trackByte = (trackByte + 1) % floppy::nybblizedTrackSize;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void control_track_motor(unsigned int addr)
|
||||
bool readDriveTrack()
|
||||
{
|
||||
if(!floppyPresent[driveSelected]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = floppy::nybblizeTrackFromFile(floppyImageFiles[driveSelected], trackNumber[driveSelected] / 2, trackBytes, floppySectorSkew[driveSelected]);
|
||||
if(!success) {
|
||||
fprintf(stderr, "unexpected failure reading track from disk \"%s\"\n", floppyImageNames[driveSelected].c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void controlTrackMotor(unsigned int addr)
|
||||
{
|
||||
int phase = (addr & 0x7) >> 1;
|
||||
int state = addr & 0x1;
|
||||
head_stepper_phase[phase] = state;
|
||||
|
||||
headStepperPhase[phase] = state;
|
||||
|
||||
if(debug & DEBUG_FLOPPY) printf("stepper %04X, phase %d, state %d, so stepper motor state now: %d, %d, %d, %d\n",
|
||||
addr, phase, state,
|
||||
head_stepper_phase[0], head_stepper_phase[1],
|
||||
head_stepper_phase[2], head_stepper_phase[3]);
|
||||
headStepperPhase[0], headStepperPhase[1],
|
||||
headStepperPhase[2], headStepperPhase[3]);
|
||||
|
||||
if(state == 1) { // turn stepper motor phase on
|
||||
if(head_stepper_most_recent_phase == (((phase - 1) + 4) % 4)) { // stepping up
|
||||
track_number = min(track_number + 1, 70);
|
||||
// XXX load track
|
||||
if(debug & DEBUG_FLOPPY) printf("track number now %d\n", track_number);
|
||||
} else if(head_stepper_most_recent_phase == ((phase + 1) % 4)) { // stepping down
|
||||
track_number = max(0, track_number - 1);
|
||||
// XXX load track
|
||||
if(debug & DEBUG_FLOPPY) printf("track number now %d\n", track_number);
|
||||
} else if(head_stepper_most_recent_phase == phase) { // unexpected condition
|
||||
|
||||
if(headStepperMostRecentPhase == (((phase - 1) + 4) % 4)) { // stepping up
|
||||
|
||||
trackNumber[driveSelected] = min(trackNumber[driveSelected] + 1, 69);
|
||||
readDriveTrack();
|
||||
if(debug & DEBUG_FLOPPY) printf("track number now %d\n", trackNumber[driveSelected]);
|
||||
|
||||
} else if(headStepperMostRecentPhase == ((phase + 1) % 4)) { // stepping down
|
||||
|
||||
trackNumber[driveSelected] = max(0, trackNumber[driveSelected] - 1);
|
||||
readDriveTrack();
|
||||
if(debug & DEBUG_FLOPPY) printf("track number now %d\n", trackNumber[driveSelected]);
|
||||
|
||||
} else if(headStepperMostRecentPhase == phase) { // unexpected condition
|
||||
|
||||
if(debug & DEBUG_FLOPPY) printf("track head stepper no change\n");
|
||||
|
||||
} else { // unexpected condition
|
||||
|
||||
if(debug & DEBUG_WARN) fprintf(stderr, "unexpected track stepper motor state: %d, %d, %d, %d\n",
|
||||
head_stepper_phase[0], head_stepper_phase[1],
|
||||
head_stepper_phase[2], head_stepper_phase[3]);
|
||||
if(debug & DEBUG_WARN) fprintf(stderr, "most recent phase: %d\n", head_stepper_most_recent_phase);
|
||||
headStepperPhase[0], headStepperPhase[1],
|
||||
headStepperPhase[2], headStepperPhase[3]);
|
||||
if(debug & DEBUG_WARN) fprintf(stderr, "most recent phase: %d\n", headStepperMostRecentPhase);
|
||||
|
||||
}
|
||||
head_stepper_most_recent_phase = phase;
|
||||
|
||||
headStepperMostRecentPhase = phase;
|
||||
}
|
||||
}
|
||||
|
||||
@ -548,9 +573,10 @@ struct DISKIIboard : board_base
|
||||
{
|
||||
if(addr < 0xC0E0 || addr > 0xC0EF)
|
||||
return false;
|
||||
if(debug & DEBUG_WARN) printf("DISK II unhandled write of %02X to %04X (%s)\n", data, addr, io[addr].c_str());
|
||||
if(debug & DEBUG_WARN) printf("DISK II unhandled write of %02X to %04X (%s)\n", data, addr, io.at(addr).c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool read(int addr, unsigned char &data)
|
||||
{
|
||||
if(rom_C600.read(addr, data)) {
|
||||
@ -564,52 +590,54 @@ struct DISKIIboard : board_base
|
||||
|
||||
if(addr >= CA0 && addr <= (CA3 + 1)) {
|
||||
if(debug & DEBUG_FLOPPY) printf("floppy control track motor\n");
|
||||
control_track_motor(addr);
|
||||
controlTrackMotor(addr);
|
||||
data = 0;
|
||||
return true;
|
||||
} else if(addr == Q6L) { // 0xC0EC
|
||||
data = read_next_nybblized_byte();
|
||||
data = readNextTrackByte();
|
||||
if(debug & DEBUG_FLOPPY) printf("floppy read byte : %02X\n", data);
|
||||
return true;
|
||||
} else if(addr == Q6H) { // 0xC0ED
|
||||
if(debug & DEBUG_FLOPPY) printf("floppy read latch\n");
|
||||
data = data_latch; // XXX do something with the latch - e.g. set write-protect bit
|
||||
data = dataLatch; // XXX do something with the latch - e.g. set write-protect bit
|
||||
data = 0;
|
||||
return true;
|
||||
} else if(addr == Q7L) { // 0xC0EE
|
||||
if(debug & DEBUG_FLOPPY) printf("floppy set read\n");
|
||||
head_mode = READ;
|
||||
headMode = READ;
|
||||
data = 0;
|
||||
return true;
|
||||
} else if(addr == Q7H) { // 0xC0EF
|
||||
if(debug & DEBUG_FLOPPY) printf("floppy set write\n");
|
||||
head_mode = WRITE;
|
||||
headMode = WRITE;
|
||||
data = 0;
|
||||
return true;
|
||||
} else if(addr == SELECT) {
|
||||
if(debug & DEBUG_FLOPPY) printf("floppy select first drive\n");
|
||||
drive_selected = 0;
|
||||
driveSelected = 0;
|
||||
readDriveTrack();
|
||||
return true;
|
||||
} else if(addr == SELECT + 1) {
|
||||
if(debug & DEBUG_FLOPPY) printf("floppy select second drive\n");
|
||||
drive_selected = 1;
|
||||
driveSelected = 1;
|
||||
readDriveTrack();
|
||||
return true;
|
||||
} else if(addr == ENABLE) {
|
||||
if(debug & DEBUG_FLOPPY) printf("floppy switch off\n");
|
||||
drive_motor_enabled[drive_selected] = false;
|
||||
floppy_activity(drive_selected, false);
|
||||
driveMotorEnabled[driveSelected] = false;
|
||||
floppy_activity(driveSelected, false);
|
||||
// go disable reading
|
||||
// disable other drive?
|
||||
return true;
|
||||
} else if(addr == ENABLE + 1) {
|
||||
if(debug & DEBUG_FLOPPY) printf("floppy switch on\n");
|
||||
drive_motor_enabled[drive_selected] = true;
|
||||
floppy_activity(drive_selected, true);
|
||||
driveMotorEnabled[driveSelected] = true;
|
||||
floppy_activity(driveSelected, true);
|
||||
// go enable reading
|
||||
// disable other drive?
|
||||
return true;
|
||||
}
|
||||
if(debug & DEBUG_WARN) printf("DISK II unhandled read from %04X (%s)\n", addr, io[addr].c_str());
|
||||
if(debug & DEBUG_WARN) printf("DISK II unhandled read from %04X (%s)\n", addr, io.at(addr).c_str());
|
||||
data = 0;
|
||||
return true;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user