From bae812fec4751916b122305c4d2dfc999006fa5e Mon Sep 17 00:00:00 2001 From: Toni Wilen Date: Sat, 18 Nov 2023 16:34:07 +0200 Subject: [PATCH] "Also, the last word in a disk-read DMA operation may not come in (that is, one less word may be read than you asked for)" Paula disk bug emulation. --- custom.cpp | 3 +- disk.cpp | 255 ++++++++++++++++++++++++++++++++----------------- include/disk.h | 2 +- 3 files changed, 169 insertions(+), 91 deletions(-) diff --git a/custom.cpp b/custom.cpp index 4c3456d8..57b40812 100644 --- a/custom.cpp +++ b/custom.cpp @@ -12645,6 +12645,7 @@ static void dmal_emu(uae_u32 val) AUDxDAT(nr, dat, pt); } else if (dmalpos >= 0 && dmalpos < 6) { uae_u16 dat = 0; + int s = (dmalpos / 2); int w = (dmalbits & 3) == 3; // disk_fifostatus() needed in >100% disk speed modes if (w) { @@ -12679,7 +12680,7 @@ static void dmal_emu(uae_u32 val) // read from disk if (disk_fifostatus() >= 0) { uaecptr pt = disk_getpt(); - dat = DSKDATR(); + dat = DSKDATR(s); if (dmal_ce) { #ifdef DEBUGGER if (debug_dma) { diff --git a/disk.cpp b/disk.cpp index b010932f..7211f4fc 100644 --- a/disk.cpp +++ b/disk.cpp @@ -22,6 +22,7 @@ int disk_debug_track = -1; #define REVOLUTION_DEBUG 0 #define MFM_VALIDATOR 0 +#define DISK_DEBUG_X 0 #include "uae.h" #include "options.h" @@ -111,17 +112,17 @@ static uae_u8 writesecheadbuffer[16 * MAX_SECTORS]; #define DISK_INDEXSYNC 1 #define DISK_WORDSYNC 2 -#define DISK_REVOLUTION 4 /* 8,16,32,64 */ +#define DISK_DMAEND 4 +#define DISK_REVOLUTION 8 /* 8,16,32,64 */ #define DSKREADY_UP_TIME 18 #define DSKREADY_DOWN_TIME 24 #define DSKDMA_OFF 0 -#define DSKDMA_INIT 1 #define DSKDMA_READ 2 #define DSKDMA_WRITE 3 -static int dskdmaen, dsklength, dsklength2, dsklen; +static int dskdmaen, dsklength, dsklength2, dsklen, dsklen2; static uae_u16 dskbytr_val, dskbytr_val_prev; static bool dskbytr_delay; static uae_u32 dskpt; @@ -132,7 +133,7 @@ static int dma_enable, bitoffset, syncoffset; static uae_u16 word, dsksync; static bool dsksync_on; /* Always carried through to the next line. */ -int disk_hpos; +static int disk_hpos; static int disk_jitter; static int indexdecay; static uae_u8 prev_data; @@ -3818,7 +3819,7 @@ void dumpdisk (const TCHAR *name) side, dskdmaen, bitoffset, word, dskpt, dsklen, dskbytr_val, adkcon, dsksync); } -static void disk_dmafinished (void) +static void disk_dmafinished(void) { INTREQ_INT(1, 0); if (floppy_writemode > 0) @@ -3833,6 +3834,13 @@ static void disk_dmafinished (void) write_log (_T("%d%s"), floppy[dr].mfmpos, dr < MAX_FLOPPY_DRIVES - 1 ? _T(",") : _T("")); write_log (_T("\n")); } + for (int i = 0; i < 3; i++) { + fifo_inuse[i] = false; + } + fifo_filled = 0; +#if DISK_DEBUG_X + write_log("dma finished %d %08x\n", floppy[0].mfmpos, M68K_GETPC); +#endif } static void fetchnextrevolution (drive *drv) @@ -3874,6 +3882,54 @@ static void fetchnextrevolution (drive *drv) } } +STATIC_INLINE bool iswrite(void) +{ + return dskdmaen == DSKDMA_WRITE; +} + +void DSKDAT(uae_u16 v) +{ + if (fifo_inuse[2]) { + write_log(_T("DSKDAT: FIFO overflow!\n")); + return; + } + fifo_inuse[2] = fifo_inuse[1]; + fifo[2] = fifo[1]; + fifo_inuse[1] = fifo_inuse[0]; + fifo[1] = fifo[0]; + fifo_inuse[0] = iswrite() ? 2 : 1; + fifo[0] = v; + fifo_filled = 1; +} + +uae_u16 DSKDATR(int slot) +{ + int i; + uae_u16 v = 0; + + for (i = 2; i >= 0; i--) { + if (fifo_inuse[i]) { + fifo_inuse[i] = 0; + v = fifo[i]; + break; + } + } + +#if DISK_DEBUG_X > 1 + write_log("datr %d %d\n", dsklength, floppy[0].mfmpos); +#endif + + if (i < 0) { + write_log(_T("DSKDATR: FIFO underflow!\n")); + } else if (dskdmaen == DSKDMA_READ && dsklength == 0) { + // len==0 and (all slots empty or last slot read and one slot in use) + if (disk_fifostatus() < 0 || (slot == 2 && fifo_inuse[0] && !fifo_inuse[1] && !fifo_inuse[2])) { + disk_dmafinished(); + } + } + return v; +} + static void do_disk_index (void) { #if REVOLUTION_DEBUG @@ -3889,9 +3945,9 @@ void event_DISK_handler(uae_u32 data) { int flag = data & 255; int disk_sync_cycle = data >> 8; - int hpos = current_hpos(); DISK_update(disk_sync_cycle); + if (!dskdmaen) { if (flag & (DISK_REVOLUTION << 0)) fetchnextrevolution(&floppy[0]); @@ -3933,7 +3989,7 @@ static bool canloaddskbytr(void) return (bitoffset & 7) == 7; } -static void disk_doupdate_write(int floppybits, int trackspeed) +static int disk_doupdate_write(int floppybits, int trackspeed) { int dr; int drives[4]; @@ -3971,11 +4027,11 @@ static void disk_doupdate_write(int floppybits, int trackspeed) dskpt += 2; } } - if (disk_fifostatus () >= 0) { + if (disk_fifostatus() >= 0) { #ifdef FLOPPYBRIDGE bool wasBridge = false; #endif - uae_u16 w = DSKDATR (); + uae_u16 w = DSKDATR(0); for (dr = 0; dr < MAX_FLOPPY_DRIVES; dr++) { drive *drv2 = &floppy[dr]; if (drives[dr]) { @@ -4022,6 +4078,7 @@ static void disk_doupdate_write(int floppybits, int trackspeed) } floppybits -= trackspeed; } + return floppybits; } static void update_jitter (void) @@ -4062,12 +4119,14 @@ static void disk_doupdate_predict (int startcycle) for (int dr = 0; dr < MAX_FLOPPY_DRIVES; dr++) { drive *drv = &floppy[dr]; + if (drv->motoroff) continue; if (!drv->trackspeed) continue; if ((selected | disabled) & (1 << dr)) continue; + bool isempty = drive_empty(drv); bool isunformatted = unformatted(drv); int mfmpos = drv->mfmpos; @@ -4078,11 +4137,13 @@ static void disk_doupdate_predict (int startcycle) uae_u32 tword = word; noselected = false; int countcycle = startcycle; + int tdsklength = dsklength; + int tbitoffset = bitoffset; + while (countcycle < (maxhpos << 8)) { int inc = nextbit(drv); if (drv->tracktiming[0]) updatetrackspeed (drv, mfmpos); - countcycle += drv->trackspeed; if (dskdmaen != DSKDMA_WRITE || (dskdmaen == DSKDMA_WRITE && !dma_enable)) { tword <<= 1; if (!isempty) { @@ -4121,6 +4182,37 @@ static void disk_doupdate_predict (int startcycle) } } } + + bool skipbit = false; + // MSBSYNC + if (adkcon & 0x200) { + if ((tword & 0x0001) == 0 && tbitoffset == 0) { + tword = 0; + skipbit = true; + } + if ((tword & 0x0001) == 0 && tbitoffset == 8) { + tword >>= 1; + skipbit = true; + } + } + + if (!skipbit) { + tbitoffset++; + tbitoffset &= 15; + + if (dmaen(DMA_DISK) && tbitoffset == 0 && dskdmaen == DSKDMA_READ) { + tdsklength--; + if (tdsklength == 0) { +#if DISK_DEBUG_X + write_log("word skip predicted %d %d->%d %d %d\n", countcycle >> 8, drv->mfmpos, mfmpos, tdsklength, disk_fifostatus()); +#endif + diskevent_flag |= DISK_DMAEND; + } + } + } + + countcycle += drv->trackspeed; + if (diskevent_flag) break; } @@ -4145,32 +4237,40 @@ int disk_fifostatus (void) return 0; } -static int doreaddma (void) +static int doreaddma(void) { - if (dmaen(DMA_DISK) && dmaen(DMA_MASTER) && bitoffset == 15 && dma_enable && dskdmaen == DSKDMA_READ && dsklength >= 0) { + if (dmaen(DMA_DISK) && bitoffset == 15 && dma_enable && dskdmaen == DSKDMA_READ && dsklength >= 0) { + if (dsklength == 0) { + disk_dmafinished(); + return 1; + } if (dsklength > 0) { // DSKLEN == 1: finish without DMA transfer. if (dsklength == 1 && dsklength2 == 1) { - disk_dmafinished (); + disk_dmafinished(); return 0; } // fast disk modes, just flush the fifo if (currprefs.floppy_speed > 100 && fifo_inuse[0] && fifo_inuse[1] && fifo_inuse[2]) { while (fifo_inuse[0]) { - uae_u16 w = DSKDATR (); + uae_u16 w = DSKDATR(0); chipmem_wput_indirect (dskpt, w); dskpt += 2; } } - if (disk_fifostatus () > 0) { + if (disk_fifostatus() > 0) { return -1; } else { - DSKDAT (word); dsklength--; -#if 0 - if (dsklength < 1 && (adkcon & 0x200)) - activate_debugger(); + // Paula bug: when dsklength reaches zero, FIFO status is checked too early. + if (disk_fifostatus() < 0 && dsklength == 0) { + disk_dmafinished(); + } else { + DSKDAT(word); +#if DISK_DEBUG_X > 1 + write_log("buffer load %d %d\n", floppy[0].mfmpos, dsklength); #endif + } } } return 1; @@ -4181,7 +4281,15 @@ static int doreaddma (void) static void wordsync_detected(bool startup) { if (dskdmaen != DSKDMA_OFF) { - if (disk_debug_logging && dma_enable == 0) { + int prev_dma_enabled = dma_enable; + if (!startup) { + dma_enable = 1; + } + if (!dsksync_on) { + INTREQ_INT(12, 0); + dsksync_on = true; + } + if ((disk_debug_logging && dma_enable == 0)) { int pos = -1; for (int i = 0; i < MAX_FLOPPY_DRIVES; i++) { drive *drv = &floppy[i]; @@ -4190,16 +4298,11 @@ static void wordsync_detected(bool startup) break; } } - write_log(_T("Sync match %04x mfmpos %d enable %d wordsync %d\n"), dsksync, pos, dma_enable, (adkcon & 0x0400) != 0); + write_log(_T("Sync match %04x mfmpos %d enable %d->%d bcnt %d wordsync %d\n"), + dsksync, pos, prev_dma_enabled, dma_enable, bitoffset, (adkcon & 0x0400) != 0); if (disk_debug_logging > 1) dumpdisk(_T("SYNC")); } - if (!startup) - dma_enable = 1; - if (!dsksync_on) { - INTREQ_INT(12, 0); - dsksync_on = true; - } } if (adkcon & 0x0400) { // WORDSYNC bitoffset = 15; @@ -4276,7 +4379,7 @@ static void disk_doupdate_read_nothing(int floppybits) } } -static void disk_doupdate_read (drive *drv, int floppybits) +static int disk_doupdate_read(drive *drv, int floppybits) { /* uae_u16 *mfmbuf = drv->bigmfmbuf; @@ -4296,6 +4399,7 @@ static void disk_doupdate_read (drive *drv, int floppybits) mfmbuf[6] = 0x4444; mfmbuf[7] = 0x4444; */ + bool isempty = drive_empty(drv); bool isunformatted = unformatted(drv); while (floppybits >= drv->trackspeed) { @@ -4315,9 +4419,9 @@ static void disk_doupdate_read (drive *drv, int floppybits) } //write_log (_T("%08X bo=%d so=%d mfmpos=%d dma=%d\n"), (word & 0xffffff), bitoffset, syncoffset, drv->mfmpos, dma_enable); - if (doreaddma () < 0) { + if (doreaddma() < 0) { word >>= 1; - return; + return floppybits; } int pmfmpos = drv->mfmpos; drv->mfmpos += inc; @@ -4374,6 +4478,9 @@ static void disk_doupdate_read (drive *drv, int floppybits) } if (!(adkcon & 0x200) && word == dsksync) { wordsync_detected(false); +#if DISK_DEBUG_X + write_log("%d %d %08x\n", dskdmaen, drv->mfmpos, M68K_GETPC); +#endif } if (!skipbit) { @@ -4383,6 +4490,7 @@ static void disk_doupdate_read (drive *drv, int floppybits) floppybits -= drv->trackspeed; } + return floppybits; } static void disk_dma_debugmsg (void) @@ -4411,7 +4519,7 @@ uae_u16 DSKBYTR(int hpos) dumpdisk(_T("DSKBYTR SYNC")); } } - if (dskdmaen != DSKDMA_OFF && dmaen(DMA_DISK) && dmaen(DMA_MASTER)) + if (dskdmaen != DSKDMA_OFF && dmaen(DMA_DISK)) v |= 0x4000; if (dsklen & 0x4000) v |= 0x2000; @@ -4467,6 +4575,10 @@ static void DISK_start(void) int tr = drv->cyl * 2 + side; trackid *ti = drv->trackdata + tr; +#if DISK_DEBUG_X + write_log("DMA start %d %08x %08x\n", drv->mfmpos, dskpt, M68K_GETPC); +#endif + if (drv->dskchange_time == -1) { drv->dskchange_time = -2; write_log(_T("Accessing state restored non-existing disk '%s'!\n"), drv->newname); @@ -4564,12 +4676,14 @@ void DISK_hsync (void) } - if (indexdecay) + if (indexdecay) { indexdecay--; + } if (linecounter) { linecounter--; - if (! linecounter) - disk_dmafinished (); + if (! linecounter) { + disk_dmafinished(); + } return; } DISK_update (maxhpos); @@ -4592,12 +4706,6 @@ void DISK_update (int tohpos) } cycles = (tohpos << 8) - disk_hpos; -#if 0 - if (tohpos == 228) - write_log (_T("x")); - if (tohpos != maxhpos || cycles / 256 != maxhpos) - write_log (_T("%d %d %d\n"), tohpos, cycles / 256, disk_hpos / 256); -#endif if (cycles <= 0) { return; } @@ -4606,6 +4714,7 @@ void DISK_update (int tohpos) if (disk_hpos >= (maxhpos << 8)) { disk_hpos %= 1 << 8; } + dskbytr_delay = false; for (dr = 0; dr < MAX_FLOPPY_DRIVES; dr++) { @@ -4645,12 +4754,11 @@ void DISK_update (int tohpos) } /* write dma and wordsync enabled: read until wordsync match found */ if (dskdmaen == DSKDMA_WRITE && dma_enable) { - disk_doupdate_write(drv->floppybitcounter, drv->trackspeed); + drv->floppybitcounter = disk_doupdate_write(drv->floppybitcounter, drv->trackspeed); } else { - disk_doupdate_read(drv, drv->floppybitcounter); + drv->floppybitcounter = disk_doupdate_read(drv, drv->floppybitcounter); } - drv->floppybitcounter %= drv->trackspeed; didaccess = 1; } if (!didaccess) { @@ -4673,33 +4781,41 @@ void DISK_update (int tohpos) } /* instantly finish dma if dsklen==0 and wordsync detected */ - if (dskdmaen != DSKDMA_OFF && dma_enable && dsklength2 == 0 && dsklength == 0) - disk_dmafinished (); + if (dskdmaen != DSKDMA_OFF && dma_enable && dsklength2 == 0 && dsklength == 0) { + disk_dmafinished(); + } if (!done_jitter) { update_jitter(); done_jitter = true; } + // predict events in next line disk_doupdate_predict (disk_hpos); } void DSKLEN (uae_u16 v, int hpos) { int dr; - int prevlen = dsklen; + int prevlen = dsklen2; int prevdatalen = dsklength; int noselected = 0; int motormask; DISK_update (hpos); - dsklen = v; + dsklen2 = dsklen = v; dsklength2 = dsklength = dsklen & 0x3fff; + if (0 && dsklength > 1) { + dsklength++; + dsklength2++; + } if ((v & 0x8000) && (prevlen & 0x8000)) { if (dskdmaen == DSKDMA_READ && !(v & 0x4000)) { // update only currently active DMA length, don't change DMA state - write_log(_T("warning: Disk read DMA length rewrite %d -> %d. (%04x) PC=%08x\n"), prevlen & 0x3fff, v & 0x3fff, v, M68K_GETPC); + if ((prevlen & 0x3fff) != (v & 0x3fff)) { + write_log(_T("warning: Disk read DMA length rewrite %d -> %d. (%04x) PC=%08x\n"), prevlen & 0x3fff, v & 0x3fff, v, M68K_GETPC); + } return; } dskdmaen = DSKDMA_READ; @@ -4728,7 +4844,7 @@ void DSKLEN (uae_u16 v, int hpos) return; if (dsklength == 0 && dma_enable) { - disk_dmafinished (); + disk_dmafinished(); return; } @@ -4736,7 +4852,7 @@ void DSKLEN (uae_u16 v, int hpos) if (dsklength == 0) return; if (dsklength == 1) { - disk_dmafinished (); + disk_dmafinished(); return; } if (dskdmaen == DSKDMA_WRITE) { @@ -4995,45 +5111,6 @@ void DSKSYNC(int hpos, uae_u16 v) } } -STATIC_INLINE bool iswrite(void) -{ - return dskdmaen == DSKDMA_WRITE; -} - -void DSKDAT(uae_u16 v) -{ - if (fifo_inuse[2]) { - write_log(_T("DSKDAT: FIFO overflow!\n")); - return; - } - fifo_inuse[2] = fifo_inuse[1]; - fifo[2] = fifo[1]; - fifo_inuse[1] = fifo_inuse[0]; - fifo[1] = fifo[0]; - fifo_inuse[0] = iswrite() ? 2 : 1; - fifo[0] = v; - fifo_filled = 1; -} - -uae_u16 DSKDATR(void) -{ - int i; - uae_u16 v = 0; - for (i = 2; i >= 0; i--) { - if (fifo_inuse[i]) { - fifo_inuse[i] = 0; - v = fifo[i]; - break; - } - } - if (i < 0) { - write_log(_T("DSKDATR: FIFO underflow!\n")); - } else if (dskdmaen > 0 && dskdmaen < 3 && dsklength <= 0 && disk_fifostatus() < 0) { - disk_dmafinished(); - } - return v; -} - uae_u16 disk_dmal(void) { uae_u16 dmal = 0; diff --git a/include/disk.h b/include/disk.h index fc2814ec..d9fd67cd 100644 --- a/include/disk.h +++ b/include/disk.h @@ -104,7 +104,7 @@ extern void DSKSYNC (int, uae_u16); extern void DSKPTL (uae_u16); extern void DSKPTH (uae_u16); extern void DSKDAT (uae_u16); -extern uae_u16 DSKDATR (void); +extern uae_u16 DSKDATR (int); extern uae_u16 disk_dmal (void); extern uaecptr disk_getpt (void); extern int disk_fifostatus (void); -- 2.47.3