--- uae/src/disk.c 2018/04/24 16:58:12 1.1.1.10 +++ uae/src/disk.c 2018/04/24 17:07:10 1.1.1.15 @@ -3,7 +3,12 @@ * * Floppy disk emulation * - * Copyright 1995 Bernd Schmidt, Hannu Rummukainen + * Copyright 1995 Hannu Rummukainen + * Copyright 1995-2001 Bernd Schmidt + * Copyright 2000, 2001 Toni Wilen + * + * High Density Drive Handling by Dr. Adil Temel (C) 2001 [atemel1@hotmail.com] + * */ #include @@ -13,7 +18,7 @@ #include "config.h" #include "options.h" -#include "threaddep/penguin.h" +#include "threaddep/thread.h" #include "memory.h" #include "events.h" #include "custom.h" @@ -26,16 +31,19 @@ #include "xwin.h" #include "osemu.h" #include "execlib.h" +#include "savestate.h" -#define FLOPPY_GAP_LEN 360 /* writable track length with normal 2us bitcell/300RPM motor */ -#define FLOPPY_WRITE_LEN 6250 +#define FLOPPY_WRITE_LEN (12650 / 2) +/* This works out to 341 */ +#define FLOPPY_GAP_LEN (FLOPPY_WRITE_LEN - 11 * 544) /* (cycles/bitcell) << 8, normal = ((2us/280ns)<<8) = ~1830 */ #define NORMAL_FLOPPY_SPEED 1830 static int floppy_speed = NORMAL_FLOPPY_SPEED; #undef DISK_DEBUG +#undef DEBUG_DRIVE_ID /* UAE-1ADF (ADF_EXT2) * W reserved @@ -47,10 +55,10 @@ static int floppy_speed = NORMAL_FLOPPY_ * L track length in bits */ -static int side, direction, step, writing; -static uae_u8 selected = 15; +static int side, direction, writing; +static uae_u8 selected = 15, disabled; -static uae_u8 *writebuffer[544 * 11]; +static uae_u8 *writebuffer[544 * 22]; typedef enum { TRACK_AMIGADOS, TRACK_RAW, TRACK_RAW1 } image_tracktype; typedef struct { @@ -63,6 +71,11 @@ typedef struct { #define MAX_TRACKS 328 +#define DRIVE_ID_NONE 0xFFFFFFFF +#define DRIVE_ID_35DD 0x00000000 +#define DRIVE_ID_35HD 0xAAAAAAAA +#define DRIVE_ID_525DD 0x55555555 /* 40 track 5.25 drive , kickstart does not recognize this */ + typedef enum { ADF_NORMAL, ADF_EXT1, ADF_EXT2 } drive_filetype; typedef struct { FILE *diskfile; @@ -72,29 +85,67 @@ typedef struct { int cyl; int motoroff; int state; - int disabled; int wrprot; - uae_u16 bigmfmbuf[0x4000]; + uae_u16 bigmfmbuf[0x8000]; int mfmpos; - int mfmpos_hsync; int tracklen; int trackspeed; int dmalen; int num_tracks, num_secs; - /* dskchange is set to a small number when a disk is removed. At a step - * impulse, it is reset if there is a new disk in the drive or counted - * down to 1 if the drive contains a disk. DISK_check_change will only - * insert a new disk when dskchange is 1, or if the dskchange_time timer - * expires. The last condition can happen if AmigaOS isn't running and a - * game/demo doesn't use the step motor to check for a new disk. - */ int dskchange; int dskchange_time; int dskready; int dskready_time; + int steplimit; + int ddhd; /* 1=DD 2=HD */ + int drive_id_scnt; /* drive id shift counter */ + unsigned long drive_id; /* drive id to be reported */ + char newname[256]; /* storage space for new filename during eject delay */ } drive; -drive floppy[4]; +static drive floppy[4]; + +/* Keeps track of whether the Amiga program seems to be using the data coming + in from the disk; if this remains 0 for several seconds, we stop calling + disk_doupdate_read, even if the motor is still on. This is a rather large + performance win. This contains the number of frames since we last accessed + the disk. */ +static int disk_data_used; + +#ifdef DEBUG_DRIVE_ID +static char *drive_id_name(drive *drv) +{ + switch(drv->drive_id) + { + case DRIVE_ID_35HD : return "3.5HD"; + case DRIVE_ID_525DD: return "5.25DD"; + case DRIVE_ID_35DD : return "3.5DD"; + } + return "UNKNOWN"; +} +#endif + +/* Simulate exact behaviour of an A3000T 3.5 HD disk drive. + * The drive reports to be a 3.5 DD drive whenever there is no + * disk or a 3.5 DD disk is inserted. Only 3.5 HD drive id is reported + * when a real 3.5 HD disk is inserted. -Adil + */ +static void drive_settype_id(drive *drv) +{ + if (!drv->diskfile || drv->num_secs == 11) { + /* Double density */ + drv->drive_id = DRIVE_ID_35DD; + drv->ddhd = 1; + } else { + /* High density */ + drv->drive_id = DRIVE_ID_35HD; + drv->ddhd = 2; + } + drv->drive_id_scnt = 0; +#ifdef DEBUG_DRIVE_ID + write_log("drive_settype_id: DF%d: set to %s\n",drv-floppy, drive_id_name(drv)); +#endif +} /* code for track display */ static void update_drive_gui (int num) @@ -225,6 +276,7 @@ static int drive_insert (drive * drv, in tid->offs = i * 512 * drv->num_secs; } } + drive_settype_id(drv); /* Set DD or HD drive */ drv->buffered_side = 2; /* will force read */ drive_fill_bigbuf (drv); return 1; @@ -237,18 +289,27 @@ static int drive_empty (drive * drv) static void drive_step (drive * drv) { + if (drv->steplimit) + return; + /* A1200's floppy drive needs at least 30 raster lines between steps + * but we'll use very small value for better compatibility with faster CPU emulation + * (stupid trackloaders with CPU delay loops) + */ + drv->steplimit = 2; if (!drive_empty (drv)) drv->dskchange = 0; - else if (drv->dskchange > 1) { -/* printf("Stepping...\n"); - drv->dskchange--;*/ - } if (direction) { if (drv->cyl) drv->cyl--; - } else { +/* else + write_log("program tried to step beyond track zero\n"); + "no-click" programs does that +*/ + } else { if (drv->cyl < 83) drv->cyl++; + else + write_log("program tried to step over track 83\n"); } #if 0 && defined DISK_DEBUG @@ -273,14 +334,17 @@ static int drive_running (drive * drv) static void drive_motor (drive * drv, int off) { - if (drv->disabled) - off = 1; - /* A value of 2 works out to a guaranteed delay of 1/10 of a second - (which is the frequency with which DISK_check_change is called). + /* A value of 5 works out to a guaranteed delay of 1/2 of a second Higher values are dangerous, e.g. a value of 8 breaks the RSI demo. */ - if (drv->motoroff && !off) - drv->dskready_time = 2; + if (drv->motoroff && !off) { + drv->dskready_time = 5; + disk_data_used = 0; + drv->drive_id_scnt = 0; /* Reset id shift reg counter */ +#ifdef DEBUG_DRIVE_ID + write_log("drive_motor: Selected DF%d: reset id shift reg.\n",drv-floppy); +#endif + } drv->motoroff = off; if (drv->motoroff) { drv->dskready = 0; @@ -319,9 +383,9 @@ static void drive_fill_bigbuf (drive * d trackid *ti = drv->trackdata + tr; if (!drv->diskfile || tr >= drv->num_tracks) { - drv->tracklen = FLOPPY_WRITE_LEN * 2 * 8; + drv->tracklen = FLOPPY_WRITE_LEN * drv->ddhd * 2 * 8; drv->trackspeed = floppy_speed; - memset (drv->bigmfmbuf, 0xaa, FLOPPY_WRITE_LEN * 2); + memset (drv->bigmfmbuf, 0xaa, FLOPPY_WRITE_LEN * 2 * drv->ddhd); return; } if (drv->buffered_cyl == drv->cyl && drv->buffered_side == side) @@ -465,18 +529,19 @@ static uae_u32 getmfmlong (uae_u16 * mbu static int drive_write_adf_amigados (drive * drv) { int i, secwritten = 0; - int length = 2 * FLOPPY_WRITE_LEN; + int fwlen = FLOPPY_WRITE_LEN * drv->ddhd; + int length = 2 * fwlen; + int drvsec= drv->num_secs; uae_u32 odd, even, chksum, id, dlong; uae_u8 *secdata; uae_u8 secbuf[544]; uae_u16 *mbuf = drv->bigmfmbuf; uae_u16 *mend = mbuf + length; - char sectable[11]; - + char sectable[22]; memset (sectable, 0, sizeof (sectable)); - memcpy (mbuf + FLOPPY_WRITE_LEN, mbuf, FLOPPY_WRITE_LEN * sizeof (uae_u16)); + memcpy (mbuf + fwlen, mbuf, fwlen * sizeof (uae_u16)); mend -= (4 + 16 + 8 + 512); - while (secwritten < 11) { + while (secwritten < drvsec) { int trackoffs; do { @@ -492,7 +557,7 @@ static int drive_write_adf_amigados (dri id = (odd << 1) | even; trackoffs = (id & 0xff00) >> 8; - if (trackoffs > 10) { + if (trackoffs + 1 > drvsec) { if (drv->filetype == ADF_EXT2) return 2; write_log ("Disk write: weird sector number %d\n", trackoffs); @@ -556,8 +621,8 @@ static int drive_write_adf_amigados (dri if (secwritten < 0) write_log ("Disk write: sector labels ignored\n"); - diskfile_update (drv, 11 * 512 * 8, TRACK_AMIGADOS); - for (i = 0; i < 11; i++) { + diskfile_update (drv, drvsec * 512 * 8, TRACK_AMIGADOS); + for (i = 0; i < drvsec; i++) { fseek (drv->diskfile, drv->trackdata[drv->cyl * 2 + side].offs + i * 512, SEEK_SET); fwrite (writebuffer + i * 512, sizeof (uae_u8), 512, drv->diskfile); } @@ -616,11 +681,12 @@ static void drive_eject (drive * drv) { if (!drive_empty (drv)) zfile_close (drv->diskfile); - - drv->dskchange = 4; - drv->dskchange_time = 20; -/* printf("setting changed bit %d\n", drv-floppy);*/ drv->diskfile = 0; + drv->dskchange = 1; + drive_settype_id(drv); /* Back to 35 DD */ +#ifdef DISK_DEBUG + write_log ("eject drive %d\n", drv - &floppy[0]); +#endif } /* We use this function if we have no Kickstart ROM. @@ -637,43 +703,55 @@ void disk_eject (int num) gui_filename (num, ""); drive_eject (floppy + num); *currprefs.df[num] = *changed_prefs.df[num] = 0; + floppy[num].newname[0] = 0; } void disk_insert (int num, const char *name) { - /* just to be sure */ - drive_eject (floppy + num); - drive_insert (floppy + num, num, name); + drive *drv = floppy + num; + if (name[0] == 0) { + disk_eject (num); + } else if (!drive_empty(drv) || drv->dskchange_time > 0) { + drive_eject (drv); + /* set dskchange_time, disk_insert() will be + * called from DISK_check_change() after 1 second delay + * this makes sure that all programs detect disk change correctly + */ + strcpy (drv->newname, name); + drv->dskchange_time = 10; /* 1 second disk change delay */ + } else { + /* no delayed insert if drive is already empty */ + drive_insert (drv, num, name); + } } void DISK_check_change (void) { int i; - static int count = 0; - count++; for (i = 0; i < 4; i++) { - /* emulate drive motor turn on time */ - if (floppy[i].dskready_time) { - floppy[i].dskready_time--; - if (floppy[i].dskready_time == 0) { - floppy[i].dskready = 1; - } - } - if (strcmp (currprefs.df[i], changed_prefs.df[i]) != 0) { - if (currprefs.df[i][0] != '\0') { - drive_eject (floppy + i); - currprefs.df[i][0] = '\0'; - gui_filename (i, ""); - } else if (floppy[i].dskchange == 1) { - /* In theory, it should work without the dskchange test. - * In practice, it doesn't. */ - drive_insert (floppy + i, i, changed_prefs.df[i]); - } else if (floppy[i].dskchange > 1 && floppy[i].dskchange_time > 0) { - /* Force the dskchange bit to go to 1 after a given timeout */ - if (--floppy[i].dskchange_time == 0) { - floppy[i].dskchange = 1; - } + drive *drv = floppy + i; + gui_lock (); + if (strcmp (currprefs.df[i], changed_prefs.df[i])) { + strcpy (currprefs.df[i], changed_prefs.df[i]); + disk_insert (i, currprefs.df[i]); + } + gui_unlock (); + /* emulate drive motor turn on time */ + if (drv->dskready_time) { + drv->dskready_time--; + if (drv->dskready_time == 0) + drv->dskready = 1; + } + /* delay until new disk image is inserted */ + if (drv->dskchange_time) { + drv->dskchange_time--; + if (drv->dskchange_time == 0) { + drive_insert (drv, i, drv->newname); +#ifdef DISK_DEBUG + write_log ("delayed insert, drive %d, image '%s'\n", i, drv->newname); +#endif + drv->newname[0] = 0; } } } @@ -684,25 +762,26 @@ int disk_empty (int num) return drive_empty (floppy + num); } -static int not_ready = 0; +static int step; void DISK_select (uae_u8 data) { - static int lastselected; - int step_pulse; + int step_pulse, lastselected; int dr; + static uae_u8 prevdata; lastselected = selected; selected = (data >> 3) & 15; + selected |= disabled; if (selected != lastselected) - not_ready = 1; + disk_data_used = 1; side = 1 - ((data >> 2) & 1); direction = (data >> 1) & 1; step_pulse = data & 1; if (step != step_pulse) { step = step_pulse; - if (step == 0) { + if (step) { for (dr = 0; dr < 4; dr++) { if (!(selected & (1 << dr))) { drive_step (floppy + dr); @@ -711,14 +790,26 @@ void DISK_select (uae_u8 data) } } for (dr = 0; dr < 4; dr++) { - if (!(selected & (1 << dr)) && (lastselected & (1 << dr))) { - drive_motor (floppy + dr, data >> 7); + /* motor on/off workings tested with small assembler code on real Amiga 1200. */ + /* motor flipflop is set only when drive select goes from high to low */ + if (!(selected & (1 << dr)) && (lastselected & (1 << dr)) ) { + if (floppy[dr].motoroff) { + /* motor off: if motor bit = 0 in prevdata or data -> turn motor on */ + if ((prevdata & 0x80) == 0 || (data & 0x80) == 0) + drive_motor (floppy + dr, 0); + } else { + /* motor on: if motor bit = 1 in prevdata only (motor flag state in data has no effect) + -> turn motor off */ + if (prevdata & 0x80) + drive_motor (floppy + dr, 1); + } } } for (dr = 0; dr < 4; dr++) { floppy[dr].state = (!(selected & (1 << dr))) | !floppy[dr].motoroff; update_drive_gui (dr); } + prevdata = data; } uae_u8 DISK_status (void) @@ -730,26 +821,29 @@ uae_u8 DISK_status (void) drive *drv = floppy + dr; if (!(selected & (1 << dr))) { if (drive_running (drv)) { - if (drv->dskready && ! not_ready) + if (drv->dskready) + st &= ~0x20; + } else { + /* report drive ID */ + if (drv->drive_id & (1L << (31 - drv->drive_id_scnt))) + st |= 0x20; + else st &= ~0x20; - not_ready = 0; - } else if (!drv->disabled) { - st &= ~0x20; /* report drive ID */ +#ifdef DEBUG_DRIVE_ID + write_log("DISK_status: sel %d id %s [0x%08lx, bit #%02d: %d]\n", + dr,drive_id_name(drv), drv->drive_id << drv->drive_id_scnt, 31 - drv->drive_id_scnt, st & 0x20 ? 1:0); +#endif + /* Left shift id reg bit should be done with each LH transition of drv_sel */ + drv->drive_id_scnt++; + drv->drive_id_scnt &= 31; } - - if (drive_track0 (drv)) { + if (drive_track0 (drv)) st &= ~0x10; - } - if (drive_writeprotected (drv)) { + if (drive_writeprotected (drv)) st &= ~8; - } - if (drv->dskchange) { - /*printf("changed bit set: %d\n",dr); */ - st &= ~0x4; - if (drv->dskchange > 1) - drv->dskchange--; - } - } + if (drv->dskchange) + st &= ~4; + } } return st; } @@ -761,12 +855,10 @@ uae_u8 DISK_status (void) static uae_u16 dskbytr_tab[MAX_DISK_WORDS_PER_LINE * 2 + 1]; static uae_u8 dskbytr_cycle[MAX_DISK_WORDS_PER_LINE * 2 + 1]; -static uae_u16 dskdatr_tab[MAX_DISK_WORDS_PER_LINE + 1]; -static uae_u8 dskdatr_cycle[MAX_DISK_WORDS_PER_LINE + 1]; +static short wordsync_cycle[MAX_DISK_WORDS_PER_LINE * 2 + 1]; static uae_u32 dma_tab[MAX_DISK_WORDS_PER_LINE + 1]; static uae_u8 disk_sync[MAXHPOS]; static int disk_sync_cycle; -static int indexsync_last_cycle; static int dskdmaen, dsklength; static uae_u16 dsksync; static uae_u32 dskpt; @@ -774,13 +866,16 @@ static uae_u32 dskpt; static void disk_dmafinished (void) { INTREQ (0x8002); - dskdmaen = 0; -} + dskdmaen = 0; /* ??? */ +#ifdef DISK_DEBUG + write_log("disk dma finished %08.8X\n", dskpt); +#endif +} static void disk_events (int last) { eventtab[ev_disk].active = 0; - for (disk_sync_cycle = last; disk_sync_cycle < MAXHPOS; disk_sync_cycle++) { + for (disk_sync_cycle = last; disk_sync_cycle < maxhpos; disk_sync_cycle++) { if (disk_sync[disk_sync_cycle]) { eventtab[ev_disk].oldcycles = get_cycles (); eventtab[ev_disk].evtime = get_cycles () + (disk_sync_cycle - last) * CYCLE_UNIT; @@ -796,18 +891,16 @@ extern void cia_diskindex (void); void DISK_handler (void) { eventtab[ev_disk].active = 0; - if (disk_sync[disk_sync_cycle] & DISK_WORDSYNC) { + if (disk_sync[disk_sync_cycle] & DISK_WORDSYNC) INTREQ (0x9000); - indexsync_last_cycle = disk_sync_cycle + ((floppy_speed + 255) >> 8); - } - if (disk_sync[disk_sync_cycle] & DISK_INDEXSYNC) { + if (disk_sync[disk_sync_cycle] & DISK_INDEXSYNC) cia_diskindex (); - } disk_sync[disk_sync_cycle] = 0; disk_events (disk_sync_cycle); } -static int syncfound, dmaon, word, bitoffset; +static int dma_enable, bitoffset; +static uae_u32 word; /* Always carried through to the next line. */ static int disk_hpos; @@ -816,7 +909,7 @@ static int disk_hpos; static void disk_doupdate_write (drive * drv) { int hpos = disk_hpos; - while (hpos < (MAXHPOS << 8)) { + while (hpos < (maxhpos << 8)) { drv->mfmpos++; drv->mfmpos %= drv->tracklen; if (!drv->mfmpos) { @@ -838,11 +931,11 @@ static void disk_doupdate_write (drive * } hpos += drv->trackspeed; } - disk_hpos = hpos - (MAXHPOS << 8); + disk_hpos = hpos - (maxhpos << 8); } /* get one bit from MFM bit stream */ -static uae_u16 getonebit (uae_u16 * mfmbuf, int mfmpos, uae_u16 word) +static uae_u32 getonebit (uae_u16 * mfmbuf, int mfmpos, uae_u32 word) { uae_u16 *buf; @@ -852,64 +945,78 @@ static uae_u16 getonebit (uae_u16 * mfmb return word; } +#define WORDSYNC_CYCLES 7 /* (~7 * 280ns = 2us) */ + /* emulate disk read dma for full horizontal line */ static void disk_doupdate_read (drive * drv) { int hpos = disk_hpos; - int is_sync = 0, dmaon; - int j = 0, l = 0, k = 0; - - while (hpos < (MAXHPOS << 8)) { - word = getonebit (drv->bigmfmbuf, drv->mfmpos, word); + int is_sync = 0; + int j = 0, k = 1, l = 0; + uae_u16 synccheck; + static int dskbytr_last = 0, wordsync_last = -1; + + dskbytr_tab[0] = dskbytr_tab[dskbytr_last]; + + if (wordsync_last >= 0 && maxhpos - wordsync_cycle[wordsync_last] < WORDSYNC_CYCLES) + wordsync_cycle[l++] = (maxhpos - wordsync_cycle[wordsync_last]) - WORDSYNC_CYCLES; + wordsync_last = -1; + + while (hpos < (maxhpos << 8)) { + if (drv->dskready) + word = getonebit (drv->bigmfmbuf, drv->mfmpos, word); + else + word <<= 1; drv->mfmpos++; drv->mfmpos %= drv->tracklen; if (!drv->mfmpos) { disk_sync[hpos >> 8] |= DISK_INDEXSYNC; is_sync = 1; } - bitoffset++; - bitoffset &= 15; - if (bitoffset == 0 && (!(adkcon & 0x400) || syncfound)) - dmaon = 1; - else - dmaon = 0; - if (word == dsksync) { - if ((adkcon & 0x400) == 0x400 && ! syncfound) { - syncfound = 1; - bitoffset = 0; - } - disk_sync[hpos >> 8] |= DISK_WORDSYNC; - is_sync = 1; - } - if (dmaon) { - dma_tab[j++] = word; + if (bitoffset == 31 && dma_enable) { + dma_tab[j++] = (word >> 16) & 0xffff; if (j == MAX_DISK_WORDS_PER_LINE - 1) { write_log ("Bug: Disk DMA buffer overflow!\n"); j--; } } - if (bitoffset == 0 || bitoffset == 8) { - dskbytr_tab[k] = (word & 0xff) | 0x8000; + if (bitoffset == 15 || bitoffset == 23 || bitoffset == 31) { + dskbytr_tab[k] = (word >> 8) & 0xff; + dskbytr_tab[k] |= 0x8000; + dskbytr_last = k; dskbytr_cycle[k++] = hpos >> 8; - if (bitoffset == 0) { - dskdatr_tab[l] = word; - dskdatr_cycle[l++] = hpos >> 8; + } + synccheck = (word >> 8) & 0xffff; + if (synccheck == dsksync) { + if (adkcon & 0x400) { + if (bitoffset != 23 || !dma_enable) + bitoffset = 7; + dma_enable = 1; } + wordsync_last = l; + wordsync_cycle[l++] = hpos >> 8; + disk_sync[hpos >> 8] |= DISK_WORDSYNC; + is_sync = 1; } + bitoffset++; + if (bitoffset == 32) bitoffset = 16; hpos += drv->trackspeed; } dma_tab[j] = 0xffffffff; + dskbytr_cycle[k] = 255; + wordsync_cycle[l] = 255; if (is_sync) disk_events (0); - dskbytr_cycle[k] = 255; - dskdatr_cycle[l] = 255; - disk_hpos = hpos - (MAXHPOS << 8); + disk_hpos = hpos - (maxhpos << 8); } /* disk DMA fetch happens on real Amiga at the beginning of next horizontal line (cycles 9, 11 and 13 according to hardware manual) We transfer all DMA'd data at cycle 0. I don't think any program cares about this small difference. + + We must handle dsklength = 0 because some copy protections use it to detect + wordsync without transferring any data. */ static void dodmafetch (void) { @@ -917,14 +1024,15 @@ static void dodmafetch (void) i = 0; while (dma_tab[i] != 0xffffffff && dskdmaen == 2 && (dmacon & 0x210) == 0x210) { - put_word (dskpt, dma_tab[i]); - dskpt += 2; - dsklength--; + if (dsklength > 0) { + put_word (dskpt, dma_tab[i++]); + dskpt += 2; + dsklength--; + } if (dsklength == 0) { disk_dmafinished (); break; } - i++; } dma_tab[0] = 0xffffffff; } @@ -939,20 +1047,17 @@ static void DISK_start (void) int tr = drv->cyl * 2 + side; trackid *ti = drv->trackdata + tr; - drive_fill_bigbuf (drv); if (dskdmaen == 3) { - drv->tracklen = FLOPPY_WRITE_LEN * 8 * 2; + drv->tracklen = FLOPPY_WRITE_LEN * drv->ddhd * 8 * 2; drv->trackspeed = floppy_speed; } /* Ugh. A nasty hack. Assume ADF_EXT1 tracks are always read from the start. */ if (ti->type == TRACK_RAW1) - drv->mfmpos = 0; - drv->mfmpos %= drv->tracklen; + drv->mfmpos = 0; } } - syncfound = 0; - dmaon = 0; + dma_enable = (adkcon & 0x400) ? 0 : 1; word = 0; bitoffset = 0; dma_tab[0] = 0xffffffff; @@ -966,7 +1071,8 @@ void DISK_update (void) for (dr = 0; dr < 4; dr++) { drive *drv = &floppy[dr]; - drv->mfmpos_hsync = drv->mfmpos; + if (drv->steplimit) + drv->steplimit--; } if (linecounter) { linecounter--; @@ -976,21 +1082,27 @@ void DISK_update (void) } dodmafetch (); - indexsync_last_cycle = -1; for (dr = 0; dr < 4; dr++) { drive *drv = &floppy[dr]; if (drv->motoroff) continue; if (selected & (1 << dr)) { - drv->mfmpos += (MAXHPOS << 8) / drv->trackspeed; + drv->mfmpos += (maxhpos << 8) / drv->trackspeed; drv->mfmpos %= drv->tracklen; continue; } + drive_fill_bigbuf (drv); + drv->mfmpos %= drv->tracklen; + if (dskdmaen > 1) + disk_data_used = 0; + if (dskdmaen == 3) disk_doupdate_write (drv); - else + else if (1 || disk_data_used < MAXVPOS * 50 * 2) { + disk_data_used++; disk_doupdate_read (drv); + } return; } } @@ -1009,7 +1121,7 @@ void DSKLEN (uae_u16 v, int hpos) write_log ("warning: Disk write DMA aborted, %d words left\n", dsklength); dskdmaen = 0; } - dsklength = v & 0x3fff; + dsklength = v & 0x3ffe; if (dskdmaen <= 1) return; if (v & 0x4000) @@ -1053,12 +1165,17 @@ void DSKLEN (uae_u16 v, int hpos) int pos = drv->mfmpos & ~15; int i; + drive_fill_bigbuf (drv); if (adkcon & 0x400) { for (i = 0; i < drv->tracklen; i += 16) { pos += 16; pos %= drv->tracklen; - if (drv->bigmfmbuf[pos >> 4] == dsksync) + if (drv->bigmfmbuf[pos >> 4] == dsksync) { + /* must skip first disk sync marker */ + pos += 16; + pos %= drv->tracklen; break; + } } if (i >= drv->tracklen) return; @@ -1078,7 +1195,7 @@ void DSKLEN (uae_u16 v, int hpos) } } -/* this unoptimized code should be enough (DSKBYTR and DSKDATR are used very rarely) */ +/* this is very unoptimized. DSKBYTR is used very rarely, so it should not matter. */ uae_u16 DSKBYTR (int hpos) { @@ -1086,27 +1203,31 @@ uae_u16 DSKBYTR (int hpos) int i; i = 0; - while (hpos > dskbytr_cycle[i]) + while (hpos > dskbytr_cycle[i + 1]) i++; v = dskbytr_tab[i]; - if (indexsync_last_cycle >= 0 && indexsync_last_cycle <= hpos) - v |= 0x1000; dskbytr_tab[i] &= ~0x8000; + if (wordsync_cycle[0] != 255) { + i = 0; + while (hpos < wordsync_cycle[i]) + i++; + if (hpos - wordsync_cycle[i] <= WORDSYNC_CYCLES) + v |= 0x1000; + } if (dskdmaen && (dmacon & 0x210) == 0x210) v |= 0x4000; if (dskdmaen == 3) v |= 0x2000; + + disk_data_used = 0; + return v; } +/* not a real hardware register */ uae_u16 DSKDATR (int hpos) { - int i; - - i = 0; - while (hpos > dskdatr_cycle[i]) - i++; - return dskdatr_tab[i]; + return 0; } void DSKSYNC (uae_u16 v) @@ -1138,16 +1259,19 @@ void DSKPTL (uae_u16 v) void DISK_init (void) { - int i; + int dr; dma_tab[0] = 0xffffffff; - dskbytr_cycle[0] = 255; - dskdatr_cycle[0] = 255; - indexsync_last_cycle = -1; + dskbytr_cycle[0] = 0; + dskbytr_cycle[1] = 255; + wordsync_cycle[0] = 255; - for (i = 0; i < 4; i++) { - if (!drive_insert (floppy + i, i, currprefs.df[i])) - disk_eject (i); + for (dr = 0; dr < 4; dr++) { + drive *drv = &floppy[dr]; + /* reset all drive types to 3.5 DD */ + drive_settype_id (drv); + if (!drive_insert (drv, dr, currprefs.df[dr])) + disk_eject (dr); } if (disk_empty (0)) write_log ("No disk in drive 0.\n"); @@ -1156,11 +1280,18 @@ void DISK_init (void) void DISK_reset (void) { int i; + + if (savestate_state) + return; + disk_hpos = 0; + disk_data_used = 0; + disabled = 0; for (i = 0; i < 4; i++) { - floppy[i].disabled = 0; - if (i >= currprefs.nr_floppies) - floppy[i].disabled = 1; + if (i >= currprefs.nr_floppies) { + disabled |= 1 << i; + floppy[i].motoroff = 1; + } } } @@ -1172,3 +1303,99 @@ void dumpdisk (void) printf ("Drive %d: motor %s cylinder %2d selected %d\n", i, drv->motoroff ? "off" : " on", drv->cyl, selected & (1 << i)); } } + +/* Disk save/restore code */ + +void DISK_save_custom (uae_u32 *pdskpt, uae_u16 *pdsklength, uae_u16 *pdsksync, uae_u16 *pdskdatr, uae_u16 *pdskbytr) +{ + if(pdskpt) *pdskpt = dskpt; + if(pdsklength) *pdsklength = dsklength; + if(pdsksync) *pdsksync = dsksync; + if(pdskdatr) *pdskdatr = 0; + if(pdskbytr) *pdskbytr = dskbytr_tab[0]; +} + +void DISK_restore_custom (uae_u32 pdskpt, uae_u16 pdsklength, uae_u16 pdskdatr, uae_u16 pdskbytr) +{ + dskpt = pdskpt; + dsklength = pdsklength; + dskbytr_tab[0] = pdskbytr; +} + +uae_u8 *restore_disk(int num,uae_u8 *src) +{ + drive *drv; + int state; + + drv = &floppy[num]; + disabled &= ~(1 << num); + drv->drive_id = restore_u32 (); + state = restore_u8 (); + if (state & 2) + disabled |= 1 << num; + else + drv->motoroff = (state & 1) ? 0 : 1; + drv->cyl = restore_u8 (); + drv->dskready = restore_u8 (); + drv->drive_id_scnt = restore_u8 (); + drv->mfmpos = restore_u32 (); + restore_u32 (); + strncpy(currprefs.df[num],src,255); + src+=strlen(src)+1; + + return src; +} + +uae_u8 *save_disk(int num,int *len) +{ + uae_u8 *dstbak,*dst; + drive *drv; + + drv = &floppy[num]; + dstbak = dst = malloc (2+1+1+1+1+4+4+256); + save_u32 (drv->drive_id); /* drive type ID */ + save_u8 ((drv->motoroff ? 0:1) | ((disabled & (1 << num)) ? 2 : 0)); /* state */ + save_u8 (drv->cyl); /* cylinder */ + save_u8 (drv->dskready); /* dskready */ + save_u8 (drv->drive_id_scnt); /* id mode position */ + save_u32 (drv->mfmpos); /* disk position */ + save_u32 (0); /* CRC of disk image */ + strcpy (dst, currprefs.df[num]);/* image name */ + dst += strlen(dst) + 1; + + *len = dst - dstbak; + return dstbak; +} + +/* internal floppy controller variables */ + +uae_u8 *restore_floppy(uae_u8 *src) +{ + word = restore_u16(); + bitoffset = restore_u8(); + dma_enable = restore_u8(); + disk_hpos = restore_u8(); + dskdmaen = restore_u8(); + word |= restore_u16() << 16; + + return src; +} + +uae_u8 *save_floppy(int *len) +{ + uae_u8 *dstbak, *dst; + + /* flush dma buffer before saving */ + dodmafetch(); + + dstbak = dst = malloc(2+1+1+1+1+2); + save_u16 (word); /* current fifo (low word) */ + save_u8 (bitoffset); /* dma bit offset */ + save_u8 (dma_enable); /* disk sync found */ + save_u8 (disk_hpos); /* next bit read position */ + save_u8 (dskdmaen); /* dma status */ + save_u16 (word >> 16); /* current fifo (high word) */ + + *len = dst - dstbak; + return dstbak; +}