2018-08-20 19:43:06 +00:00
|
|
|
|
|
|
|
#define _BSD_SOURCE
|
|
|
|
|
2018-08-23 04:38:53 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
#include <Windows.h>
|
|
|
|
#else
|
|
|
|
|
2018-08-20 19:43:06 +00:00
|
|
|
#include <dirent.h>
|
2018-08-23 04:38:53 +00:00
|
|
|
#include <fcntl.h>
|
|
|
|
#include <libgen.h>
|
|
|
|
#include <time.h>
|
2018-08-20 19:43:06 +00:00
|
|
|
#include <unistd.h>
|
2018-08-23 04:38:53 +00:00
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <assert.h>
|
2018-08-20 19:43:06 +00:00
|
|
|
#include <ctype.h>
|
2018-08-23 04:38:53 +00:00
|
|
|
#include <errno.h>
|
2018-08-20 19:43:06 +00:00
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
|
|
|
|
#include "defc.h"
|
|
|
|
#include "gsos.h"
|
|
|
|
#include "fst.h"
|
|
|
|
|
|
|
|
|
|
|
|
#include "host_common.h"
|
|
|
|
|
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
#define LEVEL 0xBFD8 // current file level
|
|
|
|
#define DEVNUM 0xBF30 // last slot / drive
|
|
|
|
#define DEVCNT 0xBF31 // count - 1
|
|
|
|
#define DEVLST 0xBF32 // active device list
|
|
|
|
#define PFIXPTR 0xbf9a // active prefix?
|
2018-08-20 19:43:06 +00:00
|
|
|
|
|
|
|
/* bf97 is appletalk prefix? */
|
|
|
|
|
|
|
|
|
|
|
|
#define ENTRY_LENGTH 0x27
|
|
|
|
#define ENTRIES_PER_BLOCK 0x0d
|
|
|
|
|
|
|
|
|
|
|
|
static unsigned saved_call;
|
|
|
|
static unsigned saved_dcb;
|
|
|
|
static unsigned saved_p;
|
|
|
|
static unsigned saved_patch_address;
|
|
|
|
static unsigned saved_mli_address;
|
|
|
|
static unsigned saved_unit;
|
|
|
|
static char *saved_prefix;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
enum {
|
2018-08-21 00:57:45 +00:00
|
|
|
CREATE = 0xc0,
|
|
|
|
DESTROY = 0xc1,
|
|
|
|
RENAME = 0xc2,
|
|
|
|
SET_FILE_INFO = 0xc3,
|
|
|
|
GET_FILE_INFO = 0xc4,
|
|
|
|
ONLINE = 0xc5,
|
|
|
|
SET_PREFIX = 0xc6,
|
|
|
|
GET_PREFIX = 0xc7,
|
|
|
|
OPEN = 0xc8,
|
|
|
|
NEWLINE = 0xc9,
|
|
|
|
READ = 0xca,
|
|
|
|
WRITE = 0xcb,
|
|
|
|
CLOSE = 0xcc,
|
|
|
|
FLUSH = 0xcd,
|
|
|
|
SET_MARK = 0xce,
|
|
|
|
GET_MARK = 0xcf,
|
|
|
|
SET_EOF = 0xd0,
|
|
|
|
GET_EOF = 0xd1,
|
|
|
|
SET_BUF = 0xd2,
|
|
|
|
GET_BUF = 0xd3,
|
|
|
|
ALLOC_INTERRUPT = 0x40,
|
|
|
|
DEALLOC_INTERRUPT = 0x41,
|
|
|
|
ATINIT = 0x99,
|
|
|
|
READ_BLOCK = 0x80,
|
|
|
|
WRITE_BLOCK = 0x81,
|
|
|
|
GET_TIME = 0x82,
|
|
|
|
QUIT = 0x65
|
2018-08-20 19:43:06 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
#define badBufferAddress 0x56
|
|
|
|
|
|
|
|
struct file_entry {
|
2018-08-21 00:57:45 +00:00
|
|
|
unsigned type;
|
|
|
|
unsigned translate;
|
2018-08-23 04:38:53 +00:00
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
HANDLE h;
|
|
|
|
#else
|
2018-08-21 00:57:45 +00:00
|
|
|
int fd;
|
2018-08-23 04:38:53 +00:00
|
|
|
#endif
|
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
unsigned buffer;
|
|
|
|
unsigned level;
|
|
|
|
unsigned newline_mask;
|
|
|
|
unsigned newline_char;
|
|
|
|
|
|
|
|
/* directory stuff */
|
|
|
|
unsigned mark;
|
|
|
|
unsigned eof;
|
|
|
|
unsigned char *directory_buffer;
|
2018-08-20 19:43:06 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
#define MAX_FILES 8
|
|
|
|
#define MIN_FILE 0x40
|
|
|
|
#define MAX_FILE 0x48
|
|
|
|
struct file_entry files[MAX_FILES];
|
|
|
|
|
|
|
|
|
2018-08-23 14:56:09 +00:00
|
|
|
|
|
|
|
|
|
|
|
#if _WIN32
|
|
|
|
static word16 file_open(const char *path, struct file_entry *file) {
|
|
|
|
|
|
|
|
HANDLE h;
|
|
|
|
|
|
|
|
if (g_cfg_host_read_only) {
|
|
|
|
h = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
} else {
|
|
|
|
h = CreateFile(path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
if (h == INVALID_HANDLE_VALUE) {
|
|
|
|
h = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (h == INVALID_HANDLE_VALUE) return host_map_win32_error(GetLastError());
|
|
|
|
|
|
|
|
file->h = h;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static word16 file_close(struct file_entry *file) {
|
|
|
|
|
|
|
|
if (!file->type) return invalidRefNum;
|
|
|
|
|
|
|
|
if (file->h != INVALID_HANDLE_VALUE) CloseHandle(file->h);
|
|
|
|
|
|
|
|
if (file->directory_buffer) free(file->directory_buffer);
|
|
|
|
memset(file, 0, sizeof(*file));
|
|
|
|
file->h = INVALID_HANDLE_VALUE;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static word16 file_flush(struct file_entry *file) {
|
|
|
|
if (!file->type) return invalidRefNum;
|
|
|
|
|
|
|
|
if (file->h != INVALID_HANDLE_VALUE)
|
|
|
|
FlushFileBuffers(file->h);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static word16 file_eof(struct file_entry *file) {
|
|
|
|
LARGE_INTEGER li;
|
|
|
|
if (!GetFileSizeEx(file->h, &li))
|
|
|
|
return host_map_win32_error(GetLastError());
|
|
|
|
if (li.QuadPart > 0xffffff) {
|
|
|
|
file->eof = 0xffffff;
|
|
|
|
return outOfRange;
|
|
|
|
}
|
|
|
|
file->eof = li.QuadPart;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
static word16 file_open(const char *path, struct file_entry *file) {
|
|
|
|
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
if (g_cfg_host_read_only) {
|
|
|
|
fd = open(path, O_RDONLY | O_NONBLOCK);
|
|
|
|
} else {
|
|
|
|
fd = open(path, O_RDWR | O_NONBLOCK);
|
|
|
|
if (fd < 0) {
|
|
|
|
fd = open(path, O_RDONLY | O_NONBLOCK);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (fd < 0) return host_map_errno_path(errno, path);
|
|
|
|
|
|
|
|
file->fd = fd;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static word16 file_close(struct file_entry *file) {
|
|
|
|
|
|
|
|
if (!file->type) return invalidRefNum;
|
|
|
|
|
|
|
|
if (file->fd >= 0) close(file->fd);
|
|
|
|
|
|
|
|
if (file->directory_buffer) free(file->directory_buffer);
|
|
|
|
memset(file, 0, sizeof(*file));
|
|
|
|
file->fd = -1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static word16 file_flush(struct file_entry *file) {
|
|
|
|
if (!file->type) return invalidRefNum;
|
|
|
|
|
|
|
|
if (file->fd >= 0)
|
|
|
|
fsync(file->fd);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static word16 file_eof(struct file_entry *file) {
|
|
|
|
off_t eof;
|
|
|
|
eof = lseek(file->fd, 0, SEEK_END);
|
|
|
|
if (eof < 0) return host_map_errno(errno);
|
|
|
|
if (eof > 0xffffff) {
|
|
|
|
file->eof = 0xffffff;
|
|
|
|
return outOfRange;
|
|
|
|
}
|
|
|
|
file->eof = eof;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-08-20 19:43:06 +00:00
|
|
|
static char *get_pstr(word32 ptr) {
|
2018-08-21 00:57:45 +00:00
|
|
|
if (!ptr) return NULL;
|
|
|
|
int length = get_memory_c(ptr, 0);
|
|
|
|
ptr += 1;
|
|
|
|
char *str = host_gc_malloc(length + 1);
|
|
|
|
for (int i = 0; i < length; ++i) {
|
|
|
|
char c = get_memory_c(ptr+i, 0) & 0x7f;
|
|
|
|
str[i] = c;
|
|
|
|
}
|
|
|
|
str[length] = 0;
|
|
|
|
return str;
|
2018-08-20 19:43:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static word32 set_pstr(word32 ptr, const char *str) {
|
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
if (!ptr) return paramRangeErr;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
int l = str ? strlen(str) : 0;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
if (l > 255) return buffTooSmall;
|
|
|
|
// / is the pascal separator.
|
|
|
|
set_memory_c(ptr++, l, 0);
|
|
|
|
for (int i = 0; i < l; ++i) {
|
|
|
|
set_memory_c(ptr++, *str++, 0);
|
|
|
|
}
|
|
|
|
return 0;
|
2018-08-20 19:43:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static char *is_host_path(unsigned pathname) {
|
2018-08-21 00:57:45 +00:00
|
|
|
/* add + 5 below to skip past the /HOST/ part */
|
|
|
|
/* (prefix may be missing trailing / ) */
|
|
|
|
char *p = get_pstr(pathname);
|
|
|
|
if (!p) return NULL;
|
|
|
|
if (*p == '/') {
|
|
|
|
if (!strncasecmp(p, "/HOST", 5)) {
|
|
|
|
if (p[5] == 0) return "/";
|
|
|
|
if (p[5] == '/') return p + 5;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (saved_prefix) {
|
|
|
|
return host_gc_append_path(saved_prefix + 5, p);
|
|
|
|
}
|
|
|
|
return NULL;
|
2018-08-20 19:43:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static unsigned lowercase_bits(const char *name) {
|
2018-08-21 00:57:45 +00:00
|
|
|
unsigned rv = 0x8000;
|
|
|
|
unsigned bit = 0x4000;
|
|
|
|
for(unsigned i = 0; i < 15; ++i, bit >>= 1) {
|
|
|
|
char c = name[i];
|
|
|
|
if (c == 0) break;
|
|
|
|
if (islower(c)) rv |= bit;
|
|
|
|
}
|
|
|
|
return rv;
|
2018-08-20 19:43:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* name is relative to the host directory. */
|
|
|
|
|
2018-08-23 04:38:53 +00:00
|
|
|
void *create_directory_file(const char *name, const char *path, word16 *error_ptr, unsigned *block_ptr) {
|
2018-08-20 19:43:06 +00:00
|
|
|
|
|
|
|
|
2018-08-23 04:38:53 +00:00
|
|
|
byte *data = NULL;
|
2018-08-21 00:57:45 +00:00
|
|
|
int capacity = 0;
|
|
|
|
int count = 0;
|
|
|
|
unsigned offset = 0;
|
|
|
|
int i;
|
|
|
|
struct file_info fi;
|
|
|
|
unsigned blocks = 1;
|
|
|
|
unsigned terr;
|
|
|
|
|
|
|
|
word32 w32;
|
|
|
|
|
2018-08-23 04:38:53 +00:00
|
|
|
char **entries = NULL;
|
|
|
|
size_t entry_count = 0;
|
|
|
|
terr = host_scan_directory(path, &entries, &entry_count, 1);
|
|
|
|
if (terr) {
|
|
|
|
*error_ptr = terr;
|
|
|
|
return NULL;
|
2018-08-21 00:57:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* also need space for volume/directory header */
|
|
|
|
capacity = 1 + (1 + entry_count) / ENTRIES_PER_BLOCK;
|
|
|
|
capacity *= 512;
|
|
|
|
|
|
|
|
data = malloc(capacity);
|
|
|
|
if (!data) {
|
|
|
|
*error_ptr = outOfMem;
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
memset(data, 0, capacity);
|
|
|
|
|
|
|
|
|
|
|
|
terr = host_get_file_info(path, &fi);
|
|
|
|
if (terr) {
|
|
|
|
*error_ptr = terr;
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
2018-08-23 04:38:53 +00:00
|
|
|
/* trailing /s should already be stripped */
|
2018-08-21 00:57:45 +00:00
|
|
|
const char *base_name = strchr(name, '/');
|
|
|
|
base_name = base_name ? base_name + 1 : name;
|
|
|
|
if (!base_name || !*base_name) base_name = "HOST";
|
|
|
|
|
|
|
|
int len = strlen(base_name);
|
|
|
|
if (len > 15) len = 15;
|
|
|
|
/* previous / next pointers */
|
|
|
|
data[0] = 0;
|
|
|
|
data[1] = 0;
|
|
|
|
data[2] = 0;
|
|
|
|
data[3] = 0;
|
|
|
|
offset = 4;
|
|
|
|
|
|
|
|
int root = fi.storage_type == 0x0f;
|
|
|
|
if (root) {
|
|
|
|
data[offset++] = 0xf0 | len;
|
|
|
|
} else {
|
|
|
|
data[offset++] = 0xe0 | len;
|
|
|
|
}
|
|
|
|
for (i = 0; i < len; ++i) {
|
|
|
|
data[offset++] = toupper(base_name[i]);
|
|
|
|
}
|
|
|
|
for(; i < 15; ++i) { data[offset++] = 0; }
|
|
|
|
if (root) {
|
|
|
|
data[offset++] = 0; /* reserved */
|
|
|
|
data[offset++] = 0;
|
|
|
|
|
|
|
|
w32 = host_convert_date_time(fi.modified_date);
|
|
|
|
|
|
|
|
data[offset++] = (w32 >> 0) & 0xff; /* last modified... */
|
|
|
|
data[offset++] = (w32 >> 8) & 0xff;
|
|
|
|
data[offset++] = (w32 >> 16) & 0xff;
|
|
|
|
data[offset++] = (w32 >> 24) & 0xff;
|
|
|
|
|
|
|
|
w32 = lowercase_bits(base_name);
|
|
|
|
data[offset++] = (w32 >> 0) & 0xff; /* file name case bits */
|
|
|
|
data[offset++] = (w32 >> 8) & 0xff;
|
|
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
data[offset++] = 0x75; /* password enabled */
|
|
|
|
for (i = 0; i < 7; ++i) data[offset++] = 0; /* password */
|
|
|
|
}
|
|
|
|
|
|
|
|
w32 = host_convert_date_time(fi.create_date);
|
|
|
|
|
|
|
|
data[offset++] = (w32 >> 0) & 0xff; /* creation... */
|
|
|
|
data[offset++] = (w32 >> 8) & 0xff;
|
|
|
|
data[offset++] = (w32 >> 16) & 0xff;
|
|
|
|
data[offset++] = (w32 >> 24) & 0xff;
|
|
|
|
|
|
|
|
data[offset++] = 0; /* version */
|
|
|
|
data[offset++] = 0; /* min version */
|
|
|
|
data[offset++] = fi.access; /* access */
|
|
|
|
data[offset++] = ENTRY_LENGTH; /* entry length */
|
|
|
|
data[offset++] = ENTRIES_PER_BLOCK; /* entries per block */
|
|
|
|
|
|
|
|
data[offset++] = 0; /* file count (placeholder) */
|
|
|
|
data[offset++] = 0;
|
|
|
|
|
|
|
|
data[offset++] = 0; /* bitmap ptr, parent ptr, etc */
|
|
|
|
data[offset++] = 0;
|
|
|
|
data[offset++] = 0;
|
|
|
|
data[offset++] = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int path_len = strlen(path);
|
|
|
|
|
|
|
|
count = 0;
|
|
|
|
blocks = 1;
|
|
|
|
for (int j = 0; j < entry_count; ++j) {
|
|
|
|
|
2018-08-23 04:38:53 +00:00
|
|
|
char *name = entries[j];
|
2018-08-21 00:57:45 +00:00
|
|
|
int len = strlen(name);
|
|
|
|
char *tmp = malloc(path_len + 2 + len);
|
|
|
|
if (!tmp) continue;
|
|
|
|
|
|
|
|
memcpy(tmp, path, path_len);
|
|
|
|
tmp[path_len] = '/';
|
|
|
|
strcpy(tmp + path_len + 1, name);
|
|
|
|
|
|
|
|
unsigned terr = host_get_file_info(tmp, &fi);
|
|
|
|
free(tmp);
|
|
|
|
if (terr) continue;
|
|
|
|
|
|
|
|
/* wrap to next block ? */
|
|
|
|
if ((offset + ENTRY_LENGTH) >> 9 == blocks) {
|
|
|
|
blocks++;
|
|
|
|
offset = (offset + 511) & ~511;
|
|
|
|
data[offset++] = 0; /* next/previous block pointers */
|
|
|
|
data[offset++] = 0;
|
|
|
|
data[offset++] = 0;
|
|
|
|
data[offset++] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(offset + ENTRY_LENGTH < capacity);
|
|
|
|
|
|
|
|
/* create a prodos entry... */
|
|
|
|
|
|
|
|
|
|
|
|
if (fi.storage_type == extendedFile)
|
|
|
|
fi.storage_type = 1;
|
|
|
|
int st = fi.storage_type << 4;
|
|
|
|
|
|
|
|
data[offset++] = st | len;
|
|
|
|
for(i = 0; i < len; ++i) {
|
|
|
|
data[offset++] = toupper(name[i]);
|
|
|
|
}
|
|
|
|
for(; i < 15; ++i) data[offset++] = 0;
|
|
|
|
data[offset++] = fi.file_type;
|
|
|
|
data[offset++] = 0; /* key ptr */
|
|
|
|
data[offset++] = 0; /* key ptr */
|
|
|
|
data[offset++] = fi.blocks & 0xff;
|
|
|
|
data[offset++] = (fi.blocks >> 8) & 0xff;
|
|
|
|
data[offset++] = fi.eof & 0xff;
|
|
|
|
data[offset++] = (fi.eof >> 8) & 0xff;
|
|
|
|
data[offset++] = (fi.eof >> 16) & 0xff;
|
|
|
|
|
|
|
|
w32 = host_convert_date_time(fi.create_date);
|
|
|
|
|
|
|
|
data[offset++] = (w32 >> 0) & 0xff; /* creation... */
|
|
|
|
data[offset++] = (w32 >> 8) & 0xff;
|
|
|
|
data[offset++] = (w32 >> 16) & 0xff;
|
|
|
|
data[offset++] = (w32 >> 24) & 0xff;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
w32 = lowercase_bits(name);
|
|
|
|
data[offset++] = (w32 >> 0) & 0xff; /* file name case bits */
|
|
|
|
data[offset++] = (w32 >> 8) & 0xff;
|
|
|
|
|
|
|
|
data[offset++] = fi.access;
|
|
|
|
data[offset++] = fi.aux_type & 0xff;
|
|
|
|
data[offset++] = (fi.aux_type >> 8) & 0xff;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
w32 = host_convert_date_time(fi.modified_date);
|
|
|
|
data[offset++] = (w32 >> 0) & 0xff; /* last mod... */
|
|
|
|
data[offset++] = (w32 >> 8) & 0xff;
|
|
|
|
data[offset++] = (w32 >> 16) & 0xff;
|
|
|
|
data[offset++] = (w32 >> 24) & 0xff;
|
|
|
|
|
|
|
|
data[offset++] = 0; /* header ptr */
|
|
|
|
data[offset++] = 0;
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
data[4 + 0x21] = count & 0xff;
|
|
|
|
data[4 + 0x22] = (count >> 8)& 0xff;
|
|
|
|
*block_ptr = blocks;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
|
|
|
exit:
|
2018-08-23 04:38:53 +00:00
|
|
|
if (entries) host_free_directory(entries, entry_count);
|
2018-08-21 00:57:45 +00:00
|
|
|
return data;
|
2018-08-20 19:43:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int mli_atinit(unsigned dcb) {
|
2018-08-21 00:57:45 +00:00
|
|
|
unsigned pcount = get_memory_c(dcb, 0);
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
if (pcount < 3|| pcount > 4) return invalidPcount;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
unsigned version = get_memory_c(dcb + 1, 0);
|
|
|
|
saved_mli_address = get_memory16_c(dcb + 2, 0);
|
|
|
|
saved_patch_address = get_memory16_c(dcb + 4, 0);
|
|
|
|
if (!saved_patch_address) return paramRangeErr;
|
|
|
|
if (!saved_mli_address) return paramRangeErr;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
saved_unit = 0x80;
|
2018-08-21 01:34:59 +00:00
|
|
|
if (pcount >= 4) saved_unit = get_memory_c(dcb + 6, 0) & 0xf0;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
if (!saved_unit) return paramRangeErr;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
saved_prefix = 0;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
memset(files, 0, sizeof(files)); /* ??? */
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
return host_startup();
|
2018-08-20 19:43:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
enum {
|
2018-08-21 00:57:45 +00:00
|
|
|
FILE_INFO_pcount = 0,
|
|
|
|
FILE_INFO_pathname = 1,
|
|
|
|
FILE_INFO_access = 3,
|
|
|
|
FILE_INFO_file_type = 4,
|
|
|
|
FILE_INFO_aux_type = 5,
|
|
|
|
FILE_INFO_storage_type = 7,
|
|
|
|
FILE_INFO_blocks = 8,
|
|
|
|
FILE_INFO_mod_date = 10,
|
|
|
|
FILE_INFO_mod_time = 12,
|
|
|
|
FILE_INFO_create_date = 14,
|
|
|
|
FILE_INFO_create_time = 16
|
2018-08-20 19:43:06 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static int mli_set_file_info(unsigned dcb, const char *path) {
|
|
|
|
|
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
struct file_info fi;
|
|
|
|
memset(&fi, 0, sizeof(fi));
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
fi.access = get_memory_c(dcb + FILE_INFO_access, 0);
|
|
|
|
fi.file_type = get_memory_c(dcb + FILE_INFO_file_type, 0);
|
|
|
|
fi.aux_type = get_memory16_c(dcb + FILE_INFO_aux_type, 0);
|
|
|
|
fi.modified_date = host_get_date_time(dcb + FILE_INFO_mod_date);
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
return host_set_file_info(path, &fi);
|
2018-08-20 19:43:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int mli_get_file_info(unsigned dcb, const char *path) {
|
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
struct file_info fi;
|
|
|
|
int rv = host_get_file_info(path, &fi);
|
|
|
|
if (rv) return rv;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
/* ignore resource fork */
|
|
|
|
if (fi.storage_type == extendedFile)
|
|
|
|
fi.storage_type = seedling;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
set_memory_c(dcb + FILE_INFO_access, fi.access, 0);
|
|
|
|
set_memory_c(dcb + FILE_INFO_file_type, fi.file_type, 0);
|
|
|
|
set_memory16_c(dcb + FILE_INFO_aux_type, fi.aux_type, 0);
|
|
|
|
set_memory_c(dcb + FILE_INFO_storage_type, fi.storage_type, 0);
|
|
|
|
set_memory16_c(dcb + FILE_INFO_blocks, fi.blocks, 0);
|
|
|
|
host_set_date_time(dcb + FILE_INFO_mod_date, fi.modified_date);
|
|
|
|
host_set_date_time(dcb + FILE_INFO_create_date, fi.create_date);
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
return 0;
|
2018-08-20 19:43:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
enum {
|
2018-08-21 00:57:45 +00:00
|
|
|
CREATE_pcount = 0,
|
|
|
|
CREATE_pathname = 1,
|
|
|
|
CREATE_access = 3,
|
|
|
|
CREATE_file_type = 4,
|
|
|
|
CREATE_aux_type = 5,
|
|
|
|
CREATE_storage_type = 7,
|
|
|
|
CREATE_create_date = 8,
|
|
|
|
CREATE_create_time = 10
|
2018-08-20 19:43:06 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static int mli_create(unsigned dcb, const char *path) {
|
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
struct file_info fi;
|
|
|
|
memset(&fi, 0, sizeof(fi));
|
|
|
|
|
|
|
|
fi.access = get_memory_c(dcb + CREATE_access, 0);
|
|
|
|
fi.file_type = get_memory_c(dcb + CREATE_file_type, 0);
|
|
|
|
fi.aux_type = get_memory16_c(dcb + CREATE_aux_type, 0);
|
|
|
|
fi.storage_type = get_memory_c(dcb + CREATE_storage_type, 0);
|
|
|
|
fi.create_date = host_get_date_time(dcb + CREATE_create_date);
|
|
|
|
|
|
|
|
if (g_cfg_host_read_only) return drvrWrtProt;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* actual prodos needs the correct storage type and file type
|
|
|
|
* for a usable directory. this just does the right thing.
|
|
|
|
* TODO - does ProDOS update the storage type in the dcb?
|
|
|
|
*/
|
|
|
|
switch(fi.storage_type) {
|
|
|
|
case 0x0d:
|
|
|
|
fi.file_type = 0x0f;
|
|
|
|
break;
|
|
|
|
case 0x01:
|
|
|
|
case 0x02:
|
|
|
|
case 0x03:
|
|
|
|
fi.storage_type = 0x01;
|
|
|
|
break;
|
|
|
|
case 0x00:
|
|
|
|
if (fi.file_type == 0x0f) fi.storage_type = 0x0d;
|
|
|
|
else fi.storage_type = 0x01;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return badStoreType;
|
|
|
|
}
|
|
|
|
// todo -- remap access.
|
|
|
|
|
|
|
|
if (fi.storage_type == 0x0d) {
|
2018-08-23 04:38:53 +00:00
|
|
|
#if _WIN32
|
|
|
|
if (!CreateDirectory(path, NULL))
|
|
|
|
return host_map_win32_error(GetLastError());
|
|
|
|
#else
|
|
|
|
if (mkdir(path, 0777) < 0)
|
2018-08-21 00:57:45 +00:00
|
|
|
return host_map_errno_path(errno, path);
|
2018-08-23 04:38:53 +00:00
|
|
|
#endif
|
2018-08-21 00:57:45 +00:00
|
|
|
} else {
|
2018-08-23 04:38:53 +00:00
|
|
|
#if _WIN32
|
|
|
|
HANDLE h = CreateFile(path, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
if (h == INVALID_HANDLE_VALUE) return host_map_win32_error(GetLastError());
|
|
|
|
|
|
|
|
afp_init(&fi.afp, fi.file_type, fi.aux_type);
|
|
|
|
fi.has_fi = 1;
|
|
|
|
|
|
|
|
// set ftype, auxtype...
|
|
|
|
host_set_file_info(path, &fi);
|
|
|
|
CloseHandle(h);
|
|
|
|
#else
|
2018-08-21 00:57:45 +00:00
|
|
|
int fd = open(path, O_CREAT | O_EXCL | O_RDONLY, 0666);
|
|
|
|
if (fd < 0)
|
|
|
|
return host_map_errno_path(errno, path);
|
|
|
|
|
|
|
|
host_file_type_to_finder_info(fi.finder_info, fi.file_type, fi.aux_type);
|
|
|
|
fi.has_fi = 1;
|
|
|
|
host_set_file_info(path, &fi);
|
|
|
|
close(fd);
|
2018-08-23 04:38:53 +00:00
|
|
|
#endif
|
2018-08-21 00:57:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2018-08-20 19:43:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int mli_destroy(unsigned dcb, const char *path) {
|
|
|
|
|
2018-08-23 04:38:53 +00:00
|
|
|
word16 terr = 0;
|
|
|
|
unsigned type = host_storage_type(path, &terr);
|
|
|
|
if (type == 0) return terr;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-23 04:38:53 +00:00
|
|
|
switch(type) {
|
|
|
|
case 0: return terr;
|
|
|
|
case 0x0f: return badPathSyntax; /* root directory */
|
|
|
|
case 0x0d:
|
|
|
|
#if _WIN32
|
|
|
|
if (!RemoveDirectory(path))
|
|
|
|
return host_map_win32_error(GetLastError());
|
|
|
|
#else
|
|
|
|
if (rmdir(path) < 0)
|
|
|
|
return host_map_errno_path(errno, path);
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
case 2:
|
|
|
|
case 3:
|
|
|
|
case 5:
|
|
|
|
#if _WIN32
|
|
|
|
if (!DeleteFile(path))
|
|
|
|
return host_map_win32_error(GetLastError());
|
|
|
|
#else
|
|
|
|
if (unlink(path) < 0)
|
|
|
|
return host_map_errno_path(errno, path);
|
|
|
|
#endif
|
|
|
|
default:
|
|
|
|
return badStoreType;
|
|
|
|
}
|
2018-08-21 00:57:45 +00:00
|
|
|
return 0;
|
2018-08-20 19:43:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int mli_rename(unsigned dcb, const char *path1, const char *path2) {
|
2018-08-21 00:57:45 +00:00
|
|
|
/* can't rename the root directory */
|
2018-08-23 04:38:53 +00:00
|
|
|
word16 terr = 0;
|
|
|
|
unsigned type;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
if (!path1 || !path2) return badPathSyntax;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
|
|
|
|
2018-08-23 04:38:53 +00:00
|
|
|
type = host_storage_type(path1, &terr);
|
|
|
|
if (!type) return terr;
|
|
|
|
if (type == 0x0f) return badPathSyntax;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-23 04:38:53 +00:00
|
|
|
type = host_storage_type(path2, &terr);
|
|
|
|
if (type) return dupPathname;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-23 04:38:53 +00:00
|
|
|
#if _WIN32
|
|
|
|
if (!MoveFile(path1, path2))
|
|
|
|
return host_map_win32_error(GetLastError());
|
|
|
|
#else
|
|
|
|
if (rename(path1, path2) < 0)
|
|
|
|
return host_map_errno(errno);
|
|
|
|
#endif
|
2018-08-21 00:57:45 +00:00
|
|
|
return 0;
|
2018-08-20 19:43:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int mli_write(unsigned dcb, struct file_entry *file) {
|
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
/* todo -- translate */
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
set_memory16_c(dcb + 6, 0, 0);
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
if (!file->type) return invalidRefNum;
|
|
|
|
if (file->type != file_regular) return invalidAccess;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
unsigned data_buffer = get_memory16_c(dcb + 2, 0);
|
|
|
|
unsigned request_count = get_memory16_c(dcb + 4, 0);
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
if (!data_buffer) return badBufferAddress;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
byte *data = host_gc_malloc(request_count);
|
|
|
|
if (!data) return drvrIOError;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
for (unsigned i = 0; i < request_count; ++i) {
|
|
|
|
data[i] = get_memory_c(data_buffer + i, 0);
|
|
|
|
}
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
switch (file->translate) {
|
|
|
|
case translate_crlf:
|
|
|
|
host_cr_to_lf(data, request_count);
|
|
|
|
break;
|
|
|
|
case translate_merlin:
|
|
|
|
host_merlin_to_text(data, request_count);
|
|
|
|
break;
|
|
|
|
}
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-23 04:38:53 +00:00
|
|
|
#if _WIN32
|
|
|
|
DWORD rv;
|
|
|
|
LARGE_INTEGER li;
|
|
|
|
li.QuadPart = file->mark;
|
|
|
|
if (!SetFilePointerEx(file->h, li, NULL, FILE_BEGIN))
|
|
|
|
return host_map_win32_error(GetLastError());
|
|
|
|
if (!WriteFile(file->h, data, request_count, &rv, NULL))
|
|
|
|
return host_map_win32_error(GetLastError());
|
|
|
|
#else
|
2018-08-21 00:57:45 +00:00
|
|
|
int rv = pwrite(file->fd, data, request_count, file->mark);
|
|
|
|
if (rv < 0) return host_map_errno(errno);
|
2018-08-23 04:38:53 +00:00
|
|
|
#endif
|
2018-08-21 00:57:45 +00:00
|
|
|
set_memory16_c(dcb + 6, rv, 0);
|
|
|
|
file->mark += rv;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
return 0;
|
2018-08-20 19:43:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int mli_read(unsigned dcb, struct file_entry *file) {
|
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
/* todo -- translate */
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
set_memory16_c(dcb + 6, 0, 0);
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
if (!file->type) return invalidRefNum;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
unsigned data_buffer = get_memory16_c(dcb + 2, 0);
|
|
|
|
unsigned request_count = get_memory16_c(dcb + 4, 0);
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
if (!data_buffer) return badBufferAddress;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
unsigned count = 0;
|
|
|
|
unsigned mask = file->newline_mask;
|
|
|
|
unsigned nl = file->newline_char;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
if (file->type == file_directory) {
|
|
|
|
unsigned mark = file->mark;
|
|
|
|
if (mark >= file->eof) return eofEncountered;
|
|
|
|
/* support newline mode ... */
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
if (mark + request_count > file->eof)
|
|
|
|
request_count = file->eof - mark;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
for (count = 0; count < request_count; ++count) {
|
|
|
|
byte b = file->directory_buffer[mark++];
|
|
|
|
set_memory_c(data_buffer + count, b, 0);
|
|
|
|
if (mask && (b & mask) == nl) break;
|
|
|
|
}
|
|
|
|
file->mark = mark;
|
|
|
|
set_memory16_c(dcb + 6, count, 0);
|
|
|
|
return 0;
|
|
|
|
}
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
if (file->type != file_regular) return invalidAccess;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
byte *data = host_gc_malloc(request_count);
|
|
|
|
if (!data) return drvrIOError;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-23 04:38:53 +00:00
|
|
|
#if _WIN32
|
|
|
|
LARGE_INTEGER li;
|
|
|
|
DWORD rv;
|
|
|
|
li.QuadPart = file->mark;
|
|
|
|
if (!SetFilePointerEx(file->h, li, NULL, FILE_BEGIN))
|
|
|
|
return host_map_win32_error(GetLastError());
|
|
|
|
|
|
|
|
if (!ReadFile(file->h, data, request_count, &rv, NULL))
|
|
|
|
return host_map_win32_error(GetLastError());
|
|
|
|
#else
|
2018-08-21 00:57:45 +00:00
|
|
|
int rv = pread(file->fd, data, request_count, file->mark);
|
|
|
|
if (rv < 0) return host_map_errno(errno);
|
2018-08-23 04:38:53 +00:00
|
|
|
#endif
|
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
if (rv == 0) return eofEncountered;
|
|
|
|
count = rv;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
switch (file->translate) {
|
|
|
|
case translate_crlf:
|
|
|
|
host_lf_to_cr(data, count);
|
|
|
|
break;
|
|
|
|
case translate_merlin:
|
|
|
|
host_text_to_merlin(data, count);
|
|
|
|
break;
|
|
|
|
}
|
2018-08-20 19:43:06 +00:00
|
|
|
|
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
if (mask) {
|
|
|
|
for (int count = 0; count < rv; ) {
|
|
|
|
byte b = data[count++];
|
|
|
|
if ((b & mask) == nl) break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (unsigned i = 0; i < count; ++i)
|
|
|
|
set_memory_c(data_buffer + i, data[i], 0);
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
file->mark += count;
|
|
|
|
set_memory16_c(dcb + 6, count, 0);
|
|
|
|
return 0;
|
2018-08-20 19:43:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int mli_get_buf(unsigned dcb, struct file_entry *file) {
|
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
if (!file->type) return invalidRefNum;
|
|
|
|
set_memory16_c(dcb + 2, file->buffer, 0);
|
|
|
|
return 0;
|
2018-08-20 19:43:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int mli_set_buf(unsigned dcb, struct file_entry *file) {
|
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
unsigned buffer = get_memory16_c(dcb + 2, 0);
|
|
|
|
if (!file->type) return invalidRefNum;
|
|
|
|
//if (!buffer) return badBufferAddress;
|
|
|
|
if (buffer & 0xff) return badBufferAddress;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
file->buffer = buffer;
|
|
|
|
return 0;
|
2018-08-20 19:43:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int mli_get_eof(unsigned dcb, struct file_entry *file) {
|
|
|
|
|
2018-08-23 14:56:09 +00:00
|
|
|
word16 terr = 0;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
switch (file->type) {
|
|
|
|
default:
|
|
|
|
return invalidRefNum;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
case file_directory:
|
|
|
|
break;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
case file_regular:
|
2018-08-23 14:56:09 +00:00
|
|
|
terr = file_eof(file);
|
|
|
|
if (terr) return terr;
|
2018-08-21 00:57:45 +00:00
|
|
|
break;
|
2018-08-23 14:56:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
set_memory24_c(dcb + 2, file->eof, 0);
|
2018-08-21 00:57:45 +00:00
|
|
|
return 0;
|
2018-08-20 19:43:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int mli_set_eof(unsigned dcb, struct file_entry *file) {
|
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
off_t eof = get_memory24_c(dcb + 2, 0);
|
2018-08-23 04:38:53 +00:00
|
|
|
#if _WIN32
|
|
|
|
LARGE_INTEGER tmp;
|
|
|
|
#endif
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
switch (file->type) {
|
|
|
|
default:
|
|
|
|
return invalidRefNum;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
case file_directory:
|
|
|
|
return invalidAccess;
|
|
|
|
break;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
case file_regular:
|
2018-08-23 04:38:53 +00:00
|
|
|
#if _WIN32
|
|
|
|
tmp.QuadPart = eof;
|
|
|
|
if (!SetFilePointerEx(file->h, tmp, NULL, FILE_BEGIN))
|
|
|
|
return host_map_win32_error(GetLastError());
|
|
|
|
if (!SetEndOfFile(file->h))
|
|
|
|
return host_map_win32_error(GetLastError());
|
|
|
|
#else
|
2018-08-21 00:57:45 +00:00
|
|
|
if (ftruncate(file->fd, eof) < 0)
|
|
|
|
return host_map_errno(errno);
|
2018-08-23 04:38:53 +00:00
|
|
|
#endif
|
2018-08-21 00:57:45 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
2018-08-20 19:43:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int mli_get_mark(unsigned dcb, struct file_entry *file) {
|
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
off_t position = 0;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
switch (file->type) {
|
|
|
|
default:
|
|
|
|
return invalidRefNum;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
case file_directory:
|
|
|
|
case file_regular:
|
|
|
|
position = file->mark;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (position > 0xffffff) return outOfRange;
|
|
|
|
set_memory24_c(dcb + 2, position, 0);
|
|
|
|
return 0;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mli_set_mark(unsigned dcb, struct file_entry *file) {
|
|
|
|
|
2018-08-23 14:56:09 +00:00
|
|
|
word16 terr = 0;
|
2018-08-23 04:38:53 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
word32 position = get_memory24_c(dcb + 2, 0);
|
|
|
|
|
|
|
|
switch (file->type) {
|
|
|
|
default:
|
|
|
|
return invalidRefNum;
|
|
|
|
case file_directory:
|
|
|
|
break;
|
|
|
|
case file_regular:
|
2018-08-23 14:56:09 +00:00
|
|
|
terr = file_eof(file);
|
|
|
|
if (terr) return terr;
|
2018-08-21 00:57:45 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2018-08-23 14:56:09 +00:00
|
|
|
if (position > file->eof) return outOfRange;
|
2018-08-21 00:57:45 +00:00
|
|
|
file->mark = position;
|
|
|
|
|
|
|
|
return 0;
|
2018-08-20 19:43:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int mli_newline(unsigned dcb, struct file_entry *file) {
|
2018-08-21 00:57:45 +00:00
|
|
|
if (!file->type) return invalidRefNum;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
file->newline_mask = get_memory_c(dcb + 2, 0);
|
|
|
|
file->newline_char = get_memory_c(dcb + 3, 0);
|
|
|
|
return 0;
|
2018-08-20 19:43:06 +00:00
|
|
|
}
|
|
|
|
|
2018-08-23 04:38:53 +00:00
|
|
|
|
|
|
|
|
2018-08-20 19:43:06 +00:00
|
|
|
static int mli_close(unsigned dcb, struct file_entry *file) {
|
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
if (!file) {
|
|
|
|
unsigned level = get_memory_c(LEVEL, 0);
|
|
|
|
unsigned i;
|
|
|
|
for (i = 0; i < MAX_FILES; ++i) {
|
|
|
|
file = &files[i];
|
|
|
|
if (!file->type) continue;
|
|
|
|
if (file->level < level) continue;
|
2018-08-23 04:38:53 +00:00
|
|
|
file_close(file);
|
2018-08-21 00:57:45 +00:00
|
|
|
}
|
|
|
|
return -1; /* pass to prodos mli */
|
|
|
|
}
|
|
|
|
|
2018-08-23 04:38:53 +00:00
|
|
|
return file_close(file);
|
2018-08-20 19:43:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int mli_flush(unsigned dcb, struct file_entry *file) {
|
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
if (!file) {
|
|
|
|
unsigned level = get_memory_c(LEVEL, 0);
|
|
|
|
unsigned i;
|
|
|
|
for (i = 0; i < MAX_FILES; ++i) {
|
|
|
|
file = &files[i];
|
|
|
|
if (!file->type) continue;
|
|
|
|
if (file->level < level) continue;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-23 04:38:53 +00:00
|
|
|
file_flush(file);
|
2018-08-21 00:57:45 +00:00
|
|
|
}
|
|
|
|
return -1; /* pass to prodos mli */
|
|
|
|
}
|
2018-08-23 04:38:53 +00:00
|
|
|
return file_flush(file);
|
2018-08-20 19:43:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* quit
|
|
|
|
* close all open files.
|
|
|
|
*/
|
|
|
|
static int mli_quit(unsigned dcb) {
|
2018-08-21 00:57:45 +00:00
|
|
|
unsigned i;
|
|
|
|
for (i = 0; i < MAX_FILES; ++i) {
|
|
|
|
struct file_entry *file = &files[i];
|
|
|
|
if (!file->type) continue;
|
2018-08-23 04:38:53 +00:00
|
|
|
|
|
|
|
#if _WIN32
|
|
|
|
if (file->h != INVALID_HANDLE_VALUE) CloseHandle(file->h);
|
|
|
|
#else
|
|
|
|
if (file->fd >= 0) close(file->fd);
|
|
|
|
#endif
|
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
if (file->directory_buffer) free(file->directory_buffer);
|
|
|
|
memset(file, 0, sizeof(*file));
|
2018-08-23 04:38:53 +00:00
|
|
|
|
|
|
|
#if _WIN32
|
|
|
|
file->h = INVALID_HANDLE_VALUE;
|
|
|
|
#else
|
|
|
|
file->fd = -1;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
}
|
|
|
|
/* need a better way to know... */
|
|
|
|
/* host_shutdown(); */
|
|
|
|
return -1;
|
2018-08-20 19:43:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
|
2018-08-23 04:38:53 +00:00
|
|
|
|
|
|
|
static int mli_open(unsigned dcb, const char *name, const char *path) {
|
|
|
|
|
|
|
|
int refnum = 0;
|
|
|
|
|
|
|
|
struct file_entry *file = NULL;
|
|
|
|
for (unsigned i = 0; i < MAX_FILES; ++i) {
|
|
|
|
file = &files[i];
|
|
|
|
if (!file->type) {
|
|
|
|
refnum = MIN_FILE + i;
|
|
|
|
break;
|
|
|
|
}
|
2018-08-21 00:57:45 +00:00
|
|
|
}
|
2018-08-23 04:38:53 +00:00
|
|
|
if (!refnum) return tooManyFilesOpen;
|
2018-08-21 00:57:45 +00:00
|
|
|
|
2018-08-23 04:38:53 +00:00
|
|
|
unsigned buffer = get_memory_c(dcb + 3, 0);
|
|
|
|
//if (buffer == 0) return badBufferAddress;
|
|
|
|
if (buffer & 0xff) return badBufferAddress;
|
2018-08-21 00:57:45 +00:00
|
|
|
|
2018-08-23 04:38:53 +00:00
|
|
|
struct file_info fi;
|
|
|
|
word16 terr = host_get_file_info(path, &fi);
|
|
|
|
if (terr) return terr;
|
|
|
|
|
|
|
|
|
|
|
|
if (fi.storage_type == 0x0f || fi.storage_type == 0x0d) {
|
|
|
|
unsigned blocks;
|
|
|
|
void *tmp;
|
|
|
|
tmp = create_directory_file(name, path, &terr, &blocks);
|
|
|
|
if (!tmp) return terr;
|
|
|
|
file->type = file_directory;
|
|
|
|
file->directory_buffer = tmp;
|
|
|
|
file->eof = blocks * 512;
|
|
|
|
} else {
|
|
|
|
terr = file_open(path, file);
|
|
|
|
if (terr) return terr;
|
2018-08-21 00:57:45 +00:00
|
|
|
|
2018-08-23 04:38:53 +00:00
|
|
|
file->type = file_regular;
|
2018-08-21 00:57:45 +00:00
|
|
|
|
|
|
|
if (g_cfg_host_crlf) {
|
|
|
|
if (fi.file_type == 0x04 || fi.file_type == 0xb0)
|
|
|
|
file->translate = translate_crlf;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_cfg_host_merlin && fi.file_type == 0x04) {
|
|
|
|
int n = strlen(path);
|
|
|
|
if (n >= 3 && path[n-1] == 'S' && path[n-2] == '.')
|
|
|
|
file->translate = translate_merlin;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
file->level = get_memory_c(LEVEL, 0);
|
|
|
|
file->buffer = buffer;
|
|
|
|
file->mark = 0;
|
|
|
|
set_memory_c(dcb + 5, refnum, 0);
|
|
|
|
return 0;
|
2018-08-20 19:43:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int mli_get_prefix(unsigned dcb) {
|
2018-08-21 00:57:45 +00:00
|
|
|
if (!saved_prefix) return -1;
|
|
|
|
unsigned buffer = get_memory16_c(dcb + 1, 0);
|
|
|
|
if (!buffer) return badBufferAddress;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
set_pstr(buffer, saved_prefix);
|
|
|
|
return 0;
|
2018-08-20 19:43:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* name is relative the the /HOST device.
|
|
|
|
* path is the path on the native fs.
|
|
|
|
*/
|
|
|
|
static int mli_set_prefix(unsigned dcb, char *name, char *path) {
|
2018-08-21 00:57:45 +00:00
|
|
|
/*
|
|
|
|
* IF path is null, tail patch if we own the prefix.
|
|
|
|
* otherwise, handle it or forward to mli.
|
|
|
|
*/
|
|
|
|
if (!path) {
|
|
|
|
return saved_prefix ? -2 : -1;
|
|
|
|
}
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-23 19:27:23 +00:00
|
|
|
unsigned type;
|
|
|
|
word16 terr;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-23 19:27:23 +00:00
|
|
|
type = host_storage_type(path, &terr);
|
|
|
|
switch(type) {
|
|
|
|
case 0x0f:
|
|
|
|
case 0x0d:
|
|
|
|
break;
|
|
|
|
case 0: return terr;
|
|
|
|
default: return badStoreType;
|
2018-08-21 00:57:45 +00:00
|
|
|
}
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
/* /HOST/ was previously stripped... add it back. */
|
|
|
|
name = host_gc_append_path("/HOST", name);
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-23 19:27:23 +00:00
|
|
|
int l = strlen(name);
|
2018-08-21 00:57:45 +00:00
|
|
|
/* trim trailing / */
|
|
|
|
while (l > 1 && name[l-1] == '/') --l;
|
|
|
|
name[l] = 0;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
if (l > 63) return badPathSyntax;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
if (saved_prefix)
|
|
|
|
free(saved_prefix);
|
|
|
|
saved_prefix = strdup(name);
|
|
|
|
set_memory_c(PFIXPTR, 1, 0);
|
|
|
|
return 0;
|
2018-08-20 19:43:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int mli_online(unsigned dcb) {
|
2018-08-21 00:57:45 +00:00
|
|
|
/* not yet ... */
|
|
|
|
unsigned unit = get_memory_c(dcb + 1, 0);
|
|
|
|
unsigned buffer = get_memory16_c(dcb + 2, 0);
|
|
|
|
if (unit == saved_unit) {
|
|
|
|
if (!buffer) return badBufferAddress;
|
|
|
|
/* slot 2, drive 1 ... why not*/
|
|
|
|
set_memory_c(buffer++, saved_unit | 0x04, 0);
|
|
|
|
set_memory_c(buffer++, 'H', 0);
|
|
|
|
set_memory_c(buffer++, 'O', 0);
|
|
|
|
set_memory_c(buffer++, 'S', 0);
|
|
|
|
set_memory_c(buffer++, 'T', 0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (unit == 0) return -2;
|
|
|
|
return -1;
|
2018-08-20 19:43:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int mli_online_tail(unsigned dcb) {
|
2018-08-21 00:57:45 +00:00
|
|
|
unsigned buffer = get_memory16_c(dcb + 2, 0);
|
|
|
|
|
|
|
|
host_hexdump(buffer, 256);
|
|
|
|
|
|
|
|
/* if there was an error with the device
|
|
|
|
there will be an error code instead of a name (length = 0)
|
|
|
|
*/
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < 16; ++i, buffer += 16) {
|
|
|
|
unsigned x = get_memory_c(buffer, 0);
|
|
|
|
if (x == 0 || ((x & 0xf0) == saved_unit)) {
|
|
|
|
set_memory_c(buffer++, saved_unit | 0x04, 0);
|
|
|
|
set_memory_c(buffer++, 'H', 0);
|
|
|
|
set_memory_c(buffer++, 'O', 0);
|
|
|
|
set_memory_c(buffer++, 'S', 0);
|
|
|
|
set_memory_c(buffer++, 'T', 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2018-08-20 19:43:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int mli_rw_block(unsigned dcb) {
|
2018-08-21 00:57:45 +00:00
|
|
|
unsigned unit = get_memory_c(dcb + 1, 0);
|
|
|
|
unsigned buffer = get_memory16_c(dcb + 2, 0);
|
|
|
|
|
|
|
|
if (unit == saved_unit) {
|
|
|
|
if (!buffer) return badBufferAddress;
|
|
|
|
return notBlockDev; /* network error? */
|
|
|
|
}
|
|
|
|
return -1;
|
2018-08-20 19:43:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static unsigned call_pcount(unsigned call) {
|
2018-08-21 00:57:45 +00:00
|
|
|
switch (call) {
|
|
|
|
case CREATE: return 7;
|
|
|
|
case DESTROY: return 1;
|
|
|
|
case RENAME: return 2;
|
|
|
|
case SET_FILE_INFO: return 7;
|
|
|
|
case GET_FILE_INFO: return 10;
|
|
|
|
case ONLINE: return 2;
|
|
|
|
case SET_PREFIX: return 1;
|
|
|
|
case GET_PREFIX: return 1;
|
|
|
|
case OPEN: return 3;
|
|
|
|
case NEWLINE: return 3;
|
|
|
|
case READ: return 4;
|
|
|
|
case WRITE: return 4;
|
|
|
|
case CLOSE: return 1;
|
|
|
|
case FLUSH: return 1;
|
|
|
|
case SET_MARK: return 2;
|
|
|
|
case GET_MARK: return 2;
|
|
|
|
case SET_EOF: return 2;
|
|
|
|
case GET_EOF: return 2;
|
|
|
|
case SET_BUF: return 2;
|
|
|
|
case GET_BUF: return 2;
|
|
|
|
|
|
|
|
default: return 0;
|
|
|
|
}
|
2018-08-20 19:43:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static const char *call_name(unsigned call) {
|
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
switch (call) {
|
|
|
|
case CREATE: return "CREATE";
|
|
|
|
case DESTROY: return "DESTROY";
|
|
|
|
case RENAME: return "RENAME";
|
|
|
|
case SET_FILE_INFO: return "SET_FILE_INFO";
|
|
|
|
case GET_FILE_INFO: return "GET_FILE_INFO";
|
|
|
|
case ONLINE: return "ONLINE";
|
|
|
|
case SET_PREFIX: return "SET_PREFIX";
|
|
|
|
case GET_PREFIX: return "GET_PREFIX";
|
|
|
|
case OPEN: return "OPEN";
|
|
|
|
case NEWLINE: return "NEWLINE";
|
|
|
|
case READ: return "READ";
|
|
|
|
case WRITE: return "WRITE";
|
|
|
|
case CLOSE: return "CLOSE";
|
|
|
|
case FLUSH: return "FLUSH";
|
|
|
|
case SET_MARK: return "SET_MARK";
|
|
|
|
case GET_MARK: return "GET_MARK";
|
|
|
|
case SET_EOF: return "SET_EOF";
|
|
|
|
case GET_EOF: return "GET_EOF";
|
|
|
|
case SET_BUF: return "SET_BUF";
|
|
|
|
case GET_BUF: return "GET_BUF";
|
|
|
|
case ATINIT: return "ATINIT";
|
|
|
|
|
|
|
|
default: return "";
|
|
|
|
}
|
2018-08-20 19:43:06 +00:00
|
|
|
}
|
|
|
|
|
2018-08-23 14:56:09 +00:00
|
|
|
|
|
|
|
#ifdef __CYGWIN__
|
|
|
|
|
|
|
|
#include <sys/cygwin.h>
|
|
|
|
|
|
|
|
static char *expand_path(const char *path, word32 *error) {
|
|
|
|
|
|
|
|
char buffer[PATH_MAX];
|
|
|
|
if (!path) return path;
|
|
|
|
|
|
|
|
ssize_t ok = cygwin_conv_path(CCP_POSIX_TO_WIN_A, path, buffer, sizeof(buffer));
|
|
|
|
if (ok < 0) {
|
2018-08-24 00:47:09 +00:00
|
|
|
if (error) *error = fstError;
|
2018-08-23 14:56:09 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return host_gc_strdup(buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
#define expand_path(x, y) (x)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2018-08-20 19:43:06 +00:00
|
|
|
/*
|
|
|
|
* mli head patch. called before ProDOS mli.
|
|
|
|
* this call will either
|
|
|
|
* 1. handle and return
|
|
|
|
* 2. redirect to the real mli
|
|
|
|
* 3. call real mli, then call host_mli_tail after
|
|
|
|
*/
|
|
|
|
void host_mli_head() {
|
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
word32 rts = get_memory16_c(engine.stack+1, 0);
|
|
|
|
saved_call = get_memory_c(rts + 1, 0);
|
|
|
|
saved_dcb = get_memory16_c(rts + 2, 0);
|
|
|
|
saved_p = engine.psr;
|
2018-08-24 00:47:09 +00:00
|
|
|
word16 terr = 0;
|
2018-08-21 00:57:45 +00:00
|
|
|
|
|
|
|
/* do pcount / path stuff here */
|
|
|
|
char *path1 = NULL;
|
|
|
|
char *path2 = NULL;
|
|
|
|
char *path3 = NULL;
|
|
|
|
char *path4 = NULL;
|
|
|
|
|
|
|
|
struct file_entry *file = NULL;
|
|
|
|
|
|
|
|
unsigned pcount = get_memory_c(saved_dcb, 0);
|
|
|
|
unsigned xpcount = call_pcount(saved_call);
|
|
|
|
int acc = 0;
|
|
|
|
int refnum = 0;
|
|
|
|
|
|
|
|
if (xpcount && xpcount != pcount) {
|
|
|
|
acc = invalidPcount;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch(saved_call) {
|
|
|
|
case CREATE:
|
|
|
|
case DESTROY:
|
|
|
|
case SET_FILE_INFO:
|
|
|
|
case GET_FILE_INFO:
|
|
|
|
case OPEN:
|
|
|
|
path1 = is_host_path(get_memory16_c(saved_dcb + 1, 0));
|
|
|
|
if (!path1) goto prodos_mli;
|
|
|
|
path3 = host_gc_append_path(host_root, path1);
|
2018-08-24 00:47:09 +00:00
|
|
|
path3 = expand_path(path3, &terr);
|
2018-08-21 00:57:45 +00:00
|
|
|
break;
|
2018-08-24 00:47:09 +00:00
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
case RENAME:
|
|
|
|
path1 = is_host_path(get_memory16_c(saved_dcb + 1,0));
|
|
|
|
path2 = is_host_path(get_memory16_c(saved_dcb + 3,0));
|
|
|
|
if (!path1 && !path2) goto prodos_mli;
|
|
|
|
if (path1) path3 = host_gc_append_path(host_root, path1);
|
|
|
|
if (path2) path4 = host_gc_append_path(host_root, path2);
|
2018-08-24 00:47:09 +00:00
|
|
|
path3 = expand_path(path3, &terr);
|
|
|
|
path4 = expand_path(path4, &terr);
|
2018-08-21 00:57:45 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SET_PREFIX:
|
|
|
|
path1 = is_host_path(get_memory16_c(saved_dcb + 1,0));
|
|
|
|
if (!path1 && !saved_prefix) goto prodos_mli;
|
|
|
|
if (path1) path3 = host_gc_append_path(host_root, path1);
|
2018-08-24 00:47:09 +00:00
|
|
|
path3 = expand_path(path3, &terr);
|
2018-08-21 00:57:45 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case GET_PREFIX:
|
|
|
|
if (!saved_prefix) goto prodos_mli;
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* refnum based */
|
|
|
|
case NEWLINE:
|
|
|
|
case READ:
|
|
|
|
case WRITE:
|
|
|
|
case SET_MARK:
|
|
|
|
case GET_MARK:
|
|
|
|
case SET_EOF:
|
|
|
|
case GET_EOF:
|
|
|
|
case SET_BUF:
|
|
|
|
case GET_BUF:
|
|
|
|
refnum = get_memory_c(saved_dcb + 1, 0);
|
|
|
|
|
|
|
|
if (refnum >= MIN_FILE && refnum < MAX_FILE) {
|
|
|
|
file = &files[refnum - MIN_FILE];
|
|
|
|
} else {
|
|
|
|
goto prodos_mli;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CLOSE:
|
|
|
|
case FLUSH:
|
|
|
|
/* special case for refnum == 0 */
|
|
|
|
refnum = get_memory_c(saved_dcb + 1, 0);
|
|
|
|
|
|
|
|
if (refnum >= MIN_FILE && refnum < MAX_FILE) {
|
|
|
|
file = &files[refnum - MIN_FILE];
|
|
|
|
} else if (refnum) {
|
|
|
|
goto prodos_mli;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(stderr, "MLI: %02x %s", saved_call, call_name(saved_call));
|
|
|
|
if (path1) fprintf(stderr, " - %s", path1);
|
|
|
|
if (path2) fprintf(stderr, " - %s", path2);
|
|
|
|
|
|
|
|
switch (saved_call) {
|
|
|
|
default:
|
|
|
|
engine.kpc = saved_mli_address;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case ATINIT:
|
|
|
|
acc = mli_atinit(saved_dcb);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CREATE:
|
|
|
|
acc = mli_create(saved_dcb, path3);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DESTROY:
|
|
|
|
acc = mli_destroy(saved_dcb, path3);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SET_FILE_INFO:
|
|
|
|
acc = mli_set_file_info(saved_dcb, path3);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GET_FILE_INFO:
|
|
|
|
acc = mli_get_file_info(saved_dcb, path3);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OPEN:
|
|
|
|
acc = mli_open(saved_dcb, path1, path3);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RENAME:
|
|
|
|
acc = mli_rename(saved_dcb, path3, path4);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SET_PREFIX:
|
|
|
|
acc = mli_set_prefix(saved_dcb, path1, path3);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GET_PREFIX:
|
|
|
|
acc = mli_get_prefix(saved_dcb);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ONLINE:
|
|
|
|
acc = mli_online(saved_dcb);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NEWLINE:
|
|
|
|
acc = mli_newline(saved_dcb, file);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case READ:
|
|
|
|
acc = mli_read(saved_dcb, file);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WRITE:
|
|
|
|
acc = mli_write(saved_dcb, file);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SET_MARK:
|
|
|
|
acc = mli_set_mark(saved_dcb, file);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GET_MARK:
|
|
|
|
acc = mli_get_mark(saved_dcb, file);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SET_EOF:
|
|
|
|
acc = mli_set_eof(saved_dcb, file);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GET_EOF:
|
|
|
|
acc = mli_get_eof(saved_dcb, file);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SET_BUF:
|
|
|
|
acc = mli_set_buf(saved_dcb, file);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GET_BUF:
|
|
|
|
acc = mli_get_buf(saved_dcb, file);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CLOSE:
|
|
|
|
acc = mli_close(saved_dcb, file);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case FLUSH:
|
|
|
|
acc = mli_flush(saved_dcb, file);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WRITE_BLOCK:
|
|
|
|
case READ_BLOCK:
|
|
|
|
acc = mli_rw_block(saved_dcb);
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case QUIT:
|
|
|
|
acc = mli_quit(saved_dcb);
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
fputs("\n", stderr);
|
2018-08-23 19:27:23 +00:00
|
|
|
fflush(stderr);
|
2018-08-21 00:57:45 +00:00
|
|
|
host_gc_free();
|
|
|
|
|
|
|
|
|
|
|
|
if (acc == -2) {
|
|
|
|
/* tail call needed */
|
|
|
|
/*
|
|
|
|
jsr xxxx
|
|
|
|
dc.b xx
|
|
|
|
dc.w xxxx
|
|
|
|
wdm ..
|
|
|
|
rts
|
|
|
|
*/
|
|
|
|
SEI();
|
|
|
|
set_memory_c(saved_patch_address + 0, saved_call, 0);
|
|
|
|
set_memory16_c(saved_patch_address + 1, saved_dcb, 0);
|
|
|
|
set_memory16_c(engine.stack+1, rts + 3, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (acc < 0) {
|
2018-08-20 19:43:06 +00:00
|
|
|
prodos_mli:
|
2018-08-21 00:57:45 +00:00
|
|
|
host_gc_free();
|
|
|
|
/* pass to normal dispatcher */
|
|
|
|
engine.kpc = saved_mli_address;
|
|
|
|
return;
|
|
|
|
}
|
2018-08-20 19:43:06 +00:00
|
|
|
cleanup:
|
2018-08-21 00:57:45 +00:00
|
|
|
/* fixup */
|
|
|
|
acc &= 0xff;
|
|
|
|
|
|
|
|
if (acc) fprintf(stderr, " %02x %s\n", acc, host_error_name(acc));
|
|
|
|
if (acc == 0 && saved_call != ATINIT) {
|
|
|
|
set_memory_c(DEVNUM, saved_unit, 0);
|
|
|
|
}
|
|
|
|
engine.acc &= 0xff00;
|
|
|
|
engine.acc |= acc;
|
|
|
|
if (acc) {
|
|
|
|
SEC();
|
|
|
|
CLZ();
|
|
|
|
} else {
|
|
|
|
CLC();
|
|
|
|
SEZ();
|
|
|
|
}
|
|
|
|
engine.kpc = rts + 4;
|
|
|
|
engine.stack += 2;
|
|
|
|
return;
|
2018-08-20 19:43:06 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void host_mli_tail() {
|
|
|
|
|
2018-08-21 00:57:45 +00:00
|
|
|
if (!(engine.psr & C)) {
|
|
|
|
|
|
|
|
switch(saved_call) {
|
|
|
|
case SET_PREFIX:
|
|
|
|
free(saved_prefix);
|
|
|
|
saved_prefix = NULL;
|
|
|
|
break;
|
|
|
|
case ONLINE:
|
|
|
|
mli_online_tail(saved_dcb);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// clean up I bit (set in head)
|
|
|
|
engine.psr &= ~I;
|
|
|
|
engine.psr |= (saved_p & I);
|
|
|
|
host_gc_free();
|
2018-08-20 19:43:06 +00:00
|
|
|
}
|