Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
b697be894d
7 changed files with 117 additions and 33 deletions
|
@ -8,6 +8,12 @@ https://github.com/AppleWin/AppleWin/issues/new
|
|||
|
||||
Tom Charlesworth
|
||||
|
||||
1.27.9.0 - 2 Oct 2018
|
||||
---------------------
|
||||
. [Bug #582] Support for partial disk II latch reads when accesses are very close.
|
||||
- Fixes Curse of the Azure Bonds & Pool of Radiance (saving characters and creating disks).
|
||||
|
||||
|
||||
1.27.8.0 - 9 Sep 2018
|
||||
---------------------
|
||||
. [Bug #555] Fix for showing 559th DHGR/DGR/TEXT80 vertical column (retaining 560-pixel display width).
|
||||
|
|
|
@ -252,8 +252,8 @@ DISK_ICON ICON "DISK.ICO"
|
|||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,27,8,0
|
||||
PRODUCTVERSION 1,27,8,0
|
||||
FILEVERSION 1,27,9,0
|
||||
PRODUCTVERSION 1,27,9,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
|
@ -271,12 +271,12 @@ BEGIN
|
|||
VALUE "Comments", "https://github.com/AppleWin"
|
||||
VALUE "CompanyName", "AppleWin"
|
||||
VALUE "FileDescription", "Apple //e Emulator for Windows"
|
||||
VALUE "FileVersion", "1, 27, 8, 0"
|
||||
VALUE "FileVersion", "1, 27, 9, 0"
|
||||
VALUE "InternalName", "APPLEWIN"
|
||||
VALUE "LegalCopyright", " 1994-2018 Michael O'Brien, Oliver Schmidt, Tom Charlesworth, Michael Pohoreski, Nick Westgate, Linards Ticmanis"
|
||||
VALUE "OriginalFilename", "APPLEWIN.EXE"
|
||||
VALUE "ProductName", "Apple //e Emulator"
|
||||
VALUE "ProductVersion", "1, 27, 8, 0"
|
||||
VALUE "ProductVersion", "1, 27, 9, 0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
|
|
@ -129,13 +129,16 @@ void LogFileTimeUntilFirstKeyReadReset(void)
|
|||
}
|
||||
|
||||
// Log the time from emulation restart/reboot until the first key read: BIT $C000
|
||||
// . NB. AZTEC.DSK does prior LDY $C000 reads, but the BIT $C000 is at the "Press any key" message
|
||||
// . AZTEC.DSK (DOS 3.3) does prior LDY $C000 reads, but the BIT $C000 is at the "Press any key" message
|
||||
// . Phasor1.dsk / ProDOS 1.1.1: PC=E797: B1 50: LDA ($50),Y / "Select an Option:" message
|
||||
void LogFileTimeUntilFirstKeyRead(void)
|
||||
{
|
||||
if (!g_fh || bLogKeyReadDone)
|
||||
return;
|
||||
|
||||
if (mem[regs.pc-3] != 0x2C) // bit $c000
|
||||
if ( (mem[regs.pc-3] != 0x2C) // AZTEC: bit $c000
|
||||
&& !((regs.pc-2) == 0xE797 && mem[regs.pc-2] == 0xB1 && mem[regs.pc-1] == 0x50) // Phasor1: lda ($50),y
|
||||
)
|
||||
return;
|
||||
|
||||
DWORD dwTime = GetTickCount() - dwLogKeyReadTickStart;
|
||||
|
|
|
@ -1493,7 +1493,7 @@ int GetDisassemblyLine ( WORD nBaseAddress, DisasmLine_t & line_ )
|
|||
|
||||
if (! (bDisasmFormatFlags & DISASM_FORMAT_SYMBOL))
|
||||
{
|
||||
pTarget = FormatAddress( nTarget, nOpbyte );
|
||||
pTarget = FormatAddress( nTarget, (iOpmode != AM_R) ? nOpbyte : 3 ); // GH#587: For Bcc opcodes, pretend it's a 3-byte opcode to print a 16-bit target addr
|
||||
}
|
||||
|
||||
// sprintf( sTarget, g_aOpmodes[ iOpmode ]._sFormat, pTarget );
|
||||
|
|
|
@ -86,6 +86,7 @@ static WORD phases = 0; // state bits for stepper magnet phases 0 - 3
|
|||
static bool g_bSaveDiskImage = true; // Save the DiskImage name to Registry
|
||||
static UINT g_uSlot = 0;
|
||||
static unsigned __int64 g_uDiskLastCycle = 0;
|
||||
static unsigned __int64 g_uDiskLastReadLatchCycle = 0;
|
||||
static FormatTrack g_formatTrack;
|
||||
|
||||
static bool IsDriveValid( const int iDrive );
|
||||
|
@ -895,7 +896,23 @@ static void __stdcall DiskReadWrite(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULO
|
|||
// but Sherwood Forest sets shift mode and reads with the drive off, so don't check for now
|
||||
if (!floppywritemode)
|
||||
{
|
||||
const ULONG nReadCycleDiff = (ULONG) (g_nCumulativeCycles - g_uDiskLastReadLatchCycle);
|
||||
|
||||
// Support partial nibble read if disk reads are very close: (GH#582)
|
||||
// . 6 cycles (1st->2nd read) for DOS 3.3 / $BD34: "read with delays to see if disk is spinning." (Beneath Apple DOS)
|
||||
// . 6 cycles (1st->2nd read) for Curse of the Azure Bonds (loop to see if disk is spinning)
|
||||
// . 31 cycles is the max for a partial 8-bit nibble
|
||||
const ULONG kReadAccessThreshold = enhancedisk ? 6 : 31;
|
||||
|
||||
if (nReadCycleDiff <= kReadAccessThreshold)
|
||||
{
|
||||
UINT invalidBits = 8 - (nReadCycleDiff / 4); // 4 cycles per bit-cell
|
||||
floppylatch = *(pFloppy->trackimage + pFloppy->byte) >> invalidBits;
|
||||
return; // Early return so don't update: g_uDiskLastReadLatchCycle & pFloppy->byte
|
||||
}
|
||||
|
||||
floppylatch = *(pFloppy->trackimage + pFloppy->byte);
|
||||
g_uDiskLastReadLatchCycle = g_nCumulativeCycles;
|
||||
|
||||
#if LOG_DISK_NIBBLES_READ
|
||||
#if LOG_DISK_NIBBLES_USE_RUNTIME_VAR
|
||||
|
@ -959,6 +976,10 @@ void DiskReset(const bool bIsPowerCycle/*=false*/)
|
|||
|
||||
if (bIsPowerCycle) // GH#460
|
||||
{
|
||||
// NB. This doesn't affect the drive head (ie. drive's track position)
|
||||
// . The initial machine start-up state is track=0, but after a power-cycle the track could be any value.
|
||||
// . (For DiskII firmware, this results in a subtle extra latch read in this latter case, for the track!=0 case)
|
||||
|
||||
g_aFloppyDrive[DRIVE_1].spinning = 0;
|
||||
g_aFloppyDrive[DRIVE_1].writelight = 0;
|
||||
g_aFloppyDrive[DRIVE_2].spinning = 0;
|
||||
|
@ -1363,7 +1384,8 @@ int DiskSetSnapshot_v1(const SS_CARD_DISK2* const pSS)
|
|||
|
||||
// Unit version history:
|
||||
// 2: Added: Format Track state & DiskLastCycle
|
||||
static const UINT kUNIT_VERSION = 2;
|
||||
// 3: Added: DiskLastReadLatchCycle
|
||||
static const UINT kUNIT_VERSION = 3;
|
||||
|
||||
#define SS_YAML_VALUE_CARD_DISK2 "Disk]["
|
||||
|
||||
|
@ -1375,6 +1397,7 @@ static const UINT kUNIT_VERSION = 2;
|
|||
#define SS_YAML_KEY_FLOPPY_MOTOR_ON "Floppy Motor On"
|
||||
#define SS_YAML_KEY_FLOPPY_WRITE_MODE "Floppy Write Mode"
|
||||
#define SS_YAML_KEY_LAST_CYCLE "Last Cycle"
|
||||
#define SS_YAML_KEY_LAST_READ_LATCH_CYCLE "Last Read Latch Cycle"
|
||||
|
||||
#define SS_YAML_KEY_DISK2UNIT "Unit"
|
||||
#define SS_YAML_KEY_FILENAME "Filename"
|
||||
|
@ -1429,6 +1452,7 @@ void DiskSaveSnapshot(class YamlSaveHelper& yamlSaveHelper)
|
|||
yamlSaveHelper.SaveBool(SS_YAML_KEY_FLOPPY_MOTOR_ON, floppymotoron == TRUE);
|
||||
yamlSaveHelper.SaveBool(SS_YAML_KEY_FLOPPY_WRITE_MODE, floppywritemode == TRUE);
|
||||
yamlSaveHelper.SaveHexUint64(SS_YAML_KEY_LAST_CYCLE, g_uDiskLastCycle); // v2
|
||||
yamlSaveHelper.SaveHexUint64(SS_YAML_KEY_LAST_READ_LATCH_CYCLE, g_uDiskLastReadLatchCycle); // v3
|
||||
g_formatTrack.SaveSnapshot(yamlSaveHelper); // v2
|
||||
|
||||
DiskSaveSnapshotDisk2Unit(yamlSaveHelper, DRIVE_1);
|
||||
|
@ -1534,6 +1558,11 @@ bool DiskLoadSnapshot(class YamlLoadHelper& yamlLoadHelper, UINT slot, UINT vers
|
|||
g_formatTrack.LoadSnapshot(yamlLoadHelper);
|
||||
}
|
||||
|
||||
if (version >= 3)
|
||||
{
|
||||
g_uDiskLastReadLatchCycle = yamlLoadHelper.LoadUint64(SS_YAML_KEY_LAST_READ_LATCH_CYCLE);
|
||||
}
|
||||
|
||||
// Eject all disks first in case Drive-2 contains disk to be inserted into Drive-1
|
||||
for(UINT i=0; i<NUM_DRIVES; i++)
|
||||
{
|
||||
|
|
|
@ -79,7 +79,7 @@ MEMORY MANAGEMENT SOFT SWITCHES
|
|||
$C000 W 80STOREOFF Allow page2 to switch video page1 page2
|
||||
$C001 W 80STOREON Allow page2 to switch main & aux video memory
|
||||
$C002 W RAMRDOFF Read enable main memory from $0200-$BFFF
|
||||
$C003 W RAMDRON Read enable aux memory from $0200-$BFFF
|
||||
$C003 W RAMRDON Read enable aux memory from $0200-$BFFF
|
||||
$C004 W RAMWRTOFF Write enable main memory from $0200-$BFFF
|
||||
$C005 W RAMWRTON Write enable aux memory from $0200-$BFFF
|
||||
$C006 W INTCXROMOFF Enable slot ROM from $C100-$C7FF (but $C800-$CFFF depends on INTC8ROM)
|
||||
|
@ -129,29 +129,44 @@ SOFT SWITCH STATUS FLAGS
|
|||
// Notes
|
||||
// -----
|
||||
//
|
||||
// mem
|
||||
// - a copy of the memimage ptr
|
||||
//
|
||||
// memimage
|
||||
// - 64KB
|
||||
//
|
||||
// mem
|
||||
// (a pointer to memimage 64KB)
|
||||
// - a 64KB r/w memory cache
|
||||
// - reflects the current readable memory in the 6502's 64K address space
|
||||
// . excludes $Cxxx I/O memory
|
||||
// . could be a mix of RAM/ROM, main/aux, etc
|
||||
// - may also reflect the current writeable memory (on a 256-page granularity) if the write page addr == read page addr
|
||||
// . for this case, the memdirty flag(s) are valid
|
||||
// . when writes instead occur to backing-store, then memdirty flag(s) can be ignored
|
||||
//
|
||||
// memmain, memaux
|
||||
// - physical contiguous 64KB RAM for main & aux respectively
|
||||
// - physical contiguous 64KB "backing-store" for main & aux respectively
|
||||
// - NB. 4K bank1 BSR is at $C000-$CFFF
|
||||
//
|
||||
// memwrite
|
||||
// - 1 ptr entry per 256-byte page
|
||||
// - 1 pointer entry per 256-byte page
|
||||
// - used to write to a page
|
||||
// - if RD & WR point to the same 256-byte RAM page, then memwrite will point to the page in 'mem'
|
||||
// . ie. when SW_AUXREAD==SW_AUXWRITE, or 4K-BSR is r/w, or 8K BSR is r/w, or SW_80STORE=1
|
||||
// . So 'mem' remains correct for both r&w operations, but the physical 64K mem block becomes stale
|
||||
// - if RD & WR point to different 256-byte pages, then memwrite will point to the page in the physical 64K mem block
|
||||
// . writes will still set the dirty flag (but can be ignored)
|
||||
// . UpdatePaging() ignores this, as it only copies back to the physical 64K mem block when memshadow changes (for that 256-byte page)
|
||||
//
|
||||
// memdirty
|
||||
// - 1 byte entry per 256-byte page
|
||||
// - set when a write occurs to a 256-byte page
|
||||
// - indicates that 'mem' (ie. the cache) is out-of-sync with the "physical" 64K backing-store memory
|
||||
// - NB. a page's dirty flag is only useful(valid) when 'mem' is used for both read & write for the corresponding page
|
||||
// When they differ, then writes go directly to the backing-store.
|
||||
// . In this case, the dirty flag will just force a memcpy() to the same address in backing-store.
|
||||
//
|
||||
// memshadow
|
||||
// - 1 ptr entry per 256-byte page
|
||||
// - reflects how 'mem' is setup
|
||||
// - 1 pointer entry per 256-byte page
|
||||
// - reflects how 'mem' is setup for read operations (at a 256-byte granularity)
|
||||
// . EG: if ALTZP=1, then:
|
||||
// . mem will have copies of memaux's ZP & stack
|
||||
// . memshadow[0] = &memaux[0x0000]
|
||||
|
@ -817,19 +832,6 @@ static bool IsCardInSlot(const UINT uSlot)
|
|||
|
||||
//===========================================================================
|
||||
|
||||
static void BackMainImage(void)
|
||||
{
|
||||
for (UINT loop = 0; loop < 256; loop++)
|
||||
{
|
||||
if (memshadow[loop] && ((*(memdirty+loop) & 1) || (loop <= 1)))
|
||||
CopyMemory(memshadow[loop], memimage+(loop << 8), 256);
|
||||
|
||||
*(memdirty+loop) &= ~1;
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
|
||||
DWORD GetMemMode(void)
|
||||
{
|
||||
return memmode;
|
||||
|
@ -925,7 +927,7 @@ static void UpdatePaging(BOOL initialize)
|
|||
|
||||
for (loop = 0xC0; loop < 0xC8; loop++)
|
||||
{
|
||||
memdirty[loop] = 0; // ROM can't be dirty
|
||||
memdirty[loop] = 0; // mem(cache) can't be dirty for ROM (but STA $Csnn will set the dirty flag)
|
||||
const UINT uSlotOffset = (loop & 0x0f) * 0x100;
|
||||
if (loop == 0xC3)
|
||||
memshadow[loop] = (SW_SLOTC3ROM && !SW_INTCXROM) ? pCxRomPeripheral+uSlotOffset // C300..C3FF - Slot 3 ROM (all 0x00's)
|
||||
|
@ -937,7 +939,7 @@ static void UpdatePaging(BOOL initialize)
|
|||
|
||||
for (loop = 0xC8; loop < 0xD0; loop++)
|
||||
{
|
||||
memdirty[loop] = 0; // ROM can't be dirty (but STA $CFFF will set the dirty flag)
|
||||
memdirty[loop] = 0; // mem(cache) can't be dirty for ROM (but STA $Cnnn will set the dirty flag)
|
||||
const UINT uRomOffset = (loop & 0x0f) * 0x100;
|
||||
memshadow[loop] = (!SW_INTCXROM && !INTC8ROM) ? pCxRomPeripheral+uRomOffset // C800..CFFF - Peripheral ROM (GH#486)
|
||||
: pCxRomInternal+uRomOffset; // C800..CFFF - Internal ROM
|
||||
|
@ -1070,6 +1072,19 @@ bool MemCheckINTCXROM()
|
|||
|
||||
//===========================================================================
|
||||
|
||||
static void BackMainImage(void)
|
||||
{
|
||||
for (UINT loop = 0; loop < 256; loop++)
|
||||
{
|
||||
if (memshadow[loop] && ((*(memdirty+loop) & 1) || (loop <= 1)))
|
||||
CopyMemory(memshadow[loop], mem+(loop << 8), 256);
|
||||
|
||||
*(memdirty+loop) &= ~1;
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
|
||||
static LPBYTE MemGetPtrBANK1(const WORD offset, const LPBYTE pMemBase)
|
||||
{
|
||||
if ((offset & 0xF000) != 0xC000) // Requesting RAM at physical addr $Cxxx (ie. 4K RAM BANK1)
|
||||
|
@ -1114,6 +1129,34 @@ LPBYTE MemGetAuxPtr(const WORD offset)
|
|||
|
||||
//-------------------------------------
|
||||
|
||||
// if memshadow == memmain
|
||||
// so RD memmain
|
||||
// case: RD == WR
|
||||
// so RD(mem),WR(mem)
|
||||
// so 64K memmain could be incorrect
|
||||
// *therefore mem is correct
|
||||
// case: RD != WR
|
||||
// so RD(mem),WR(memaux)
|
||||
// doesn't matter since RD != WR, then it's guaranteed that memaux is correct
|
||||
// *therefore either mem or memmain is correct
|
||||
// else ; memshadow != memmain
|
||||
// so RD memaux (or ROM)
|
||||
// case: RD == WR
|
||||
// so RD(mem),WR(mem)
|
||||
// so 64K memaux could be incorrect
|
||||
// *therefore memmain is correct
|
||||
// case: RD != WR
|
||||
// so RD(mem),WR(memmain)
|
||||
// doesn't matter since RD != WR, then it's guaranteed that memmain is correct
|
||||
// *therefore memmain is correct
|
||||
//
|
||||
// *OR*
|
||||
//
|
||||
// Is the mem(cache) setup to read (via memshadow) from memmain?
|
||||
// . if yes, then return the mem(cache) address as writes (via memwrite) may've made the mem(cache) dirty.
|
||||
// . if no, then return memmain, as the mem(cache) isn't involved in memmain (any writes will go directly to this backing-store).
|
||||
//
|
||||
|
||||
LPBYTE MemGetMainPtr(const WORD offset)
|
||||
{
|
||||
LPBYTE lpMem = MemGetPtrBANK1(offset, memmain);
|
||||
|
@ -1127,6 +1170,9 @@ LPBYTE MemGetMainPtr(const WORD offset)
|
|||
|
||||
//===========================================================================
|
||||
|
||||
// Used by:
|
||||
// . Savestate: MemSaveSnapshotMemory(), MemLoadSnapshotAux()
|
||||
// . Debugger : CmdMemorySave(), CmdMemoryLoad()
|
||||
LPBYTE MemGetBankPtr(const UINT nBank)
|
||||
{
|
||||
BackMainImage(); // Flush any dirty pages to back-buffer
|
||||
|
@ -1784,7 +1830,7 @@ _done_saturn:
|
|||
// IT DOES SO.
|
||||
//
|
||||
// NB. A 6502 interrupt occurring between these memory write & read updates could lead to incorrect behaviour.
|
||||
// - although any date-race is probably a bug in the 6502 code too.
|
||||
// - although any data-race is probably a bug in the 6502 code too.
|
||||
if ((address >= 4) && (address <= 5) &&
|
||||
((*(LPDWORD)(mem+programcounter) & 0x00FFFEFF) == 0x00C0028D)) {
|
||||
modechanging = 1;
|
||||
|
|
|
@ -24,7 +24,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||
#include "Applewin.h"
|
||||
#include "CPU.h" // CpuGetCyclesThisVideoFrame()
|
||||
#include "Frame.h"
|
||||
#include "Memory.h" // MemGetMainPtr() MemGetBankPtr()
|
||||
#include "Memory.h" // MemGetMainPtr() MemGetAuxPtr()
|
||||
#include "Video.h" // g_pFramebufferbits
|
||||
|
||||
#include "NTSC.h"
|
||||
|
|
Loading…
Add table
Reference in a new issue