"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.

This commit is contained in:
Toni Wilen 2023-11-18 16:34:07 +02:00
parent eb30bdb03f
commit bae812fec4
3 changed files with 169 additions and 91 deletions

View File

@ -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) {

255
disk.cpp
View File

@ -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;

View File

@ -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);