- Changed Disk_IORead and Disk_IOWrite and related functions to better reflect actual Disk II controller operation, mainly around reading & writing the latch

- Removed old disk stepping code as DataWiz confirmed he modified his Chessmaster 2000 crack to work on old AppleWin - this closes bug #18109 - https://groups.google.com/d/topic/comp.sys.apple2/xHrW45igYY8/discussion
- Added a floppyloadmode variable for emulating $C08D (see UTA2E page 9-13) but commented out use of it as it's mostly implicit in this implementation
- Tidied up logic in DiskReadWrite
- Improved disk debug logging
- Added TODO: ImageWriteTrack shouldn't take pFloppy->phase as a parameter
This commit is contained in:
sicklittlemonkey 2015-07-04 01:19:26 +12:00
parent ed0e8925a4
commit e37223feb0

View File

@ -4,7 +4,7 @@ AppleWin : An Apple //e emulator for Windows
Copyright (C) 1994-1996, Michael O'Brien Copyright (C) 1994-1996, Michael O'Brien
Copyright (C) 1999-2001, Oliver Schmidt Copyright (C) 1999-2001, Oliver Schmidt
Copyright (C) 2002-2005, Tom Charlesworth Copyright (C) 2002-2005, Tom Charlesworth
Copyright (C) 2006-2014, Tom Charlesworth, Michael Pohoreski Copyright (C) 2006-2015, Tom Charlesworth, Michael Pohoreski, Nick Westgate
AppleWin is free software; you can redistribute it and/or modify AppleWin is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -24,6 +24,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/* Description: Disk /* Description: Disk
* *
* Author: Various * Author: Various
*
* In comments, UTA2E is an abbreviation for a reference to "Understanding the Apple //e" by James Sather
*/ */
#include "StdAfx.h" #include "StdAfx.h"
@ -32,6 +34,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "Disk.h" #include "Disk.h"
#include "DiskImage.h" #include "DiskImage.h"
#include "Frame.h" #include "Frame.h"
#include "Log.h"
#include "Memory.h" #include "Memory.h"
#include "Registry.h" #include "Registry.h"
#include "Video.h" #include "Video.h"
@ -108,6 +111,7 @@ static BOOL diskaccessed = 0;
static Disk_t g_aFloppyDisk[NUM_DRIVES]; static Disk_t g_aFloppyDisk[NUM_DRIVES];
static BYTE floppylatch = 0; static BYTE floppylatch = 0;
static BOOL floppymotoron = 0; static BOOL floppymotoron = 0;
static BOOL floppyloadmode = 0; // for efficiency this is not used; it's extremely unlikely to affect emulation (nickw)
static BOOL floppywritemode = 0; static BOOL floppywritemode = 0;
static WORD phases; // state bits for stepper magnet phases 0 - 3 static WORD phases; // state bits for stepper magnet phases 0 - 3
static bool g_bSaveDiskImage = true; // Save the DiskImage name to Registry static bool g_bSaveDiskImage = true; // Save the DiskImage name to Registry
@ -152,13 +156,21 @@ char* DiskGetCurrentState(void)
else if (floppywritemode) else if (floppywritemode)
{ {
if (g_aFloppyDisk[currdrive].bWriteProtected) if (g_aFloppyDisk[currdrive].bWriteProtected)
return "Writing";
else
return "Writing (write protected)"; return "Writing (write protected)";
else
return "Writing";
} }
else else
{ {
return "Reading"; /*if (floppyloadmode)
{
if (g_aFloppyDisk[currdrive].bWriteProtected)
return "Reading write protect state (write protected)";
else
return "Reading write protect state (not write protected)";
}
else*/
return "Reading";
} }
} }
@ -331,7 +343,7 @@ static void ReadTrack(const int iDrive)
if (pFloppy->trackimage && pFloppy->imagehandle) if (pFloppy->trackimage && pFloppy->imagehandle)
{ {
LOG_DISK("read track %2X%s\r", pFloppy->track, (pFloppy->phase & 1) ? ".5" : ""); LOG_DISK("track $%02X%s read\r\n", pFloppy->track, (pFloppy->phase & 1) ? ".5" : " ");
ImageReadTrack( ImageReadTrack(
pFloppy->imagehandle, pFloppy->imagehandle,
@ -389,12 +401,16 @@ static void WriteTrack(const int iDrive)
return; return;
if (pFloppy->trackimage && pFloppy->imagehandle) if (pFloppy->trackimage && pFloppy->imagehandle)
{
LOG_DISK("track $%02X%s write\r\n", pFloppy->track, (pFloppy->phase & 0) ? ".5" : " "); // TODO: hard-coded to whole tracks - see below (nickw)
ImageWriteTrack( ImageWriteTrack(
pFloppy->imagehandle, pFloppy->imagehandle,
pFloppy->track, pFloppy->track,
pFloppy->phase, pFloppy->phase, // TODO: this should never be used; it's the current phase (half-track), not that of the track to be written (nickw)
pFloppy->trackimage, pFloppy->trackimage,
pFloppy->nibbles ); pFloppy->nibbles);
}
pFloppy->trackimagedirty = 0; pFloppy->trackimagedirty = 0;
} }
@ -415,34 +431,32 @@ void DiskBoot(void)
//=========================================================================== //===========================================================================
static BYTE __stdcall DiskControlMotor(WORD, WORD address, BYTE, BYTE, ULONG uExecutedCycles) static void __stdcall DiskControlMotor(WORD, WORD address, BYTE, BYTE, ULONG uExecutedCycles)
{ {
floppymotoron = address & 1; floppymotoron = address & 1;
//LOG_DISK("motor %s\r\n", (floppymotoron) ? "on" : "off");
CheckSpinning(); CheckSpinning();
return MemReadFloatingBus(1, uExecutedCycles); // TC-TODO: Check b7 always set
} }
//=========================================================================== //===========================================================================
static BYTE __stdcall DiskControlStepper(WORD, WORD address, BYTE, BYTE, ULONG uExecutedCycles) static void __stdcall DiskControlStepper(WORD, WORD address, BYTE, BYTE, ULONG uExecutedCycles)
{ {
Disk_t * fptr = &g_aFloppyDisk[currdrive]; Disk_t * fptr = &g_aFloppyDisk[currdrive];
#if 1 int phase = (address >> 1) & 3;
int phase = (address >> 1) & 3;
int phase_bit = (1 << phase); int phase_bit = (1 << phase);
#if 1
// update the magnet states // update the magnet states
if (address & 1) if (address & 1)
{ {
// phase on // phase on
phases |= phase_bit; phases |= phase_bit;
LOG_DISK("track %02X phases %X phase %d on address $C0E%X\r", fptr->phase, phases, phase, address & 0xF);
} }
else else
{ {
// phase off // phase off
phases &= ~phase_bit; phases &= ~phase_bit;
LOG_DISK("track %02X phases %X phase %d off address $C0E%X\r", fptr->phase, phases, phase, address & 0xF);
} }
// check for any stepping effect from a magnet // check for any stepping effect from a magnet
@ -463,7 +477,6 @@ static BYTE __stdcall DiskControlStepper(WORD, WORD address, BYTE, BYTE, ULONG u
const int nNumTracksInImage = ImageGetNumTracks(fptr->imagehandle); const int nNumTracksInImage = ImageGetNumTracks(fptr->imagehandle);
const int newtrack = (nNumTracksInImage == 0) ? 0 const int newtrack = (nNumTracksInImage == 0) ? 0
: MIN(nNumTracksInImage-1, fptr->phase >> 1); // (round half tracks down) : MIN(nNumTracksInImage-1, fptr->phase >> 1); // (round half tracks down)
LOG_DISK("newtrack %2X%s\r", newtrack, (fptr->phase & 1) ? ".5" : "");
if (newtrack != fptr->track) if (newtrack != fptr->track)
{ {
if (fptr->trackimage && fptr->trackimagedirty) if (fptr->trackimage && fptr->trackimagedirty)
@ -478,31 +491,21 @@ static BYTE __stdcall DiskControlStepper(WORD, WORD address, BYTE, BYTE, ULONG u
// https://github.com/AppleWin/AppleWin/issues/201 // https://github.com/AppleWin/AppleWin/issues/201
FrameDrawDiskStatus( (HDC)0 ); FrameDrawDiskStatus( (HDC)0 );
} }
#else // Old 1.13.1 code for Chessmaster 2000 to work! (see bug#18109) #else
const int nNumTracksInImage = ImageGetNumTracks(fptr->imagehandle); // substitute alternate stepping code here to test
if (address & 1) { #endif
int phase = (address >> 1) & 3; #if 0
int direction = 0; LOG_DISK("track $%02X%s phases %d%d%d%d phase %d %s address $%4X\r\n",
if (phase == ((fptr->phase+1) & 3)) fptr->phase >> 1,
direction = 1; (fptr->phase & 1) ? ".5" : " ",
if (phase == ((fptr->phase+3) & 3)) (phases >> 3) & 1,
direction = -1; (phases >> 2) & 1,
if (direction) { (phases >> 1) & 1,
fptr->phase = MAX(0,MIN(79,fptr->phase+direction)); (phases >> 0) & 1,
if (!(fptr->phase & 1)) { phase,
int newtrack = MIN(nNumTracksInImage-1,fptr->phase >> 1); (address & 1) ? "on " : "off",
if (newtrack != fptr->track) { address);
if (fptr->trackimage && fptr->trackimagedirty)
WriteTrack(currdrive);
fptr->track = newtrack;
fptr->trackimagedata = 0;
}
}
}
}
#endif #endif
return ((address & 0xF) == 0) ? 0xFF // TC-TODO: Check why $C0E0 only returns 0xFF
: MemReadFloatingBus(1, uExecutedCycles); // TC-TODO: Check b7 always set
} }
//=========================================================================== //===========================================================================
@ -520,13 +523,12 @@ void DiskDestroy(void)
//=========================================================================== //===========================================================================
static BYTE __stdcall DiskEnable(WORD, WORD address, BYTE, BYTE, ULONG uExecutedCycles) static void __stdcall DiskEnable(WORD, WORD address, BYTE, BYTE, ULONG uExecutedCycles)
{ {
currdrive = address & 1; currdrive = address & 1;
g_aFloppyDisk[!currdrive].spinning = 0; g_aFloppyDisk[!currdrive].spinning = 0;
g_aFloppyDisk[!currdrive].writelight = 0; g_aFloppyDisk[!currdrive].writelight = 0;
CheckSpinning(); CheckSpinning();
return MemReadFloatingBus(uExecutedCycles);
} }
//=========================================================================== //===========================================================================
@ -793,8 +795,9 @@ bool Disk_IsDriveEmpty(const int iDrive)
//=========================================================================== //===========================================================================
static BYTE __stdcall DiskReadWrite (WORD programcounter, WORD, BYTE, BYTE, ULONG) static void __stdcall DiskReadWrite(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nCyclesLeft)
{ {
/* floppyloadmode = 0; */
Disk_t * fptr = &g_aFloppyDisk[currdrive]; Disk_t * fptr = &g_aFloppyDisk[currdrive];
diskaccessed = 1; diskaccessed = 1;
@ -803,32 +806,23 @@ static BYTE __stdcall DiskReadWrite (WORD programcounter, WORD, BYTE, BYTE, ULON
ReadTrack(currdrive); ReadTrack(currdrive);
if (!fptr->trackimagedata) if (!fptr->trackimagedata)
return 0xFF;
BYTE result = 0;
if (!floppywritemode || !fptr->bWriteProtected)
{ {
if (floppywritemode) floppylatch = 0xFF;
{ return;
if (floppylatch & 0x80)
{
*(fptr->trackimage+fptr->byte) = floppylatch;
fptr->trackimagedirty = 1;
}
else
{
return 0;
}
}
else
{
result = *(fptr->trackimage+fptr->byte);
}
} }
if (0) if (!floppywritemode)
{ LOG_DISK("nib %4X = %2X\r", fptr->byte, result); } {
floppylatch = *(fptr->trackimage + fptr->byte);
#if 0
LOG_DISK("read %4X = %2X\r\n", fptr->byte, floppylatch);
#endif
}
else if ((floppylatch & 0x80) && !fptr->bWriteProtected) // && floppywritemode
{
*(fptr->trackimage + fptr->byte) = floppylatch;
fptr->trackimagedirty = 1;
}
if (++fptr->byte >= fptr->nibbles) if (++fptr->byte >= fptr->nibbles)
fptr->byte = 0; fptr->byte = 0;
@ -838,8 +832,6 @@ static BYTE __stdcall DiskReadWrite (WORD programcounter, WORD, BYTE, BYTE, ULON
// NB. Prevent flooding of forcing UI to redraw!!! // NB. Prevent flooding of forcing UI to redraw!!!
if( ((fptr->byte) & 0xFF) == 0 ) if( ((fptr->byte) & 0xFF) == 0 )
FrameDrawDiskStatus( (HDC)0 ); FrameDrawDiskStatus( (HDC)0 );
return result;
} }
//=========================================================================== //===========================================================================
@ -909,24 +901,31 @@ void DiskSelect(const int iDrive)
//=========================================================================== //===========================================================================
static BYTE __stdcall DiskSetLatchValue(WORD, WORD, BYTE write, BYTE value, ULONG) static void __stdcall DiskLoadWriteProtect(WORD, WORD, BYTE write, BYTE value, ULONG) {
{ /* floppyloadmode = 1; */
if (write) if (!write)
floppylatch = value; {
return floppylatch; if (floppymotoron && !floppywritemode)
{
// phase 1 on also forces write protect in the Disk II drive (UTA2E page 9-7) but we don't implement that
if (g_aFloppyDisk[currdrive].bWriteProtected)
floppylatch |= 0x80;
else
floppylatch &= 0x7F;
}
}
} }
//=========================================================================== //===========================================================================
static BYTE __stdcall DiskSetReadMode(WORD, WORD, BYTE, BYTE, ULONG uExecutedCycles) static void __stdcall DiskSetReadMode(WORD, WORD, BYTE, BYTE, ULONG)
{ {
floppywritemode = 0; floppywritemode = 0;
return MemReadFloatingBus(g_aFloppyDisk[currdrive].bWriteProtected, uExecutedCycles);
} }
//=========================================================================== //===========================================================================
static BYTE __stdcall DiskSetWriteMode(WORD, WORD, BYTE, BYTE, ULONG uExecutedCycles) static void __stdcall DiskSetWriteMode(WORD, WORD, BYTE, BYTE, ULONG uExecutedCycles)
{ {
floppywritemode = 1; floppywritemode = 1;
BOOL modechange = !g_aFloppyDisk[currdrive].writelight; BOOL modechange = !g_aFloppyDisk[currdrive].writelight;
@ -936,8 +935,6 @@ static BYTE __stdcall DiskSetWriteMode(WORD, WORD, BYTE, BYTE, ULONG uExecutedCy
//FrameRefreshStatus(DRAW_LEDS); //FrameRefreshStatus(DRAW_LEDS);
FrameDrawDiskLEDS( (HDC)0 ); FrameDrawDiskLEDS( (HDC)0 );
} }
return MemReadFloatingBus(1, uExecutedCycles); // TC-TODO: Check b7 always set
} }
//=========================================================================== //===========================================================================
@ -1032,12 +1029,12 @@ void DiskLoadRom(LPBYTE pCxRomPeripheral, UINT uSlot)
memcpy(pCxRomPeripheral + uSlot*APPLE_SLOT_SIZE, pData, DISK2_FW_SIZE); memcpy(pCxRomPeripheral + uSlot*APPLE_SLOT_SIZE, pData, DISK2_FW_SIZE);
// NB. We used to disable the track stepping delay in the Disk II controller firmware by // Note: We used to disable the track stepping delay in the Disk II controller firmware by
// patching $C64C with $A9,$00,$EA. Now not doing this since: // patching $C64C with $A9,$00,$EA. Now not doing this since:
// . Authentic Speed should be authentic // . Authentic Speed should be authentic
// . Enhanced Speed runs emulation unthrottled, so removing the delay has negligible effect // . Enhanced Speed runs emulation unthrottled, so removing the delay has negligible effect
// . Patching the firmware breaks the ADC checksum used by "The CIA Files" (Tricky Dick) // . Patching the firmware breaks the ADC checksum used by "The CIA Files" (Tricky Dick)
// . In this case we can patch to compensate for an ADC or EOR checksum but not both // . In this case we can patch to compensate for an ADC or EOR checksum but not both (nickw)
RegisterIoHandler(uSlot, Disk_IORead, Disk_IOWrite, NULL, NULL, NULL, NULL); RegisterIoHandler(uSlot, Disk_IORead, Disk_IOWrite, NULL, NULL, NULL, NULL);
} }
@ -1046,55 +1043,60 @@ void DiskLoadRom(LPBYTE pCxRomPeripheral, UINT uSlot)
static BYTE __stdcall Disk_IORead(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nCyclesLeft) static BYTE __stdcall Disk_IORead(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nCyclesLeft)
{ {
addr &= 0xFF; switch (addr & 0xF)
switch (addr & 0xf)
{ {
case 0x0: return DiskControlStepper(pc, addr, bWrite, d, nCyclesLeft); case 0x0: DiskControlStepper(pc, addr, bWrite, d, nCyclesLeft); break;
case 0x1: return DiskControlStepper(pc, addr, bWrite, d, nCyclesLeft); case 0x1: DiskControlStepper(pc, addr, bWrite, d, nCyclesLeft); break;
case 0x2: return DiskControlStepper(pc, addr, bWrite, d, nCyclesLeft); case 0x2: DiskControlStepper(pc, addr, bWrite, d, nCyclesLeft); break;
case 0x3: return DiskControlStepper(pc, addr, bWrite, d, nCyclesLeft); case 0x3: DiskControlStepper(pc, addr, bWrite, d, nCyclesLeft); break;
case 0x4: return DiskControlStepper(pc, addr, bWrite, d, nCyclesLeft); case 0x4: DiskControlStepper(pc, addr, bWrite, d, nCyclesLeft); break;
case 0x5: return DiskControlStepper(pc, addr, bWrite, d, nCyclesLeft); case 0x5: DiskControlStepper(pc, addr, bWrite, d, nCyclesLeft); break;
case 0x6: return DiskControlStepper(pc, addr, bWrite, d, nCyclesLeft); case 0x6: DiskControlStepper(pc, addr, bWrite, d, nCyclesLeft); break;
case 0x7: return DiskControlStepper(pc, addr, bWrite, d, nCyclesLeft); case 0x7: DiskControlStepper(pc, addr, bWrite, d, nCyclesLeft); break;
case 0x8: return DiskControlMotor(pc, addr, bWrite, d, nCyclesLeft); case 0x8: DiskControlMotor(pc, addr, bWrite, d, nCyclesLeft); break;
case 0x9: return DiskControlMotor(pc, addr, bWrite, d, nCyclesLeft); case 0x9: DiskControlMotor(pc, addr, bWrite, d, nCyclesLeft); break;
case 0xA: return DiskEnable(pc, addr, bWrite, d, nCyclesLeft); case 0xA: DiskEnable(pc, addr, bWrite, d, nCyclesLeft); break;
case 0xB: return DiskEnable(pc, addr, bWrite, d, nCyclesLeft); case 0xB: DiskEnable(pc, addr, bWrite, d, nCyclesLeft); break;
case 0xC: return DiskReadWrite(pc, addr, bWrite, d, nCyclesLeft); case 0xC: DiskReadWrite(pc, addr, bWrite, d, nCyclesLeft); break;
case 0xD: return DiskSetLatchValue(pc, addr, bWrite, d, nCyclesLeft); case 0xD: DiskLoadWriteProtect(pc, addr, bWrite, d, nCyclesLeft); break;
case 0xE: return DiskSetReadMode(pc, addr, bWrite, d, nCyclesLeft); case 0xE: DiskSetReadMode(pc, addr, bWrite, d, nCyclesLeft); break;
case 0xF: return DiskSetWriteMode(pc, addr, bWrite, d, nCyclesLeft); case 0xF: DiskSetWriteMode(pc, addr, bWrite, d, nCyclesLeft); break;
} }
return 0; // only even addresses return the latch (UTA2E Table 9.1)
if (!(addr & 1))
return floppylatch;
else
return MemReadFloatingBus(nCyclesLeft);
} }
static BYTE __stdcall Disk_IOWrite(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nCyclesLeft) static BYTE __stdcall Disk_IOWrite(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nCyclesLeft)
{ {
addr &= 0xFF; switch (addr & 0xF)
switch (addr & 0xf)
{ {
case 0x0: return DiskControlStepper(pc, addr, bWrite, d, nCyclesLeft); case 0x0: DiskControlStepper(pc, addr, bWrite, d, nCyclesLeft); break;
case 0x1: return DiskControlStepper(pc, addr, bWrite, d, nCyclesLeft); case 0x1: DiskControlStepper(pc, addr, bWrite, d, nCyclesLeft); break;
case 0x2: return DiskControlStepper(pc, addr, bWrite, d, nCyclesLeft); case 0x2: DiskControlStepper(pc, addr, bWrite, d, nCyclesLeft); break;
case 0x3: return DiskControlStepper(pc, addr, bWrite, d, nCyclesLeft); case 0x3: DiskControlStepper(pc, addr, bWrite, d, nCyclesLeft); break;
case 0x4: return DiskControlStepper(pc, addr, bWrite, d, nCyclesLeft); case 0x4: DiskControlStepper(pc, addr, bWrite, d, nCyclesLeft); break;
case 0x5: return DiskControlStepper(pc, addr, bWrite, d, nCyclesLeft); case 0x5: DiskControlStepper(pc, addr, bWrite, d, nCyclesLeft); break;
case 0x6: return DiskControlStepper(pc, addr, bWrite, d, nCyclesLeft); case 0x6: DiskControlStepper(pc, addr, bWrite, d, nCyclesLeft); break;
case 0x7: return DiskControlStepper(pc, addr, bWrite, d, nCyclesLeft); case 0x7: DiskControlStepper(pc, addr, bWrite, d, nCyclesLeft); break;
case 0x8: return DiskControlMotor(pc, addr, bWrite, d, nCyclesLeft); case 0x8: DiskControlMotor(pc, addr, bWrite, d, nCyclesLeft); break;
case 0x9: return DiskControlMotor(pc, addr, bWrite, d, nCyclesLeft); case 0x9: DiskControlMotor(pc, addr, bWrite, d, nCyclesLeft); break;
case 0xA: return DiskEnable(pc, addr, bWrite, d, nCyclesLeft); case 0xA: DiskEnable(pc, addr, bWrite, d, nCyclesLeft); break;
case 0xB: return DiskEnable(pc, addr, bWrite, d, nCyclesLeft); case 0xB: DiskEnable(pc, addr, bWrite, d, nCyclesLeft); break;
case 0xC: return DiskReadWrite(pc, addr, bWrite, d, nCyclesLeft); case 0xC: DiskReadWrite(pc, addr, bWrite, d, nCyclesLeft); break;
case 0xD: return DiskSetLatchValue(pc, addr, bWrite, d, nCyclesLeft); case 0xD: DiskLoadWriteProtect(pc, addr, bWrite, d, nCyclesLeft); break;
case 0xE: return DiskSetReadMode(pc, addr, bWrite, d, nCyclesLeft); case 0xE: DiskSetReadMode(pc, addr, bWrite, d, nCyclesLeft); break;
case 0xF: return DiskSetWriteMode(pc, addr, bWrite, d, nCyclesLeft); case 0xF: DiskSetWriteMode(pc, addr, bWrite, d, nCyclesLeft); break;
} }
// any address writes the latch via sequencer LD command (74LS323 datasheet)
if (floppywritemode /* && floppyloadmode */)
{
floppylatch = d;
}
return 0; return 0;
} }