Xemu [doxygen]  hyppo 0a42be3a057156924bc1b626a687bd6e27349c45 @ Sat 19 Mar 02:15:11 CET 2022
cbmhostfs.c
Go to the documentation of this file.
1 /* Test-case for a very simple, inaccurate, work-in-progress Commodore 65 / MEGA65 emulator,
2  within the Xemu project.
3  Copyright (C)2016 LGB (Gábor Lénárt) <lgblgblgb@gmail.com>
4 
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
18 
19 
20 #include "xemu/emutools.h"
21 #include "xemu/cbmhostfs.h"
22 
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <unistd.h>
26 #include <time.h>
27 #include <dirent.h>
28 #include <fcntl.h>
29 #include <errno.h>
30 #include <stdlib.h>
31 
32 
33 #define DEBUG_HOSTFS printf
34 //#define DEBUG_HOSTFS DEBUG
35 
36 
37 /*
38 
39 Future plans to have some KERNAL-DOS-access-alike functions to make easy to patch ROM
40 to use HostFS instead of DOS/IEC/whatever routines for a given device number.
41 
42 http://www.pagetable.com/docs/Inside%20Commodore%20DOS.pdf
43 http://www.zimmers.net/anonftp/pub/cbm/programming/serial-bus.pdf
44 
45 
46 Current quick'n'dirty usage:
47 
48  C65 I/O mode, $D0FE = reg0, $D0FF = reg1
49 
50 
51  reg0: read access: status register (low level, emulator specific error singaling)
52  write access: command register (low level, emulator specific command selection)
53  reg1: read/write data (depending on the last command used, may be invalid to be used, see command descriptions!)
54 
55  Status register:
56  zero byte = no error, and no EOF
57  bit6 = EOF condition, NOT only for the next I/O, but the previous! That is, the last
58  read didn't fetched anything for real, so the read byte should not be used, as it's
59  some dummy byte, and not a real fetched data! This bit is also set, if data port
60  read/written after a command/etc which does not allow that, since there is no buffer to use (End of Buffer/ No buffer)
61  also, non-existing command generates this
62  basically this bit means that nothing to do with the written/read data or command code, because non-available data, storage or command
63  bit7 = error occured (other than EOF), it means actual hostFS task could be done but resulted error
64  !!STATUS REGISTER IS RESET AFTER A READ!!
65 
66  Should be checked after each R/W of data register.
67 
68  Command register (low-level protocol commands only):
69  High nibble of the written byte specifies the command (low nibble may specify channel, but see descriptions):
70  0 = Start to send specification for open
71  low nibble has no purpose. Data write register must be used to transfer bytes
72  for the specification, surely data register can be written multiple times to
73  transfer a strin g. After last write of data register, command 1 must be used.
74  status register *MAY* have indicate "EOF" if too long string is given, there can
75  be no other kind of error.
76  1 = terminates and "finalizes" the string sent after issuing command 0, open / execute!
77  the stored string remains intact till the next received command 1.
78  Low nibble MUST specifies a "secondary channel" the specification is refeered to
79  that is (like with CBM DOS stuffs):
80  0 = prg reading
81  1 = prg writing
82  2 - 14 = custom open files
83  15 = command/status channel
84  Status register may inidicate error on command interpreting the specification.
85  (but not "EOF"). In case of an error, command channel can be open/read to
86  describe the error. If EOF is given during write data register, you will not able to
87  terminate your string, since there is not place in buffer.
88  It opens the given channel! If the channel was already open, it will be closed first.
89  2 = repeat cmd 1
90  It's like cmd-1, however there is no need for cmd0 + specification send,
91  the same spec is used again (with possible close the channel if it was open before).
92  The low nibble must specify a channel, and can be different as with cmd1 (however this is
93  the situation of "do this only if you really know what you are doing")
94  Otherwise the same rules.
95  3 = close channel
96  low nibble must specify an already open channel.
97  Command closes the given channel
98  4 = use channel
99  low nibble must specify an already open channel
100  sets the "target/source" of data port. NOTE: depending on the channel, it's possible
101  that either of read or write of data register is not supported.
102  NOTE: WITHOUT THIS COMMAND, YOU CAN'T WRITE OR READ DATA REGISTER (for other purpose
103  that after cmd-0 for writing, which is valid, of course, but even then read is not supported)
104  You can have more open channel, and use cmd-4 to "switch" between them.
105  Other not implemented command codes result in EOF in status register.
106 
107  Currently the implementation is rather primitive. the "specification string" cannot specify commands, just file names.
108  Though, if first character of file name is "$" (the others are not checked!) it means directory reading, and "standard"
109  pseudo-BASIC listing can be read (as with CBM DOS). Otherwise, the string interpreted as file name to open, in the
110  following syntax (ONLY!!!!):
111 
112  @FILENAME,P,R
113 
114  @: this can be omitted, and have only meaning if write access is requested, then it will overwrite possible existing file
115  P: this is the file type, like S(SEQ), P(PRG), etc, but currently NOT CHECKED AT ALL
116  R: this can be "R" or "W" for read or write
117  for "R" it can be omitted (also the whole ,P,R like part)
118 
119  All directory entries are handled/shown as "PRG" files. File names are "normalized" upon directory listing and also
120  on opening files, as there is fundamental problem with PETSCII-ASCII conversion and also the differences with hostFS
121  (on Windows, FS is case insensitive, where with UNIX it's case sensitive). Non-conforming file names (too long, not
122  allowed CBM DOS chars, cannot be converted to PETSCII, etc) may not appear in directory listings. If multiple files
123  have the same name (ie, case sensitive names like "readme" and "README" in hostFS, normalized to the same name)
124  will be shown, however you may have interesting problem if you try to access one (ie "wrong" file is used or even
125  overwritten, etc).
126 
127  Channel number a freely choosable channel, so you can use multiple open files. Channel numbers are 0-15, though 15 cannot
128  be used (in the future it will be the CBM DOS status/command channel), also 0,1 can have special meaning (load/save
129  PRG file) in the future. The future usage of this channel number will be the same as the 3rd parameter of BASIC OPEN statement, also
130  called "SECONDARY CHANNEL", "SECONDARY ADDRESS" or "SA".
131 
132 */
133 
134 
135 #define WRITE_BUFFER_SIZE 256
136 #define READ_BUFFER_SIZE 256
137 #define SPEC_BUFFER_SIZE 4096
138 #define CBM_MAX_DIR_ENTRIES 144
139 
140 
145  DIR *dir;
146  int fd, id;
149 };
150 
151 static struct hostfs_channels_st hostfs_channels[16];
152 static Uint8 hfs_status;
153 static Uint8 spec_filling[SPEC_BUFFER_SIZE];
154 static Uint8 spec_used[SPEC_BUFFER_SIZE];
155 static int spec_filling_index;
156 static int last_command;
157 static struct hostfs_channels_st *use_channel;
158 static char hostfs_directory[PATH_MAX];
159 
160 
161 
162 static Uint8 set_error ( int status, int errorcode, int tracknum, int sectnum, const char *description )
163 {
164  const char *p;
165  hfs_status = status;
166  switch (errorcode) {
167  case 0: p = "OK"; break;
168  case 73: p = "XEMU SOFTWARE CBM-DOS SUBSET"; break;
169  default:
170  p = "UNHANDLED XEMU ERROR";
171  fprintf(stderr, "HOSTFS DOS: error code %02d is not defined, please report this." NL, errorcode);
172  break;
173  }
174  hostfs_channels[15].read_used = sprintf((char*)hostfs_channels[15].read_buffer, "%02d, %s,%02d,%02d", errorcode, p, tracknum, sectnum);
175  DEBUG_HOSTFS("HOSTFS: error (host_status=%d) is set to \"%s\": %s." NL, status, (char*)hostfs_channels[15].read_buffer, description ? description : "-");
176  return 0xFF;
177 }
178 
179 #define NO_ERROR() set_error(0, 0, 0, 0, NULL)
180 
181 
182 
183 void hostfs_init ( const char *basedir, const char *subdir )
184 {
185  DIR *dir;
186  int a;
187  if (subdir) {
188  sprintf(hostfs_directory, "%s" DIRSEP_STR "%s" DIRSEP_STR, basedir, subdir);
189  MKDIR(hostfs_directory);
190  } else {
191  sprintf(hostfs_directory, "%s" DIRSEP_STR, basedir);
192  }
193  // opendir() here is only for testing ...
194  dir = opendir(hostfs_directory);
195  if (!dir) {
196  ERROR_WINDOW("Warning, specified hostFS directory (%s) cannot be used, you will have problems: %s", hostfs_directory, strerror(errno));
197  } else
198  closedir(dir);
199  // intialize channels to a known state
200  for (a = 0; a < 16; a++) {
201  hostfs_channels[a].id = a;
202  hostfs_channels[a].dir = NULL;
203  hostfs_channels[a].fd = -1;
204  hostfs_channels[a].allow_write = 0;
205  hostfs_channels[a].allow_read = 0;
206  hostfs_channels[a].read_used = 0;
207  hostfs_channels[a].write_used = 0;
208  hostfs_channels[a].file_size = 0;
209  hostfs_channels[a].trans_bytes = 0;
210  }
211  set_error(0, 73, 0, 0, "init"); // set status reg, and DOS "error" ...
212  last_command = -1;
213  use_channel = NULL;
214  printf("HOSTFS: init @ %s" NL, hostfs_directory);
215  atexit(hostfs_close_all); // registering function for exit, otherwise un-flushed write buffers may not be written on exit or panic!
216 }
217 
218 
219 static void hostfs_flush ( struct hostfs_channels_st *channel )
220 {
221  int pos;
222  if (channel->fd < 0 || !channel->write_used)
223  return;
224  pos = 0;
225  DEBUG_HOSTFS("HOSTFS: flusing write cache on channel #%d for %d bytes ..." NL, channel->id, channel->write_used);
226  while (channel->write_used) {
227  int ret = write(channel->fd, channel->write_buffer + pos, channel->write_used);
228  switch (ret) {
229  case -1:
230  FATAL("HOSTFS: cannot write file, error got: %s", strerror(errno));
231  break;
232  case 0:
233  FATAL("HOSTFS: cannot write file, zero bytes could written!");
234  break;
235  default:
236  channel->write_used -= ret;
237  pos += ret;
238  break;
239  }
240  }
241 }
242 
243 
244 static void hostfs_close ( struct hostfs_channels_st *channel )
245 {
246  if (channel->allow_write || channel->allow_read)
247  DEBUG_HOSTFS("HOSTFS: closing channel #%d" NL, channel->id);
248  if (channel->dir)
249  closedir(channel->dir);
250  if (channel->fd >= 0) {
251  hostfs_flush(channel);
252  close(channel->fd);
253  }
254  channel->dir = NULL;
255  channel->fd = -1;
256  channel->allow_write = 0;
257  channel->allow_read = 0;
258  hfs_status = 0;
259 }
260 
261 
262 void hostfs_close_all ( void )
263 {
264  int channel;
265  for (channel = 0; channel < 16; channel++)
266  hostfs_close(hostfs_channels + channel);
267 }
268 
269 
270 void hostfs_flush_all ( void )
271 {
272  int channel;
273  for (channel = 0; channel < 16; channel++)
274  hostfs_flush(hostfs_channels + channel);
275 }
276 
277 
278 
279 // Converts host filename to PETSCII, used by directory reading. Both of them are NUL terminated strings.
280 // Returns zero if conversion cannot be done (long filename, non-PETSCII representable char, etc)
281 // In case of zero value, the converted buffer is invalid, and should be not used! [zero length input is also invalid]
282 // Returns non-zero if success when it means the length of the file name.
283 // "out" buffer must be maxlen+1 bytes at least (for the plus NULL byte)
284 static const char banned_hostfilename_chars[] = ":;,/\\#$=*?@";
285 static int filename_host2cbm ( const Uint8 *in, Uint8 *out, int maxlen)
286 {
287  int len = 0;
288  if (*in == '.')
289  return 0;
290  for (;;) {
291  Uint8 c = *(in++);
292  if (!c)
293  break;
294  if (++len > maxlen || c < 32 || c > 126 || strchr(banned_hostfilename_chars, c))
295  return 0;
296  if (c >= 97 && c <= 122)
297  c -= 32;
298  *(out++) = c;
299  }
300  *out = 0;
301  return len;
302 }
303 
304 
305 static int xemu_stat_at ( const char *dirname, const char *filename, struct stat *st )
306 {
307  char namebuffer[PATH_MAX];
308  if (snprintf(namebuffer, sizeof namebuffer, "%s" DIRSEP_STR "%s", dirname, filename) >= sizeof namebuffer) {
309  errno = ENAMETOOLONG;
310  return -1;
311  }
312  return stat(namebuffer, st);
313 }
314 
315 
316 static int xemu_open_at ( const char *dirname, const char *filename, int flags, mode_t mode )
317 {
318  char namebuffer[PATH_MAX];
319  if (snprintf(namebuffer, sizeof namebuffer, "%s" DIRSEP_STR "%s", dirname, filename) >= sizeof namebuffer) {
320  errno = ENAMETOOLONG;
321  return -1;
322  }
323  return open(namebuffer, flags, mode);
324 }
325 
326 
327 // Note: CBM DOS standard for pseudo-BASIC directory to use load address $0401, this is because of the PET-era.
328 // PETs were incapable of relocting BASIC programs, so directory still use their BASIC load addr, newer CBM
329 // machines can relocate, so they have no problem with this address, at the other hand.
330 #define CBM_DIR_LOAD_ADDRESS 0x0401
331 
332 
333 static const Uint8 dirheadline[] = {
334  CBM_DIR_LOAD_ADDRESS & 0xFF, CBM_DIR_LOAD_ADDRESS >> 8, // load address (not the part of the basic program for real)
335  1, 1, // link to the next line, simple version, just say something non-zero ...
336  0, 0, // line number
337  0x12, '"', // REVS ON and quote mark
338  'X','E','M','U','-','C','B','M',' ','H','O','S','T','-','F','S',
339  '"',' ','X','E',' ','M','U',0
340 };
341 static const Uint8 dirtailline[] = {
342  1, 1, // link addr
343  0xFF,0xFF, // free blocks number as line number, FIXME: later it should be set, if host OS has disk with less free space than 0xFFFF * 254 bytes ?!
344  'B','L','O','C','K','S',' ','F','R','E','E','.',
345  ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
346  0, 0, 0
347 };
348 
349 
350 
351 static int cbm_read_directory ( struct hostfs_channels_st *channel )
352 {
353  // channel->allow_read is also used to track the memory address in the generated BASIC program for directory listing,
354  // since it's basically a boolean value, so any non-zero value has the same "boolean value".
355  if (channel->allow_read == 1) {
356  // first item ... not a real directory entry, but the "header"
357  memcpy(channel->read_buffer, dirheadline, sizeof dirheadline);
358  channel->allow_read = 2;
359  channel->read_used = sizeof dirheadline;
360  return 0;
361  }
362  if (!channel->dir) {
363  channel->eof = 1;
364  return 64;
365  }
366  for (;;) {
367  struct dirent *entry = readdir(channel->dir); // read host directory
368  if (entry) {
369  Uint8 *p = channel->read_buffer;
370  Uint8 filename[17];
371  int namelength, i;
372  struct stat st;
373  if (!(namelength = filename_host2cbm((Uint8*)entry->d_name, filename, 16)))
374  continue; // cannot be represented name, skip it!
375  if (xemu_stat_at(hostfs_directory, entry->d_name, &st))
376  continue;
377  if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
378  continue;
379  if (channel->allow_read - 2 == CBM_MAX_DIR_ENTRIES)
380  goto too_many_entries;
381  channel->allow_read++;
382  // Ok, it seems to be OK, let's present a directory line for the entry!
383  *(p++) = 1; // link address, lo
384  *(p++) = 1; // link address, hi
385  st.st_size = (st.st_size + 253) / 254; // convert size to "blocks"
386  i = st.st_size > 0xFFFF ? 0xFFFF : st.st_size;
387  *(p++) = i & 0xFF;
388  *(p++) = i >> 8;
389  if (i < 10) *(p++) = ' ';
390  if (i < 100) *(p++) = ' ';
391  if (i < 1000) *(p++) = ' ';
392  *(p++) = '"';
393  for (i = 0; i < 16; i++)
394  *(p++) = i < namelength ? filename[i] : (i == namelength ? '"' : ' ');
395  if (S_ISDIR(st.st_mode)) {
396  *(p++) = ' ';
397  *(p++) = 'D';
398  *(p++) = 'I';
399  *(p++) = 'R';
400  } else {
401  *(p++) = st.st_size ? ' ' : '*';
402  *(p++) = 'P';
403  *(p++) = 'R';
404  *(p++) = 'G';
405  }
406  *(p++) = ' '; // later: read-only files with '<' TODO
407  *(p++) = 0;
408  channel->read_used = p - channel->read_buffer;
409  } else {
410  too_many_entries:
411  closedir(channel->dir);
412  channel->dir = NULL;
413  // Put tail basic line ("BLOCKS FREE")!
414  // Note, we always give back 65535 BLOCK FREE, but since about ~16Mbyte is usually have on host-FS nowadays,
415  // I guess it simple does not worth to invoke a statfs() call just for this ...
416  memcpy(channel->read_buffer, dirtailline, sizeof dirtailline);
417  channel->read_used = sizeof dirtailline;
418  }
419  return 0;
420  }
421 }
422 
423 
424 
425 static Uint8 char_cbm2ascii ( Uint8 c )
426 {
427  if (c >= 65 && c <= 90)
428  return c + 32;
429  if (c >= 193 && c <= 218)
430  return c - 96;
431  return c;
432 }
433 
434 
435 
436 static void hostfs_open ( struct hostfs_channels_st *channel )
437 {
438  DEBUG_HOSTFS("HOSTFS: opening channel #%d ..." NL, channel->id);
439  if (channel->id == 15) {
440  FATAL("Sorry, channel#15 is not supported yet, and you must not use it!");
441  }
442  hostfs_close(channel); // close channel (maybe it was used before)
443  if (spec_used[0] == 0) { // empty name/command is not valid
444  hfs_status = 64;
445  return;
446  }
447  channel->write_used = 0;
448  channel->read_used = 0;
449  channel->eof = 0;
450  channel->file_size = 0;
451  channel->trans_bytes = 0;
452  if (spec_used[0] == '$') {
453  channel->dir = opendir(hostfs_directory);
454  if (!channel->dir) {
455  hfs_status = 128;
456  DEBUG_HOSTFS("HOSTFS: cannot open directory '%s': %s" NL, hostfs_directory, strerror(errno));
457  return;
458  }
459  channel->allow_write = 0;
460  channel->allow_read = 1;
461  hfs_status = cbm_read_directory(channel);
462  } else {
463  /* normal file must be open ... */
464  int flags, len;
465  Uint8 *p0, *p1;
466  DIR *dir;
467  struct dirent *entry;
468  char filename[PATH_MAX];
469  p1 = (Uint8*)strchr((char*)spec_used, ',');
470  if (p1) {
471  Uint8 *p2;
472  flags = char_cbm2ascii(p1[1]);
473  DEBUG_HOSTFS("HOSTFS: file type character is '%c'" NL, flags);
474  if (flags != 'p' && flags != 'u' && flags != 's') {
475  DEBUG_HOSTFS("HOSTFS: unknown file type character!" NL);
476  hfs_status = 128;
477  return;
478  }
479  p2 = (Uint8*)strchr((char*)p1 + 1, ',');
480  len = (int)(p1 - spec_used);
481  if (p2) {
482  if (strchr((char*)p2 + 1, ',')) {
483  hfs_status = 128;
484  return;
485  }
486  flags = char_cbm2ascii(p2[1]);
487  } else
488  flags = channel->id == 1 ? 'w' : 'r';
489  } else {
490  len = strlen((char*)spec_used);
491  flags = channel->id == 1 ? 'w' : 'r'; // TODO: is it correct? channel-1 is write by default. OR ONLY?! FIXME
492  }
493  if (len >= PATH_MAX) {
494  hfs_status = 64;
495  return;
496  }
497  // first char of file name can be '@' to signal overwriting situation!
498  if (spec_used[0] == '@') {
499  p0 = spec_used + 1;
500  len--;
501  if (flags == 'w') flags = 'o'; // overwrite mode
502  } else
503  p0 = spec_used;
504  // Skip the possible part of drive assignment (ie "0:FILENAME...")
505  if (len > 1 && p0[1] == ':') {
506  if (p0[0] != '0') {
507  hfs_status = 128;
508  return;
509  }
510  p0 += 2;
511  len -= 2;
512  }
513  // check if we have an invalid, empty file name
514  if (!len) {
515  hfs_status = 128;
516  return;
517  }
518  DEBUG_HOSTFS("HOSTFS: file mode char: '%c'" NL, flags);
519  switch (flags) {
520  case 'a': // append mode
521  channel->allow_write = 1;
522  channel->allow_read = 0;
523  flags = O_WRONLY | O_APPEND | O_BINARY; // FIXME: append mode allows _creation_ of file?! TODO
524  DEBUG_HOSTFS("HOSTFS: file is selected for append" NL);
525  break;
526  case 'o': // overwrite mode ("fake" mode, created with 'w' mode and '@' prefix above)
527  channel->allow_write = 1;
528  channel->allow_read = 0;
529  flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
530  DEBUG_HOSTFS("HOSTFS: file is selected for overwrite" NL);
531  break;
532  case 'w': // write mode
533  channel->allow_write = 1;
534  channel->allow_read = 0;
535  flags = O_WRONLY | O_CREAT | O_EXCL | O_BINARY;
536  DEBUG_HOSTFS("HOSTFS: file is selected for write" NL);
537  break;
538  case 'r': // read mode
539  case 'm': // no idea what it is, "modify" but strangely it seems also to be 'read' ...
540  channel->allow_write = 0;
541  channel->allow_read = 1;
542  flags = O_RDONLY | O_BINARY;
543  DEBUG_HOSTFS("HOSTFS: file is selected for read" NL);
544  break;
545  default:
546  hfs_status = 128;
547  DEBUG_HOSTFS("HOSTFS: invalid file mode char!" NL);
548  return;
549  }
550  memcpy(filename, p0, len);
551  filename[len] = 0;
552  p0 = (Uint8*)filename;
553  while (*p0) { // convert filename
554  *p0 = (*p0 == '/' || *p0 == '\\') ? '.' : char_cbm2ascii(*p0);
555  p0++;
556  }
557  dir = opendir(hostfs_directory);
558  if (!dir) {
559  hfs_status = 128;
560  DEBUG_HOSTFS("HOSTFS: cannot open directory '%s': %s" NL, hostfs_directory, strerror(errno));
561  return;
562  }
563  DEBUG_HOSTFS("HOSTFS: trying to open file %s" NL, filename);
564  /* Note: we have to walk the directory, as host OS may have case sensitive, or case-insensitive
565  file names, also there will be PETSCII-ASCII file name conversion later, etc ...
566  So we try to find a matching file, even if file name can't contain "joker" character for now ... */
567  while ((entry = readdir(dir))) {
568  DEBUG_HOSTFS("HOSTFS: considering file %s" NL, entry->d_name);
569  if (!strcasecmp(entry->d_name, filename)) {
570  struct stat st;
571  DEBUG_HOSTFS("HOSTFS: file name accepted." NL);
572  closedir(dir);
573  if (xemu_stat_at(hostfs_directory, entry->d_name, &st)) {
574  DEBUG_HOSTFS("HOSTFS: cannot stat file!" NL);
575  hfs_status = 128;
576  return;
577  }
578  if (!S_ISREG(st.st_mode)) {
579  // TODO: maybe in the future directory entries are "fake-loadable" to be able to change hostFS directory?
580  DEBUG_HOSTFS("HOSTFS: not a regular file!" NL);
581  hfs_status = 128;
582  return;
583  }
584  channel->file_size = st.st_size;
585  channel->fd = xemu_open_at(hostfs_directory, entry->d_name, flags, 0666);
586  if (channel->fd < 0) {
587  DEBUG_HOSTFS("HOSTFS: could not open host file '%s" DIRSEP_STR "%s': %s" NL, hostfs_directory, entry->d_name, strerror(errno));
588  hfs_status = 128;
589  } else {
590  DEBUG_HOSTFS("HOSTFS: great, file is open as '%s'" NL, filename);
591  hfs_status = 0;
592  }
593  return;
594  }
595  }
596  closedir(dir);
597  // on write, if we don't have matching entry (we are here ...), we want to create new file here!!!
598  if (flags & O_CREAT) {
599  channel->fd = xemu_open_at(hostfs_directory, filename, flags, 0666);
600  if (channel->fd < 0) {
601  DEBUG_HOSTFS("HOSTFS: could not create new host file entry '%s" DIRSEP_STR "%s': %s" NL, hostfs_directory, filename, strerror(errno));
602  hfs_status = 128;
603  } else {
604  DEBUG_HOSTFS("HOSTFS: great, new host file is created as '%s'" NL, filename);
605  hfs_status = 0;
606  }
607  } else {
608  hfs_status = 128;
609  DEBUG_HOSTFS("HOSTFS: no matching pattern found for opening file" NL);
610  }
611  }
612 }
613 
614 
615 
616 
618 {
619  Uint8 ret = hfs_status;
620  hfs_status = 0;
621  return ret;
622 }
623 
624 
626 {
627  struct hostfs_channels_st *channel = hostfs_channels + (data & 15);
628  last_command = data >> 4;
629  hfs_status = 0;
630  switch (last_command) {
631  case 0: // start specification of channel, low nibble has no meaning
632  spec_filling_index = 0;
633  break;
634  case 1: // open/execute specified file/command, low nibble is the channel number
635  if (spec_filling_index >= SPEC_BUFFER_SIZE) {
636  hfs_status = 64;
637  } else {
638  spec_filling[spec_filling_index] = 0;
639  strcpy((char*)spec_used, (char*)spec_filling);
640  spec_filling_index = 0;
641  DEBUG_HOSTFS("HOSTFS: command=\"%s\" on channel #%d" NL, (char*)spec_used, channel->id);
642  hostfs_open(channel);
643  }
644  break;
645  case 2: // re-execute/re-open last issued specification for channel in low-nibble
646  hostfs_open(channel);
647  break;
648  case 3: // close channel for low-nibble
649  hostfs_close(channel);
650  break;
651  case 4: // set channel (in low nibble) to be used for data register R/W
652  use_channel = channel;
653  break;
654  case 5: // get number of bytes info [special command, the answer is sent back via the status register!]
655  if (use_channel) {
656  last_command = 4;
657  hfs_status = ((data & 8 ? use_channel->trans_bytes : use_channel->file_size) >> ((data & 3) << 3)) & 0xFF;
658  if (data & 4)
659  use_channel->trans_bytes = 0;
660  }
661  break;
662  default:
663  hfs_status = 64;
664  break;
665  }
666 }
667 
668 
670 {
671  Uint8 result;
672  if (last_command == 0) {
673  hfs_status = 64;
674  return 0xFF;
675  }
676  if (!use_channel) {
677  hfs_status = 64;
678  return 0xFF;
679  }
680  if (!use_channel->allow_read) {
681  hfs_status = 64;
682  return 0xFF;
683  }
684  if (use_channel->eof) {
685  hfs_status = 64;
686  return 0xFF;
687  }
688  if (use_channel->read_used)
689  goto ret_one;
690  /* empty buffer, must read more bytes ... */
691  if (use_channel->dir) {
692  hfs_status = cbm_read_directory(use_channel);
693  if (!hfs_status)
694  goto ret_one;
695  return 0xFF;
696  }
697  if (use_channel->fd < 0) {
698  hfs_status = 64; // not open channel
699  return 0xFF;
700  }
701  use_channel->read_used = read(use_channel->fd, use_channel->read_buffer, READ_BUFFER_SIZE);
702  if (use_channel->read_used == 0) {
703  hfs_status = 64;
704  use_channel->eof = 1;
705  return 0xFF; // dummy byte!
706  }
707  if (use_channel->read_used < 0) {
708  FATAL("read error @ hostFS!");
709  return 0xFF;
710  }
711 ret_one:
712  result = use_channel->read_buffer[0];
713  memmove(use_channel->read_buffer, use_channel->read_buffer + 1, --use_channel->read_used);
714  hfs_status = 0;
715  use_channel->trans_bytes++;
716  return result;
717 }
718 
719 
721 {
722  if (last_command == 0) {
723  if (spec_filling_index >= SPEC_BUFFER_SIZE - 1) {
724  hfs_status = 64;
725  } else {
726  spec_filling[spec_filling_index++] = data;
727  hfs_status = 0;
728  }
729  return;
730  }
731  if (!use_channel) {
732  hfs_status = 64;
733  return;
734  }
735  if (!use_channel->allow_write) {
736  hfs_status = 64;
737  return;
738  }
739  if (use_channel->fd < 0) {
740  hfs_status = 64;
741  return;
742  }
743  if (use_channel->write_used == WRITE_BUFFER_SIZE)
744  hostfs_flush(use_channel);
745  use_channel->write_buffer[use_channel->write_used++] = data;
746  use_channel->trans_bytes++;
747  hfs_status = 0;
748 }
hostfs_read_reg1
Uint8 hostfs_read_reg1(void)
Definition: cbmhostfs.c:669
hostfs_channels_st::write_buffer
Uint8 write_buffer[WRITE_BUFFER_SIZE]
Definition: cbmhostfs.c:143
emutools.h
CBM_DIR_LOAD_ADDRESS
#define CBM_DIR_LOAD_ADDRESS
Definition: cbmhostfs.c:330
flags
Uint8 flags
Definition: z8k1.c:126
READ_BUFFER_SIZE
#define READ_BUFFER_SIZE
Definition: cbmhostfs.c:136
hostfs_channels_st::dir
DIR * dir
Definition: cbmhostfs.c:145
O_BINARY
#define O_BINARY
Definition: emutools_basicdefs.h:140
hostfs_channels_st::read_used
int read_used
Definition: cbmhostfs.c:144
WRITE_BUFFER_SIZE
#define WRITE_BUFFER_SIZE
Definition: cbmhostfs.c:135
hostfs_channels_st::fd
int fd
Definition: cbmhostfs.c:146
CBM_MAX_DIR_ENTRIES
#define CBM_MAX_DIR_ENTRIES
Definition: cbmhostfs.c:138
hostfs_channels_st
Definition: cbmhostfs.c:141
hostfs_channels_st::allow_read
int allow_read
Definition: cbmhostfs.c:147
hostfs_close_all
void hostfs_close_all(void)
Definition: cbmhostfs.c:262
hostfs_init
void hostfs_init(const char *basedir, const char *subdir)
Definition: cbmhostfs.c:183
hostfs_channels_st::eof
int eof
Definition: cbmhostfs.c:147
hostfs_channels_st::id
int id
Definition: cbmhostfs.c:146
hostfs_channels_st::trans_bytes
off_t trans_bytes
Definition: cbmhostfs.c:148
m65-memcontent-generator.data
data
Definition: m65-memcontent-generator.py:119
Uint8
uint8_t Uint8
Definition: fat32.c:51
hostfs_channels_st::allow_write
int allow_write
Definition: cbmhostfs.c:147
cbmhostfs.h
DEBUG_HOSTFS
#define DEBUG_HOSTFS
Definition: cbmhostfs.c:33
dir
DIR * dir
Definition: cpmfs.c:46
ERROR_WINDOW
#define ERROR_WINDOW(...)
Definition: xep128.h:116
hostfs_write_reg1
void hostfs_write_reg1(Uint8 data)
Definition: cbmhostfs.c:720
NL
#define NL
Definition: fat32.c:37
hostfs_channels_st::file_size
off_t file_size
Definition: cbmhostfs.c:148
DIRSEP_STR
#define DIRSEP_STR
Definition: emutools_basicdefs.h:141
hostfs_flush_all
void hostfs_flush_all(void)
Definition: cbmhostfs.c:270
hostfs_write_reg0
void hostfs_write_reg0(Uint8 data)
Definition: cbmhostfs.c:625
hostfs_channels_st::read_buffer
Uint8 read_buffer[READ_BUFFER_SIZE]
Definition: cbmhostfs.c:142
hostfs_read_reg0
Uint8 hostfs_read_reg0(void)
Definition: cbmhostfs.c:617
status
enum @26::@29 status
MKDIR
#define MKDIR(__n)
Definition: emutools_basicdefs.h:147
hostfs_channels_st::write_used
int write_used
Definition: cbmhostfs.c:144
SPEC_BUFFER_SIZE
#define SPEC_BUFFER_SIZE
Definition: cbmhostfs.c:137
FATAL
#define FATAL(...)
Definition: xep128.h:117
st
struct stat st
Definition: cpmfs.c:43