gsplus/src/iwm_35_525.h
Dagen Brock d9dde67041 uhm
2016-02-07 23:01:25 -06:00

340 lines
7.9 KiB
C

/*
GSPLUS - Advanced Apple IIGS Emulator Environment
Copyright (C) 2016 - Dagen Brock
Copyright (C) 2010 by GSport contributors
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
int
IWM_READ_ROUT (Disk *dsk, int fast_disk_emul, double dcycs)
{
Trk *trk;
double dcycs_last_read;
int pos;
int pos2;
int size;
int next_size;
int qtr_track;
int skip_nibs;
int track_len;
byte ret;
int shift;
int skip;
int cycs_this_nib;
int cycs_passed;
double dcycs_this_nib;
double dcycs_next_nib;
double dcycs_passed;
double track_dcycs;
double dtmp;
iwm.previous_write_bits = 0;
qtr_track = dsk->cur_qtr_track;
#if IWM_DISK_525
qtr_track = qtr_track & -4; /* round to nearest whole trk! */
#endif
trk = 0;
track_len = 0;
if(dsk->trks) {
trk = &(dsk->trks[qtr_track]);
track_len = trk->track_len;
}
dcycs_last_read = dsk->dcycs_last_read;
dcycs_passed = dcycs - dcycs_last_read;
cycs_passed = (int)dcycs_passed;
if(track_len == 0) {
ret = (cycs_passed & 0x7f) + 0x80;
iwm_printf("Reading c0ec, track_len 0, returning %02x\n", ret);
return ret;
}
pos = dsk->nib_pos;
if(pos >= track_len) {
/* Arm may have moved from inner 3.5 track to outer one, */
/* and so must make pos fit on smaller sized track */
pos = 0;
}
size = trk->nib_area[pos];
while(size == 0) {
pos += 2;
if(pos >= track_len) {
pos = 0;
}
size = trk->nib_area[pos];
}
cycs_this_nib = size * (2 * IWM_CYC_MULT);
dcycs_this_nib = (double)cycs_this_nib;
if(fast_disk_emul) {
cycs_passed = cycs_this_nib;
dcycs_passed = dcycs_this_nib;
/* pull a trick to make disk motor-on test pass ($bd34 RWTS) */
/* if this would be a sync byte, and we didn't just do this */
/* then don't return whole byte */
/* BUT, don't do this if g_fast_disk_unnib, since it will */
/* cause the dsk->unix routines to break */
if(size > 8 && !g_fast_disk_unnib && (g_iwm_fake_fast == 0)) {
cycs_passed = cycs_passed >> 1;
dcycs_passed = dcycs_passed * 0.5;
g_iwm_fake_fast = 1;
} else {
g_iwm_fake_fast = 0;
}
}
skip = 0;
if(cycs_passed >= (cycs_this_nib + 11)) {
/* skip some bits? */
skip = 1;
if(iwm.iwm_mode & 1) {
/* latch mode */
pos2 = pos + 2;
if(pos2 >= track_len) {
pos2 = 0;
}
next_size = trk->nib_area[pos2];
while(next_size == 0) {
pos2 += 2;
if(pos2 >= track_len) {
pos2 = 0;
}
next_size = trk->nib_area[pos2];
}
dcycs_next_nib = next_size * (2 * IWM_CYC_MULT);
if(dcycs_passed < (dcycs_this_nib + dcycs_next_nib)) {
skip = 0;
}
}
}
if(skip) {
iwm_printf("skip since cycs_passed: %f, cycs_this_nib: %f\n",
dcycs_passed, dcycs_this_nib);
track_dcycs = IWM_CYC_MULT * (track_len * 8);
if(dcycs_passed >= track_dcycs) {
dtmp = (int)(dcycs_passed / track_dcycs);
dcycs_passed = dcycs_passed -
(dtmp * track_dcycs);
dcycs_last_read += (dtmp * track_dcycs);
}
if(dcycs_passed >= track_dcycs || dcycs_passed < 0.0) {
dcycs_passed = 0.0;
}
cycs_passed = (int)dcycs_passed;
skip_nibs = ((word32)cycs_passed) >> (4 + IWM_DISK_525);
pos += skip_nibs * 2;
while(pos >= track_len) {
pos -= track_len;
}
dcycs_last_read += (skip_nibs * 16 * IWM_CYC_MULT);
dsk->dcycs_last_read = dcycs_last_read;
size = trk->nib_area[pos];
dcycs_passed = dcycs - dcycs_last_read;
if(dcycs_passed < 0.0 || dcycs_passed > 64.0) {
halt_printf("skip, last_read:%f, dcycs:%f, dcyc_p:%f\n",
dcycs_last_read, dcycs, dcycs_passed);
}
while(size == 0) {
pos += 2;
if(pos >= track_len) {
pos = 0;
}
size = trk->nib_area[pos];
}
cycs_this_nib = size * (2 * IWM_CYC_MULT);
cycs_passed = (int)dcycs_passed;
dcycs_this_nib = cycs_this_nib;
}
if(cycs_passed < cycs_this_nib) {
/* partial */
#if 0
iwm_printf("Disk partial, %f < %f, size: %d\n",
dcycs_passed, dcycs_this_nib, size);
#endif
shift = (cycs_passed) >> (1 + IWM_DISK_525);
ret = trk->nib_area[pos+1] >> (size - shift);
if(ret & 0x80) {
halt_printf("Bad shift in partial read: %02x, but "
"c_pass:%f, this_nib:%f, shift: %d, size: %d\n",
ret, dcycs_passed, dcycs_this_nib, shift, size);
}
} else {
/* whole thing */
ret = trk->nib_area[pos+1];
pos += 2;
if(pos >= track_len) {
pos = 0;
}
if(!fast_disk_emul) {
dsk->dcycs_last_read = dcycs_last_read + dcycs_this_nib;
}
}
dsk->nib_pos = pos;
if(pos < 0 || pos > track_len) {
halt_printf("I just set nib_pos: %d!\n", pos);
}
#if 0
iwm_printf("Disk read, returning: %02x\n", ret);
#endif
return ret;
}
void
IWM_WRITE_ROUT (Disk *dsk, word32 val, int fast_disk_emul, double dcycs)
{
double dcycs_last_read;
word32 bits_read;
word32 mask;
word32 prev_val;
double dcycs_this_nib;
double dcycs_passed;
double sdiff;
int prev_bits;
if((!dsk->file) || dsk->trks == 0) {
halt_printf("Tried to write to type: %d, drive: %d!\n",
IWM_DISK_525, dsk->drive, dsk->trks);
return;
}
dcycs_last_read = dsk->dcycs_last_read;
dcycs_passed = dcycs - dcycs_last_read;
prev_val = iwm.previous_write_val;
prev_bits = iwm.previous_write_bits;
mask = 0x100;
iwm_printf("Iwm write: prev: %x,%d, new:%02x\n", prev_val, prev_bits,
val);
if(IWM_DISK_525) {
/* Activate slow write emulation mode */
g_dcycs_end_emul_wr = dcycs + 64.0;
if(!g_slow_525_emul_wr) {
set_halt(HALT_EVENT);
g_slow_525_emul_wr = 1;
}
} else {
/* disable slow writes on 3.5" drives */
if(g_slow_525_emul_wr) {
set_halt(HALT_EVENT);
printf("HACK3: g_slow_525_emul_wr set to 0\n");
g_slow_525_emul_wr = 0;
}
}
if(iwm.iwm_mode & 2) {
/* async mode = 3.5" default */
bits_read = 8;
} else {
/* sync mode, 5.25" drives */
bits_read = ((int)dcycs_passed) >> (1 + IWM_DISK_525);
if(bits_read < 8) {
bits_read = 8;
}
}
if(fast_disk_emul) {
bits_read = 8;
}
dcycs_this_nib = bits_read * (2 * IWM_CYC_MULT);
if(fast_disk_emul) {
dcycs_passed = dcycs_this_nib;
}
if(prev_bits > 0) {
while((prev_val & 0x80) == 0 && bits_read > 0) {
/* previous byte needs some bits */
mask = mask >> 1;
prev_val = (prev_val << 1) + ((val & mask) !=0);
prev_bits++;
bits_read--;
}
}
val = val & (mask - 1);
if(prev_bits) {
/* force out prev_val if it had many bits before */
/* this prevents writes of 0 from messing us up */
if(((prev_val & 0x80) == 0) && (prev_bits < 10)) {
/* special case: we still don't have enough to go */
iwm_printf("iwm_write: zip2: %02x, %d, left:%02x,%d\n",
prev_val, prev_bits, val,bits_read);
val = prev_val;
bits_read = prev_bits;
} else {
iwm_printf("iwm_write: prev: %02x, %d, left:%02x, %d\n",
prev_val, prev_bits, val, bits_read);
disk_nib_out(dsk, prev_val, prev_bits);
}
} else if(val & 0x80) {
iwm_printf("iwm_write: new: %02x, %d\n", val,bits_read);
disk_nib_out(dsk, val, bits_read);
bits_read = 0;
} else {
iwm_printf("iwm_write: zip: %02x, %d, left:%02x,%d\n",
prev_val, prev_bits, val,bits_read);
}
iwm.previous_write_val = val;
iwm.previous_write_bits = bits_read;
if(bits_read < 0) {
halt_printf("iwm, bits_rd:%d, val:%08x, prev:%02x, prevb:%d\n",
bits_read, val, prev_val, prev_bits);
}
sdiff = dcycs - dcycs_last_read;
if(sdiff < (dcycs_this_nib) || (sdiff > (2*dcycs_this_nib)) ) {
dsk->dcycs_last_read = dcycs;
} else {
dsk->dcycs_last_read = dcycs_last_read + dcycs_this_nib;
}
}