diff options
Diffstat (limited to 'sftsr.c')
-rw-r--r-- | sftsr.c | 891 |
1 files changed, 891 insertions, 0 deletions
@@ -0,0 +1,891 @@ +#include <stdlib.h> +#include <string.h> +#include <i86.h> + +#include "int21dos.h" +#include "vboxshfl.h" +#include "sftsr.h" + +TSRDATA data; + +/** Private buffer for VirtualBox filenames. */ +static SHFLSTRING_WITH_BUF(shflstr, SHFL_MAX_LEN); + +static SHFLDIRINFO_WITH_BUF(shfldirinfo, SHFL_MAX_LEN); + +static SHFLCREATEPARMS createparms; + +union qword_to_words { + uint64_t q; + struct { + uint16_t w[4]; + }; +}; + +#if 0 +static uint32_t map_timestampns_to_timestamps(uint64_t timestampns) +{ + unsigned long timestampsec; + + __asm { + push eax ; Preserve 32-bit regs + push edx + mov eax, dword ptr [timestampns] + mov edx, dword ptr [timestampns + 4] + + mov ecx, 1000000000 /* ns to seconds */ + div ecx + mov [timestampsec], eax + pop edx + pop eax + } + + return timestampsec; +} +#endif + +static void map_timestampns_to_dostime(uint64_t timestampns, + uint16_t __far * time, uint16_t __far * date) +{ + // TODO + (void) timestampns; + *time = 0; + *date = 0; +} + +static uint8_t map_shfl_attr_to_dosattr(const SHFLFSOBJATTR *a) +{ + // DOS attributes are in the higher word of fMode (see RTFS_DOS_*) + uint8_t attr = (a->fMode >> 16) & 0x3F; + if (a->fMode & 0x4000U) attr |= _A_SUBDIR; + return attr; +} + +static void map_shfl_info_to_dossft(DOSSFT __far *sft, SHFLFSOBJINFO *i) +{ + sft->attr = map_shfl_attr_to_dosattr(&i->Attr); + sft->f_size = i->cbObject; + map_timestampns_to_dostime(i->ModificationTime, &sft->f_time, &sft->f_date); +} + +static void map_shfl_info_to_dosdir(DOSDIR __far *dir, SHFLFSOBJINFO *i) +{ + dir->attr = map_shfl_attr_to_dosattr(&i->Attr); + dir->f_size = i->cbObject; + map_timestampns_to_dostime(i->ModificationTime, &dir->f_time, &dir->f_date); + dir->start_cluster = 0; +} + +static uint64_t get_sft_vbox_handle(DOSSFT __far *sft) +{ + union qword_to_words u; + u.w[0] = sft->start_cluster; + u.w[1] = sft->last_rel_cluster; + u.w[2] = sft->last_abs_cluster; + u.w[3] = sft->dir_sector; + return u.q; +} + +static void set_sft_vbox_handle(DOSSFT __far *sft, uint64_t handle) +{ + union qword_to_words u; + u.q = handle; + sft->start_cluster = u.w[0]; + sft->last_rel_cluster = u.w[1]; + sft->last_abs_cluster = u.w[2]; + sft->dir_sector = u.w[3]; +} + +#if ENABLE_DLOG +static void print_handle(uint64_t handle) +{ + + union qword_to_words u; + u.q = handle; + dlog_printx(u.w[3]); + dlog_putc('.'); + dlog_printx(u.w[2]); + dlog_putc('.'); + dlog_printx(u.w[1]); + dlog_putc('.'); + dlog_printx(u.w[0]); +} +#else +#define print_handle(h) dlog_nop() +#endif + +static int get_op_drive_num(union INTPACK __far *r) +{ + DOSSFT __far *sft; + + switch (r->h.al) { + case DOS_FN_CLOSE: + case DOS_FN_COMMIT: + case DOS_FN_READ: + case DOS_FN_WRITE: + case DOS_FN_LOCK: + case DOS_FN_UNLOCK: + case DOS_FN_SEEK_END: + // Some operations use an SFT and we directly get the drive from it + sft = MK_FP(r->x.es, r->x.di); + return sft->dev_info & 0x1F; + + case DOS_FN_RMDIR: + case DOS_FN_MKDIR: + case DOS_FN_CHDIR: + case DOS_FN_SET_FILE_ATTR: + case DOS_FN_GET_FILE_ATTR: + case DOS_FN_RENAME: + case DOS_FN_DELETE: + case DOS_FN_OPEN: + case DOS_FN_CREATE: + case DOS_FN_FIND_FIRST: + case DOS_FN_OPEN_EX: + // We can use the drive of the first filename argument for these operations + return drive_letter_to_index(data.dossda->fn1[0]); + + case DOS_FN_GET_DISK_FREE: + // Otherwise just get the current drive from dos + return drive_letter_to_index(data.dossda->drive_cds->curr_path[0]); + + case DOS_FN_FIND_NEXT: + return data.dossda->sdb.drive_letter & 0x1F; + + default: + // We don't support this operation + return -1; + } +} + +static bool is_call_for_mounted_drive(union INTPACK __far *r) +{ + int drive = get_op_drive_num(r); + + if (drive < 0 || drive >= MAX_NUM_DRIVE) { + return false; + } + + return data.drives[drive].root != SHFL_ROOT_NIL; +} + +static void clear_dos_err(union INTPACK __far *r) +{ + r->x.flags &= ~INTR_CF; +} + +static void set_dos_err(union INTPACK __far *r, int err) +{ + r->x.flags |= INTR_CF; + r->x.ax = err; +} + +static int vbox_err_to_dos(int32_t err) +{ + switch (err) { + case VINF_SUCCESS: + return DOS_ERROR_SUCCESS; + case VERR_INVALID_PARAMETER: + return DOS_ERROR_INVALID_FUNCTION; + case VERR_INVALID_HANDLE: + return DOS_ERROR_INVALID_HANDLE; + case VERR_FILE_NOT_FOUND: + return DOS_ERROR_FILE_NOT_FOUND; + case VERR_PATH_NOT_FOUND: + return DOS_ERROR_PATH_NOT_FOUND; + case VERR_NO_MORE_FILES: + return DOS_ERROR_NO_MORE_FILES; + default: + return DOS_ERROR_GEN_FAILURE; + } +} + +static void set_vbox_err(union INTPACK __far *r, int32_t err) +{ + dlog_print("vbox error "); + dlog_printx(err >> 16); + dlog_print(":"); + dlog_printx(err & 0xFFFF); + dlog_endline(); + r->x.flags |= INTR_CF; + r->x.ax = vbox_err_to_dos(err); +} + +static int my_strrchr(const char __far *str, char c) +{ + int last = -1; + const char __far *s = str; + while (*s) { + if (*s == c) last = s - str; + s++; + } + return last; +} + +static const char * get_basename(const char *path) +{ + int last_sep = my_strrchr(path, '\\'); + if (last_sep >= 0) { + return &path[last_sep+1]; + } else { + return path; + } +} + +static void translate_filename_to_host(SHFLSTRING *str) +{ + // TODO This should map UTF-8 to local CP :( + (void) str; +} + +static void translate_filename_from_host(SHFLSTRING *str) +{ + // TODO This should map UTF-8 to local CP :( + // At least do a poor man's uppercase... + unsigned i; + for (i = 0; i < str->u16Length; i++) { + if (str->ach[i] >= 'a' && str->ach[i] <= 'z') { + str->ach[i] = 'A' + (str->ach[i] - 'a'); + } + } +} + +static void fix_wildcards(SHFLSTRING *str) +{ + unsigned i; + + // If this is the standard ????????.??? patterns, replace with * + if (str->u16Length >= 8+1+3) { + i = str->u16Length - (8+1+3); + if (memcmp(&str->ach[i], "????????.???", (8+1+3)) == 0) { + strcpy(&str->ach[i], "*"); + str->u16Length = i + 1; + } + } + + for (i = 1; i < str->u16Length; i++) { + // VirtualBox will only match N consecutive ?s to filenames longer than N + // Replace consecutive ???? with ?*** + if ( str->ach[i] == '?' && + (str->ach[i-1] == '*' || str->ach[i-1] == '?') ) { + str->ach[i] = '*'; + } + } +} + + +static void copy_drive_relative_filename(SHFLSTRING *str, const char __far *path) +{ + // Assume X:.... path for now, i.e. drive_relative path starts at char 2 + shflstring_strcpy(str, path + 2); +} + +static void copy_drive_relative_dirname(SHFLSTRING *str, const char __far *path) +{ + int last_sep = my_strrchr(path + 2, '\\'); + if (last_sep >= 0) { + shflstring_strncpy(str, path + 2, last_sep == 0 ? 1 : last_sep); + } else { + shflstring_strcpy(str, path + 2); + } +} + +static bool copy_to_8_3_filename(char __far *dst, const SHFLSTRING *str) +{ + int last_dot = my_strrchr(str->ach, '.'); + unsigned namelen, extlen; + bool valid_8_3 = true; + + namelen = last_dot >= 0 ? last_dot : str->u16Length; + extlen = last_dot >= 0 ? str->u16Length - (last_dot + 1) : 0; + + if (extlen == 0 && (namelen <= 1 && str->ach[0] == '.')) { + // . , .. files + namelen = str->u16Length; + extlen = 0; + } + + if (namelen > 8) { + namelen = 8; + valid_8_3 = false; + } + + if (extlen > 3) { + extlen = 3; + valid_8_3 = false; + } + + _fmemcpy(&dst[0], str->ach, namelen); + _fmemset(&dst[namelen], ' ', 8 - namelen); + _fmemcpy(&dst[8], str->ach + last_dot + 1, extlen); + _fmemset(&dst[8+extlen], ' ', 3 - extlen); + + return valid_8_3; +} + +static void handle_create_open_ex(union INTPACK __far *r) +{ + const char __far *path = data.dossda->fn1; + int drive = drive_letter_to_index(path[0]); + SHFLROOT root = data.drives[drive].root; + DOSSFT __far *sft = MK_FP(r->x.es, r->x.di); + unsigned int action, mode; + bool save_result; + int32_t err; + + switch (r->h.al) { + case DOS_FN_CREATE: + action = OPENEX_CREATE_IF_NEW | OPENEX_REPLACE_IF_EXISTS; + mode = OPENEX_MODE_WRITE; + save_result = false; + break; + case DOS_FN_OPEN: + action = OPENEX_FAIL_IF_NEW | OPENEX_OPEN_IF_EXISTS; + mode = data.dossda->open_mode & 0x7F; + save_result = false; + break; + case DOS_FN_OPEN_EX: + default: + action = data.dossda->openex_act; + mode = data.dossda->openex_mode & 0x7F; + save_result = true; + break; + } + + dlog_print("handle_open for "); + dlog_fprint(path); + dlog_print(" act="); + dlog_printx(action); + dlog_print(" mode="); + dlog_printx(mode); + dlog_endline(); + + copy_drive_relative_filename(&shflstr.shflstr, path); + translate_filename_to_host(&shflstr.shflstr); + + memset(&createparms, 0, sizeof(SHFLCREATEPARMS)); + if (action & OPENEX_REPLACE_IF_EXISTS) { + createparms.CreateFlags |= SHFL_CF_ACT_REPLACE_IF_EXISTS; + } else if (action & OPENEX_OPEN_IF_EXISTS) { + createparms.CreateFlags |= SHFL_CF_ACT_OPEN_IF_EXISTS; + } else { + createparms.CreateFlags |= SHFL_CF_ACT_FAIL_IF_EXISTS; + } + if (action & OPENEX_CREATE_IF_NEW) { + createparms.CreateFlags |= SHFL_CF_ACT_CREATE_IF_NEW; + } else { + createparms.CreateFlags |= SHFL_CF_ACT_FAIL_IF_NEW; + } + + if (mode == OPENEX_MODE_RDWR) { + createparms.CreateFlags |= SHFL_CF_ACCESS_READWRITE; + } else if (mode == OPENEX_MODE_WRITE) { + createparms.CreateFlags |= SHFL_CF_ACCESS_WRITE; + } else { + createparms.CreateFlags |= SHFL_CF_ACCESS_READ; + } + + dlog_print("vbox createparms flags="); + dlog_printx(createparms.CreateFlags); + dlog_endline(); + + err = vbox_shfl_open(&data.vb, data.hgcm_client_id, root, &shflstr.shflstr, &createparms); + if (err) { + set_vbox_err(r, err); + return; + } + + dlog_print("vbox success result="); + dlog_printd(createparms.Result); + dlog_print(" handle="); + print_handle(createparms.Handle); + dlog_endline(); + + switch (createparms.Result) { + case SHFL_PATH_NOT_FOUND: + r->x.ax = DOS_ERROR_PATH_NOT_FOUND; + r->x.flags |= INTR_CF; + return; + case SHFL_FILE_NOT_FOUND: + r->x.ax = DOS_ERROR_FILE_NOT_FOUND; + r->x.flags |= INTR_CF; + return; + case SHFL_FILE_EXISTS: + if (save_result) r->x.cx = OPENEX_FILE_OPENED; + break; + case SHFL_FILE_CREATED: + if (save_result) r->x.cx = OPENEX_FILE_CREATED; + break; + case SHFL_FILE_REPLACED: + if (save_result) r->x.cx = OPENEX_FILE_REPLACED; + break; + } + + // Fill in the SFT + map_shfl_info_to_dossft(sft, &createparms.Info); + sft->open_mode = mode; + sft->dev_info = 0x8040 | drive; // "Network drive, unwritten to" + sft->f_pos = 0; + set_sft_vbox_handle(sft, createparms.Handle); + + clear_dos_err(r); +} + +static void handle_close(union INTPACK __far *r) +{ + const char __far *path = data.dossda->fn1; + int drive = drive_letter_to_index(path[0]); + SHFLROOT root = data.drives[drive].root; + DOSSFT __far *sft = MK_FP(r->x.es, r->x.di); + uint64_t handle = get_sft_vbox_handle(sft); + int32_t err; + + dlog_print("handle_close for "); + print_handle(handle); + dlog_endline(); + + err = vbox_shfl_close(&data.vb, data.hgcm_client_id, root, handle); + if (err) { + set_vbox_err(r, err); + return; + } + + clear_dos_err(r); +} + +static void handle_read(union INTPACK __far *r) +{ + DOSSFT __far *sft = MK_FP(r->x.es, r->x.di); + int drive = sft->dev_info & 0x1F; + SHFLROOT root = data.drives[drive].root; + uint64_t handle = get_sft_vbox_handle(sft); + uint8_t __far *buffer = data.dossda->cur_dta; + unsigned long offset = sft->f_pos; + unsigned bytes = r->x.cx; + int32_t err; + + dlog_print("handle_read handle="); + print_handle(handle); + dlog_print(" bytes="); + dlog_printu(bytes); + dlog_endline(); + + err = vbox_shfl_read(&data.vb, data.hgcm_client_id, root, handle, + offset, &bytes, buffer); + if (err) { + set_vbox_err(r, err); + return; + } + + dlog_print("handle_read bytes_read="); + dlog_printu(bytes); + dlog_endline(); + + // Advance the file position + sft->f_pos += bytes; + + r->x.cx = bytes; + clear_dos_err(r); +} + +static void handle_write(union INTPACK __far *r) +{ + DOSSFT __far *sft = MK_FP(r->x.es, r->x.di); + int drive = sft->dev_info & 0x1F; + SHFLROOT root = data.drives[drive].root; + uint64_t handle = get_sft_vbox_handle(sft); + uint8_t __far *buffer = data.dossda->cur_dta; + unsigned long offset = sft->f_pos; + unsigned bytes = r->x.cx; + int32_t err; + + dlog_print("handle_write handle="); + print_handle(handle); + dlog_print(" bytes="); + dlog_printu(bytes); + dlog_endline(); + + err = vbox_shfl_write(&data.vb, data.hgcm_client_id, root, handle, + offset, &bytes, buffer); + if (err) { + set_vbox_err(r, err); + return; + } + + dlog_print("handle_write bytes_written="); + dlog_printu(bytes); + dlog_endline(); + + // Advance the file position + sft->f_pos += bytes; + + // Assume the file has grown if we've written past the end + if (sft->f_pos > sft->f_size) sft->f_size = sft->f_pos; + + r->x.cx = bytes; + clear_dos_err(r); +} + +static int32_t open_search_dir(SHFLROOT root, const char __far *path) +{ + int32_t err; + + dlog_puts("open_search_dir"); + + copy_drive_relative_dirname(&shflstr.shflstr, path); + translate_filename_to_host(&shflstr.shflstr); + + memset(&createparms, 0, sizeof(SHFLCREATEPARMS)); + createparms.CreateFlags = SHFL_CF_DIRECTORY + | SHFL_CF_ACT_OPEN_IF_EXISTS | SHFL_CF_ACT_FAIL_IF_NEW + | SHFL_CF_ACCESS_READ; + + err = vbox_shfl_open(&data.vb, data.hgcm_client_id, root, + &shflstr.shflstr, &createparms); + if (err) { + dlog_puts("open search dir failed"); + return err; + } + + switch (createparms.Result) { + case SHFL_PATH_NOT_FOUND: + return VERR_PATH_NOT_FOUND; + case SHFL_FILE_NOT_FOUND: + return VERR_FILE_NOT_FOUND; + default: + break; + } + + data.search_dir_handle = createparms.Handle; + return 0; +} + +static int close_search_dir(SHFLROOT root) +{ + int32_t err; + + if (data.search_dir_handle == SHFL_HANDLE_NIL) return 0; + + err = vbox_shfl_close(&data.vb, data.hgcm_client_id, root, + data.search_dir_handle); + + data.search_dir_handle = SHFL_HANDLE_NIL; + + if (err) { + return vbox_err_to_dos(err); + } + + return 0; +} + +static int32_t find_volume_label(SHFLROOT root) +{ + DOSDIR __far *found_file = &data.dossda->found_file; + int32_t err; + + shflstring_clear(&shflstr.shflstr); + + err = vbox_shfl_query_map_name(&data.vb, data.hgcm_client_id, root, &shflstr.shflstr); + if (err) return err; + + translate_filename_from_host(&shflstr.shflstr); + + dlog_print("label : "); + dlog_fprint(shflstr.buf); + dlog_endline(); + + copy_to_8_3_filename(found_file->filename, &shflstr.shflstr); + found_file->attr = _A_VOLID | _A_HIDDEN; + + return 0; +} + +static int32_t find_next_from_vbox(SHFLROOT root, uint8_t search_attr) +{ + DOSDIR __far *found_file = &data.dossda->found_file; + int32_t err; + + shfldirinfo.dirinfo.name.u16Size = sizeof(shfldirinfo.buf); + + while (1) { // Loop until we have a valid file (or an error) + unsigned size = sizeof(shfldirinfo), resume = 0, count = 0; + dlog_puts("calling vbox list"); + + err = vbox_shfl_list(&data.vb, data.hgcm_client_id, root, data.search_dir_handle, + SHFL_LIST_RETURN_ONE, &size, &shflstr.shflstr, &shfldirinfo.dirinfo, + &resume, &count); + if (err) { + return err; + } + + if (count != 1) { + return VERR_IO_BAD_LENGTH; + } + + dlog_print("got diritem name="); + dlog_fprint(shfldirinfo.dirinfo.name.ach); + dlog_print(" sfnLen="); + dlog_printu(shfldirinfo.dirinfo.cucShortName); + dlog_endline(); + + // TODO Use the short filename if available from a windows host + + translate_filename_from_host(&shfldirinfo.dirinfo.name); + + if (!copy_to_8_3_filename(found_file->filename, &shfldirinfo.dirinfo.name)) { + dlog_puts("hiding file with long filename"); + continue; + } + map_shfl_info_to_dosdir(found_file, &shfldirinfo.dirinfo.Info); + + if (found_file->attr & ~search_attr) { + continue; // Skip this one + } + + break; + }; + + return 0; +} + +static void handle_find(union INTPACK __far *r) +{ + const char __far *path = data.dossda->fn1; + int drive = drive_letter_to_index(path[0]); + SHFLROOT root = data.drives[drive].root; + DOSDIR __far *found_file = &data.dossda->found_file; + uint8_t search_attr; + int32_t err; + + if (r->h.al == DOS_FN_FIND_FIRST) { + search_attr = data.dossda->search_attr; + + dlog_print("find_first path="); + dlog_fprint(path); + dlog_print(" attr="); + dlog_printx(search_attr); + dlog_endline(); + + close_search_dir(root); + err = open_search_dir(root, path); + if (err) { + set_vbox_err(r, err); + return; + } + + copy_drive_relative_filename(&shflstr.shflstr, path); + translate_filename_to_host(&shflstr.shflstr); + fix_wildcards(&shflstr.shflstr); + + data.dossda->sdb.drive_letter = 0x80 | drive; + data.dossda->sdb.search_attr = search_attr; + _fmemset(data.dossda->sdb.search_templ, ' ', 8+3); + data.dossda->sdb.dir_entry = 0; + data.dossda->sdb.par_clstr = 0; + + if (search_attr & _A_VOLID) { + // Simulate an initial entry: volume label + dlog_puts("search volid"); + // DOS wants volume label; optimize that + err = find_volume_label(root); + if (err) { + dlog_puts("search volid err"); + set_vbox_err(r, err); + return; + } + dlog_puts("search volid OK"); + clear_dos_err(r); + return; + } + } else { + search_attr = data.dossda->sdb.search_attr; + + dlog_print("find_next"); + dlog_print(" attr="); + dlog_printx(search_attr); + dlog_endline(); + } + + found_file->attr = 0; + + err = find_next_from_vbox(root, search_attr); + if (err) { + if (err == VERR_NO_MORE_FILES) { + dlog_puts("no more files"); + } + close_search_dir(root); + set_vbox_err(r, err); + return; + } + + dlog_print("accepted file name='"); + dlog_fnprint(&found_file->filename[0], 8); + dlog_putc(' '); + dlog_fnprint(&found_file->filename[8], 3); + dlog_print("' attr="); + dlog_printx(found_file->attr); + dlog_endline(); + + clear_dos_err(r); +} + +static void handle_chdir(union INTPACK __far *r) +{ + const char __far *path = data.dossda->fn1; + int drive = drive_letter_to_index(path[0]); + SHFLROOT root = data.drives[drive].root; + int32_t err; + + dlog_print("handle_chdir to "); + dlog_fprint(path); + dlog_endline(); + + // Just have to check if the directory exists + copy_drive_relative_filename(&shflstr.shflstr, path); + translate_filename_to_host(&shflstr.shflstr); + + memset(&createparms, 0, sizeof(SHFLCREATEPARMS)); + createparms.CreateFlags = SHFL_CF_LOOKUP; + + err = vbox_shfl_open(&data.vb, data.hgcm_client_id, root, + &shflstr.shflstr, &createparms); + if (err) { + set_vbox_err(r, err); + return; + } + + switch (createparms.Result) { + case SHFL_PATH_NOT_FOUND: + case SHFL_FILE_NOT_FOUND: + set_dos_err(r, DOS_ERROR_PATH_NOT_FOUND); + return; + default: + break; + } + + // Also check whether it is really a directory + if (!(map_shfl_attr_to_dosattr(&createparms.Info.Attr) & _A_SUBDIR)) { + set_dos_err(r, DOS_ERROR_PATH_NOT_FOUND); + return; + } + + clear_dos_err(r); +} + +static bool int2f_11_handler(union INTPACK r) +#pragma aux int2f_11_handler "*" parm caller [] value [al] modify [ax bx cx dx si di es gs fs] +{ + if (r.h.ah != 0x11) return false; // Only interested in network redirector functions + if (r.h.al == 0xff && r.x.bx == 0x5742 && r.x.cx == 0x5346) { + // These are the magic numbers to our private "Get TSR data" function + dlog_puts("Get TSR data"); + r.x.es = get_ds(); + r.x.di = FP_OFF(&data); + r.x.bx = 0x5444; + r.x.cx = 1; + return true; + } + + dlog_print("2f al="); + dlog_printx(r.h.al); + dlog_endline(); + + if (!is_call_for_mounted_drive(&r)) { + return false; + } + + switch (r.h.al) { + case DOS_FN_CLOSE: + handle_close(&r); + return true; + case DOS_FN_CREATE: + case DOS_FN_OPEN: + case DOS_FN_OPEN_EX: + handle_create_open_ex(&r); + return true; + case DOS_FN_READ: + handle_read(&r); + return true; + case DOS_FN_WRITE: + handle_write(&r); + return true; + case DOS_FN_FIND_FIRST: + case DOS_FN_FIND_NEXT: + handle_find(&r); + return true; + case DOS_FN_CHDIR: + handle_chdir(&r); + return true; + case DOS_FN_GET_DISK_FREE: + // We don't support this + set_dos_err(&r, DOS_ERROR_INVALID_FUNCTION); + return true; + } + + return false; +} + +void __declspec(naked) __far int2f_isr(void) +{ + __asm { + pusha + push ds + push es + push fs + push gs + + mov bp, sp + push cs + pop ds + + call int2f_11_handler + test al, al + jnz handled + + pop gs + pop fs + pop es + pop ds + popa + + ; Jump to the next handler in the chain + jmp dword ptr cs:[data + 0] ; wasm doesn't support structs, this is data.prev_int2f_handler + + handled: + pop gs + pop fs + pop es + pop ds + popa + iret + } +} + +static LPTSRDATA int2f_get_tsr_data(void); +#pragma aux int2f_get_tsr_data = \ + "mov ax, 0x11ff" \ + "mov bx, 0x5742" /* Add magic numbers */ \ + "mov cx, 0x5346" \ + "int 0x2f" \ + "cmp bx, 0x5444" /* Test output magic number */ \ + "jne fail" \ + "cmp cx, 1" \ + "jne fail" \ + "jmp end" \ + "fail:" \ + "xor ax, ax" \ + "mov es, ax" \ + "mov di, ax" \ + "end:" \ + __value [es di] \ + __modify [ax bx cx] + +LPTSRDATA __far get_tsr_data(bool installed) +{ + if (installed) { + return int2f_get_tsr_data(); + } else { + return MK_FP(get_cs(), FP_OFF(&data)); + } +} + +int resident_end; |