21 #define XEMU_MEGA65_HDOS_H_ALLOWED
30 #include <sys/types.h>
38 #define DEBUGHDOS(...) DEBUGPRINT(__VA_ARGS__)
56 static int hdos_init_is_done = 0;
58 #define HDOS_DESCRIPTORS 4
65 enum { HDOS_DESC_CLOSED, HDOS_DESC_FILE, HDOS_DESC_DIR }
status;
70 #define HDOSERR_INVALID_ADDRESS 0x10
71 #define HDOSERR_FILE_NOT_FOUND 0x88
72 #define HDOSERR_INVALID_DESC 0x89
73 #define HDOSERR_IS_DIRECTORY 0x86
74 #define HDOSERR_NOT_DIRECTORY 0x87
75 #define HDOSERR_IMAGE_WRONG_LEN 0x8A
76 #define HDOSERR_TOO_MANY_OPEN 0x84
77 #define HDOSERR_NO_SUCH_DISK 0x80
79 #define HDOSERR_END_DIR 0x85 // invalid cluster, it is returned by readdir if trying to read beyond end of directory
80 #define HDOSERR_CANNOT_OPEN_DIR HDOSERR_FILE_NOT_FOUND
84 static int copy_mem_from_user (
Uint8 *target,
int max_size,
const int terminator_byte,
unsigned int source_cpu_addr )
87 if (terminator_byte >= 0)
91 if (source_cpu_addr >= 0x8000)
96 if (len >= max_size) {
97 if (terminator_byte >= 0)
98 *target = terminator_byte;
101 if ((
int)
byte == terminator_byte)
108 static int copy_string_from_user (
char *target,
const int max_size,
unsigned int source_cpu_addr )
110 return copy_mem_from_user((
Uint8*)target, max_size, 0, source_cpu_addr);
114 static int copy_mem_to_user (
unsigned int target_cpu_addr,
const Uint8 *source,
int size )
118 if (target_cpu_addr >= 0x8000)
133 static void reconstruct_commandline (
char *p,
unsigned int max_size )
139 for (
int a = 0; a < argc; a++) {
140 if (strlen(p) + strlen(argv[a]) >= max_size - 2)
161 const char *res =
"";
183 reconstruct_commandline(work,
sizeof work);
201 int len = strlen(res) + 1;
202 if (len >=
sizeof work) {
206 for (
int a = 0; a < len; a++) {
208 work[a] = (*res >=
'a' && *res <=
'z') ? *res - 32 : *res;
230 static const char *hdos_get_func_name (
const int func_no )
232 if (
XEMU_UNLIKELY((func_no & 1) || (
unsigned int)func_no >= 0x80U)) {
233 FATAL(
"%s(%d) invalid DOS trap function number", __func__, func_no);
239 static const char INVALID_SUBFUNCTION[] =
"INVALID_DOS_FUNC";
240 static const char *func_names[] = {
245 "getdisksize [UNIMPLEMENTED]",
246 "getcwd [UNIMPLEMENTED]",
248 "mkdir [UNIMPLEMENTED]",
249 "rmdir [UNIMPLEMENTED]",
255 "writefile [UNIMPLEMENTED]",
259 "seekfile [UNIMPLEMENTED]",
260 "rmfile [UNIMPLEMENTED]",
261 "fstat [UNIMPLEMENTED]",
262 "rename [UNIMPLEMENTED]",
263 "filedate [UNIMPLEMENTED]",
270 "setup_transfer_area",
281 "gettasklist [UNIMPLEMENTED]",
282 "sendmessage [UNIMPLEMENTED]",
283 "receivemessage [UNIMPLEMENTED]",
284 "writeintotask [UNIMPLEMENTED]",
285 "readoutoftask [UNIMPLEMENTED]",
289 "terminateothertask [UNIMPLEMENTED]",
290 "create_task_native [UNIMPLEMENTED]",
291 "load_into_task [UNIMPLEMENTED]",
292 "create_task_c64 [UNIMPLEMENTED]",
293 "create_task_c65 [UNIMPLEMENTED]",
294 "exit_and_switch_to_task [UNIMPLEMENTED]",
295 "switch_to_task [UNIMPLEMENTED]",
296 "exit_task [UNIMPLEMENTED]",
297 "trap_task_toggle_rom_writeprotect",
298 "trap_task_toggle_force_4502",
299 "trap_task_get_mapping",
300 "trap_task_set_mapping",
303 "trap_serial_monitor_write",
306 return func_names[func_no >> 1];
310 static int find_empty_desc_tab_slot (
void )
313 if (desc_table[a].
status == HDOS_DESC_CLOSED)
319 static int close_desc (
unsigned int entry )
325 desc_table[entry].basedirpath = NULL;
327 if (desc_table[entry].
status == HDOS_DESC_FILE) {
328 const int ret = close(desc_table[entry].
fd);
329 desc_table[entry].status = HDOS_DESC_CLOSED;
330 DEBUGHDOS(
"HDOS: closing file descriptor #$%02X: %d" NL, entry, ret);
334 }
else if (desc_table[entry].
status == HDOS_DESC_DIR) {
336 desc_table[entry].status = HDOS_DESC_CLOSED;
337 DEBUGHDOS(
"HDOS: closing directory descriptor #$%02X: %d" NL, entry, ret);
341 }
else if (desc_table[entry].
status == HDOS_DESC_CLOSED) {
344 FATAL(
"HDOS: %s() trying to close desc with unknown status!", __func__);
353 static int try_open (
const char *basedirfn,
const char *needfn,
int open_mode,
struct stat *
st,
char *fullpathout,
void *result )
355 if (strchr(needfn,
'/') || strchr(needfn,
'\\') || !*needfn)
360 char fn_found[FILENAME_MAX];
363 if (!strcasecmp(fn_found, needfn) && strlen(fn_found) <= 63)
371 xemu_os_rewinddir(
dirp);
380 strcpy(fullpathout, basedirfn);
381 if (fullpathout[strlen(fullpathout) - 1] !=
DIRSEP_CHR)
383 strcat(fullpathout, fn_found);
386 const int type =
st->st_mode & S_IFMT;
387 if (open_mode == -1) {
407 static void hdos_virt_mount (
const int unit )
409 hdos.func_is_virtualized = 1;
411 char fullpath[PATH_MAX + 1];
413 const int ret = try_open(hdos.cwd, hdos.setname_fn, O_RDWR, &
st, fullpath, &
fd);
415 DEBUGHDOS(
"HDOS: VIRT: >> mount << would fail on try_open() = %d!" NL, ret);
416 hdos.virt_out_a = ret;
421 DEBUGHDOS(
"HDOS: VIRT: mount of image \"%s\" on unit %d FAILED :(" NL, fullpath, unit);
426 DEBUGHDOS(
"HDOS: VIRT: mount of image \"%s\" on unit %d went well." NL, fullpath, unit);
427 hdos.virt_out_carry = 1;
431 static void hdos_virt_opendir (
void )
433 hdos.func_is_virtualized = 1;
434 const int e = find_empty_desc_tab_slot();
445 desc_table[e].status = HDOS_DESC_DIR;
446 desc_table[e].dirp =
dirp;
447 desc_table[e].dir_entry_no = 0;
449 hdos.virt_out_carry = 1;
453 static void hdos_virt_close_dir_or_file (
void )
455 hdos.func_is_virtualized = 1;
456 if (close_desc(hdos.in_x))
459 hdos.virt_out_carry = 1;
463 static void hdos_virt_readdir (
void )
465 hdos.func_is_virtualized = 1;
466 if (hdos.in_x >=
HDOS_DESCRIPTORS || desc_table[hdos.in_x].status != HDOS_DESC_DIR) {
470 if (hdos.in_y >= 0x80) {
475 char fn_found[FILENAME_MAX];
478 if (in_emu_root && desc_table[hdos.in_x].dir_entry_no == 0) {
480 memset(mem, 0,
sizeof mem);
481 static const char volume_name[] =
"XEMU-VRT";
482 memcpy(mem, volume_name, strlen(volume_name));
483 memcpy(mem + 65, volume_name, strlen(volume_name));
484 mem[64] = strlen(volume_name);
486 desc_table[hdos.in_x].dir_entry_no = 1;
487 if (copy_mem_to_user(hdos.in_y << 8, mem,
sizeof mem) !=
sizeof mem)
490 hdos.virt_out_carry = 1;
498 DEBUGHDOS(
"HDOS: VIRT: %s(): end-of-directory" NL, __func__);
502 desc_table[hdos.in_x].dir_entry_no++;
503 memset(mem, 0,
sizeof mem);
504 memset(mem + 65, 0x20, 8 + 3);
505 if (fn_found[0] ==
'.') {
506 if (fn_found[1] ==
'\0' || (fn_found[1] ==
'.' && fn_found[2] ==
'\0')) {
507 if (hdos.cwd_is_root)
511 mem[64] = fn_found[1] ? 2 : 1;
512 memcpy(mem, fn_found, mem[64]);
513 memcpy(mem + 65, fn_found, mem[64]);
519 for (
Uint8 *s = (
Uint8*)fn_found, *t = mem, *sn = mem + 65; *s; s++, t++, mem[64]++) {
523 if (c < 0x20 || c >= 0x80)
525 if (c >=
'a' && c <=
'z')
533 }
else if (sn < mem + 65 + 11)
538 char fn[strlen(desc_table[hdos.in_x].basedirpath) + strlen(fn_found) + 1];
539 sprintf(
fn,
"%s%s", desc_table[hdos.in_x].basedirpath, fn_found);
543 const int type =
st.st_mode & S_IFMT;
544 if (type != S_IFDIR && type != S_IFREG)
546 if (type == S_IFREG &&
st.st_size >= 0x80000000U)
549 DEBUGHDOS(
"HDOS: VIRT: %s(): accepted filename = \"%s\"" NL, __func__, fn_found);
550 const unsigned int fake_start_cluster = desc_table[hdos.in_x].dir_entry_no + 0x10;
551 mem[78] = fake_start_cluster & 0xFF;
552 mem[79] = (fake_start_cluster >> 8) & 0xFF;
553 mem[80] = (fake_start_cluster >> 16) & 0xFF;
554 mem[80] = (fake_start_cluster >> 24) & 0xFF;
556 if (type == S_IFDIR) {
563 mem[82] =
st.st_size & 0xFF;
564 mem[83] = (
st.st_size >> 8) & 0xFF;
565 mem[84] = (
st.st_size >> 16) & 0xFF;
566 mem[85] = (
st.st_size >> 24) & 0xFF;
568 if (copy_mem_to_user(hdos.in_y << 8, mem,
sizeof mem) !=
sizeof mem) {
572 hdos.virt_out_carry = 1;
576 static void hdos_virt_cdroot (
void )
578 hdos.func_is_virtualized = 1;
579 if (!hdos.cwd_is_root) {
580 strcpy(hdos.cwd, hdos.rootdir);
581 hdos.cwd_is_root = 1;
583 hdos.virt_out_carry = 1;
587 static void hdos_virt_cd (
void )
589 hdos.func_is_virtualized = 1;
590 if (hdos.setname_fn[0] ==
'.') {
591 if (hdos.setname_fn[1] ==
'\0') {
593 hdos.virt_out_carry = 1;
596 if (hdos.setname_fn[1] ==
'.' && hdos.setname_fn[2] ==
'\0') {
598 if (hdos.cwd_is_root) {
604 for (
char *p = hdos.cwd + strlen(hdos.cwd) - 2;; p--)
609 hdos.cwd_is_root = !strcmp(hdos.cwd, hdos.rootdir);
610 DEBUGHDOS(
"HDOS: VIRT: %s(\"..\"): now at \"%s\" is_root=%d" NL, __func__, hdos.cwd, hdos.cwd_is_root);
611 hdos.virt_out_carry = 1;
621 char pathout[PATH_MAX + 1];
623 int ret = try_open(hdos.cwd, hdos.setname_fn, -1, &
st, pathout, &
dirp);
625 hdos.virt_out_a = ret;
631 hdos.cwd_is_root = 0;
632 hdos.virt_out_carry = 1;
636 #define HDOS_VIRT_HYPPO_UNIMPLEMENTED() hdos.func_is_virtualized = 1
637 #define HDOS_VIRT_XEMU_UNIMPLEMENTED() do { \
638 DEBUGPRINT("HDOS: VIRT: Unimplemented by Xemu!! %s ~ #$%02X)" NL, hdos.func_name, hdos.func); \
639 hdos.func_is_virtualized = 1; \
649 FATAL(
"%s() is called before HDOS subsystem init!", __func__);
651 hdos.func_name = hdos_get_func_name(hdos.func);
652 hdos.func_is_virtualized = 0;
659 DEBUGHDOS(
"HDOS: entering function #$%02X (%s) A=$%02X X=$%02X Y=$%02X Z=$%02X" NL, hdos.func, hdos.func_name, cpu65.a, cpu65.x, cpu65.y, cpu65.z);
663 hdos.virt_out_a = 0xFF;
664 hdos.virt_out_x = cpu65.x;
665 hdos.virt_out_y = cpu65.y;
666 hdos.virt_out_z = cpu65.z;
667 hdos.virt_out_carry = 0;
675 switch (hdos.func >> 1) {
679 hdos.func_is_virtualized = 1;
681 hdos.virt_out_carry = 1;
684 hdos.func_is_virtualized = 1;
688 hdos.virt_out_carry = 1;
713 hdos_virt_close_dir_or_file();
718 hdos.func_is_virtualized = 1;
719 hdos.virt_out_a = hdos.error_code;
720 hdos.virt_out_carry = 1;
734 if (hdos.func_is_virtualized) {
739 if (hdos.virt_out_carry) {
741 if (hdos.func != 0x38)
745 hdos.error_code = hdos.virt_out_a;
747 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);
751 DEBUGHDOS(
"HDOS: VIRT: unvirtualized DOS function #$%02X pass-through to Hyppo" NL, hdos.func);
761 hdos.func_name = hdos_get_func_name(hdos.func);
762 DEBUGHDOS(
"HDOS: leaving function #$%02X (%s) with carry %s (A=$%02X)" NL, hdos.func, hdos.func_name, cpu65.pf_c ?
"SET" :
"CLEAR", cpu65.a);
764 if (hdos.func_is_virtualized) {
765 DEBUGHDOS(
"HDOS: VIRT: was marked as virtualized, so end of %s in %s()" NL, hdos.func_name, __func__);
766 hdos.func_is_virtualized = 0;
769 if (hdos.func == 0x2E && cpu65.pf_c) {
775 char setnam_current[
sizeof hdos.setname_fn];
777 if (copy_string_from_user(hdos.setname_fn,
sizeof hdos.setname_fn, hdos.in_x + (hdos.in_y << 8)) >= 0)
779 for (
char *p = hdos.setname_fn; *p; p++) {
780 if (*p < 0x20 || *p >= 0x7F) {
782 DEBUGHDOS(
"HDOS: setnam(): invalid character in filename $%02X" NL, *p);
783 hdos.setname_fn[0] =
'\0';
786 if (*p >=
'A' && *p <=
'Z')
789 DEBUGHDOS(
"HDOS: %s: selected filename is [%s] from $%04X" NL, hdos.func_name, hdos.setname_fn, hdos.in_x + (hdos.in_y << 8));
792 if (hdos.func == 0x3A && cpu65.pf_c) {
793 hdos.transfer_area_addr = hdos.in_y << 8;
794 DEBUGHDOS(
"HDOS: transfer area address is set to $%04X" NL, hdos.transfer_area_addr);
797 if (hdos.func == 0x40) {
798 DEBUGPRINT(
"HDOS: %s(\"%s\") = %s" NL, hdos.func_name, hdos.setname_fn, cpu65.pf_c ?
"OK" :
"FAILED");
799 if (!strcasecmp(hdos.setname_fn,
"MEGA65.D81"))
800 OSD(-1, -1,
"MEGA65.D81 ;-)");
808 static void hdos_reset (
void )
811 hdos.setname_fn[0] =
'\0';
814 hdos.transfer_area_addr = 0;
822 if (hdos_init_is_done)
832 if (!!hdos.do_virt != !!set) {
834 DEBUGPRINT(
"HDOS: virtualization is now %s" NL, set ?
"ENABLED" :
"DISABLED");
838 *root_ptr = hdos.rootdir;
849 DEBUGHDOS(
"HDOS: system-start-begin notification received." NL);
857 DEBUGHDOS(
"HDOS: system-start-end notification recevied." NL);
865 if (hdos_init_is_done)
866 FATAL(
"%s() called more than once!", __func__);
867 hdos_init_is_done = 1;
868 DEBUGHDOS(
"HDOS: initialization with do_virt=%d and virtroot=\"%s\"" NL,
do_virt, virtroot ? virtroot :
"<NULL>");
870 desc_table[a].status = HDOS_DESC_CLOSED;
871 desc_table[a].basedirpath = NULL;
877 char hdosdir[PATH_MAX + 1];
882 FATAL(
"Cannot open default HDOS root: %s", hdosdir);
884 if (virtroot && *virtroot) {
889 strcpy(hdosdir, virtroot);
890 if (hdosdir[strlen(hdosdir) - 1] !=
DIRSEP_CHR)
893 ERROR_WINDOW(
"HDOS: bad HDOS virtual directory given (cannot open as directory): %s\nUsing the default instead: %s", virtroot, hdosdir);
897 hdos.cwd_is_root = 1;
899 DEBUGPRINT(
"HDOS: virtualization is %s, root = \"%s\"" NL, hdos.do_virt ?
"ENABLED" :
"DISABLED", hdos.rootdir);