Xemu [doxygen]  hyppo 0a42be3a057156924bc1b626a687bd6e27349c45 @ Sat 19 Mar 02:15:11 CET 2022
fileio.c
Go to the documentation of this file.
1 /* Minimalistic Enterprise-128 emulator with focus on "exotic" hardware
2  Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu
3  Copyright (C)2015-2016,2019-2020 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 #include "xemu/emutools.h"
20 #include "xemu/emutools_gui.h"
21 #include "enterprise128.h"
22 #include "fileio.h"
23 #include "xemu/z80.h"
24 #include "cpu.h"
25 #include "emu_rom_interface.h"
26 
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <unistd.h>
30 #include <time.h>
31 #include <dirent.h>
32 #include <fcntl.h>
33 #include <ctype.h>
34 #include <errno.h>
35 
36 #define EXOS_USER_SEGMAP_P (0X3FFFFC + memory)
37 #define HOST_OS_STR "Host OS "
38 #define FILE_TOO_LARGE "Too large file"
39 
40 #define FILEIO_MAX_FILE_SIZE 67108864L
41 
42 #define SET_CHANNEL(v) channel = ((v) - 1) & 0xFF
43 
44 char fileio_cwd[PATH_MAX + 1];
45 static int channel = 0;
46 
47 static int fio_fd [0x100];
48 static int fio_off [0x100];
49 static char *fio_name[0x100];
50 static int fio_size[0x100];
51 static Uint8 fio_prot[0x100];
52 
53 
54 
55 void fileio_init ( const char *dir, const char *subdir )
56 {
57  int a;
58  for (a = 0; a < 0x100; a++)
59  fio_fd[a] = -1;
60  if (dir && *dir && *dir != '?') {
61  strcpy(fileio_cwd, dir);
62  if (subdir) {
63  strcat(fileio_cwd, subdir);
64  if (subdir[strlen(subdir) - 1] != DIRSEP_CHR)
65  strcat(fileio_cwd, DIRSEP_STR);
66  }
67  DEBUGPRINT("FILEIO: base directory is: %s" NL, fileio_cwd);
68  mkdir(fileio_cwd
69 #ifndef XEMU_ARCH_WIN
70  , 0777
71 #endif
72  );
73  } else
74  fileio_cwd[0] = '\0';
75 }
76 
77 
78 static void fileio_host_errstr ( void )
79 {
80  char buffer[65];
81  snprintf(buffer, sizeof buffer, HOST_OS_STR "%s", ERRSTR());
82  xep_set_error(buffer);
83 }
84 
85 
86 
87 static int host_file_check ( int fd, int *file_size, char **filename_store, const char *filename_in )
88 {
89  struct stat st;
90  if (fstat(fd, &st)) {
91  fileio_host_errstr();
92  close(fd);
93  return -1;
94  }
95  if (st.st_size > FILEIO_MAX_FILE_SIZE) {
97  close(fd);
98  return -1;
99  }
100  *file_size = st.st_size;
101  *filename_store = strdup(filename_in);
102  if (!*filename_store) {
103  xep_set_error(HOST_OS_STR "Cannot allocate memory");
104  close(fd);
105  return -1;
106  }
107  return fd;
108 }
109 
110 
111 /* Opens a file on host-OS/FS side.
112  Note: EXOS is case-insensitve on file names.
113  Host OS FS "under" Xep128 may (Win32) or may not (UNIX) be case sensitve, thus we walk through the directory with tring to find matching file name.
114  Argument "create" must be non-zero for create channel call, otherwise it should be zero.
115  O_BINARY flag is a *MUST* for Windows! On non-windows systems we define O_BINARY as zero in a header file, thus using it won't affect non-win32 systems!
116 */
117 static int open_host_file ( const char *dirname, const char *filename, int create, char **used_filename, int *file_size )
118 {
119  DIR *dir;
120  struct dirent *entry;
121  int mode = create ? (O_TRUNC | O_CREAT | O_RDWR) : O_RDWR;
122  dir = opendir(dirname);
123  if (!dir) {
124  xep_set_error(HOST_OS_STR "Cannot open base directory");
125  return -1;
126  }
127  while ((entry = readdir(dir))) {
128  if (!strcasecmp(entry->d_name, filename)) {
129  int ret;
130  char buffer[PATH_MAX + 1];
131  closedir(dir);
132  if (CHECK_SNPRINTF(snprintf(buffer, sizeof buffer, "%s%s%s", dirname, DIRSEP_STR, entry->d_name), sizeof buffer))
133  return -1;
134  DEBUGPRINT("FILEIO: opening file \"%s\"" NL, buffer);
135  ret = open(buffer, mode | O_BINARY, 0666);
136  if (ret < 0 && !create) // handle the situation when host OS file is read-only ...
137  ret = open(buffer, O_RDONLY | O_BINARY); // re-try in read-only mode ...
138  if (ret < 0) {
139  fileio_host_errstr();
140  DEBUGPRINT("FILEIO: open in directory-walk-method failed: %s" NL, ERRSTR());
141  } else
142  ret = host_file_check(ret, file_size, used_filename, buffer);
143  return ret;
144  }
145  }
146  closedir(dir);
147  if (create) {
148  int ret;
149  char buffer[PATH_MAX + 1];
150  if (CHECK_SNPRINTF(snprintf(buffer, sizeof buffer, "%s%s%s", dirname, DIRSEP_STR, filename), sizeof buffer))
151  return -1;
152  ret = open(buffer, mode | O_BINARY, 0666);
153  if (ret < 0) {
154  fileio_host_errstr();
155  DEBUGPRINT("FILEIO: open in create-case-for-new-file failed: %s" NL, ERRSTR());
156  } else
157  ret = host_file_check(ret, file_size, used_filename, buffer);
158  return ret;
159  }
160  xep_set_error(HOST_OS_STR "File not found");
161  DEBUGPRINT("FILEIO: No file found matching the open request" NL);
162  return -1;
163 }
164 
165 
166 static void get_file_name ( char *p )
167 {
168  int de = Z80_DE;
169  int len = read_cpu_byte(de);
170  while (len--)
171  *(p++) = tolower(read_cpu_byte(++de));
172  *p = '\0';
173 }
174 
175 
177 {
179 }
180 
181 
182 // channel number is set up with fileio_func_open_channel_remember() *before* this call! from XEP ROM separated trap!
184 {
185  int r;
186  char fnbuf[PATH_MAX + 1];
187  // check content of Z80 A register. It should be zero from channel RAM allocate func. If not, it's an error!
188  if (Z80_A) {
189  DEBUGPRINT("FILEIO: channel RAM allocation EXOS call in XEP ROM failed (A = %02Xh)? Return!" NL, Z80_A);
190  return; // simply pass control back with the same error code in A we got
191  }
192  // channel number (C variable channel) already set via fileio_func_open_channel_remember() in a separated XEP TRAP!
193  if (fio_fd[channel] >= 0) {
194  DEBUGPRINT("FILEIO: open/create channel, already used channel for %d, fd is %d" NL, channel, fio_fd[channel]);
195  Z80_A = 0xF9; // channel number is already used! (maybe it's useless to be tested, as EXOS wouldn't allow that anyway?)
196  return;
197  }
198  get_file_name(fnbuf);
199  DEBUGPRINT("FILEIO: file name got = \"%s\"" NL, fnbuf);
200  if (!*fnbuf) {
201  DEBUGPRINT("FILEIO: file name was empty \"%s\" ..." NL, fnbuf);
202  if (create)
203  r = -1; // GUI for create is not yet supported ...
204  else
207  "Select file for opening via FILE: device",
208  fileio_cwd,
209  fnbuf,
210  sizeof fnbuf
211  );
212  if (r) {
213  xep_set_error(HOST_OS_STR "No file selected");
215  DEBUGPRINT("FILEIO: no file selected!" NL);
216  return;
217  }
218  memmove(fnbuf, fnbuf + strlen(fileio_cwd), strlen(fnbuf + strlen(fileio_cwd)) + 1);
219  }
220  r = open_host_file(fileio_cwd, fnbuf, create, &fio_name[channel], &fio_size[channel]);
221  //xep_set_error(ERRSTR());
222  DEBUGPRINT("FILEIO: %s channel #%d result = %d filename = \"%s\" as %s with size of %d" NL, create ? "create" : "open", channel, r, fnbuf, fio_name[channel], fio_size[channel]);
223  if (r < 0) {
224  // open_host_file() already issued the xep_set_error() call to set a message up ...
226  } else {
227  fio_fd[channel] = r;
228  fio_off[channel] = 0; // file offset
229  fio_prot[channel] = 0; // protection byte not so much used by EXOS, but anyway ...
230  Z80_A = 0;
231  }
232 }
233 
234 
236 {
238  if (fio_fd[channel] < 0) {
239  DEBUGPRINT("FILEIO: close, invalid channel for %d, fd is %d" NL, channel, fio_fd[channel]);
240  Z80_A = 0xFB; // invalid channel
241  } else {
242  DEBUGPRINT("FILEIO: close, closing channel %d (fd = %d)" NL, channel, fio_fd[channel]);
243  close(fio_fd[channel]);
244  fio_fd[channel] = -1;
245  free(fio_name[channel]);
246  fio_name[channel] = NULL;
247  Z80_A = 0;
248  }
249 }
250 
251 
252 
253 static int increment_offset ( int inc /*, int is_write */ )
254 {
255  fio_off[channel] += inc;
256  if (/*is_write &&*/ fio_off[channel] > fio_size[channel])
257  fio_size[channel] = fio_off[channel];
258  if (fio_off[channel] > FILEIO_MAX_FILE_SIZE) {
261  return -1;
262  }
263  return 0;
264 }
265 
266 
267 static int host_reseek ( void )
268 {
269  off_t ret = lseek(fio_fd[channel], fio_off[channel], SEEK_SET);
270  if (ret != fio_off[channel]) {
272  if (ret)
273  fileio_host_errstr();
274  else
275  xep_set_error(HOST_OS_STR "Invalid seek retval");
276  return -1;
277  }
278  if (fio_off[channel] > FILEIO_MAX_FILE_SIZE) {
281  return -1;
282  }
283  DEBUG("FILEIO: internal host seek %d for channel %d" NL, fio_off[channel], channel);
284  return 0;
285 }
286 
287 
289 {
290  int rb;
291  Uint8 buffer[0xFFFF], *p;
293  DEBUGPRINT("FILEIO: read block on channel %d (fd = %d) BC=%04Xh DE=%04Xh" NL, channel, fio_fd[channel], Z80_BC, Z80_DE);
294  if (fio_fd[channel] < 0) {
295  DEBUGPRINT("FILEIO: read block problem = invalid channel" NL);
296  Z80_A = 0xFB; // invalid channel
297  return;
298  }
299  if (host_reseek()) // this may set error str and Z80_A!
300  return;
301  Z80_A = 0;
302  rb = 0;
303  while (Z80_BC) {
304  int r;
305  r = read(fio_fd[channel], buffer + rb, Z80_BC);
306  DEBUGPRINT("FILEIO: read block on channel %d (fd = %d), %d byte(s) requested at offset %d (file size = %d), result is %d (got so far: %d)" NL,
307  channel, fio_fd[channel], Z80_BC,
308  fio_off[channel], fio_size[channel],
309  r, rb
310  );
311  if (r > 0) {
312  rb += r;
313  Z80_BC -= r;
314  if (increment_offset(r)) // this may set error str and Z80_A!
315  break;
316  } else if (!r) {
317  Z80_A = 0xE4; // attempt to read after end of file
318  break;
319  } else {
320  fileio_host_errstr();
322  break;
323  }
324  }
325  p = buffer;
326  while (rb--)
328 }
329 
330 
332 {
334  if (fio_fd[channel] < 0) {
335  DEBUGPRINT("FILEIO: read character, invalid channel for %d, fd is %d" NL, channel, fio_fd[channel]);
336  Z80_A = 0xFB; // invalid channel
337  } else {
338  int r;
339  if (host_reseek()) // this may set error str and Z80_A!
340  return;
341  r = read(fio_fd[channel], &Z80_B, 1);
342  if (r == 1) {
343  Z80_A = 0;
344  increment_offset(1); // this may set error str and Z80_A!
345  } else if (!r)
346  Z80_A = 0xE4; // attempt to read after end of file
347  else {
348  fileio_host_errstr();
350  }
351  }
352 }
353 
354 
355 
357 {
358  int wb, de;
359  Uint8 buffer[0xFFFF], *p;
361  if (fio_fd[channel] < 0) {
362  Z80_A = 0xFB; // invalid channel
363  return;
364  }
365  if (host_reseek()) // this may set error str and Z80_A!
366  return;
367  wb = Z80_BC;
368  p = buffer;
369  de = Z80_DE;
370  Z80_A = 0;
371  while (wb--)
373  p = buffer;
374  while (Z80_BC) {
375  int r = write(fio_fd[channel], p, Z80_BC);
376  if (r > 0) {
377  Z80_BC -= r;
378  Z80_DE += r;
379  if (increment_offset(r)) // this may set error str and Z80_A!
380  break;
381  } else if (!r) {
382  xep_set_error(HOST_OS_STR "Cannot write block");
384  break;
385  } else {
386  fileio_host_errstr();
388  break;
389  }
390  }
391 }
392 
393 
395 {
397  if (fio_fd[channel] < 0)
398  Z80_A = 0xFB; // invalid channel
399  else {
400  int r;
401  if (host_reseek()) // this may set error str and Z80_A!
402  return;
403  r = write(fio_fd[channel], &Z80_B, 1);
404  if (r == 1) {
405  Z80_A = 0;
406  increment_offset(1); // this may set error str and Z80_A!
407  } else if (!r) {
408  xep_set_error(HOST_OS_STR "Cannot write character");
410  } else {
411  fileio_host_errstr();
413  }
414  }
415 }
416 
417 
419 {
420  Z80_A = 0xE7;
421 }
422 
423 
424 
426 {
428  if (fio_fd[channel] < 0) {
429  Z80_A = 0xFB; // invalid channel
430  } else {
431  Uint8 stat[16];
432  int a, de;
433  for (a = 0, de = Z80_DE; a < 16; a++)
434  stat[a] = a < 9 ? read_cpu_byte_by_segmap(de++, EXOS_USER_SEGMAP_P) : 0;
435  if (Z80_C & 1)
436  fio_off[channel] = stat[0] | (stat[1] << 8) | (stat[2] << 16) | (stat[3] << 24);
437  else {
438  stat[0] = fio_off[channel] & 0xFF;
439  stat[1] = (fio_off[channel] >> 8) & 0xFF;
440  stat[2] = (fio_off[channel] >> 16) & 0xFF;
441  stat[3] = (fio_off[channel] >> 24) & 0xFF;
442  }
443  if (Z80_C & 4)
444  fio_prot[channel] = stat[8]; // protection byte?!
445  else
446  stat[8] = fio_prot[channel];
447  stat[4] = fio_size[channel] & 0xFF;
448  stat[5] = (fio_size[channel] >> 8) & 0xFF;
449  stat[6] = (fio_size[channel] >> 16) & 0xFF;
450  stat[7] = (fio_size[channel] >> 24) & 0xFF;
451  for (a = 0, de = Z80_DE; a < 16; a++)
453  Z80_DE = de; // will DE change?! FIXME ! simply remove this line, if it's not the case ...
454  Z80_A = 0; // OK
455  Z80_C = 3; // read flags
456  }
457 }
458 
459 
460 
462 {
463  Z80_A = 0xE7;
464 }
465 
466 
467 
468 void fileio_func_init ( void )
469 {
470  int a;
471  for (a = 0; a < 0x100; a++)
472  if (fio_fd[a] != -1) {
473  close(fio_fd[a]);
474  fio_fd[a] = -1;
475  free(fio_name[a]);
476  fio_name[a] = NULL;
477  }
478 }
479 
480 
482 {
483  // no return code needed, and we don't care as we have our data in the C code :)
484 }
485 
486 
487 
489 {
490  // Currently we don't allow files to be deleted via FILE:.
491  // Let's just close the channel ...
493 }
494 
495 
496 
498 {
499  Z80_A = 0xE7;
500 }
Z80_DE
#define Z80_DE
Definition: z80ex.h:85
fileio_func_write_character
void fileio_func_write_character(void)
Definition: fileio.c:394
fileio_func_open_channel_remember
void fileio_func_open_channel_remember(void)
Definition: fileio.c:176
read_cpu_byte
Uint8 read_cpu_byte(Uint16 addr)
Definition: cpu.c:258
fileio_func_not_used_call
void fileio_func_not_used_call(void)
Definition: fileio.c:497
Z80_A
#define Z80_A
Definition: z80ex.h:75
emutools.h
O_BINARY
#define O_BINARY
Definition: emutools_basicdefs.h:140
xemugui_file_selector
int xemugui_file_selector(int dialog_mode, const char *dialog_title, char *default_dir, char *selected, int path_max_size)
Definition: emutools_gui.c:126
fileio_func_destroy_channel
void fileio_func_destroy_channel(void)
Definition: fileio.c:488
FILEIO_MAX_FILE_SIZE
#define FILEIO_MAX_FILE_SIZE
Definition: fileio.c:40
fd
int fd
Definition: hdos.c:62
fileio_func_write_block
void fileio_func_write_block(void)
Definition: fileio.c:356
emu_rom_interface.h
fileio_func_special_function
void fileio_func_special_function(void)
Definition: fileio.c:461
emutools_gui.h
fileio_func_read_character
void fileio_func_read_character(void)
Definition: fileio.c:331
fileio.h
HOST_OS_STR
#define HOST_OS_STR
Definition: fileio.c:37
EXOS_USER_SEGMAP_P
#define EXOS_USER_SEGMAP_P
Definition: fileio.c:36
XEMUGUI_FSEL_FLAG_STORE_DIR
#define XEMUGUI_FSEL_FLAG_STORE_DIR
Definition: emutools_gui.h:28
Uint8
uint8_t Uint8
Definition: fat32.c:51
write_cpu_byte_by_segmap
void write_cpu_byte_by_segmap(Uint16 addr, Uint8 *segmap, Uint8 data)
Definition: cpu.c:270
inc
int inc
Definition: vera.c:53
z80.h
DEBUGPRINT
#define DEBUGPRINT(...)
Definition: emutools_basicdefs.h:171
fileio_func_buffer_moved
void fileio_func_buffer_moved(void)
Definition: fileio.c:481
fileio_func_open_or_create_channel
void fileio_func_open_or_create_channel(int create)
Definition: fileio.c:183
fileio_func_set_channel_status
void fileio_func_set_channel_status(void)
Definition: fileio.c:425
fileio_func_init
void fileio_func_init(void)
Definition: fileio.c:468
dir
DIR * dir
Definition: cpmfs.c:46
fileio_init
void fileio_init(const char *dir, const char *subdir)
Definition: fileio.c:55
mode
int mode
Definition: vera.c:61
cpu.h
NL
#define NL
Definition: fat32.c:37
compress_sd_image.r
def r
Definition: compress_sd_image.py:76
XEMUGUI_FSEL_OPEN
#define XEMUGUI_FSEL_OPEN
Definition: emutools_gui.h:26
XEP_ERROR_CODE
#define XEP_ERROR_CODE
Definition: emu_rom_interface.h:22
xep_set_error
void xep_set_error(const char *msg)
Definition: emu_rom_interface.c:64
FILE_TOO_LARGE
#define FILE_TOO_LARGE
Definition: fileio.c:38
DIRSEP_STR
#define DIRSEP_STR
Definition: emutools_basicdefs.h:141
Z80_C
#define Z80_C
Definition: z80ex.h:80
Z80_BC
#define Z80_BC
Definition: z80ex.h:81
Z80_B
#define Z80_B
Definition: z80ex.h:79
fileio_func_channel_read_status
void fileio_func_channel_read_status(void)
Definition: fileio.c:418
SET_CHANNEL
#define SET_CHANNEL(v)
Definition: fileio.c:42
read_cpu_byte_by_segmap
Uint8 read_cpu_byte_by_segmap(Uint16 addr, Uint8 *segmap)
Definition: cpu.c:264
ERRSTR
#define ERRSTR()
Definition: enterprise128.h:56
fileio_func_close_channel
void fileio_func_close_channel(void)
Definition: fileio.c:235
enterprise128.h
DEBUG
#define DEBUG(...)
Definition: emutools_basicdefs.h:167
fileio_func_read_block
void fileio_func_read_block(void)
Definition: fileio.c:288
st
struct stat st
Definition: cpmfs.c:43
fileio_cwd
char fileio_cwd[PATH_MAX+1]
Definition: fileio.c:44
DIRSEP_CHR
#define DIRSEP_CHR
Definition: emutools_basicdefs.h:142