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