Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
dir HDOS virtualization #140
Browse files Browse the repository at this point in the history
lgblgblgb committed Dec 25, 2021

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent cf92da5 commit eead98d
Showing 6 changed files with 265 additions and 34 deletions.
278 changes: 255 additions & 23 deletions targets/mega65/hdos.c
Original file line number Diff line number Diff line change
@@ -22,6 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include "hdos.h"
#include "hypervisor.h"
#include "memory_mapper.h"
#include "io_mapper.h"

#include <fcntl.h>
#include <sys/stat.h>
@@ -40,7 +41,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include "hypervisor.h"
#include "vic4.h"
#include "dma65.h"
#include "io_mapper.h"
#include "xemu/emutools_config.h"
#include "configdb.h"
#include "rom.h"
@@ -53,27 +53,40 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
static struct {
Uint8 func;
const char *func_name;
int func_is_virtualized;
Uint8 in_x, in_y, in_z;
// The following ones are ONLY used in virtualized functions originated from hdos_enter()
Uint8 virt_out_a, virt_out_x, virt_out_y, virt_out_z, virt_out_carry;
char setnam_fn[64];
const char *rootdir;
int do_virt;
int fd_tab[0x100];
XDIR *dp_tab[0x100];
} hdos;

static int hdos_init_is_done = 0;

#define HDOS_DESCRIPTORS 4

static struct {
union {
int fd;
XDIR *dirp;
};
enum { HDOS_DESC_CLOSED, HDOS_DESC_FILE, HDOS_DESC_DIR } status;
} desc_table[0x100];
char *basedirpath;
int dir_entry_no;
} desc_table[HDOS_DESCRIPTORS];



//#define DEBUGHDOS(...) DEBUG(__VA_ARGS__)
#define DEBUGHDOS(...) DEBUGPRINT(__VA_ARGS__)

#define HDOSERR_INVALID_ADDRESS 0x10
#define HDOSERR_FILE_NOT_FOUND 0x88
#define HDOSERR_INVALID_DESC 0x89
// ??
#define HDOSERR_END_DIR 0x85
#define HDOSERR_CANNOT_OPEN_DIR HDOSERR_FILE_NOT_FOUND


static const char *hdos_get_func_name ( const int func_no )
@@ -200,20 +213,225 @@ static int copy_mem_to_user ( unsigned int target_cpu_addr, Uint8 *source, int s
}


static int find_empty_desc_tab_slot ( void )
{
for (int a = 0; a < HDOS_DESCRIPTORS; a++)
if (desc_table[a].status == HDOS_DESC_CLOSED)
return a;
return -1;
}


static int close_desc ( unsigned int entry )
{
if (entry >= HDOS_DESCRIPTORS)
return -1;
if (desc_table[entry].basedirpath) {
free(desc_table[entry].basedirpath);
desc_table[entry].basedirpath = NULL;
}
if (desc_table[entry].status == HDOS_DESC_FILE) {
const int ret = close(desc_table[entry].fd);
desc_table[entry].status = HDOS_DESC_CLOSED;
DEBUGHDOS("HDOS: closing file descriptor #$%02X: %d" NL, entry, ret);
if (ret)
return -1; // error value??
return 0;
} else if (desc_table[entry].status == HDOS_DESC_DIR) {
const int ret = xemu_os_closedir(desc_table[entry].dirp);
desc_table[entry].status = HDOS_DESC_CLOSED;
DEBUGHDOS("HDOS: closing directory descriptor #$%02X: %d" NL, entry, ret);
if (ret)
return -1; // error value??
return 0;
}
return -1; // already closed, error value??
}


static inline void hdos_virt_opendir ( void )
{
hdos.func_is_virtualized = 1;
int e = find_empty_desc_tab_slot();
if (e == -1) {
hdos.virt_out_a = HDOSERR_INVALID_DESC; // some error code; No free file descriptor
return;
}
// FIXME, later, it shouldn't be the only choice!
desc_table[e].basedirpath = xemu_strdup(hdos.rootdir);
XDIR *dirp = xemu_os_opendir(desc_table[e].basedirpath);
if (!dirp) {
hdos.virt_out_a = HDOSERR_CANNOT_OPEN_DIR; // some error code; directory cannot be open
return;
}
desc_table[e].status = HDOS_DESC_DIR;
desc_table[e].dirp = dirp;
desc_table[e].dir_entry_no = 0;
hdos.virt_out_a = e; // return the file descriptor
hdos.virt_out_carry = 1; // signal OK status
}


static inline void hdos_virt_close_dir_or_file ( void )
{
hdos.func_is_virtualized = 1;
if (close_desc(hdos.in_x))
hdos.virt_out_a = HDOSERR_INVALID_DESC;
else
hdos.virt_out_carry = 1; // signal OK status
}



static inline void hdos_virt_readdir ( void )
{
hdos.func_is_virtualized = 1;
if (hdos.in_x >= HDOS_DESCRIPTORS || desc_table[hdos.in_x].status != HDOS_DESC_DIR) {
hdos.virt_out_a = HDOSERR_INVALID_DESC;
return;
}
if (hdos.in_y >= 0x80) {
hdos.virt_out_a = HDOSERR_INVALID_ADDRESS;
return;
}
Uint8 mem[87];
struct dirent entry_storage;
struct dirent *ent;
const int in_emu_root = !strcmp(desc_table[hdos.in_x].basedirpath, hdos.rootdir);
#if 0
if (in_emu_root && desc_table[hdos.in_x].dir_entry_no == 0) {
// fake a volume label as the response, in case of emulation as root dir
memset(mem, 0, sizeof mem);
static const char volume_name[] = "XEMU-VRT";
memcpy(mem, volume_name, strlen(volume_name));
memcpy(mem + 65, volume_name, strlen(volume_name));
mem[64] = strlen(volume_name);
mem[86] = 8; // type: volume label
desc_table[hdos.in_x].dir_entry_no = 1;
if (copy_mem_to_user(hdos.in_y << 8, mem, sizeof mem) != sizeof mem)
hdos.virt_out_a = HDOSERR_INVALID_ADDRESS;
else
hdos.virt_out_carry = 1; // signal OK status
return;
}
#endif
readdir_again:
ent = xemu_os_readdir(desc_table[hdos.in_x].dirp, &entry_storage);
if (!ent) {
// FIXME: there should be error checking here, but we assume for now, that NULL means end of directory
DEBUGHDOS("HDOS: VIRT: %s(): end-of-directory" NL, __func__);
hdos.virt_out_a = HDOSERR_END_DIR;
return;
}
if (in_emu_root && (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))) // if emulated as root dir, do not pass back "." and ".."
goto readdir_again;
memset(mem, 0, sizeof mem);
// Copy, check (length and chars) and convert filename
for (Uint8 *s = (unsigned char *)ent->d_name, *t = mem; *s; s++, t++, mem[64]++) {
if (mem[64] >= 63)
goto readdir_again;
if (*s < 0x20 || *s >= 0x80)
goto readdir_again;
*t = (*s >= 'a' && *s <= 'z') ? *s - 32 : *s;
}
// Stat our file about details
//char fn[PATH_MAX + 1];
char fn[strlen(desc_table[hdos.in_x].basedirpath) + strlen(ent->d_name) + 1];
strcpy(fn, desc_table[hdos.in_x].basedirpath);
strcat(fn, ent->d_name);
struct stat st;
if (xemu_os_stat(fn, &st))
goto readdir_again;
const int type = st.st_mode & S_IFMT;
if (type != S_IFDIR && type != S_IFREG)
goto readdir_again;
// -- OK, directory entry - finally - seems to be OK to pass back as the result --
DEBUGHDOS("HDOS: VIRT: %s(): accepted filename = \"%s\"" NL, __func__, ent->d_name);
// Short name should be created for the answer ...
strcpy((char*)mem + 65, "12345678.EXT"); // ?? Not used by BASIC65, it seems? (short name) FIXME: we should present some short name!!
// Set some value for starting cluster. We cannot emulate this too much, but for
// some try, let's present not the same value all the time, at least ...
const unsigned int fake_start_cluster = desc_table[hdos.in_x].dir_entry_no + 0x10; // don't use too low cluster numbers
mem[78] = fake_start_cluster & 0xFF;
mem[79] = (fake_start_cluster >> 8) & 0xFF;
mem[80] = (fake_start_cluster >> 16) & 0xFF;
mem[80] = (fake_start_cluster >> 24) & 0xFF;
// File size
mem[82] = st.st_size & 0xFF;
mem[83] = (st.st_size >> 8) & 0xFF;
mem[84] = (st.st_size >> 16) & 0xFF;
mem[85] = (st.st_size >> 24) & 0xFF;
// file type
if (type == S_IFDIR)
mem[86] = 0x10; // type should be set to "subdiretory"
// Copy the answer to the memory of the caller requested
if (copy_mem_to_user(hdos.in_y << 8, mem, sizeof mem) != sizeof mem) {
hdos.virt_out_a = HDOSERR_INVALID_ADDRESS;
return;
}
desc_table[hdos.in_x].dir_entry_no++;
hdos.virt_out_carry = 1; // signal OK status
}


// Called when DOS trap is triggered.
// Can be used to take control (without hyppo doing it) but setting the needed register values,
// and calling hypervisor_leave() to avoid Hyppo to run.
void hdos_enter ( const Uint8 func_no )
{
if (XEMU_UNLIKELY(!hdos_init_is_done))
FATAL("%s() is called before HDOS subsystem init!", __func__);
hdos.func = func_no;
hdos.func_name = hdos_get_func_name(hdos.func);
hdos.func_is_virtualized = 0;
// NOTE: hdos.in_ things are the *INPUT* registers of the trap, cannot be used
// to override the result passing back by the trap!
// Here we store input register values can be even examined at the hdos_leave() stage
hdos.in_x = cpu65.x;
hdos.in_y = cpu65.y;
hdos.in_z = cpu65.z;
DEBUGHDOS("HDOS: entering function #$%02X (%s)" NL, hdos.func, hdos.func_name);
if (hdos.do_virt) {
// Can be overriden by virtualized functions.
// these virt_out stuffs won't be used otherwise, if a virtualized function does not set hdos.func_is_virtualized
hdos.virt_out_a = 0xFF; // set to $FF by default, override in virtualized function implementations if needed
hdos.virt_out_x = cpu65.x;
hdos.virt_out_y = cpu65.y;
hdos.virt_out_z = cpu65.z;
hdos.virt_out_carry = 0; // assumming **ERROR** (carry flag is clear) by default! Override that to '1' in virt functions if it's OK!
// Let's see the virualized functions.
// Those should set hdos.func_is_virtualized, also the hdos.virt_out_carry is operation was OK, and probably they want to
// set some register values as output in hdos.virt_out_a, hdos.virt_out_x, hdos.virt_out_y and hdos.virt_out_z
// The reason of this complicated method: the switch-case below contains the call of virtualized DOS function implementations and
// they can decide to NOT set hdos.func_is_virtualized conditionally, so it's even possible to have virtualized DOS file functions
// just for a certain directory / file names, and so on! (though it's currently not so much planned to use ...)
switch (hdos.func) {
case 0x12:
hdos_virt_opendir();
break;
case 0x14:
hdos_virt_readdir();
break;
case 0x16:
case 0x20:
hdos_virt_close_dir_or_file();
break;
}
if (hdos.func_is_virtualized) {
D6XX_registers[0x40] = hdos.virt_out_a;
D6XX_registers[0x41] = hdos.virt_out_x;
D6XX_registers[0x42] = hdos.virt_out_y;
D6XX_registers[0x43] = hdos.virt_out_z;
if (hdos.virt_out_carry)
D6XX_registers[0x47] |= CPU65_PF_C;
else
D6XX_registers[0x47] &= ~CPU65_PF_C;
DEBUGHDOS("HDOS: VIRT: returning %s (A=$%02X) from virtualized function #$%02X bypassing Hyppo" NL, hdos.virt_out_carry ? "OK" : "ERROR", hdos.virt_out_a, hdos.func);
// forced leave of hypervisor mode now, bypassing hyppo to process this DOS trap function
hypervisor_leave();
} else
DEBUGHDOS("HDOS: VIRT: unvirtualized DOS function #$%02X pass-through to Hyppo" NL, hdos.func);
}
}


@@ -224,9 +442,17 @@ void hdos_leave ( const Uint8 func_no )
hdos.func = func_no;
hdos.func_name = hdos_get_func_name(hdos.func);
DEBUGHDOS("HDOS: leaving function #$%02X (%s) with carry %s" NL, hdos.func, hdos.func_name, cpu65.pf_c ? "SET" : "CLEAR");
// if "func_is_virtualized" flag is set, we don't want to mess things up further, as it was handled before in hdos.c somewhere
if (hdos.func_is_virtualized) {
DEBUGHDOS("HDOS: VIRT: already marked as virtualized" NL);
hdos.func_is_virtualized = 0; // just to be sure, though it's set to zero on next hdos_enter()
return;
}
if (hdos.func == 0x2E && cpu65.pf_c) { // HDOS setnam function. Also check carry set (which means "ok" by HDOS trap)
// we always track this call, because we should know the actual name selected with some other calls then
// let's do a local copy of the successfully selected name via hyppo (we know this by knowing that C flag is set by hyppo)
// We always track this call, because we should know the actual name selected with some other calls then,
// since the result can be used _later_ by both of virtualized and non-virtualized functions, possibly.
// Let's do a local copy of the successfully selected name via hyppo (we know this by knowing that C flag is set by hyppo)
char setnam_current[sizeof hdos.setnam_fn];
if (copy_string_from_user(hdos.setnam_fn, sizeof hdos.setnam_fn, hdos.in_x + (hdos.in_y << 8)) >= 0)
for (char *p = hdos.setnam_fn; *p; p++) {
if (*p < 0x20 || *p >= 0x7F) {
@@ -247,33 +473,34 @@ void hdos_leave ( const Uint8 func_no )
}




// Must be called on TRAP RESET, also causes to close all file descriptors (BTW, may be called on exit xemu,
// to nicely close open files/directories ...)
void hdos_reset ( void )
{
DEBUGHDOS("HDOS: reset" NL);
hdos.setnam_fn[0] = '\0';
hdos.func = -1;
for (int a = 0; a < 0x100; a++)
if (desc_table[a].status == HDOS_DESC_FILE) {
const int ret = close(desc_table[a].fd);
DEBUGHDOS("HDOS: reset: closing file descriptor #$%02X: %d" NL, a, ret);
desc_table[a].status = HDOS_DESC_CLOSED;
} else if (desc_table[a].status == HDOS_DESC_DIR) {
const int ret = xemu_os_closedir(desc_table[a].dirp);
DEBUGHDOS("HDOS: reset: closing directory descriptor #$%02X: %d" NL, a , ret);
desc_table[a].status = HDOS_DESC_CLOSED;
}
hypervisor_hdos_close_descriptors();
}

// implementation is here, but prototype is in hypervisor.h and not in hdos.h as you would expect!
void hypervisor_hdos_close_descriptors ( void )
{
if (hdos_init_is_done)
for (int a = 0; a < HDOS_DESCRIPTORS; a++)
(void)close_desc(a);
}


// implementation is here, but prototype is in hypervisor.h and not in hdos.h as you would expect!
int hypervisor_hdos_virtualization_status ( const int to_enable, const char **root_ptr )
int hypervisor_hdos_virtualization_status ( const int set, const char **root_ptr )
{
if (to_enable >= 0) {
if (!!hdos.do_virt != !!to_enable) {
hdos.do_virt = to_enable;
DEBUGPRINT("HDOS: switcihing virtualization %s" NL, to_enable ? "ON" : "OFF");
if (set >= 0) {
if (!!hdos.do_virt != !!set) {
hdos.do_virt = set;
DEBUGPRINT("HDOS: run-time switching virtualization %s" NL, set ? "ON" : "OFF");
}
}
if (root_ptr)
@@ -285,9 +512,14 @@ int hypervisor_hdos_virtualization_status ( const int to_enable, const char **ro
// Must be called, but **ONLY** once in Xemu's running lifetime, before using XEMU HDOS related things here!
void hdos_init ( const int do_virt, const char *virtroot )
{
if (hdos_init_is_done)
FATAL("%s() called more than once!", __func__);
hdos_init_is_done = 1;
DEBUGHDOS("HDOS: initialization with do_virt=%d and virtroot=\"%s\"" NL, do_virt, virtroot ? virtroot : "<NULL>");
for (int a = 0; a < 0x100; a++)
for (int a = 0; a < HDOS_DESCRIPTORS; a++) {
desc_table[a].status = HDOS_DESC_CLOSED;
desc_table[a].basedirpath = NULL;
}
hdos_reset();
// HDOS virtualization related stuff
// First build the default path for HDOS root, also try to create, maybe hasn't existed yet.
@@ -304,7 +536,7 @@ void hdos_init ( const int do_virt, const char *virtroot )
strcpy(hdosdir, virtroot);
if (hdosdir[strlen(hdosdir) - 1] != DIRSEP_CHR)
strcat(hdosdir, DIRSEP_STR);
}
} else
ERROR_WINDOW("HDOS: bad HDOS virtual directory given (cannot open as directory): %s\nUsing the default instead: %s", virtroot, hdosdir);
}
hdos.rootdir = xemu_strdup(hdosdir); // populate the result of HDOS virtualization root directory
12 changes: 3 additions & 9 deletions targets/mega65/hypervisor.c
Original file line number Diff line number Diff line change
@@ -247,20 +247,14 @@ static void extract_version_string ( char *target, int target_max_size )
}


static inline void hypervisor_xemu_init ( void )
{
hyppo_version_string[0] = '\0';
hdos_init(configdb.hdosvirt, configdb.hdosdir);
}


// Actual (CPU level opcode execution) emulation of MEGA65 should start with calling this function (surely after initialization of every subsystems etc).
void hypervisor_start_machine ( void )
{
static int init_done = 0;
if (XEMU_UNLIKELY(!init_done)) {
init_done = 1;
hypervisor_xemu_init();
hyppo_version_string[0] = '\0';
hdos_init(configdb.hdosvirt, configdb.hdosdir);
}
in_hypervisor = 0;
hypervisor_queued_trap = -1;
@@ -323,8 +317,8 @@ void hypervisor_leave ( void )
// Sanity check
if (XEMU_UNLIKELY(!in_hypervisor))
FATAL("FATAL: not in hypervisor mode while calling hypervisor_leave()");
// First, restore machine status from hypervisor registers
DEBUG("HYPERVISOR: leaving hypervisor mode @ $%04X -> $%04X" NL, cpu65.pc, D6XX_registers[0x48] | (D6XX_registers[0x49] << 8));
// First, restore machine status from hypervisor registers
cpu65.a = D6XX_registers[0x40];
cpu65.x = D6XX_registers[0x41];
cpu65.y = D6XX_registers[0x42];
3 changes: 2 additions & 1 deletion targets/mega65/hypervisor.h
Original file line number Diff line number Diff line change
@@ -42,6 +42,7 @@ extern void hypervisor_leave ( void );
extern void hypervisor_serial_monitor_push_char ( Uint8 chr );
extern void hypervisor_debug_invalidate ( const char *reason );
extern void hypervisor_debug_late_enable ( void );
extern int hypervisor_hdos_virtualization_status ( const int to_enable, const char **root_ptr ); // prototype is here, but implemented in hdos.c!
extern int hypervisor_hdos_virtualization_status ( const int set, const char **root_ptr ); // prototype is here, but it's implemented in hdos.c not in hypervisor.c
extern void hypervisor_hdos_close_descriptors ( void ); // prototype is here, but it's implemented in hdos.c not in hypervisor.c

#endif
1 change: 1 addition & 0 deletions targets/mega65/mega65.c
Original file line number Diff line number Diff line change
@@ -465,6 +465,7 @@ static void shutdown_callback ( void )
#ifdef XEMU_HAS_SOCKET_API
xemusock_uninit();
#endif
hypervisor_hdos_close_descriptors();
if (emulation_is_running)
DEBUGPRINT("CPU: Execution ended at PC=$%04X (linear=%X)" NL, cpu65.pc, memory_cpurd2linear_xlat(cpu65.pc));
}
2 changes: 1 addition & 1 deletion xemu/emutools.c
Original file line number Diff line number Diff line change
@@ -692,7 +692,7 @@ int xemu_init_sdl ( void )
#if defined(XEMU_ARCH_WIN) && defined(SDL_INIT_SENSOR)
// FIXME: SDL or Windows has the bug that SDL_INIT_SENSOR when used, there is some "sensor manager" problem, so we left it out
// SDL_INIT_SENSOR was introduced somewhere in 2.0.9, however since it's a macro, it's safer not to test actual SDL version number
SDL_INIT_EVERYTHING & (~SDL_INIT_SENSOR);
SDL_INIT_EVERYTHING & ~(SDL_INIT_SENSOR | SDL_INIT_HAPTIC);
#warning "Activating windows + SDL sensor init problem workaround ..."
#else
SDL_INIT_EVERYTHING;
3 changes: 3 additions & 0 deletions xemu/emutools.h
Original file line number Diff line number Diff line change
@@ -246,6 +246,7 @@ extern void sha1_checksum_as_string ( sha1_hash_str hash_str, const Uint8 *data,

#include <dirent.h>
#ifdef XEMU_ARCH_WIN
# include <sys/stat.h>
typedef _WDIR XDIR;
extern int xemu_winos_utf8_to_wchar ( wchar_t *restrict o, const char *restrict i, size_t size );
extern int xemu_os_open ( const char *fn, int flags );
@@ -256,6 +257,7 @@ extern void sha1_checksum_as_string ( sha1_hash_str hash_str, const Uint8 *data,
extern XDIR *xemu_os_opendir ( const char *fn );
extern struct dirent *xemu_os_readdir ( XDIR *dirp, struct dirent *entry );
extern int xemu_os_closedir ( XDIR *dir );
extern int xemu_os_stat ( const char *fn, struct stat *statbuf );
#else
typedef DIR XDIR;
# define xemu_os_open open
@@ -266,6 +268,7 @@ extern void sha1_checksum_as_string ( sha1_hash_str hash_str, const Uint8 *data,
# define xemu_os_opendir opendir
# define xemu_os_readdir(dirp,not_used) readdir(dirp)
# define xemu_os_closedir closedir
# define xemu_os_stat stat
#endif
#define xemu_os_close close

1 comment on commit eead98d

@lgblgblgb
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For #331

Please sign in to comment.