redquark-amiberry-rb/src/disk.cpp
2019-12-14 13:17:13 +01:00

4077 lines
106 KiB
C++

/*
* UAE - The Un*x Amiga Emulator
*
* Floppy disk emulation
*
* Copyright 1995 Hannu Rummukainen
* Copyright 1995-2001 Bernd Schmidt
* Copyright 2000-2003 Toni Wilen
*
* Original High Density Drive Handling by Dr. Adil Temel (C) 2001 [atemel1@hotmail.com]
*
*/
#include <string.h>
#include <ctype.h>
#include <sys/time.h>
#include <stdbool.h>
#include <stdlib.h>
#include "sysdeps.h"
#include "uae.h"
#include "options.h"
#include "memory.h"
#include "custom.h"
#include "disk.h"
#include "gui.h"
#include "zfile.h"
#include "newcpu.h"
#include "savestate.h"
#include "cia.h"
#ifdef FDI2RAW
#include "fdi2raw.h"
#endif
#ifdef CAPS
#include "uae/caps.h"
#endif
#ifdef SCP
#include "scp.h"
#endif
#include "crc32.h"
#include "fsdb.h"
/* support HD floppies */
#define FLOPPY_DRIVE_HD
/* writable track length with normal 2us bitcell/300RPM motor, 12667 PAL, 12797 NTSC */
#define FLOPPY_WRITE_LEN_PAL 12668
#define FLOPPY_WRITE_LEN_NTSC 12798
#define FLOPPY_WRITE_LEN (currprefs.floppy_write_length > 256 ? currprefs.floppy_write_length / 2 : (currprefs.ntscmode ? (FLOPPY_WRITE_LEN_NTSC / 2) : (FLOPPY_WRITE_LEN_PAL / 2)))
#define FLOPPY_WRITE_MAXLEN 0x3800
/* This works out to 350 */
#define FLOPPY_GAP_LEN (FLOPPY_WRITE_LEN - 11 * 544)
/* (cycles/bitcell) << 8, normal = ((2us/280ns)<<8) = ~1828.5714 */
#define NORMAL_FLOPPY_SPEED (currprefs.ntscmode ? 1812 : 1829)
/* max supported floppy drives, for small memory systems */
#define MAX_FLOPPY_DRIVES 4
#ifdef FLOPPY_DRIVE_HD
#define DDHDMULT 2
#else
#define DDHDMULT 1
#endif
#define MAX_SECTORS (DDHDMULT * 11)
/* UAE-1ADF (ADF_EXT2)
* W reserved
* W number of tracks (default 2*80=160)
*
* W reserved
* W type, 0=normal AmigaDOS track, 1 = raw MFM (upper byte = disk revolutions - 1)
* L available space for track in bytes (must be even)
* L track length in bits
*/
static int side, direction;
static uae_u8 selected = 15, disabled;
static uae_u8 writebuffer[544 * MAX_SECTORS];
#define DISK_INDEXSYNC 1
#define DISK_WORDSYNC 2
#define DISK_REVOLUTION 4 /* 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 uae_u16 dskbytr_val;
static uae_u32 dskpt;
static bool fifo_filled;
static uae_u16 fifo[3];
static int fifo_inuse[3];
static int dma_enable, bitoffset;
static uae_u16 word, dsksync;
static unsigned long dsksync_cycles;
#define WORDSYNC_TIME 11
/* Always carried through to the next line. */
static int disk_hpos;
static int disk_jitter;
static int indexdecay;
static uae_u8 prev_data;
static int prev_step;
static struct diskinfo disk_info_data = { 0 };
typedef enum { TRACK_AMIGADOS, TRACK_RAW, TRACK_RAW1, TRACK_PCDOS, TRACK_DISKSPARE, TRACK_NONE } image_tracktype;
typedef struct {
uae_u16 len;
uae_u32 offs;
int bitlen, track;
uae_u16 sync;
image_tracktype type;
int revolutions;
} trackid;
#define MAX_TRACKS (2 * 83)
/* 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_NONE = -1, ADF_NORMAL, ADF_EXT1, ADF_EXT2, ADF_FDI, ADF_IPF, ADF_SCP, ADF_CATWEASEL, ADF_PCDOS, ADF_KICK, ADF_SKICK } drive_filetype;
typedef struct {
struct zfile *diskfile;
struct zfile *writediskfile;
drive_filetype filetype;
trackid trackdata[MAX_TRACKS];
trackid writetrackdata[MAX_TRACKS];
int buffered_cyl, buffered_side;
int cyl;
bool motoroff;
int motordelay; /* dskrdy needs some clock cycles before it changes after switching off motor */
bool state;
int selected_delay;
bool wrprot;
bool forcedwrprot;
uae_u16 bigmfmbuf[0x4000 * DDHDMULT];
uae_u16 tracktiming[0x4000 * DDHDMULT];
int multi_revolution;
int revolution_check;
int skipoffset;
int mfmpos;
int indexoffset;
int tracklen;
int revolutions;
int prevtracklen;
int trackspeed;
int num_tracks, write_num_tracks, num_secs, num_heads;
int hard_num_cyls;
bool dskeject;
bool dskchange;
int dskchange_time;
bool dskready;
int dskready_up_time;
int dskready_down_time;
int writtento;
int steplimit;
frame_time_t steplimitcycle;
int indexhack, indexhackmode;
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 */
TCHAR newname[256]; /* storage space for new filename during eject delay */
bool newnamewriteprotected;
uae_u32 crc32;
#ifdef FDI2RAW
FDI *fdi;
#endif
int useturbo;
int floppybitcounter; /* number of bits left */
#ifdef CAPS
int lastdataacesstrack;
int lastrev;
bool track_access_done;
#endif
} drive;
#define MIN_STEPLIMIT_CYCLE (CYCLE_UNIT * 140)
static uae_u16 bigmfmbufw[0x4000 * DDHDMULT];
static drive floppy[MAX_FLOPPY_DRIVES];
static uae_u8 exeheader[]={0x00,0x00,0x03,0xf3,0x00,0x00,0x00,0x00};
static uae_u8 bootblock_ofs[]={
0x44,0x4f,0x53,0x00,0xc0,0x20,0x0f,0x19,0x00,0x00,0x03,0x70,0x43,0xfa,0x00,0x18,
0x4e,0xae,0xff,0xa0,0x4a,0x80,0x67,0x0a,0x20,0x40,0x20,0x68,0x00,0x16,0x70,0x00,
0x4e,0x75,0x70,0xff,0x60,0xfa,0x64,0x6f,0x73,0x2e,0x6c,0x69,0x62,0x72,0x61,0x72,
0x79
};
static uae_u8 bootblock_ffs[]={
0x44, 0x4F, 0x53, 0x01, 0xE3, 0x3D, 0x0E, 0x72, 0x00, 0x00, 0x03, 0x70, 0x43, 0xFA, 0x00, 0x3E,
0x70, 0x25, 0x4E, 0xAE, 0xFD, 0xD8, 0x4A, 0x80, 0x67, 0x0C, 0x22, 0x40, 0x08, 0xE9, 0x00, 0x06,
0x00, 0x22, 0x4E, 0xAE, 0xFE, 0x62, 0x43, 0xFA, 0x00, 0x18, 0x4E, 0xAE, 0xFF, 0xA0, 0x4A, 0x80,
0x67, 0x0A, 0x20, 0x40, 0x20, 0x68, 0x00, 0x16, 0x70, 0x00, 0x4E, 0x75, 0x70, 0xFF, 0x4E, 0x75,
0x64, 0x6F, 0x73, 0x2E, 0x6C, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x00, 0x65, 0x78, 0x70, 0x61,
0x6E, 0x73, 0x69, 0x6F, 0x6E, 0x2E, 0x6C, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x00, 0x00, 0x00,
};
#define FS_OFS_DATABLOCKSIZE 488
#define FS_FLOPPY_BLOCKSIZE 512
#define FS_EXTENSION_BLOCKS 72
#define FS_FLOPPY_TOTALBLOCKS 1760
#define FS_FLOPPY_RESERVED 2
static void writeimageblock (struct zfile *dst, uae_u8 *sector, int offset)
{
zfile_fseek (dst, offset, SEEK_SET);
zfile_fwrite (sector, FS_FLOPPY_BLOCKSIZE, 1, dst);
}
static uae_u32 disk_checksum (uae_u8 *p, uae_u8 *c)
{
uae_u32 cs = 0;
int i;
for (i = 0; i < FS_FLOPPY_BLOCKSIZE; i+= 4)
cs += (p[i] << 24) | (p[i+1] << 16) | (p[i+2] << 8) | (p[i+3] << 0);
cs = -cs;
if (c) {
c[0] = cs >> 24; c[1] = cs >> 16; c[2] = cs >> 8; c[3] = cs >> 0;
}
return cs;
}
static int dirhash (const uae_char *name)
{
unsigned long hash;
int i;
hash = strlen (name);
for(i = 0; i < strlen (name); i++) {
hash = hash * 13;
hash = hash + toupper (name[i]);
hash = hash & 0x7ff;
}
hash = hash % ((FS_FLOPPY_BLOCKSIZE / 4) - 56);
return hash;
}
static void disk_date (uae_u8 *p)
{
static int pdays, pmins, pticks;
int days, mins, ticks;
struct timeval tv;
struct mytimeval mtv;
gettimeofday (&tv, NULL);
#ifndef __PSP2__
tv.tv_sec -= _timezone;
#endif
mtv.tv_sec = tv.tv_sec;
mtv.tv_usec = tv.tv_usec;
timeval_to_amiga (&mtv, &days, &mins, &ticks, 50);
if (days == pdays && mins == pmins && ticks == pticks) {
ticks++;
if (ticks >= 50 * 60) {
ticks = 0;
mins++;
if (mins >= 24 * 60)
days++;
}
}
pdays = days;
pmins = mins;
pticks = ticks;
p[0] = days >> 24; p[1] = days >> 16; p[2] = days >> 8; p[3] = days >> 0;
p[4] = mins >> 24; p[5] = mins >> 16; p[6] = mins >> 8; p[7] = mins >> 0;
p[8] = ticks >> 24; p[9] = ticks >> 16; p[10] = ticks >> 8; p[11] = ticks >> 0;
}
static void createbootblock (uae_u8 *sector, int bootable)
{
memset (sector, 0, FS_FLOPPY_BLOCKSIZE);
memcpy (sector, "DOS", 3);
if (bootable)
memcpy (sector, bootblock_ofs, sizeof bootblock_ofs);
}
static void createrootblock (uae_u8 *sector, const TCHAR *disk_name)
{
char *dn = ua (disk_name);
if (strlen (dn) >= 30)
dn[30] = 0;
const char *dn2 = dn;
if (dn2[0] == 0)
dn2 = "empty";
memset (sector, 0, FS_FLOPPY_BLOCKSIZE);
sector[0+3] = 2;
sector[12+3] = 0x48;
sector[312] = sector[313] = sector[314] = sector[315] = (uae_u8)0xff;
sector[316+2] = 881 >> 8; sector[316+3] = 881 & 255;
sector[432] = strlen (dn2);
strcpy ((char*)sector + 433, dn2);
sector[508 + 3] = 1;
disk_date (sector + 420);
memcpy (sector + 472, sector + 420, 3 * 4);
memcpy (sector + 484, sector + 420, 3 * 4);
xfree (dn);
}
static int getblock (uae_u8 *bitmap, int *prev)
{
int i = *prev;
while (bitmap[i] != 0xff) {
if (bitmap[i] == 0) {
bitmap[i] = 1;
*prev = i;
return i;
}
i++;
}
i = 0;
while (bitmap[i] != 0xff) {
if (bitmap[i] == 0) {
bitmap[i] = 1;
*prev = i;
return i;
}
i++;
}
return -1;
}
static void pl (uae_u8 *sector, int offset, uae_u32 v)
{
sector[offset + 0] = v >> 24;
sector[offset + 1] = v >> 16;
sector[offset + 2] = v >> 8;
sector[offset + 3] = v >> 0;
}
static int createdirheaderblock (uae_u8 *sector, int parent, const char *filename, uae_u8 *bitmap, int *prevblock)
{
int block = getblock (bitmap, prevblock);
memset (sector, 0, FS_FLOPPY_BLOCKSIZE);
pl (sector, 0, 2);
pl (sector, 4, block);
disk_date (sector + 512 - 92);
sector[512 - 80] = strlen (filename);
strcpy ((char *)sector + 512 - 79, filename);
pl (sector, 512 - 12, parent);
pl (sector, 512 - 4, 2);
return block;
}
static int createfileheaderblock (struct zfile *z,uae_u8 *sector, int parent, const char *filename, struct zfile *src, uae_u8 *bitmap, int *prevblock)
{
uae_u8 sector2[FS_FLOPPY_BLOCKSIZE];
uae_u8 sector3[FS_FLOPPY_BLOCKSIZE];
int block = getblock (bitmap, prevblock);
int datablock = getblock (bitmap, prevblock);
int datasec = 1;
int extensions;
int extensionblock, extensioncounter, headerextension = 1;
int size;
zfile_fseek (src, 0, SEEK_END);
size = zfile_ftell (src);
zfile_fseek (src, 0, SEEK_SET);
extensions = (size + FS_OFS_DATABLOCKSIZE - 1) / FS_OFS_DATABLOCKSIZE;
memset (sector, 0, FS_FLOPPY_BLOCKSIZE);
pl (sector, 0, 2);
pl (sector, 4, block);
pl (sector, 8, extensions > FS_EXTENSION_BLOCKS ? FS_EXTENSION_BLOCKS : extensions);
pl (sector, 16, datablock);
pl (sector, FS_FLOPPY_BLOCKSIZE - 188, size);
disk_date (sector + FS_FLOPPY_BLOCKSIZE - 92);
sector[FS_FLOPPY_BLOCKSIZE - 80] = strlen (filename);
strcpy ((char *)sector + FS_FLOPPY_BLOCKSIZE - 79, filename);
pl (sector, FS_FLOPPY_BLOCKSIZE - 12, parent);
pl (sector, FS_FLOPPY_BLOCKSIZE - 4, -3);
extensioncounter = 0;
extensionblock = 0;
while (size > 0) {
int datablock2 = datablock;
int extensionblock2 = extensionblock;
if (extensioncounter == FS_EXTENSION_BLOCKS) {
extensioncounter = 0;
extensionblock = getblock (bitmap, prevblock);
if (datasec > FS_EXTENSION_BLOCKS + 1) {
pl (sector3, 8, FS_EXTENSION_BLOCKS);
pl (sector3, FS_FLOPPY_BLOCKSIZE - 8, extensionblock);
pl (sector3, 4, extensionblock2);
disk_checksum(sector3, sector3 + 20);
writeimageblock (z, sector3, extensionblock2 * FS_FLOPPY_BLOCKSIZE);
} else {
pl (sector, 512 - 8, extensionblock);
}
memset (sector3, 0, FS_FLOPPY_BLOCKSIZE);
pl (sector3, 0, 16);
pl (sector3, FS_FLOPPY_BLOCKSIZE - 12, block);
pl (sector3, FS_FLOPPY_BLOCKSIZE - 4, -3);
}
memset (sector2, 0, FS_FLOPPY_BLOCKSIZE);
pl (sector2, 0, 8);
pl (sector2, 4, block);
pl (sector2, 8, datasec++);
pl (sector2, 12, size > FS_OFS_DATABLOCKSIZE ? FS_OFS_DATABLOCKSIZE : size);
zfile_fread (sector2 + 24, size > FS_OFS_DATABLOCKSIZE ? FS_OFS_DATABLOCKSIZE : size, 1, src);
size -= FS_OFS_DATABLOCKSIZE;
datablock = 0;
if (size > 0) datablock = getblock (bitmap, prevblock);
pl (sector2, 16, datablock);
disk_checksum(sector2, sector2 + 20);
writeimageblock (z, sector2, datablock2 * FS_FLOPPY_BLOCKSIZE);
if (datasec <= FS_EXTENSION_BLOCKS + 1)
pl (sector, 512 - 204 - extensioncounter * 4, datablock2);
else
pl (sector3, 512 - 204 - extensioncounter * 4, datablock2);
extensioncounter++;
}
if (datasec > FS_EXTENSION_BLOCKS) {
pl (sector3, 8, extensioncounter);
disk_checksum(sector3, sector3 + 20);
writeimageblock (z, sector3, extensionblock * FS_FLOPPY_BLOCKSIZE);
}
disk_checksum(sector, sector + 20);
writeimageblock (z, sector, block * FS_FLOPPY_BLOCKSIZE);
return block;
}
static void createbitmapblock (uae_u8 *sector, uae_u8 *bitmap)
{
int i, j;
memset (sector, 0, FS_FLOPPY_BLOCKSIZE);
i = 0;
for (;;) {
uae_u32 mask = 0;
for (j = 0; j < 32; j++) {
if (bitmap[2 + i * 32 + j] == 0xff)
break;
if (!bitmap[2 + i * 32 + j])
mask |= 1 << j;
}
sector[4 + i * 4 + 0] = mask >> 24;
sector[4 + i * 4 + 1] = mask >> 16;
sector[4 + i * 4 + 2] = mask >> 8;
sector[4 + i * 4 + 3] = mask >> 0;
if (bitmap[2 + i * 32 + j] == 0xff)
break;
i++;
}
disk_checksum(sector, sector + 0);
}
static int createimagefromexe (struct zfile *src, struct zfile *dst)
{
uae_u8 sector1[FS_FLOPPY_BLOCKSIZE], sector2[FS_FLOPPY_BLOCKSIZE];
uae_u8 bitmap[FS_FLOPPY_TOTALBLOCKS + 8];
int exesize;
int blocksize = FS_OFS_DATABLOCKSIZE;
int blocks, extensionblocks;
int totalblocks;
int fblock1, dblock1;
const char *fname1 = "runme.exe";
const TCHAR *fname1b = _T("runme.adf");
const char *fname2 = "startup-sequence";
const char *dirname1 = "s";
struct zfile *ss;
int prevblock;
memset (bitmap, 0, sizeof bitmap);
zfile_fseek (src, 0, SEEK_END);
exesize = zfile_ftell (src);
blocks = (exesize + blocksize - 1) / blocksize;
extensionblocks = (blocks + FS_EXTENSION_BLOCKS - 1) / FS_EXTENSION_BLOCKS;
/* bootblock=2, root=1, bitmap=1, startup-sequence=1+1, exefileheader=1 */
totalblocks = 2 + 1 + 1 + 2 + 1 + blocks + extensionblocks;
if (totalblocks > FS_FLOPPY_TOTALBLOCKS)
return 0;
bitmap[880] = 1;
bitmap[881] = 1;
bitmap[0] = 1;
bitmap[1] = 1;
bitmap[1760] = -1;
prevblock = 880;
dblock1 = createdirheaderblock (sector2, 880, dirname1, bitmap, &prevblock);
ss = zfile_fopen_empty (src, fname1b, strlen(fname1));
zfile_fwrite (fname1, strlen(fname1), 1, ss);
fblock1 = createfileheaderblock (dst, sector1, dblock1, fname2, ss, bitmap, &prevblock);
zfile_fclose (ss);
pl (sector2, 24 + dirhash (fname2) * 4, fblock1);
disk_checksum(sector2, sector2 + 20);
writeimageblock (dst, sector2, dblock1 * FS_FLOPPY_BLOCKSIZE);
fblock1 = createfileheaderblock (dst, sector1, 880, fname1, src, bitmap, &prevblock);
createrootblock (sector1, zfile_getfilename (src));
pl (sector1, 24 + dirhash (fname1) * 4, fblock1);
pl (sector1, 24 + dirhash (dirname1) * 4, dblock1);
disk_checksum(sector1, sector1 + 20);
writeimageblock (dst, sector1, 880 * FS_FLOPPY_BLOCKSIZE);
createbitmapblock (sector1, bitmap);
writeimageblock (dst, sector1, 881 * FS_FLOPPY_BLOCKSIZE);
createbootblock (sector1, 1);
writeimageblock (dst, sector1, 0 * FS_FLOPPY_BLOCKSIZE);
return 1;
}
static int get_floppy_speed(void)
{
int m = currprefs.floppy_speed;
if (m <= 10)
m = 100;
m = NORMAL_FLOPPY_SPEED * 100 / m;
return m;
}
static int get_floppy_speed_from_image(drive* drv)
{
int l, m;
l = drv->tracklen;
m = get_floppy_speed() * l / (2 * 8 * FLOPPY_WRITE_LEN * drv->ddhd);
// 4us track?
if (l < (FLOPPY_WRITE_LEN_PAL * 8) * 4 / 6)
m *= 2;
if (m <= 0)
m = 1;
return m;
}
/* 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 = currprefs.floppyslots[drv - &floppy[0]].dfxtype;
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:
case DRV_525_DD:
default:
drv->drive_id = DRIVE_ID_35DD;
break;
case DRV_525_SD:
drv->drive_id = DRIVE_ID_525SD;
break;
case DRV_NONE:
case DRV_PC_525_ONLY_40:
case DRV_PC_525_40_80:
case DRV_PC_35_ONLY_80:
drv->drive_id = DRIVE_ID_NONE;
break;
}
}
static void drive_image_free(drive *drv)
{
switch (drv->filetype)
{
case ADF_IPF:
#ifdef CAPS
caps_unloadimage(drv - floppy);
#endif
break;
case ADF_SCP:
#ifdef SCP
scp_close(drv - floppy);
#endif
break;
case ADF_FDI:
#ifdef FDI2RAW
fdi2raw_header_free(drv->fdi);
drv->fdi = 0;
#endif
break;
}
drv->filetype = ADF_NONE;
zfile_fclose(drv->diskfile);
drv->diskfile = NULL;
zfile_fclose(drv->writediskfile);
drv->writediskfile = NULL;
}
static int drive_insert (drive * drv, struct uae_prefs *p, int dnum, const TCHAR *fname, bool fake, bool writeprotected);
static void reset_drive_gui(int num)
{
gui_data.drive_disabled[num] = 0;
gui_data.df[num][0] = 0;
gui_data.crc32[num] = 0;
if (currprefs.floppyslots[num].dfxtype < 0)
gui_data.drive_disabled[num] = 1;
}
static bool ispcbridgedrive(int num)
{
int type = currprefs.floppyslots[num].dfxtype;
return type == DRV_PC_525_ONLY_40 || type == DRV_PC_35_ONLY_80 || type == DRV_PC_525_40_80;
}
static void reset_drive(int num)
{
drive *drv = &floppy[num];
drive_image_free (drv);
drv->motoroff = 1;
drv->idbit = 0;
drv->drive_id = 0;
drv->drive_id_scnt = 0;
drv->lastdataacesstrack = -1;
disabled &= ~(1 << num);
if (currprefs.floppyslots[num].dfxtype < 0 || ispcbridgedrive(num))
disabled |= 1 << num;
reset_drive_gui(num);
/* most internal Amiga floppy drives won't enable
* diskready until motor is running at full speed
* and next indexsync has been passed
*/
drv->indexhackmode = 0;
if (num == 0 && currprefs.floppyslots[num].dfxtype == 0)
drv->indexhackmode = 1;
drv->dskchange_time = 0;
drv->dskchange = false;
drv->dskready_down_time = 0;
drv->dskready_up_time = 0;
drv->buffered_cyl = -1;
drv->buffered_side = -1;
gui_led (num + LED_DF0, 0, -1);
drive_settype_id (drv);
_tcscpy (currprefs.floppyslots[num].df, changed_prefs.floppyslots[num].df);
drv->newname[0] = 0;
drv->newnamewriteprotected = false;
if (!drive_insert (drv, &currprefs, num, currprefs.floppyslots[num].df, false, false))
disk_eject (num);
}
/* code for track display */
static void update_drive_gui (int num, bool force)
{
if (num >= currprefs.nr_floppies)
return;
drive *drv = floppy + num;
bool writ = dskdmaen == DSKDMA_WRITE && drv->state && !((selected | disabled) & (1 << num));
if (!force && drv->state == gui_data.drive_motor[num]
&& drv->cyl == gui_data.drive_track[num]
&& side == gui_data.drive_side
&& drv->crc32 == gui_data.crc32[num]
&& writ == gui_data.drive_writing[num]
&& !_tcscmp (gui_data.df[num], currprefs.floppyslots[num].df))
return;
_tcscpy (gui_data.df[num], currprefs.floppyslots[num].df);
gui_data.crc32[num] = drv->crc32;
gui_data.drive_motor[num] = drv->state;
gui_data.drive_track[num] = drv->cyl;
gui_data.drive_side = side;
gui_data.drive_writing[num] = writ;
gui_led (num + LED_DF0, (gui_data.drive_motor[num] ? 1 : 0) | (gui_data.drive_writing[num] ? 2 : 0), -1);
}
static void drive_fill_bigbuf (drive *drv, int);
int DISK_validate_filename (struct uae_prefs *p, const TCHAR *fname_in, TCHAR *outfname, int leave_open, bool *wrprot, uae_u32 *crc32, struct zfile **zf)
{
TCHAR outname[MAX_DPATH];
if (zf)
*zf = NULL;
if (crc32)
*crc32 = 0;
if (wrprot)
*wrprot = p->floppy_read_only ? 1 : 0;
cfgfile_resolve_path_out_load(fname_in, outname, MAX_DPATH, PATH_FLOPPY);
if (outfname)
_tcscpy(outfname, outname);
if (leave_open || !zf) {
struct zfile *f = zfile_fopen (outname, _T("r+b"), ZFD_NORMAL | ZFD_DISKHISTORY);
if (!f) {
if (wrprot)
*wrprot = 1;
f = zfile_fopen (outname, _T("rb"), ZFD_NORMAL | ZFD_DISKHISTORY);
}
if (f && crc32)
*crc32 = zfile_crc32 (f);
if (!zf)
zfile_fclose (f);
else
*zf = f;
return f ? 1 : 0;
} else {
if (zfile_exists (outname)) {
if (wrprot && !p->floppy_read_only)
*wrprot = 0;
if (crc32) {
struct zfile *f = zfile_fopen (outname, _T("rb"), ZFD_NORMAL | ZFD_DISKHISTORY);
if (f)
*crc32 = zfile_crc32 (f);
zfile_fclose (f);
}
return 1;
} else {
if (wrprot)
*wrprot = 1;
return 0;
}
}
}
static void updatemfmpos (drive *drv)
{
if (drv->prevtracklen) {
drv->mfmpos = drv->mfmpos * (drv->tracklen * 1000 / drv->prevtracklen) / 1000;
if (drv->mfmpos >= drv->tracklen)
drv->mfmpos = drv->tracklen - 1;
}
drv->mfmpos %= drv->tracklen;
drv->prevtracklen = drv->tracklen;
}
static void track_reset (drive *drv)
{
drv->tracklen = FLOPPY_WRITE_LEN * drv->ddhd * 2 * 8;
drv->revolutions = 1;
drv->trackspeed = get_floppy_speed ();
drv->buffered_side = -1;
drv->skipoffset = -1;
drv->tracktiming[0] = 0;
memset (drv->bigmfmbuf, 0xaa, FLOPPY_WRITE_LEN * 2 * drv->ddhd);
updatemfmpos (drv);
}
static int read_header_ext2 (struct zfile *diskfile, trackid *trackdata, int *num_tracks, int *ddhd)
{
uae_u8 buffer[2 + 2 + 4 + 4];
trackid *tid;
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++) {
tid = trackdata + i;
zfile_fread (buffer, 2 + 2 + 4 + 4, 1, diskfile);
tid->type = (image_tracktype)buffer[3];
tid->revolutions = buffer[2] + 1;
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 void saveimagecutpathpart(TCHAR *name)
{
int i;
i = _tcslen (name) - 1;
while (i > 0) {
if (name[i] == '/' || name[i] == '\\') {
name[i] = 0;
break;
}
if (name[i] == '.') {
name[i] = 0;
break;
}
i--;
}
while (i > 0) {
if (name[i] == '/' || name[i] == '\\') {
name[i] = 0;
break;
}
i--;
}
}
static void saveimagecutfilepart(TCHAR *name)
{
TCHAR tmp[MAX_DPATH];
int i;
_tcscpy(tmp, name);
i = _tcslen (tmp) - 1;
while (i > 0) {
if (tmp[i] == '/' || tmp[i] == '\\') {
_tcscpy(name, tmp + i + 1);
break;
}
if (tmp[i] == '.') {
tmp[i] = 0;
break;
}
i--;
}
while (i > 0) {
if (tmp[i] == '/' || tmp[i] == '\\') {
_tcscpy(name, tmp + i + 1);
break;
}
i--;
}
}
static void saveimageaddfilename(TCHAR *dst, const TCHAR *src, int type)
{
_tcscat(dst, src);
if (type)
_tcscat(dst, _T(".save_adf"));
else
_tcscat(dst, _T("_save.adf"));
}
static TCHAR *DISK_get_default_saveimagepath (const TCHAR *name)
{
TCHAR name1[MAX_DPATH];
TCHAR path[MAX_DPATH];
_tcscpy(name1, name);
saveimagecutfilepart(name1);
fetch_saveimagepath (path, sizeof path / sizeof (TCHAR), 1);
saveimageaddfilename(path, name1, 0);
return my_strdup(path);
}
// -2 = existing, if not, use 0.
// -1 = as configured
// 0 = saveimages-dir
// 1 = image dir
static TCHAR *DISK_get_saveimagepath(const TCHAR *name, int type)
{
int typev = type;
for (int i = 0; i < 2; i++) {
if (typev == 1 || (typev == -2 && i == 1)) {
TCHAR si_name[MAX_DPATH], si_path[MAX_DPATH];
_tcscpy(si_name, name);
_tcscpy(si_path, name);
saveimagecutfilepart(si_name);
saveimagecutpathpart(si_path);
_tcscat(si_path, FSDB_DIR_SEPARATOR_S);
saveimageaddfilename(si_path, si_name, 1);
if (typev != -2 || (typev == -2 && zfile_exists(si_path)))
return my_strdup(si_path);
}
if (typev == 2 || typev == -1 || typev == -2) {
TCHAR *p = DISK_get_default_saveimagepath(name);
if (typev != -2 || (typev == -2 && zfile_exists(p)))
return p;
xfree(p);
}
}
return DISK_get_saveimagepath(name, -1);
}
static struct zfile *getexistingwritefile(struct uae_prefs *p, const TCHAR *name, bool *wrprot)
{
struct zfile *zf = NULL;
TCHAR *path;
TCHAR outname[MAX_DPATH];
path = DISK_get_saveimagepath(name, 0);
DISK_validate_filename (p, path, outname, 1, wrprot, NULL, &zf);
xfree(path);
if (zf)
return zf;
path = DISK_get_saveimagepath(name, 1);
DISK_validate_filename (p, path, outname, 1, wrprot, NULL, &zf);
xfree(path);
return zf;
}
static int iswritefileempty (struct uae_prefs *p, const TCHAR *name)
{
struct zfile *zf;
bool wrprot;
uae_char buffer[8];
trackid td[MAX_TRACKS];
int tracks, ddhd, i, ret;
zf = getexistingwritefile (p, name, &wrprot);
if (!zf)
return 1;
zfile_fread (buffer, sizeof (char), 8, zf);
if (strncmp ((uae_char*)buffer, "UAE-1ADF", 8)) {
zfile_fclose(zf);
return 0;
}
ret = read_header_ext2 (zf, td, &tracks, &ddhd);
zfile_fclose (zf);
if (!ret)
return 1;
for (i = 0; i < tracks; i++) {
if (td[i].bitlen)
return 0;
}
return 1;
}
static int openwritefile (struct uae_prefs *p, drive *drv, int create)
{
bool wrprot = 0;
drv->writediskfile = getexistingwritefile(p, currprefs.floppyslots[drv - &floppy[0]].df, &wrprot);
if (drv->writediskfile) {
drv->wrprot = wrprot;
if (!read_header_ext2 (drv->writediskfile, drv->writetrackdata, &drv->write_num_tracks, 0)) {
zfile_fclose (drv->writediskfile);
drv->writediskfile = 0;
drv->wrprot = 1;
} else {
if (drv->write_num_tracks > drv->num_tracks)
drv->num_tracks = drv->write_num_tracks;
}
} else if (zfile_iscompressed (drv->diskfile)) {
drv->wrprot = 1;
}
return drv->writediskfile ? 1 : 0;
}
static bool diskfile_iswriteprotect (struct uae_prefs *p, const TCHAR *fname_in, int *needwritefile, drive_type *drvtype)
{
struct zfile *zf1, *zf2;
bool wrprot1 = 0, wrprot2 = 1;
uae_char buffer[25];
TCHAR outname[MAX_DPATH];
*needwritefile = 0;
*drvtype = DRV_35_DD;
DISK_validate_filename (p, fname_in, outname, 1, &wrprot1, NULL, &zf1);
if (!zf1)
return 1;
if (zfile_iscompressed(zf1)) {
wrprot1 = 1;
*needwritefile = 1;
}
zf2 = getexistingwritefile(p, fname_in, &wrprot2);
zfile_fclose(zf2);
zfile_fread(buffer, sizeof(char), 25, zf1);
zfile_fclose(zf1);
if (strncmp((uae_char*)buffer, "CAPS", 4) == 0) {
*needwritefile = 1;
return wrprot2;
}
if (strncmp((uae_char *)buffer, "Formatted Disk Image file", 25) == 0) {
*needwritefile = 1;
return wrprot2;
}
if (strncmp((uae_char *)buffer, "UAE-1ADF", 8) == 0) {
if (wrprot1)
return wrprot2;
return wrprot1;
}
if (strncmp((uae_char *)buffer, "UAE--ADF", 8) == 0) {
*needwritefile = 1;
return wrprot2;
}
if (memcmp(exeheader, buffer, sizeof exeheader) == 0)
return 0;
if (wrprot1)
return wrprot2;
return wrprot1;
}
static bool isrecognizedext (const TCHAR *name)
{
const TCHAR *ext = _tcsrchr (name, '.');
if (ext) {
ext++;
if (!_tcsicmp (ext, _T("adf")) || !_tcsicmp (ext, _T("adz")) || !_tcsicmp (ext, _T("st")) || !_tcsicmp (ext, _T("ima")) || !_tcsicmp (ext, _T("img")))
return true;
}
return false;
}
static int DISK_examine_image (struct uae_prefs *p, int num, struct diskinfo *di);
static int drive_insert(drive* drv, struct uae_prefs* p, int dnum, const TCHAR* fname_in, bool fake, bool forcedwriteprotect)
{
uae_u8 buffer[2 + 2 + 4 + 4];
trackid* tid;
int num_tracks, size;
int canauto;
TCHAR outname[MAX_DPATH];
drive_image_free(drv);
if (!fake)
DISK_examine_image(p, dnum, &disk_info_data);
DISK_validate_filename(p, fname_in, outname, 1, &drv->wrprot, &drv->crc32, &drv->diskfile);
drv->forcedwrprot = forcedwriteprotect;
if (drv->forcedwrprot)
drv->wrprot = true;
drv->ddhd = 1;
drv->num_heads = 2;
drv->num_secs = 0;
drv->hard_num_cyls = p->floppyslots[dnum].dfxtype == DRV_525_SD ? 40 : 80;
drv->tracktiming[0] = 0;
drv->useturbo = 0;
drv->indexoffset = 0;
if (!fake) {
drv->dskeject = false;
}
if (!drv->motoroff) {
drv->dskready_up_time = DSKREADY_UP_TIME * 312 + (uaerand() & 511);
drv->dskready_down_time = 0;
}
if (drv->diskfile == NULL) {
track_reset(drv);
return 0;
}
if (!fake) {
if (currprefs.floppyslots[dnum].df != fname_in) {
_tcsncpy(currprefs.floppyslots[dnum].df, fname_in, 255);
currprefs.floppyslots[dnum].df[255] = 0;
}
currprefs.floppyslots[dnum].forcedwriteprotect = forcedwriteprotect;
_tcsncpy(changed_prefs.floppyslots[dnum].df, fname_in, 255);
changed_prefs.floppyslots[dnum].df[255] = 0;
changed_prefs.floppyslots[dnum].forcedwriteprotect = forcedwriteprotect;
_tcscpy(drv->newname, fname_in);
drv->newnamewriteprotected = forcedwriteprotect;
gui_filename(dnum, outname);
}
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);
}
canauto = 0;
if (isrecognizedext(outname))
canauto = 1;
if (!canauto && drv->diskfile && isrecognizedext(zfile_getname(drv->diskfile)))
canauto = 1;
// if PC-only drive, make sure PC-like floppies are always detected
if (!canauto && ispcbridgedrive(dnum))
canauto = 1;
if (0) {
#ifdef CAPS
}
else if (strncmp((char*)buffer, "CAPS", 4) == 0) {
drv->wrprot = true;
if (!caps_loadimage(drv->diskfile, drv - floppy, &num_tracks)) {
zfile_fclose(drv->diskfile);
drv->diskfile = 0;
return 0;
}
drv->num_tracks = num_tracks;
drv->filetype = ADF_IPF;
#endif
#ifdef SCP
}
else if (strncmp((char*)buffer, "SCP", 3) == 0) {
drv->wrprot = true;
if (!scp_open(drv->diskfile, drv - floppy, &num_tracks)) {
zfile_fclose(drv->diskfile);
drv->diskfile = 0;
return 0;
}
drv->num_tracks = num_tracks;
drv->filetype = ADF_SCP;
#endif
#ifdef FDI2RAW
}
else if ((drv->fdi = fdi2raw_header(drv->diskfile))) {
drv->wrprot = true;
drv->num_tracks = fdi2raw_get_last_track(drv->fdi);
drv->num_secs = fdi2raw_get_num_sector(drv->fdi);
drv->filetype = ADF_FDI;
#endif
}
else if (strncmp((char*)buffer, "UAE-1ADF", 8) == 0) {
read_header_ext2(drv->diskfile, drv->trackdata, &drv->num_tracks, &drv->ddhd);
drv->filetype = ADF_EXT2;
drv->num_secs = 11;
if (drv->ddhd > 1)
drv->num_secs = 22;
}
else if (strncmp((char*)buffer, "UAE--ADF", 8) == 0) {
int offs = 160 * 4 + 8;
int i;
drv->wrprot = true;
drv->filetype = ADF_EXT1;
drv->num_tracks = 160;
drv->num_secs = 11;
zfile_fseek(drv->diskfile, 8, SEEK_SET);
for (i = 0; i < 160; i++) {
tid = &drv->trackdata[i];
zfile_fread(buffer, 4, 1, drv->diskfile);
tid->sync = buffer[0] * 256 + buffer[1];
tid->len = buffer[2] * 256 + buffer[3];
tid->offs = offs;
tid->revolutions = 1;
if (tid->sync == 0) {
tid->type = TRACK_AMIGADOS;
tid->bitlen = 0;
}
else {
tid->type = TRACK_RAW1;
tid->bitlen = tid->len * 8;
}
offs += tid->len;
}
} else if (memcmp (exeheader, buffer, sizeof exeheader) == 0) {
int i;
struct zfile *z = zfile_fopen_empty (NULL, _T(""), 512 * 1760);
createimagefromexe (drv->diskfile, z);
drv->filetype = ADF_NORMAL;
zfile_fclose (drv->diskfile);
drv->diskfile = z;
drv->num_tracks = 160;
drv->num_secs = 11;
for (i = 0; i < drv->num_tracks; i++) {
tid = &drv->trackdata[i];
tid->type = TRACK_AMIGADOS;
tid->len = 512 * drv->num_secs;
tid->bitlen = 0;
tid->offs = i * 512 * drv->num_secs;
tid->revolutions = 1;
}
drv->useturbo = 1;
} else if (canauto && (
// 320k double sided
size == 8 * 40 * 2 * 512 ||
// 320k single sided
size == 8 * 40 * 1 * 512 ||
// 360k double sided
size == 9 * 40 * 2 * 512 ||
// 360k single sided
size == 9 * 40 * 1 * 512 ||
// 1.2M double sided
size == 15 * 80 * 2 * 512 ||
// 720k/1440k double sided
size == 9 * 80 * 2 * 512 || size == 18 * 80 * 2 * 512 || size == 10 * 80 * 2 * 512 || size == 20 * 80 * 2 * 512 || size == 21 * 80 * 2 * 512 ||
size == 9 * 81 * 2 * 512 || size == 18 * 81 * 2 * 512 || size == 10 * 81 * 2 * 512 || size == 20 * 81 * 2 * 512 || size == 21 * 81 * 2 * 512 ||
size == 9 * 82 * 2 * 512 || size == 18 * 82 * 2 * 512 || size == 10 * 82 * 2 * 512 || size == 20 * 82 * 2 * 512 || size == 21 * 82 * 2 * 512 ||
// 720k/1440k single sided
size == 9 * 80 * 1 * 512 || size == 18 * 80 * 1 * 512 || size == 10 * 80 * 1 * 512 || size == 20 * 80 * 1 * 512 ||
size == 9 * 81 * 1 * 512 || size == 18 * 81 * 1 * 512 || size == 10 * 81 * 1 * 512 || size == 20 * 81 * 1 * 512 ||
size == 9 * 82 * 1 * 512 || size == 18 * 82 * 1 * 512 || size == 10 * 82 * 1 * 512 || size == 20 * 82 * 1 * 512)) {
/* PC formatted image */
int side, sd;
int dfxtype = p->floppyslots[dnum].dfxtype;
drv->num_secs = 9;
drv->ddhd = 1;
sd = 0;
bool can40 = dfxtype == DRV_525_DD || dfxtype == DRV_PC_525_ONLY_40 || dfxtype == DRV_PC_525_40_80;
bool can80 = dfxtype == DRV_35_HD || dfxtype == DRV_PC_35_ONLY_80 || dfxtype == DRV_PC_525_40_80;
bool drv525 = dfxtype == DRV_525_DD || dfxtype == DRV_PC_525_ONLY_40 || dfxtype == DRV_PC_525_40_80;
for (side = 2; side > 0; side--) {
if (drv->hard_num_cyls >= 80 && can80) {
if ( size == 9 * 80 * side * 512 || size == 9 * 81 * side * 512 || size == 9 * 82 * side * 512) {
drv->num_secs = 9;
drv->ddhd = 1;
break;
} else if (!drv525 && (size == 18 * 80 * side * 512 || size == 18 * 81 * side * 512 || size == 18 * 82 * side * 512)) {
drv->num_secs = 18;
drv->ddhd = 2;
break;
} else if (!drv525 && (size == 10 * 80 * side * 512 || size == 10 * 81 * side * 512 || size == 10 * 82 * side * 512)) {
drv->num_secs = 10;
drv->ddhd = 1;
break;
} else if (!drv525 && (size == 20 * 80 * side * 512 || size == 20 * 81 * side * 512 || size == 20 * 82 * side * 512)) {
drv->num_secs = 20;
drv->ddhd = 2;
break;
} else if (!drv525 && (size == 21 * 80 * side * 512 || size == 21 * 81 * side * 512 || size == 21 * 82 * side * 512)) {
drv->num_secs = 21;
drv->ddhd = 2;
break;
} else if (size == 15 * 80 * side * 512) {
drv->num_secs = 15;
drv->ddhd = 1;
break;
}
}
if (drv->hard_num_cyls == 40 || can40) {
if (size == 9 * 40 * side * 512) {
drv->num_secs = 9;
drv->ddhd = 1;
sd = 1;
break;
} else if (size == 8 * 40 * side * 512) {
drv->num_secs = 8;
drv->ddhd = 1;
sd = 1;
break;
}
}
}
drv->num_tracks = size / (drv->num_secs * 512);
// SD disk in 5.25 drive = duplicate each track
if (sd && p->floppyslots[dnum].dfxtype == DRV_525_DD) {
drv->num_tracks *= 2;
} else {
sd = 0;
}
drv->filetype = ADF_PCDOS;
tid = &drv->trackdata[0];
for (int i = 0; i < drv->num_tracks; i++) {
tid->type = TRACK_PCDOS;
tid->len = 512 * drv->num_secs;
tid->bitlen = 0;
tid->offs = (sd ? i / 2 : i) * 512 * drv->num_secs;
if (side == 1) {
tid++;
tid->type = TRACK_NONE;
tid->len = 512 * drv->num_secs;
}
tid->revolutions = 1;
tid++;
}
drv->num_heads = side;
if (side == 1)
drv->num_tracks *= 2;
} else if ((size == 262144 || size == 524288) && buffer[0] == 0x11 && (buffer[1] == 0x11 || buffer[1] == 0x14)) {
// 256k -> KICK disk, 512k -> SuperKickstart disk
drv->filetype = size == 262144 ? ADF_KICK : ADF_SKICK;
drv->num_tracks = 1760 / (drv->num_secs = 11);
for (int i = 0; i < drv->num_tracks; i++) {
tid = &drv->trackdata[i];
tid->type = TRACK_AMIGADOS;
tid->len = 512 * drv->num_secs;
tid->bitlen = 0;
tid->offs = i * 512 * drv->num_secs - (drv->filetype == ADF_KICK ? 512 : 262144 + 1024);
tid->track = i;
tid->revolutions = 1;
}
} else {
int ds;
ds = 0;
drv->filetype = ADF_NORMAL;
/* High-density or diskspare disk? */
drv->num_tracks = 0;
if (size > 160 * 11 * 512 + 511) { // larger than standard adf?
for (int i = 80; i <= 83; i++) {
if (size == i * 22 * 512 * 2) { // HD
drv->ddhd = 2;
drv->num_tracks = size / (512 * (drv->num_secs = 22));
break;
}
if (size == i * 11 * 512 * 2) { // >80 cyl DD
drv->num_tracks = size / (512 * (drv->num_secs = 11));
break;
}
if (size == i * 12 * 512 * 2) { // ds 12 sectors
drv->num_tracks = size / (512 * (drv->num_secs = 12));
ds = 1;
break;
}
if (size == i * 24 * 512 * 2) { // ds 24 sectors
drv->num_tracks = size / (512 * (drv->num_secs = 24));
drv->ddhd = 2;
ds = 1;
break;
}
}
if (drv->num_tracks == 0) {
drv->num_tracks = size / (512 * (drv->num_secs = 22));
drv->ddhd = 2;
}
} else {
drv->num_tracks = size / (512 * (drv->num_secs = 11));
}
if (!ds &&drv->num_tracks > MAX_TRACKS)
write_log (_T("Your diskfile is too big, %d bytes!\n"), size);
for (int i = 0; i < drv->num_tracks; i++) {
tid = &drv->trackdata[i];
tid->type = ds ? TRACK_DISKSPARE : TRACK_AMIGADOS;
tid->len = 512 * drv->num_secs;
tid->bitlen = 0;
tid->offs = i * 512 * drv->num_secs;
tid->revolutions = 1;
}
}
openwritefile (p, drv, 0);
drive_settype_id (drv); /* Set DD or HD drive */
drive_fill_bigbuf (drv, 1);
drv->mfmpos = uaerand ();
drv->mfmpos |= (uaerand () << 16);
drv->mfmpos %= drv->tracklen;
drv->prevtracklen = 0;
if (!fake) {
update_drive_gui (drv - floppy, false);
}
return 1;
}
static void rand_shifter (drive *drv)
{
int r = ((uaerand () >> 4) & 7) + 1;
while (r-- > 0) {
word <<= 1;
word |= (uaerand () & 0x1000) ? 1 : 0;
bitoffset++;
bitoffset &= 15;
}
}
static void set_steplimit (drive *drv)
{
// emulate step limit only if cycle-exact or approximate CPU speed
if (currprefs.m68k_speed != 0)
return;
drv->steplimit = 4;
drv->steplimitcycle = get_cycles ();
}
static int drive_empty (drive * drv)
{
return drv->diskfile == 0 && drv->dskchange_time >= 0;
}
static void drive_step (drive * drv, int step_direction)
{
if (!drive_empty (drv))
drv->dskchange = 0;
if (drv->steplimit && get_cycles() - drv->steplimitcycle < MIN_STEPLIMIT_CYCLE) {
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)
*/
set_steplimit (drv);
if (step_direction) {
if (drv->cyl) {
drv->cyl--;
}
} else {
int maxtrack = drv->hard_num_cyls;
if (drv->cyl < maxtrack + 3) {
drv->cyl++;
}
if (drv->cyl >= maxtrack)
write_log (_T("program tried to step over track %d\n"), maxtrack);
}
rand_shifter (drv);
}
static int drive_track0 (drive * drv)
{
return drv->cyl == 0;
}
static int drive_writeprotected (drive * drv)
{
return currprefs.floppy_read_only || drv->wrprot || drv->forcedwrprot || drv->diskfile == NULL;
}
static int drive_running (drive * drv)
{
return !drv->motoroff;
}
static void motordelay_func (uae_u32 v)
{
floppy[v].motordelay = 0;
}
static void drive_motor (drive * drv, bool off)
{
if (drv->motoroff && !off) {
drv->dskready_up_time = DSKREADY_UP_TIME * 312 + (uaerand() & 511);
rand_shifter (drv);
}
if (!drv->motoroff && off) {
drv->drive_id_scnt = 0; /* Reset id shift reg counter */
drv->dskready_down_time = DSKREADY_DOWN_TIME * 312 + (uaerand() & 511);
if (currprefs.cpu_model <= 68010 && currprefs.m68k_speed == 0) {
drv->motordelay = 1;
event2_newevent2 (30, drv - floppy, motordelay_func);
}
}
drv->motoroff = off;
if (drv->motoroff) {
drv->dskready = 0;
drv->dskready_up_time = 0;
} else {
drv->dskready_down_time = 0;
}
}
static void read_floppy_data (struct zfile *diskfile, int type, trackid *tid, int offset, uae_u8 *dst, int len)
{
if (len == 0)
return;
if (tid->track == 0) {
if (type == ADF_KICK) {
memset (dst, 0, len > 512 ? 512 : len);
if (offset == 0) {
memcpy (dst, "KICK", 4);
len -= 512;
}
} else if (type == ADF_SKICK) {
memset (dst, 0, len > 512 ? 512 : len);
if (offset == 0) {
memcpy (dst, "KICKSUP0", 8);
len -= 1024;
} else if (offset == 512) {
len -= 512;
}
}
}
int off = tid->offs + offset;
if (off >= 0 && len > 0) {
zfile_fseek (diskfile, off, SEEK_SET);
zfile_fread (dst, 1, len, diskfile);
}
}
/* Megalomania does not like zero MFM words... */
static void mfmcode (uae_u16 * mfm, int words)
{
uae_u32 lastword = 0;
while (words--) {
uae_u32 v = (*mfm) & 0x55555555;
uae_u32 lv = (lastword << 16) | v;
uae_u32 nlv = 0x55555555 & ~lv;
uae_u32 mfmbits = (nlv << 1) & (nlv >> 1);
*mfm++ = v | mfmbits;
lastword = v;
}
}
static const 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, len;
int tr = drv->cyl * 2 + side;
uae_u16 *dstmfmbuf, *mfm2;
uae_u8 secbuf[1000];
uae_u16 crc16;
trackid *ti = drv->trackdata + tr;
int tracklen = 12500;
mfm2 = drv->bigmfmbuf;
*mfm2++ = 0x9254;
memset (secbuf, 0x4e, 40);
memset (secbuf + 40, 0x00, 12);
secbuf[52] = 0xc2;
secbuf[53] = 0xc2;
secbuf[54] = 0xc2;
secbuf[55] = 0xfc;
memset (secbuf + 56, 0x4e, 40);
dstmfmbuf = mfmcoder (secbuf, mfm2, 96);
mfm2[52] = 0x5224;
mfm2[53] = 0x5224;
mfm2[54] = 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, drv->filetype, ti, i * 512, &secbuf[60], 512);
crc16 = get_crc16(secbuf + 56, 3 + 1 + 512);
secbuf[60 + 512] = crc16 >> 8;
secbuf[61 + 512] = crc16 & 0xff;
len = (tracklen / 2 - 96) / drv->num_secs - 574 / drv->ddhd;
if (len > 0)
memset(secbuf + 512 + 62, 0x4e, len);
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;
}
while (dstmfmbuf - drv->bigmfmbuf < tracklen / 2)
*dstmfmbuf++ = 0x9254;
drv->skipoffset = 0;
drv->tracklen = (dstmfmbuf - drv->bigmfmbuf) * 16;
}
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;
int prevbit;
trackid *ti = drv->trackdata + tr;
memset (dstmfmbuf, 0xaa, len * 2);
dstmfmoffset += FLOPPY_GAP_LEN;
drv->skipoffset = (FLOPPY_GAP_LEN * 8) / 3 * 2;
drv->tracklen = len * 2 * 8;
prevbit = 0;
for (sec = 0; sec < drv->num_secs; sec++) {
uae_u8 secbuf[544];
uae_u16 mfmbuf[544 + 1];
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, drv->filetype, ti, sec * 512, &secbuf[32], 512);
mfmbuf[0] = prevbit ? 0x2aaa : 0xaaaa;
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;
mfmbuf[544] = 0;
mfmcode (mfmbuf + 4, 544 - 4 + 1);
for (i = 0; i < 544; i++) {
dstmfmbuf[dstmfmoffset % len] = mfmbuf[i];
dstmfmoffset++;
}
prevbit = mfmbuf[i - 1] & 1;
// so that final word has correct MFM encoding
dstmfmbuf[dstmfmoffset % len] = mfmbuf[i];
}
}
/*
* diskspare format
*
* 0 <4489> <4489> 0 track sector crchi, crclo, data[512] (520 bytes per sector)
*
* 0xAAAA 0x4489 0x4489 0x2AAA oddhi, oddlo, evenhi, evenlo, ...
*
* NOTE: data is MFM encoded using same method as ADOS header, not like ADOS data!
*
*/
static void decode_diskspare (drive *drv)
{
int tr = drv->cyl * 2 + side;
int sec;
int dstmfmoffset = 0;
int size = 512 + 8;
uae_u16 *dstmfmbuf = drv->bigmfmbuf;
int len = drv->num_secs * size + FLOPPY_GAP_LEN;
trackid *ti = drv->trackdata + tr;
memset (dstmfmbuf, 0xaa, len * 2);
dstmfmoffset += FLOPPY_GAP_LEN;
drv->skipoffset = (FLOPPY_GAP_LEN * 8) / 3 * 2;
drv->tracklen = len * 2 * 8;
for (sec = 0; sec < drv->num_secs; sec++) {
uae_u8 secbuf[512 + 8];
uae_u16 mfmbuf[512 + 8];
int i;
uae_u32 deven, dodd;
uae_u16 chk;
secbuf[0] = tr;
secbuf[1] = sec;
secbuf[2] = 0;
secbuf[3] = 0;
read_floppy_data (drv->diskfile, drv->filetype, ti, sec * 512, &secbuf[4], 512);
mfmbuf[0] = 0xaaaa;
mfmbuf[1] = 0x4489;
mfmbuf[2] = 0x4489;
mfmbuf[3] = 0x2aaa;
for (i = 0; i < 512; i += 4) {
deven = ((secbuf[i + 4] << 24) | (secbuf[i + 5] << 16)
| (secbuf[i + 6] << 8) | (secbuf[i + 7]));
dodd = deven >> 1;
deven &= 0x55555555;
dodd &= 0x55555555;
mfmbuf[i + 8 + 0] = dodd >> 16;
mfmbuf[i + 8 + 1] = dodd;
mfmbuf[i + 8 + 2] = deven >> 16;
mfmbuf[i + 8 + 3] = deven;
}
mfmcode (mfmbuf + 8, 512);
i = 8;
chk = mfmbuf[i++] & 0x7fff;
while (i < 512 + 8)
chk ^= mfmbuf[i++];
secbuf[2] = chk >> 8;
secbuf[3] = chk;
deven = ((secbuf[0] << 24) | (secbuf[1] << 16)
| (secbuf[2] << 8) | (secbuf[3]));
dodd = deven >> 1;
deven &= 0x55555555;
dodd &= 0x55555555;
mfmbuf[4] = dodd >> 16;
mfmbuf[5] = dodd;
mfmbuf[6] = deven >> 16;
mfmbuf[7] = deven;
mfmcode (mfmbuf + 4, 4);
for (i = 0; i < 512 + 8; i++) {
dstmfmbuf[dstmfmoffset % len] = mfmbuf[i];
dstmfmoffset++;
}
}
}
static void drive_fill_bigbuf(drive * drv, int force)
{
int tr = drv->cyl * 2 + side;
trackid *ti = drv->trackdata + tr;
bool retrytrack;
int rev = -1;
if ((!drv->diskfile) || tr >= drv->num_tracks) {
track_reset(drv);
return;
}
if (!force && drv->buffered_cyl == drv->cyl && drv->buffered_side == side)
return;
drv->indexoffset = 0;
drv->multi_revolution = 0;
drv->tracktiming[0] = 0;
drv->skipoffset = -1;
drv->revolutions = 1;
retrytrack = drv->lastdataacesstrack == drv->cyl * 2 + side;
if (!dskdmaen && !retrytrack)
drv->track_access_done = false;
if (drv->writediskfile && drv->writetrackdata[tr].bitlen > 0) {
int i;
trackid *wti = &drv->writetrackdata[tr];
drv->tracklen = wti->bitlen;
drv->revolutions = wti->revolutions;
read_floppy_data(drv->writediskfile, drv->filetype, wti, 0, (uae_u8*)drv->bigmfmbuf, (wti->bitlen + 7) / 8);
for (i = 0; i < (drv->tracklen + 15) / 16; i++) {
uae_u16 *mfm = drv->bigmfmbuf + i;
uae_u8 *data = (uae_u8 *)mfm;
*mfm = 256 * *data + *(data + 1);
}
} else if (drv->filetype == ADF_IPF) {
#ifdef CAPS
caps_loadtrack(drv->bigmfmbuf, drv->tracktiming, drv - floppy, tr, &drv->tracklen, &drv->multi_revolution, &drv->skipoffset, &drv->lastrev, retrytrack);
#endif
} else if (drv->filetype == ADF_SCP) {
#ifdef SCP
scp_loadtrack(drv->bigmfmbuf, drv->tracktiming, drv - floppy, tr, &drv->tracklen, &drv->multi_revolution, &drv->skipoffset, &drv->lastrev, retrytrack);
#endif
} else if (drv->filetype == ADF_FDI) {
#ifdef FDI2RAW
fdi2raw_loadtrack(drv->fdi, drv->bigmfmbuf, drv->tracktiming, tr, &drv->tracklen, &drv->indexoffset, &drv->multi_revolution, 1);
#endif
}
else if (ti->type == TRACK_PCDOS) {
decode_pcdos(drv);
}
else if (ti->type == TRACK_AMIGADOS) {
decode_amigados(drv);
}
else if (ti->type == TRACK_DISKSPARE) {
decode_diskspare(drv);
}
else if (ti->type == TRACK_NONE) {
;
}
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->diskfile, drv->filetype, ti, 0, (uae_u8*)(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;
*mfm = 256 * *data + *(data + 1);
}
}
drv->buffered_side = side;
drv->buffered_cyl = drv->cyl;
if (drv->tracklen == 0) {
drv->tracklen = FLOPPY_WRITE_LEN * drv->ddhd * 2 * 8;
memset(drv->bigmfmbuf, 0, FLOPPY_WRITE_LEN * 2 * drv->ddhd);
}
drv->trackspeed = get_floppy_speed_from_image (drv);
updatemfmpos(drv);
}
/* Update ADF_EXT2 track header */
static void diskfile_update (struct zfile *diskfile, trackid *ti, int len, image_tracktype type)
{
uae_u8 buf[2 + 2 + 4 + 4], *zerobuf;
ti->revolutions = 1;
ti->bitlen = len;
zfile_fseek (diskfile, 8 + 4 + (2 + 2 + 4 + 4) * ti->track, SEEK_SET);
memset (buf, 0, sizeof buf);
ti->type = type;
buf[2] = 0;
buf[3] = ti->type;
do_put_mem_long ((uae_u32 *) (buf + 4), ti->len);
do_put_mem_long ((uae_u32 *) (buf + 8), ti->bitlen);
zfile_fwrite (buf, sizeof buf, 1, diskfile);
if (ti->len > (len + 7) / 8) {
zerobuf = xmalloc (uae_u8, ti->len);
memset (zerobuf, 0, ti->len);
zfile_fseek (diskfile, ti->offs, SEEK_SET);
zfile_fwrite (zerobuf, 1, ti->len, diskfile);
free (zerobuf);
}
}
#define MFMMASK 0x55555555
static uae_u16 getmfmword (uae_u16 *mbuf, int shift)
{
return (mbuf[0] << shift) | (mbuf[1] >> (16 - shift));
}
static uae_u32 getmfmlong (uae_u16 *mbuf, int shift)
{
return ((getmfmword (mbuf, shift) << 16) | getmfmword (mbuf + 1, shift)) & MFMMASK;
}
static int decode_buffer (uae_u16 *mbuf, int cyl, int drvsec, int ddhd, int filetype, int *drvsecp, int *sectable, int checkmode)
{
int i, secwritten = 0;
int fwlen = FLOPPY_WRITE_LEN * ddhd;
int length = 2 * fwlen;
uae_u32 odd, even, chksum, id, dlong;
uae_u8 *secdata;
uae_u8 secbuf[544];
uae_u16 *mend = mbuf + length, *mstart;
uae_u32 sechead[4];
int shift = 0;
bool issechead;
memset (sectable, 0, MAX_SECTORS * sizeof (int));
mstart = mbuf;
memcpy (mbuf + fwlen, mbuf, fwlen * sizeof (uae_u16));
mend -= (4 + 16 + 8 + 512);
while (secwritten < drvsec) {
int trackoffs;
while (getmfmword (mbuf, shift) != 0x4489) {
if (mbuf >= mend)
return 1;
shift++;
if (shift == 16) {
shift = 0;
mbuf++;
}
}
while (getmfmword (mbuf, shift) == 0x4489) {
if (mbuf >= mend)
return 1;
mbuf++;
}
odd = getmfmlong (mbuf, shift);
even = getmfmlong (mbuf + 2, shift);
mbuf += 4;
id = (odd << 1) | even;
trackoffs = (id & 0xff00) >> 8;
if (trackoffs + 1 > drvsec) {
write_log (_T("Disk decode: weird sector number %d (%08x, %ld)\n"), trackoffs, id, mbuf - mstart);
if (filetype == ADF_EXT2)
return 2;
continue;
}
issechead = false;
chksum = odd ^ even;
for (i = 0; i < 4; i++) {
odd = getmfmlong (mbuf, shift);
even = getmfmlong (mbuf + 8, shift);
mbuf += 2;
dlong = (odd << 1) | even;
if (dlong && !checkmode) {
issechead = true;
}
sechead[i] = dlong;
chksum ^= odd ^ even;
}
if (issechead) {
if (filetype == ADF_EXT2)
return 6;
}
mbuf += 8;
odd = getmfmlong (mbuf, shift);
even = getmfmlong (mbuf + 2, shift);
mbuf += 4;
if (((odd << 1) | even) != chksum) {
write_log (_T("Disk decode: checksum error on sector %d header\n"), trackoffs);
if (filetype == ADF_EXT2)
return 3;
continue;
}
if (((id & 0x00ff0000) >> 16) != cyl * 2 + side) {
write_log (_T("Disk decode: mismatched track (%d <> %d) on sector %d header (%08X)\n"), (id & 0x00ff0000) >> 16, cyl * 2 + side, trackoffs, id);
if (filetype == ADF_EXT2)
return 3;
continue;
}
odd = getmfmlong (mbuf, shift);
even = getmfmlong (mbuf + 2, shift);
mbuf += 4;
chksum = (odd << 1) | even;
secdata = secbuf + 32;
for (i = 0; i < 128; i++) {
odd = getmfmlong (mbuf, shift);
even = getmfmlong (mbuf + 256, shift);
mbuf += 2;
dlong = (odd << 1) | even;
*secdata++ = dlong >> 24;
*secdata++ = dlong >> 16;
*secdata++ = dlong >> 8;
*secdata++ = dlong;
chksum ^= odd ^ even;
}
if (chksum) {
write_log (_T("Disk decode: sector %d, data checksum error\n"), trackoffs);
if (filetype == ADF_EXT2)
return 4;
continue;
}
mbuf += 256;
sectable[trackoffs] = 1;
secwritten++;
memcpy (writebuffer + trackoffs * 512, secbuf + 32, 512);
}
if (filetype == ADF_EXT2 && (secwritten == 0 || secwritten < 0))
return 5;
if (secwritten == 0)
write_log (_T("Disk decode: unsupported format\n"));
if (secwritten < 0)
write_log (_T("Disk decode: sector labels ignored\n"));
*drvsecp = drvsec;
return 0;
}
static uae_u8 mfmdecode(uae_u16 **mfmp, int shift)
{
uae_u16 mfm = getmfmword (*mfmp, shift);
uae_u8 out = 0;
int i;
(*mfmp)++;
mfm &= 0x5555;
for (i = 0; i < 8; i++) {
out >>= 1;
if (mfm & 1)
out |= 0x80;
mfm >>= 2;
}
return out;
}
static int drive_write_pcdos (drive *drv, struct zfile *zf, bool count)
{
int i;
int drvsec = drv->num_secs;
int fwlen = FLOPPY_WRITE_LEN * drv->ddhd;
int length = 2 * fwlen;
uae_u16 *mbuf = drv->bigmfmbuf;
uae_u16 *mend = mbuf + length;
int secwritten = 0, seccnt = 0;
int shift = 0, sector = -1;
int sectable[24];
uae_u8 secbuf[3 + 1 + 512];
uae_u8 mark;
uae_u16 crc;
memset (sectable, 0, sizeof sectable);
memcpy (mbuf + fwlen, mbuf, fwlen * sizeof (uae_u16));
mend -= 518;
secbuf[0] = secbuf[1] = secbuf[2] = 0xa1;
secbuf[3] = 0xfb;
while (seccnt < drvsec) {
int mfmcount;
mfmcount = 0;
while (getmfmword (mbuf, shift) != 0x4489) {
mfmcount++;
if (mbuf >= mend)
return -1;
shift++;
if (shift == 16) {
shift = 0;
mbuf++;
}
if (sector >= 0 && mfmcount / 16 >= 43)
sector = -1;
}
mfmcount = 0;
while (getmfmword (mbuf, shift) == 0x4489) {
mfmcount++;
if (mbuf >= mend)
return -1;
mbuf++;
}
if (mfmcount < 3) // ignore if less than 3 sync markers
continue;
mark = mfmdecode(&mbuf, shift);
if (mark == 0xfe) {
uae_u8 tmp[8];
uae_u8 cyl, head, size;
cyl = mfmdecode(&mbuf, shift);
head = mfmdecode(&mbuf, shift);
sector = mfmdecode(&mbuf, shift);
size = mfmdecode(&mbuf, shift);
crc = (mfmdecode(&mbuf, shift) << 8) | mfmdecode(&mbuf, shift);
tmp[0] = 0xa1; tmp[1] = 0xa1; tmp[2] = 0xa1; tmp[3] = mark;
tmp[4] = cyl; tmp[5] = head; tmp[6] = sector; tmp[7] = size;
// skip 28 bytes
for (i = 0; i < 28; i++)
mfmdecode (&mbuf, shift);
if (get_crc16 (tmp, 8) != crc || cyl != drv->cyl || head != side || size != 2 || sector < 1 || sector > drv->num_secs || sector >= sizeof sectable) {
write_log (_T("PCDOS: track %d, corrupted sector header\n"), drv->cyl * 2 + side);
return -1;
}
sector--;
continue;
}
if (mark != 0xfb && mark != 0xfa) {
write_log (_T("PCDOS: track %d: unknown address mark %02X\n"), drv->cyl * 2 + side, mark);
continue;
}
if (sector < 0)
continue;
for (i = 0; i < 512; i++)
secbuf[i + 4] = mfmdecode(&mbuf, shift);
crc = (mfmdecode(&mbuf, shift) << 8) | mfmdecode(&mbuf, shift);
if (get_crc16(secbuf, 3 + 1 + 512) != crc) {
write_log (_T("PCDOS: track %d, sector %d data checksum error\n"),
drv->cyl * 2 + side, sector + 1);
continue;
}
seccnt++;
if (count && sectable[sector])
break;
if (!sectable[sector]) {
secwritten++;
sectable[sector] = 1;
zfile_fseek (zf, drv->trackdata[drv->cyl * 2 + side].offs + sector * 512, SEEK_SET);
zfile_fwrite (secbuf + 4, sizeof (uae_u8), 512, zf);
}
sector = -1;
}
if (!count && secwritten != drv->num_secs)
write_log (_T("PCDOS: track %d, %d corrupted sectors ignored\n"),
drv->cyl * 2 + side, drv->num_secs - secwritten);
return secwritten;
}
static int drive_write_adf_amigados (drive * drv)
{
int drvsec, i;
int sectable[MAX_SECTORS];
if (decode_buffer (drv->bigmfmbuf, drv->cyl, drv->num_secs, drv->ddhd, drv->filetype, &drvsec, sectable, 0))
return 2;
if (!drvsec)
return 2;
if (drv->filetype == ADF_EXT2)
diskfile_update (drv->diskfile, &drv->trackdata[drv->cyl * 2 + side], 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;
}
/* write raw track to disk file */
static int drive_write_ext2 (uae_u16 *bigmfmbuf, struct zfile *diskfile, trackid *ti, int tracklen)
{
int len, i, offset;
offset = 0;
len = (tracklen + 7) / 8;
if (len > ti->len) {
write_log (_T("disk raw write: image file's track %d is too small (%d < %d)!\n"), ti->track, ti->len, len);
offset = (len - ti->len) / 2;
len = ti->len;
}
diskfile_update (diskfile, ti, tracklen, TRACK_RAW);
for (i = 0; i < ti->len / 2; i++) {
uae_u16 *mfm = bigmfmbuf + i + offset;
uae_u16 *mfmw = bigmfmbufw + i + offset;
uae_u8 *data = (uae_u8 *) mfm;
*mfmw = 256 * *data + *(data + 1);
}
zfile_fseek (diskfile, ti->offs, SEEK_SET);
zfile_fwrite (bigmfmbufw, 1, len, diskfile);
return 1;
}
static void drive_write_data (drive * drv);
static bool convert_adf_to_ext2 (drive *drv, int mode)
{
TCHAR name[MAX_DPATH];
bool hd = drv->ddhd == 2;
struct zfile *f;
if (drv->filetype != ADF_NORMAL)
return false;
_tcscpy (name, currprefs.floppyslots[drv - floppy].df);
if (!name[0])
return false;
if (mode == 1) {
TCHAR *p = _tcsrchr (name, '.');
if (!p)
p = name + _tcslen (name);
_tcscpy (p, _T(".extended.adf"));
if (!disk_creatediskfile (&currprefs, name, 1, hd ? DRV_35_HD : DRV_35_DD, -1, NULL, false, false, drv->diskfile))
return false;
} else if (mode == 2) {
struct zfile *tmp = zfile_fopen_load_zfile (drv->diskfile);
if (!tmp)
return false;
zfile_fclose (drv->diskfile);
drv->diskfile = NULL;
if (!disk_creatediskfile (&currprefs, name, 1, hd ? DRV_35_HD : DRV_35_DD, -1, NULL, false, false, tmp)) {
zfile_fclose (tmp);
return false;
}
zfile_fclose(tmp);
} else {
return false;
}
f = zfile_fopen (name, _T("r+b"));
if (!f)
return false;
_tcscpy (currprefs.floppyslots[drv - floppy].df, name);
_tcscpy (changed_prefs.floppyslots[drv - floppy].df, name);
zfile_fclose (drv->diskfile);
drv->diskfile = f;
drv->filetype = ADF_EXT2;
read_header_ext2 (drv->diskfile, drv->trackdata, &drv->num_tracks, &drv->ddhd);
drive_write_data (drv);
drive_fill_bigbuf (drv, 1);
return true;
}
static void drive_write_data (drive * drv)
{
int ret = -1;
int tr = drv->cyl * 2 + side;
if (drive_writeprotected (drv) || drv->trackdata[tr].type == TRACK_NONE) {
/* read original track back because we didn't really write anything */
drv->buffered_side = 2;
return;
}
if (drv->writediskfile) {
drive_write_ext2 (drv->bigmfmbuf, drv->writediskfile, &drv->writetrackdata[tr],
drv->tracklen);
}
switch (drv->filetype) {
case ADF_NORMAL:
if (drv->ddhd > 1 && currprefs.floppyslots[drv - &floppy[0]].dfxtype != DRV_35_HD) {
// HD image in DD drive: ignore writing.
drv->buffered_side = 2;
} else {
if (drive_write_adf_amigados (drv)) {
if (currprefs.floppy_auto_ext2) {
convert_adf_to_ext2 (drv, currprefs.floppy_auto_ext2);
} else {
static int warned;
if (!warned)
notify_user (NUMSG_NEEDEXT2);
warned = 1;
}
}
}
return;
case ADF_EXT1:
break;
case ADF_EXT2:
ret = drive_write_adf_amigados (drv);
if (ret) {
write_log (_T("not an amigados track %d (error %d), writing as raw track\n"), drv->cyl * 2 + side, ret);
drive_write_ext2 (drv->bigmfmbuf, drv->diskfile, &drv->trackdata[drv->cyl * 2 + side],
drv->tracklen);
}
return;
case ADF_IPF:
break;
case ADF_PCDOS:
ret = drive_write_pcdos (drv, drv->diskfile, 0);
if (ret < 0)
write_log (_T("not a PC formatted track %d (error %d)\n"), drv->cyl * 2 + side, ret);
break;
}
drv->tracktiming[0] = 0;
}
static void drive_eject (drive * drv)
{
drive_image_free (drv);
drv->dskeject = false;
drv->dskchange = true;
drv->ddhd = 1;
drv->dskchange_time = 0;
drv->dskready = 0;
drv->dskready_up_time = 0;
drv->dskready_down_time = 0;
drv->forcedwrprot = false;
drv->crc32 = 0;
drive_settype_id (drv); /* Back to 35 DD */
}
static void floppy_get_bootblock (uae_u8 *dst, bool ffs, bool bootable)
{
strcpy ((char*)dst, "DOS");
dst[3] = ffs ? 1 : 0;
if (bootable)
memcpy (dst, ffs ? bootblock_ffs : bootblock_ofs, ffs ? sizeof bootblock_ffs : sizeof bootblock_ofs);
}
static void floppy_get_rootblock (uae_u8 *dst, int block, const TCHAR *disk_name, bool hd)
{
dst[0+3] = 2; // primary type
dst[12+3] = 0x48; // size of hash table
dst[312] = dst[313] = dst[314] = dst[315] = (uae_u8)0xff; // bitmap valid
dst[316+2] = (block + 1) >> 8; dst[316+3] = (block + 1) & 255; // bitmap pointer
char *s = ua ((disk_name && _tcslen (disk_name) > 0) ? disk_name : _T("empty"));
dst[432] = strlen (s); // name length
strcpy ((char*)dst + 433, s); // name
xfree (s);
dst[508 + 3] = 1; // secondary type
disk_date (dst + 420); // root modification date
disk_date(dst + 484); // creation date
disk_checksum (dst, dst + 20);
/* bitmap block */
memset (dst + 512 + 4, 0xff, 2 * block / 8);
if (!hd)
dst[512 + 0x72] = 0x3f;
else
dst[512 + 0xdc] = 0x3f;
disk_checksum (dst + 512, dst + 512);
}
/* type: 0=regular, 1=ext2adf */
bool disk_creatediskfile (struct uae_prefs *p, const TCHAR *name, int type, drive_type adftype, int hd, const TCHAR *disk_name, bool ffs, bool bootable, struct zfile *copyfrom)
{
int size = 32768;
struct zfile *f;
int i, l, file_size, tracks, track_len, sectors;
uae_u8 *chunk = NULL;
int ddhd = 1;
bool ok = false;
uae_u64 pos;
if (type == 1)
tracks = 2 * 83;
else
tracks = 2 * 80;
file_size = 880 * 1024;
sectors = 11;
if (adftype == DRV_PC_525_ONLY_40 || adftype == DRV_PC_35_ONLY_80 || adftype == DRV_PC_525_40_80) {
file_size = 720 * 1024;
sectors = 9;
}
// largest needed
track_len = FLOPPY_WRITE_LEN_NTSC;
if (p->floppy_write_length > track_len && p->floppy_write_length < 2 * FLOPPY_WRITE_LEN_NTSC)
track_len = p->floppy_write_length;
if (adftype == DRV_35_HD || hd > 0) {
file_size *= 2;
track_len *= 2;
ddhd = 2;
if (adftype == DRV_PC_525_40_80) {
file_size = 15 * 512 * 80 * 2;
}
} else if (adftype == DRV_PC_525_ONLY_40) {
file_size /= 2;
tracks /= 2;
}
if (copyfrom) {
pos = zfile_ftell (copyfrom);
zfile_fseek (copyfrom, 0, SEEK_SET);
}
f = zfile_fopen (name, _T("wb"), 0);
chunk = xmalloc (uae_u8, size);
if (f && chunk) {
int cylsize = sectors * 2 * 512;
memset(chunk, 0 ,size);
if (type == 0) {
for (i = 0; i < file_size; i += cylsize) {
memset(chunk, 0, cylsize);
if (adftype == DRV_35_DD || adftype == DRV_35_HD) {
if (i == 0) {
/* boot block */
floppy_get_bootblock (chunk, ffs, bootable);
} else if (i == file_size / 2) {
/* root block */
floppy_get_rootblock (chunk, file_size / 1024, disk_name, ddhd > 1);
}
}
zfile_fwrite (chunk, cylsize, 1, f);
}
ok = true;
} else {
uae_u8 root[4];
uae_u8 rawtrack[3 * 4], dostrack[3 * 4];
l = track_len;
zfile_fwrite ("UAE-1ADF", 8, 1, f);
root[0] = 0; root[1] = 0; /* flags (reserved) */
root[2] = 0; root[3] = tracks; /* number of tracks */
zfile_fwrite (root, 4, 1, f);
rawtrack[0] = 0; rawtrack[1] = 0; /* flags (reserved) */
rawtrack[2] = 0; rawtrack[3] = 1; /* track type */
rawtrack[4] = 0; rawtrack[5] = 0; rawtrack[6]=(uae_u8)(l >> 8); rawtrack[7] = (uae_u8)l;
rawtrack[8] = 0; rawtrack[9] = 0; rawtrack[10] = 0; rawtrack[11] = 0;
memcpy (dostrack, rawtrack, sizeof rawtrack);
dostrack[3] = 0;
dostrack[9] = (l * 8) >> 16; dostrack[10] = (l * 8) >> 8; dostrack[11] = (l * 8) >> 0;
bool dodos = ffs || bootable || (disk_name && _tcslen (disk_name) > 0);
for (i = 0; i < tracks; i++) {
uae_u8 tmp[3 * 4];
memcpy (tmp, rawtrack, sizeof rawtrack);
if (dodos || copyfrom)
memcpy (tmp, dostrack, sizeof dostrack);
zfile_fwrite (tmp, sizeof tmp, 1, f);
}
for (i = 0; i < tracks; i++) {
memset (chunk, 0, size);
if (copyfrom) {
zfile_fread (chunk, 11 * ddhd, 512, copyfrom);
} else {
if (dodos) {
if (i == 0)
floppy_get_bootblock (chunk, ffs, bootable);
else if (i == 80)
floppy_get_rootblock (chunk, 80 * 11 * ddhd, disk_name, adftype == DRV_35_HD);
}
}
zfile_fwrite (chunk, l, 1, f);
}
ok = true;
}
}
xfree (chunk);
zfile_fclose (f);
if (copyfrom)
zfile_fseek (copyfrom, pos, SEEK_SET);
return ok;
}
int disk_getwriteprotect (struct uae_prefs *p, const TCHAR *name)
{
int needwritefile;
drive_type drvtype;
return diskfile_iswriteprotect (p, name, &needwritefile, &drvtype);
}
static void diskfile_readonly (const TCHAR *name, bool readonly)
{
struct mystat st;
int mode, oldmode;
if (!my_stat (name, &st)) {
write_log (_T("failed to access '%s'\n"), name);
return;
}
write_log(_T("'%s': old mode = %x\n"), name, st.mode);
oldmode = mode = st.mode;
mode &= ~FILEFLAG_WRITE;
if (!readonly)
mode |= FILEFLAG_WRITE;
if (mode != oldmode) {
if (!my_chmod (name, mode))
write_log(_T("chmod failed!\n"));
}
write_log(_T("'%s': new mode = %x\n"), name, mode);
}
static void setdskchangetime(drive *drv, int dsktime)
{
int i;
/* prevent multiple disk insertions at the same time */
if (drv->dskchange_time > 0)
return;
for (i = 0; i < MAX_FLOPPY_DRIVES; i++) {
if (&floppy[i] != drv && floppy[i].dskchange_time > 0 && floppy[i].dskchange_time + 1 >= dsktime) {
dsktime = floppy[i].dskchange_time + 1;
}
}
drv->dskchange_time = dsktime;
}
void DISK_reinsert (int num)
{
drive_eject (&floppy[num]);
setdskchangetime (&floppy[num], 2 * 50 * 312);
}
int disk_setwriteprotect (struct uae_prefs *p, int num, const TCHAR *fname_in, bool writeprotected)
{
int needwritefile, oldprotect;
struct zfile *zf1, *zf2;
bool wrprot1, wrprot2;
int i;
TCHAR *name2;
drive_type drvtype;
TCHAR outfname[MAX_DPATH];
write_log(_T("disk_setwriteprotect %d '%s' %d\n"), num, fname_in, writeprotected);
oldprotect = diskfile_iswriteprotect (p, fname_in, &needwritefile, &drvtype);
DISK_validate_filename (p, fname_in, outfname, 1, &wrprot1, NULL, &zf1);
if (!zf1)
return 0;
write_log(_T("old = %d writeprot = %d master = %d\n"), oldprotect, wrprot1, p->floppy_read_only);
if (wrprot1 && p->floppy_read_only) {
zfile_fclose(zf1);
return 0;
}
if (zfile_iscompressed (zf1))
wrprot1 = 1;
zfile_fclose (zf1);
zf2 = getexistingwritefile(p, fname_in, &wrprot2);
name2 = DISK_get_saveimagepath(fname_in, -2);
if (needwritefile && zf2 == NULL)
disk_creatediskfile (p, name2, 1, drvtype, -1, NULL, false, false, NULL);
zfile_fclose (zf2);
if (writeprotected && iswritefileempty (p, fname_in)) {
for (i = 0; i < MAX_FLOPPY_DRIVES; i++) {
if (!_tcscmp (fname_in, floppy[i].newname))
drive_eject (&floppy[i]);
}
_wunlink (name2);
}
if (!needwritefile)
diskfile_readonly (outfname, writeprotected);
diskfile_readonly (name2, writeprotected);
floppy[num].forcedwrprot = false;
floppy[num].newnamewriteprotected = false;
return 1;
}
void disk_eject (int num)
{
set_config_changed ();
gui_filename (num, _T(""));
drive_eject (floppy + num);
*currprefs.floppyslots[num].df = *changed_prefs.floppyslots[num].df = 0;
floppy[num].newname[0] = 0;
update_drive_gui (num, true);
}
static void disk_insert_2 (int num, const TCHAR *name, bool forced, bool forcedwriteprotect)
{
drive *drv = floppy + num;
if (forced) {
drive_insert (drv, &currprefs, num, name, false, forcedwriteprotect);
return;
}
if (!_tcscmp (currprefs.floppyslots[num].df, name))
return;
drv->dskeject = false;
_tcscpy (drv->newname, name);
drv->newnamewriteprotected = forcedwriteprotect;
_tcscpy (currprefs.floppyslots[num].df, name);
currprefs.floppyslots[num].forcedwriteprotect = forcedwriteprotect;
if (name[0] == 0) {
disk_eject (num);
} else if (!drive_empty(drv) || drv->dskchange_time > 0) {
// delay eject so that it is always called when emulation is active
drv->dskeject = true;
} else {
setdskchangetime (drv, 1 * 312);
}
}
void disk_insert (int num, const TCHAR *name, bool forcedwriteprotect)
{
set_config_changed ();
target_addtorecent (name, 0);
disk_insert_2 (num, name, 0, forcedwriteprotect);
}
void disk_insert (int num, const TCHAR *name)
{
set_config_changed ();
target_addtorecent (name, 0);
disk_insert_2 (num, name, 0, false);
}
static void DISK_check_change(void)
{
if (currprefs.floppy_speed != changed_prefs.floppy_speed)
currprefs.floppy_speed = changed_prefs.floppy_speed;
if (currprefs.floppy_read_only != changed_prefs.floppy_read_only)
currprefs.floppy_read_only = changed_prefs.floppy_read_only;
for (int i = 0; i < MAX_FLOPPY_DRIVES; i++) {
drive* drv = floppy + i;
if (drv->dskeject) {
drive_eject(drv);
/* set dskchange_time, disk_insert() will be
* called from DISK_check_change() after 2 second delay
* this makes sure that all programs detect disk change correctly
*/
setdskchangetime(drv, 2 * 50 * 312);
}
if (currprefs.floppyslots[i].dfxtype != changed_prefs.floppyslots[i].dfxtype) {
currprefs.floppyslots[i].dfxtype = changed_prefs.floppyslots[i].dfxtype;
reset_drive(i);
}
}
}
void DISK_vsync (void)
{
DISK_check_change ();
for (int i = 0; i < MAX_FLOPPY_DRIVES; i++) {
drive *drv = floppy + i;
if (drv->selected_delay > 0) {
drv->selected_delay--;
}
if (drv->dskchange_time == 0 && _tcscmp (currprefs.floppyslots[i].df, changed_prefs.floppyslots[i].df))
disk_insert (i, changed_prefs.floppyslots[i].df, changed_prefs.floppyslots[i].forcedwriteprotect);
}
}
int disk_empty (int num)
{
return drive_empty (floppy + num);
}
static void fetch_DISK_select(uae_u8 data)
{
selected = (data >> 3) & 15;
side = 1 - ((data >> 2) & 1);
direction = (data >> 1) & 1;
}
void DISK_select_set (uae_u8 data)
{
prev_data = data;
prev_step = data & 1;
fetch_DISK_select (data);
}
void DISK_select (uae_u8 data)
{
int step_pulse, prev_selected, dr;
prev_selected = selected;
fetch_DISK_select (data);
step_pulse = data & 1;
if ((prev_data & 0x80) != (data & 0x80)) {
for (dr = 0; dr < 4; dr++) {
if (floppy[dr].indexhackmode > 1 && !((selected | disabled) & (1 << dr))) {
floppy[dr].indexhack = 1;
}
}
}
// step goes high and drive was selected when step pulse changes: step
if (prev_step != step_pulse) {
prev_step = step_pulse;
if (prev_step && !savestate_state) {
for (dr = 0; dr < MAX_FLOPPY_DRIVES; dr++) {
if (!((prev_selected | disabled) & (1 << dr))) {
drive_step (floppy + dr, direction);
if (floppy[dr].indexhackmode > 1 && (data & 0x80))
floppy[dr].indexhack = 1;
}
}
}
}
if (!savestate_state) {
for (dr = 0; dr < MAX_FLOPPY_DRIVES; 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 | disabled) & (1 << dr)) && (prev_selected & (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 ((prev_data & 0x80) == 0 || (data & 0x80) == 0) {
/* motor off: if motor bit = 0 in prevdata or data -> turn motor on */
drive_motor (drv, 0);
} else if (prev_data & 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);
}
}
if (!currprefs.cs_df0idhw && dr == 0)
drv->idbit = 0;
}
}
}
for (dr = 0; dr < MAX_FLOPPY_DRIVES; dr++) {
// selected
if (!(selected & (1 << dr)) && floppy[dr].selected_delay < 0) {
floppy[dr].selected_delay = 2;
}
// not selected
if ((selected & (1 << dr))) {
floppy[dr].selected_delay = -1;
}
// external drives usually (always?) light activity led when selected. Internal only when motor is running.
bool selected_led = !(selected & (1 << dr)) && floppy[dr].selected_delay == 0 && dr > 0;
floppy[dr].state = selected_led || !floppy[dr].motoroff;
update_drive_gui (dr, false);
}
prev_data = data;
}
uae_u8 DISK_status_ciaa(void)
{
uae_u8 st = 0x3c;
for (int dr = 0; dr < MAX_FLOPPY_DRIVES; dr++) {
drive *drv = floppy + dr;
if (!((selected | disabled) & (1 << dr))) {
if (drive_running (drv)) {
if (drv->dskready && !drv->indexhack && currprefs.floppyslots[dr].dfxtype != DRV_35_DD_ESCOM)
st &= ~0x20;
} else {
if (currprefs.cs_df0idhw || dr > 0) {
/* report drive ID */
if (drv->idbit && currprefs.floppyslots[dr].dfxtype != DRV_35_DD_ESCOM)
st &= ~0x20;
} else {
/* non-ID internal drive: mirror real dskready */
if (drv->dskready)
st &= ~0x20;
}
/* dskrdy needs some cycles after switching the motor off.. (Pro Tennis Tour) */
if (!currprefs.cs_df0idhw && dr == 0 && drv->motordelay)
st &= ~0x20;
}
if (drive_track0 (drv))
st &= ~0x10;
if (drive_writeprotected (drv))
st &= ~8;
if (drv->dskchange && currprefs.floppyslots[dr].dfxtype != DRV_525_SD) {
st &= ~4;
}
} else if (!((selected | disabled) & (1 << dr))) {
if (drv->idbit)
st &= ~0x20;
}
}
return st;
}
static bool unformatted (drive *drv)
{
int tr = drv->cyl * 2 + side;
if (tr >= drv->num_tracks)
return true;
if (drv->filetype == ADF_EXT2 && drv->trackdata[tr].bitlen == 0 && drv->trackdata[tr].type != TRACK_AMIGADOS)
return true;
if (drv->trackdata[tr].type == TRACK_NONE)
return true;
return false;
}
/* get one bit from MFM bit stream */
STATIC_INLINE uae_u32 getonebit (uae_u16 * mfmbuf, int mfmpos)
{
uae_u16 *buf;
buf = &mfmbuf[mfmpos >> 4];
return (buf[0] & (1 << (15 - (mfmpos & 15)))) ? 1 : 0;
}
static void disk_dmafinished (void)
{
INTREQ (0x8000 | 0x0002);
dskdmaen = DSKDMA_OFF;
dsklength = 0;
dsklen = 0;
}
static void fetchnextrevolution(drive *drv)
{
if (drv->revolution_check)
return;
drv->trackspeed = get_floppy_speed_from_image (drv);
drv->revolution_check = 2;
if (!drv->multi_revolution)
return;
switch (drv->filetype)
{
case ADF_IPF:
#ifdef CAPS
caps_loadrevolution(drv->bigmfmbuf, drv->tracktiming, drv - floppy, drv->cyl * 2 + side, &drv->tracklen, &drv->lastrev, drv->track_access_done);
#endif
break;
case ADF_SCP:
#ifdef SCP
scp_loadrevolution(drv->bigmfmbuf, drv - floppy, drv->tracktiming, &drv->tracklen);
#endif
break;
case ADF_FDI:
#ifdef FDI2RAW
fdi2raw_loadrevolution(drv->fdi, drv->bigmfmbuf, drv->tracktiming, drv->cyl * 2 + side, &drv->tracklen, 1);
#endif
break;
}
}
static void do_disk_index (void)
{
if (!indexdecay) {
indexdecay = 2;
cia_diskindex ();
}
}
void DISK_handler (uae_u32 data)
{
int flag = data & 255;
int disk_sync_cycle = data >> 8;
int hpos = current_hpos ();
event2_remevent(ev2_disk);
DISK_update (disk_sync_cycle);
if (!dskdmaen) {
if (flag & (DISK_REVOLUTION << 0))
fetchnextrevolution (&floppy[0]);
if (flag & (DISK_REVOLUTION << 1))
fetchnextrevolution (&floppy[1]);
if (flag & (DISK_REVOLUTION << 2))
fetchnextrevolution (&floppy[2]);
if (flag & (DISK_REVOLUTION << 3))
fetchnextrevolution (&floppy[3]);
}
if (flag & DISK_WORDSYNC)
INTREQ (0x8000 | 0x1000);
if (flag & DISK_INDEXSYNC)
do_disk_index ();
}
static void disk_doupdate_write (drive * drv, int floppybits)
{
int dr;
int drives[4];
for (dr = 0; dr < MAX_FLOPPY_DRIVES ; dr++) {
drive *drv2 = &floppy[dr];
drives[dr] = 0;
if (drv2->motoroff)
continue;
if ((selected | disabled) & (1 << dr))
continue;
drives[dr] = 1;
}
while (floppybits >= drv->trackspeed) {
for (dr = 0; dr < MAX_FLOPPY_DRIVES; dr++) {
if (drives[dr]) {
drive *drv2 = &floppy[dr];
drv2->mfmpos++;
drv2->mfmpos %= drv2->tracklen;
if (drv2->mfmpos == drv2->indexoffset) {
do_disk_index();
}
}
}
if (dmaen (DMA_DISK) && dskdmaen == DSKDMA_WRITE && dsklength > 0 && fifo_filled) {
bitoffset++;
bitoffset &= 15;
if (!bitoffset) {
// fast disk modes, fill the fifo instantly
if (currprefs.floppy_speed > 100 && !fifo_inuse[0] && !fifo_inuse[1] && !fifo_inuse[2]) {
while (!fifo_inuse[2]) {
uae_u16 w = chipmem_wget_indirect (dskpt);
DSKDAT (w);
dskpt += 2;
}
}
if (disk_fifostatus () >= 0) {
uae_u16 w = DSKDATR ();
for (dr = 0; dr < MAX_FLOPPY_DRIVES ; dr++) {
drive *drv2 = &floppy[dr];
if (drives[dr]) {
drv2->bigmfmbuf[drv2->mfmpos >> 4] = w;
drv2->bigmfmbuf[(drv2->mfmpos >> 4) + 1] = 0x5555;
drv2->writtento = 1;
}
}
dsklength--;
if (dsklength <= 0) {
disk_dmafinished ();
for (int dr = 0; dr < MAX_FLOPPY_DRIVES ; dr++) {
drive *drv = &floppy[dr];
drv->writtento = 0;
if (drv->motoroff)
continue;
if ((selected | disabled) & (1 << dr))
continue;
drive_write_data (drv);
}
}
}
}
}
floppybits -= drv->trackspeed;
}
}
static void update_jitter (void)
{
disk_jitter = ((uaerand () >> 4) % 3) + 1;
}
static void updatetrackspeed (drive *drv, int mfmpos)
{
if (dskdmaen < DSKDMA_WRITE) {
int t = drv->tracktiming[mfmpos / 8];
int ts = get_floppy_speed_from_image (drv) * t / 1000;
if (ts < 700 || ts > 3000) {
} else {
drv->trackspeed = ts;
}
}
}
static void disk_doupdate_predict (int startcycle)
{
int finaleventcycle = maxhpos << 8;
int finaleventflag = 0;
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;
int mfmpos = drv->mfmpos;
if (drv->tracktiming[0])
updatetrackspeed (drv, mfmpos);
int diskevent_flag = 0;
uae_u32 tword = word;
int countcycle = startcycle;
while (countcycle < (maxhpos << 8)) {
if (drv->tracktiming[0])
updatetrackspeed (drv, mfmpos);
countcycle += drv->trackspeed;
if (dskdmaen != DSKDMA_WRITE || (dskdmaen == DSKDMA_WRITE && !dma_enable)) {
tword <<= 1;
if (!drive_empty (drv)) {
if (unformatted (drv))
tword |= (uaerand() & 0x1000) ? 1 : 0;
else
tword |= getonebit (drv->bigmfmbuf, mfmpos);
}
if (dskdmaen != DSKDMA_READ && (tword & 0xffff) == dsksync && dsksync != 0)
diskevent_flag |= DISK_WORDSYNC;
}
mfmpos++;
mfmpos %= drv->tracklen;
if (!dskdmaen) {
if (mfmpos == 0)
diskevent_flag |= DISK_REVOLUTION << (drv - floppy);
if (mfmpos == drv->indexoffset)
diskevent_flag |= DISK_INDEXSYNC;
}
if (dskdmaen != DSKDMA_WRITE && mfmpos == drv->skipoffset) {
int skipcnt = disk_jitter;
while (skipcnt-- > 0) {
mfmpos++;
mfmpos %= drv->tracklen;
if (!dskdmaen) {
if (mfmpos == 0)
diskevent_flag |= DISK_REVOLUTION << (drv - floppy);
if (mfmpos == drv->indexoffset)
diskevent_flag |= DISK_INDEXSYNC;
}
}
}
if (diskevent_flag)
break;
}
if (drv->tracktiming[0])
updatetrackspeed (drv, drv->mfmpos);
if (diskevent_flag && countcycle < finaleventcycle) {
finaleventcycle = countcycle;
finaleventflag = diskevent_flag;
}
}
if (finaleventflag && (finaleventcycle >> 8) < maxhpos) {
event2_newevent (ev2_disk, (finaleventcycle - startcycle) >> 8, ((finaleventcycle >> 8) << 8) | finaleventflag);
}
}
int disk_fifostatus (void)
{
if (fifo_inuse[0] && fifo_inuse[1] && fifo_inuse[2])
return 1;
if (!fifo_inuse[0] && !fifo_inuse[1] && !fifo_inuse[2])
return -1;
return 0;
}
static int doreaddma (void)
{
if (dmaen (DMA_DISK) && bitoffset == 15 && dma_enable && dskdmaen == DSKDMA_READ && dsklength >= 0) {
if (dsklength > 0) {
// DSKLEN == 1: finish without DMA transfer.
if (dsklength == 1 && dsklength2 == 1) {
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 ();
chipmem_wput_indirect (dskpt, w);
dskpt += 2;
}
}
if (disk_fifostatus () > 0) {
write_log (_T("doreaddma() fifo overflow detected, retrying..\n"));
return -1;
} else {
DSKDAT (word);
dsklength--;
}
}
return 1;
}
return 0;
}
static void disk_doupdate_read_nothing (int floppybits)
{
while (floppybits >= get_floppy_speed()) {
word <<= 1;
doreaddma ();
if ((bitoffset & 7) == 7) {
dskbytr_val = word & 0xff;
dskbytr_val |= 0x8000;
}
bitoffset++;
bitoffset &= 15;
floppybits -= get_floppy_speed();
}
}
static void wordsync_detected(bool startup)
{
dsksync_cycles = get_cycles () + WORDSYNC_TIME * CYCLE_UNIT;
if (dskdmaen != DSKDMA_OFF) {
if (!startup)
dma_enable = 1;
INTREQ (0x8000 | 0x1000);
}
if (adkcon & 0x0400) { // WORDSYNC
bitoffset = 15;
}
}
static void disk_doupdate_read (drive * drv, int floppybits)
{
/*
uae_u16 *mfmbuf = drv->bigmfmbuf;
dsksync = 0x4444;
adkcon |= 0x400;
drv->mfmpos = 0;
memset (mfmbuf, 0, 1000);
cycles = 0x1000000;
// 4444 4444 4444 aaaa aaaaa 4444 4444 4444
// 4444 aaaa aaaa 4444
mfmbuf[0] = 0x4444;
mfmbuf[1] = 0x4444;
mfmbuf[2] = 0x4444;
mfmbuf[3] = 0xaaaa;
mfmbuf[4] = 0xaaaa;
mfmbuf[5] = 0x4444;
mfmbuf[6] = 0x4444;
mfmbuf[7] = 0x4444;
*/
while (floppybits >= drv->trackspeed) {
bool skipbit = false;
if (drv->tracktiming[0])
updatetrackspeed (drv, drv->mfmpos);
word <<= 1;
if (!drive_empty (drv)) {
if (unformatted (drv))
word |= (uaerand() & 0x1000) ? 1 : 0;
else
word |= getonebit (drv->bigmfmbuf, drv->mfmpos);
}
if (doreaddma () < 0) {
word >>= 1;
return;
}
drv->mfmpos++;
drv->mfmpos %= drv->tracklen;
if (drv->mfmpos == drv->indexoffset) {
drv->indexhack = 0;
do_disk_index ();
}
if (drv->mfmpos == 0) {
fetchnextrevolution (drv);
if (drv->tracktiming[0])
updatetrackspeed (drv, drv->mfmpos);
}
if (drv->mfmpos == drv->skipoffset) {
int skipcnt = disk_jitter;
while (skipcnt-- > 0) {
drv->mfmpos++;
drv->mfmpos %= drv->tracklen;
if (drv->mfmpos == drv->indexoffset) {
drv->indexhack = 0;
do_disk_index ();
}
if (drv->mfmpos == 0) {
fetchnextrevolution (drv);
if (drv->tracktiming[0])
updatetrackspeed (drv, drv->mfmpos);
}
}
}
// MSBSYNC
if (adkcon & 0x200) {
if ((word & 0x0001) == 0 && bitoffset == 0) {
word = 0;
skipbit = true;
}
if ((word & 0x0001) == 0 && bitoffset == 8) {
word >>= 1;
skipbit = true;
}
}
if (!skipbit && (bitoffset & 7) == 7) {
dskbytr_val = word & 0xff;
dskbytr_val |= 0x8000;
}
// WORDSYNC
if (!(adkcon & 0x200) && word == dsksync) {
wordsync_detected(false);
}
if (!skipbit) {
bitoffset++;
bitoffset &= 15;
}
floppybits -= drv->trackspeed;
}
}
/* this is very unoptimized. DSKBYTR is used very rarely, so it should not matter. */
uae_u16 DSKBYTR (int hpos)
{
uae_u16 v;
DISK_update (hpos);
v = dskbytr_val;
dskbytr_val &= ~0x8000;
if (word == dsksync && cycles_in_range (dsksync_cycles)) {
v |= 0x1000;
}
if (dskdmaen != DSKDMA_OFF && dmaen (DMA_DISK))
v |= 0x4000;
if (dsklen & 0x4000)
v |= 0x2000;
for (int dr = 0; dr < MAX_FLOPPY_DRIVES; dr++) {
drive *drv = &floppy[dr];
if (drv->motoroff)
continue;
if (!((selected | disabled) & (1 << dr))) {
drv->lastdataacesstrack = drv->cyl * 2 + side;
drv->track_access_done = true;
}
}
return v;
}
static void DISK_start (void)
{
int dr;
for (int i = 0; i < 3; i++)
fifo_inuse[i] = false;
fifo_filled = 0;
for (dr = 0; dr < MAX_FLOPPY_DRIVES; dr++) {
drive *drv = &floppy[dr];
if (!((selected | disabled) & (1 << dr))) {
int tr = drv->cyl * 2 + side;
trackid *ti = drv->trackdata + tr;
if (dskdmaen == DSKDMA_WRITE) {
word = 0;
drv->tracklen = FLOPPY_WRITE_LEN * drv->ddhd * 8 * 2;
drv->trackspeed = get_floppy_speed();
drv->skipoffset = -1;
updatemfmpos (drv);
}
/* Ugh. A nasty hack. Assume ADF_EXT1 tracks are always read
from the start. */
if (ti->type == TRACK_RAW1) {
drv->mfmpos = 0;
bitoffset = 0;
word = 0;
}
}
drv->floppybitcounter = 0;
}
dma_enable = (adkcon & 0x400) ? 0 : 1;
if (word == dsksync)
wordsync_detected(true);
}
static int linecounter;
void DISK_hsync (void)
{
int dr;
for (dr = 0; dr < MAX_FLOPPY_DRIVES; dr++) {
drive *drv = &floppy[dr];
if (drv->steplimit)
drv->steplimit--;
if (drv->revolution_check)
drv->revolution_check--;
if (drv->dskready_down_time > 0)
drv->dskready_down_time--;
/* emulate drive motor turn on time */
if (drv->dskready_up_time > 0 && !drive_empty (drv)) {
drv->dskready_up_time--;
if (drv->dskready_up_time == 0 && !drv->motoroff)
drv->dskready = true;
}
/* delay until new disk image is inserted */
if (drv->dskchange_time > 0) {
drv->dskchange_time--;
if (drv->dskchange_time == 0) {
drive_insert (drv, &currprefs, dr, drv->newname, false, drv->newnamewriteprotected);
update_drive_gui (dr, false);
}
}
}
if (indexdecay)
indexdecay--;
if (linecounter) {
linecounter--;
if (! linecounter)
disk_dmafinished ();
return;
}
DISK_update (maxhpos);
}
void DISK_update (int tohpos)
{
int dr;
int cycles;
if (disk_hpos < 0) {
disk_hpos = - disk_hpos;
return;
}
cycles = (tohpos << 8) - disk_hpos;
if (cycles <= 0)
return;
disk_hpos += cycles;
if (disk_hpos >= (maxhpos << 8))
disk_hpos %= 1 << 8;
for (dr = 0; dr < MAX_FLOPPY_DRIVES; dr++) {
drive *drv = &floppy[dr];
if (drv->motoroff || !drv->tracklen || !drv->trackspeed)
continue;
drv->floppybitcounter += cycles;
if ((selected | disabled) & (1 << dr)) {
drv->mfmpos += drv->floppybitcounter / drv->trackspeed;
drv->mfmpos %= drv->tracklen;
drv->floppybitcounter %= drv->trackspeed;
continue;
}
if (drv->diskfile)
drive_fill_bigbuf (drv, 0);
drv->mfmpos %= drv->tracklen;
}
int didaccess = 0;
bool done_jitter = false;
for (dr = 0; dr < MAX_FLOPPY_DRIVES; dr++) {
drive *drv = &floppy[dr];
if (drv->motoroff || !drv->trackspeed)
continue;
if ((selected | disabled) & (1 << dr))
continue;
if (!done_jitter) {
update_jitter();
done_jitter = true;
}
/* write dma and wordsync enabled: read until wordsync match found */
if (dskdmaen == DSKDMA_WRITE && dma_enable)
disk_doupdate_write (drv, drv->floppybitcounter);
else
disk_doupdate_read (drv, drv->floppybitcounter);
drv->floppybitcounter %= drv->trackspeed;
didaccess = 1;
}
/* no floppy selected but read dma */
if (!didaccess && dskdmaen == DSKDMA_READ) {
disk_doupdate_read_nothing (cycles);
}
/* instantly finish dma if dsklen==0 and wordsync detected */
if (dskdmaen != DSKDMA_OFF && dma_enable && dsklength2 == 0 && dsklength == 0)
disk_dmafinished ();
if (!done_jitter) {
update_jitter();
done_jitter = true;
}
disk_doupdate_predict (disk_hpos);
}
void DSKLEN(uae_u16 v, int hpos)
{
int dr;
int prevlen = dsklen;
int prevdatalen = dsklength;
int noselected = 0;
int motormask;
DISK_update(hpos);
dsklen = v;
dsklength2 = dsklength = dsklen & 0x3fff;
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);
return;
}
dskdmaen = DSKDMA_READ;
DISK_start();
}
if (!(v & 0x8000)) {
if (dskdmaen != DSKDMA_OFF) {
/* Megalomania and Knightmare does this */
if (dskdmaen == DSKDMA_WRITE) {
write_log(_T("warning: Disk write DMA aborted, %d words left PC=%x\n"), prevdatalen, M68K_GETPC);
// did program write something that needs to be stored to file?
for (dr = 0; dr < MAX_FLOPPY_DRIVES; dr++) {
drive* drv2 = &floppy[dr];
if (!drv2->writtento)
continue;
drive_write_data(drv2);
}
}
dskdmaen = DSKDMA_OFF;
}
}
if (dskdmaen == DSKDMA_OFF)
return;
if (dsklength == 0 && dma_enable) {
disk_dmafinished();
return;
}
if ((v & 0x4000) && (prevlen & 0x4000)) {
if (dsklength == 0)
return;
if (dsklength == 1) {
disk_dmafinished();
return;
}
if (dskdmaen == DSKDMA_WRITE) {
write_log(_T("warning: Disk write DMA length rewrite %d -> %d\n"), prevlen & 0x3fff, v & 0x3fff);
return;
}
dskdmaen = DSKDMA_WRITE;
DISK_start();
}
for (dr = 0; dr < MAX_FLOPPY_DRIVES; dr++) {
drive* drv = &floppy[dr];
if (drv->motoroff)
continue;
if (selected & (1 << dr))
continue;
if (dskdmaen == DSKDMA_READ) {
drv->lastdataacesstrack = drv->cyl * 2 + side;
drv->track_access_done = true;
}
}
motormask = 0;
for (dr = 0; dr < MAX_FLOPPY_DRIVES; dr++) {
drive* drv = &floppy[dr];
drv->writtento = 0;
if (drv->motoroff)
continue;
motormask |= 1 << dr;
if ((selected & (1 << dr)) == 0)
break;
}
if (dr == 4) {
noselected = 1;
}
for (dr = 0; dr < MAX_FLOPPY_DRIVES; dr++)
update_drive_gui(dr, false);
/* Try to make floppy access from Kickstart faster. */
if (dskdmaen != DSKDMA_READ && dskdmaen != DSKDMA_WRITE)
return;
for (dr = 0; dr < MAX_FLOPPY_DRIVES; dr++) {
drive* drv = &floppy[dr];
if (selected & (1 << dr))
continue;
if (drv->filetype != ADF_NORMAL && drv->filetype != ADF_KICK && drv->filetype != ADF_SKICK)
break;
}
if (dr < MAX_FLOPPY_DRIVES) /* no turbo mode if any selected drive has non-standard ADF */
return;
{
int done = 0;
for (dr = 0; dr < MAX_FLOPPY_DRIVES; dr++) {
drive* drv = &floppy[dr];
bool floppysupported = (drv->ddhd < 2) || (drv->ddhd > 1 && currprefs.floppyslots[dr].dfxtype == DRV_35_HD);
int pos, i;
if (drv->motoroff)
continue;
if (!drv->useturbo && currprefs.floppy_speed > 0)
continue;
if (selected & (1 << dr))
continue;
pos = drv->mfmpos & ~15;
drive_fill_bigbuf(drv, 0);
if (dskdmaen == DSKDMA_READ) { /* TURBO read */
if ((adkcon & 0x400) && floppysupported) {
for (i = 0; i < drv->tracklen; i += 16) {
pos += 16;
pos %= drv->tracklen;
if (drv->bigmfmbuf[pos >> 4] == dsksync) {
/* must skip first disk sync marker */
pos += 16;
pos %= drv->tracklen;
break;
}
}
if (i >= drv->tracklen)
return;
}
// read nothing if not supported and MFMSYNC is on.
if ((floppysupported) || (!floppysupported && !(adkcon & 0x400))) {
while (dsklength-- > 0) {
chipmem_wput_indirect(dskpt, floppysupported ? drv->bigmfmbuf[pos >> 4] : uaerand());
dskpt += 2;
pos += 16;
pos %= drv->tracklen;
}
}
else {
pos += uaerand();
pos %= drv->tracklen;
}
drv->mfmpos = pos;
if (floppysupported)
INTREQ(0x8000 | 0x1000);
done = 2;
}
else if (dskdmaen == DSKDMA_WRITE) { /* TURBO write */
if (floppysupported) {
for (i = 0; i < dsklength; i++) {
uae_u16 w = chipmem_wget_indirect(dskpt + i * 2);
drv->bigmfmbuf[pos >> 4] = w;
pos += 16;
pos %= drv->tracklen;
}
drv->mfmpos = pos;
drive_write_data(drv);
done = 2;
}
else {
pos += uaerand();
pos %= drv->tracklen;
drv->mfmpos = pos;
done = 2;
}
}
}
if (!done && noselected) {
int bits = -1;
while (dsklength-- > 0) {
if (dskdmaen == DSKDMA_WRITE) {
uae_u16 w = chipmem_wget_indirect(dskpt);
}
else {
chipmem_wput_indirect(dskpt, 0);
}
dskpt += 2;
}
if (bits == 0) {
done = 1;
}
else {
INTREQ(0x8000 | 0x1000);
done = 2;
}
}
if (done) {
linecounter = done;
dskdmaen = DSKDMA_OFF;
return;
}
}
}
void DISK_update_adkcon(int hpos, uae_u16 v)
{
uae_u16 vold = adkcon;
uae_u16 vnew = adkcon;
if (v & 0x8000)
vnew |= v & 0x7FFF;
else
vnew &= ~v;
if ((vnew & 0x400) && !(vold & 0x400))
bitoffset = 0;
}
void DSKSYNC(int hpos, uae_u16 v)
{
if (v == dsksync)
return;
DISK_update(hpos);
dsksync = 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;
if (dskdmaen) {
if (dskdmaen == 3) {
dmal = (1 + 2) * (fifo_inuse[0] ? 1 : 0) + (4 + 8) * (fifo_inuse[1] ? 1 : 0) + (16 + 32) * (fifo_inuse[2] ? 1 : 0);
dmal ^= 63;
if (dsklength == 2)
dmal &= ~(16 + 32);
if (dsklength == 1)
dmal &= ~(16 + 32 + 4 + 8);
} else {
dmal = 16 * (fifo_inuse[0] ? 1 : 0) + 4 * (fifo_inuse[1] ? 1 : 0) + 1 * (fifo_inuse[2] ? 1 : 0);
}
}
return dmal;
}
uaecptr disk_getpt (void)
{
uaecptr pt = dskpt;
dskpt += 2;
return pt;
}
void DSKPTH (uae_u16 v)
{
dskpt = (dskpt & 0xffff) | ((uae_u32) v << 16);
}
void DSKPTL (uae_u16 v)
{
dskpt = (dskpt & ~0xffff) | (v);
}
void DISK_free (void)
{
for (int dr = 0; dr < MAX_FLOPPY_DRIVES; dr++) {
drive *drv = &floppy[dr];
drive_image_free (drv);
}
}
void DISK_init (void)
{
for (int dr = MAX_FLOPPY_DRIVES - 1; dr >= 0; dr--) {
drive *drv = &floppy[dr];
/* reset all drive types to 3.5 DD */
drive_settype_id (drv);
if (!drive_insert (drv, &currprefs, dr, currprefs.floppyslots[dr].df, false, currprefs.floppyslots[dr].forcedwriteprotect))
disk_eject (dr);
}
if (disk_empty (0))
write_log (_T("No disk in drive 0.\n"));
}
void DISK_reset (void)
{
if (savestate_state)
return;
disk_hpos = 0;
dskdmaen = 0;
disabled = 0;
memset(&disk_info_data, 0, sizeof disk_info_data);
for (int dr = MAX_FLOPPY_DRIVES - 1; dr >= 0; dr--) {
reset_drive (dr);
}
}
static void load_track (int num, int cyl, int side, int *sectable)
{
int oldcyl, oldside, drvsec;
drive *drv = &floppy[num];
oldcyl = drv->cyl;
oldside = side;
drv->cyl = cyl;
side = 0;
drv->buffered_cyl = -1;
drive_fill_bigbuf (drv, 1);
decode_buffer (drv->bigmfmbuf, drv->cyl, 11, drv->ddhd, drv->filetype, &drvsec, sectable, 1);
drv->cyl = oldcyl;
side = oldside;
drv->buffered_cyl = -1;
}
static int DISK_examine_image (struct uae_prefs *p, int num, struct diskinfo *di)
{
int drvsec;
int ret, i;
drive *drv = &floppy[num];
uae_u32 dos, crc, crc2;
int wasdelayed = drv->dskchange_time;
int sectable[MAX_SECTORS];
int oldcyl, oldside;
uae_u32 v = 0;
ret = 0;
memset (di, 0, sizeof (struct diskinfo));
oldcyl = drv->cyl;
oldside = side;
drv->cyl = 0;
side = 0;
if (!drive_insert (drv, p, num, p->floppyslots[num].df, true, true) || !drv->diskfile) {
drv->cyl = oldcyl;
side = oldside;
return 1;
}
di->crc32 = zfile_crc32 (drv->diskfile);
decode_buffer (drv->bigmfmbuf, drv->cyl, 11, drv->ddhd, drv->filetype, &drvsec, sectable, 1);
di->hd = drv->ddhd == 2;
drv->cyl = oldcyl;
side = oldside;
if (sectable[0] == 0 || sectable[1] == 0) {
ret = 2;
goto end2;
}
crc = crc2 = 0;
for (i = 0; i < 1024; i += 4) {
di->bootblock[i + 0] = writebuffer[i + 0];
di->bootblock[i + 1] = writebuffer[i + 1];
di->bootblock[i + 2] = writebuffer[i + 2];
di->bootblock[i + 3] = writebuffer[i + 3];
v = (writebuffer[i] << 24) | (writebuffer[i + 1] << 16) | (writebuffer[i + 2] << 8) | writebuffer[i + 3];
if (i == 0)
dos = v;
if (i == 4) {
crc2 = v;
v = 0;
}
if (crc + v < crc)
crc++;
crc += v;
}
if (dos == 0x4b49434b) { /* KICK */
ret = 10;
goto end;
}
crc ^= 0xffffffff;
if (crc != crc2) {
ret = 3;
goto end;
}
writebuffer[4] = writebuffer[5] = writebuffer[6] = writebuffer[7] = 0;
if (dos == 0x444f5300)
ret = 10;
else if (dos == 0x444f5301 || dos == 0x444f5302 || dos == 0x444f5303)
ret = 11;
else if (dos == 0x444f5304 || dos == 0x444f5305 || dos == 0x444f5306 || dos == 0x444f5307)
ret = 12;
else
ret = 4;
v = get_crc32 (writebuffer + 8, 0x5c - 8);
end:
load_track (num, 40, 0, sectable);
if (sectable[0]) {
if (!disk_checksum (writebuffer, NULL) &&
writebuffer[0] == 0 && writebuffer[1] == 0 && writebuffer[2] == 0 && writebuffer[3] == 2 &&
writebuffer[508] == 0 && writebuffer[509] == 0 && writebuffer[510] == 0 && writebuffer[511] == 1) {
writebuffer[512 - 20 * 4 + 1 + writebuffer[512 - 20 * 4]] = 0;
TCHAR *n = au ((const char*)(writebuffer + 512 - 20 * 4 + 1));
if (_tcslen (n) >= sizeof (di->diskname))
n[sizeof (di->diskname) - 1] = 0;
_tcscpy (di->diskname, n);
xfree (n);
}
}
end2:
drive_image_free (drv);
if (wasdelayed > 1) {
drive_eject (drv);
currprefs.floppyslots[num].df[0] = 0;
drv->dskchange_time = wasdelayed;
disk_insert (num, drv->newname);
}
return ret;
}
/* Disk save/restore code */
#if defined SAVESTATE
void DISK_save_custom (uae_u32 *pdskpt, uae_u16 *pdsklength, uae_u16 *pdsksync, uae_u16 *pdskbytr)
{
if (pdskpt)
*pdskpt = dskpt;
if (pdsklength)
*pdsklength = dsklen;
if (pdsksync)
*pdsksync = dsksync;
if(pdskbytr)
*pdskbytr = dskbytr_val;
}
#endif /* SAVESTATE */
static uae_u32 getadfcrc (drive *drv)
{
uae_u8 *b;
uae_u32 crc32;
int size;
if (!drv->diskfile)
return 0;
zfile_fseek (drv->diskfile, 0, SEEK_END);
size = zfile_ftell (drv->diskfile);
b = xmalloc (uae_u8, size);
if (!b)
return 0;
zfile_fseek (drv->diskfile, 0, SEEK_SET);
zfile_fread (b, 1, size, drv->diskfile);
crc32 = get_crc32 (b, size);
free (b);
return crc32;
}
#ifdef SAVESTATE
void DISK_restore_custom (uae_u32 pdskpt, uae_u16 pdsklength, uae_u16 pdskbytr)
{
dskpt = pdskpt;
dsklen = pdsklength;
dskbytr_val = pdskbytr;
}
void restore_disk_finish (void)
{
int cnt = 0;
for (int i = 0; i < MAX_FLOPPY_DRIVES; i++) {
if (currprefs.floppyslots[i].dfxtype >= 0) {
update_drive_gui (i, true);
cnt++;
}
}
currprefs.nr_floppies = changed_prefs.nr_floppies = cnt;
DISK_check_change ();
}
uae_u8 *restore_disk(int num,uae_u8 *src)
{
drive *drv;
int state;
TCHAR old[MAX_DPATH];
TCHAR *s;
int newis;
drive_type dfxtype;
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.floppyslots[num].dfxtype = -1;
} else {
drv->motoroff = (state & 1) ? 0 : 1;
drv->idbit = (state & 4) ? 1 : 0;
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;
}
currprefs.floppyslots[num].dfxtype = changed_prefs.floppyslots[num].dfxtype = dfxtype;
}
drv->dskchange = (state & 8) != 0;
side = (state & 16) ? 1 : 0;
drv->indexhackmode = 0;
if (num == 0 && currprefs.floppyslots[num].dfxtype == 0)
drv->indexhackmode = 1;
drv->buffered_cyl = -1;
drv->buffered_side = -1;
drv->cyl = restore_u8 ();
drv->dskready = restore_u8 () != 0;
drv->drive_id_scnt = restore_u8 ();
int mfmpos = restore_u32 ();
drv->dskchange_time = 0;
restore_u32 ();
s = restore_path (SAVESTATE_PATH_FLOPPY);
int dskready_up_time = restore_u16 ();
int dskready_down_time = restore_u16 ();
if (restore_u32() & 1) {
xfree(s);
s = restore_path_full();
}
_tcscpy (old, currprefs.floppyslots[num].df);
_tcsncpy(changed_prefs.floppyslots[num].df, s, MAX_DPATH);
xfree (s);
newis = changed_prefs.floppyslots[num].df[0] ? 1 : 0;
if (!(disabled & (1 << num))) {
if (!newis && old[0]) {
*currprefs.floppyslots[num].df = *changed_prefs.floppyslots[num].df = 0;
drv->dskchange = false;
} else if (newis) {
drive_insert (floppy + num, &currprefs, num, changed_prefs.floppyslots[num].df, false, false);
if (drive_empty (floppy + num)) {
if (newis && zfile_exists(old)) {
_tcscpy (changed_prefs.floppyslots[num].df, old);
drive_insert (floppy + num, &currprefs, num, changed_prefs.floppyslots[num].df, false, false);
if (drive_empty (floppy + num))
drv->dskchange = true;
} else {
drv->dskchange_time = -1;
_tcscpy(drv->newname, changed_prefs.floppyslots[num].df);
_tcscpy(currprefs.floppyslots[num].df, drv->newname);
write_log(_T("Disk image not found, faking inserted disk.\n"));
}
}
}
}
drv->mfmpos = mfmpos;
drv->prevtracklen = drv->tracklen;
drv->dskready_up_time = dskready_up_time;
drv->dskready_down_time = dskready_down_time;
reset_drive_gui(num);
return src;
}
uae_u8 *restore_disk2 (int num,uae_u8 *src)
{
drive *drv = &floppy[num];
uae_u32 m = restore_u32 ();
if (m) {
drv->floppybitcounter = restore_u16 ();
drv->tracklen = restore_u32 ();
drv->trackspeed = restore_u16 ();
drv->skipoffset = restore_u32 ();
drv->indexoffset = restore_u32 ();
drv->buffered_cyl = drv->cyl;
drv->buffered_side = side;
for (int j = 0; j < (drv->tracklen + 15) / 16; j++) {
drv->bigmfmbuf[j] = restore_u16 ();
if (m & 2)
drv->tracktiming[j] = restore_u16 ();
}
drv->revolutions = restore_u16 ();
}
return src;
}
uae_u8 *save_disk (int num, int *len, uae_u8 *dstptr, bool usepath)
{
uae_u8 *dstbak,*dst;
drive *drv = &floppy[num];
if (dstptr)
dstbak = dst = dstptr;
else
dstbak = dst = xmalloc (uae_u8, 2 + 1 + 1 + 1 + 1 + 4 + 4 + MAX_DPATH + 2 + 2 + 4 + 2 * MAX_DPATH);
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) | (side ? 16 : 0) | (drv->wrprot ? 32 : 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 (getadfcrc (drv)); /* CRC of disk image */
save_path (usepath ? currprefs.floppyslots[num].df : _T(""), SAVESTATE_PATH_FLOPPY);/* image name */
save_u16 (drv->dskready_up_time);
save_u16 (drv->dskready_down_time);
if (usepath) {
save_u32(1);
save_path_full(currprefs.floppyslots[num].df, SAVESTATE_PATH_FLOPPY);
}
*len = dst - dstbak;
return dstbak;
}
uae_u8 *save_disk2 (int num, int *len, uae_u8 *dstptr)
{
uae_u8 *dstbak,*dst;
drive *drv = &floppy[num];
int m = 0;
int size = 0;
if (drv->motoroff == 0 && drv->buffered_side >= 0 && drv->tracklen > 0) {
m = 1;
size += ((drv->tracklen + 15) * 2) / 8;
if (drv->tracktiming[0]) {
m |= 2;
size *= 2;
}
}
if (!m)
return NULL;
if (dstptr)
dstbak = dst = dstptr;
else
dstbak = dst = xmalloc (uae_u8, 4 + 2 + 4 + 2 + 4 + 4 + size + 2);
save_u32 (m);
save_u16 (drv->floppybitcounter);
save_u32 (drv->tracklen);
save_u16 (drv->trackspeed);
save_u32 (drv->skipoffset);
save_u32 (drv->indexoffset);
for (int j = 0; j < (drv->tracklen + 15) / 16; j++) {
save_u16 (drv->bigmfmbuf[j]);
if (drv->tracktiming[0])
save_u16 (drv->tracktiming[j]);
}
save_u16 (drv->revolutions);
*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 () & 0xff;
dskdmaen = restore_u8();
for (int i = 0; i < 3; i++) {
fifo[i] = restore_u16 ();
fifo_inuse[i] = restore_u8 ();
if (dskdmaen == 0)
fifo_inuse[i] = false;
}
fifo_filled = fifo_inuse[0] || fifo_inuse[1] || fifo_inuse[2];
dsklength = restore_u16 ();
return src;
}
uae_u8 *save_floppy(int *len, uae_u8 *dstptr)
{
uae_u8 *dstbak, *dst;
if (dstptr)
dstbak = dst = dstptr;
else
dstbak = dst = xmalloc (uae_u8, 100);
save_u16 (word); /* shift register */
save_u8 (bitoffset); /* dma bit offset */
save_u8 (dma_enable); /* disk sync found */
save_u8 (disk_hpos & 0xff); /* next bit read position */
save_u8 (dskdmaen); /* dma status */
for (int i = 0; i < 3; i++) {
save_u16 (fifo[i]);
save_u8 (fifo_inuse[i]);
}
save_u16 (dsklength);
*len = dst - dstbak;
return dstbak;
}
#endif /* SAVESTATE */