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