#include "sysconfig.h" #include "sysdeps.h" #ifdef CATWEASEL #include "options.h" #include "memory.h" #include "ioport.h" #include "catweasel.h" #include "uae.h" struct catweasel_contr cwc; static int cwhsync; static int handshake; void catweasel_hsync (void) { if (cwhsync <= 0) return; cwhsync--; if (cwhsync == 0) { if (handshake) ioport_write (currprefs.catweasel_io + 0xd0, 0); handshake = 0; } } int catweasel_read_joystick (uae_u8 *dir, uae_u8 *buttons) { if (cwc.type != CATWEASEL_TYPE_MK3) return 0; *dir = ioport_read (currprefs.catweasel_io + 0xc0); *buttons = ioport_read (currprefs.catweasel_io + 0xc8); return 1; } int catweasel_read_keyboard (uae_u8 *keycode) { uae_u8 v; if (cwc.type != CATWEASEL_TYPE_MK3) return 0; v = ioport_read (currprefs.catweasel_io + 0xd4); if (!(v & 0x80)) return 0; if (handshake) return 0; *keycode = ioport_read (currprefs.catweasel_io + 0xd0); ioport_write (currprefs.catweasel_io + 0xd0, 0); handshake = 1; cwhsync = 10; return 1; } uae_u32 catweasel_do_bget (uaecptr addr) { if (cwc.type == CATWEASEL_TYPE_MK3) { if ((currprefs.catweasel_io & 3) == 0 && addr >= 0xc0 && addr <= 0xfc) return ioport_read (currprefs.catweasel_io + addr); } else { if (addr >= currprefs.catweasel_io && addr <= currprefs.catweasel_io + 8) { return ioport_read (addr & 0x3ff); } else if(addr >= 0x10000 + currprefs.catweasel_io && addr <= 0x10000 + currprefs.catweasel_io) { return ioport_read (addr & 0x3ff); } else if ((addr & 0x3ff) < 0x200 || (addr & 0x3ff) >= 0x400) { write_log("catweasel_bget @%08.8X!\n",addr); } } return 0; } void catweasel_do_bput (uaecptr addr, uae_u32 b) { if (cwc.type == CATWEASEL_TYPE_MK3) { if ((currprefs.catweasel_io & 3) == 0 && addr >= 0xc0 && addr <= 0xfc) ioport_write (currprefs.catweasel_io + addr, b); } else { if (addr >= currprefs.catweasel_io && addr <= currprefs.catweasel_io + 8) { ioport_write (addr & 0x3ff, b); } else if(addr >= 0x10000 + currprefs.catweasel_io && addr <= 0x10000 + currprefs.catweasel_io) { ioport_write (addr & 0x3ff, b); } else if ((addr & 0x3ff) < 0x200 || (addr & 0x3ff) >= 0x400) { write_log("catweasel_bput @%08.8X=%02.2X!\n",addr,b); } } } int catweasel_init (void) { if (!currprefs.catweasel_io) return 0; if (!ioport_init ()) return 0; cwc.type = currprefs.catweasel_io >= 0x400 ? CATWEASEL_TYPE_MK3 : CATWEASEL_TYPE_MK1; cwc.iobase = currprefs.catweasel_io; catweasel_init_controller (&cwc); return 1; } void catweasel_free (void) { if (!currprefs.catweasel_io) return; ioport_free (); } #define outb(v,port) ioport_write(port,v) #define inb(port) ioport_read(port) #define LONGEST_TRACK 16000 static uae_u8 mfmbuf[LONGEST_TRACK * 4]; static uae_u8 tmpmfmbuffer[LONGEST_TRACK * 2]; static int bitshiftcompare(uae_u8 *src,int bit,int len,uae_u8 *comp) { uae_u8 b; int ones,zeros,len2; ones=zeros=0; len2=len; while(len--) { b = (comp[0] << bit) | (comp[1] >> (8 - bit)); if(b != *src) return 1; if(b==0x00) zeros++; if(b==0xff) ones++; src++; comp++; } if(ones==len2||zeros==len2) return 1; return 0; } static uae_u8 *mergepieces(uae_u8 *start,int len,int bits,uae_u8 *sync) { uae_u8 *dst=tmpmfmbuffer; uae_u8 b; int size; int shift; size=len-(sync-start); memcpy(dst,sync,size); dst+=size; b=start[len]; b&=~(255>>bits); b|=start[0]>>bits; *dst++=b; shift=8-bits; while(start<=sync+2000) { *dst++=(start[0]<>(8-shift)); start++; } return tmpmfmbuffer; } #define SCANOFFSET 1 /* scanning range in bytes, -SCANOFFSET to SCANOFFSET */ #define SCANOFFSET2 20 #define SCANLENGHT 200 /* scanning length in bytes */ static uae_u8* scantrack(uae_u8 *sync1,uae_u8 *sync2,int *trackbytes,int *trackbits) { int i,bits,bytes,matched; uae_u8 *sync2bak=sync2; sync1+=SCANOFFSET2; sync2+=SCANOFFSET2; while(sync1 < sync2bak - 2*SCANOFFSET - SCANOFFSET2 - SCANLENGHT) { matched=0x7fff; for(i=0;i<2*SCANOFFSET*8;i++) { bits=i&7; bytes=-SCANOFFSET+(i>>3); if(!bitshiftcompare(sync1,bits,SCANLENGHT,sync2+bytes)) { if(matched==0x7fff) { matched=i; } else { break; } } } if(matched!=0x7fff && i>=2*SCANOFFSET*8) { bits=matched&7; bytes=-SCANOFFSET+(matched>>3); *trackbytes=sync2+bytes-sync1; *trackbits=bits; return mergepieces(sync1,*trackbytes,*trackbits,sync2bak); } sync1++; sync2++; } return 0; } static unsigned char threshtab[128]; static void codec_makethresh(int trycnt, const unsigned char *origt, unsigned char *t, int numthresh) { static unsigned char tab[10] = { 0, 0, 0, 0, -1, -2, 1, 2, -1, 1 }; if (trycnt >= sizeof (tab)) trycnt = sizeof (tab) - 1; while(numthresh--) t[numthresh] = origt[numthresh] + tab[trycnt]; } static void codec_init_threshtab(int trycnt, const unsigned char *origt) { static unsigned char old_thresholds[2] = { 0, 0 }; unsigned char t[2]; int a, i; codec_makethresh(trycnt, origt, t, 2); if(*(unsigned short*)t == *(unsigned short*)old_thresholds) return; for(i=0,a=2; i<128; i++) { if(i == t[0] || i == t[1]) a++; threshtab[i] = a; } *(unsigned short*)&old_thresholds = *(unsigned short*)t; } static __inline__ void CWSetCReg(catweasel_contr *c, unsigned char clear, unsigned char set) { c->control_register = (c->control_register & ~clear) | set; outb(c->control_register, c->io_sr); } static void CWTriggerStep(catweasel_contr *c) { CWSetCReg(c, c->crm_step, 0); CWSetCReg(c, 0, c->crm_step); } void catweasel_init_controller(catweasel_contr *c) { int i, j; if(!c->iobase) return; switch(c->type) { case CATWEASEL_TYPE_MK1: c->crm_sel0 = 1 << 5; c->crm_sel1 = 1 << 4; c->crm_mot0 = 1 << 3; c->crm_mot1 = 1 << 7; c->crm_dir = 1 << 1; c->crm_step = 1 << 0; c->srm_trk0 = 1 << 4; c->srm_dchg = 1 << 5; c->srm_writ = 1 << 1; c->io_sr = c->iobase + 2; c->io_mem = c->iobase; break; case CATWEASEL_TYPE_MK3: c->crm_sel0 = 1 << 2; c->crm_sel1 = 1 << 3; c->crm_mot0 = 1 << 1; c->crm_mot1 = 1 << 5; c->crm_dir = 1 << 4; c->crm_step = 1 << 7; c->srm_trk0 = 1 << 2; c->srm_dchg = 1 << 5; c->srm_writ = 1 << 6; c->srm_dskready = 1 << 4; c->io_sr = c->iobase + 0xe8; c->io_mem = c->iobase + 0xe0; break; default: return; } c->control_register = 255; /* select all drives, step inside */ CWSetCReg(c, c->crm_dir | c->crm_sel0 | c->crm_sel1, 0); for(i=0;i<2;i++) { c->drives[i].number = i; c->drives[i].contr = c; c->drives[i].diskindrive = 0; /* select only the respective drive, step to track 0 */ if(i == 0) { CWSetCReg(c, c->crm_sel0, c->crm_dir | c->crm_sel1); } else { CWSetCReg(c, c->crm_sel1, c->crm_dir | c->crm_sel0); } for(j = 0; j < 86 && (inb(c->io_sr) & c->srm_trk0); j++) { CWTriggerStep(c); sleep_millis(6); } if(j < 86) { c->drives[i].type = 1; c->drives[i].track = 0; } else { c->drives[i].type = 0; } } c->drives[0].sel = c->crm_sel0; c->drives[0].mot = c->crm_mot0; c->drives[1].sel = c->crm_sel1; c->drives[1].mot = c->crm_mot1; CWSetCReg(c, 0, c->crm_sel0 | c->crm_sel1); /* deselect all drives */ } void catweasel_free_controller(catweasel_contr *c) { if(!c->iobase) return; /* all motors off, deselect all drives */ CWSetCReg(c, 0, c->crm_mot0 | c->crm_mot1 | c->crm_sel0 | c->crm_sel1); } void catweasel_set_motor(catweasel_drive *d, int on) { CWSetCReg(d->contr, d->sel, 0); if (on) CWSetCReg(d->contr, d->mot, 0); else CWSetCReg(d->contr, 0, d->mot); CWSetCReg(d->contr, 0, d->sel); } int catweasel_step(catweasel_drive *d, int dir) { catweasel_contr *c = d->contr; CWSetCReg(c, d->sel, 0); if (dir > 0) CWSetCReg(c, c->crm_dir, 0); else CWSetCReg(c, 0, c->crm_dir); CWTriggerStep (c); CWSetCReg(c, 0, d->sel); d->track += dir > 0 ? 1 : -1; return 1; } int catweasel_disk_changed(catweasel_drive *d) { int ret; CWSetCReg(d->contr, d->sel, 0); ret = (inb(d->contr->io_sr) & d->contr->srm_dchg) ? 0 : 1; CWSetCReg(d->contr, 0, d->sel); return ret; } int catweasel_diskready(catweasel_drive *d) { int ret; CWSetCReg(d->contr, d->sel, 0); ret = (inb(d->contr->io_sr) & d->contr->srm_dskready) ? 0 : 1; CWSetCReg(d->contr, 0, d->sel); return ret; } int catweasel_track0(catweasel_drive *d) { int ret; CWSetCReg(d->contr, d->sel, 0); ret = (inb(d->contr->io_sr) & d->contr->srm_trk0) ? 0 : 1; CWSetCReg(d->contr, 0, d->sel); if (ret) d->track = 0; return ret; } int catweasel_write_protected(catweasel_drive *d) { int ret; CWSetCReg(d->contr, d->sel, 0); ret = !(inb(d->contr->io_sr) & 8); CWSetCReg(d->contr, 0, d->sel); return ret; } uae_u8 catweasel_read_byte(catweasel_drive *d) { return inb(d->contr->io_mem); } static const unsigned char amiga_thresholds[] = { 0x22, 0x30 }; // 27, 38 for 5.25" #define FLOPPY_WRITE_LEN 6250 #define MFMMASK 0x55555555 static uae_u32 getmfmlong (uae_u16 * mbuf) { return (uae_u32)(((*mbuf << 16) | *(mbuf + 1)) & MFMMASK); } static int drive_write_adf_amigados (uae_u16 *mbuf, uae_u16 *mend, uae_u8 *writebuffer, int track) { int i, secwritten = 0; uae_u32 odd, even, chksum, id, dlong; uae_u8 *secdata; uae_u8 secbuf[544]; char sectable[22]; int num_sectors = 11; int ec = 0; memset (sectable, 0, sizeof (sectable)); mend -= (4 + 16 + 8 + 512); while (secwritten < num_sectors) { int trackoffs; do { while (*mbuf++ != 0x4489) { if (mbuf >= mend) { ec = 1; goto err; } } } while (*mbuf++ != 0x4489); odd = getmfmlong (mbuf); even = getmfmlong (mbuf + 2); mbuf += 4; id = (odd << 1) | even; trackoffs = (id & 0xff00) >> 8; if (trackoffs > 10) { ec = 2; goto err; } chksum = odd ^ even; for (i = 0; i < 4; i++) { odd = getmfmlong (mbuf); even = getmfmlong (mbuf + 8); mbuf += 2; dlong = (odd << 1) | even; if (dlong) { ec = 6; goto err; } chksum ^= odd ^ even; } /* could check here if the label is nonstandard */ mbuf += 8; odd = getmfmlong (mbuf); even = getmfmlong (mbuf + 2); mbuf += 4; if (((odd << 1) | even) != chksum) { ec = 3; goto err; } odd = (id & 0x00ff0000) >> 16; if (odd != track) { ec = 7; goto err; } odd = getmfmlong (mbuf); even = getmfmlong (mbuf + 2); mbuf += 4; chksum = (odd << 1) | even; secdata = secbuf + 32; for (i = 0; i < 128; i++) { odd = getmfmlong (mbuf); even = getmfmlong (mbuf + 256); mbuf += 2; dlong = (odd << 1) | even; *secdata++ = dlong >> 24; *secdata++ = dlong >> 16; *secdata++ = dlong >> 8; *secdata++ = dlong; chksum ^= odd ^ even; } mbuf += 256; if (chksum) { ec = 4; goto err; } sectable[trackoffs] = 1; secwritten++; memcpy (writebuffer + trackoffs * 512, secbuf + 32, 512); } if (secwritten == 0 || secwritten < 0) { ec = 5; goto err; } return 0; err: write_log ("mfm decode error %d. secwritten=%d\n", ec, secwritten); for (i = 0; i < num_sectors; i++) write_log ("%d:%d ", i, sectable[i]); write_log ("\n"); return ec; } static void mfmcode (uae_u16 * mfm, int words) { uae_u32 lastword = 0; while (words--) { uae_u32 v = *mfm; uae_u32 lv = (lastword << 16) | v; uae_u32 nlv = 0x55555555 & ~lv; uae_u32 mfmbits = (nlv << 1) & (nlv >> 1); *mfm++ = v | mfmbits; lastword = v; } } #define FLOPPY_GAP_LEN 360 static int amigados_mfmcode (uae_u8 *src, uae_u16 *dst, int num_secs, int track) { int sec; memset (dst, 0xaa, FLOPPY_GAP_LEN * 2); for (sec = 0; sec < num_secs; sec++) { uae_u8 secbuf[544]; int i; uae_u16 *mfmbuf = dst + 544 * sec + FLOPPY_GAP_LEN; uae_u32 deven, dodd; uae_u32 hck = 0, dck = 0; secbuf[0] = secbuf[1] = 0x00; secbuf[2] = secbuf[3] = 0xa1; secbuf[4] = 0xff; secbuf[5] = track; secbuf[6] = sec; secbuf[7] = num_secs - sec; for (i = 8; i < 24; i++) secbuf[i] = 0; mfmbuf[0] = mfmbuf[1] = 0xaaaa; mfmbuf[2] = mfmbuf[3] = 0x4489; memcpy (secbuf + 32, src + sec * 512, 512); deven = ((secbuf[4] << 24) | (secbuf[5] << 16) | (secbuf[6] << 8) | (secbuf[7])); dodd = deven >> 1; deven &= 0x55555555; dodd &= 0x55555555; mfmbuf[4] = dodd >> 16; mfmbuf[5] = dodd; mfmbuf[6] = deven >> 16; mfmbuf[7] = deven; for (i = 8; i < 48; i++) mfmbuf[i] = 0xaaaa; for (i = 0; i < 512; i += 4) { deven = ((secbuf[i + 32] << 24) | (secbuf[i + 33] << 16) | (secbuf[i + 34] << 8) | (secbuf[i + 35])); dodd = deven >> 1; deven &= 0x55555555; dodd &= 0x55555555; mfmbuf[(i >> 1) + 32] = dodd >> 16; mfmbuf[(i >> 1) + 33] = dodd; mfmbuf[(i >> 1) + 256 + 32] = deven >> 16; mfmbuf[(i >> 1) + 256 + 33] = deven; } for (i = 4; i < 24; i += 2) hck ^= (mfmbuf[i] << 16) | mfmbuf[i + 1]; deven = dodd = hck; dodd >>= 1; mfmbuf[24] = dodd >> 16; mfmbuf[25] = dodd; mfmbuf[26] = deven >> 16; mfmbuf[27] = deven; for (i = 32; i < 544; i += 2) dck ^= (mfmbuf[i] << 16) | mfmbuf[i + 1]; deven = dodd = dck; dodd >>= 1; mfmbuf[28] = dodd >> 16; mfmbuf[29] = dodd; mfmbuf[30] = deven >> 16; mfmbuf[31] = deven; mfmcode (mfmbuf + 4, 544 - 4); } return (num_secs * 544 + FLOPPY_GAP_LEN) * 2 * 8; } static uae_u16 amigamfmbuffer[LONGEST_TRACK]; static uae_u8 amigabuffer[512*22]; /* search and align to 0x4489 WORDSYNC markers */ static int isamigatrack(uae_u8 *mfmdata, uae_u8 *mfmdatae, uae_u16 *mfmdst, int track) { uae_u16 *dst = amigamfmbuffer; int len; int shift, syncshift, sync,ret; uae_u32 l; uae_u16 w; sync = syncshift = shift = 0; len = (mfmdatae - mfmdata) * 8; if (len > LONGEST_TRACK * 8) len = LONGEST_TRACK * 8; while (len--) { l = (mfmdata[0] << 16) | (mfmdata[1] << 8) | (mfmdata[2] << 0); w = l >> (8 - shift); if (w == 0x4489) { sync = 1; syncshift = 0; } if (sync) { if (syncshift == 0) *dst++ = w; syncshift ++; if (syncshift == 16) syncshift = 0; } shift++; if (shift == 8) { mfmdata++; shift = 0; } } if (sync) { ret=drive_write_adf_amigados (amigamfmbuffer, dst, amigabuffer, track); if(!ret) return amigados_mfmcode (amigabuffer, mfmdst, 11, track); write_log ("decode error %d\n", ret); } else { write_log ("decode error: no sync found\n"); } return 0; } int catweasel_fillmfm (catweasel_drive *d, uae_u16 *mfm, int side, int clock, int rawmode) { int i, j, oldsync, syncs[10], synccnt, endcnt; uae_u32 tt1 = 0, tt2 = 0; uae_u8 *p1; int bytes = 0, bits = 0; static int lasttrack, trycnt; if (cwc.type == 0) return 0; if (d->contr->control_register & d->mot) return 0; if (!catweasel_read (d, side, 1, rawmode)) return 0; if(d->contr->type == CATWEASEL_TYPE_MK1) { inb(d->contr->iobase + 1); inb(d->contr->io_mem); /* ignore first byte */ } else { outb(0, d->contr->iobase + 0xe4); } catweasel_read_byte (d); if (lasttrack == d->track) trycnt++; else trycnt = 0; lasttrack = d->track; codec_init_threshtab(trycnt, amiga_thresholds); i = 0; j = 0; synccnt = 0; oldsync = -1; endcnt = 0; while (j < LONGEST_TRACK * 4) { uae_u8 b = catweasel_read_byte (d); if (b >= 250) { if (b == 255 - endcnt) { endcnt++; if (endcnt == 5) break; } else endcnt = 0; } if (rawmode) { if (b & 0x80) { if (oldsync < j) { syncs[synccnt++] = j; oldsync = j + 300; } } if (synccnt >= 3 && j > oldsync) break; } b = threshtab[b & 0x7f]; tt1 = (tt1 << b) + 1; tt2 += b; if (tt2 >= 16) { tt2 -= 16; mfmbuf[j++] = tt1 >> (tt2 + 8); mfmbuf[j++] = tt1 >> tt2; } i++; } write_log ("cyl=%d, side=%d, length %d, syncs %d\n", d->track, side, j, synccnt); if (rawmode) { if (synccnt >= 3) { p1 = scantrack (mfmbuf + syncs[1], mfmbuf + syncs[2], &bytes, &bits); if (p1) { j = 0; for (i = 0; i < bytes + 2; i+=2) { mfm[j++] = (p1[i] << 8) | p1[i + 1]; } return bytes * 8 + bits; } } } else { return isamigatrack (mfmbuf, mfmbuf + j, mfm, d->track * 2 + side); } return 0; } int catweasel_read(catweasel_drive *d, int side, int clock, int rawmode) { int iobase = d->contr->iobase; CWSetCReg(d->contr, d->sel, 0); if(d->contr->type == CATWEASEL_TYPE_MK1) { CWSetCReg(d->contr, 1<<2, (!side)<<2); /* set disk side */ inb(iobase+1); /* ra reset */ outb(clock*128, iobase+3); inb(iobase+1); inb(iobase+0); // inb(iobase+0); // outb(0, iobase+3); /* don't store index pulse */ inb(iobase+1); inb(iobase+7); /* start reading */ sleep_millis(rawmode ? 550 : 225); outb(0, iobase+1); /* stop reading, don't reset RAM pointer */ outb(128, iobase+0); /* add data end mark */ outb(128, iobase+0); inb(iobase+1); /* Reset RAM pointer */ } else { CWSetCReg(d->contr, 1<<6, (!side)<<6); /* set disk side */ outb(0, iobase + 0xe4); /* Reset memory pointer */ switch(clock) { case 0: /* 28MHz */ outb(128, iobase + 0xec); break; case 1: /* 14MHz */ outb(0, iobase + 0xec); break; } inb(iobase + 0xe0); inb(iobase + 0xe0); outb(0, iobase + 0xec); /* no IRQs, no MFM predecode */ inb(iobase + 0xe0); outb(0, iobase + 0xec); /* don't store index pulse */ outb(0, iobase + 0xe4); /* Reset memory pointer */ inb(iobase + 0xf0); /* start reading */ sleep_millis(rawmode ? 550 : 225); inb(iobase + 0xe4); /* stop reading, don't reset RAM pointer */ outb(255, iobase + 0xe0); /* add data end mark */ outb(254, iobase + 0xe0); /* add data end mark */ outb(253, iobase + 0xe0); /* add data end mark */ outb(252, iobase + 0xe0); /* add data end mark */ outb(251, iobase + 0xe0); /* add data end mark */ outb(0, iobase + 0xe4); /* Reset memory pointer */ } CWSetCReg(d->contr, 0, d->sel); return 1; } #endif