472 lines
15 KiB
C
472 lines
15 KiB
C
/* $Id: s100_fif.c 1995 2008-07-15 03:59:13Z hharte $
|
|
|
|
IMSAI FIF Disk Controller by Ernie Price
|
|
|
|
Based on altairz80_dsk.c, Copyright (c) 2002-2011, Peter Schorn
|
|
|
|
Plug-n-Play added by Howard M. Harte
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a
|
|
copy of this software and associated documentation files (the "Software"),
|
|
to deal in the Software without restriction, including without limitation
|
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
and/or sell copies of the Software, and to permit persons to whom the
|
|
Software is furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
Except as contained in this notice, the name of Peter Schorn shall not
|
|
be used in advertising or otherwise to promote the sale, use or other dealings
|
|
in this Software without prior written authorization from Peter Schorn.
|
|
|
|
*/
|
|
|
|
#include "altairz80_defs.h"
|
|
|
|
#define UNIT_V_DSK_WLK (UNIT_V_UF + 0) /* write locked */
|
|
#define UNIT_DSK_WLK (1 << UNIT_V_DSK_WLK)
|
|
#define UNIT_V_DSK_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */
|
|
#define UNIT_DSK_VERBOSE (1 << UNIT_V_DSK_VERBOSE)
|
|
#define DSK_SECTSIZE 137 /* size of sector */
|
|
#define DSK_SECT 32 /* sectors per track */
|
|
#define MAX_TRACKS 254 /* number of tracks, original Altair has 77 tracks only */
|
|
#define DSK_TRACSIZE (DSK_SECTSIZE * DSK_SECT)
|
|
#define MAX_DSK_SIZE (DSK_TRACSIZE * MAX_TRACKS)
|
|
|
|
static t_stat fif_reset(DEVICE *dptr);
|
|
static t_stat fif_set_verbose(UNIT *uptr, int32 value, char *cptr, void *desc);
|
|
static int32 fif_io(const int32 port, const int32 io, const int32 data);
|
|
|
|
extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc);
|
|
extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc);
|
|
extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type,
|
|
int32 (*routine)(const int32, const int32, const int32), uint8 unmap);
|
|
extern uint8 GetBYTEWrapper(const uint32 Addr);
|
|
extern void PutBYTEWrapper(const uint32 Addr, const uint32 Value);
|
|
|
|
extern uint32 PCX;
|
|
|
|
/* global data on status */
|
|
|
|
/* currently selected drive (values are 0 .. NUM_OF_DSK)
|
|
current_disk < NUM_OF_DSK implies that the corresponding disk is attached to a file */
|
|
static int32 current_disk = NUM_OF_DSK;
|
|
static int32 warnLevelDSK = 3;
|
|
static int32 warnLock [NUM_OF_DSK] = {0, 0, 0, 0, 0, 0, 0, 0};
|
|
static int32 warnAttached [NUM_OF_DSK] = {0, 0, 0, 0, 0, 0, 0, 0};
|
|
static int32 warnDSK11 = 0;
|
|
|
|
/* 88DSK Standard I/O Data Structures */
|
|
|
|
typedef struct {
|
|
PNP_INFO pnp; /* Plug and Play */
|
|
} FIF_INFO;
|
|
|
|
FIF_INFO fif_info_data = { { 0x0000, 0, 0xFD, 1 } };
|
|
FIF_INFO *fif_info = &fif_info_data;
|
|
|
|
static UNIT fif_unit[] = {
|
|
{ UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) },
|
|
{ UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) },
|
|
{ UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) },
|
|
{ UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) },
|
|
{ UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) },
|
|
{ UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) },
|
|
{ UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) },
|
|
{ UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) }
|
|
};
|
|
|
|
static REG fif_reg[] = {
|
|
{ DRDATA (DISK, current_disk, 4) },
|
|
{ DRDATA (DSKWL, warnLevelDSK, 32) },
|
|
{ BRDATA (WARNLOCK, warnLock, 10, 32, NUM_OF_DSK), REG_CIRC + REG_RO },
|
|
{ BRDATA (WARNATTACHED, warnAttached, 10, 32, NUM_OF_DSK), REG_CIRC + REG_RO },
|
|
{ DRDATA (WARNDSK11, warnDSK11, 4), REG_RO },
|
|
{ NULL }
|
|
};
|
|
|
|
static MTAB fif_mod[] = {
|
|
{ MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL },
|
|
{ UNIT_DSK_WLK, 0, "WRTENB", "WRTENB", NULL },
|
|
{ UNIT_DSK_WLK, UNIT_DSK_WLK, "WRTLCK", "WRTLCK", NULL },
|
|
/* quiet, no warning messages */
|
|
{ UNIT_DSK_VERBOSE, 0, "QUIET", "QUIET", NULL },
|
|
/* verbose, show warning messages */
|
|
{ UNIT_DSK_VERBOSE, UNIT_DSK_VERBOSE, "VERBOSE", "VERBOSE", &fif_set_verbose },
|
|
{ 0 }
|
|
};
|
|
|
|
DEVICE fif_dev = {
|
|
"FIF", fif_unit, fif_reg, fif_mod,
|
|
8, 10, 31, 1, 8, 8,
|
|
NULL, NULL, &fif_reset,
|
|
NULL, NULL, NULL,
|
|
&fif_info_data, (DEV_DISABLE | DEV_DIS), 0,
|
|
NULL, NULL, "IMSAI FIF"
|
|
};
|
|
|
|
static void resetDSKWarningFlags(void) {
|
|
int32 i;
|
|
for (i = 0; i < NUM_OF_DSK; i++) {
|
|
warnLock[i] = 0;
|
|
warnAttached[i] = 0;
|
|
}
|
|
warnDSK11 = 0;
|
|
}
|
|
|
|
static t_stat fif_set_verbose(UNIT *uptr, int32 value, char *cptr, void *desc) {
|
|
resetDSKWarningFlags();
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* returns TRUE iff there exists a disk with VERBOSE */
|
|
static int32 hasVerbose(void) {
|
|
int32 i;
|
|
for (i = 0; i < NUM_OF_DSK; i++) {
|
|
if (((fif_dev.units + i) -> flags) & UNIT_DSK_VERBOSE) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/* service routines to handle simulator functions */
|
|
|
|
/* Reset routine */
|
|
static t_stat fif_reset(DEVICE *dptr)
|
|
{
|
|
PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt;
|
|
|
|
resetDSKWarningFlags();
|
|
current_disk = NUM_OF_DSK;
|
|
|
|
if(dptr->flags & DEV_DIS) {
|
|
sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &fif_io, TRUE);
|
|
} else {
|
|
/* Connect HDSK at base address */
|
|
if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &fif_io, FALSE) != 0) {
|
|
printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->mem_base);
|
|
dptr->flags |= DEV_DIS;
|
|
return SCPE_ARG;
|
|
}
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
typedef struct desc_t
|
|
{
|
|
uint8
|
|
cmd_unit, /* (cmd << 4) | unit : 1 = A: */
|
|
result, /* result: 0 == busy, 1 = normal completion, */
|
|
nn, /* number of secs ? */
|
|
track, /* track */
|
|
sector, /* sector */
|
|
addr_l, /* low (transfer address) */
|
|
addr_h; /* high (transfer address) */
|
|
} desc_t;
|
|
|
|
static desc_t mydesc;
|
|
|
|
enum {NONE, WRITE_SEC, READ_SEC, FMT_TRACK};
|
|
|
|
#define SEC_SZ 128
|
|
#define SPT 26
|
|
#define UMASK 0xf
|
|
|
|
static uint8 blanksec[SEC_SZ];
|
|
/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
|
|
static const uint8 utrans[] = {0,1,2,0,3,0,0,0,4,0,0,0,0,0,0,0};
|
|
|
|
/**************************************************
|
|
|
|
Translate an IMSAI FIF disk request into an access into the harddrive file
|
|
|
|
*/
|
|
static int DoDiskOperation(desc_t *dsc, uint8 val)
|
|
{
|
|
int32 current_disk_flags;
|
|
int kt,
|
|
addr;
|
|
FILE *cpx;
|
|
UNIT *uptr;
|
|
int32 rtn;
|
|
|
|
#if 0
|
|
printf("%02x %02x %02x %02x %02x %02x %02x %02x \n",
|
|
val,
|
|
dsc->cmd_unit,
|
|
dsc->result,
|
|
dsc->nn,
|
|
dsc->track,
|
|
dsc->sector,
|
|
dsc->addr_l,
|
|
dsc->addr_h);
|
|
#endif
|
|
|
|
current_disk = (utrans[dsc->cmd_unit & UMASK]) - 1; /* 0 <= current_disk < NUM_OF_DSK */
|
|
if (current_disk >= NUM_OF_DSK) {
|
|
if (hasVerbose() && (warnDSK11 < warnLevelDSK)) {
|
|
warnDSK11++;
|
|
/*03*/ printf("FIF%i: " ADDRESS_FORMAT " Attempt disk io on illegal disk %d - ignored." NLP, current_disk, PCX, current_disk);
|
|
}
|
|
return 0; /* no drive selected - can do nothing */
|
|
}
|
|
current_disk_flags = (fif_dev.units + current_disk) -> flags;
|
|
if ((current_disk_flags & UNIT_ATT) == 0) { /* nothing attached? */
|
|
if ( (current_disk_flags & UNIT_DSK_VERBOSE) && (warnAttached[current_disk] < warnLevelDSK) ) {
|
|
warnAttached[current_disk]++;
|
|
/*02*/printf("FIF%i: " ADDRESS_FORMAT " Attempt to select unattached FIF%d - ignored." NLP, current_disk, PCX, current_disk);
|
|
}
|
|
current_disk = NUM_OF_DSK;
|
|
return 2;
|
|
}
|
|
|
|
uptr = fif_dev.units + current_disk;
|
|
cpx = uptr->fileref;
|
|
|
|
/* decode request: */
|
|
switch (dsc->cmd_unit >> 4) {
|
|
case FMT_TRACK:
|
|
/*printf("%c", dsc->track % 10 ? '*' : '0' + + dsc->track / 10); */
|
|
/*Sleep(250); */
|
|
memset(blanksec, 0, SEC_SZ);
|
|
addr = dsc->track * SPT;
|
|
sim_fseek(cpx, addr * SEC_SZ, SEEK_SET);
|
|
|
|
/* write a track worth of sectors */
|
|
for (kt=0; kt < SPT; kt++) {
|
|
sim_fwrite(blanksec, 1, sizeof(blanksec), cpx);
|
|
}
|
|
break;
|
|
|
|
case READ_SEC:
|
|
addr = (dsc->track * SPT) + dsc->sector - 1;
|
|
sim_fseek(cpx, addr * SEC_SZ, SEEK_SET);
|
|
rtn = sim_fread(blanksec, 1, SEC_SZ, cpx);
|
|
if ( (rtn != SEC_SZ) && (current_disk_flags & UNIT_DSK_VERBOSE) &&
|
|
(warnAttached[current_disk] < warnLevelDSK) ) {
|
|
warnAttached[current_disk]++;
|
|
printf("FIF%i: " ADDRESS_FORMAT " sim_fread error." NLP, current_disk, PCX);
|
|
}
|
|
addr = dsc->addr_l + (dsc->addr_h << 8); /* no assumption on endianness */
|
|
for (kt = 0; kt < SEC_SZ; kt++) {
|
|
PutBYTEWrapper(addr++, blanksec[kt]);
|
|
}
|
|
break;
|
|
|
|
case WRITE_SEC:
|
|
addr = (dsc->track * SPT) + dsc->sector - 1;
|
|
sim_fseek(cpx, addr * SEC_SZ, SEEK_SET);
|
|
addr = dsc->addr_l + (dsc->addr_h << 8); /* no assumption on endianness */
|
|
for (kt = 0; kt < SEC_SZ; kt++) {
|
|
blanksec[kt] = GetBYTEWrapper(addr++);
|
|
}
|
|
sim_fwrite(blanksec, 1, SEC_SZ, cpx);
|
|
break;
|
|
|
|
default:
|
|
;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/**********************************************************************
|
|
|
|
Copy the disk descriptor from target RAM
|
|
|
|
*/
|
|
static void getdesc(uint16 addr) {
|
|
uint32 x;
|
|
uint8 *p1 = (uint8*)&mydesc;
|
|
|
|
for (x = 0; x < sizeof(mydesc); x++) {
|
|
*p1++ = GetBYTEWrapper(addr++);
|
|
}
|
|
}
|
|
|
|
/**********************************************************************
|
|
|
|
handle the IMSAI FIF floppy controller
|
|
|
|
*/
|
|
static int32 fif_io(const int32 port, const int32 io, const int32 data) {
|
|
|
|
static int32 fdstate = 0; /* chan 0xfd state */
|
|
static int32 desc;
|
|
static uint16 fdAdr[16]; /* disk descriptor address in 8080/z80 RAM */
|
|
|
|
/* cmd | desc# */
|
|
/* cmd == 0x00 do operation */
|
|
/* cmd == 0x10 next 2 transfers are desc address */
|
|
/* desc# is one of 16 0x0 - 0xf */
|
|
|
|
if (!io) {
|
|
return 0;
|
|
}
|
|
|
|
switch (fdstate) {
|
|
case 0:
|
|
desc = data & 0xf;
|
|
if ((data & 0x10) != 0) { /* prefix 0x10 */
|
|
fdstate++; /* means desc address is next 2 out (fd),a */
|
|
}
|
|
else { /* do what descriptor says */
|
|
getdesc(fdAdr[desc]);
|
|
PutBYTEWrapper(fdAdr[desc] + 1,
|
|
(uint8)DoDiskOperation(&mydesc, (uint8)data));
|
|
}
|
|
break;
|
|
|
|
case 1:
|
|
/*printf("D1 %02x %02x\n", desc, data); */
|
|
fdAdr[desc] = data; /* LSB of descriptor address */
|
|
fdstate++;
|
|
break;
|
|
|
|
case 2:
|
|
/*printf("D2 %02x %02x\n", desc, data); */
|
|
fdAdr[desc] |= data << 8; /* MSB of descriptor address */
|
|
fdstate = 0;
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#define ERNIES_FTP 0
|
|
#if ERNIES_FTP
|
|
|
|
#define WRK_BUF_SZ 150
|
|
#define FCB_SIZE 32
|
|
#define NAME_LTH 8
|
|
#define EXT_LTH 3
|
|
|
|
|
|
/**************************************************
|
|
*/
|
|
static void xfero(int32 addr, char *src, int32 lth)
|
|
{
|
|
while (lth--) {
|
|
PutBYTEWrapper(addr++, *src++);
|
|
}
|
|
}
|
|
|
|
/**************************************************
|
|
*/
|
|
static void xferi(int32 addr, char *dst, int32 lth)
|
|
{
|
|
while (lth--) {
|
|
*dst++ = GetBYTEWrapper(addr++);
|
|
}
|
|
}
|
|
|
|
#if !defined (_WIN32)
|
|
static void strupr(char *fn) {
|
|
while (*fn) {
|
|
if (('a' <= *fn) && (*fn <= 'z'))
|
|
*fn -= 'a' - 'A';
|
|
fn++;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/**************************************************
|
|
*/
|
|
static void initfcb(char *fcb, char *fn, int32 flg)
|
|
{
|
|
char *p1 = fcb;
|
|
|
|
if (flg)
|
|
{
|
|
strupr(fn);
|
|
}
|
|
memset (fcb, 0 , FCB_SIZE);
|
|
memset (fcb + 1, ' ', NAME_LTH + EXT_LTH);
|
|
p1++;
|
|
while (*fn && (*fn != '.'))
|
|
{
|
|
*p1++ = *fn++;
|
|
}
|
|
if (*fn == '.')
|
|
{
|
|
fn++;
|
|
}
|
|
p1 = fcb + NAME_LTH + 1;
|
|
while (*fn && (*fn != '.'))
|
|
{
|
|
*p1++ = *fn++;
|
|
}
|
|
}
|
|
|
|
/**************************************************
|
|
|
|
FTP interface - most of the work is done here
|
|
The IMDOS/CPM application only does minimal work
|
|
|
|
*/
|
|
|
|
char message[WRK_BUF_SZ];
|
|
char temp [WRK_BUF_SZ];
|
|
FILE * myfile;
|
|
|
|
uint8 FTP(int32 BC, int32 DE)
|
|
{
|
|
char *p1, *p2;
|
|
int32 retval;
|
|
|
|
xferi(DE, temp, SEC_SZ);
|
|
p1 = temp;
|
|
switch (BC & 0x7f)
|
|
{
|
|
case 0:
|
|
memcpy(message, p1 + 2, *(p1 + 1));
|
|
*(message + *(p1 + 1)) = 0;
|
|
p2 = strtok(message, " \t");
|
|
if (!strcmp(p2, "get"))
|
|
{
|
|
p2 = strtok(NULL, " \t");
|
|
if (myfile = fopen(p2, "rb"))
|
|
{
|
|
initfcb(temp, p2, 1);
|
|
xfero(DE + 2, temp, 32);
|
|
retval = 0;
|
|
break;
|
|
}
|
|
}
|
|
if (!strcmp(p2, "era"))
|
|
{
|
|
p2 = strtok(NULL, " \t");
|
|
initfcb(temp, p2, 0);
|
|
xfero(DE + 2, temp, 32);
|
|
retval = 1;
|
|
break;
|
|
}
|
|
retval = 0xff;
|
|
break;
|
|
|
|
case 20:
|
|
memset(temp, 0x1a, SEC_SZ);
|
|
retval = sim_fread(temp, 1, SEC_SZ, myfile) ? 0 : 1;
|
|
xfero( DE, temp, SEC_SZ);
|
|
if (retval)
|
|
{
|
|
fclose(myfile);
|
|
}
|
|
break;
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
#endif /* ERNIES_FTP */
|
|
|
|
/* end of the source */
|
|
|
|
|
|
|