uae-wii/src/catweasel.c

777 lines
18 KiB
C

#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]<<shift)|(start[1]>>(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