From ef46cac9c795acf9511c2337fc329f23fa2f057c Mon Sep 17 00:00:00 2001 From: Daniel Serpell Date: Mon, 11 May 2020 20:13:11 -0400 Subject: Minimal support for child processes. Implements DOS functions 50h, 51h, 55h and expands 4Ch, adds a handler for int-22 that terminates the emulator. With this the in memory debugger in Turbo Pascal 5.5 works. --- src/dos.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++---------- src/dos.h | 1 + src/loader.c | 19 ++++++++++++ src/loader.h | 1 + src/main.c | 2 ++ 5 files changed, 104 insertions(+), 15 deletions(-) diff --git a/src/dos.c b/src/dos.c index 4ac59ec..203eff0 100644 --- a/src/dos.c +++ b/src/dos.c @@ -892,7 +892,7 @@ static void int21_debug(void) ax, fn, cpuGetBX(), cpuGetCX(), cpuGetDX(), cpuGetDI(), cpuGetDS(), cpuGetES()); } -// DOS int +// DOS int 21 void int21() { // Check CP/M call, INT 21h from address 0x000C0 @@ -918,8 +918,19 @@ void int21() debug(debug_int, "D-21%04X: BX=%04X\n", cpuGetAX(), cpuGetBX()); if(debug_active(debug_dos)) int21_debug(); - unsigned ax = cpuGetAX(); - switch(ax >> 8) + + // Process interrupt + unsigned ax = cpuGetAX(), ah = ax >> 8; + + // Store SS:SP into PSP, used at return from child process + // According to DOSBOX, only set for certain functions: + if(ah != 0x50 && ah != 0x51 && ah != 0x62 && ah != 0x64 && ah < 0x6c) + { + put16(cpuGetAddress(get_current_PSP(), 0x2E), cpuGetSP()); + put16(cpuGetAddress(get_current_PSP(), 0x30), cpuGetSS()); + } + + switch(ah) { case 0: // TERMINATE PROGRAM exit(0); @@ -1070,22 +1081,17 @@ void int21() put16(4 * (ax & 0xFF), cpuGetDX()); put16(4 * (ax & 0xFF) + 2, cpuGetDS()); break; - case 0x26: // Create PSP (allocate segment (singleton), and copy PSP) + case 0x26: // Create PSP (duplicate current PSP) { - uint8_t *new_base = getptr(cpuGetAddress(cpuGetDX(), 0), 0x100); - uint8_t *orig = getptr(cpuGetAddress(get_current_PSP(), 0), 0x100); - if(!new_base || !orig) + uint8_t *new_psp = getptr(cpuGetAddress(cpuGetDX(), 0), 0x100); + uint8_t *orig_psp = getptr(cpuGetAddress(get_current_PSP(), 0), 0x100); + if(!new_psp || !orig_psp) { debug(debug_dos, "\tinvalid new PSP segment %04x.\n", cpuGetDX()); break; } // Copy PSP to the new segment, 0x80 is what DOS does - this excludes command line - memcpy(new_base, orig, 0x80); - debug(debug_dos, "\tnew PSP segment %04x.\n", cpuGetDX()); - debug(debug_dos, "\tnew PSP size %02x%02x.\n", new_base[7], new_base[6]); - debug(debug_dos, "\toriginal PSP segment %04x.\n", get_current_PSP()); - debug(debug_dos, "\toriginal PSP size %02x%02x.\n", orig[7], orig[6]); - // TODO: Initialize PSP values: int 22h, 23h, 24h and parent PSP segment to 0 + memcpy(new_psp, orig_psp, 0x80); break; } case 0x27: // BLOCK READ FROM FCB @@ -1672,7 +1678,37 @@ void int21() break; } case 0x4C: // EXIT - exit(ax & 0xFF); + // Detect if our PSP is last one + debug(debug_dos, "\texit PSP:'%04x', PARENT:%04x.\n", get_current_PSP(), + get16(cpuGetAddress(get_current_PSP(), 22))); + if(0xFFFE == get16(cpuGetAddress(get_current_PSP(), 22))) + exit(ax & 0xFF); + else + { + // Exit to parent + // TODO: we must close all child file descriptors and dealocate + // child memory. + return_code = cpuGetAX() & 0xFF; + // Patch INT 22h, 23h and 24h addresses to the ones saved in new PSP + put16(0x88, get16(cpuGetAddress(get_current_PSP(), 10))); + put16(0x8A, get16(cpuGetAddress(get_current_PSP(), 12))); + put16(0x8C, get16(cpuGetAddress(get_current_PSP(), 14))); + put16(0x8E, get16(cpuGetAddress(get_current_PSP(), 16))); + put16(0x90, get16(cpuGetAddress(get_current_PSP(), 18))); + put16(0x92, get16(cpuGetAddress(get_current_PSP(), 20))); + // Set PSP to parent + set_current_PSP(get16(cpuGetAddress(get_current_PSP(), 22))); + // Get last stack + cpuSetSS(get16(cpuGetAddress(get_current_PSP(), 0x30))); + cpuSetSP(get16(cpuGetAddress(get_current_PSP(), 0x2E))); + int stack = cpuGetAddress(cpuGetSS(), cpuGetSP()); + // Fixup interrupt return + put16(stack, get16(0x22 * 4)); + put16(stack + 2, get16(0x22 * 4 + 2)); + put16(stack + 4, 0xf202); + // And exit! + } + break; case 0x4D: // GET RETURN CODE (ERRORLEVEL) cpuSetAX(return_code); return_code = 0; @@ -1684,11 +1720,33 @@ void int21() case 0x4F: // FIND NEXT MATCHING FILE dos_find_next(0); break; + case 0x50: // SET CURRENT PSP + set_current_PSP(cpuGetBX()); + break; + case 0x51: // GET CURRENT PSP + cpuSetBX(get_current_PSP()); + break; case 0x52: // GET SYSVARS cpuSetES(dos_sysvars >> 4); cpuSetBX((dos_sysvars & 0xF) + 24); - cpuClrFlag(cpuFlag_CF); break; + case 0x55: // Create CHILD PSP + { + uint8_t *new_psp = getptr(cpuGetAddress(cpuGetDX(), 0), 0x100); + uint8_t *orig_psp = getptr(cpuGetAddress(get_current_PSP(), 0), 0x100); + if(!new_psp || !orig_psp) + { + debug(debug_dos, "\tinvalid new PSP segment %04x.\n", cpuGetDX()); + break; + } + // Copy PSP to the new segment, 0x80 is what DOS does - this excludes command line + memcpy(new_psp, orig_psp, 0x80); + // Set parent PSP to the current one + new_psp[22] = get_current_PSP() & 0xFF; + new_psp[23] = get_current_PSP() >> 8; + set_current_PSP(cpuGetDX()); + break; + } case 0x56: // RENAME { char *fname1 = dos_unix_path(cpuGetAddrDS(cpuGetDX()), 0); @@ -1806,6 +1864,14 @@ void int21() } } +// DOS int 22 - TERMINATE ADDRESS +void int22() +{ + debug(debug_dos, "D-22: TERMINATE HANDLER CALLED\n"); + // If we reached here, we must terminate now + exit(return_code & 0xFF); +} + static char *addstr(char *dst, const char *src, int limit) { while(limit > 0 && *src) diff --git a/src/dos.h b/src/dos.h index 0ead371..412f580 100644 --- a/src/dos.h +++ b/src/dos.h @@ -6,4 +6,5 @@ void init_dos(int argc, char **argv); void int20(void); void int21(void); +void int22(void); void int28(void); diff --git a/src/loader.c b/src/loader.c index 1d8225f..7c6c839 100644 --- a/src/loader.c +++ b/src/loader.c @@ -666,6 +666,20 @@ uint16_t create_PSP(const char *cmdline, const char *environment, int env_size, dosPSP[7] = 0xFE; // this jumps to 0xC0, where an dosPSP[8] = 0x1D; // INT 21h is patched. dosPSP[9] = 0xF0; + dosPSP[10] = 0x22; // Handler for INT 22h + dosPSP[11] = 0x00; + dosPSP[12] = 0x00; + dosPSP[13] = 0x00; + dosPSP[14] = 0x23; // Handler for INT 23h + dosPSP[15] = 0x00; + dosPSP[16] = 0x00; + dosPSP[17] = 0x00; + dosPSP[18] = 0x24; // Handler for INT 24h + dosPSP[19] = 0x00; + dosPSP[20] = 0x00; + dosPSP[21] = 0x00; + dosPSP[22] = 0xFE; // 16: Parent PSP, use special value of FFFE + dosPSP[23] = 0xFF; // to signal no parent DOS process dosPSP[44] = 0xFF & env_seg; // 2C: environment segment dosPSP[45] = 0xFF & (env_seg >> 8); // dosPSP[80] = 0xCD; // 50: INT 21h / RETF @@ -698,6 +712,11 @@ unsigned get_current_PSP(void) return current_PSP; } +void set_current_PSP(unsigned psp_seg) +{ + current_PSP = psp_seg; +} + static int g16(uint8_t *buf) { return buf[0] + (buf[1] << 8); diff --git a/src/loader.h b/src/loader.h index 6f5bfa1..b44f414 100644 --- a/src/loader.h +++ b/src/loader.h @@ -7,6 +7,7 @@ uint16_t create_PSP(const char *cmdline, const char *environment, int env_size, const char *progname); unsigned get_current_PSP(void); +void set_current_PSP(unsigned psp_seg); // DOS Memory handling int mem_resize_segment(int seg, int size); diff --git a/src/main.c b/src/main.c index 9bc5b09..d0c0d2f 100644 --- a/src/main.c +++ b/src/main.c @@ -84,6 +84,8 @@ void bios_routine(unsigned inum) int21(); else if(inum == 0x20) int20(); + else if(inum == 0x22) + int22(); else if(inum == 0x1A) int1A(); else if(inum == 0x19) -- cgit v1.2.3