1 /*  $Id: s100_fif.c 1995 2008-07-15 03:59:13Z hharte $
2 
3     IMSAI FIF Disk Controller by Ernie Price
4 
5     Based on altairz80_dsk.c, Copyright (c) 2002-2011, Peter Schorn
6 
7     Plug-n-Play added by Howard M. Harte
8 
9     Permission is hereby granted, free of charge, to any person obtaining a
10     copy of this software and associated documentation files (the "Software"),
11     to deal in the Software without restriction, including without limitation
12     the rights to use, copy, modify, merge, publish, distribute, sublicense,
13     and/or sell copies of the Software, and to permit persons to whom the
14     Software is furnished to do so, subject to the following conditions:
15 
16     The above copyright notice and this permission notice shall be included in
17     all copies or substantial portions of the Software.
18 
19     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22     PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
23     IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24     CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 
26     Except as contained in this notice, the name of Peter Schorn shall not
27     be used in advertising or otherwise to promote the sale, use or other dealings
28     in this Software without prior written authorization from Peter Schorn.
29 
30 */
31 
32 #include "altairz80_defs.h"
33 
34 #define UNIT_V_DSK_WLK      (UNIT_V_UF + 0) /* write locked                             */
35 #define UNIT_DSK_WLK        (1 << UNIT_V_DSK_WLK)
36 #define UNIT_V_DSK_VERBOSE  (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages   */
37 #define UNIT_DSK_VERBOSE    (1 << UNIT_V_DSK_VERBOSE)
38 #define DSK_SECTSIZE        137 /* size of sector                                       */
39 #define DSK_SECT            32  /* sectors per track                                    */
40 #define MAX_TRACKS          254 /* number of tracks, original Altair has 77 tracks only */
41 #define DSK_TRACSIZE        (DSK_SECTSIZE * DSK_SECT)
42 #define MAX_DSK_SIZE        (DSK_TRACSIZE * MAX_TRACKS)
43 
44 static t_stat fif_reset(DEVICE *dptr);
45 static t_stat fif_set_verbose(UNIT *uptr, int32 value, char *cptr, void *desc);
46 static int32 fif_io(const int32 port, const int32 io, const int32 data);
47 
48 extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc);
49 extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc);
50 extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type,
51         int32 (*routine)(const int32, const int32, const int32), uint8 unmap);
52 extern uint8 GetBYTEWrapper(const uint32 Addr);
53 extern void  PutBYTEWrapper(const uint32 Addr, const uint32 Value);
54 
55 extern uint32 PCX;
56 
57 /* global data on status */
58 
59 /* currently selected drive (values are 0 .. NUM_OF_DSK)
60    current_disk < NUM_OF_DSK implies that the corresponding disk is attached to a file */
61 static int32 current_disk                   = NUM_OF_DSK;
62 static int32 warnLevelDSK                   = 3;
63 static int32 warnLock       [NUM_OF_DSK]    = {0, 0, 0, 0, 0, 0, 0, 0};
64 static int32 warnAttached   [NUM_OF_DSK]    = {0, 0, 0, 0, 0, 0, 0, 0};
65 static int32 warnDSK11                      = 0;
66 
67 /* 88DSK Standard I/O Data Structures */
68 
69 typedef struct {
70     PNP_INFO    pnp;    /* Plug and Play */
71 } FIF_INFO;
72 
73 FIF_INFO fif_info_data = { { 0x0000, 0, 0xFD, 1 } };
74 FIF_INFO *fif_info = &fif_info_data;
75 
76 static UNIT fif_unit[] = {
77     { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) },
78     { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) },
79     { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) },
80     { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) },
81     { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) },
82     { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) },
83     { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) },
84     { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) }
85 };
86 
87 static REG fif_reg[] = {
88     { DRDATA (DISK,         current_disk,   4)                                          },
89     { DRDATA (DSKWL,        warnLevelDSK, 32)                                           },
90     { BRDATA (WARNLOCK,     warnLock,       10, 32, NUM_OF_DSK),    REG_CIRC + REG_RO   },
91     { BRDATA (WARNATTACHED, warnAttached,   10, 32, NUM_OF_DSK),    REG_CIRC + REG_RO   },
92     { DRDATA (WARNDSK11,    warnDSK11, 4),                          REG_RO              },
93     { NULL }
94 };
95 
96 static MTAB fif_mod[] = {
97     { MTAB_XTD|MTAB_VDV, 0,                 "IOBASE",   "IOBASE",   &set_iobase, &show_iobase, NULL },
98     { UNIT_DSK_WLK,     0,                  "WRTENB",   "WRTENB",   NULL },
99     { UNIT_DSK_WLK,     UNIT_DSK_WLK,       "WRTLCK",   "WRTLCK",   NULL },
100     /* quiet, no warning messages       */
101     { UNIT_DSK_VERBOSE, 0,                  "QUIET",    "QUIET",    NULL },
102     /* verbose, show warning messages   */
103     { UNIT_DSK_VERBOSE, UNIT_DSK_VERBOSE,   "VERBOSE",  "VERBOSE",  &fif_set_verbose },
104     { 0 }
105 };
106 
107 DEVICE fif_dev = {
108     "FIF", fif_unit, fif_reg, fif_mod,
109     8, 10, 31, 1, 8, 8,
110     NULL, NULL, &fif_reset,
111     NULL, NULL, NULL,
112     &fif_info_data, (DEV_DISABLE | DEV_DIS), 0,
113     NULL, NULL, "IMSAI FIF"
114 };
115 
resetDSKWarningFlags(void)116 static void resetDSKWarningFlags(void) {
117     int32 i;
118     for (i = 0; i < NUM_OF_DSK; i++) {
119         warnLock[i]         = 0;
120         warnAttached[i]     = 0;
121     }
122     warnDSK11 = 0;
123 }
124 
fif_set_verbose(UNIT * uptr,int32 value,char * cptr,void * desc)125 static t_stat fif_set_verbose(UNIT *uptr, int32 value, char *cptr, void *desc) {
126     resetDSKWarningFlags();
127     return SCPE_OK;
128 }
129 
130 /* returns TRUE iff there exists a disk with VERBOSE */
hasVerbose(void)131 static int32 hasVerbose(void) {
132     int32 i;
133     for (i = 0; i < NUM_OF_DSK; i++) {
134         if (((fif_dev.units + i) -> flags) & UNIT_DSK_VERBOSE) {
135             return TRUE;
136         }
137     }
138     return FALSE;
139 }
140 
141 /* service routines to handle simulator functions */
142 
143 /* Reset routine */
fif_reset(DEVICE * dptr)144 static t_stat fif_reset(DEVICE *dptr)
145 {
146     PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt;
147 
148     resetDSKWarningFlags();
149     current_disk = NUM_OF_DSK;
150 
151     if(dptr->flags & DEV_DIS) {
152         sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &fif_io, TRUE);
153     } else {
154         /* Connect HDSK at base address */
155         if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &fif_io, FALSE) != 0) {
156             printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->mem_base);
157             dptr->flags |= DEV_DIS;
158             return SCPE_ARG;
159         }
160     }
161     return SCPE_OK;
162 }
163 
164 typedef struct desc_t
165 {
166     uint8
167         cmd_unit,   /* (cmd << 4) | unit : 1 = A: */
168         result,     /* result: 0 == busy, 1 = normal completion, */
169         nn,         /* number of secs ? */
170         track,      /* track */
171         sector,     /* sector */
172         addr_l,     /* low  (transfer address) */
173         addr_h;     /* high (transfer address) */
174 } desc_t;
175 
176 static desc_t mydesc;
177 
178 enum {NONE, WRITE_SEC, READ_SEC, FMT_TRACK};
179 
180 #define SEC_SZ      128
181 #define SPT         26
182 #define UMASK       0xf
183 
184 static uint8 blanksec[SEC_SZ];
185 /*                      0 1 2 3 4 5 6 7 8 9 a b c d e f */
186 static const uint8 utrans[] = {0,1,2,0,3,0,0,0,4,0,0,0,0,0,0,0};
187 
188 /**************************************************
189 
190     Translate an IMSAI FIF disk request into an access into the harddrive file
191 
192 */
DoDiskOperation(desc_t * dsc,uint8 val)193 static int DoDiskOperation(desc_t *dsc, uint8 val)
194 {
195     int32   current_disk_flags;
196     int     kt,
197             addr;
198     FILE    *cpx;
199     UNIT    *uptr;
200     int32   rtn;
201 
202 #if 0
203     printf("%02x %02x %02x %02x %02x %02x %02x %02x \n",
204         val,
205         dsc->cmd_unit,
206         dsc->result,
207         dsc->nn,
208         dsc->track,
209         dsc->sector,
210         dsc->addr_l,
211         dsc->addr_h);
212 #endif
213 
214     current_disk = (utrans[dsc->cmd_unit & UMASK]) - 1; /* 0 <= current_disk < NUM_OF_DSK */
215     if (current_disk >= NUM_OF_DSK) {
216         if (hasVerbose() && (warnDSK11 < warnLevelDSK)) {
217             warnDSK11++;
218 /*03*/      printf("FIF%i: " ADDRESS_FORMAT " Attempt disk io on illegal disk %d - ignored." NLP, current_disk, PCX, current_disk);
219         }
220         return 0;               /* no drive selected - can do nothing */
221     }
222     current_disk_flags = (fif_dev.units + current_disk) -> flags;
223     if ((current_disk_flags & UNIT_ATT) == 0) { /* nothing attached? */
224         if ( (current_disk_flags & UNIT_DSK_VERBOSE) && (warnAttached[current_disk] < warnLevelDSK) ) {
225             warnAttached[current_disk]++;
226 /*02*/printf("FIF%i: " ADDRESS_FORMAT " Attempt to select unattached FIF%d - ignored." NLP, current_disk, PCX, current_disk);
227         }
228         current_disk = NUM_OF_DSK;
229         return 2;
230     }
231 
232     uptr = fif_dev.units + current_disk;
233     cpx = uptr->fileref;
234 
235     /* decode request: */
236     switch (dsc->cmd_unit >> 4) {
237         case FMT_TRACK:
238             /*printf("%c", dsc->track % 10 ? '*' : '0' + + dsc->track / 10); */
239             /*Sleep(250); */
240             memset(blanksec, 0, SEC_SZ);
241             addr = dsc->track * SPT;
242             sim_fseek(cpx, addr * SEC_SZ, SEEK_SET);
243 
244             /* write a track worth of sectors */
245             for (kt=0; kt < SPT; kt++) {
246                 sim_fwrite(blanksec, 1, sizeof(blanksec), cpx);
247             }
248             break;
249 
250         case READ_SEC:
251             addr = (dsc->track * SPT) + dsc->sector - 1;
252             sim_fseek(cpx, addr * SEC_SZ, SEEK_SET);
253             rtn = sim_fread(blanksec, 1, SEC_SZ, cpx);
254             if ( (rtn != SEC_SZ) && (current_disk_flags & UNIT_DSK_VERBOSE) &&
255                 (warnAttached[current_disk] < warnLevelDSK) ) {
256                 warnAttached[current_disk]++;
257                 printf("FIF%i: " ADDRESS_FORMAT " sim_fread error." NLP, current_disk, PCX);
258             }
259             addr = dsc->addr_l + (dsc->addr_h << 8); /* no assumption on endianness */
260             for (kt = 0; kt < SEC_SZ; kt++) {
261                 PutBYTEWrapper(addr++, blanksec[kt]);
262             }
263             break;
264 
265         case WRITE_SEC:
266             addr = (dsc->track * SPT) + dsc->sector - 1;
267             sim_fseek(cpx, addr * SEC_SZ, SEEK_SET);
268             addr = dsc->addr_l + (dsc->addr_h << 8); /* no assumption on endianness */
269             for (kt = 0; kt < SEC_SZ; kt++) {
270                 blanksec[kt] = GetBYTEWrapper(addr++);
271             }
272             sim_fwrite(blanksec, 1, SEC_SZ, cpx);
273             break;
274 
275         default:
276             ;
277     }
278     return 1;
279 }
280 
281 /**********************************************************************
282 
283     Copy the disk descriptor from target RAM
284 
285 */
getdesc(uint16 addr)286 static void getdesc(uint16 addr) {
287     uint32 x;
288     uint8 *p1 = (uint8*)&mydesc;
289 
290     for (x = 0; x < sizeof(mydesc); x++) {
291         *p1++ = GetBYTEWrapper(addr++);
292     }
293 }
294 
295 /**********************************************************************
296 
297     handle the IMSAI FIF floppy controller
298 
299 */
fif_io(const int32 port,const int32 io,const int32 data)300 static int32 fif_io(const int32 port, const int32 io, const int32 data) {
301 
302     static int32    fdstate = 0;    /* chan 0xfd state */
303     static int32    desc;
304     static uint16   fdAdr[16];      /* disk descriptor address in 8080/z80 RAM */
305 
306     /* cmd | desc# */
307     /*    cmd == 0x00 do operation */
308     /*    cmd == 0x10 next 2 transfers are desc address */
309     /* desc# is one of 16 0x0 - 0xf */
310 
311     if (!io) {
312         return 0;
313     }
314 
315     switch (fdstate) {
316         case 0:
317             desc = data & 0xf;
318             if ((data & 0x10) != 0) { /* prefix 0x10 */
319                 fdstate++;          /* means desc address is next 2 out (fd),a */
320             }
321             else { /* do what descriptor says */
322                 getdesc(fdAdr[desc]);
323                 PutBYTEWrapper(fdAdr[desc] + 1,
324                     (uint8)DoDiskOperation(&mydesc, (uint8)data));
325             }
326             break;
327 
328         case 1:
329             /*printf("D1 %02x %02x\n", desc, data); */
330             fdAdr[desc] = data;        /* LSB of descriptor address */
331             fdstate++;
332             break;
333 
334         case 2:
335             /*printf("D2 %02x %02x\n", desc, data); */
336             fdAdr[desc] |= data << 8;  /* MSB of descriptor address */
337             fdstate = 0;
338             break;
339     }
340     return 0;
341 }
342 
343 #define ERNIES_FTP 0
344 #if ERNIES_FTP
345 
346 #define WRK_BUF_SZ  150
347 #define FCB_SIZE    32
348 #define NAME_LTH    8
349 #define EXT_LTH     3
350 
351 
352 /**************************************************
353 */
xfero(int32 addr,char * src,int32 lth)354 static void xfero(int32 addr, char *src, int32 lth)
355 {
356     while (lth--) {
357         PutBYTEWrapper(addr++, *src++);
358     }
359 }
360 
361 /**************************************************
362 */
xferi(int32 addr,char * dst,int32 lth)363 static void xferi(int32 addr, char *dst, int32 lth)
364 {
365     while (lth--) {
366         *dst++ = GetBYTEWrapper(addr++);
367     }
368 }
369 
370 #if !defined (_WIN32)
strupr(char * fn)371 static void strupr(char *fn) {
372     while (*fn) {
373         if (('a' <= *fn) && (*fn <= 'z'))
374             *fn -= 'a' - 'A';
375         fn++;
376     }
377 }
378 #endif
379 
380 /**************************************************
381 */
initfcb(char * fcb,char * fn,int32 flg)382 static void initfcb(char *fcb, char *fn, int32 flg)
383 {
384     char *p1 = fcb;
385 
386     if (flg)
387     {
388         strupr(fn);
389     }
390     memset (fcb, 0 , FCB_SIZE);
391     memset (fcb + 1, ' ', NAME_LTH + EXT_LTH);
392     p1++;
393     while (*fn && (*fn != '.'))
394     {
395         *p1++ = *fn++;
396     }
397     if (*fn == '.')
398     {
399         fn++;
400     }
401     p1 = fcb + NAME_LTH + 1;
402     while (*fn && (*fn != '.'))
403     {
404         *p1++ = *fn++;
405     }
406 }
407 
408 /**************************************************
409 
410     FTP interface - most of the work is done here
411     The IMDOS/CPM application only does minimal work
412 
413 */
414 
415 char message[WRK_BUF_SZ];
416 char temp   [WRK_BUF_SZ];
417 FILE * myfile;
418 
FTP(int32 BC,int32 DE)419 uint8 FTP(int32 BC, int32 DE)
420 {
421     char   *p1, *p2;
422     int32   retval;
423 
424     xferi(DE, temp, SEC_SZ);
425     p1 = temp;
426     switch (BC & 0x7f)
427     {
428         case 0:
429             memcpy(message, p1 + 2, *(p1 + 1));
430             *(message + *(p1 + 1)) = 0;
431             p2 = strtok(message, " \t");
432             if (!strcmp(p2, "get"))
433             {
434                 p2 = strtok(NULL, " \t");
435                 if (myfile = fopen(p2, "rb"))
436                 {
437                     initfcb(temp, p2, 1);
438                     xfero(DE + 2, temp, 32);
439                     retval = 0;
440                     break;
441                 }
442             }
443             if (!strcmp(p2, "era"))
444             {
445                 p2 = strtok(NULL, " \t");
446                 initfcb(temp, p2, 0);
447                 xfero(DE + 2, temp, 32);
448                 retval = 1;
449                 break;
450             }
451             retval = 0xff;
452             break;
453 
454         case 20:
455             memset(temp, 0x1a, SEC_SZ);
456             retval = sim_fread(temp, 1, SEC_SZ, myfile) ? 0 : 1;
457             xfero( DE, temp, SEC_SZ);
458             if (retval)
459             {
460                 fclose(myfile);
461             }
462             break;
463     }
464     return retval;
465 }
466 
467 #endif /* ERNIES_FTP */
468 
469 /* end of the source */
470 
471 
472 
473