diff options
author | Daniel Serpell <daniel.serpell@gmail.com> | 2019-12-17 00:53:42 -0300 |
---|---|---|
committer | Daniel Serpell <daniel.serpell@gmail.com> | 2019-12-17 00:53:42 -0300 |
commit | fd32200c9a504c101907ce7e029174acf573f988 (patch) | |
tree | 9a2f7092bb2d06bf33f2c98f6551b847e346bbaa | |
parent | 513a61c5fc73f7cd2d766d8d89f85219e9069e71 (diff) | |
download | emu2-fd32200c9a504c101907ce7e029174acf573f988.tar.gz |
Implement minimal support for FCB operations.
This allows running some very old DOS programs.
-rw-r--r-- | src/dos.c | 147 | ||||
-rw-r--r-- | src/dosnames.c | 37 | ||||
-rw-r--r-- | src/dosnames.h | 6 |
3 files changed, 190 insertions, 0 deletions
@@ -257,6 +257,76 @@ static void dos_open_file(int create) free(fname); } +static int get_fcb_handle(void) +{ + return get16(0x18 + cpuGetAddrDS(cpuGetDX())); +} + +static void dos_show_fcb() +{ + if(!debug_active(debug_dos)) + return; + + int addr = cpuGetAddrDS(cpuGetDX()); + char name[12]; + memcpy(name, &memory[addr+1], 11); + + debug(debug_dos,"\tFCB:[d=%02x:n=%.8s.%.3s:bn=%04x:rs=%04x:fs=%08x:h=%04x]\n", + memory[0xFFFFF&addr],name,name+8,get16(addr+0x0C), + get16(addr+0x0E),get16(addr+0x10)+65536*get16(addr+0x12), + get16(addr+0x18)); +} + +static void dos_open_file_fcb(int create) +{ + int h = get_new_handle(); + if(!h) + { + cpuSetAX(0xFF); + cpuSetFlag(cpuFlag_CF); + return; + } + int fcb_addr = cpuGetAddrDS(cpuGetDX()); + char *fname = dos_unix_path_fcb(fcb_addr, create); + if(!fname) + { + debug(debug_dos, "\t(file not found)\n"); + cpuSetAX(0xFF); + cpuSetFlag(cpuFlag_CF); + return; + } + const char *mode = create ? "w+b" : "r+b"; + debug(debug_dos, "\topen fcb '%s', '%s', %04x ", fname, mode, h); + handles[h] = fopen(fname, mode); + if(!handles[h]) + { + debug(debug_dos, "%s.\n", strerror(errno)); + cpuSetAX(0xFF); + cpuSetFlag(cpuFlag_CF); + free(fname); + return; + } + // Get file size + fseek(handles[h], 0, SEEK_END); + long sz = ftell(handles[h]); + fseek(handles[h], 0, SEEK_SET); + // Set FCB info: + put16(fcb_addr+0x0C, 0); // block number + put16(fcb_addr+0x0E, 0); // record size + put16(fcb_addr+0x10, sz & 0xFFFF); + put16(fcb_addr+0x12, sz >> 16); + put16(fcb_addr+0x14, 0); // date of last write + put16(fcb_addr+0x16, 0); // time of last write + put16(fcb_addr+0x18, h); // reserved - store DOS handle! + + debug(debug_dos, "OK.\n"); + cpuClrFlag(cpuFlag_CF); + cpuSetAX(0x00); + dos_show_fcb(); + free(fname); +} + + // Converts Unix time_t to DOS time/date static uint32_t get_time_date(time_t tm) { @@ -779,6 +849,17 @@ void int21() // Number of drives = 3, 'A:', 'B:' and 'C:' cpuSetAX(0x0E03); break; + case 0x0F: // OPEN FILE USING FCB + dos_open_file_fcb(0); + break; + case 0x10: // CLOSE FILE USING FCB + dos_show_fcb(); + dos_close_file(get_fcb_handle()); + cpuSetAX(0); + break; + case 0x16: // CREATE FILE USING FCB + dos_open_file_fcb(1); + break; case 0x19: // GET DEFAULT DRIVE debug(debug_dos, "\tget default drive = '%c'\n", dos_get_default_drive() + 'A'); cpuSetAX(dos_get_default_drive()); @@ -796,6 +877,72 @@ void int21() put16(4 * (ax & 0xFF), cpuGetDX()); put16(4 * (ax & 0xFF) + 2, cpuGetDS()); break; + case 0x27: // BLOCK READ FROM FCB + { + dos_show_fcb(); + FILE *f = handles[get_fcb_handle()]; + if(!f) + { + cpuSetFlag(cpuFlag_CF); + cpuSetAX(1); // no data read + return; + } + unsigned bnum = get16(0x0C + cpuGetAddrDS(cpuGetDX())); + unsigned bsize = get16(0x0E + cpuGetAddrDS(cpuGetDX())); + unsigned len = cpuGetCX(); + uint8_t *buf = getptr(dosDTA, bsize * len); + if(!buf) + { + debug(debug_dos, "\tbuffer pointer invalid\n"); + cpuSetAX(2); // segment wrap in DTA + cpuSetFlag(cpuFlag_CF); + break; + } + // Seek to block and read + fseek(f, bnum * bsize, SEEK_SET); + unsigned n = fread(buf, bsize, len, f); + if( n == len ) + cpuSetAX(0); + else + cpuSetAX(1); + // Update position + put16(0x0C + cpuGetAddrDS(cpuGetDX()), bnum + n); + cpuSetCX(n); + cpuClrFlag(cpuFlag_CF); + dos_show_fcb(); + break; + } + case 0x28: // BLOCK WRITE TO FCB + { + dos_show_fcb(); + FILE *f = handles[get_fcb_handle()]; + if(!f) + { + cpuSetFlag(cpuFlag_CF); + cpuSetAX(1); // disk full or file read-only + return; + } + unsigned bnum = get16(0x0C + cpuGetAddrDS(cpuGetDX())); + unsigned bsize = get16(0x0E + cpuGetAddrDS(cpuGetDX())); + unsigned len = cpuGetCX(); + uint8_t *buf = getptr(dosDTA, bsize * len); + if(!buf) + { + debug(debug_dos, "\tbuffer pointer invalid\n"); + cpuSetAX(2); // segment wrap in DTA + cpuSetFlag(cpuFlag_CF); + break; + } + // Seek to block and write + fseek(f, bnum * bsize, SEEK_SET); + unsigned n = fwrite(buf, 1, len, f); + // Update position + put16(0x0C + cpuGetAddrDS(cpuGetDX()), bnum + n); + cpuSetAX(0); + cpuSetCX(n); + cpuClrFlag(cpuFlag_CF); + break; + } case 0x29: // PARSE FILENAME TO FCB { // TODO: length could be more than 64 bytes! diff --git a/src/dosnames.c b/src/dosnames.c index 3a91a76..07ad46d 100644 --- a/src/dosnames.c +++ b/src/dosnames.c @@ -532,6 +532,43 @@ char *dos_unix_path(int addr, int force) return dos_unix_path_rec(base, path, force); } +// Converts a FCB path to equivalent Unix filename +char *dos_unix_path_fcb(int addr, int force) +{ + char path[64]; + char fcb_name[12]; + int opos = 0; + // Copy drive number from the FCB structure: + int drive = memory[addr] & 0xFF; + if( !drive ) + drive = dos_default_drive; + else + drive = drive - 1; + // And copy file name + memcpy(fcb_name, &memory[addr+1], 11); + fcb_name[12] = 0; + debug(debug_dos, "\tconvert dos fcb name %c:'%s'\n", drive + 'A', fcb_name); + + // Copy current directory + memcpy(path, dos_cwd[drive], 64); + opos = strlen(path); + + for(int pos=0; pos<8 && opos<63; pos++, opos++) + if( 0 == (path[opos] = dos_valid_char(fcb_name[pos])) ) + break; + if(opos<63 && dos_valid_char(fcb_name[8])) + path[opos++] = '.'; + for(int pos=8; pos<11 && opos<63; pos++, opos++) + if( 0 == (path[opos] = dos_valid_char(fcb_name[pos])) ) + break; + path[opos] = 0; + // Get UNIX base path: + const char *base = get_base_path(drive); + // Adds CWD if path is not absolute + return dos_unix_path_rec(base, path, force); +} + + //////////////////////////////////////////////////////////////////// // Implements FindFirstFile struct dos_file_list *dos_find_first_file(int addr) diff --git a/src/dosnames.h b/src/dosnames.h index 61858d7..38362e6 100644 --- a/src/dosnames.h +++ b/src/dosnames.h @@ -12,6 +12,12 @@ // If the file does not exists, and "force" is false, returns null. char *dos_unix_path(int addr, int force); +// Converts a DOS FCB file name to equivalent Unix filename +// If the file exists, returns the name of the file. +// If the file does not exists, and "force" is true, returns the possible lowercase name. +// If the file does not exists, and "force" is false, returns null. +char *dos_unix_path_fcb(int addr, int force); + // Changes current working directory int dos_change_cwd(char *path); int dos_change_dir(int addr); |