--- uae/src/disk.c 2018/04/24 16:58:12 1.1.1.10 +++ uae/src/disk.c 2018/04/24 17:21:42 1.1.1.21 @@ -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 @@ -11,9 +16,8 @@ #include "sysconfig.h" #include "sysdeps.h" -#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 +30,28 @@ #include "xwin.h" #include "osemu.h" #include "execlib.h" +#include "savestate.h" +#include "crc32.h" -#define FLOPPY_GAP_LEN 360 +/* support HD floppies */ +#define FLOPPY_DRIVE_HD /* 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 +#ifdef FLOPPY_DRIVE_HD +#define DDHDMULT 2 +#else +#define DDHDMULT 1 +#endif + static int floppy_speed = NORMAL_FLOPPY_SPEED; #undef DISK_DEBUG +#undef DEBUG_DRIVE_ID /* UAE-1ADF (ADF_EXT2) * W reserved @@ -47,54 +63,129 @@ 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 * DDHDMULT]; -typedef enum { TRACK_AMIGADOS, TRACK_RAW, TRACK_RAW1 } image_tracktype; +typedef enum { TRACK_AMIGADOS, TRACK_RAW, TRACK_RAW1, TRACK_PCDOS } image_tracktype; typedef struct { - uae_u16 len; uae_u32 offs; - int bitlen; + int bitlen, track; unsigned int sync; image_tracktype type; + uae_u16 len; } trackid; #define MAX_TRACKS 328 +/* We have three kinds of Amiga floppy drives + * - internal A500/A2000 drive: + * ID is always DRIVE_ID_NONE (S.T.A.G expects this) + * - HD drive (A3000/A4000): + * ID is DRIVE_ID_35DD if DD floppy is inserted or drive is empty + * ID is DRIVE_ID_35HD if HD floppy is inserted + * - regular external drive: + * ID is always DRIVE_ID_35DD + */ + +#define DRIVE_ID_NONE 0x00000000 +#define DRIVE_ID_35DD 0xFFFFFFFF +#define DRIVE_ID_35HD 0xAAAAAAAA +#define DRIVE_ID_525SD 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; + drive_type type; + struct zfile *diskfile; drive_filetype filetype; trackid trackdata[MAX_TRACKS]; int buffered_cyl, buffered_side; 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 hard_num_cyls; 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 */ + int idbit; + 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) +{ + int t = drv->type; + + switch (t) + { + case DRV_35_HD: +#ifdef FLOPPY_DRIVE_HD + if (!drv->diskfile || drv->ddhd <= 1) + drv->drive_id = DRIVE_ID_35DD; + else + drv->drive_id = DRIVE_ID_35HD; +#else + drv->drive_id = DRIVE_ID_35DD; +#endif + break; + case DRV_35_DD_ESCOM: + case DRV_35_DD: + default: + drv->drive_id = DRIVE_ID_35DD; + break; + case DRV_525_SD: + drv->drive_id = DRIVE_ID_525SD; + break; + case DRV_NONE: + drv->drive_id = DRIVE_ID_NONE; + break; + } +#ifdef DEBUG_DRIVE_ID + write_log ("drive_settype_id: DF%d: set to %s\n", drv-floppy, drive_id_name (drv)); +#endif +#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) @@ -117,11 +208,20 @@ static void update_drive_gui (int num) gui_led (num + 1, gui_data.drive_motor[num]); } -static void drive_fill_bigbuf (drive * drv); +static void drive_fill_bigbuf (drive *drv); -FILE *DISK_validate_filename (const char *fname, int leave_open, int *wrprot) +static void drive_image_free (drive *drv) { - FILE *f = zfile_open (fname, "r+b"); + drv->filetype = -1; + if (!drv->diskfile) + return; + zfile_fclose (drv->diskfile); + drv->diskfile = 0; +} + +struct zfile *DISK_validate_filename (const char *fname, int leave_open, int *wrprot) +{ + struct zfile *f = zfile_open (fname, "r+b"); if (f) { if (wrprot) *wrprot = 0; @@ -131,19 +231,62 @@ FILE *DISK_validate_filename (const char f = zfile_open (fname, "rb"); } if (!leave_open) - zfile_close (f); + zfile_fclose (f); return f; } -static int drive_insert (drive * drv, int dnum, const char *fname) +static void track_reset (drive *drv) +{ + drv->tracklen = FLOPPY_WRITE_LEN * 2 * 8; + drv->trackspeed = floppy_speed; + drv->buffered_side = -1; + memset (drv->bigmfmbuf, 0xaa, FLOPPY_WRITE_LEN * 2 * drv->ddhd); +} + +static int read_header_ext2 (struct zfile *diskfile, trackid *trackdata, int *num_tracks, int *ddhd) +{ + uae_u8 buffer[2 + 2 + 4 + 4]; + int offs; + int i; + + zfile_fseek (diskfile, 0, SEEK_SET); + zfile_fread (buffer, 1, 8, diskfile); + if (strncmp ((char *)buffer, "UAE-1ADF", 8)) + return 0; + + zfile_fread (buffer, 1, 4, diskfile); + *num_tracks = buffer[2] * 256 + buffer[3]; + offs = 8 + 2 + 2 + *num_tracks * (2 + 2 + 4 + 4); + + for (i = 0; i < *num_tracks; i++) { + trackid *tid = trackdata + i; + zfile_fread (buffer, 2 + 2 + 4 + 4, 1, diskfile); + tid->type = buffer[2] * 256 + buffer[3]; + tid->len = buffer[5] * 65536 + buffer[6] * 256 + buffer[7]; + tid->bitlen = buffer[9] * 65536 + buffer[10] * 256 + buffer[11]; + tid->offs = offs; + if (tid->len > 20000 && ddhd) + *ddhd = 2; + tid->track = i; + offs += tid->len; + } + return 1; +} + +static int drive_insert (drive *drv, int dnum, const char *fname) { unsigned char buffer[2 + 2 + 4 + 4]; trackid *tid; + int size; + drive_image_free (drv); drv->diskfile = DISK_validate_filename (fname, 1, &drv->wrprot); + drv->ddhd = 1; + drv->num_secs = 0; + drv->hard_num_cyls = drv->type == DRV_525_SD ? 40 : 80; + if (drv->diskfile == 0) { - drv->tracklen = FLOPPY_WRITE_LEN * 2 * 8; - drv->trackspeed = floppy_speed; + track_reset (drv); return 0; } strncpy (currprefs.df[dnum], fname, 255); @@ -152,30 +295,22 @@ static int drive_insert (drive * drv, in changed_prefs.df[dnum][255] = 0; gui_filename (dnum, fname); - fread (buffer, sizeof (char), 8, drv->diskfile); + memset (buffer, 0, sizeof (buffer)); + size = 0; + if (drv->diskfile) { + zfile_fread (buffer, sizeof (char), 8, drv->diskfile); + zfile_fseek (drv->diskfile, 0, SEEK_END); + size = zfile_ftell (drv->diskfile); + zfile_fseek (drv->diskfile, 0, SEEK_SET); + } + if (strncmp ((char *) buffer, "UAE-1ADF", 8) == 0) { - int offs; - int i; + read_header_ext2 (drv->diskfile, drv->trackdata, &drv->num_tracks, &drv->ddhd); - fread (buffer, 1, 4, drv->diskfile); - drv->num_tracks = buffer[2] * 256 + buffer[3]; drv->filetype = ADF_EXT2; drv->num_secs = 11; - offs = 8 + 2 + 2 + drv->num_tracks * (2 + 2 + 4 + 4); - - for (i = 0; i < drv->num_tracks; i++) { - tid = &drv->trackdata[i]; - fread (buffer, 2 + 2 + 4 + 4, 1, drv->diskfile); - tid->type = buffer[2] * 256 + buffer[3]; - tid->len = buffer[5] * 65536 + buffer[6] * 256 + buffer[7]; - tid->bitlen = buffer[9] * 65536 + buffer[10] * 256 + buffer[11]; - tid->offs = offs; - if (tid->type == TRACK_AMIGADOS) { - if (tid->len > 20000) - drv->num_secs = 22; - } - offs += tid->len; - } + if (drv->ddhd > 1) + drv->num_secs = 22; } else if (strncmp ((char *) buffer, "UAE--ADF", 8) == 0) { int offs = 160 * 4 + 8; int i; @@ -184,10 +319,12 @@ static int drive_insert (drive * drv, in drv->num_tracks = 160; drv->num_secs = 11; + zfile_fseek (drv->diskfile, 8, SEEK_SET); + drv->wrprot = 1; /* write to adf_ext1 not implemented */ for (i = 0; i < 160; i++) { tid = &drv->trackdata[i]; - fread (buffer, 4, 1, drv->diskfile); + zfile_fread (buffer, 4, 1, drv->diskfile); tid->sync = buffer[0] * 256 + buffer[1]; tid->len = buffer[2] * 256 + buffer[3]; tid->offs = offs; @@ -202,18 +339,14 @@ static int drive_insert (drive * drv, in } } else { int i; - drv->filetype = ADF_NORMAL; - fseek (drv->diskfile, 0, SEEK_END); - i = ftell (drv->diskfile); - fseek (drv->diskfile, 0, SEEK_SET); - /* High-density disk? */ - if (i >= 160 * 22 * 512) - drv->num_tracks = i / (512 * (drv->num_secs = 22)); - else - drv->num_tracks = i / (512 * (drv->num_secs = 11)); + if (size >= 160 * 22 * 512) { + drv->num_tracks = size / (512 * (drv->num_secs = 22)); + drv->ddhd = 2; + } else + drv->num_tracks = size / (512 * (drv->num_secs = 11)); if (drv->num_tracks > MAX_TRACKS) write_log ("Your diskfile is too big!\n"); @@ -225,6 +358,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 +371,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 +416,19 @@ 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; +#ifdef DEBUG_DRIVE_ID + write_log ("drive_motor: Selected DF%d: reset id shift reg.\n", drv-floppy); +#endif + } + if (!drv->motoroff && off) { + drv->drive_id_scnt = 0; /* Reset id shift reg counter */ + } drv->motoroff = off; if (drv->motoroff) { drv->dskready = 0; @@ -288,13 +436,120 @@ static void drive_motor (drive * drv, in } } -static void read_floppy_data (drive * drv, int track, int offset, unsigned char *dst, int len) +static void read_floppy_data (struct zfile *diskfile, trackid *ti, int offset, unsigned char *dst, int len) { - trackid *ti; + zfile_fseek (diskfile, ti->offs + offset, SEEK_SET); + zfile_fread (dst, 1, len, diskfile); +} - ti = &drv->trackdata[track]; - fseek (drv->diskfile, ti->offs + offset, SEEK_SET); - fread (dst, 1, len, drv->diskfile); +static void reset_drive (int i) +{ + drive *drv = &floppy[i]; + drive_image_free (drv); + drv->motoroff = 1; + disabled &= ~(1 << i); + if (currprefs.dfxtype[i] < 0) + disabled |= 1 << i; + + drv->dskchange_time = 0; + drv->buffered_cyl = -1; + drv->buffered_side = -1; + gui_led (i + 1, 0); + drive_settype_id (drv); + strcpy (currprefs.df[i], changed_prefs.df[i]); + drv->newname[0] = 0; + if (!drive_insert (drv, i, currprefs.df[i])) + disk_eject (i); +} + +static uae_u8 mfmencodetable[16] = { + 0x2a, 0x29, 0x24, 0x25, 0x12, 0x11, 0x14, 0x15, + 0x4a, 0x49, 0x44, 0x45, 0x52, 0x51, 0x54, 0x55 +}; + + +static uae_u16 dos_encode_byte (uae_u8 byte) +{ + uae_u8 b2, b1; + uae_u16 word; + + b1 = byte; + b2 = b1 >> 4; + b1 &= 15; + word = mfmencodetable[b2] <<8 | mfmencodetable[b1]; + return (word | ((word & (256 | 64)) ? 0 : 128)); +} + +static uae_u16 *mfmcoder (uae_u8 *src, uae_u16 *dest, int len) +{ + int i; + + for (i = 0; i < len; i++) { + *dest = dos_encode_byte (*src++); + *dest |= ((dest[-1] & 1)|| (*dest & 0x4000)) ? 0: 0x8000; + dest++; + } + return dest; +} + +static void decode_pcdos (drive *drv) +{ + int i; + int tr = drv->cyl * 2 + side; + uae_u16 *dstmfmbuf, *mfm2; + uae_u8 secbuf[700]; + uae_u16 crc16; + trackid *ti = drv->trackdata + tr; + + mfm2 = drv->bigmfmbuf; + *mfm2++ = 0x9254; + memset (secbuf, 0x4e, 80); // 94 + memset (secbuf + 80, 0x00, 12); // 12 + secbuf[92] = 0xc2; + secbuf[93] = 0xc2; + secbuf[94] = 0xc2; + secbuf[95] = 0xfc; + memset (secbuf + 96, 0x4e, 50); // 50 + dstmfmbuf = mfmcoder (secbuf, mfm2, 146); + mfm2[92] = 0x5224; + mfm2[93] = 0x5224; + mfm2[94] = 0x5224; + for (i = 0; i < drv->num_secs; i++) { + mfm2 = dstmfmbuf; + memset (secbuf, 0x00, 12); + secbuf[12] = 0xa1; + secbuf[13] = 0xa1; + secbuf[14] = 0xa1; + secbuf[15] = 0xfe; + secbuf[16] = drv->cyl; + secbuf[17] = side; + secbuf[18] = 1 + i; + secbuf[19] = 2; // 128 << 2 = 512 + crc16 = get_crc16 (secbuf + 12, 3 + 1 + 4); + secbuf[20] = crc16 >> 8; + secbuf[21] = crc16 & 0xff; + memset (secbuf + 22, 0x4e, 22); + memset (secbuf + 44, 0x00, 12); + secbuf[56] = 0xa1; + secbuf[57] = 0xa1; + secbuf[58] = 0xa1; + secbuf[59] = 0xfb; + read_floppy_data (drv->diskfile, ti, i * 512, &secbuf[60], 512); + crc16 = get_crc16 (secbuf + 56, 3 + 1 + 512); + secbuf[60 + 512] = crc16 >> 8; + secbuf[61 + 512] = crc16 & 0xff; + memset (secbuf + 512 + 62, 0x4e, 76 / drv->ddhd); + dstmfmbuf = mfmcoder (secbuf, mfm2, 60 + 512 + 2 + 76 / drv->ddhd); + mfm2[12] = 0x4489; + mfm2[13] = 0x4489; + mfm2[14] = 0x4489; + mfm2[56] = 0x4489; + mfm2[57] = 0x4489; + mfm2[58] = 0x4489; + } + for (i = 0; i < 200; i++) + *dstmfmbuf++ = 0x9254; + drv->tracklen = (dstmfmbuf - drv->bigmfmbuf) * 16; } /* Megalomania does not like zero MFM words... */ @@ -313,103 +568,123 @@ static void mfmcode (uae_u16 * mfm, int } } +static void decode_amigados (drive *drv) +{ + /* Normal AmigaDOS format track */ + int tr = drv->cyl * 2 + side; + int sec; + int dstmfmoffset = 0; + uae_u16 *dstmfmbuf = drv->bigmfmbuf; + int len = drv->num_secs * 544 + FLOPPY_GAP_LEN; + trackid *ti = drv->trackdata + tr; + + memset (dstmfmbuf, 0xaa, len * 2); + dstmfmoffset += FLOPPY_GAP_LEN; + drv->tracklen = len * 2 * 8; + + for (sec = 0; sec < drv->num_secs; sec++) { + uae_u8 secbuf[544]; + uae_u16 mfmbuf[544]; + int i; + uae_u32 deven, dodd; + uae_u32 hck = 0, dck = 0; + + secbuf[0] = secbuf[1] = 0x00; + secbuf[2] = secbuf[3] = 0xa1; + secbuf[4] = 0xff; + secbuf[5] = tr; + secbuf[6] = sec; + secbuf[7] = drv->num_secs - sec; + + for (i = 8; i < 24; i++) + secbuf[i] = 0; + + read_floppy_data (drv->diskfile, ti, sec * 512, &secbuf[32], 512); + + mfmbuf[0] = mfmbuf[1] = 0xaaaa; + mfmbuf[2] = mfmbuf[3] = 0x4489; + + deven = ((secbuf[4] << 24) | (secbuf[5] << 16) + | (secbuf[6] << 8) | (secbuf[7])); + dodd = deven >> 1; + deven &= 0x55555555; + dodd &= 0x55555555; + + mfmbuf[4] = dodd >> 16; + mfmbuf[5] = dodd; + mfmbuf[6] = deven >> 16; + mfmbuf[7] = deven; + + for (i = 8; i < 48; i++) + mfmbuf[i] = 0xaaaa; + for (i = 0; i < 512; i += 4) { + deven = ((secbuf[i + 32] << 24) | (secbuf[i + 33] << 16) + | (secbuf[i + 34] << 8) | (secbuf[i + 35])); + dodd = deven >> 1; + deven &= 0x55555555; + dodd &= 0x55555555; + mfmbuf[(i >> 1) + 32] = dodd >> 16; + mfmbuf[(i >> 1) + 33] = dodd; + mfmbuf[(i >> 1) + 256 + 32] = deven >> 16; + mfmbuf[(i >> 1) + 256 + 33] = deven; + } + + for (i = 4; i < 24; i += 2) + hck ^= (mfmbuf[i] << 16) | mfmbuf[i + 1]; + + deven = dodd = hck; + dodd >>= 1; + mfmbuf[24] = dodd >> 16; + mfmbuf[25] = dodd; + mfmbuf[26] = deven >> 16; + mfmbuf[27] = deven; + + for (i = 32; i < 544; i += 2) + dck ^= (mfmbuf[i] << 16) | mfmbuf[i + 1]; + + deven = dodd = dck; + dodd >>= 1; + mfmbuf[28] = dodd >> 16; + mfmbuf[29] = dodd; + mfmbuf[30] = deven >> 16; + mfmbuf[31] = deven; + mfmcode (mfmbuf + 4, 544 - 4); + + for (i = 0; i < 544; i++) { + dstmfmbuf[dstmfmoffset % len] = mfmbuf[i]; + dstmfmoffset++; + } + } +#ifdef DISK_DEBUG + write_log ("amigados read track %d\n", tr); +#endif +} + static void drive_fill_bigbuf (drive * drv) { int tr = drv->cyl * 2 + side; 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) return; - if (ti->type == TRACK_AMIGADOS) { - /* Normal AmigaDOS format track */ - int sec; - drv->tracklen = (drv->num_secs * 544 + FLOPPY_GAP_LEN) * 2 * 8; - memset (drv->bigmfmbuf, 0xaa, FLOPPY_GAP_LEN * 2); - - for (sec = 0; sec < drv->num_secs; sec++) { - uae_u8 secbuf[544]; - int i; - uae_u16 *mfmbuf = drv->bigmfmbuf + 544 * sec + FLOPPY_GAP_LEN; - uae_u32 deven, dodd; - uae_u32 hck = 0, dck = 0; - - secbuf[0] = secbuf[1] = 0x00; - secbuf[2] = secbuf[3] = 0xa1; - secbuf[4] = 0xff; - secbuf[5] = tr; - secbuf[6] = sec; - secbuf[7] = drv->num_secs - sec; - - for (i = 8; i < 24; i++) - secbuf[i] = 0; - - read_floppy_data (drv, tr, sec * 512, &secbuf[32], 512); - - mfmbuf[0] = mfmbuf[1] = 0xaaaa; - mfmbuf[2] = mfmbuf[3] = 0x4489; - - deven = ((secbuf[4] << 24) | (secbuf[5] << 16) - | (secbuf[6] << 8) | (secbuf[7])); - dodd = deven >> 1; - deven &= 0x55555555; - dodd &= 0x55555555; - - mfmbuf[4] = dodd >> 16; - mfmbuf[5] = dodd; - mfmbuf[6] = deven >> 16; - mfmbuf[7] = deven; - - for (i = 8; i < 48; i++) - mfmbuf[i] = 0xaaaa; - for (i = 0; i < 512; i += 4) { - deven = ((secbuf[i + 32] << 24) | (secbuf[i + 33] << 16) - | (secbuf[i + 34] << 8) | (secbuf[i + 35])); - dodd = deven >> 1; - deven &= 0x55555555; - dodd &= 0x55555555; - mfmbuf[(i >> 1) + 32] = dodd >> 16; - mfmbuf[(i >> 1) + 33] = dodd; - mfmbuf[(i >> 1) + 256 + 32] = deven >> 16; - mfmbuf[(i >> 1) + 256 + 33] = deven; - } - - for (i = 4; i < 24; i += 2) - hck ^= (mfmbuf[i] << 16) | mfmbuf[i + 1]; + if (ti->type == TRACK_PCDOS) { + decode_pcdos (drv); - deven = dodd = hck; - dodd >>= 1; - mfmbuf[24] = dodd >> 16; - mfmbuf[25] = dodd; - mfmbuf[26] = deven >> 16; - mfmbuf[27] = deven; - - for (i = 32; i < 544; i += 2) - dck ^= (mfmbuf[i] << 16) | mfmbuf[i + 1]; - - deven = dodd = dck; - dodd >>= 1; - mfmbuf[28] = dodd >> 16; - mfmbuf[29] = dodd; - mfmbuf[30] = deven >> 16; - mfmbuf[31] = deven; - mfmcode (mfmbuf + 4, 544 - 4); - } -#ifdef DISK_DEBUG - write_log ("amigados read track %d\n", tr); -#endif + } else if (ti->type == TRACK_AMIGADOS) { + decode_amigados (drv); } else { int i; int base_offset = ti->type == TRACK_RAW ? 0 : 1; drv->tracklen = ti->bitlen + 16 * base_offset; drv->bigmfmbuf[0] = ti->sync; - read_floppy_data (drv, tr, 0, (char *) (drv->bigmfmbuf + base_offset), (ti->bitlen + 7) / 8); + read_floppy_data (drv->diskfile, ti, 0, (unsigned char *) (drv->bigmfmbuf + base_offset), (ti->bitlen + 7) / 8); for (i = base_offset; i < (drv->tracklen + 15) / 16; i++) { uae_u16 *mfm = drv->bigmfmbuf + i; uae_u8 *data = (uae_u8 *) mfm; @@ -437,18 +712,18 @@ static void diskfile_update (drive * drv track = drv->cyl * 2 + side; ti = &drv->trackdata[track]; ti->bitlen = len; - fseek (drv->diskfile, 8 + 4 + (2 + 2 + 4 + 4) * track, SEEK_SET); + zfile_fseek (drv->diskfile, 8 + 4 + (2 + 2 + 4 + 4) * track, SEEK_SET); memset (buf, 0, sizeof buf); ti->type = type; buf[3] = ti->type; do_put_mem_long ((uae_u32 *) (buf + 4), ti->len); do_put_mem_long ((uae_u32 *) (buf + 8), ti->bitlen); - fwrite (buf, sizeof (buf), 1, drv->diskfile); + zfile_fwrite (buf, sizeof (buf), 1, drv->diskfile); if (ti->len > (len + 7) / 8) { zerobuf = malloc (ti->len); memset (zerobuf, 0, ti->len); - fseek (drv->diskfile, ti->offs, SEEK_SET); - fwrite (zerobuf, 1, ti->len, drv->diskfile); + zfile_fseek (drv->diskfile, ti->offs, SEEK_SET); + zfile_fwrite (zerobuf, 1, ti->len, drv->diskfile); free (zerobuf); } #ifdef DISK_DEBUG @@ -465,18 +740,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 +768,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,10 +832,10 @@ 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++) { - fseek (drv->diskfile, drv->trackdata[drv->cyl * 2 + side].offs + i * 512, SEEK_SET); - fwrite (writebuffer + i * 512, sizeof (uae_u8), 512, drv->diskfile); + diskfile_update (drv, drvsec * 512 * 8, TRACK_AMIGADOS); + for (i = 0; i < drvsec; i++) { + zfile_fseek (drv->diskfile, drv->trackdata[drv->cyl * 2 + side].offs + i * 512, SEEK_SET); + zfile_fwrite (writebuffer + i * 512, sizeof (uae_u8), 512, drv->diskfile); } return 0; @@ -584,8 +860,8 @@ static int drive_write_ext2 (drive * drv uae_u8 *data = (uae_u8 *) mfm; *mfm = 256 * *data + *(data + 1); } - fseek (drv->diskfile, ti->offs, SEEK_SET); - fwrite (drv->bigmfmbuf, 1, len, drv->diskfile); + zfile_fseek (drv->diskfile, ti->offs, SEEK_SET); + zfile_fwrite (drv->bigmfmbuf, 1, len, drv->diskfile); return 1; } @@ -604,7 +880,7 @@ static void drive_write_data (drive * dr case ADF_EXT2: ret = drive_write_adf_amigados (drv); if (ret) { - /*write_log("not an amigados track (error %d), writing as raw track\n",ret); */ + /*write_log ("not an amigados track (error %d), writing as raw track\n", ret); */ drive_write_ext2 (drv); } break; @@ -614,13 +890,16 @@ static void drive_write_data (drive * dr 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; + drive_image_free (drv); + drv->dskchange = 1; + drv->ddhd = 1; + drv->dskchange_time = 0; + drv->dskready = 0; + drv->dskready_time = 0; + 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. @@ -628,8 +907,8 @@ static void drive_eject (drive * drv) void DISK_ersatz_read (int tr, int sec, uaecptr dest) { uae_u8 *dptr = get_real_address (dest); - fseek (floppy[0].diskfile, floppy[0].trackdata[tr].offs + sec * 512, SEEK_SET); - fread (dptr, 1, 512, floppy[0].diskfile); + zfile_fseek (floppy[0].diskfile, floppy[0].trackdata[tr].offs + sec * 512, SEEK_SET); + zfile_fread (dptr, 1, 512, floppy[0].diskfile); } void disk_eject (int num) @@ -637,43 +916,61 @@ 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 (!strcmp (currprefs.df[num], name)) + return; + strcpy (drv->newname, name); + strcpy (currprefs.df[num], name); + + 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 + */ + drv->dskchange_time = 10; /* 1 second disk change delay */ + } else { + drv->dskchange_time = 1; + } } 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 (currprefs.dfxtype[i] != changed_prefs.dfxtype[i]) { + drv->type = currprefs.dfxtype[i] = changed_prefs.dfxtype[i]; + reset_drive (i); + } + if (drv->dskchange_time == 0 && strcmp (currprefs.df[i], changed_prefs.df[i])) + disk_insert (i, changed_prefs.df[i]); + gui_unlock (); + /* emulate drive motor turn on time */ + if (drv->dskready_time && !drive_empty (drv)) { + 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,41 +981,60 @@ 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))) { + if (!((selected | disabled) & (1 << dr))) { drive_step (floppy + dr); } } } } - for (dr = 0; dr < 4; dr++) { - if (!(selected & (1 << dr)) && (lastselected & (1 << dr))) { - drive_motor (floppy + dr, data >> 7); + if (!savestate_state) { + for (dr = 0; dr < 4; dr++) { + drive *drv = floppy + dr; + /* motor on/off workings tested with small assembler code on real Amiga 1200. */ + /* motor/id flipflop is set only when drive select goes from high to low */ + if (!(selected & (1 << dr)) && (lastselected & (1 << dr)) ) { + drv->drive_id_scnt++; + drv->drive_id_scnt &= 31; + drv->idbit = (drv->drive_id & (1L << (31 - drv->drive_id_scnt))) ? 1 : 0; + if (!(disabled & (1 << dr))) { + if ((prevdata & 0x80) == 0 || (data & 0x80) == 0) { + /* motor off: if motor bit = 0 in prevdata or data -> turn motor on */ + drive_motor (drv, 0); + } else if (prevdata & 0x80) { + /* motor on: if motor bit = 1 in prevdata only (motor flag state in data has no effect) + -> turn motor off */ + drive_motor (drv, 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) @@ -728,27 +1044,28 @@ uae_u8 DISK_status (void) for (dr = 0; dr < 4; dr++) { drive *drv = floppy + dr; - if (!(selected & (1 << dr))) { + if (!((selected | disabled) & (1 << dr))) { if (drive_running (drv)) { - if (drv->dskready && ! not_ready) + if (drv->dskready) st &= ~0x20; - not_ready = 0; - } else if (!drv->disabled) { - st &= ~0x20; /* report drive ID */ + } else { + /* report drive ID */ + if (drv->idbit && currprefs.dfxtype[dr] != DRV_35_DD_ESCOM) + st &= ~0x20; +#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 } - - 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; + } else if (!(selected & (1 << dr))) { + if (drv->idbit) + st &= ~0x20; } } return st; @@ -761,12 +1078,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 +1089,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; @@ -791,23 +1109,19 @@ static void disk_events (int last) } } -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 +1130,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 +1152,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 +1166,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,18 +1245,48 @@ 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; } +/* this is very unoptimized. DSKBYTR is used very rarely, so it should not matter. */ + +uae_u16 DSKBYTR (int hpos) +{ + uae_u16 v; + int i; + + i = 0; + while (hpos > dskbytr_cycle[i + 1]) + i++; + v = dskbytr_tab[i]; + 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; +} + static void DISK_start (void) { int dr; @@ -939,20 +1297,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; @@ -963,10 +1318,11 @@ static int linecounter; void DISK_update (void) { int dr; - + 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 +1332,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 +1371,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 +1415,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,35 +1445,10 @@ void DSKLEN (uae_u16 v, int hpos) } } -/* this unoptimized code should be enough (DSKBYTR and DSKDATR are used very rarely) */ - -uae_u16 DSKBYTR (int hpos) -{ - uae_u16 v; - int i; - - i = 0; - while (hpos > dskbytr_cycle[i]) - i++; - v = dskbytr_tab[i]; - if (indexsync_last_cycle >= 0 && indexsync_last_cycle <= hpos) - v |= 0x1000; - dskbytr_tab[i] &= ~0x8000; - if (dskdmaen && (dmacon & 0x210) == 0x210) - v |= 0x4000; - if (dskdmaen == 3) - v |= 0x2000; - 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 +1480,17 @@ 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; + drv->type = currprefs.dfxtype[dr]; + reset_drive (dr); } if (disk_empty (0)) write_log ("No disk in drive 0.\n"); @@ -1156,11 +1499,16 @@ void DISK_init (void) void DISK_reset (void) { int i; + + if (savestate_state) + return; + disk_hpos = 0; + disk_data_used = 0; + dskdmaen = 0; + disabled = 0; for (i = 0; i < 4; i++) { - floppy[i].disabled = 0; - if (i >= currprefs.nr_floppies) - floppy[i].disabled = 1; + reset_drive (i); } } @@ -1172,3 +1520,144 @@ 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; +} + +const uae_u8 *restore_disk (int num, const uae_u8 *src) +{ + drive *drv; + int state; + int newis; + + drv = &floppy[num]; + disabled &= ~(1 << num); + drv->drive_id = restore_u32 (); + drv->motoroff = 1; + drv->idbit = 0; + state = restore_u8 (); + if (state & 2) { + disabled |= 1 << num; + if (changed_prefs.nr_floppies > num) + changed_prefs.nr_floppies = num; + changed_prefs.dfxtype[num] = -1; + } else { + drive_type dfxtype; + drv->motoroff = (state & 1) ? 0 : 1; + drv->idbit = (state & 4) ? 1 : 0; + if (changed_prefs.nr_floppies < num) + changed_prefs.nr_floppies = num; + switch (drv->drive_id) + { + case DRIVE_ID_35HD: + dfxtype = DRV_35_HD; + break; + case DRIVE_ID_525SD: + dfxtype = DRV_525_SD; + break; + default: + dfxtype = DRV_35_DD; + break; + } + changed_prefs.dfxtype[num] = dfxtype; + } + drv->type = changed_prefs.dfxtype[num]; + drv->buffered_cyl = -1; + drv->buffered_side = -1; + drv->cyl = restore_u8 (); + drv->dskready = restore_u8 (); + drv->drive_id_scnt = restore_u8 (); + drv->mfmpos = restore_u32 (); + drv->dskchange = 0; + drv->dskchange_time = 0; + restore_u32 (); + strncpy (changed_prefs.df[num], src, 255); + changed_prefs.df[num][255] = 0; + newis = changed_prefs.df[num][0] ? 1 : 0; + src += strlen (src) + 1; + + if (!(disabled & (1 << num))) { + if (!newis) { + drv->dskchange = 1; + } else { + drive_insert (floppy + num, num, changed_prefs.df[num]); + } + } + + return src; +} + +uae_u8 *save_disk (int num, int *len, uae_u8 *dstptr) +{ + uae_u8 *dstbak,*dst; + drive *drv; + + drv = &floppy[num]; + if (dstptr) + dstbak = dst = dstptr; + else + 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) | (drv->idbit ? 4 : 0) | (drv->dskchange ? 8 : 0)); + 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 */ + +const uae_u8 *restore_floppy (const 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 *dstptr) +{ + uae_u8 *dstbak, *dst; + + /* flush dma buffer before saving */ + dodmafetch (); + + if (dstptr) + dstbak = dst = dstptr; + else + 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; +}