tie disk spin-down to CPU cycles

This commit is contained in:
Jorj Bauer 2019-02-22 01:01:48 -05:00
parent f333780348
commit c5e41a5ea7
6 changed files with 90 additions and 63 deletions

View File

@ -132,6 +132,7 @@ void AppleVM::cpuMaintenance(uint32_t cycles)
} }
keyboard->maintainKeyboard(cycles); keyboard->maintainKeyboard(cycles);
disk6->maintenance(cycles);
} }
void AppleVM::Reset() void AppleVM::Reset()

View File

@ -19,6 +19,9 @@
#define DISKIIMAGIC 0xAA #define DISKIIMAGIC 0xAA
// how many CPU cycles do we wait to spin down the disk drive?
#define SPINDOWNDELAY (1023)
DiskII::DiskII(AppleMMU *mmu) DiskII::DiskII(AppleMMU *mmu)
{ {
this->mmu = mmu; this->mmu = mmu;
@ -35,7 +38,7 @@ DiskII::DiskII(AppleMMU *mmu)
lastDiskRead[0] = lastDiskRead[1] = 0; lastDiskRead[0] = lastDiskRead[1] = 0;
disk[0] = disk[1] = NULL; disk[0] = disk[1] = NULL;
indicatorIsOn[0] = indicatorIsOn[1] = 0; diskIsSpinningUntil[0] = diskIsSpinningUntil[1] = 0;
selectedDisk = 0; selectedDisk = 0;
} }
@ -71,8 +74,24 @@ bool DiskII::Serialize(int8_t fd)
return false; return false;
} }
g_filemanager->writeByte(fd, indicatorIsOn[0]); g_filemanager->writeByte(fd,
g_filemanager->writeByte(fd, indicatorIsOn[1]); (diskIsSpinningUntil[0] & 0xFF000000) >> 24);
g_filemanager->writeByte(fd,
(diskIsSpinningUntil[0] & 0x00FF0000) >> 16);
g_filemanager->writeByte(fd,
(diskIsSpinningUntil[0] & 0x0000FF00) >> 8);
g_filemanager->writeByte(fd,
(diskIsSpinningUntil[0] & 0x000000FF) );
g_filemanager->writeByte(fd,
(diskIsSpinningUntil[1] & 0xFF000000) >> 24);
g_filemanager->writeByte(fd,
(diskIsSpinningUntil[1] & 0x00FF0000) >> 16);
g_filemanager->writeByte(fd,
(diskIsSpinningUntil[1] & 0x0000FF00) >> 8);
g_filemanager->writeByte(fd,
(diskIsSpinningUntil[1] & 0x000000FF) );
g_filemanager->writeByte(fd, selectedDisk); g_filemanager->writeByte(fd, selectedDisk);
@ -112,8 +131,15 @@ bool DiskII::Deserialize(int8_t fd)
} }
} }
indicatorIsOn[0] = g_filemanager->readByte(fd); diskIsSpinningUntil[0] = g_filemanager->readByte(fd);
indicatorIsOn[1] = g_filemanager->readByte(fd); diskIsSpinningUntil[0] <<= 8;diskIsSpinningUntil[0] = g_filemanager->readByte(fd);
diskIsSpinningUntil[0] <<= 8;diskIsSpinningUntil[0] = g_filemanager->readByte(fd);
diskIsSpinningUntil[0] <<= 8;diskIsSpinningUntil[0] = g_filemanager->readByte(fd);
diskIsSpinningUntil[1] = g_filemanager->readByte(fd);
diskIsSpinningUntil[1] <<= 8;diskIsSpinningUntil[1] = g_filemanager->readByte(fd);
diskIsSpinningUntil[1] <<= 8;diskIsSpinningUntil[1] = g_filemanager->readByte(fd);
diskIsSpinningUntil[1] <<= 8;diskIsSpinningUntil[1] = g_filemanager->readByte(fd);
selectedDisk = g_filemanager->readByte(fd); selectedDisk = g_filemanager->readByte(fd);
@ -162,11 +188,13 @@ uint8_t DiskII::readSwitches(uint8_t s)
break; break;
case 0x08: // drive off case 0x08: // drive off
indicatorIsOn[selectedDisk] = 99; diskIsSpinningUntil[selectedDisk] = g_cpu->cycles + SPINDOWNDELAY; // 1 second lag
g_ui->drawOnOffUIElement(UIeDisk1_activity + selectedDisk, false); // FIXME: delay a bit? Queue for later drawing? *** if (diskIsSpinningUntil[selectedDisk] == -1 ||
diskIsSpinningUntil[selectedDisk] == 0)
diskIsSpinningUntil[selectedDisk] = 2; // fudge magic numbers; 0 is "off" and -1 is "forever".
break; break;
case 0x09: // drive on case 0x09: // drive on
indicatorIsOn[selectedDisk] = 100; diskIsSpinningUntil[selectedDisk] = -1; // magic "forever"
g_ui->drawOnOffUIElement(UIeDisk1_activity + selectedDisk, true); // FIXME: delay a bit? Queue for later drawing? *** g_ui->drawOnOffUIElement(UIeDisk1_activity + selectedDisk, true); // FIXME: delay a bit? Queue for later drawing? ***
// Start the given disk drive spinning // Start the given disk drive spinning
@ -182,10 +210,6 @@ uint8_t DiskII::readSwitches(uint8_t s)
case 0x0C: // shift one read or write byte case 0x0C: // shift one read or write byte
readWriteLatch = readOrWriteByte(); readWriteLatch = readOrWriteByte();
/*
if (readWriteLatch & 0x80)
printf(" => Disk II reads 0x%.2X @ $%.4X\n", sequencer, g_cpu->pc);
*/
if (readWriteLatch & 0x80) if (readWriteLatch & 0x80)
sequencer = 0; sequencer = 0;
break; break;
@ -209,24 +233,6 @@ uint8_t DiskII::readSwitches(uint8_t s)
break; break;
} }
// FIXME: improve the spin-down here. We need a CPU cycle callback
// for some period of time instead of this silly decrement counter ***
if (!indicatorIsOn[selectedDisk]) {
// printf("Unexpected read while disk isn't on?\n");
indicatorIsOn[selectedDisk] = 100;
g_ui->drawOnOffUIElement(UIeDisk1_activity + selectedDisk, true); // FIXME: queue for later drawing?
}
if (indicatorIsOn[selectedDisk] > 0 && indicatorIsOn[selectedDisk] < 100) {
// slowly spin it down...
if (--indicatorIsOn[selectedDisk] == 0) {
g_ui->drawOnOffUIElement(UIeDisk1_activity + selectedDisk, false); // FIXME: queue for later drawing?
// Stop the given disk drive spinning
lastDiskRead[selectedDisk] = 0; // FIXME: magic value. We need a tristate for this. ***
}
}
// Any even address read returns the readWriteLatch (UTA2E Table 9.1, // Any even address read returns the readWriteLatch (UTA2E Table 9.1,
// p. 9-12, note 2) // p. 9-12, note 2)
return (s & 1) ? FLOATING : readWriteLatch; return (s & 1) ? FLOATING : readWriteLatch;
@ -257,8 +263,17 @@ void DiskII::writeSwitches(uint8_t s, uint8_t v)
break; break;
case 0x08: // drive off case 0x08: // drive off
diskIsSpinningUntil[selectedDisk] = g_cpu->cycles + SPINDOWNDELAY; // 1 second lag
if (diskIsSpinningUntil[selectedDisk] == -1 ||
diskIsSpinningUntil[selectedDisk] == 0)
diskIsSpinningUntil[selectedDisk] = 2; // fudge magic numbers; 0 is "off" and -1 is "forever".
break; break;
case 0x09: // drive on case 0x09: // drive on
diskIsSpinningUntil[selectedDisk] = -1; // magic "forever"
g_ui->drawOnOffUIElement(UIeDisk1_activity + selectedDisk, true); // FIXME: delay a bit? Queue for later drawing? ***
// Start the given disk drive spinning
lastDiskRead[selectedDisk] = g_cpu->cycles;
break; break;
case 0x0A: // select drive 1 case 0x0A: // select drive 1
@ -418,8 +433,11 @@ void DiskII::select(int8_t which)
return; return;
if (which != selectedDisk) { if (which != selectedDisk) {
#if 0
*** fixme check if the drive is still "on"
indicatorIsOn[selectedDisk] = 100; // spindown time (fixme) indicatorIsOn[selectedDisk] = 100; // spindown time (fixme)
g_ui->drawOnOffUIElement(UIeDisk1_activity + selectedDisk, false); // FIXME: queue for later drawing? g_ui->drawOnOffUIElement(UIeDisk1_activity + selectedDisk, false); // FIXME: queue for later drawing?
#endif
// set the selected disk drive // set the selected disk drive
selectedDisk = which; selectedDisk = which;
@ -445,40 +463,41 @@ uint8_t DiskII::readOrWriteByte()
uint32_t curCycles = g_cpu->cycles; uint32_t curCycles = g_cpu->cycles;
bool updateCycles = false; bool updateCycles = false;
if (lastDiskRead[selectedDisk] == 0) { if (diskIsSpinningUntil[selectedDisk] >= curCycles) {
// assume it's a first-read-after-spinup; return the first valid data
sequencer = disk[selectedDisk]->nextDiskByte(curWozTrack[selectedDisk]); if (lastDiskRead[selectedDisk] == 0) {
updateCycles = true; // assume it's a first-read-after-spinup; return the first valid data
goto done; sequencer = disk[selectedDisk]->nextDiskBit(curWozTrack[selectedDisk]);
} updateCycles = true;
goto done;
// Otherwise we figure out how many cycles we missed since the last }
// disk read, and pop the right number of bits off the woz track
uint32_t missedCycles; // Otherwise we figure out how many cycles we missed since the last
missedCycles = curCycles - lastDiskRead[selectedDisk]; // disk read, and pop the right number of bits off the woz track
uint32_t missedCycles;
missedCycles >>= 2; missedCycles = curCycles - lastDiskRead[selectedDisk];
if (missedCycles)
updateCycles = true; missedCycles >>= 2;
while (missedCycles) { if (missedCycles)
sequencer <<= 1; updateCycles = true;
sequencer |= disk[selectedDisk]->nextDiskBit(curWozTrack[selectedDisk]); while (missedCycles) {
missedCycles--; sequencer <<= 1;
sequencer |= disk[selectedDisk]->nextDiskBit(curWozTrack[selectedDisk]);
missedCycles--;
}
} }
done: done:
if (updateCycles) { if (updateCycles) {
// We only update the lastDiskRead counter if the number of passed // We only update the lastDiskRead counter if the number of passed
// cycles indicates that we did some sort of work... // cycles indicates that we did some sort of work...
lastDiskRead[selectedDisk] = curCycles; lastDiskRead[selectedDisk] = curCycles;
} }
return sequencer; return sequencer;
} }
void DiskII::fillDiskBuffer()
{
}
const char *DiskII::DiskName(int8_t num) const char *DiskII::DiskName(int8_t num)
{ {
// *** // ***
@ -509,3 +528,18 @@ void DiskII::flushTrack(int8_t track, int8_t sel)
// *** // ***
} }
void DiskII::maintenance(uint32_t cycle)
{
// Handle spin-down for the drive. Drives stay on for a second after
// the stop was noticed.
for (int i=0; i<2; i++) {
if (diskIsSpinningUntil[i] &&
g_cpu->cycles > diskIsSpinningUntil[i]) {
// Stop the given disk drive spinning
lastDiskRead[i] = 0; // FIXME: magic value. We need a tristate for this. ***
diskIsSpinningUntil[i] = 0;
g_ui->drawOnOffUIElement(UIeDisk1_activity + i, false); // FIXME: queue for later drawing?
}
}
}

View File

@ -36,7 +36,7 @@ class DiskII : public Slot {
const char *DiskName(int8_t num); const char *DiskName(int8_t num);
void flushTrack(int8_t track, int8_t sel); void flushTrack(int8_t track, int8_t sel);
void fillDiskBuffer(); // called from main loop void maintenance(uint32_t cycles);
private: private:
void setPhase(uint8_t phase); void setPhase(uint8_t phase);
@ -63,7 +63,7 @@ class DiskII : public Slot {
bool writeProt; bool writeProt;
AppleMMU *mmu; AppleMMU *mmu;
volatile uint8_t indicatorIsOn[2]; volatile uint32_t diskIsSpinningUntil[2];
volatile int8_t selectedDisk; volatile int8_t selectedDisk;
}; };

View File

@ -422,9 +422,6 @@ int main(int argc, char *argv[])
static uint32_t usleepcycles = 16384; // step-down for display drawing. Dynamically updated based on FPS calculations. static uint32_t usleepcycles = 16384; // step-down for display drawing. Dynamically updated based on FPS calculations.
// fill disk buffer when needed
((AppleVM*)g_vm)->disk6->fillDiskBuffer();
g_ui->blit(); g_ui->blit();
if (g_vm->vmdisplay->needsRedraw()) { if (g_vm->vmdisplay->needsRedraw()) {
AiieRect what = g_vm->vmdisplay->getDirtyRect(); AiieRect what = g_vm->vmdisplay->getDirtyRect();

View File

@ -293,9 +293,6 @@ int main(int argc, char *argv[])
static uint32_t usleepcycles = 16384*4; // step-down for display drawing. Dynamically updated based on FPS calculations. static uint32_t usleepcycles = 16384*4; // step-down for display drawing. Dynamically updated based on FPS calculations.
// fill disk buffer when needed
((AppleVM*)g_vm)->disk6->fillDiskBuffer();
if (g_vm->vmdisplay->needsRedraw()) { if (g_vm->vmdisplay->needsRedraw()) {
AiieRect what = g_vm->vmdisplay->getDirtyRect(); AiieRect what = g_vm->vmdisplay->getDirtyRect();
// make sure to clear the flag before drawing; there's no lock // make sure to clear the flag before drawing; there's no lock

View File

@ -244,8 +244,6 @@ void loop()
biosInterrupt(); biosInterrupt();
} }
((AppleVM*)g_vm)->disk6->fillDiskBuffer();
g_keyboard->maintainKeyboard(); g_keyboard->maintainKeyboard();
//debugLCDState = !debugLCDState; //debugLCDState = !debugLCDState;