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