| 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 |