Annotation of msdos-player/source/msdos.cpp, revision 1.1

1.1     ! root        1: /*
        !             2:        MS-DOS Player for Win32 console
        !             3: 
        !             4:        Author : Takeda.Toshiya
        !             5:        Date   : 2009.11.09-
        !             6: */
        !             7: 
        !             8: #include "msdos.h"
        !             9: 
        !            10: #ifdef _MSC_VER
        !            11: #pragma warning( disable : 4018 )
        !            12: #pragma warning( disable : 4065 )
        !            13: #pragma warning( disable : 4146 )
        !            14: #pragma warning( disable : 4244 )
        !            15: #pragma warning( disable : 4267 )
        !            16: #endif
        !            17: 
        !            18: #define my_strchr(str, chr) (char *)_mbschr((unsigned char *)(str), (unsigned int)(chr))
        !            19: #define my_strtok(tok, del) (char *)_mbstok((unsigned char *)(tok), (const unsigned char *)(del))
        !            20: #define my_strupr(str) (char *)_mbsupr((unsigned char *)(str))
        !            21: 
        !            22: #define fatalerror(...) { \
        !            23:        fprintf(stderr, __VA_ARGS__); \
        !            24:        exit(1); \
        !            25: }
        !            26: #define error(...) fprintf(stderr, "error: " __VA_ARGS__)
        !            27: 
        !            28: /* ----------------------------------------------------------------------------
        !            29:        MAME i386
        !            30: ---------------------------------------------------------------------------- */
        !            31: 
        !            32: //#define SUPPORT_DISASSEMBLER
        !            33: 
        !            34: #define CPU_MODEL i386
        !            35: //#define CPU_MODEL i486
        !            36: //#define CPU_MODEL pentium
        !            37: //#define CPU_MODEL mediagx
        !            38: //#define CPU_MODEL pentium_pro
        !            39: //#define CPU_MODEL pentium_mmx
        !            40: //#define CPU_MODEL pentium2
        !            41: //#define CPU_MODEL pentium3
        !            42: //#define CPU_MODEL pentium4
        !            43: 
        !            44: #define LSB_FIRST
        !            45: 
        !            46: #ifndef INLINE
        !            47: #define INLINE inline
        !            48: #endif
        !            49: #define U64(v) UINT64(v)
        !            50: 
        !            51: //#define logerror(...) fprintf(stderr, __VA_ARGS__)
        !            52: #define logerror(...)
        !            53: //#define popmessage(...) fprintf(stderr, __VA_ARGS__)
        !            54: #define popmessage(...)
        !            55: 
        !            56: /*****************************************************************************/
        !            57: /* src/emu/diexec.h */
        !            58: 
        !            59: // I/O line states
        !            60: enum line_state
        !            61: {
        !            62:        CLEAR_LINE = 0,                         // clear (a fired or held) line
        !            63:        ASSERT_LINE,                            // assert an interrupt immediately
        !            64:        HOLD_LINE,                              // hold interrupt line until acknowledged
        !            65:        PULSE_LINE                              // pulse interrupt line instantaneously (only for NMI, RESET)
        !            66: };
        !            67: 
        !            68: // I/O line definitions
        !            69: enum
        !            70: {
        !            71:        INPUT_LINE_IRQ = 0,
        !            72:        INPUT_LINE_NMI
        !            73: };
        !            74: 
        !            75: /*****************************************************************************/
        !            76: /* src/emu/devcpu.h */
        !            77: 
        !            78: // CPU interface functions
        !            79: #define CPU_INIT_NAME(name)                    cpu_init_##name
        !            80: #define CPU_INIT(name)                         void* CPU_INIT_NAME(name)()
        !            81: #define CPU_INIT_CALL(name)                    CPU_INIT_NAME(name)()
        !            82: 
        !            83: #define CPU_RESET_NAME(name)                   cpu_reset_##name
        !            84: #define CPU_RESET(name)                                void CPU_RESET_NAME(name)(i386_state *cpustate)
        !            85: #define CPU_RESET_CALL(name)                   CPU_RESET_NAME(name)(cpustate)
        !            86: 
        !            87: #define CPU_EXECUTE_NAME(name)                 cpu_execute_##name
        !            88: #define CPU_EXECUTE(name)                      void CPU_EXECUTE_NAME(name)(i386_state *cpustate)
        !            89: #define CPU_EXECUTE_CALL(name)                 CPU_EXECUTE_NAME(name)(cpustate)
        !            90: 
        !            91: #define CPU_DISASSEMBLE_NAME(name)             cpu_disassemble_##name
        !            92: #define CPU_DISASSEMBLE(name)                  int CPU_DISASSEMBLE_NAME(name)(char *buffer, offs_t eip, const UINT8 *oprom)
        !            93: #define CPU_DISASSEMBLE_CALL(name)             CPU_DISASSEMBLE_NAME(name)(buffer, eip, oprom)
        !            94: 
        !            95: /*****************************************************************************/
        !            96: /* src/emu/memory.h */
        !            97: 
        !            98: // offsets and addresses are 32-bit (for now...)
        !            99: typedef UINT32 offs_t;
        !           100: 
        !           101: // read accessors
        !           102: UINT8 read_byte(offs_t byteaddress)
        !           103: {
        !           104:        if(byteaddress < MAX_MEM) {
        !           105:                return mem[byteaddress];
        !           106:        } else if((byteaddress & 0xfffffff0) == 0xfffffff0) {
        !           107:                return read_byte(byteaddress & 0xfffff);
        !           108:        }
        !           109:        return 0;
        !           110: }
        !           111: 
        !           112: UINT16 read_word(offs_t byteaddress)
        !           113: {
        !           114:        if(byteaddress < MAX_MEM - 1) {
        !           115:                return *(UINT16 *)(mem + byteaddress);
        !           116:        } else if((byteaddress & 0xfffffff0) == 0xfffffff0) {
        !           117:                return read_word(byteaddress & 0xfffff);
        !           118:        }
        !           119:        return 0;
        !           120: }
        !           121: 
        !           122: UINT32 read_dword(offs_t byteaddress)
        !           123: {
        !           124:        if(byteaddress < MAX_MEM - 3) {
        !           125:                return *(UINT32 *)(mem + byteaddress);
        !           126:        } else if((byteaddress & 0xfffffff0) == 0xfffffff0) {
        !           127:                return read_dword(byteaddress & 0xfffff);
        !           128:        }
        !           129:        return 0;
        !           130: }
        !           131: 
        !           132: // write accessors
        !           133: void write_byte(offs_t byteaddress, UINT8 data)
        !           134: {
        !           135:        if(byteaddress < MAX_MEM) {
        !           136:                if(byteaddress >= tvram_base_address && byteaddress < tvram_base_address + 4000) {
        !           137:                        if(int_10h_feh_called && !int_10h_ffh_called && mem[byteaddress] != data) {
        !           138:                                COORD co;
        !           139:                                DWORD num;
        !           140:                                
        !           141:                                co.X = ((byteaddress - tvram_base_address) >> 1) % 80;
        !           142:                                co.Y = ((byteaddress - tvram_base_address) >> 1) / 80;
        !           143:                                
        !           144:                                if(byteaddress & 1) {
        !           145:                                        scr_attr[0] = data;
        !           146:                                        WriteConsoleOutputAttribute(hStdout, scr_attr, 1, co, &num);
        !           147:                                } else {
        !           148:                                        scr_char[0] = data;
        !           149:                                        WriteConsoleOutputCharacter(hStdout, scr_char, 1, co, &num);
        !           150:                                }
        !           151:                        }
        !           152:                }
        !           153:                mem[byteaddress] = data;
        !           154:        }
        !           155: }
        !           156: 
        !           157: void write_word(offs_t byteaddress, UINT16 data)
        !           158: {
        !           159:        if(byteaddress < MAX_MEM - 1) {
        !           160:                if(byteaddress >= tvram_base_address && byteaddress < tvram_base_address + 4000) {
        !           161:                        if(int_10h_feh_called && !int_10h_ffh_called && *(UINT16 *)(mem + byteaddress) != data) {
        !           162:                                if(byteaddress & 1) {
        !           163:                                        write_byte(byteaddress    , data     );
        !           164:                                        write_byte(byteaddress + 1, data >> 8);
        !           165:                                } else {
        !           166:                                        COORD co;
        !           167:                                        DWORD num;
        !           168:                                        
        !           169:                                        co.X = ((byteaddress - tvram_base_address) >> 1) % 80;
        !           170:                                        co.Y = ((byteaddress - tvram_base_address) >> 1) / 80;
        !           171:                                        
        !           172:                                        scr_char[0] = data;
        !           173:                                        scr_attr[0] = data >> 8;
        !           174:                                        
        !           175:                                        WriteConsoleOutputCharacter(hStdout, scr_char, 1, co, &num);
        !           176:                                        WriteConsoleOutputAttribute(hStdout, scr_attr, 1, co, &num);
        !           177:                                }
        !           178:                                return;
        !           179:                        }
        !           180:                }
        !           181:                *(UINT16 *)(mem + byteaddress) = data;
        !           182:        }
        !           183: }
        !           184: 
        !           185: void write_dword(offs_t byteaddress, UINT32 data)
        !           186: {
        !           187:        if(byteaddress < MAX_MEM - 3) {
        !           188:                if(byteaddress >= tvram_base_address && byteaddress < tvram_base_address + 4000) {
        !           189:                        if(int_10h_feh_called && !int_10h_ffh_called && *(UINT32 *)(mem + byteaddress) != data) {
        !           190:                                if(byteaddress & 1) {
        !           191:                                        write_byte(byteaddress    , data      );
        !           192:                                        write_byte(byteaddress + 1, data >>  8);
        !           193:                                        write_byte(byteaddress + 2, data >> 16);
        !           194:                                        write_byte(byteaddress + 3, data >> 24);
        !           195:                                } else {
        !           196:                                        COORD co;
        !           197:                                        DWORD num;
        !           198:                                        
        !           199:                                        co.X = ((byteaddress - tvram_base_address) >> 1) % 80;
        !           200:                                        co.Y = ((byteaddress - tvram_base_address) >> 1) / 80;
        !           201:                                        
        !           202:                                        scr_char[0] = data;
        !           203:                                        scr_attr[0] = data >> 8;
        !           204:                                        scr_char[1] = data >> 16;
        !           205:                                        scr_attr[1] = data >> 24;
        !           206:                                        
        !           207:                                        WriteConsoleOutputCharacter(hStdout, scr_char, 2, co, &num);
        !           208:                                        WriteConsoleOutputAttribute(hStdout, scr_attr, 2, co, &num);
        !           209:                                }
        !           210:                                return;
        !           211:                        }
        !           212:                }
        !           213:                *(UINT32 *)(mem + byteaddress) = data;
        !           214:        }
        !           215: }
        !           216: 
        !           217: // accessor methods for reading decrypted data
        !           218: #define read_decrypted_byte read_byte
        !           219: #define read_decrypted_word read_word
        !           220: #define read_decrypted_dword read_dword
        !           221: 
        !           222: UINT8 read_io_byte(offs_t byteaddress);
        !           223: UINT16 read_io_word(offs_t byteaddress);
        !           224: UINT32 read_io_dword(offs_t byteaddress);
        !           225: 
        !           226: void write_io_byte(offs_t byteaddress, UINT8 data);
        !           227: void write_io_word(offs_t byteaddress, UINT16 data);
        !           228: void write_io_dword(offs_t byteaddress, UINT32 data);
        !           229: 
        !           230: /*****************************************************************************/
        !           231: /* src/emu/emucore.h */
        !           232: 
        !           233: // constants for expression endianness
        !           234: enum endianness_t
        !           235: {
        !           236:        ENDIANNESS_LITTLE,
        !           237:        ENDIANNESS_BIG
        !           238: };
        !           239: 
        !           240: // declare native endianness to be one or the other
        !           241: #ifdef LSB_FIRST
        !           242: const endianness_t ENDIANNESS_NATIVE = ENDIANNESS_LITTLE;
        !           243: #else
        !           244: const endianness_t ENDIANNESS_NATIVE = ENDIANNESS_BIG;
        !           245: #endif
        !           246: 
        !           247: // endian-based value: first value is if 'endian' is little-endian, second is if 'endian' is big-endian
        !           248: #define ENDIAN_VALUE_LE_BE(endian,leval,beval) (((endian) == ENDIANNESS_LITTLE) ? (leval) : (beval))
        !           249: 
        !           250: // endian-based value: first value is if native endianness is little-endian, second is if native is big-endian
        !           251: #define NATIVE_ENDIAN_VALUE_LE_BE(leval,beval) ENDIAN_VALUE_LE_BE(ENDIANNESS_NATIVE, leval, beval)
        !           252: 
        !           253: // endian-based value: first value is if 'endian' matches native, second is if 'endian' doesn't match native
        !           254: #define ENDIAN_VALUE_NE_NNE(endian,leval,beval)        (((endian) == ENDIANNESS_NATIVE) ? (neval) : (nneval))
        !           255: 
        !           256: /*****************************************************************************/
        !           257: /* src/emu/didisasm.h */
        !           258: 
        !           259: // Disassembler constants
        !           260: const UINT32 DASMFLAG_SUPPORTED     = 0x80000000;   // are disassembly flags supported?
        !           261: const UINT32 DASMFLAG_STEP_OUT      = 0x40000000;   // this instruction should be the end of a step out sequence
        !           262: const UINT32 DASMFLAG_STEP_OVER     = 0x20000000;   // this instruction should be stepped over by setting a breakpoint afterwards
        !           263: const UINT32 DASMFLAG_OVERINSTMASK  = 0x18000000;   // number of extra instructions to skip when stepping over
        !           264: const UINT32 DASMFLAG_OVERINSTSHIFT = 27;           // bits to shift after masking to get the value
        !           265: const UINT32 DASMFLAG_LENGTHMASK    = 0x0000ffff;   // the low 16-bits contain the actual length
        !           266: 
        !           267: /*****************************************************************************/
        !           268: /* src/osd/osdcomm.h */
        !           269: 
        !           270: /* Highly useful macro for compile-time knowledge of an array size */
        !           271: #define ARRAY_LENGTH(x)     (sizeof(x) / sizeof(x[0]))
        !           272: 
        !           273: #include "softfloat/softfloat.c"
        !           274: #include "i386/i386.c"
        !           275: #ifdef SUPPORT_DISASSEMBLER
        !           276: #include "i386/i386dasm.c"
        !           277: bool dasm = false;
        !           278: #endif
        !           279: 
        !           280: i386_state *cpustate;
        !           281: int cpu_type, cpu_step;
        !           282: 
        !           283: void i386_jmp_far(UINT16 selector, UINT32 address)
        !           284: {
        !           285:        if(PROTECTED_MODE && !V8086_MODE) {
        !           286:                i386_protected_mode_jump(cpustate, selector, address, 1, cpustate->operand_size);
        !           287:        } else {
        !           288:                cpustate->sreg[CS].selector = selector;
        !           289:                cpustate->performed_intersegment_jump = 1;
        !           290:                i386_load_segment_descriptor(cpustate, CS);
        !           291:                cpustate->eip = address;
        !           292:                CHANGE_PC(cpustate, cpustate->eip);
        !           293:        }
        !           294: }
        !           295: 
        !           296: /* ----------------------------------------------------------------------------
        !           297:        main
        !           298: ---------------------------------------------------------------------------- */
        !           299: 
        !           300: int main(int argc, char *argv[], char *envp[])
        !           301: {
        !           302:        int standard_env = (argc > 1 && _stricmp(argv[1], "-e") == 0);
        !           303:        
        !           304:        if(argc < 2 + standard_env) {
        !           305: #ifdef _WIN64
        !           306:                fprintf(stderr, "MS-DOS Player for Win32-x64 console\n\n");
        !           307: #else
        !           308:                fprintf(stderr, "MS-DOS Player for Win32 console\n\n");
        !           309: #endif
        !           310:                fprintf(stderr, "Usage: MSDOS [-e] (command file) [opions]\n");
        !           311:                return(EXIT_FAILURE);
        !           312:        }
        !           313:        
        !           314:        CONSOLE_SCREEN_BUFFER_INFO csbi;
        !           315:        hStdin = GetStdHandle(STD_INPUT_HANDLE);
        !           316:        hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
        !           317:        GetConsoleScreenBufferInfo(hStdout, &csbi);
        !           318:        
        !           319:        for(int y = 0; y < SCR_BUF_SIZE; y++) {
        !           320:                for(int x = 0; x < 80; x++) {
        !           321:                        scr_buf[y][x].Char.AsciiChar = ' ';
        !           322:                        scr_buf[y][x].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
        !           323:                }
        !           324:        }
        !           325:        scr_buf_size.X = 80;
        !           326:        scr_buf_size.Y = SCR_BUF_SIZE;
        !           327:        scr_buf_pos.X = scr_buf_pos.Y = 0;
        !           328:        scr_width = csbi.dwSize.X;
        !           329:        scr_height = csbi.dwSize.Y;
        !           330:        cursor_moved = false;
        !           331:        
        !           332:        key_buf_char = new FIFO();
        !           333:        key_buf_scan = new FIFO();
        !           334:        
        !           335:        hardware_init();
        !           336:        
        !           337:        if(msdos_init(argc - (standard_env + 1), argv + (standard_env + 1), envp, standard_env)) {
        !           338:                retval = EXIT_FAILURE;
        !           339:        } else {
        !           340:                timeBeginPeriod(1);
        !           341:                hardware_run();
        !           342:                msdos_finish();
        !           343:                timeEndPeriod(1);
        !           344:        }
        !           345:        hardware_finish();
        !           346:        
        !           347:        delete key_buf_char;
        !           348:        delete key_buf_scan;
        !           349:        
        !           350:        SetConsoleTextAttribute(hStdout, csbi.wAttributes);
        !           351:        
        !           352:        return(retval);
        !           353: }
        !           354: 
        !           355: /* ----------------------------------------------------------------------------
        !           356:        MS-DOS virtual machine
        !           357: ---------------------------------------------------------------------------- */
        !           358: 
        !           359: void update_key_buffer()
        !           360: {
        !           361:        DWORD dwRead;
        !           362:        INPUT_RECORD ir[16];
        !           363:        
        !           364:        if(ReadConsoleInputA(hStdin, ir, 16, &dwRead)) {
        !           365:                for(int i = 0; i < dwRead; i++) {
        !           366:                        if((ir[i].EventType & KEY_EVENT) && ir[i].Event.KeyEvent.bKeyDown) {
        !           367:                                if(ir[i].Event.KeyEvent.uChar.AsciiChar == 0) {
        !           368:                                        // ignore shift, ctrl and alt keys
        !           369:                                        if(ir[i].Event.KeyEvent.wVirtualScanCode != 0x1d &&
        !           370:                                           ir[i].Event.KeyEvent.wVirtualScanCode != 0x2a &&
        !           371:                                           ir[i].Event.KeyEvent.wVirtualScanCode != 0x36 &&
        !           372:                                           ir[i].Event.KeyEvent.wVirtualScanCode != 0x38) {
        !           373:                                                key_buf_char->write(0x00);
        !           374:                                                key_buf_scan->write(ir[i].Event.KeyEvent.dwControlKeyState & ENHANCED_KEY ? 0xe0 : 0x00);
        !           375:                                                key_buf_char->write(0x00);
        !           376:                                                key_buf_scan->write(ir[i].Event.KeyEvent.wVirtualScanCode & 0xff);
        !           377:                                        }
        !           378:                                } else {
        !           379:                                        key_buf_char->write(ir[i].Event.KeyEvent.uChar.AsciiChar & 0xff);
        !           380:                                        key_buf_scan->write(ir[i].Event.KeyEvent.wVirtualScanCode & 0xff);
        !           381:                                }
        !           382:                        }
        !           383:                }
        !           384:        }
        !           385:        if(key_buf_char->count() == 0) {
        !           386:                Sleep(10);
        !           387:        }
        !           388: }
        !           389: 
        !           390: // process info
        !           391: 
        !           392: process_t *msdos_process_info_create(UINT16 psp_seg)
        !           393: {
        !           394:        for(int i = 0; i < MAX_PROCESS; i++) {
        !           395:                if(process[i].psp == 0 || process[i].psp == psp_seg) {
        !           396:                        memset(&process[i], 0, sizeof(process_t));
        !           397:                        process[i].psp = psp_seg;
        !           398:                        return(&process[i]);
        !           399:                }
        !           400:        }
        !           401:        fatalerror("too many processes\n");
        !           402:        return(NULL);
        !           403: }
        !           404: 
        !           405: process_t *msdos_process_info_get(UINT16 psp_seg)
        !           406: {
        !           407:        for(int i = 0; i < MAX_PROCESS; i++) {
        !           408:                if(process[i].psp == psp_seg) {
        !           409:                        return(&process[i]);
        !           410:                }
        !           411:        }
        !           412:        fatalerror("invalid psp address\n");
        !           413:        return(NULL);
        !           414: }
        !           415: 
        !           416: void msdos_cds_update(int drv)
        !           417: {
        !           418:        cds_t *cds = (cds_t *)(mem + CDS_TOP);
        !           419:        
        !           420:        memset(mem + CDS_TOP, 0, CDS_SIZE);
        !           421:        sprintf(cds->path_name, "%c:\\", 'A' + drv);
        !           422:        cds->drive_attrib = 0x4000;     // physical drive
        !           423:        cds->physical_drive_number = drv;
        !           424: }
        !           425: 
        !           426: // dbcs
        !           427: 
        !           428: void msdos_dbcs_table_update()
        !           429: {
        !           430:        UINT8 dbcs_data[DBCS_SIZE];
        !           431:        memset(dbcs_data, 0, sizeof(dbcs_data));
        !           432:        
        !           433:        CPINFO info;
        !           434:        GetCPInfo(active_code_page, &info);
        !           435:        
        !           436:        if(info.MaxCharSize != 1) {
        !           437:                for(int i = 0;; i += 2) {
        !           438:                        UINT8 lo = info.LeadByte[i + 0];
        !           439:                        UINT8 hi = info.LeadByte[i + 1];
        !           440:                        dbcs_data[2 + i + 0] = lo;
        !           441:                        dbcs_data[2 + i + 1] = hi;
        !           442:                        if(lo == 0 && hi == 0) {
        !           443:                                dbcs_data[0] = i + 2;
        !           444:                                break;
        !           445:                        }
        !           446:                }
        !           447:        } else {
        !           448:                dbcs_data[0] = 2;       // ???
        !           449:        }
        !           450:        memcpy(mem + DBCS_TOP, dbcs_data, sizeof(dbcs_data));
        !           451: }
        !           452: 
        !           453: void msdos_dbcs_table_init()
        !           454: {
        !           455:        system_code_page = active_code_page = _getmbcp();
        !           456:        msdos_dbcs_table_update();
        !           457: }
        !           458: 
        !           459: void msdos_dbcs_table_finish()
        !           460: {
        !           461:        if(active_code_page != system_code_page) {
        !           462:                _setmbcp(system_code_page);
        !           463:        }
        !           464: }
        !           465: 
        !           466: int msdos_lead_byte_check(UINT8 code)
        !           467: {
        !           468:        UINT8 *dbcs_table = mem + DBCS_TABLE;
        !           469:        
        !           470:        for(int i = 0;; i += 2) {
        !           471:                UINT8 lo = dbcs_table[i + 0];
        !           472:                UINT8 hi = dbcs_table[i + 1];
        !           473:                if(lo == 0 && hi == 0) {
        !           474:                        break;
        !           475:                }
        !           476:                if(lo <= code && code <= hi) {
        !           477:                        return(1);
        !           478:                }
        !           479:        }
        !           480:        return(0);
        !           481: }
        !           482: 
        !           483: // file control
        !           484: 
        !           485: char *msdos_trimmed_path(char *path, int lfn)
        !           486: {
        !           487:        static char tmp[MAX_PATH];
        !           488:        
        !           489:        if(lfn) {
        !           490:                strcpy(tmp, path);
        !           491:        } else {
        !           492:                // remove space in the path
        !           493:                char *src = path, *dst = tmp;
        !           494:                
        !           495:                while(*src != '\0') {
        !           496:                        if(msdos_lead_byte_check(*src)) {
        !           497:                                *dst++ = *src++;
        !           498:                                *dst++ = *src++;
        !           499:                        } else if(*src != ' ') {
        !           500:                                *dst++ = *src++;
        !           501:                        } else {
        !           502:                                src++;  // skip space
        !           503:                        }
        !           504:                }
        !           505:                *dst = '\0';
        !           506:        }
        !           507:        return(tmp);
        !           508: }
        !           509: 
        !           510: bool match(char *text, char *pattern)
        !           511: {
        !           512:        //http://www.prefield.com/algorithm/string/wildcard.html
        !           513:        switch (*pattern) {
        !           514:        case '\0':
        !           515:                return !*text;
        !           516:        case '*':
        !           517:                return match(text, pattern + 1) || *text && match(text + 1, pattern);
        !           518:        case '?':
        !           519:                return *text && match(text + 1, pattern + 1);
        !           520:        default:
        !           521:                return (*text == *pattern) && match(text + 1, pattern + 1);
        !           522:        }
        !           523: }
        !           524: 
        !           525: bool msdos_match_volume_label(char *path, char *volume)
        !           526: {
        !           527:        char *p;
        !           528:        
        !           529:        if((p = my_strchr(path, ':')) != NULL) {
        !           530:                return msdos_match_volume_label(p + 1, volume);
        !           531:        } else if((p = my_strchr(path, '\\')) != NULL) {
        !           532:                return msdos_match_volume_label(p + 1, volume);
        !           533:        } else if((p = my_strchr(path, '.')) != NULL) {
        !           534:                *p = '\0';
        !           535:                bool result = match(volume, path);
        !           536:                *p = '.';
        !           537:                return result;
        !           538:        } else {
        !           539:                return match(volume, path);
        !           540:        }
        !           541: }
        !           542: 
        !           543: char *msdos_fcb_path(fcb_t *fcb)
        !           544: {
        !           545:        static char tmp[MAX_PATH];
        !           546:        char name[9], ext[4];
        !           547:        
        !           548:        memset(name, 0, sizeof(name));
        !           549:        memcpy(name, fcb->file_name, 8);
        !           550:        strcpy(name, msdos_trimmed_path(name, 0));
        !           551:        
        !           552:        memset(ext, 0, sizeof(ext));
        !           553:        memcpy(ext, fcb->file_name + 8, 3);
        !           554:        strcpy(ext, msdos_trimmed_path(ext, 0));
        !           555:        
        !           556:        if(name[0] == '\0' || strcmp(name, "????????") == 0) {
        !           557:                strcpy(name, "*");
        !           558:        }
        !           559:        if(ext[0] == '\0') {
        !           560:                strcpy(tmp, name);
        !           561:        } else {
        !           562:                if(strcmp(ext, "???") == 0) {
        !           563:                        strcpy(ext, "*");
        !           564:                }
        !           565:                sprintf(tmp, "%s.%s", name, ext);
        !           566:        }
        !           567:        return(tmp);
        !           568: }
        !           569: 
        !           570: void msdos_set_fcb_path(fcb_t *fcb, char *path)
        !           571: {
        !           572:        char *ext = my_strchr(path, '.');
        !           573:        
        !           574:        memset(fcb->file_name, 0x20, 8 + 3);
        !           575:        if(ext != NULL && path[0] != '.') {
        !           576:                *ext = '\0';
        !           577:                memcpy(fcb->file_name + 8, ext + 1, strlen(ext + 1));
        !           578:        }
        !           579:        memcpy(fcb->file_name, path, strlen(path));
        !           580: }
        !           581: 
        !           582: char *msdos_short_path(char *path)
        !           583: {
        !           584:        static char tmp[MAX_PATH];
        !           585:        
        !           586:        GetShortPathName(path, tmp, MAX_PATH);
        !           587:        my_strupr(tmp);
        !           588:        return(tmp);
        !           589: }
        !           590: 
        !           591: char *msdos_short_full_path(char *path)
        !           592: {
        !           593:        static char tmp[MAX_PATH];
        !           594:        char full[MAX_PATH], *name;
        !           595:        
        !           596:        GetFullPathName(path, MAX_PATH, full, &name);
        !           597:        GetShortPathName(full, tmp, MAX_PATH);
        !           598:        my_strupr(tmp);
        !           599:        return(tmp);
        !           600: }
        !           601: 
        !           602: char *msdos_short_full_dir(char *path)
        !           603: {
        !           604:        static char tmp[MAX_PATH];
        !           605:        char full[MAX_PATH], *name;
        !           606:        
        !           607:        GetFullPathName(path, MAX_PATH, full, &name);
        !           608:        name[-1] = '\0';
        !           609:        GetShortPathName(full, tmp, MAX_PATH);
        !           610:        my_strupr(tmp);
        !           611:        return(tmp);
        !           612: }
        !           613: 
        !           614: char *msdos_local_file_path(char *path, int lfn)
        !           615: {
        !           616:        char *trimmed = msdos_trimmed_path(path, lfn);
        !           617:        
        !           618:        if(_access(trimmed, 0) != 0) {
        !           619:                process_t *process = msdos_process_info_get(current_psp);
        !           620:                static char tmp[MAX_PATH];
        !           621:                
        !           622:                sprintf(tmp, "%s\\%s", process->module_dir, trimmed);
        !           623:                if(_access(tmp, 0) == 0) {
        !           624:                        return(tmp);
        !           625:                }
        !           626:        }
        !           627:        return(trimmed);
        !           628: }
        !           629: 
        !           630: int msdos_drive_number(char *path)
        !           631: {
        !           632:        char tmp[MAX_PATH], *name;
        !           633:        
        !           634:        GetFullPathName(path, MAX_PATH, tmp, &name);
        !           635:        if(tmp[0] >= 'a' && tmp[0] <= 'z') {
        !           636:                return(tmp[0] - 'a');
        !           637:        } else {
        !           638:                return(tmp[0] - 'A');
        !           639:        }
        !           640: }
        !           641: 
        !           642: char *msdos_volume_label(char *path)
        !           643: {
        !           644:        static char tmp[MAX_PATH];
        !           645:        char volume[] = "A:\\";
        !           646:        
        !           647:        if(path[1] == ':') {
        !           648:                volume[0] = path[0];
        !           649:        } else {
        !           650:                volume[0] = 'A' + _getdrive() - 1;
        !           651:        }
        !           652:        if(!GetVolumeInformation(volume, tmp, MAX_PATH, NULL, NULL, NULL, NULL, 0)) {
        !           653:                memset(tmp, 0, sizeof(tmp));
        !           654:        }
        !           655:        return(tmp);
        !           656: }
        !           657: 
        !           658: char *msdos_short_volume_label(char *label)
        !           659: {
        !           660:        static char tmp[(8 + 1 + 3) + 1];
        !           661:        char *src = label;
        !           662:        int remain = strlen(label);
        !           663:        char *dst_n = tmp;
        !           664:        char *dst_e = tmp + 9;
        !           665:        
        !           666:        strcpy(tmp, "        .   ");
        !           667:        for(int i = 0; i < 8 && remain > 0; i++) {
        !           668:                if(msdos_lead_byte_check(*src)) {
        !           669:                        if(++i == 8) {
        !           670:                                break;
        !           671:                        }
        !           672:                        *dst_n++ = *src++;
        !           673:                        remain--;
        !           674:                }
        !           675:                *dst_n++ = *src++;
        !           676:                remain--;
        !           677:        }
        !           678:        if(remain > 0) {
        !           679:                for(int i = 0; i < 3 && remain > 0; i++) {
        !           680:                        if(msdos_lead_byte_check(*src)) {
        !           681:                                if(++i == 3) {
        !           682:                                        break;
        !           683:                                }
        !           684:                                *dst_e++ = *src++;
        !           685:                                remain--;
        !           686:                        }
        !           687:                        *dst_e++ = *src++;
        !           688:                        remain--;
        !           689:                }
        !           690:                *dst_e = '\0';
        !           691:        } else {
        !           692:                *dst_n = '\0';
        !           693:        }
        !           694:        my_strupr(tmp);
        !           695:        return(tmp);
        !           696: }
        !           697: 
        !           698: void msdos_file_handler_open(int fd, char *path, int atty, int mode, UINT16 info, UINT16 psp_seg)
        !           699: {
        !           700:        static int id = 0;
        !           701:        char full[MAX_PATH], *name;
        !           702:        
        !           703:        if(psp_seg && fd < 20) {
        !           704:                psp_t *psp = (psp_t *)(mem + (psp_seg << 4));
        !           705:                psp->file_table[fd] = fd;
        !           706:        }
        !           707:        if(GetFullPathName(path, MAX_PATH, full, &name) != 0) {
        !           708:                strcpy(file_handler[fd].path, full);
        !           709:        } else {
        !           710:                strcpy(file_handler[fd].path, path);
        !           711:        }
        !           712:        file_handler[fd].valid = 1;
        !           713:        file_handler[fd].id = id++;     // dummy id for int 21h ax=71a6h
        !           714:        file_handler[fd].atty = atty;
        !           715:        file_handler[fd].mode = mode;
        !           716:        file_handler[fd].info = info;
        !           717:        file_handler[fd].psp = psp_seg;
        !           718: }
        !           719: 
        !           720: void msdos_file_handler_dup(int dst, int src, UINT16 psp_seg)
        !           721: {
        !           722:        if(psp_seg && dst < 20) {
        !           723:                psp_t *psp = (psp_t *)(mem + (psp_seg << 4));
        !           724:                psp->file_table[dst] = dst;
        !           725:        }
        !           726:        strcpy(file_handler[dst].path, file_handler[src].path);
        !           727:        file_handler[dst].valid = 1;
        !           728:        file_handler[dst].id = file_handler[src].id;
        !           729:        file_handler[dst].atty = file_handler[src].atty;
        !           730:        file_handler[dst].mode = file_handler[src].mode;
        !           731:        file_handler[dst].info = file_handler[src].info;
        !           732:        file_handler[dst].psp = psp_seg;
        !           733: }
        !           734: 
        !           735: void msdos_file_handler_close(int fd, UINT16 psp_seg)
        !           736: {
        !           737:        if(psp_seg && fd < 20) {
        !           738:                psp_t *psp = (psp_t *)(mem + (psp_seg << 4));
        !           739:                psp->file_table[fd] = 0xff;
        !           740:        }
        !           741:        file_handler[fd].valid = 0;
        !           742: }
        !           743: 
        !           744: int msdos_file_attribute_create(UINT16 new_attr)
        !           745: {
        !           746:        int attr = 0;
        !           747:        
        !           748:        if(REG16(CX) & 0x01) {
        !           749:                attr |= FILE_ATTRIBUTE_READONLY;
        !           750:        }
        !           751:        if(REG16(CX) & 0x02) {
        !           752:                attr |= FILE_ATTRIBUTE_HIDDEN;
        !           753:        }
        !           754:        if(REG16(CX) & 0x04) {
        !           755:                attr |= FILE_ATTRIBUTE_SYSTEM;
        !           756:        }
        !           757:        if(REG16(CX) & 0x20) {
        !           758:                attr |= FILE_ATTRIBUTE_ARCHIVE;
        !           759:        }
        !           760:        return(attr);
        !           761: }
        !           762: 
        !           763: // find file
        !           764: 
        !           765: int msdos_find_file_check_attribute(int attribute, int allowed_mask, int required_mask)
        !           766: {
        !           767:        if((allowed_mask & 0x08) && !(attribute & FILE_ATTRIBUTE_DIRECTORY)) {
        !           768:                return(0);      // search directory only !!!
        !           769:        } else if(!(allowed_mask & 0x02) && (attribute & FILE_ATTRIBUTE_HIDDEN)) {
        !           770:                return(0);
        !           771:        } else if(!(allowed_mask & 0x04) && (attribute & FILE_ATTRIBUTE_SYSTEM)) {
        !           772:                return(0);
        !           773:        } else if(!(allowed_mask & 0x10) && (attribute & FILE_ATTRIBUTE_DIRECTORY)) {
        !           774:                return(0);
        !           775:        } else if((attribute & required_mask) != required_mask) {
        !           776:                return(0);
        !           777:        } else {
        !           778:                return(1);
        !           779:        }
        !           780: }
        !           781: 
        !           782: void msdos_find_file_conv_local_time(WIN32_FIND_DATA *fd)
        !           783: {
        !           784:        FILETIME local;
        !           785:        
        !           786:        FileTimeToLocalFileTime(&fd->ftCreationTime, &local);
        !           787:        fd->ftCreationTime.dwLowDateTime = local.dwLowDateTime;
        !           788:        fd->ftCreationTime.dwHighDateTime = local.dwHighDateTime;
        !           789:        
        !           790:        FileTimeToLocalFileTime(&fd->ftLastAccessTime, &local);
        !           791:        fd->ftLastAccessTime.dwLowDateTime = local.dwLowDateTime;
        !           792:        fd->ftLastAccessTime.dwHighDateTime = local.dwHighDateTime;
        !           793:        
        !           794:        FileTimeToLocalFileTime(&fd->ftLastWriteTime, &local);
        !           795:        fd->ftLastWriteTime.dwLowDateTime = local.dwLowDateTime;
        !           796:        fd->ftLastWriteTime.dwHighDateTime = local.dwHighDateTime;
        !           797: }
        !           798: 
        !           799: // i/o
        !           800: 
        !           801: void msdos_putch(UINT8 data);
        !           802: 
        !           803: void msdos_stdio_reopen()
        !           804: {
        !           805:        if(!file_handler[0].valid) {
        !           806:                _dup2(DUP_STDIN, 0);
        !           807:                msdos_file_handler_open(0, "STDIN", _isatty(0), 0, 0x80d3, 0);
        !           808:        }
        !           809:        if(!file_handler[1].valid) {
        !           810:                _dup2(DUP_STDOUT, 1);
        !           811:                msdos_file_handler_open(1, "STDOUT", _isatty(1), 1, 0x80d3, 0);
        !           812:        }
        !           813:        if(!file_handler[2].valid) {
        !           814:                _dup2(DUP_STDERR, 2);
        !           815:                msdos_file_handler_open(2, "STDERR", _isatty(2), 1, 0x80d3, 0);
        !           816:        }
        !           817: }
        !           818: 
        !           819: int msdos_kbhit()
        !           820: {
        !           821:        msdos_stdio_reopen();
        !           822:        
        !           823:        if(!file_handler[0].atty) {
        !           824:                // stdin is redirected to file
        !           825:                return(eof(0) == 0);
        !           826:        }
        !           827:        
        !           828:        // check keyboard status
        !           829:        if(key_buf_char->count() != 0) {
        !           830:                return(1);
        !           831:        } else {
        !           832:                return(_kbhit());
        !           833:        }
        !           834: }
        !           835: 
        !           836: int msdos_getch_ex(int echo)
        !           837: {
        !           838:        static char prev = 0;
        !           839:        
        !           840:        msdos_stdio_reopen();
        !           841:        
        !           842:        if(!file_handler[0].atty) {
        !           843:                // stdin is redirected to file
        !           844: retry:
        !           845:                char data;
        !           846:                if(_read(0, &data, 1) == 1) {
        !           847:                        char tmp = data;
        !           848:                        if(data == 0x0a) {
        !           849:                                if(prev == 0x0d) {
        !           850:                                        goto retry; // CRLF -> skip LF
        !           851:                                } else {
        !           852:                                        data = 0x0d; // LF only -> CR
        !           853:                                }
        !           854:                        }
        !           855:                        prev = tmp;
        !           856:                        return(data);
        !           857:                }
        !           858:                return(EOF);
        !           859:        }
        !           860:        
        !           861:        // input from console
        !           862:        while(key_buf_char->count() == 0) {
        !           863:                update_key_buffer();
        !           864:        }
        !           865:        int key_char = key_buf_char->read();
        !           866:        int key_scan = key_buf_scan->read();
        !           867:        
        !           868:        if(echo && key_char) {
        !           869:                msdos_putch(key_char);
        !           870:        }
        !           871:        return key_char ? key_char : (key_scan != 0xe0) ? key_scan : 0;
        !           872: }
        !           873: 
        !           874: inline int msdos_getch()
        !           875: {
        !           876:        return(msdos_getch_ex(0));
        !           877: }
        !           878: 
        !           879: inline int msdos_getche()
        !           880: {
        !           881:        return(msdos_getch_ex(1));
        !           882: }
        !           883: 
        !           884: int msdos_write(int fd, const void *buffer, unsigned int count)
        !           885: {
        !           886:        static int is_cr = 0;
        !           887:        
        !           888:        if(fd == 1 && !file_handler[1].atty) {
        !           889:                // CR+LF -> LF
        !           890:                UINT8 *buf = (UINT8 *)buffer;
        !           891:                for(unsigned int i = 0; i < count; i++) {
        !           892:                        UINT8 data = buf[i];
        !           893:                        if(is_cr) {
        !           894:                                if(data != 0x0a) {
        !           895:                                        UINT8 tmp = 0x0d;
        !           896:                                        _write(1, &tmp, 1);
        !           897:                                }
        !           898:                                _write(1, &data, 1);
        !           899:                                is_cr = 0;
        !           900:                        } else if(data == 0x0d) {
        !           901:                                is_cr = 1;
        !           902:                        } else {
        !           903:                                _write(1, &data, 1);
        !           904:                        }
        !           905:                }
        !           906:                return(count);
        !           907:        }
        !           908:        return(_write(fd, buffer, count));
        !           909: }
        !           910: 
        !           911: void msdos_putch(UINT8 data)
        !           912: {
        !           913:        static int p = 0;
        !           914:        static int is_kanji = 0;
        !           915:        static int is_esc = 0;
        !           916:        static int stored_x;
        !           917:        static int stored_y;
        !           918:        static WORD stored_a;
        !           919:        static char tmp[64];
        !           920:        
        !           921:        msdos_stdio_reopen();
        !           922:        
        !           923:        if(!file_handler[1].atty) {
        !           924:                // stdout is redirected to file
        !           925:                msdos_write(1, &data, 1);
        !           926:                return;
        !           927:        }
        !           928:        
        !           929:        // output to console
        !           930:        tmp[p++] = data;
        !           931:        
        !           932:        if(is_kanji) {
        !           933:                // kanji character
        !           934:                is_kanji = 0;
        !           935:        } else if(is_esc) {
        !           936:                // escape sequense
        !           937:                if((tmp[1] == ')' || tmp[1] == '(') && p == 3) {
        !           938:                        p = is_esc = 0;
        !           939:                } else if(tmp[1] == '=' && p == 4) {
        !           940:                        COORD co;
        !           941:                        co.X = tmp[3] - 0x20;
        !           942:                        co.Y = tmp[2] - 0x20;
        !           943:                        SetConsoleCursorPosition(hStdout, co);
        !           944:                        mem[0x450 + mem[0x462] * 2] = co.X;
        !           945:                        mem[0x451 + mem[0x462] * 2] = co.Y;
        !           946:                        cursor_moved = false;
        !           947:                        p = is_esc = 0;
        !           948:                } else if((data >= 'a' && data <= 'z') || (data >= 'A' && data <= 'Z') || data == '*') {
        !           949:                        CONSOLE_SCREEN_BUFFER_INFO csbi;
        !           950:                        COORD co;
        !           951:                        GetConsoleScreenBufferInfo(hStdout, &csbi);
        !           952:                        co.X = csbi.dwCursorPosition.X;
        !           953:                        co.Y = csbi.dwCursorPosition.Y;
        !           954:                        WORD wAttributes = csbi.wAttributes;
        !           955:                        
        !           956:                        if(tmp[1] == 'D') {
        !           957:                                co.Y++;
        !           958:                        } else if(tmp[1] == 'E') {
        !           959:                                co.X = 0;
        !           960:                                co.Y++;
        !           961:                        } else if(tmp[1] == 'M') {
        !           962:                                co.Y--;
        !           963:                        } else if(tmp[1] == '*') {
        !           964:                                SMALL_RECT rect;
        !           965:                                SET_RECT(rect, 0, 0, csbi.dwSize.X - 1, csbi.dwSize.Y - 1);
        !           966:                                WriteConsoleOutput(hStdout, &scr_buf[0][0], scr_buf_size, scr_buf_pos, &rect);
        !           967:                                co.X = co.Y = 0;
        !           968:                        } else if(tmp[1] == '[') {
        !           969:                                int param[256], params = 0;
        !           970:                                memset(param, 0, sizeof(param));
        !           971:                                for(int i = 2; i < p; i++) {
        !           972:                                        if(tmp[i] >= '0' && tmp[i] <= '9') {
        !           973:                                                param[params] *= 10;
        !           974:                                                param[params] += tmp[i] - '0';
        !           975:                                        } else {
        !           976:                                                params++;
        !           977:                                        }
        !           978:                                }
        !           979:                                if(data == 'A') {
        !           980:                                        co.Y -= param[0];
        !           981:                                } else if(data == 'B') {
        !           982:                                        co.Y += param[0];
        !           983:                                } else if(data == 'C') {
        !           984:                                        co.X += param[0];
        !           985:                                } else if(data == 'D') {
        !           986:                                        co.X -= param[0];
        !           987:                                } else if(data == 'H' || data == 'f') {
        !           988:                                        co.X = param[1] - 1;
        !           989:                                        co.Y = param[0] - 1;
        !           990:                                } else if(data == 'J') {
        !           991:                                        SMALL_RECT rect;
        !           992:                                        if(param[0] == 0) {
        !           993:                                                SET_RECT(rect, co.X, co.Y, csbi.dwSize.X - 1, co.Y);
        !           994:                                                WriteConsoleOutput(hStdout, &scr_buf[0][0], scr_buf_size, scr_buf_pos, &rect);
        !           995:                                                if(co.Y < csbi.dwSize.Y - 1) {
        !           996:                                                        SET_RECT(rect, 0, co.Y + 1, csbi.dwSize.X - 1, csbi.dwSize.Y - 1);
        !           997:                                                        WriteConsoleOutput(hStdout, &scr_buf[0][0], scr_buf_size, scr_buf_pos, &rect);
        !           998:                                                }
        !           999:                                        } else if(param[0] == 1) {
        !          1000:                                                if(co.Y > 0) {
        !          1001:                                                        SET_RECT(rect, 0, 0, csbi.dwSize.X - 1, co.Y - 1);
        !          1002:                                                        WriteConsoleOutput(hStdout, &scr_buf[0][0], scr_buf_size, scr_buf_pos, &rect);
        !          1003:                                                }
        !          1004:                                                SET_RECT(rect, 0, co.Y, co.X, co.Y);
        !          1005:                                                WriteConsoleOutput(hStdout, &scr_buf[0][0], scr_buf_size, scr_buf_pos, &rect);
        !          1006:                                        } else if(param[0] == 2) {
        !          1007:                                                SET_RECT(rect, 0, 0, csbi.dwSize.X - 1, csbi.dwSize.Y - 1);
        !          1008:                                                WriteConsoleOutput(hStdout, &scr_buf[0][0], scr_buf_size, scr_buf_pos, &rect);
        !          1009:                                                co.X = co.Y = 0;
        !          1010:                                        }
        !          1011:                                } else if(data == 'K') {
        !          1012:                                        SMALL_RECT rect;
        !          1013:                                        if(param[0] == 0) {
        !          1014:                                                SET_RECT(rect, co.X, co.Y, csbi.dwSize.X - 1, co.Y);
        !          1015:                                                WriteConsoleOutput(hStdout, &scr_buf[0][0], scr_buf_size, scr_buf_pos, &rect);
        !          1016:                                        } else if(param[0] == 1) {
        !          1017:                                                SET_RECT(rect, 0, co.Y, co.X, co.Y);
        !          1018:                                                WriteConsoleOutput(hStdout, &scr_buf[0][0], scr_buf_size, scr_buf_pos, &rect);
        !          1019:                                        } else if(param[0] == 2) {
        !          1020:                                                SET_RECT(rect, 0, co.Y, csbi.dwSize.X - 1, co.Y);
        !          1021:                                                WriteConsoleOutput(hStdout, &scr_buf[0][0], scr_buf_size, scr_buf_pos, &rect);
        !          1022:                                        }
        !          1023:                                } else if(data == 'L') {
        !          1024:                                        SMALL_RECT rect;
        !          1025:                                        SET_RECT(rect, 0, co.Y, csbi.dwSize.X - 1, csbi.dwSize.Y - 1);
        !          1026:                                        ReadConsoleOutput(hStdout, &scr_buf[0][0], scr_buf_size, scr_buf_pos, &rect);
        !          1027:                                        SET_RECT(rect, 0, co.Y + param[0], csbi.dwSize.X - 1, csbi.dwSize.Y - 1);
        !          1028:                                        WriteConsoleOutput(hStdout, &scr_buf[0][0], scr_buf_size, scr_buf_pos, &rect);
        !          1029:                                        // clear buffer
        !          1030:                                        for(int y = 0; y < SCR_BUF_SIZE; y++) {
        !          1031:                                                for(int x = 0; x < 80; x++) {
        !          1032:                                                        scr_buf[y][x].Char.AsciiChar = ' ';
        !          1033:                                                        scr_buf[y][x].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
        !          1034:                                                }
        !          1035:                                        }
        !          1036:                                        SET_RECT(rect, 0, co.Y, csbi.dwSize.X - 1, co.Y + param[0] - 1);
        !          1037:                                        WriteConsoleOutput(hStdout, &scr_buf[0][0], scr_buf_size, scr_buf_pos, &rect);
        !          1038:                                        co.X = 0;
        !          1039:                                } else if(data == 'M') {
        !          1040:                                        SMALL_RECT rect;
        !          1041:                                        if(co.Y + param[0] > csbi.dwSize.Y - 1) {
        !          1042:                                                SET_RECT(rect, 0, co.Y, csbi.dwSize.X - 1, csbi.dwSize.Y - 1);
        !          1043:                                                WriteConsoleOutput(hStdout, &scr_buf[0][0], scr_buf_size, scr_buf_pos, &rect);
        !          1044:                                        } else {
        !          1045:                                                SET_RECT(rect, 0, co.Y + param[0], csbi.dwSize.X - 1, csbi.dwSize.Y - 1);
        !          1046:                                                ReadConsoleOutput(hStdout, &scr_buf[0][0], scr_buf_size, scr_buf_pos, &rect);
        !          1047:                                                SET_RECT(rect, 0, co.Y, csbi.dwSize.X - 1, csbi.dwSize.Y - 1);
        !          1048:                                                WriteConsoleOutput(hStdout, &scr_buf[0][0], scr_buf_size, scr_buf_pos, &rect);
        !          1049:                                                // clear buffer
        !          1050:                                                for(int y = 0; y < SCR_BUF_SIZE; y++) {
        !          1051:                                                        for(int x = 0; x < 80; x++) {
        !          1052:                                                                scr_buf[y][x].Char.AsciiChar = ' ';
        !          1053:                                                                scr_buf[y][x].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
        !          1054:                                                        }
        !          1055:                                                }
        !          1056:                                        }
        !          1057:                                        co.X = 0;
        !          1058:                                } else if(data == 'h') {
        !          1059:                                        if(tmp[2] == '>' && tmp[3] == '5') {
        !          1060:                                                CONSOLE_CURSOR_INFO cur;
        !          1061:                                                GetConsoleCursorInfo(hStdout, &cur);
        !          1062:                                                if(cur.bVisible) {
        !          1063:                                                        cur.bVisible = FALSE;
        !          1064:                                                        GetConsoleCursorInfo(hStdout, &cur);
        !          1065:                                                }
        !          1066:                                        }
        !          1067:                                } else if(data == 'l') {
        !          1068:                                        if(tmp[2] == '>' && tmp[3] == '5') {
        !          1069:                                                CONSOLE_CURSOR_INFO cur;
        !          1070:                                                GetConsoleCursorInfo(hStdout, &cur);
        !          1071:                                                if(!cur.bVisible) {
        !          1072:                                                        cur.bVisible = TRUE;
        !          1073:                                                        GetConsoleCursorInfo(hStdout, &cur);
        !          1074:                                                }
        !          1075:                                        }
        !          1076:                                } else if(data == 'm') {
        !          1077:                                        wAttributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
        !          1078:                                        int reverse = 0, hidden = 0;
        !          1079:                                        for(int i = 0; i < params; i++) {
        !          1080:                                                if(param[i] == 1) {
        !          1081:                                                        wAttributes |= FOREGROUND_INTENSITY;
        !          1082:                                                } else if(param[i] == 4) {
        !          1083:                                                        wAttributes |= COMMON_LVB_UNDERSCORE;
        !          1084:                                                } else if(param[i] == 7) {
        !          1085:                                                        reverse = 1;
        !          1086:                                                } else if(param[i] == 8 || param[i] == 16) {
        !          1087:                                                        hidden = 1;
        !          1088:                                                } else if((param[i] >= 17 && param[i] <= 23) || (param[i] >= 30 && param[i] <= 37)) {
        !          1089:                                                        wAttributes &= ~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
        !          1090:                                                        if(param[i] >= 17 && param[i] <= 23) {
        !          1091:                                                                param[i] -= 16;
        !          1092:                                                        } else {
        !          1093:                                                                param[i] -= 30;
        !          1094:                                                        }
        !          1095:                                                        if(param[i] & 1) {
        !          1096:                                                                wAttributes |= FOREGROUND_RED;
        !          1097:                                                        }
        !          1098:                                                        if(param[i] & 2) {
        !          1099:                                                                wAttributes |= FOREGROUND_GREEN;
        !          1100:                                                        }
        !          1101:                                                        if(param[i] & 4) {
        !          1102:                                                                wAttributes |= FOREGROUND_BLUE;
        !          1103:                                                        }
        !          1104:                                                } else if(param[i] >= 40 && param[i] <= 47) {
        !          1105:                                                        wAttributes &= ~(BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE);
        !          1106:                                                        if((param[i] - 40) & 1) {
        !          1107:                                                                wAttributes |= BACKGROUND_RED;
        !          1108:                                                        }
        !          1109:                                                        if((param[i] - 40) & 2) {
        !          1110:                                                                wAttributes |= BACKGROUND_GREEN;
        !          1111:                                                        }
        !          1112:                                                        if((param[i] - 40) & 4) {
        !          1113:                                                                wAttributes |= BACKGROUND_BLUE;
        !          1114:                                                        }
        !          1115:                                                }
        !          1116:                                        }
        !          1117:                                        if(reverse) {
        !          1118:                                                wAttributes &= ~0xff;
        !          1119:                                                wAttributes |= BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
        !          1120:                                        }
        !          1121:                                        if(hidden) {
        !          1122:                                                wAttributes &= ~0x0f;
        !          1123:                                                wAttributes |= (wAttributes >> 4) & 0x0f;
        !          1124:                                        }
        !          1125:                                } else if(data == 'n') {
        !          1126:                                        if(param[0] == 6) {
        !          1127:                                                char tmp[16];
        !          1128:                                                sprintf(tmp, "\x1b[%d;%dR", co.Y + 1, co.X + 1);
        !          1129:                                                int len = strlen(tmp);
        !          1130:                                                for(int i = 0; i < len; i++) {
        !          1131:                                                        key_buf_char->write(tmp[i]);
        !          1132:                                                        key_buf_scan->write(0x00);
        !          1133:                                                }
        !          1134:                                        }
        !          1135:                                } else if(data == 's') {
        !          1136:                                        stored_x = co.X;
        !          1137:                                        stored_y = co.Y;
        !          1138:                                        stored_a = wAttributes;
        !          1139:                                } else if(data == 'u') {
        !          1140:                                        co.X = stored_x;
        !          1141:                                        co.Y = stored_y;
        !          1142:                                        wAttributes = stored_a;
        !          1143:                                }
        !          1144:                        }
        !          1145:                        if(co.X < 0) {
        !          1146:                                co.X = 0;
        !          1147:                        } else if(co.X >= csbi.dwSize.X) {
        !          1148:                                co.X = csbi.dwSize.X - 1;
        !          1149:                        }
        !          1150:                        if(co.Y < 0) {
        !          1151:                                co.Y = 0;
        !          1152:                        } else if(co.Y >= csbi.dwSize.Y) {
        !          1153:                                co.Y = csbi.dwSize.Y - 1;
        !          1154:                        }
        !          1155:                        if(co.X != csbi.dwCursorPosition.X || co.Y != csbi.dwCursorPosition.Y) {
        !          1156:                                SetConsoleCursorPosition(hStdout, co);
        !          1157:                                mem[0x450 + mem[0x462] * 2] = co.X;
        !          1158:                                mem[0x451 + mem[0x462] * 2] = co.Y;
        !          1159:                                cursor_moved = false;
        !          1160:                        }
        !          1161:                        if(wAttributes != csbi.wAttributes) {
        !          1162:                                SetConsoleTextAttribute(hStdout, wAttributes);
        !          1163:                        }
        !          1164:                        p = is_esc = 0;
        !          1165:                }
        !          1166:                return;
        !          1167:        } else {
        !          1168:                if(msdos_lead_byte_check(data)) {
        !          1169:                        is_kanji = 1;
        !          1170:                        return;
        !          1171:                } else if(data == 0x1b) {
        !          1172:                        is_esc = 1;
        !          1173:                        return;
        !          1174:                }
        !          1175:        }
        !          1176:        tmp[p++] = '\0';
        !          1177:        p = 0;
        !          1178:        printf("%s", tmp);
        !          1179:        cursor_moved = true;
        !          1180: }
        !          1181: 
        !          1182: int msdos_aux_in()
        !          1183: {
        !          1184: #ifdef SUPPORT_AUX_PRN
        !          1185:        if(file_handler[3].valid && !eof(3)) {
        !          1186:                char data = 0;
        !          1187:                _read(3, &data, 1);
        !          1188:                return(data);
        !          1189:        } else {
        !          1190:                return(EOF);
        !          1191:        }
        !          1192: #else
        !          1193:        return(0);
        !          1194: #endif
        !          1195: }
        !          1196: 
        !          1197: void msdos_aux_out(char data)
        !          1198: {
        !          1199: #ifdef SUPPORT_AUX_PRN
        !          1200:        if(file_handler[3].valid) {
        !          1201:                msdos_write(3, &data, 1);
        !          1202:        }
        !          1203: #endif
        !          1204: }
        !          1205: 
        !          1206: void msdos_prn_out(char data)
        !          1207: {
        !          1208: #ifdef SUPPORT_AUX_PRN
        !          1209:        if(file_handler[4].valid) {
        !          1210:                msdos_write(4, &data, 1);
        !          1211:        }
        !          1212: #endif
        !          1213: }
        !          1214: 
        !          1215: // memory control
        !          1216: 
        !          1217: mcb_t *msdos_mcb_create(int mcb_seg, UINT8 mz, UINT16 psp, UINT16 paragraphs)
        !          1218: {
        !          1219:        mcb_t *mcb = (mcb_t *)(mem + (mcb_seg << 4));
        !          1220:        
        !          1221:        mcb->mz = mz;
        !          1222:        mcb->psp = psp;
        !          1223:        mcb->paragraphs = paragraphs;
        !          1224:        return(mcb);
        !          1225: }
        !          1226: 
        !          1227: void msdos_mcb_check(mcb_t *mcb)
        !          1228: {
        !          1229:        if(!(mcb->mz == 'M' || mcb->mz == 'Z')) {
        !          1230:                fatalerror("broken mcb\n");
        !          1231:        }
        !          1232: }
        !          1233: 
        !          1234: int msdos_mem_split(int seg, int paragraphs)
        !          1235: {
        !          1236:        int mcb_seg = seg - 1;
        !          1237:        mcb_t *mcb = (mcb_t *)(mem + (mcb_seg << 4));
        !          1238:        msdos_mcb_check(mcb);
        !          1239:        
        !          1240:        if(mcb->paragraphs > paragraphs) {
        !          1241:                int new_seg = mcb_seg + 1 + paragraphs;
        !          1242:                int new_paragraphs = mcb->paragraphs - paragraphs - 1;
        !          1243:                
        !          1244:                msdos_mcb_create(new_seg, mcb->mz, 0, new_paragraphs);
        !          1245:                mcb->mz = 'M';
        !          1246:                mcb->paragraphs = paragraphs;
        !          1247:                return(0);
        !          1248:        }
        !          1249:        return(-1);
        !          1250: }
        !          1251: 
        !          1252: void msdos_mem_merge(int seg)
        !          1253: {
        !          1254:        int mcb_seg = seg - 1;
        !          1255:        mcb_t *mcb = (mcb_t *)(mem + (mcb_seg << 4));
        !          1256:        msdos_mcb_check(mcb);
        !          1257:        
        !          1258:        while(1) {
        !          1259:                if(mcb->mz == 'Z') {
        !          1260:                        break;
        !          1261:                }
        !          1262:                int next_seg = mcb_seg + 1 + mcb->paragraphs;
        !          1263:                mcb_t *next_mcb = (mcb_t *)(mem + (next_seg << 4));
        !          1264:                msdos_mcb_check(next_mcb);
        !          1265:                
        !          1266:                if(next_mcb->psp != 0) {
        !          1267:                        break;
        !          1268:                }
        !          1269:                mcb->mz = next_mcb->mz;
        !          1270:                mcb->paragraphs += 1 + next_mcb->paragraphs;
        !          1271:        }
        !          1272: }
        !          1273: 
        !          1274: int msdos_mem_alloc(int paragraphs, int new_process)
        !          1275: {
        !          1276:        int mcb_seg = current_psp - 1;
        !          1277:        
        !          1278:        while(1) {
        !          1279:                mcb_t *mcb = (mcb_t *)(mem + (mcb_seg << 4));
        !          1280:                msdos_mcb_check(mcb);
        !          1281:                
        !          1282:                if(!new_process || mcb->mz == 'Z') {
        !          1283:                        if(mcb->psp == 0 && mcb->paragraphs >= paragraphs) {
        !          1284:                                msdos_mem_split(mcb_seg + 1, paragraphs);
        !          1285:                                mcb->psp = current_psp;
        !          1286:                                return(mcb_seg + 1);
        !          1287:                        }
        !          1288:                }
        !          1289:                if(mcb->mz == 'Z') {
        !          1290:                        break;
        !          1291:                }
        !          1292:                mcb_seg += 1 + mcb->paragraphs;
        !          1293:        }
        !          1294:        return(-1);
        !          1295: }
        !          1296: 
        !          1297: int msdos_mem_realloc(int seg, int paragraphs, int *max_paragraphs)
        !          1298: {
        !          1299:        int mcb_seg = seg - 1;
        !          1300:        mcb_t *mcb = (mcb_t *)(mem + (mcb_seg << 4));
        !          1301:        msdos_mcb_check(mcb);
        !          1302:        int current_paragraphs = mcb->paragraphs;
        !          1303:        
        !          1304:        msdos_mem_merge(seg);
        !          1305:        if(paragraphs > mcb->paragraphs) {
        !          1306:                *max_paragraphs = mcb->paragraphs;
        !          1307:                msdos_mem_split(seg, current_paragraphs);
        !          1308:                return(-1);
        !          1309:        }
        !          1310:        msdos_mem_split(seg, paragraphs);
        !          1311:        return(0);
        !          1312: }
        !          1313: 
        !          1314: void msdos_mem_free(int seg)
        !          1315: {
        !          1316:        int mcb_seg = seg - 1;
        !          1317:        mcb_t *mcb = (mcb_t *)(mem + (mcb_seg << 4));
        !          1318:        msdos_mcb_check(mcb);
        !          1319:        
        !          1320:        mcb->psp = 0;
        !          1321:        msdos_mem_merge(seg);
        !          1322: }
        !          1323: 
        !          1324: int msdos_mem_get_free(int new_process)
        !          1325: {
        !          1326:        int mcb_seg = current_psp - 1;
        !          1327:        int max_paragraphs = 0;
        !          1328:        
        !          1329:        while(1) {
        !          1330:                mcb_t *mcb = (mcb_t *)(mem + (mcb_seg << 4));
        !          1331:                msdos_mcb_check(mcb);
        !          1332:                
        !          1333:                if(!new_process || mcb->mz == 'Z') {
        !          1334:                        if(mcb->psp == 0 && mcb->paragraphs > max_paragraphs) {
        !          1335:                                max_paragraphs = mcb->paragraphs;
        !          1336:                        }
        !          1337:                }
        !          1338:                if(mcb->mz == 'Z') {
        !          1339:                        break;
        !          1340:                }
        !          1341:                mcb_seg += 1 + mcb->paragraphs;
        !          1342:        }
        !          1343:        return(max_paragraphs);
        !          1344: }
        !          1345: 
        !          1346: // environment
        !          1347: 
        !          1348: void msdos_env_set_argv(int env_seg, char *argv)
        !          1349: {
        !          1350:        char *dst = (char *)(mem + (env_seg << 4));
        !          1351:        
        !          1352:        while(1) {
        !          1353:                if(dst[0] == 0) {
        !          1354:                        break;
        !          1355:                }
        !          1356:                dst += strlen(dst) + 1;
        !          1357:        }
        !          1358:        *dst++ = 0; // end of environment
        !          1359:        *dst++ = 1; // top of argv[0]
        !          1360:        *dst++ = 0;
        !          1361:        memcpy(dst, argv, strlen(argv));
        !          1362:        dst += strlen(argv);
        !          1363:        *dst++ = 0;
        !          1364:        *dst++ = 0;
        !          1365: }
        !          1366: 
        !          1367: char *msdos_env_get_argv(int env_seg)
        !          1368: {
        !          1369:        static char env[ENV_SIZE];
        !          1370:        char *src = env;
        !          1371:        
        !          1372:        memcpy(src, mem + (env_seg << 4), ENV_SIZE);
        !          1373:        while(1) {
        !          1374:                if(src[0] == 0) {
        !          1375:                        if(src[1] == 1) {
        !          1376:                                return(src + 3);
        !          1377:                        }
        !          1378:                        break;
        !          1379:                }
        !          1380:                src += strlen(src) + 1;
        !          1381:        }
        !          1382:        return(NULL);
        !          1383: }
        !          1384: 
        !          1385: char *msdos_env_get(int env_seg, const char *name)
        !          1386: {
        !          1387:        static char env[ENV_SIZE];
        !          1388:        char *src = env;
        !          1389:        
        !          1390:        memcpy(src, mem + (env_seg << 4), ENV_SIZE);
        !          1391:        while(1) {
        !          1392:                if(src[0] == 0) {
        !          1393:                        break;
        !          1394:                }
        !          1395:                int len = strlen(src);
        !          1396:                char *n = my_strtok(src, "=");
        !          1397:                char *v = src + strlen(n) + 1;
        !          1398:                
        !          1399:                if(_stricmp(name, n) == 0) {
        !          1400:                        return(v);
        !          1401:                }
        !          1402:                src += len + 1;
        !          1403:        }
        !          1404:        return(NULL);
        !          1405: }
        !          1406: 
        !          1407: void msdos_env_set(int env_seg, char *name, char *value)
        !          1408: {
        !          1409:        char env[ENV_SIZE];
        !          1410:        char *src = env;
        !          1411:        char *dst = (char *)(mem + (env_seg << 4));
        !          1412:        char *argv = msdos_env_get_argv(env_seg);
        !          1413:        int done = 0;
        !          1414:        
        !          1415:        memcpy(src, dst, ENV_SIZE);
        !          1416:        memset(dst, 0, ENV_SIZE);
        !          1417:        while(1) {
        !          1418:                if(src[0] == 0) {
        !          1419:                        break;
        !          1420:                }
        !          1421:                int len = strlen(src);
        !          1422:                char *n = my_strtok(src, "=");
        !          1423:                char *v = src + strlen(n) + 1;
        !          1424:                char tmp[1024];
        !          1425:                
        !          1426:                if(_stricmp(name, n) == 0) {
        !          1427:                        sprintf(tmp, "%s=%s", n, value);
        !          1428:                        done = 1;
        !          1429:                } else {
        !          1430:                        sprintf(tmp, "%s=%s", n, v);
        !          1431:                }
        !          1432:                memcpy(dst, tmp, strlen(tmp));
        !          1433:                dst += strlen(tmp) + 1;
        !          1434:                src += len + 1;
        !          1435:        }
        !          1436:        if(!done) {
        !          1437:                char tmp[1024];
        !          1438:                
        !          1439:                sprintf(tmp, "%s=%s", name, value);
        !          1440:                memcpy(dst, tmp, strlen(tmp));
        !          1441:                dst += strlen(tmp) + 1;
        !          1442:        }
        !          1443:        if(argv) {
        !          1444:                *dst++ = 0; // end of environment
        !          1445:                *dst++ = 1; // top of argv[0]
        !          1446:                *dst++ = 0;
        !          1447:                memcpy(dst, argv, strlen(argv));
        !          1448:                dst += strlen(argv);
        !          1449:                *dst++ = 0;
        !          1450:                *dst++ = 0;
        !          1451:        }
        !          1452: }
        !          1453: 
        !          1454: // process
        !          1455: 
        !          1456: psp_t *msdos_psp_create(int psp_seg, UINT16 first_mcb, UINT16 parent_psp, UINT16 env_seg)
        !          1457: {
        !          1458:        psp_t *psp = (psp_t *)(mem + (psp_seg << 4));
        !          1459:        
        !          1460:        memset(psp, 0, PSP_SIZE);
        !          1461:        psp->exit[0] = 0xcd;
        !          1462:        psp->exit[1] = 0x20;
        !          1463:        psp->first_mcb = first_mcb;
        !          1464:        psp->far_call = 0xea;
        !          1465:        psp->cpm_entry.w.l = 0xfff1;    // int 21h, retf
        !          1466:        psp->cpm_entry.w.h = 0xf000;
        !          1467:        psp->int_22h.dw = *(UINT32 *)(mem + 4 * 0x22);
        !          1468:        psp->int_23h.dw = *(UINT32 *)(mem + 4 * 0x23);
        !          1469:        psp->int_24h.dw = *(UINT32 *)(mem + 4 * 0x24);
        !          1470:        psp->parent_psp = parent_psp;
        !          1471:        for(int i = 0; i < 20; i++) {
        !          1472:                if(file_handler[i].valid) {
        !          1473:                        psp->file_table[i] = i;
        !          1474:                } else {
        !          1475:                        psp->file_table[i] = 0xff;
        !          1476:                }
        !          1477:        }
        !          1478:        psp->env_seg = env_seg;
        !          1479:        psp->stack.w.l = REG16(SP);
        !          1480:        psp->stack.w.h = cpustate->sreg[SS].selector;
        !          1481:        psp->service[0] = 0xcd;
        !          1482:        psp->service[1] = 0x21;
        !          1483:        psp->service[2] = 0xcb;
        !          1484:        return(psp);
        !          1485: }
        !          1486: 
        !          1487: int msdos_process_exec(char *cmd, param_block_t *param, UINT8 al)
        !          1488: {
        !          1489:        // load command file
        !          1490:        int fd = -1;
        !          1491:        int dos_command = 0;
        !          1492:        char command[MAX_PATH], path[MAX_PATH], opt[MAX_PATH], *name;
        !          1493:        
        !          1494:        strcpy(command, cmd);
        !          1495:        int opt_ofs = (param->cmd_line.w.h << 4) + param->cmd_line.w.l;
        !          1496:        int opt_len = mem[opt_ofs];
        !          1497:        memset(opt, 0, sizeof(opt));
        !          1498:        memcpy(opt, mem + opt_ofs + 1, opt_len);
        !          1499:        
        !          1500:        // check command.com
        !          1501:        GetFullPathName(command, MAX_PATH, path, &name);
        !          1502:        if(_stricmp(name, "COMMAND.COM") == 0 || _stricmp(name, "CMD.EXE") == 0) {
        !          1503:                for(int i = 0; i < opt_len; i++) {
        !          1504:                        if(opt[i] == ' ') {
        !          1505:                                continue;
        !          1506:                        }
        !          1507:                        if(opt[i] == '/' && (opt[i + 1] == 'c' || opt[i + 1] == 'C') && opt[i + 2] == ' ') {
        !          1508:                                dos_command = 1;
        !          1509:                                for(int j = i + 3; j < opt_len; j++) {
        !          1510:                                        if(opt[j] == ' ') {
        !          1511:                                                continue;
        !          1512:                                        }
        !          1513:                                        char *token = my_strtok(opt + j, " ");
        !          1514:                                        strcpy(command, token);
        !          1515:                                        char tmp[MAX_PATH];
        !          1516:                                        strcpy(tmp, token + strlen(token) + 1);
        !          1517:                                        strcpy(opt, tmp);
        !          1518:                                        opt_len = strlen(opt);
        !          1519:                                        mem[opt_ofs] = opt_len;
        !          1520:                                        strcpy((char *)(mem + opt_ofs + 1), opt);
        !          1521:                                        break;
        !          1522:                                }
        !          1523:                        }
        !          1524:                        break;
        !          1525:                }
        !          1526:        }
        !          1527:        
        !          1528:        // load command file
        !          1529:        strcpy(path, command);
        !          1530:        if((fd = _open(path, _O_RDONLY | _O_BINARY)) == -1) {
        !          1531:                sprintf(path, "%s.COM", command);
        !          1532:                if((fd = _open(path, _O_RDONLY | _O_BINARY)) == -1) {
        !          1533:                        sprintf(path, "%s.EXE", command);
        !          1534:                        if((fd = _open(path, _O_RDONLY | _O_BINARY)) == -1) {
        !          1535:                                // search path in parent environments
        !          1536:                                psp_t *parent_psp = (psp_t *)(mem + (current_psp << 4));
        !          1537:                                char *env = msdos_env_get(parent_psp->env_seg, "PATH");
        !          1538:                                
        !          1539:                                if(env != NULL) {
        !          1540:                                        char env_path[1024];
        !          1541:                                        strcpy(env_path, env);
        !          1542:                                        char *token = my_strtok(env_path, ";");
        !          1543:                                        
        !          1544:                                        while(token != NULL) {
        !          1545:                                                sprintf(path, "%s\\%s", token, command);
        !          1546:                                                if((fd = _open(path, _O_RDONLY | _O_BINARY)) != -1) {
        !          1547:                                                        break;
        !          1548:                                                }
        !          1549:                                                sprintf(path, "%s\\%s.COM", token, command);
        !          1550:                                                if((fd = _open(path, _O_RDONLY | _O_BINARY)) != -1) {
        !          1551:                                                        break;
        !          1552:                                                }
        !          1553:                                                sprintf(path, "%s\\%s.EXE", token, command);
        !          1554:                                                if((fd = _open(path, _O_RDONLY | _O_BINARY)) != -1) {
        !          1555:                                                        break;
        !          1556:                                                }
        !          1557:                                                token = my_strtok(NULL, ";");
        !          1558:                                        }
        !          1559:                                }
        !          1560:                        }
        !          1561:                }
        !          1562:        }
        !          1563:        if(fd == -1) {
        !          1564:                if(dos_command) {
        !          1565:                        // may be dos command
        !          1566:                        char tmp[MAX_PATH];
        !          1567:                        sprintf(tmp, "%s %s", command, opt);
        !          1568:                        system(tmp);
        !          1569:                        return(0);
        !          1570:                } else {
        !          1571:                        return(-1);
        !          1572:                }
        !          1573:        }
        !          1574:        _read(fd, file_buffer, sizeof(file_buffer));
        !          1575:        _close(fd);
        !          1576:        
        !          1577:        // copy environment
        !          1578:        int env_seg, psp_seg;
        !          1579:        
        !          1580:        if((env_seg = msdos_mem_alloc(ENV_SIZE >> 4, 1)) == -1) {
        !          1581:                return(-1);
        !          1582:        }
        !          1583:        if(param->env_seg == 0) {
        !          1584:                psp_t *parent_psp = (psp_t *)(mem + (current_psp << 4));
        !          1585:                memcpy(mem + (env_seg << 4), mem + (parent_psp->env_seg << 4), ENV_SIZE);
        !          1586:        } else {
        !          1587:                memcpy(mem + (env_seg << 4), mem + (param->env_seg << 4), ENV_SIZE);
        !          1588:        }
        !          1589:        msdos_env_set_argv(env_seg, msdos_short_full_path(path));
        !          1590:        
        !          1591:        // check exe header
        !          1592:        exe_header_t *header = (exe_header_t *)file_buffer;
        !          1593:        int paragraphs, free_paragraphs = msdos_mem_get_free(1);
        !          1594:        UINT16 cs, ss, ip, sp;
        !          1595:        
        !          1596:        if(header->mz == 0x4d5a || header->mz == 0x5a4d) {
        !          1597:                // memory allocation
        !          1598:                int header_size = header->header_size * 16;
        !          1599:                int load_size = header->pages * 512 - header_size;
        !          1600:                if(header_size + load_size < 512) {
        !          1601:                        load_size = 512 - header_size;
        !          1602:                }
        !          1603:                paragraphs = (PSP_SIZE + load_size) >> 4;
        !          1604:                if(paragraphs + header->min_alloc > free_paragraphs) {
        !          1605:                        msdos_mem_free(env_seg);
        !          1606:                        return(-1);
        !          1607:                }
        !          1608:                paragraphs += header->max_alloc ? header->max_alloc : header->min_alloc;
        !          1609:                if(paragraphs > free_paragraphs) {
        !          1610:                        paragraphs = free_paragraphs;
        !          1611:                }
        !          1612:                if((psp_seg = msdos_mem_alloc(paragraphs, 1)) == -1) {
        !          1613:                        msdos_mem_free(env_seg);
        !          1614:                        return(-1);
        !          1615:                }
        !          1616:                // relocation
        !          1617:                int start_seg = psp_seg + (PSP_SIZE >> 4);
        !          1618:                for(int i = 0; i < header->relocations; i++) {
        !          1619:                        int ofs = *(UINT16 *)(file_buffer + header->relocation_table + i * 4 + 0);
        !          1620:                        int seg = *(UINT16 *)(file_buffer + header->relocation_table + i * 4 + 2);
        !          1621:                        *(UINT16 *)(file_buffer + header_size + (seg << 4) + ofs) += start_seg;
        !          1622:                }
        !          1623:                memcpy(mem + (start_seg << 4), file_buffer + header_size, load_size);
        !          1624:                // segments
        !          1625:                cs = header->init_cs + start_seg;
        !          1626:                ss = header->init_ss + start_seg;
        !          1627:                ip = header->init_ip;
        !          1628:                sp = header->init_sp - 2; // for symdeb
        !          1629:        } else {
        !          1630:                // memory allocation
        !          1631:                paragraphs = free_paragraphs;
        !          1632:                if((psp_seg = msdos_mem_alloc(paragraphs, 1)) == -1) {
        !          1633:                        msdos_mem_free(env_seg);
        !          1634:                        return(-1);
        !          1635:                }
        !          1636:                int start_seg = psp_seg + (PSP_SIZE >> 4);
        !          1637:                memcpy(mem + (start_seg << 4), file_buffer, 0x10000 - PSP_SIZE);
        !          1638:                // segments
        !          1639:                cs = ss = psp_seg;
        !          1640:                ip = 0x100;
        !          1641:                sp = 0xfffe;
        !          1642:        }
        !          1643:        
        !          1644:        // create psp
        !          1645:        *(UINT16 *)(mem + 4 * 0x22 + 0) = cpustate->eip;
        !          1646:        *(UINT16 *)(mem + 4 * 0x22 + 2) = cpustate->sreg[CS].selector;
        !          1647:        psp_t *psp = msdos_psp_create(psp_seg, psp_seg + paragraphs, current_psp, env_seg);
        !          1648:        memcpy(psp->fcb1, mem + (param->fcb1.w.h << 4) + param->fcb1.w.l, sizeof(psp->fcb1));
        !          1649:        memcpy(psp->fcb2, mem + (param->fcb2.w.h << 4) + param->fcb2.w.l, sizeof(psp->fcb2));
        !          1650:        memcpy(psp->buffer, mem + (param->cmd_line.w.h << 4) + param->cmd_line.w.l, sizeof(psp->buffer));
        !          1651:        
        !          1652:        mcb_t *mcb_env = (mcb_t *)(mem + ((env_seg - 1) << 4));
        !          1653:        mcb_t *mcb_psp = (mcb_t *)(mem + ((psp_seg - 1) << 4));
        !          1654:        mcb_psp->psp = mcb_env->psp = psp_seg;
        !          1655:        
        !          1656:        // process info
        !          1657:        process_t *process = msdos_process_info_create(psp_seg);
        !          1658:        strcpy(process->module_dir, msdos_short_full_dir(path));
        !          1659:        process->dta.w.l = 0x80;
        !          1660:        process->dta.w.h = psp_seg;
        !          1661:        process->switchar = '/';
        !          1662:        process->max_files = 20;
        !          1663:        process->find_handle = INVALID_HANDLE_VALUE;
        !          1664:        process->parent_int_10h_feh_called = int_10h_feh_called;
        !          1665:        process->parent_int_10h_ffh_called = int_10h_ffh_called;
        !          1666:        
        !          1667:        current_psp = psp_seg;
        !          1668:        
        !          1669:        if(al == 0x00) {
        !          1670:                int_10h_feh_called = int_10h_ffh_called = false;
        !          1671:                
        !          1672:                // registers and segments
        !          1673:                REG16(AX) = REG16(BX) = 0x00;
        !          1674:                REG16(CX) = 0xff;
        !          1675:                REG16(DX) = psp_seg;
        !          1676:                REG16(SI) = ip;
        !          1677:                REG16(DI) = sp;
        !          1678:                REG16(SP) = sp;
        !          1679:                cpustate->sreg[DS].selector = cpustate->sreg[ES].selector = psp_seg;
        !          1680:                cpustate->sreg[SS].selector = ss;
        !          1681:                i386_load_segment_descriptor(cpustate, DS);
        !          1682:                i386_load_segment_descriptor(cpustate, ES);
        !          1683:                i386_load_segment_descriptor(cpustate, SS);
        !          1684:                
        !          1685:                *(UINT16 *)(mem + (ss << 4) + sp) = 0;
        !          1686:                i386_jmp_far(cs, ip);
        !          1687:        } else if(al == 0x01) {
        !          1688:                // copy ss:sp and cs:ip to param block
        !          1689:                param->sp = sp;
        !          1690:                param->ss = ss;
        !          1691:                param->ip = ip;
        !          1692:                param->cs = cs;
        !          1693:        }
        !          1694:        return(0);
        !          1695: }
        !          1696: 
        !          1697: void msdos_process_terminate(int psp_seg, int ret, int mem_free)
        !          1698: {
        !          1699:        psp_t *psp = (psp_t *)(mem + (psp_seg << 4));
        !          1700:        
        !          1701:        *(UINT32 *)(mem + 4 * 0x22) = psp->int_22h.dw;
        !          1702:        *(UINT32 *)(mem + 4 * 0x23) = psp->int_23h.dw;
        !          1703:        *(UINT32 *)(mem + 4 * 0x24) = psp->int_24h.dw;
        !          1704:        
        !          1705:        cpustate->sreg[SS].selector = psp->stack.w.h;
        !          1706:        i386_load_segment_descriptor(cpustate, SS);
        !          1707:        REG16(SP) = psp->stack.w.l;
        !          1708:        i386_jmp_far(psp->int_22h.w.h, psp->int_22h.w.l);
        !          1709:        
        !          1710:        process_t *process = msdos_process_info_get(psp_seg);
        !          1711:        int_10h_feh_called = process->parent_int_10h_feh_called;
        !          1712:        int_10h_ffh_called = process->parent_int_10h_ffh_called;
        !          1713:        
        !          1714:        if(mem_free) {
        !          1715:                int mcb_seg = psp->env_seg - 1;
        !          1716:                msdos_mcb_create(mcb_seg, 'Z', 0, (MEMORY_END >> 4) - mcb_seg - 1);
        !          1717:                
        !          1718:                for(int i = 0; i < MAX_FILES; i++) {
        !          1719:                        if(file_handler[i].valid && file_handler[i].psp == psp_seg) {
        !          1720:                                _close(i);
        !          1721:                                msdos_file_handler_close(i, psp_seg);
        !          1722:                        }
        !          1723:                }
        !          1724:                msdos_stdio_reopen();
        !          1725:                
        !          1726:                if(process->find_handle != INVALID_HANDLE_VALUE) {
        !          1727:                        FindClose(process->find_handle);
        !          1728:                        process->find_handle = INVALID_HANDLE_VALUE;
        !          1729:                }
        !          1730:        }
        !          1731:        
        !          1732:        memset(process, 0, sizeof(process_t));
        !          1733:        
        !          1734:        current_psp = psp->parent_psp;
        !          1735:        retval = ret;
        !          1736: }
        !          1737: 
        !          1738: // drive
        !          1739: 
        !          1740: int msdos_drive_param_block_update(int drive_num, UINT16 *seg, UINT16 *ofs, int force_update)
        !          1741: {
        !          1742:        *seg = DPB_TOP >> 4;
        !          1743:        *ofs = sizeof(dpb_t) * drive_num;
        !          1744:        dpb_t *dpb = (dpb_t *)(mem + (*seg << 4) + *ofs);
        !          1745:        
        !          1746:        if(!force_update && dpb->free_clusters != 0) {
        !          1747:                return(dpb->bytes_per_sector ? 1 : 0);
        !          1748:        }
        !          1749:        memset(dpb, 0, sizeof(dpb_t));
        !          1750:        
        !          1751:        int res = 0;
        !          1752:        char dev[64];
        !          1753:        sprintf(dev, "\\\\.\\%c:", 'A' + drive_num);
        !          1754:        
        !          1755:        HANDLE hFile = CreateFile(dev, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
        !          1756:        if(hFile != INVALID_HANDLE_VALUE) {
        !          1757:                DISK_GEOMETRY geo;
        !          1758:                DWORD dwSize;
        !          1759:                if(DeviceIoControl(hFile, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &geo, sizeof(geo), &dwSize, NULL)) {
        !          1760:                        dpb->bytes_per_sector = (UINT16)geo.BytesPerSector;
        !          1761:                        dpb->highest_sector_num = (UINT8)(geo.SectorsPerTrack - 1);
        !          1762:                        dpb->highest_cluster_num = (UINT16)(geo.TracksPerCylinder * geo.Cylinders.QuadPart + 1);
        !          1763:                        switch(geo.MediaType) {
        !          1764:                        case F5_320_512:        // floppy, double-sided, 8 sectors per track (320K)
        !          1765:                                dpb->media_type = 0xff;
        !          1766:                                break;
        !          1767:                        case F5_160_512:        // floppy, single-sided, 8 sectors per track (160K)
        !          1768:                                dpb->media_type = 0xfe;
        !          1769:                                break;
        !          1770:                        case F5_360_512:        // floppy, double-sided, 9 sectors per track (360K)
        !          1771:                                dpb->media_type = 0xfd;
        !          1772:                                break;
        !          1773:                        case F5_180_512:        // floppy, single-sided, 9 sectors per track (180K)
        !          1774:                                dpb->media_type = 0xfc;
        !          1775:                                break;
        !          1776:                        case F5_1Pt2_512:       // floppy, double-sided, 15 sectors per track (1.2M)
        !          1777:                        case F3_720_512:        // floppy, double-sided, 9 sectors per track (720K,3.5")
        !          1778:                                dpb->media_type = 0xf9;
        !          1779:                                break;
        !          1780:                        case FixedMedia:        // hard disk
        !          1781:                        case RemovableMedia:
        !          1782:                                dpb->media_type = 0xf8;
        !          1783:                                break;
        !          1784:                        default:
        !          1785:                                dpb->media_type = 0xf0;
        !          1786:                                break;
        !          1787:                        }
        !          1788:                        res = 1;
        !          1789:                }
        !          1790:                dpb->drive_num = drive_num;
        !          1791:                dpb->unit_num = drive_num;
        !          1792:                dpb->next_dpb_ofs = *ofs + sizeof(dpb_t);
        !          1793:                dpb->next_dpb_seg = *seg;
        !          1794:                dpb->free_clusters = 0xffff;
        !          1795:                CloseHandle(hFile);
        !          1796:        }
        !          1797:        return(res);
        !          1798: }
        !          1799: 
        !          1800: // pc bios
        !          1801: 
        !          1802: int get_tvram_address(int page)
        !          1803: {
        !          1804:        if(/*mem[0x449] == 0x03 || */mem[0x449] == 0x70 || mem[0x449] == 0x71 || mem[0x449] == 0x73) {
        !          1805:                return TVRAM_TOP;
        !          1806:        } else {
        !          1807:                return TVRAM_TOP + 0x1000 * (page & 7);
        !          1808:        }
        !          1809: }
        !          1810: 
        !          1811: inline int get_tvram_address(int page, int x, int y)
        !          1812: {
        !          1813:        return get_tvram_address(page) + (x + y * 80) * 2;
        !          1814: }
        !          1815: 
        !          1816: inline void pcbios_int_10h_00h()
        !          1817: {
        !          1818:        mem[0x449] = REG8(AL) & 0x7f;
        !          1819:        tvram_base_address = get_tvram_address(mem[0x462]);
        !          1820:        
        !          1821:        if(REG8(AL) & 0x80) {
        !          1822:                mem[0x487] |= 0x80;
        !          1823:        } else {
        !          1824:                for(int y = 0, ofs = get_tvram_address(mem[0x462]); y < 25; y++) {
        !          1825:                        for(int x = 0; x < 80; x++) {
        !          1826:                                scr_buf[y][x].Char.AsciiChar = ' ';
        !          1827:                                scr_buf[y][x].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
        !          1828:                                mem[ofs++] = 0x20;
        !          1829:                                mem[ofs++] = 0x07;
        !          1830:                        }
        !          1831:                }
        !          1832:                SMALL_RECT rect;
        !          1833:                SET_RECT(rect, 0, 0, 79, 24);
        !          1834:                WriteConsoleOutput(hStdout, &scr_buf[0][0], scr_buf_size, scr_buf_pos, &rect);
        !          1835:                mem[0x487] &= ~0x80;
        !          1836:        }
        !          1837: }
        !          1838: 
        !          1839: inline void pcbios_int_10h_01h()
        !          1840: {
        !          1841:         mem[0x460] = REG8(CL);
        !          1842:         mem[0x461] = REG8(CH);
        !          1843: }
        !          1844: 
        !          1845: inline void pcbios_int_10h_02h()
        !          1846: {
        !          1847:        if(mem[0x462] == REG8(BH)) {
        !          1848:                COORD co;
        !          1849:                co.X = REG8(DL);
        !          1850:                co.Y = REG8(DH);
        !          1851:                SetConsoleCursorPosition(hStdout, co);
        !          1852:        }
        !          1853:        mem[0x450 + (REG8(BH) & 7) * 2] = REG8(DL);
        !          1854:        mem[0x451 + (REG8(BH) & 7) * 2] = REG8(DH);
        !          1855: }
        !          1856: 
        !          1857: inline void pcbios_int_10h_03h()
        !          1858: {
        !          1859:        REG8(DL) = mem[0x450 + (REG8(BH) & 7) * 2];
        !          1860:        REG8(DH) = mem[0x451 + (REG8(BH) & 7) * 2];
        !          1861:        REG8(CL) = mem[0x460];
        !          1862:        REG8(CH) = mem[0x461];
        !          1863: }
        !          1864: 
        !          1865: inline void pcbios_int_10h_05h()
        !          1866: {
        !          1867:        if(mem[0x462] != REG8(BH)) {
        !          1868:                SMALL_RECT rect;
        !          1869:                SET_RECT(rect, 0, 0, 79, 24);
        !          1870:                ReadConsoleOutput(hStdout, &scr_buf[0][0], scr_buf_size, scr_buf_pos, &rect);
        !          1871:                
        !          1872:                for(int y = 0, ofs = get_tvram_address(mem[0x462]); y < 25; y++) {
        !          1873:                        for(int x = 0; x < 80; x++) {
        !          1874:                                mem[ofs++] = scr_buf[y][x].Char.AsciiChar;
        !          1875:                                mem[ofs++] = scr_buf[y][x].Attributes;
        !          1876:                        }
        !          1877:                }
        !          1878:                for(int y = 0, ofs = get_tvram_address(REG8(BH)); y < 25; y++) {
        !          1879:                        for(int x = 0; x < 80; x++) {
        !          1880:                                scr_buf[y][x].Char.AsciiChar = mem[ofs++];
        !          1881:                                scr_buf[y][x].Attributes = mem[ofs++];
        !          1882:                        }
        !          1883:                }
        !          1884:                WriteConsoleOutput(hStdout, &scr_buf[0][0], scr_buf_size, scr_buf_pos, &rect);
        !          1885:                
        !          1886:                COORD co;
        !          1887:                co.X = mem[0x450 + (REG8(BH) & 7) * 2];
        !          1888:                co.Y = mem[0x451 + (REG8(BH) & 7) * 2];
        !          1889:                SetConsoleCursorPosition(hStdout, co);
        !          1890:        }
        !          1891:        mem[0x462] = REG8(BH) & 7;
        !          1892:        mem[0x44e] = 0;
        !          1893:        mem[0x44f] = REG8(BH) << 4;
        !          1894:        tvram_base_address = get_tvram_address(mem[0x462]);
        !          1895: }
        !          1896: 
        !          1897: inline void pcbios_int_10h_06h()
        !          1898: {
        !          1899:        SMALL_RECT rect;
        !          1900:        SET_RECT(rect, 0, 0, 79, 24);
        !          1901:        ReadConsoleOutput(hStdout, &scr_buf[0][0], scr_buf_size, scr_buf_pos, &rect);
        !          1902:        
        !          1903:        if(REG8(AL) == 0) {
        !          1904:                for(int y = REG8(CH); y <= REG8(DH); y++) {
        !          1905:                        for(int x = REG8(CL), ofs = get_tvram_address(mem[0x462], REG8(CL), y); x <= REG8(DL); x++) {
        !          1906:                                scr_buf[y][x].Char.AsciiChar = ' ';
        !          1907:                                scr_buf[y][x].Attributes = REG8(BH);
        !          1908:                                mem[ofs++] = scr_buf[y][x].Char.AsciiChar;
        !          1909:                                mem[ofs++] = scr_buf[y][x].Attributes;
        !          1910:                        }
        !          1911:                }
        !          1912:        } else {
        !          1913:                ReadConsoleOutput(hStdout, &scr_buf[0][0], scr_buf_size, scr_buf_pos, &rect);
        !          1914:                for(int y = REG8(CH), y2 = REG8(CH) + REG8(AL); y <= REG8(DH); y++, y2++) {
        !          1915:                        for(int x = REG8(CL), ofs = get_tvram_address(mem[0x462], REG8(CL), y); x <= REG8(DL); x++) {
        !          1916:                                if(y2 <= REG8(DH) && y2 >= 0 && y2 < SCR_BUF_SIZE) {
        !          1917:                                        scr_buf[y][x] = scr_buf[y2][x];
        !          1918:                                } else {
        !          1919:                                        scr_buf[y][x].Char.AsciiChar = ' ';
        !          1920:                                        scr_buf[y][x].Attributes = REG8(BH);
        !          1921:                                }
        !          1922:                                mem[ofs++] = scr_buf[y][x].Char.AsciiChar;
        !          1923:                                mem[ofs++] = scr_buf[y][x].Attributes;
        !          1924:                        }
        !          1925:                }
        !          1926:        }
        !          1927:        WriteConsoleOutput(hStdout, &scr_buf[0][0], scr_buf_size, scr_buf_pos, &rect);
        !          1928: }
        !          1929: 
        !          1930: inline void pcbios_int_10h_07h()
        !          1931: {
        !          1932:        SMALL_RECT rect;
        !          1933:        SET_RECT(rect, 0, 0, 79, 24);
        !          1934:        ReadConsoleOutput(hStdout, &scr_buf[0][0], scr_buf_size, scr_buf_pos, &rect);
        !          1935:        
        !          1936:        if(REG8(AL) == 0) {
        !          1937:                for(int y = REG8(CH); y <= REG8(DH); y++) {
        !          1938:                        for(int x = REG8(CL), ofs = get_tvram_address(mem[0x462], REG8(CL), y); x <= REG8(DL); x++) {
        !          1939:                                scr_buf[y][x].Char.AsciiChar = ' ';
        !          1940:                                scr_buf[y][x].Attributes = REG8(BH);
        !          1941:                                mem[ofs++] = scr_buf[y][x].Char.AsciiChar;
        !          1942:                                mem[ofs++] = scr_buf[y][x].Attributes;
        !          1943:                        }
        !          1944:                }
        !          1945:        } else {
        !          1946:                ReadConsoleOutput(hStdout, &scr_buf[0][0], scr_buf_size, scr_buf_pos, &rect);
        !          1947:                for(int y = REG8(DH), y2 = REG8(DH) - REG8(AL); y >= REG8(CH); y--, y2--) {
        !          1948:                        for(int x = REG8(CL), ofs = get_tvram_address(mem[0x462], REG8(CL), y); x <= REG8(DL); x++) {
        !          1949:                                if(y2 >= REG8(CH) && y2 >= 0 && y2 < SCR_BUF_SIZE) {
        !          1950:                                        scr_buf[y][x] = scr_buf[y2][x];
        !          1951:                                } else {
        !          1952:                                        scr_buf[y][x].Char.AsciiChar = ' ';
        !          1953:                                        scr_buf[y][x].Attributes = REG8(BH);
        !          1954:                                }
        !          1955:                                mem[ofs++] = scr_buf[y][x].Char.AsciiChar;
        !          1956:                                mem[ofs++] = scr_buf[y][x].Attributes;
        !          1957:                        }
        !          1958:                }
        !          1959:        }
        !          1960:        WriteConsoleOutput(hStdout, &scr_buf[0][0], scr_buf_size, scr_buf_pos, &rect);
        !          1961: }
        !          1962: 
        !          1963: inline void pcbios_int_10h_08h()
        !          1964: {
        !          1965:        COORD co;
        !          1966:        DWORD num;
        !          1967:        
        !          1968:        co.X = mem[0x450 + (REG8(BH) & 7) * 2];
        !          1969:        co.Y = mem[0x451 + (REG8(BH) & 7) * 2];
        !          1970:        
        !          1971:        if(mem[0x462] == REG8(BH)) {
        !          1972:                ReadConsoleOutputCharacter(hStdout, scr_char, 1, co, &num);
        !          1973:                ReadConsoleOutputAttribute(hStdout, scr_attr, 1, co, &num);
        !          1974:                REG8(AL) = scr_char[0];
        !          1975:                REG8(AH) = scr_attr[0];
        !          1976:        } else {
        !          1977:                REG16(AX) = *(UINT16 *)(mem + get_tvram_address(REG8(BH), co.X, co.Y));
        !          1978:        }
        !          1979: }
        !          1980: 
        !          1981: inline void pcbios_int_10h_09h()
        !          1982: {
        !          1983:        COORD co;
        !          1984:        DWORD num;
        !          1985:        
        !          1986:        co.X = mem[0x450 + (REG8(BH) & 7) * 2];
        !          1987:        co.Y = mem[0x451 + (REG8(BH) & 7) * 2];
        !          1988:        
        !          1989:        if(mem[0x462] == REG8(BH)) {
        !          1990:                for(int i = 0; i < REG16(CX) && i < 80 * 25; i++) {
        !          1991:                        scr_char[i] = REG8(AL);
        !          1992:                        scr_attr[i] = REG8(BL);
        !          1993:                }
        !          1994:                WriteConsoleOutputCharacter(hStdout, scr_char, REG16(CX), co, &num);
        !          1995:                WriteConsoleOutputAttribute(hStdout, scr_attr, REG16(CX), co, &num);
        !          1996:        } else {
        !          1997:                for(int i = 0, dest = get_tvram_address(REG8(BH), co.X, co.Y); i < REG16(CX); i++) {
        !          1998:                        mem[dest++] = REG8(AL);
        !          1999:                        mem[dest++] = REG8(BL);
        !          2000:                        if(++co.X == 80) {
        !          2001:                                if(++co.Y == 25) {
        !          2002:                                        break;
        !          2003:                                }
        !          2004:                                co.X = 0;
        !          2005:                        }
        !          2006:                }
        !          2007:        }
        !          2008: }
        !          2009: 
        !          2010: inline void pcbios_int_10h_0ah()
        !          2011: {
        !          2012:        COORD co;
        !          2013:        DWORD num;
        !          2014:        
        !          2015:        co.X = mem[0x450 + (REG8(BH) & 7) * 2];
        !          2016:        co.Y = mem[0x451 + (REG8(BH) & 7) * 2];
        !          2017:        
        !          2018:        if(mem[0x462] == REG8(BH)) {
        !          2019:                for(int i = 0; i < REG16(CX) && i < 80 * 25; i++) {
        !          2020:                        scr_char[i] = REG8(AL);
        !          2021: //                     scr_attr[i] = REG8(BL);
        !          2022:                }
        !          2023:                WriteConsoleOutputCharacter(hStdout, scr_char, REG16(CX), co, &num);
        !          2024: //             WriteConsoleOutputAttribute(hStdout, scr_attr, REG16(CX), co, &num);
        !          2025:        } else {
        !          2026:                for(int i = 0, dest = get_tvram_address(REG8(BH), co.X, co.Y); i < REG16(CX); i++) {
        !          2027:                        mem[dest++] = REG8(AL);
        !          2028: //                     mem[dest++] = REG8(BL);
        !          2029:                        dest++;
        !          2030:                        if(++co.X == 80) {
        !          2031:                                if(++co.Y == 25) {
        !          2032:                                        break;
        !          2033:                                }
        !          2034:                                co.X = 0;
        !          2035:                        }
        !          2036:                }
        !          2037:        }
        !          2038: }
        !          2039: 
        !          2040: inline void pcbios_int_10h_0eh()
        !          2041: {
        !          2042:        msdos_putch(REG8(AL));
        !          2043: }
        !          2044: 
        !          2045: inline void pcbios_int_10h_0fh()
        !          2046: {
        !          2047:        REG8(AL) = mem[0x449];
        !          2048:        REG8(AH) = mem[0x44a];
        !          2049:        REG8(BH) = mem[0x462];
        !          2050: }
        !          2051: 
        !          2052: inline void pcbios_int_10h_13h()
        !          2053: {
        !          2054:        int ofs = cpustate->sreg[ES].base + REG16(BP);
        !          2055:        COORD co;
        !          2056:        DWORD num;
        !          2057:        
        !          2058:        co.X = REG8(DL);
        !          2059:        co.Y = REG8(DH);
        !          2060:        
        !          2061:        switch(REG8(AL)) {
        !          2062:        case 0x00:
        !          2063:        case 0x01:
        !          2064:                if(mem[0x462] == REG8(BH)) {
        !          2065:                        CONSOLE_SCREEN_BUFFER_INFO csbi;
        !          2066:                        GetConsoleScreenBufferInfo(hStdout, &csbi);
        !          2067:                        SetConsoleCursorPosition(hStdout, co);
        !          2068:                        
        !          2069:                        if(csbi.wAttributes != REG8(BL)) {
        !          2070:                                SetConsoleTextAttribute(hStdout, REG8(BL));
        !          2071:                        }
        !          2072:                        for(int i = 0; i < REG16(CX); i++) {
        !          2073:                                msdos_putch(mem[ofs++]);
        !          2074:                        }
        !          2075:                        if(csbi.wAttributes != REG8(BL)) {
        !          2076:                                SetConsoleTextAttribute(hStdout, csbi.wAttributes);
        !          2077:                        }
        !          2078:                        if(REG8(AL) == 0x00) {
        !          2079:                                co.X = mem[0x450 + (REG8(BH) & 7) * 2];
        !          2080:                                co.Y = mem[0x451 + (REG8(BH) & 7) * 2];
        !          2081:                                SetConsoleCursorPosition(hStdout, co);
        !          2082:                        } else {
        !          2083:                                cursor_moved = true;
        !          2084:                        }
        !          2085:                } else {
        !          2086:                        cpustate->CF = 1;
        !          2087:                }
        !          2088:                break;
        !          2089:        case 0x02:
        !          2090:        case 0x03:
        !          2091:                if(mem[0x462] == REG8(BH)) {
        !          2092:                        CONSOLE_SCREEN_BUFFER_INFO csbi;
        !          2093:                        GetConsoleScreenBufferInfo(hStdout, &csbi);
        !          2094:                        SetConsoleCursorPosition(hStdout, co);
        !          2095:                        
        !          2096:                        WORD wAttributes = csbi.wAttributes;
        !          2097:                        for(int i = 0; i < REG16(CX); i++, ofs += 2) {
        !          2098:                                if(wAttributes != mem[ofs + 1]) {
        !          2099:                                        SetConsoleTextAttribute(hStdout, mem[ofs + 1]);
        !          2100:                                        wAttributes = mem[ofs + 1];
        !          2101:                                }
        !          2102:                                msdos_putch(mem[ofs]);
        !          2103:                        }
        !          2104:                        if(csbi.wAttributes != wAttributes) {
        !          2105:                                SetConsoleTextAttribute(hStdout, csbi.wAttributes);
        !          2106:                        }
        !          2107:                        if(REG8(AL) == 0x02) {
        !          2108:                                co.X = mem[0x450 + (REG8(BH) & 7) * 2];
        !          2109:                                co.Y = mem[0x451 + (REG8(BH) & 7) * 2];
        !          2110:                                SetConsoleCursorPosition(hStdout, co);
        !          2111:                        } else {
        !          2112:                                cursor_moved = true;
        !          2113:                        }
        !          2114:                } else {
        !          2115:                        cpustate->CF = 1;
        !          2116:                }
        !          2117:                break;
        !          2118:        case 0x10:
        !          2119:        case 0x11:
        !          2120:                if(mem[0x462] == REG8(BH)) {
        !          2121:                        ReadConsoleOutputCharacter(hStdout, scr_char, REG16(CX), co, &num);
        !          2122:                        ReadConsoleOutputAttribute(hStdout, scr_attr, REG16(CX), co, &num);
        !          2123:                        for(int i = 0; i < num; i++) {
        !          2124:                                mem[ofs++] = scr_char[i];
        !          2125:                                mem[ofs++] = scr_attr[i];
        !          2126:                                if(REG8(AL) == 0x11) {
        !          2127:                                        mem[ofs++] = 0;
        !          2128:                                        mem[ofs++] = 0;
        !          2129:                                }
        !          2130:                        }
        !          2131:                } else {
        !          2132:                        for(int i = 0, src = get_tvram_address(REG8(BH), co.X, co.Y); i < REG16(CX); i++) {
        !          2133:                                mem[ofs++] = mem[src++];
        !          2134:                                mem[ofs++] = mem[src++];
        !          2135:                                if(REG8(AL) == 0x11) {
        !          2136:                                        mem[ofs++] = 0;
        !          2137:                                        mem[ofs++] = 0;
        !          2138:                                }
        !          2139:                                if(++co.X == 80) {
        !          2140:                                        if(++co.Y == 25) {
        !          2141:                                                break;
        !          2142:                                        }
        !          2143:                                        co.X = 0;
        !          2144:                                }
        !          2145:                        }
        !          2146:                }
        !          2147:                break;
        !          2148:        case 0x20:
        !          2149:        case 0x21:
        !          2150:                if(mem[0x462] == REG8(BH)) {
        !          2151:                        for(int i = 0; i < REG16(CX) && i < 80 * 25; i++) {
        !          2152:                                scr_char[i] = mem[ofs++];
        !          2153:                                scr_attr[i] = mem[ofs++];
        !          2154:                                if(REG8(AL) == 0x21) {
        !          2155:                                        ofs += 2;
        !          2156:                                }
        !          2157:                        }
        !          2158:                        WriteConsoleOutputCharacter(hStdout, scr_char, REG16(CX), co, &num);
        !          2159:                        WriteConsoleOutputAttribute(hStdout, scr_attr, REG16(CX), co, &num);
        !          2160:                } else {
        !          2161:                        for(int i = 0, dest = get_tvram_address(REG8(BH), co.X, co.Y); i < REG16(CX); i++) {
        !          2162:                                mem[dest++] = mem[ofs++];
        !          2163:                                mem[dest++] = mem[ofs++];
        !          2164:                                if(REG8(AL) == 0x21) {
        !          2165:                                        ofs += 2;
        !          2166:                                }
        !          2167:                                if(++co.X == 80) {
        !          2168:                                        if(++co.Y == 25) {
        !          2169:                                                break;
        !          2170:                                        }
        !          2171:                                        co.X = 0;
        !          2172:                                }
        !          2173:                        }
        !          2174:                }
        !          2175:                break;
        !          2176:        default:
        !          2177:                cpustate->CF = 1;
        !          2178:                break;
        !          2179:        }
        !          2180: }
        !          2181: 
        !          2182: inline void pcbios_int_10h_1dh()
        !          2183: {
        !          2184:        switch(REG8(AL)) {
        !          2185:        case 0x01:
        !          2186:                break;
        !          2187:        case 0x02:
        !          2188:                REG16(BX) = 0;
        !          2189:                break;
        !          2190:        default:
        !          2191:                cpustate->CF = 1;
        !          2192:                break;
        !          2193:        }
        !          2194: }
        !          2195: 
        !          2196: inline void pcbios_int_10h_82h()
        !          2197: {
        !          2198:        static UINT8 mode = 0;
        !          2199:        
        !          2200:        switch(REG8(AL)) {
        !          2201:        case 0:
        !          2202:                if(REG8(BL) != 0xff) {
        !          2203:                        mode = REG8(BL);
        !          2204:                }
        !          2205:                REG8(AL) = mode;
        !          2206:                break;
        !          2207:        default:
        !          2208:                cpustate->CF = 1;
        !          2209:                break;
        !          2210:        }
        !          2211: }
        !          2212: 
        !          2213: inline void pcbios_int_10h_feh()
        !          2214: {
        !          2215:        if(mem[0x449] == 0x03 || mem[0x449] == 0x70 || mem[0x449] == 0x71 || mem[0x449] == 0x73) {
        !          2216:                cpustate->sreg[ES].selector = (TVRAM_TOP >> 4);
        !          2217:                i386_load_segment_descriptor(cpustate, ES);
        !          2218:                REG16(DI) = (TVRAM_TOP & 0x0f);
        !          2219:        }
        !          2220:        int_10h_feh_called = true;
        !          2221: }
        !          2222: 
        !          2223: inline void pcbios_int_10h_ffh()
        !          2224: {
        !          2225:        if(mem[0x449] == 0x03 || mem[0x449] == 0x70 || mem[0x449] == 0x71 || mem[0x449] == 0x73) {
        !          2226:                COORD co;
        !          2227:                DWORD num;
        !          2228:                
        !          2229:                co.X = (REG16(DI) >> 1) % 80;
        !          2230:                co.Y = (REG16(DI) >> 1) / 80;
        !          2231:                for(int i = 0, ofs = get_tvram_address(0, co.X, co.Y); i < REG16(CX) && i < 80 * 25; i++) {
        !          2232:                        scr_char[i] = mem[ofs++];
        !          2233:                        scr_attr[i] = mem[ofs++];
        !          2234:                }
        !          2235:                WriteConsoleOutputCharacter(hStdout, scr_char, REG16(CX), co, &num);
        !          2236:                WriteConsoleOutputAttribute(hStdout, scr_attr, REG16(CX), co, &num);
        !          2237:        }
        !          2238:        int_10h_ffh_called = true;
        !          2239: }
        !          2240: 
        !          2241: inline void pcbios_int_15h_23h()
        !          2242: {
        !          2243:        switch(REG8(AL)) {
        !          2244:        case 0:
        !          2245:                REG8(CL) = cmos[0x2d];
        !          2246:                REG8(CH) = cmos[0x2e];
        !          2247:                break;
        !          2248:        case 1:
        !          2249:                cmos[0x2d] = REG8(CL);
        !          2250:                cmos[0x2e] = REG8(CH);
        !          2251:                break;
        !          2252:        default:
        !          2253:                REG8(AH) = 0x86;
        !          2254:                cpustate->CF = 1;
        !          2255:                break;
        !          2256:        }
        !          2257: }
        !          2258: 
        !          2259: inline void pcbios_int_15h_24h()
        !          2260: {
        !          2261:        switch(REG8(AL)) {
        !          2262:        case 0:
        !          2263:                i386_set_a20_line(cpustate, 0);
        !          2264:                REG8(AH) = 0;
        !          2265:                break;
        !          2266:        case 1:
        !          2267:                i386_set_a20_line(cpustate, 1);
        !          2268:                REG8(AH) = 0;
        !          2269:                break;
        !          2270:        case 2:
        !          2271:                REG8(AH) = 0;
        !          2272:                REG8(AL) = (cpustate->a20_mask >> 20) & 1;
        !          2273:                REG16(CX) = 0;
        !          2274:                break;
        !          2275:        case 3:
        !          2276:                REG16(AX) = 0;
        !          2277:                REG16(BX) = 0;
        !          2278:                break;
        !          2279:        }
        !          2280: }
        !          2281: 
        !          2282: inline void pcbios_int_15h_49h()
        !          2283: {
        !          2284:        REG8(AL) = 0;
        !          2285:        REG8(BL) = 0;   // DOS/V;
        !          2286: }
        !          2287: 
        !          2288: inline void pcbios_int_15h_86h()
        !          2289: {
        !          2290:        UINT32 usec = (REG16(CX) << 16) | REG16(DX);
        !          2291:        Sleep(usec / 1000);
        !          2292: }
        !          2293: 
        !          2294: inline void pcbios_int_15h_87h()
        !          2295: {
        !          2296: #if 1
        !          2297:        // copy extended memory (from DOSBox)
        !          2298:        int len = REG16(CX) * 2;
        !          2299:        int ofs = cpustate->sreg[ES].base + REG16(SI);
        !          2300:        int src = (*(UINT32 *)(mem + ofs + 0x12) & 0xffffff); // + (mem[ofs + 0x16] << 24);
        !          2301:        int dst = (*(UINT32 *)(mem + ofs + 0x1a) & 0xffffff); // + (mem[ofs + 0x1e] << 24);
        !          2302:        memcpy(mem + dst, mem + src, len);
        !          2303:        REG16(AX) = 0x00;
        !          2304: #else
        !          2305:        REG8(AH) = 0x86;
        !          2306:        cpustate->CF = 1;
        !          2307: #endif
        !          2308: }
        !          2309: 
        !          2310: inline void pcbios_int_15h_88h()
        !          2311: {
        !          2312:        REG16(AX) = ((min(MAX_MEM, 0x1000000) - 0x100000) >> 10);
        !          2313: }
        !          2314: 
        !          2315: inline void pcbios_int_15h_89h()
        !          2316: {
        !          2317: #if 1
        !          2318:        // switch to protected mode (from DOSBox)
        !          2319:        write_io_byte(0x20, 0x10);
        !          2320:        write_io_byte(0x21, REG8(BH));
        !          2321:        write_io_byte(0x21, 0x00);
        !          2322:        write_io_byte(0xa0, 0x10);
        !          2323:        write_io_byte(0xa1, REG8(BL));
        !          2324:        write_io_byte(0xa1, 0x00);
        !          2325:        i386_set_a20_line(cpustate, 1);
        !          2326:        int ofs = cpustate->sreg[ES].base + REG16(SI);
        !          2327:        cpustate->gdtr.limit = *(UINT16 *)(mem + ofs + 0x08);
        !          2328:        cpustate->gdtr.base = *(UINT32 *)(mem + ofs + 0x08 + 0x02) & 0xffffff;
        !          2329:        cpustate->idtr.limit = *(UINT16 *)(mem + ofs + 0x10);
        !          2330:        cpustate->idtr.base = *(UINT32 *)(mem + ofs + 0x10 + 0x02) & 0xffffff;
        !          2331:        cpustate->cr[0] |= 1;
        !          2332:        cpustate->sreg[DS].selector = 0x18;
        !          2333:        cpustate->sreg[ES].selector = 0x20;
        !          2334:        cpustate->sreg[SS].selector = 0x28;
        !          2335:        i386_load_segment_descriptor(cpustate, DS);
        !          2336:        i386_load_segment_descriptor(cpustate, ES);
        !          2337:        i386_load_segment_descriptor(cpustate, SS);
        !          2338:        REG16(SP) += 6;
        !          2339:        set_flags(cpustate, 0); // ???
        !          2340:        REG16(AX) = 0x00;
        !          2341:        i386_jmp_far(0x30, REG16(CX));
        !          2342: #else
        !          2343:        REG8(AH) = 0x86;
        !          2344:        cpustate->CF = 1;
        !          2345: #endif
        !          2346: }
        !          2347: 
        !          2348: inline void pcbios_int_15h_c9h()
        !          2349: {
        !          2350:        REG8(AH) = 0x00;
        !          2351:        REG8(CH) = cpu_type;
        !          2352:        REG8(CL) = cpu_step;
        !          2353: }
        !          2354: 
        !          2355: inline void pcbios_int_15h_cah()
        !          2356: {
        !          2357:        switch(REG8(AL)) {
        !          2358:        case 0:
        !          2359:                if(REG8(BL) > 0x3f) {
        !          2360:                        REG8(AH) = 0x03;
        !          2361:                        cpustate->CF = 1;
        !          2362:                } else if(REG8(BL) < 0x0e) {
        !          2363:                        REG8(AH) = 0x04;
        !          2364:                        cpustate->CF = 1;
        !          2365:                } else {
        !          2366:                        REG8(CL) = cmos[REG8(BL)];
        !          2367:                }
        !          2368:                break;
        !          2369:        case 1:
        !          2370:                if(REG8(BL) > 0x3f) {
        !          2371:                        REG8(AH) = 0x03;
        !          2372:                        cpustate->CF = 1;
        !          2373:                } else if(REG8(BL) < 0x0e) {
        !          2374:                        REG8(AH) = 0x04;
        !          2375:                        cpustate->CF = 1;
        !          2376:                } else {
        !          2377:                        cmos[REG8(BL)] = REG8(CL);
        !          2378:                }
        !          2379:                break;
        !          2380:        default:
        !          2381:                REG8(AH) = 0x86;
        !          2382:                cpustate->CF = 1;
        !          2383:                break;
        !          2384:        }
        !          2385: }
        !          2386: 
        !          2387: UINT32 get_key_code(bool clear_buffer)
        !          2388: {
        !          2389:        UINT32 code = 0;
        !          2390:        
        !          2391:        if(key_buf_char->count() == 0) {
        !          2392:                update_key_buffer();
        !          2393:        }
        !          2394:        if(!clear_buffer) {
        !          2395:                key_buf_char->store_buffer();
        !          2396:                key_buf_scan->store_buffer();
        !          2397:        }
        !          2398:        if(key_buf_char->count() != 0) {
        !          2399:                code = key_buf_char->read() | (key_buf_scan->read() << 8);
        !          2400:        }
        !          2401:        if(key_buf_char->count() != 0) {
        !          2402:                code |= (key_buf_char->read() << 16) | (key_buf_scan->read() << 24);
        !          2403:        }
        !          2404:        if(!clear_buffer) {
        !          2405:                key_buf_char->restore_buffer();
        !          2406:                key_buf_scan->restore_buffer();
        !          2407:        }
        !          2408:        return code;
        !          2409: }
        !          2410: 
        !          2411: inline void pcbios_int_16h_00h()
        !          2412: {
        !          2413:        static UINT32 key_code = 0;
        !          2414:        
        !          2415:        while(key_code == 0) {
        !          2416:                key_code = get_key_code(true);
        !          2417:        }
        !          2418:        if((key_code & 0xffff) == 0x0000 || (key_code & 0xffff) == 0xe000) {
        !          2419:                if(REG8(AH) == 0x10) {
        !          2420:                        key_code = ((key_code >> 8) & 0xff) | ((key_code >> 16) & 0xff00);
        !          2421:                } else {
        !          2422:                        key_code = ((key_code >> 16) & 0xff00);
        !          2423:                }
        !          2424:        }
        !          2425:        REG16(AX) = key_code & 0xffff;
        !          2426:        key_code >>= 16;
        !          2427: }
        !          2428: 
        !          2429: inline void pcbios_int_16h_01h()
        !          2430: {
        !          2431:        UINT32 key_code = get_key_code(false);
        !          2432:        
        !          2433:        if(key_code != 0) {
        !          2434:                if((key_code & 0xffff) == 0x0000 || (key_code & 0xffff) == 0xe000) {
        !          2435:                        if(REG8(AH) == 0x11) {
        !          2436:                                key_code = ((key_code >> 8) & 0xff) | ((key_code >> 16) & 0xff00);
        !          2437:                        } else {
        !          2438:                                key_code = ((key_code >> 16) & 0xff00);
        !          2439:                        }
        !          2440:                }
        !          2441:        }
        !          2442:        if(key_code != 0) {
        !          2443:                REG16(AX) = key_code & 0xffff;
        !          2444:        }
        !          2445:        cpustate->ZF = (key_code == 0);
        !          2446: }
        !          2447: 
        !          2448: inline void pcbios_int_16h_02h()
        !          2449: {
        !          2450:        REG8(AL)  = (GetAsyncKeyState(VK_INSERT ) & 0x0001) ? 0x80 : 0;
        !          2451:        REG8(AL) |= (GetAsyncKeyState(VK_CAPITAL) & 0x0001) ? 0x40 : 0;
        !          2452:        REG8(AL) |= (GetAsyncKeyState(VK_NUMLOCK) & 0x0001) ? 0x20 : 0;
        !          2453:        REG8(AL) |= (GetAsyncKeyState(VK_SCROLL ) & 0x0001) ? 0x10 : 0;
        !          2454:        REG8(AL) |= (GetAsyncKeyState(VK_MENU   ) & 0x8000) ? 0x08 : 0;
        !          2455:        REG8(AL) |= (GetAsyncKeyState(VK_CONTROL) & 0x8000) ? 0x04 : 0;
        !          2456:        REG8(AL) |= (GetAsyncKeyState(VK_LSHIFT ) & 0x8000) ? 0x02 : 0;
        !          2457:        REG8(AL) |= (GetAsyncKeyState(VK_RSHIFT ) & 0x8000) ? 0x01 : 0;
        !          2458: }
        !          2459: 
        !          2460: inline void pcbios_int_16h_03h()
        !          2461: {
        !          2462:        static UINT16 status = 0;
        !          2463:        
        !          2464:        switch(REG8(AL)) {
        !          2465:        case 0x05:
        !          2466:                status = REG16(BX);
        !          2467:                break;
        !          2468:        case 0x06:
        !          2469:                REG16(BX) = status;
        !          2470:                break;
        !          2471:        default:
        !          2472:                cpustate->CF = 1;
        !          2473:                break;
        !          2474:        }
        !          2475: }
        !          2476: 
        !          2477: inline void pcbios_int_16h_05h()
        !          2478: {
        !          2479:        _ungetch(REG8(CL));
        !          2480:        REG8(AL) = 0x00;
        !          2481: }
        !          2482: 
        !          2483: inline void pcbios_int_16h_12h()
        !          2484: {
        !          2485:        pcbios_int_16h_02h();
        !          2486:        
        !          2487:        REG8(AH)  = 0;//(GetAsyncKeyState(VK_SYSREQ  ) & 0x8000) ? 0x80 : 0;
        !          2488:        REG8(AH) |= (GetAsyncKeyState(VK_CAPITAL ) & 0x8000) ? 0x40 : 0;
        !          2489:        REG8(AH) |= (GetAsyncKeyState(VK_NUMLOCK ) & 0x8000) ? 0x20 : 0;
        !          2490:        REG8(AH) |= (GetAsyncKeyState(VK_SCROLL  ) & 0x8000) ? 0x10 : 0;
        !          2491:        REG8(AH) |= (GetAsyncKeyState(VK_RMENU   ) & 0x8000) ? 0x08 : 0;
        !          2492:        REG8(AH) |= (GetAsyncKeyState(VK_RCONTROL) & 0x8000) ? 0x04 : 0;
        !          2493:        REG8(AH) |= (GetAsyncKeyState(VK_LMENU   ) & 0x8000) ? 0x02 : 0;
        !          2494:        REG8(AH) |= (GetAsyncKeyState(VK_LCONTROL) & 0x8000) ? 0x01 : 0;
        !          2495: }
        !          2496: 
        !          2497: inline void pcbios_int_16h_13h()
        !          2498: {
        !          2499:        static UINT16 status = 0;
        !          2500:        
        !          2501:        switch(REG8(AL)) {
        !          2502:        case 0x00:
        !          2503:                status = REG16(DX);
        !          2504:                break;
        !          2505:        case 0x01:
        !          2506:                REG16(DX) = status;
        !          2507:                break;
        !          2508:        default:
        !          2509:                cpustate->CF = 1;
        !          2510:                break;
        !          2511:        }
        !          2512: }
        !          2513: 
        !          2514: inline void pcbios_int_16h_14h()
        !          2515: {
        !          2516:        static UINT8 status = 0;
        !          2517:        
        !          2518:        switch(REG8(AL)) {
        !          2519:        case 0x00:
        !          2520:        case 0x01:
        !          2521:                status = REG8(AL);
        !          2522:                break;
        !          2523:        case 0x02:
        !          2524:                REG8(AL) = status;
        !          2525:                break;
        !          2526:        default:
        !          2527:                cpustate->CF = 1;
        !          2528:                break;
        !          2529:        }
        !          2530: }
        !          2531: 
        !          2532: inline void pcbios_int_1ah_00h()
        !          2533: {
        !          2534:        static WORD prev_day = 0;
        !          2535:        SYSTEMTIME time;
        !          2536:        
        !          2537:        GetLocalTime(&time);
        !          2538:        unsigned __int64 msec = ((time.wHour * 60 + time.wMinute) * 60 + time.wSecond) * 1000 + time.wMilliseconds;
        !          2539:        unsigned __int64 tick = msec * 0x1800b0 / 24 / 60 / 60 / 1000;
        !          2540:        REG16(CX) = (tick >> 16) & 0xffff;
        !          2541:        REG16(DX) = (tick      ) & 0xffff;
        !          2542:        REG8(AL) = (prev_day != 0 && prev_day != time.wDay) ? 1 : 0;
        !          2543:        prev_day = time.wDay;
        !          2544: }
        !          2545: 
        !          2546: inline int to_bcd(int t)
        !          2547: {
        !          2548:        int u = (t % 100) / 10;
        !          2549:        return (u << 4) | (t % 10);
        !          2550: }
        !          2551: 
        !          2552: inline void pcbios_int_1ah_02h()
        !          2553: {
        !          2554:        SYSTEMTIME time;
        !          2555:        
        !          2556:        GetLocalTime(&time);
        !          2557:        REG8(CH) = to_bcd(time.wHour);
        !          2558:        REG8(CL) = to_bcd(time.wMinute);
        !          2559:        REG8(DH) = to_bcd(time.wSecond);
        !          2560:        REG8(DL) = 0x00;
        !          2561: }
        !          2562: 
        !          2563: inline void pcbios_int_1ah_04h()
        !          2564: {
        !          2565:        SYSTEMTIME time;
        !          2566:        
        !          2567:        GetLocalTime(&time);
        !          2568:        REG8(CH) = to_bcd(time.wYear / 100);
        !          2569:        REG8(CL) = to_bcd(time.wYear);
        !          2570:        REG8(DH) = to_bcd(time.wMonth);
        !          2571:        REG8(DL) = to_bcd(time.wDay);
        !          2572: }
        !          2573: 
        !          2574: inline void pcbios_int_1ah_0ah()
        !          2575: {
        !          2576:        SYSTEMTIME time;
        !          2577:        FILETIME file_time;
        !          2578:        WORD dos_date, dos_time;
        !          2579:        
        !          2580:        GetLocalTime(&time);
        !          2581:        SystemTimeToFileTime(&time, &file_time);
        !          2582:        FileTimeToDosDateTime(&file_time, &dos_date, &dos_time);
        !          2583:        REG16(CX) = dos_date;
        !          2584: }
        !          2585: 
        !          2586: // msdos system call
        !          2587: 
        !          2588: inline void msdos_int_21h_00h()
        !          2589: {
        !          2590:        msdos_process_terminate(cpustate->sreg[CS].selector, retval, 1);
        !          2591: }
        !          2592: 
        !          2593: inline void msdos_int_21h_01h()
        !          2594: {
        !          2595:        REG8(AL) = msdos_getche();
        !          2596: #ifdef SUPPORT_HARDWARE
        !          2597:        hardware_update();
        !          2598: #endif
        !          2599: }
        !          2600: 
        !          2601: inline void msdos_int_21h_02h()
        !          2602: {
        !          2603:        msdos_putch(REG8(DL));
        !          2604: }
        !          2605: 
        !          2606: inline void msdos_int_21h_03h()
        !          2607: {
        !          2608:        REG8(AL) = msdos_aux_in();
        !          2609: }
        !          2610: 
        !          2611: inline void msdos_int_21h_04h()
        !          2612: {
        !          2613:        msdos_aux_out(REG8(DL));
        !          2614: }
        !          2615: 
        !          2616: inline void msdos_int_21h_05h()
        !          2617: {
        !          2618:        msdos_prn_out(REG8(DL));
        !          2619: }
        !          2620: 
        !          2621: inline void msdos_int_21h_06h()
        !          2622: {
        !          2623:        if(REG8(DL) == 0xff) {
        !          2624:                if(msdos_kbhit()) {
        !          2625:                        REG8(AL) = msdos_getch();
        !          2626:                        cpustate->ZF = 0;
        !          2627:                } else {
        !          2628:                        REG8(AL) = 0;
        !          2629:                        cpustate->ZF = 1;
        !          2630:                        Sleep(10);
        !          2631:                }
        !          2632:        } else {
        !          2633:                msdos_putch(REG8(DL));
        !          2634:        }
        !          2635: }
        !          2636: 
        !          2637: inline void msdos_int_21h_07h()
        !          2638: {
        !          2639:        REG8(AL) = msdos_getch();
        !          2640: #ifdef SUPPORT_HARDWARE
        !          2641:        hardware_update();
        !          2642: #endif
        !          2643: }
        !          2644: 
        !          2645: inline void msdos_int_21h_08h()
        !          2646: {
        !          2647:        REG8(AL) = msdos_getch();
        !          2648: #ifdef SUPPORT_HARDWARE
        !          2649:        hardware_update();
        !          2650: #endif
        !          2651: }
        !          2652: 
        !          2653: inline void msdos_int_21h_09h()
        !          2654: {
        !          2655:        char tmp[0x10000];
        !          2656:        memcpy(tmp, mem + cpustate->sreg[DS].base + REG16(DX), sizeof(tmp));
        !          2657:        tmp[sizeof(tmp) - 1] = '\0';
        !          2658:        int len = strlen(my_strtok(tmp, "$"));
        !          2659:        
        !          2660:        if(file_handler[1].valid && !file_handler[1].atty) {
        !          2661:                // stdout is redirected to file
        !          2662:                msdos_write(1, tmp, len);
        !          2663:        } else {
        !          2664:                for(int i = 0; i < len; i++) {
        !          2665:                        msdos_putch(tmp[i]);
        !          2666:                }
        !          2667:        }
        !          2668: }
        !          2669: 
        !          2670: inline void msdos_int_21h_0ah()
        !          2671: {
        !          2672:        int ofs = cpustate->sreg[DS].base + REG16(DX);
        !          2673:        int max = mem[ofs] - 1;
        !          2674:        UINT8 *buf = mem + ofs + 2;
        !          2675:        int chr, p = 0;
        !          2676:        
        !          2677:        while((chr = msdos_getch()) != 0x0d) {
        !          2678:                if(chr == 0x00) {
        !          2679:                        // skip 2nd byte
        !          2680:                        msdos_getch();
        !          2681:                } else if(chr == 0x08) {
        !          2682:                        // back space
        !          2683:                        if(p > 0) {
        !          2684:                                p--;
        !          2685:                                msdos_putch(chr);
        !          2686:                                msdos_putch(' ');
        !          2687:                                msdos_putch(chr);
        !          2688:                        }
        !          2689:                } else if(p < max) {
        !          2690:                        buf[p++] = chr;
        !          2691:                        msdos_putch(chr);
        !          2692:                }
        !          2693:        }
        !          2694:        buf[p] = 0x0d;
        !          2695:        mem[ofs + 1] = p;
        !          2696: #ifdef SUPPORT_HARDWARE
        !          2697:        hardware_update();
        !          2698: #endif
        !          2699: }
        !          2700: 
        !          2701: inline void msdos_int_21h_0bh()
        !          2702: {
        !          2703:        if(msdos_kbhit()) {
        !          2704:                REG8(AL) = 0xff;
        !          2705:        } else {
        !          2706:                REG8(AL) = 0x00;
        !          2707:                Sleep(10);
        !          2708:        }
        !          2709: }
        !          2710: 
        !          2711: inline void msdos_int_21h_0ch()
        !          2712: {
        !          2713:        // clear key buffer
        !          2714:        if(file_handler[0].valid && !file_handler[0].atty) {
        !          2715:                // stdin is redirected to file
        !          2716:        } else {
        !          2717:                while(msdos_kbhit()) {
        !          2718:                        msdos_getch();
        !          2719:                }
        !          2720:        }
        !          2721:        
        !          2722:        switch(REG8(AL)) {
        !          2723:        case 0x01:
        !          2724:                msdos_int_21h_01h();
        !          2725:                break;
        !          2726:        case 0x06:
        !          2727:                msdos_int_21h_06h();
        !          2728:                break;
        !          2729:        case 0x07:
        !          2730:                msdos_int_21h_07h();
        !          2731:                break;
        !          2732:        case 0x08:
        !          2733:                msdos_int_21h_08h();
        !          2734:                break;
        !          2735:        case 0x0a:
        !          2736:                msdos_int_21h_0ah();
        !          2737:                break;
        !          2738:        default:
        !          2739:                REG16(AX) = 0x01;
        !          2740:                cpustate->CF = 1;
        !          2741:                break;
        !          2742:        }
        !          2743: }
        !          2744: 
        !          2745: inline void msdos_int_21h_0dh()
        !          2746: {
        !          2747: }
        !          2748: 
        !          2749: inline void msdos_int_21h_0eh()
        !          2750: {
        !          2751:        if(REG8(DL) < 26) {
        !          2752:                _chdrive(REG8(DL) + 1);
        !          2753:                msdos_cds_update(REG8(DL));
        !          2754:        }
        !          2755:        REG8(AL) = 26; // zdrive
        !          2756: }
        !          2757: 
        !          2758: inline void msdos_int_21h_11h()
        !          2759: {
        !          2760:        ext_fcb_t *ext_fcb = (ext_fcb_t *)(mem + cpustate->sreg[DS].base + REG16(DX));
        !          2761:        fcb_t *fcb = (fcb_t *)(mem + cpustate->sreg[DS].base + REG16(DX) + (ext_fcb->flag == 0xff ? 7 : 0));
        !          2762:        
        !          2763:        process_t *process = msdos_process_info_get(current_psp);
        !          2764:        ext_fcb_t *ext_find = (ext_fcb_t *)(mem + (process->dta.w.h << 4) + process->dta.w.l);
        !          2765:        find_fcb_t *find = (find_fcb_t *)(mem + (process->dta.w.h << 4) + process->dta.w.l + (ext_fcb->flag == 0xff ? 7 : 0));
        !          2766:        char *path = msdos_fcb_path(fcb);
        !          2767:        WIN32_FIND_DATA fd;
        !          2768:        
        !          2769:        if(process->find_handle != INVALID_HANDLE_VALUE) {
        !          2770:                FindClose(process->find_handle);
        !          2771:                process->find_handle = INVALID_HANDLE_VALUE;
        !          2772:        }
        !          2773:        strcpy(process->volume_label, msdos_volume_label(path));
        !          2774:        process->allowable_mask = (ext_fcb->flag == 0xff) ? ext_fcb->attribute : 0x20;
        !          2775:        
        !          2776:        if((process->allowable_mask & 8) && !msdos_match_volume_label(path, msdos_short_volume_label(process->volume_label))) {
        !          2777:                process->allowable_mask &= ~8;
        !          2778:        }
        !          2779:        if((process->find_handle = FindFirstFile(path, &fd)) != INVALID_HANDLE_VALUE) {
        !          2780:                while(!msdos_find_file_check_attribute(fd.dwFileAttributes, process->allowable_mask, 0)) {
        !          2781:                        if(!FindNextFile(process->find_handle, &fd)) {
        !          2782:                                FindClose(process->find_handle);
        !          2783:                                process->find_handle = INVALID_HANDLE_VALUE;
        !          2784:                                break;
        !          2785:                        }
        !          2786:                }
        !          2787:        }
        !          2788:        if(process->find_handle != INVALID_HANDLE_VALUE) {
        !          2789:                if(ext_fcb->flag == 0xff) {
        !          2790:                        ext_find->flag = 0xff;
        !          2791:                        memset(ext_find->reserved, 0, 5);
        !          2792:                        ext_find->attribute = (UINT8)(fd.dwFileAttributes & 0x3f);
        !          2793:                }
        !          2794:                find->drive = _getdrive();
        !          2795:                msdos_set_fcb_path((fcb_t *)find, msdos_short_path(fd.cFileName));
        !          2796:                find->attribute = (UINT8)(fd.dwFileAttributes & 0x3f);
        !          2797:                find->nt_res = 0;
        !          2798:                msdos_find_file_conv_local_time(&fd);
        !          2799:                find->create_time_ms = 0;
        !          2800:                FileTimeToDosDateTime(&fd.ftCreationTime, &find->creation_date, &find->creation_time);
        !          2801:                FileTimeToDosDateTime(&fd.ftLastAccessTime, &find->last_access_date, &find->last_write_time);
        !          2802:                FileTimeToDosDateTime(&fd.ftLastWriteTime, &find->last_write_date, &find->last_write_time);
        !          2803:                find->cluster_hi = find->cluster_lo = 0;
        !          2804:                find->file_size = fd.nFileSizeLow;
        !          2805:                REG8(AL) = 0x00;
        !          2806:        } else if(process->allowable_mask & 8) {
        !          2807:                if(ext_fcb->flag == 0xff) {
        !          2808:                        ext_find->flag = 0xff;
        !          2809:                        memset(ext_find->reserved, 0, 5);
        !          2810:                        ext_find->attribute = 8;
        !          2811:                }
        !          2812:                find->drive = _getdrive();
        !          2813:                msdos_set_fcb_path((fcb_t *)find, msdos_short_volume_label(process->volume_label));
        !          2814:                find->attribute = 8;
        !          2815:                find->nt_res = 0;
        !          2816:                msdos_find_file_conv_local_time(&fd);
        !          2817:                find->create_time_ms = 0;
        !          2818:                FileTimeToDosDateTime(&fd.ftCreationTime, &find->creation_date, &find->creation_time);
        !          2819:                FileTimeToDosDateTime(&fd.ftLastAccessTime, &find->last_access_date, &find->last_write_time);
        !          2820:                FileTimeToDosDateTime(&fd.ftLastWriteTime, &find->last_write_date, &find->last_write_time);
        !          2821:                find->cluster_hi = find->cluster_lo = 0;
        !          2822:                find->file_size = 0;
        !          2823:                process->allowable_mask &= ~8;
        !          2824:                REG8(AL) = 0x00;
        !          2825:        } else {
        !          2826:                REG8(AL) = 0xff;
        !          2827:        }
        !          2828: }
        !          2829: 
        !          2830: inline void msdos_int_21h_12h()
        !          2831: {
        !          2832:        ext_fcb_t *ext_fcb = (ext_fcb_t *)(mem + cpustate->sreg[DS].base + REG16(DX));
        !          2833:        fcb_t *fcb = (fcb_t *)(mem + cpustate->sreg[DS].base + REG16(DX) + (ext_fcb->flag == 0xff ? 7 : 0));
        !          2834:        
        !          2835:        process_t *process = msdos_process_info_get(current_psp);
        !          2836:        ext_fcb_t *ext_find = (ext_fcb_t *)(mem + (process->dta.w.h << 4) + process->dta.w.l);
        !          2837:        find_fcb_t *find = (find_fcb_t *)(mem + (process->dta.w.h << 4) + process->dta.w.l + (ext_fcb->flag == 0xff ? 7 : 0));
        !          2838:        WIN32_FIND_DATA fd;
        !          2839:        
        !          2840:        if(process->find_handle != INVALID_HANDLE_VALUE) {
        !          2841:                if(FindNextFile(process->find_handle, &fd)) {
        !          2842:                        while(!msdos_find_file_check_attribute(fd.dwFileAttributes, process->allowable_mask, 0)) {
        !          2843:                                if(!FindNextFile(process->find_handle, &fd)) {
        !          2844:                                        FindClose(process->find_handle);
        !          2845:                                        process->find_handle = INVALID_HANDLE_VALUE;
        !          2846:                                        break;
        !          2847:                                }
        !          2848:                        }
        !          2849:                } else {
        !          2850:                        FindClose(process->find_handle);
        !          2851:                        process->find_handle = INVALID_HANDLE_VALUE;
        !          2852:                }
        !          2853:        }
        !          2854:        if(process->find_handle != INVALID_HANDLE_VALUE) {
        !          2855:                if(ext_fcb->flag == 0xff) {
        !          2856:                        ext_find->flag = 0xff;
        !          2857:                        memset(ext_find->reserved, 0, 5);
        !          2858:                        ext_find->attribute = (UINT8)(fd.dwFileAttributes & 0x3f);
        !          2859:                }
        !          2860:                find->drive = _getdrive();
        !          2861:                msdos_set_fcb_path((fcb_t *)find, msdos_short_path(fd.cFileName));
        !          2862:                find->attribute = (UINT8)(fd.dwFileAttributes & 0x3f);
        !          2863:                find->nt_res = 0;
        !          2864:                msdos_find_file_conv_local_time(&fd);
        !          2865:                find->create_time_ms = 0;
        !          2866:                FileTimeToDosDateTime(&fd.ftCreationTime, &find->creation_date, &find->creation_time);
        !          2867:                FileTimeToDosDateTime(&fd.ftLastAccessTime, &find->last_access_date, &find->last_write_time);
        !          2868:                FileTimeToDosDateTime(&fd.ftLastWriteTime, &find->last_write_date, &find->last_write_time);
        !          2869:                find->cluster_hi = find->cluster_lo = 0;
        !          2870:                find->file_size = fd.nFileSizeLow;
        !          2871:                REG8(AL) = 0x00;
        !          2872:        } else if(process->allowable_mask & 8) {
        !          2873:                if(ext_fcb->flag == 0xff) {
        !          2874:                        ext_find->flag = 0xff;
        !          2875:                        memset(ext_find->reserved, 0, 5);
        !          2876:                        ext_find->attribute = 8;
        !          2877:                }
        !          2878:                find->drive = _getdrive();
        !          2879:                msdos_set_fcb_path((fcb_t *)find, msdos_short_volume_label(process->volume_label));
        !          2880:                find->attribute = 8;
        !          2881:                find->nt_res = 0;
        !          2882:                msdos_find_file_conv_local_time(&fd);
        !          2883:                find->create_time_ms = 0;
        !          2884:                FileTimeToDosDateTime(&fd.ftCreationTime, &find->creation_date, &find->creation_time);
        !          2885:                FileTimeToDosDateTime(&fd.ftLastAccessTime, &find->last_access_date, &find->last_write_time);
        !          2886:                FileTimeToDosDateTime(&fd.ftLastWriteTime, &find->last_write_date, &find->last_write_time);
        !          2887:                find->cluster_hi = find->cluster_lo = 0;
        !          2888:                find->file_size = 0;
        !          2889:                process->allowable_mask &= ~8;
        !          2890:                REG8(AL) = 0x00;
        !          2891:        } else {
        !          2892:                REG8(AL) = 0xff;
        !          2893:        }
        !          2894: }
        !          2895: 
        !          2896: inline void msdos_int_21h_13h()
        !          2897: {
        !          2898:        if(remove(msdos_fcb_path((fcb_t *)(mem + cpustate->sreg[DS].base + REG16(DX))))) {
        !          2899:                REG8(AL) = 0xff;
        !          2900:        } else {
        !          2901:                REG8(AL) = 0x00;
        !          2902:        }
        !          2903: }
        !          2904: 
        !          2905: inline void msdos_int_21h_18h()
        !          2906: {
        !          2907:        REG8(AL) = 0x00;
        !          2908: }
        !          2909: 
        !          2910: inline void msdos_int_21h_19h()
        !          2911: {
        !          2912:        REG8(AL) = _getdrive() - 1;
        !          2913: }
        !          2914: 
        !          2915: inline void msdos_int_21h_1ah()
        !          2916: {
        !          2917:        process_t *process = msdos_process_info_get(current_psp);
        !          2918:        
        !          2919:        process->dta.w.l = REG16(DX);
        !          2920:        process->dta.w.h = cpustate->sreg[DS].selector;
        !          2921: }
        !          2922: 
        !          2923: inline void msdos_int_21h_1bh()
        !          2924: {
        !          2925:        int drive_num = _getdrive() - 1;
        !          2926:        UINT16 seg, ofs;
        !          2927:        
        !          2928:        if(msdos_drive_param_block_update(drive_num, &seg, &ofs, 1)) {
        !          2929:                dpb_t *dpb = (dpb_t *)(mem + (seg << 4) + ofs);
        !          2930:                REG8(AL) = dpb->highest_sector_num + 1;
        !          2931:                REG16(CX) = dpb->bytes_per_sector;
        !          2932:                REG16(DX) = dpb->highest_cluster_num - 1;
        !          2933:                *(UINT8 *)(mem + cpustate->sreg[DS].base + REG16(BX)) = dpb->media_type;
        !          2934:        } else {
        !          2935:                REG8(AL) = 0xff;
        !          2936:                cpustate->CF = 1;
        !          2937:        }
        !          2938: 
        !          2939: }
        !          2940: 
        !          2941: inline void msdos_int_21h_1ch()
        !          2942: {
        !          2943:        int drive_num = REG8(DL) ? (REG8(DL) - 1) : (_getdrive() - 1);
        !          2944:        UINT16 seg, ofs;
        !          2945:        
        !          2946:        if(msdos_drive_param_block_update(drive_num, &seg, &ofs, 1)) {
        !          2947:                dpb_t *dpb = (dpb_t *)(mem + (seg << 4) + ofs);
        !          2948:                REG8(AL) = dpb->highest_sector_num + 1;
        !          2949:                REG16(CX) = dpb->bytes_per_sector;
        !          2950:                REG16(DX) = dpb->highest_cluster_num - 1;
        !          2951:                *(UINT8 *)(mem + cpustate->sreg[DS].base + REG16(BX)) = dpb->media_type;
        !          2952:        } else {
        !          2953:                REG8(AL) = 0xff;
        !          2954:                cpustate->CF = 1;
        !          2955:        }
        !          2956: 
        !          2957: }
        !          2958: 
        !          2959: inline void msdos_int_21h_1dh()
        !          2960: {
        !          2961:        REG8(AL) = 0;
        !          2962: }
        !          2963: 
        !          2964: inline void msdos_int_21h_1eh()
        !          2965: {
        !          2966:        REG8(AL) = 0;
        !          2967: }
        !          2968: 
        !          2969: inline void msdos_int_21h_1fh()
        !          2970: {
        !          2971:        int drive_num = _getdrive() - 1;
        !          2972:        UINT16 seg, ofs;
        !          2973:        
        !          2974:        if(msdos_drive_param_block_update(drive_num, &seg, &ofs, 1)) {
        !          2975:                REG8(AL) = 0;
        !          2976:                cpustate->sreg[DS].selector = seg;
        !          2977:                i386_load_segment_descriptor(cpustate, DS);
        !          2978:                REG16(BX) = ofs;
        !          2979:        } else {
        !          2980:                REG8(AL) = 0xff;
        !          2981:                cpustate->CF = 1;
        !          2982:        }
        !          2983: }
        !          2984: 
        !          2985: inline void msdos_int_21h_20h()
        !          2986: {
        !          2987:        REG8(AL) = 0;
        !          2988: }
        !          2989: 
        !          2990: inline void msdos_int_21h_25h()
        !          2991: {
        !          2992:        *(UINT16 *)(mem + 4 * REG8(AL) + 0) = REG16(DX);
        !          2993:        *(UINT16 *)(mem + 4 * REG8(AL) + 2) = cpustate->sreg[DS].selector;
        !          2994: }
        !          2995: 
        !          2996: inline void msdos_int_21h_26h()
        !          2997: {
        !          2998:        psp_t *psp = (psp_t *)(mem + (REG16(DX) << 4));
        !          2999:        
        !          3000:        memcpy(mem + (REG16(DX) << 4), mem + (current_psp << 4), sizeof(psp_t));
        !          3001:        psp->first_mcb = REG16(DX) + 16;
        !          3002:        psp->int_22h.dw = *(UINT32 *)(mem + 4 * 0x22);
        !          3003:        psp->int_23h.dw = *(UINT32 *)(mem + 4 * 0x23);
        !          3004:        psp->int_24h.dw = *(UINT32 *)(mem + 4 * 0x24);
        !          3005:        psp->parent_psp = 0;
        !          3006: }
        !          3007: 
        !          3008: inline void msdos_int_21h_29h()
        !          3009: {
        !          3010:        int ofs = cpustate->sreg[DS].base + REG16(SI);
        !          3011:        char name[MAX_PATH], ext[MAX_PATH];
        !          3012:        UINT8 drv = 0;
        !          3013:        char sep_chars[] = ":.;,=+";
        !          3014:        char end_chars[] = "\\<>|/\"[]";
        !          3015:        char spc_chars[] = " \t";
        !          3016:        
        !          3017:        if(REG8(AL) & 1) {
        !          3018:                ofs += strspn((char *)&mem[ofs], spc_chars);
        !          3019:                if(my_strchr(sep_chars, mem[ofs]) && mem[ofs] != '\0') {
        !          3020:                        ofs++;
        !          3021:                }
        !          3022:        }
        !          3023:        ofs += strspn((char *)&mem[ofs], spc_chars);
        !          3024:        
        !          3025:        if(mem[ofs + 1] == ':') {
        !          3026:                UINT8 c = mem[ofs];
        !          3027:                if(c <= 0x20 || my_strchr(end_chars, c) || my_strchr(sep_chars, c)) {
        !          3028:                        ; // invalid drive letter
        !          3029:                } else {
        !          3030:                        if(c >= 'a' && c <= 'z') {
        !          3031:                                drv = c - 'a' + 1;
        !          3032:                        } else {
        !          3033:                                drv = c - 'A' + 1;
        !          3034:                        }
        !          3035:                        ofs += 2;
        !          3036:                }
        !          3037:        }
        !          3038:        memset(name, 0x20, sizeof(name));
        !          3039:        memset(ext, 0x20, sizeof(ext));
        !          3040:        for(int i = 0; i < MAX_PATH; i++) {
        !          3041:                UINT8 c = mem[ofs];
        !          3042:                if(c <= 0x20 || my_strchr(end_chars, c) || my_strchr(sep_chars, c)) {
        !          3043:                        break;
        !          3044:                } else if(c >= 'a' && c <= 'z') {
        !          3045:                        c -= 0x20;
        !          3046:                }
        !          3047:                ofs++;
        !          3048:                name[i] = c;
        !          3049:        }
        !          3050:        if(mem[ofs] == '.') {
        !          3051:                ofs++;
        !          3052:                for(int i = 0; i < MAX_PATH; i++) {
        !          3053:                        UINT8 c = mem[ofs];
        !          3054:                        if(c <= 0x20 || my_strchr(end_chars, c) || my_strchr(sep_chars, c)) {
        !          3055:                                break;
        !          3056:                        } else if(c >= 'a' && c <= 'z') {
        !          3057:                                c -= 0x20;
        !          3058:                        }
        !          3059:                        ofs++;
        !          3060:                        ext[i] = c;
        !          3061:                }
        !          3062:        }
        !          3063:        int si = ofs - cpustate->sreg[DS].base;
        !          3064:        int ds = cpustate->sreg[DS].selector;
        !          3065:        while(si > 0xffff) {
        !          3066:                si -= 0x10;
        !          3067:                ds++;
        !          3068:        }
        !          3069:        REG16(SI) = si;
        !          3070:        cpustate->sreg[DS].selector = ds;
        !          3071:        i386_load_segment_descriptor(cpustate, DS);
        !          3072:        
        !          3073:        UINT8 *fcb = mem + cpustate->sreg[ES].base + REG16(DI);
        !          3074:        fcb[0] = drv;
        !          3075:        memcpy(fcb + 1, name, 8);
        !          3076:        int found_star = 0;
        !          3077:        for(int i = 1; i < 1 + 8; i++) {
        !          3078:                if(fcb[i] == '*') {
        !          3079:                        found_star = 1;
        !          3080:                }
        !          3081:                if(found_star) {
        !          3082:                        fcb[i] = '?';
        !          3083:                }
        !          3084:        }
        !          3085:        memcpy(fcb + 9, ext, 3);
        !          3086:        found_star = 0;
        !          3087:        for(int i = 9; i < 9 + 3; i++) {
        !          3088:                if(fcb[i] == '*') {
        !          3089:                        found_star = 1;
        !          3090:                }
        !          3091:                if(found_star) {
        !          3092:                        fcb[i] = '?';
        !          3093:                }
        !          3094:        }
        !          3095:        
        !          3096:        REG8(AL) = 0x00;
        !          3097:        if(drv == 0 || (drv > 0 && drv <= 26 && (GetLogicalDrives() & ( 1 << (drv - 1) )))) {
        !          3098:                if(memchr(fcb + 1, '?', 8 + 3)) {
        !          3099:                        REG8(AL) = 0x01;
        !          3100:                }
        !          3101:        } else {
        !          3102:                REG8(AL) = 0xff;
        !          3103:        }
        !          3104: }
        !          3105: 
        !          3106: inline void msdos_int_21h_2ah()
        !          3107: {
        !          3108:        SYSTEMTIME sTime;
        !          3109:        
        !          3110:        GetLocalTime(&sTime);
        !          3111:        REG16(CX) = sTime.wYear;
        !          3112:        REG8(DH) = (UINT8)sTime.wMonth;
        !          3113:        REG8(DL) = (UINT8)sTime.wDay;
        !          3114:        REG8(AL) = (UINT8)sTime.wDayOfWeek;
        !          3115: }
        !          3116: 
        !          3117: inline void msdos_int_21h_2bh()
        !          3118: {
        !          3119:        REG8(AL) = 0x00;
        !          3120: }
        !          3121: 
        !          3122: inline void msdos_int_21h_2ch()
        !          3123: {
        !          3124:        SYSTEMTIME sTime;
        !          3125:        
        !          3126:        GetLocalTime(&sTime);
        !          3127:        REG8(CH) = (UINT8)sTime.wHour;
        !          3128:        REG8(CL) = (UINT8)sTime.wMinute;
        !          3129:        REG8(DH) = (UINT8)sTime.wSecond;
        !          3130: }
        !          3131: 
        !          3132: inline void msdos_int_21h_2dh()
        !          3133: {
        !          3134:        REG8(AL) = 0x00;
        !          3135: }
        !          3136: 
        !          3137: inline void msdos_int_21h_2eh()
        !          3138: {
        !          3139:        process_t *process = msdos_process_info_get(current_psp);
        !          3140:        
        !          3141:        process->verify = REG8(AL);
        !          3142: }
        !          3143: 
        !          3144: inline void msdos_int_21h_2fh()
        !          3145: {
        !          3146:        process_t *process = msdos_process_info_get(current_psp);
        !          3147:        
        !          3148:        REG16(BX) = process->dta.w.l;
        !          3149:        cpustate->sreg[ES].selector = process->dta.w.h;
        !          3150:        i386_load_segment_descriptor(cpustate, ES);
        !          3151: }
        !          3152: 
        !          3153: inline void msdos_int_21h_30h()
        !          3154: {
        !          3155:        // Version Flag / OEM
        !          3156:        if(REG8(AL) == 1) {
        !          3157:                REG8(BH) = 0x00;        // not in ROM
        !          3158:        } else {
        !          3159:                REG8(BH) = 0xff;        // OEM = Microsoft
        !          3160:        }
        !          3161:        // MS-DOS version (7.10)
        !          3162:        REG8(AL) = 7;
        !          3163:        REG8(AH) = 10;
        !          3164: }
        !          3165: 
        !          3166: inline void msdos_int_21h_31h()
        !          3167: {
        !          3168:        int mcb_seg = current_psp - 1;
        !          3169:        mcb_t *mcb = (mcb_t *)(mem + (mcb_seg << 4));
        !          3170:        
        !          3171:        mcb->paragraphs = REG16(DX);
        !          3172:        mcb_seg += mcb->paragraphs + 1;
        !          3173:        msdos_mcb_create(mcb_seg, 'Z', 0, (MEMORY_END >> 4) - mcb_seg - 1);
        !          3174:        
        !          3175:        msdos_process_terminate(current_psp, REG8(AL) | 0x300, 0);
        !          3176: }
        !          3177: 
        !          3178: inline void msdos_int_21h_32h()
        !          3179: {
        !          3180:        int drive_num = (REG8(DL) == 0) ? (_getdrive() - 1) : (REG8(DL) - 1);
        !          3181:        UINT16 seg, ofs;
        !          3182:        
        !          3183:        if(msdos_drive_param_block_update(drive_num, &seg, &ofs, 1)) {
        !          3184:                REG8(AL) = 0;
        !          3185:                cpustate->sreg[DS].selector = seg;
        !          3186:                i386_load_segment_descriptor(cpustate, DS);
        !          3187:                REG16(BX) = ofs;
        !          3188:        } else {
        !          3189:                REG8(AL) = 0xff;
        !          3190:                cpustate->CF = 1;
        !          3191:        }
        !          3192: }
        !          3193: 
        !          3194: inline void msdos_int_21h_33h()
        !          3195: {
        !          3196:        static UINT8 state = 0x00;
        !          3197:        char path[MAX_PATH];
        !          3198:        
        !          3199:        switch(REG8(AL)) {
        !          3200:        case 0x00:
        !          3201:                REG8(DL) = state;
        !          3202:                break;
        !          3203:        case 0x01:
        !          3204:                state = REG8(DL);
        !          3205:                break;
        !          3206:        case 0x05:
        !          3207:                GetSystemDirectory(path, MAX_PATH);
        !          3208:                if(path[0] >= 'a' && path[0] <= 'z') {
        !          3209:                        REG8(DL) = path[0] - 'a' + 1;
        !          3210:                } else {
        !          3211:                        REG8(DL) = path[0] - 'A' + 1;
        !          3212:                }
        !          3213:                break;
        !          3214:        case 0x06:
        !          3215:                // MS-DOS version (7.00)
        !          3216:                REG8(BL) = 7;
        !          3217:                REG8(BH) = 0;
        !          3218:                REG8(DL) = 0;
        !          3219:                REG8(DH) = 0x10; // in HMA
        !          3220:                break;
        !          3221:        default:
        !          3222:                REG16(AX) = 0x01;
        !          3223:                cpustate->CF = 1;
        !          3224:                break;
        !          3225:        }
        !          3226: }
        !          3227: 
        !          3228: inline void msdos_int_21h_35h()
        !          3229: {
        !          3230:        REG16(BX) = *(UINT16 *)(mem + 4 * REG8(AL) + 0);
        !          3231:        cpustate->sreg[ES].selector = *(UINT16 *)(mem + 4 * REG8(AL) + 2);
        !          3232:        i386_load_segment_descriptor(cpustate, ES);
        !          3233: }
        !          3234: 
        !          3235: inline void msdos_int_21h_36h()
        !          3236: {
        !          3237:        struct _diskfree_t df = {0};
        !          3238:        
        !          3239:        if(_getdiskfree(REG8(DL), &df) == 0) {
        !          3240:                REG16(AX) = (UINT16)df.sectors_per_cluster;
        !          3241:                REG16(CX) = (UINT16)df.bytes_per_sector;
        !          3242:                REG16(BX) = (UINT16)df.avail_clusters;
        !          3243:                REG16(DX) = (UINT16)df.total_clusters;
        !          3244:        } else {
        !          3245:                REG16(AX) = 0xffff;
        !          3246:        }
        !          3247: }
        !          3248: 
        !          3249: inline void msdos_int_21h_37h()
        !          3250: {
        !          3251:        process_t *process = msdos_process_info_get(current_psp);
        !          3252:        
        !          3253:        switch(REG8(AL)) {
        !          3254:        case 0x00:
        !          3255:                REG8(AL) = 0x00;
        !          3256:                REG8(DL) = process->switchar;
        !          3257:                break;
        !          3258:        case 0x01:
        !          3259:                REG8(AL) = 0x00;
        !          3260:                process->switchar = REG8(DL);
        !          3261:                break;
        !          3262:        default:
        !          3263:                REG16(AX) = 1;
        !          3264:                break;
        !          3265:        }
        !          3266: }
        !          3267: 
        !          3268: inline void msdos_int_21h_39h(int lfn)
        !          3269: {
        !          3270:        if(_mkdir(msdos_trimmed_path((char *)(mem + cpustate->sreg[DS].base + REG16(DX)), lfn))) {
        !          3271:                REG16(AX) = errno;
        !          3272:                cpustate->CF = 1;
        !          3273:        }
        !          3274: }
        !          3275: 
        !          3276: inline void msdos_int_21h_3ah(int lfn)
        !          3277: {
        !          3278:        if(_rmdir(msdos_trimmed_path((char *)(mem + cpustate->sreg[DS].base + REG16(DX)), lfn))) {
        !          3279:                REG16(AX) = errno;
        !          3280:                cpustate->CF = 1;
        !          3281:        }
        !          3282: }
        !          3283: 
        !          3284: inline void msdos_int_21h_3bh(int lfn)
        !          3285: {
        !          3286:        if(_chdir(msdos_trimmed_path((char *)(mem + cpustate->sreg[DS].base + REG16(DX)), lfn))) {
        !          3287:                REG16(AX) = errno;
        !          3288:                cpustate->CF = 1;
        !          3289:        }
        !          3290: }
        !          3291: 
        !          3292: inline void msdos_int_21h_3ch()
        !          3293: {
        !          3294:        char *path = msdos_local_file_path((char *)(mem + cpustate->sreg[DS].base + REG16(DX)), 0);
        !          3295:        int attr = GetFileAttributes(path);
        !          3296:        int fd = -1;
        !          3297:        
        !          3298:        if(_stricmp(path, "CON") == 0) {
        !          3299:                fd = _open(path, _O_WRONLY | _O_BINARY);
        !          3300:        } else {
        !          3301:                fd = _open(path, _O_RDWR | _O_BINARY | _O_CREAT | _O_TRUNC, _S_IREAD | _S_IWRITE);
        !          3302:        }
        !          3303:        if(fd != -1) {
        !          3304:                if(attr == -1) {
        !          3305:                        attr = msdos_file_attribute_create(REG16(CX)) & ~FILE_ATTRIBUTE_READONLY;
        !          3306:                }
        !          3307:                SetFileAttributes(path, attr);
        !          3308:                REG16(AX) = fd;
        !          3309:                msdos_file_handler_open(fd, path, _isatty(fd), 2, msdos_drive_number(path), current_psp);
        !          3310:        } else {
        !          3311:                REG16(AX) = errno;
        !          3312:                cpustate->CF = 1;
        !          3313:        }
        !          3314: }
        !          3315: 
        !          3316: inline void msdos_int_21h_3dh()
        !          3317: {
        !          3318:        char *path = msdos_local_file_path((char *)(mem + cpustate->sreg[DS].base + REG16(DX)), 0);
        !          3319:        int mode = REG8(AL) & 0x03;
        !          3320:        
        !          3321:        if(mode < 0x03) {
        !          3322:                int fd = _open(path, file_mode[mode].mode);
        !          3323:                
        !          3324:                if(fd != -1) {
        !          3325:                        REG16(AX) = fd;
        !          3326:                        msdos_file_handler_open(fd, path, _isatty(fd), mode, msdos_drive_number(path), current_psp);
        !          3327:                } else {
        !          3328:                        REG16(AX) = errno;
        !          3329:                        cpustate->CF = 1;
        !          3330:                }
        !          3331:        } else {
        !          3332:                REG16(AX) = 0x0c;
        !          3333:                cpustate->CF = 1;
        !          3334:        }
        !          3335: }
        !          3336: 
        !          3337: inline void msdos_int_21h_3eh()
        !          3338: {
        !          3339:        process_t *process = msdos_process_info_get(current_psp);
        !          3340:        
        !          3341:        if(REG16(BX) < process->max_files && file_handler[REG16(BX)].valid) {
        !          3342:                _close(REG16(BX));
        !          3343:                msdos_file_handler_close(REG16(BX), current_psp);
        !          3344:        } else {
        !          3345:                REG16(AX) = 0x06;
        !          3346:                cpustate->CF = 1;
        !          3347:        }
        !          3348: }
        !          3349: 
        !          3350: inline void msdos_int_21h_3fh()
        !          3351: {
        !          3352:        process_t *process = msdos_process_info_get(current_psp);
        !          3353:        
        !          3354:        if(REG16(BX) < process->max_files && file_handler[REG16(BX)].valid) {
        !          3355:                if(file_mode[file_handler[REG16(BX)].mode].in) {
        !          3356:                        if(file_handler[REG16(BX)].atty) {
        !          3357:                                // BX is stdin or is redirected to stdin
        !          3358:                                UINT8 *buf = mem + cpustate->sreg[DS].base + REG16(DX);
        !          3359:                                int max = REG16(CX);
        !          3360:                                int p = 0;
        !          3361:                                
        !          3362:                                while(max > p) {
        !          3363:                                        int chr = msdos_getch();
        !          3364:                                        
        !          3365:                                        if(chr == 0x00) {
        !          3366:                                                // skip 2nd byte
        !          3367:                                                msdos_getch();
        !          3368:                                        } else if(chr == 0x0d) {
        !          3369:                                                // carriage return
        !          3370:                                                buf[p++] = 0x0d;
        !          3371:                                                if(max > p) {
        !          3372:                                                        buf[p++] = 0x0a;
        !          3373:                                                }
        !          3374:                                                break;
        !          3375:                                        } else if(chr == 0x08) {
        !          3376:                                                // back space
        !          3377:                                                if(p > 0) {
        !          3378:                                                        p--;
        !          3379:                                                        msdos_putch(chr);
        !          3380:                                                        msdos_putch(' ');
        !          3381:                                                        msdos_putch(chr);
        !          3382:                                                }
        !          3383:                                        } else {
        !          3384:                                                buf[p++] = chr;
        !          3385:                                                msdos_putch(chr);
        !          3386:                                        }
        !          3387:                                }
        !          3388:                                REG16(AX) = p;
        !          3389: #ifdef SUPPORT_HARDWARE
        !          3390:                                hardware_update();
        !          3391: #endif
        !          3392:                        } else {
        !          3393:                                REG16(AX) = _read(REG16(BX), mem + cpustate->sreg[DS].base + REG16(DX), REG16(CX));
        !          3394:                        }
        !          3395:                } else {
        !          3396:                        REG16(AX) = 0x05;
        !          3397:                        cpustate->CF = 1;
        !          3398:                }
        !          3399:        } else {
        !          3400:                REG16(AX) = 0x06;
        !          3401:                cpustate->CF = 1;
        !          3402:        }
        !          3403: }
        !          3404: 
        !          3405: inline void msdos_int_21h_40h()
        !          3406: {
        !          3407:        process_t *process = msdos_process_info_get(current_psp);
        !          3408:        
        !          3409:        if(REG16(BX) < process->max_files && file_handler[REG16(BX)].valid) {
        !          3410:                if(file_mode[file_handler[REG16(BX)].mode].out) {
        !          3411:                        if(REG16(CX)) {
        !          3412:                                if(file_handler[REG16(BX)].atty) {
        !          3413:                                        // BX is stdout/stderr or is redirected to stdout
        !          3414:                                        for(int i = 0; i < REG16(CX); i++) {
        !          3415:                                                msdos_putch(mem[cpustate->sreg[DS].base + REG16(DX) + i]);
        !          3416:                                        }
        !          3417:                                        REG16(AX) = REG16(CX);
        !          3418:                                } else {
        !          3419:                                        REG16(AX) = msdos_write(REG16(BX), mem + cpustate->sreg[DS].base + REG16(DX), REG16(CX));
        !          3420:                                }
        !          3421:                        } else {
        !          3422:                                UINT32 pos = _tell(REG16(BX));
        !          3423:                                _lseek(REG16(BX), 0, SEEK_END);
        !          3424:                                UINT32 size = _tell(REG16(BX));
        !          3425:                                REG16(AX) = 0;
        !          3426:                                for(UINT32 i = size; i < pos; i++) {
        !          3427:                                        UINT8 tmp = 0;
        !          3428:                                        REG16(AX) += msdos_write(REG16(BX), &tmp, 1);
        !          3429:                                }
        !          3430:                                _lseek(REG16(BX), pos, SEEK_SET);
        !          3431:                        }
        !          3432:                } else {
        !          3433:                        REG16(AX) = 0x05;
        !          3434:                        cpustate->CF = 1;
        !          3435:                }
        !          3436:        } else {
        !          3437:                REG16(AX) = 0x06;
        !          3438:                cpustate->CF = 1;
        !          3439:        }
        !          3440: }
        !          3441: 
        !          3442: inline void msdos_int_21h_41h(int lfn)
        !          3443: {
        !          3444:        if(remove(msdos_trimmed_path((char *)(mem + cpustate->sreg[DS].base + REG16(DX)), lfn))) {
        !          3445:                REG16(AX) = errno;
        !          3446:                cpustate->CF = 1;
        !          3447:        }
        !          3448: }
        !          3449: 
        !          3450: inline void msdos_int_21h_42h()
        !          3451: {
        !          3452:        process_t *process = msdos_process_info_get(current_psp);
        !          3453:        
        !          3454:        if(REG16(BX) < process->max_files && file_handler[REG16(BX)].valid) {
        !          3455:                if(REG8(AL) < 0x03) {
        !          3456:                        static int ptrname[] = { SEEK_SET, SEEK_CUR, SEEK_END };
        !          3457:                        _lseek(REG16(BX), (REG16(CX) << 16) | REG16(DX), ptrname[REG8(AL)]);
        !          3458:                        UINT32 pos = _tell(REG16(BX));
        !          3459:                        REG16(AX) = pos & 0xffff;
        !          3460:                        REG16(DX) = (pos >> 16);
        !          3461:                } else {
        !          3462:                        REG16(AX) = 0x01;
        !          3463:                        cpustate->CF = 1;
        !          3464:                }
        !          3465:        } else {
        !          3466:                REG16(AX) = 0x06;
        !          3467:                cpustate->CF = 1;
        !          3468:        }
        !          3469: }
        !          3470: 
        !          3471: inline void msdos_int_21h_43h(int lfn)
        !          3472: {
        !          3473:        char *path = msdos_local_file_path((char *)(mem + cpustate->sreg[DS].base + REG16(DX)), lfn);
        !          3474:        int attr;
        !          3475:        
        !          3476:        switch(REG8(AL)) {
        !          3477:        case 0x00:
        !          3478:                if((attr = GetFileAttributes(path)) != -1) {
        !          3479:                        REG16(CX) = 0;
        !          3480:                        if(attr & FILE_ATTRIBUTE_READONLY) {
        !          3481:                                REG16(CX) |= 0x01;
        !          3482:                        }
        !          3483:                        if(attr & FILE_ATTRIBUTE_HIDDEN) {
        !          3484:                                REG16(CX) |= 0x02;
        !          3485:                        }
        !          3486:                        if(attr & FILE_ATTRIBUTE_SYSTEM) {
        !          3487:                                REG16(CX) |= 0x04;
        !          3488:                        }
        !          3489:                        if(attr & FILE_ATTRIBUTE_ARCHIVE) {
        !          3490:                                REG16(CX) |= 0x20;
        !          3491:                        }
        !          3492:                } else {
        !          3493:                        REG16(AX) = (UINT16)GetLastError();
        !          3494:                        cpustate->CF = 1;
        !          3495:                }
        !          3496:                break;
        !          3497:        case 0x01:
        !          3498:                if(SetFileAttributes(path, msdos_file_attribute_create(REG16(CX))) != 0) {
        !          3499:                        REG16(AX) = (UINT16)GetLastError();
        !          3500:                        cpustate->CF = 1;
        !          3501:                }
        !          3502:                break;
        !          3503:        case 0x02:
        !          3504:                break;
        !          3505:        case 0x03:
        !          3506:                REG16(CX) = 0x00;
        !          3507:                break;
        !          3508:        default:
        !          3509:                REG16(AX) = 0x01;
        !          3510:                cpustate->CF = 1;
        !          3511:                break;
        !          3512:        }
        !          3513: }
        !          3514: 
        !          3515: inline void msdos_int_21h_44h()
        !          3516: {
        !          3517:        switch(REG8(AL)) {
        !          3518:        case 0x00: // get ioctrl data
        !          3519:                REG16(DX) = file_handler[REG16(BX)].info;
        !          3520:                break;
        !          3521:        case 0x01: // set ioctrl data
        !          3522:                file_handler[REG16(BX)].info |= REG8(DL);
        !          3523:                break;
        !          3524:        case 0x02: // recv from character device
        !          3525:        case 0x03: // send to character device
        !          3526:                REG16(AX) = 0x06;
        !          3527:                cpustate->CF = 1;
        !          3528:                break;
        !          3529:        case 0x04: // recv from block device
        !          3530:        case 0x05: // send to block device
        !          3531:                REG16(AX) = 0x05;
        !          3532:                cpustate->CF = 1;
        !          3533:                break;
        !          3534:        case 0x06: // get read status
        !          3535:                {
        !          3536:                        process_t *process = msdos_process_info_get(current_psp);
        !          3537:                        
        !          3538:                        if(REG16(BX) < process->max_files && file_handler[REG16(BX)].valid) {
        !          3539:                                if(file_mode[file_handler[REG16(BX)].mode].in) {
        !          3540:                                        if(file_handler[REG16(BX)].atty) {
        !          3541:                                                REG8(AL) = msdos_kbhit() ? 0xff : 0x00;
        !          3542:                                        } else {
        !          3543:                                                REG8(AL) = eof(REG16(BX)) ? 0x00 : 0xff;
        !          3544:                                        }
        !          3545:                                } else {
        !          3546:                                        REG8(AL) = 0x00;
        !          3547:                                }
        !          3548:                        } else {
        !          3549:                                REG16(AX) = 0x06;
        !          3550:                                cpustate->CF = 1;
        !          3551:                        }
        !          3552:                }
        !          3553:                break;
        !          3554:        case 0x07: // get write status
        !          3555:                {
        !          3556:                        process_t *process = msdos_process_info_get(current_psp);
        !          3557:                        
        !          3558:                        if(REG16(BX) < process->max_files && file_handler[REG16(BX)].valid) {
        !          3559:                                if(file_mode[file_handler[REG16(BX)].mode].out) {
        !          3560:                                        REG8(AL) = 0xff;
        !          3561:                                } else {
        !          3562:                                        REG8(AL) = 0x00;
        !          3563:                                }
        !          3564:                        } else {
        !          3565:                                REG16(AX) = 0x06;
        !          3566:                                cpustate->CF = 1;
        !          3567:                        }
        !          3568:                }
        !          3569:                break;
        !          3570:        case 0x08: // check removable drive
        !          3571:                if(REG8(BL) < ('Z' - 'A' + 1)) {
        !          3572:                        UINT32 val;
        !          3573:                        if(REG8(BL) == 0) {
        !          3574:                                val = GetDriveType(NULL);
        !          3575:                        } else if(REG8(BL) < ('Z' - 'A' + 1)) {
        !          3576:                                char tmp[8];
        !          3577:                                sprintf(tmp, "%c:\\", 'A' + REG8(BL) - 1);
        !          3578:                                val = GetDriveType(tmp);
        !          3579:                        }
        !          3580:                        if(val == DRIVE_NO_ROOT_DIR) {
        !          3581:                                // no drive
        !          3582:                                REG16(AX) = 0x0f;
        !          3583:                                cpustate->CF = 1;
        !          3584:                        } else if(val == DRIVE_REMOVABLE || val == DRIVE_CDROM) {
        !          3585:                                // removable drive
        !          3586:                                REG16(AX) = 0x00;
        !          3587:                        } else {
        !          3588:                                // fixed drive
        !          3589:                                REG16(AX) = 0x01;
        !          3590:                        }
        !          3591:                } else {
        !          3592:                        // invalid drive number
        !          3593:                        REG16(AX) = 0x0f;
        !          3594:                        cpustate->CF = 1;
        !          3595:                }
        !          3596:                break;
        !          3597:        case 0x09: // check remote drive
        !          3598:                if(REG8(BL) < ('Z' - 'A' + 1)) {
        !          3599:                        UINT32 val;
        !          3600:                        if(REG8(BL) == 0) {
        !          3601:                                val = GetDriveType(NULL);
        !          3602:                        } else if(REG8(BL) < ('Z' - 'A' + 1)) {
        !          3603:                                char tmp[8];
        !          3604:                                sprintf(tmp, "%c:\\", 'A' + REG8(BL) - 1);
        !          3605:                                val = GetDriveType(tmp);
        !          3606:                        }
        !          3607:                        if(val == DRIVE_NO_ROOT_DIR) {
        !          3608:                                // no drive
        !          3609:                                REG16(AX) = 0x0f;
        !          3610:                                cpustate->CF = 1;
        !          3611:                        } else if(val == DRIVE_REMOTE) {
        !          3612:                                // remote drive
        !          3613:                                REG16(DX) = 0x1000;
        !          3614:                        } else {
        !          3615:                                // local drive
        !          3616:                                REG16(DX) = 0x00;
        !          3617:                        }
        !          3618:                } else {
        !          3619:                        // invalid drive number
        !          3620:                        REG16(AX) = 0x0f;
        !          3621:                        cpustate->CF = 1;
        !          3622:                }
        !          3623:                break;
        !          3624:        case 0x0b: // set retry count
        !          3625:                break;
        !          3626:        default:
        !          3627:                REG16(AX) = 0x01;
        !          3628:                cpustate->CF = 1;
        !          3629:                break;
        !          3630:        }
        !          3631: }
        !          3632: 
        !          3633: inline void msdos_int_21h_45h()
        !          3634: {
        !          3635:        process_t *process = msdos_process_info_get(current_psp);
        !          3636:        
        !          3637:        if(REG16(BX) < process->max_files && file_handler[REG16(BX)].valid) {
        !          3638:                int fd = _dup(REG16(BX));
        !          3639:                if(fd != -1) {
        !          3640:                        REG16(AX) = fd;
        !          3641:                        msdos_file_handler_dup(REG16(AX), REG16(BX), current_psp);
        !          3642:                } else {
        !          3643:                        REG16(AX) = errno;
        !          3644:                        cpustate->CF = 1;
        !          3645:                }
        !          3646:        } else {
        !          3647:                REG16(AX) = 0x06;
        !          3648:                cpustate->CF = 1;
        !          3649:        }
        !          3650: }
        !          3651: 
        !          3652: inline void msdos_int_21h_46h()
        !          3653: {
        !          3654:        process_t *process = msdos_process_info_get(current_psp);
        !          3655:        
        !          3656:        if(REG16(BX) < process->max_files && REG16(CX) < process->max_files && file_handler[REG16(BX)].valid) {
        !          3657:                if(_dup2(REG16(BX), REG16(CX)) != -1) {
        !          3658:                        msdos_file_handler_dup(REG16(CX), REG16(BX), current_psp);
        !          3659:                } else {
        !          3660:                        REG16(AX) = errno;
        !          3661:                        cpustate->CF = 1;
        !          3662:                }
        !          3663:        } else {
        !          3664:                REG16(AX) = 0x06;
        !          3665:                cpustate->CF = 1;
        !          3666:        }
        !          3667: }
        !          3668: 
        !          3669: inline void msdos_int_21h_47h(int lfn)
        !          3670: {
        !          3671:        char path[MAX_PATH];
        !          3672:        
        !          3673:        if(_getdcwd(REG8(DL), path, MAX_PATH) != NULL) {
        !          3674:                if(path[1] == ':') {
        !          3675:                        // the returned path does not include a drive or the initial backslash
        !          3676:                        strcpy((char *)(mem + cpustate->sreg[DS].base + REG16(SI)), (lfn ? path : msdos_short_path(path)) + 3);
        !          3677:                } else {
        !          3678:                        strcpy((char *)(mem + cpustate->sreg[DS].base + REG16(SI)), lfn ? path : msdos_short_path(path));
        !          3679:                }
        !          3680:        } else {
        !          3681:                REG16(AX) = errno;
        !          3682:                cpustate->CF = 1;
        !          3683:        }
        !          3684: }
        !          3685: 
        !          3686: inline void msdos_int_21h_48h()
        !          3687: {
        !          3688:        int seg;
        !          3689:        
        !          3690:        if((seg = msdos_mem_alloc(REG16(BX), 0)) != -1) {
        !          3691:                REG16(AX) = seg;
        !          3692:        } else {
        !          3693:                REG16(AX) = 0x08;
        !          3694:                REG16(BX) = msdos_mem_get_free(0);
        !          3695:                cpustate->CF = 1;
        !          3696:        }
        !          3697: }
        !          3698: 
        !          3699: inline void msdos_int_21h_49h()
        !          3700: {
        !          3701:        msdos_mem_free(cpustate->sreg[ES].selector);
        !          3702: }
        !          3703: 
        !          3704: inline void msdos_int_21h_4ah()
        !          3705: {
        !          3706:        int max_paragraphs;
        !          3707:        
        !          3708:        if(msdos_mem_realloc(cpustate->sreg[ES].selector, REG16(BX), &max_paragraphs)) {
        !          3709:                REG16(AX) = 0x08;
        !          3710:                REG16(BX) = max_paragraphs;
        !          3711:                cpustate->CF = 1;
        !          3712:        }
        !          3713: }
        !          3714: 
        !          3715: inline void msdos_int_21h_4bh()
        !          3716: {
        !          3717:        char *command = (char *)(mem + cpustate->sreg[DS].base + REG16(DX));
        !          3718:        param_block_t *param = (param_block_t *)(mem + cpustate->sreg[ES].base + REG16(BX));
        !          3719:        
        !          3720:        switch(REG8(AL)) {
        !          3721:        case 0x00:
        !          3722:        case 0x01:
        !          3723:                if(msdos_process_exec(command, param, REG8(AL))) {
        !          3724:                        REG16(AX) = 0x02;
        !          3725:                        cpustate->CF = 1;
        !          3726:                }
        !          3727:                break;
        !          3728:        default:
        !          3729:                REG16(AX) = 0x01;
        !          3730:                cpustate->CF = 1;
        !          3731:                break;
        !          3732:        }
        !          3733: }
        !          3734: 
        !          3735: inline void msdos_int_21h_4ch()
        !          3736: {
        !          3737:        msdos_process_terminate(current_psp, REG8(AL), 1);
        !          3738: }
        !          3739: 
        !          3740: inline void msdos_int_21h_4dh()
        !          3741: {
        !          3742:        REG16(AX) = retval;
        !          3743: }
        !          3744: 
        !          3745: inline void msdos_int_21h_4eh()
        !          3746: {
        !          3747:        process_t *process = msdos_process_info_get(current_psp);
        !          3748:        find_t *find = (find_t *)(mem + (process->dta.w.h << 4) + process->dta.w.l);
        !          3749:        char *path = msdos_trimmed_path((char *)(mem + cpustate->sreg[DS].base + REG16(DX)), 0);
        !          3750:        WIN32_FIND_DATA fd;
        !          3751:        
        !          3752:        if(process->find_handle != INVALID_HANDLE_VALUE) {
        !          3753:                FindClose(process->find_handle);
        !          3754:                process->find_handle = INVALID_HANDLE_VALUE;
        !          3755:        }
        !          3756:        strcpy(process->volume_label, msdos_volume_label(path));
        !          3757:        process->allowable_mask = REG8(CL);
        !          3758:        
        !          3759:        if((process->allowable_mask & 8) && !msdos_match_volume_label(path, msdos_short_volume_label(process->volume_label))) {
        !          3760:                process->allowable_mask &= ~8;
        !          3761:        }
        !          3762:        if((process->find_handle = FindFirstFile(path, &fd)) != INVALID_HANDLE_VALUE) {
        !          3763:                while(!msdos_find_file_check_attribute(fd.dwFileAttributes, process->allowable_mask, 0)) {
        !          3764:                        if(!FindNextFile(process->find_handle, &fd)) {
        !          3765:                                FindClose(process->find_handle);
        !          3766:                                process->find_handle = INVALID_HANDLE_VALUE;
        !          3767:                                break;
        !          3768:                        }
        !          3769:                }
        !          3770:        }
        !          3771:        if(process->find_handle != INVALID_HANDLE_VALUE) {
        !          3772:                find->attrib = (UINT8)(fd.dwFileAttributes & 0x3f);
        !          3773:                msdos_find_file_conv_local_time(&fd);
        !          3774:                FileTimeToDosDateTime(&fd.ftLastWriteTime, &find->date, &find->time);
        !          3775:                find->size = fd.nFileSizeLow;
        !          3776:                strcpy(find->name, msdos_short_path(fd.cFileName));
        !          3777:                REG16(AX) = 0;
        !          3778:        } else if(process->allowable_mask & 8) {
        !          3779:                find->attrib = 8;
        !          3780:                find->size = 0;
        !          3781:                strcpy(find->name, msdos_short_volume_label(process->volume_label));
        !          3782:                process->allowable_mask &= ~8;
        !          3783:                REG16(AX) = 0;
        !          3784:        } else {
        !          3785:                REG16(AX) = 0x12;       // NOTE: return 0x02 if file path is invalid
        !          3786:                cpustate->CF = 1;
        !          3787:        }
        !          3788: }
        !          3789: 
        !          3790: inline void msdos_int_21h_4fh()
        !          3791: {
        !          3792:        process_t *process = msdos_process_info_get(current_psp);
        !          3793:        find_t *find = (find_t *)(mem + (process->dta.w.h << 4) + process->dta.w.l);
        !          3794:        WIN32_FIND_DATA fd;
        !          3795:        
        !          3796:        if(process->find_handle != INVALID_HANDLE_VALUE) {
        !          3797:                if(FindNextFile(process->find_handle, &fd)) {
        !          3798:                        while(!msdos_find_file_check_attribute(fd.dwFileAttributes, process->allowable_mask, 0)) {
        !          3799:                                if(!FindNextFile(process->find_handle, &fd)) {
        !          3800:                                        FindClose(process->find_handle);
        !          3801:                                        process->find_handle = INVALID_HANDLE_VALUE;
        !          3802:                                        break;
        !          3803:                                }
        !          3804:                        }
        !          3805:                } else {
        !          3806:                        FindClose(process->find_handle);
        !          3807:                        process->find_handle = INVALID_HANDLE_VALUE;
        !          3808:                }
        !          3809:        }
        !          3810:        if(process->find_handle != INVALID_HANDLE_VALUE) {
        !          3811:                find->attrib = (UINT8)(fd.dwFileAttributes & 0x3f);
        !          3812:                msdos_find_file_conv_local_time(&fd);
        !          3813:                FileTimeToDosDateTime(&fd.ftLastWriteTime, &find->date, &find->time);
        !          3814:                find->size = fd.nFileSizeLow;
        !          3815:                strcpy(find->name, msdos_short_path(fd.cFileName));
        !          3816:                REG16(AX) = 0;
        !          3817:        } else if(process->allowable_mask & 8) {
        !          3818:                find->attrib = 8;
        !          3819:                find->size = 0;
        !          3820:                strcpy(find->name, msdos_short_volume_label(process->volume_label));
        !          3821:                process->allowable_mask &= ~8;
        !          3822:                REG16(AX) = 0;
        !          3823:        } else {
        !          3824:                REG16(AX) = 0x12;
        !          3825:                cpustate->CF = 1;
        !          3826:        }
        !          3827: }
        !          3828: 
        !          3829: inline void msdos_int_21h_50h()
        !          3830: {
        !          3831:        current_psp = REG16(BX);
        !          3832: }
        !          3833: 
        !          3834: inline void msdos_int_21h_51h()
        !          3835: {
        !          3836:        REG16(BX) = current_psp;
        !          3837: }
        !          3838: 
        !          3839: inline void msdos_int_21h_52h()
        !          3840: {
        !          3841:        cpustate->sreg[ES].selector = (DOS_INFO_BASE >> 4);
        !          3842:        i386_load_segment_descriptor(cpustate, ES);
        !          3843:        REG16(BX) = (DOS_INFO_BASE & 0x0f);
        !          3844: }
        !          3845: 
        !          3846: inline void msdos_int_21h_54h()
        !          3847: {
        !          3848:        process_t *process = msdos_process_info_get(current_psp);
        !          3849:        
        !          3850:        REG8(AL) = process->verify;
        !          3851: }
        !          3852: 
        !          3853: inline void msdos_int_21h_55h()
        !          3854: {
        !          3855:        psp_t *psp = (psp_t *)(mem + (REG16(DX) << 4));
        !          3856:        
        !          3857:        memcpy(mem + (REG16(DX) << 4), mem + (current_psp << 4), sizeof(psp_t));
        !          3858:        psp->int_22h.dw = *(UINT32 *)(mem + 4 * 0x22);
        !          3859:        psp->int_23h.dw = *(UINT32 *)(mem + 4 * 0x23);
        !          3860:        psp->int_24h.dw = *(UINT32 *)(mem + 4 * 0x24);
        !          3861:        psp->parent_psp = current_psp;
        !          3862: }
        !          3863: 
        !          3864: inline void msdos_int_21h_56h(int lfn)
        !          3865: {
        !          3866:        char src[MAX_PATH], dst[MAX_PATH];
        !          3867:        strcpy(src, msdos_trimmed_path((char *)(mem + cpustate->sreg[DS].base + REG16(DX)), lfn));
        !          3868:        strcpy(dst, msdos_trimmed_path((char *)(mem + cpustate->sreg[ES].base + REG16(DI)), lfn));
        !          3869:        
        !          3870:        if(rename(src, dst)) {
        !          3871:                REG16(AX) = errno;
        !          3872:                cpustate->CF = 1;
        !          3873:        }
        !          3874: }
        !          3875: 
        !          3876: inline void msdos_int_21h_57h()
        !          3877: {
        !          3878:        FILETIME time, local;
        !          3879:        
        !          3880:        switch(REG8(AL)) {
        !          3881:        case 0x00:
        !          3882:                if(GetFileTime((HANDLE)_get_osfhandle(REG16(BX)), NULL, NULL, &time)) {
        !          3883:                        FileTimeToLocalFileTime(&time, &local);
        !          3884:                        FileTimeToDosDateTime(&local, &REG16(DX), &REG16(CX));
        !          3885:                } else {
        !          3886:                        REG16(AX) = (UINT16)GetLastError();
        !          3887:                        cpustate->CF = 1;
        !          3888:                }
        !          3889:                break;
        !          3890:        case 0x01:
        !          3891:                DosDateTimeToFileTime(REG16(DX), REG16(CX), &local);
        !          3892:                LocalFileTimeToFileTime(&local, &time);
        !          3893:                if(!SetFileTime((HANDLE)_get_osfhandle(REG16(BX)), NULL, NULL, &time)) {
        !          3894:                        REG16(AX) = (UINT16)GetLastError();
        !          3895:                        cpustate->CF = 1;
        !          3896:                }
        !          3897:                break;
        !          3898:        default:
        !          3899:                REG16(AX) = 0x01;
        !          3900:                cpustate->CF = 1;
        !          3901:                break;
        !          3902:        }
        !          3903: }
        !          3904: 
        !          3905: inline void msdos_int_21h_58h()
        !          3906: {
        !          3907:        switch(REG8(AL)) {
        !          3908:        case 0x00:
        !          3909:                REG16(AX) = 0x00;
        !          3910:                break;
        !          3911:        default:
        !          3912:                REG16(AX) = 0x01;
        !          3913:                cpustate->CF = 1;
        !          3914:                break;
        !          3915:        }
        !          3916: }
        !          3917: 
        !          3918: inline void msdos_int_21h_59h()
        !          3919: {
        !          3920:        REG16(AX) = error_code;
        !          3921:        switch(error_code) {
        !          3922:        case  4: // Too many open files
        !          3923:        case  8: // Insufficient memory
        !          3924:                REG8(BH) = 1; // Out of resource
        !          3925:                break;
        !          3926:        case  5: // Access denied
        !          3927:                REG8(BH) = 3; // Authorization
        !          3928:                break;
        !          3929:        case  7: // Memory control block destroyed
        !          3930:                REG8(BH) = 4; // Internal
        !          3931:                break;
        !          3932:        case  2: // File not found
        !          3933:        case  3: // Path not found
        !          3934:        case 15: // Invaid drive specified
        !          3935:        case 18: // No more files
        !          3936:                REG8(BH) = 8; // Not found
        !          3937:                break;
        !          3938:        case 32: // Sharing violation
        !          3939:        case 33: // Lock violation
        !          3940:                REG8(BH) = 10; // Locked
        !          3941:                break;
        !          3942: //     case 16: // Removal of current directory attempted
        !          3943:        case 19: // Attempted write on protected disk
        !          3944:        case 21: // Drive not ready
        !          3945: //     case 29: // Write failure
        !          3946: //     case 30: // Read failure
        !          3947: //     case 82: // Cannot create subdirectory
        !          3948:                REG8(BH) = 11; // Media
        !          3949:                break;
        !          3950:        case 80: // File already exists
        !          3951:                REG8(BH) = 12; // Already exist
        !          3952:                break;
        !          3953:        default:
        !          3954:                REG8(BH) = 13; // Unknown
        !          3955:                break;
        !          3956:        }
        !          3957:        REG8(BL) = 1; // Retry
        !          3958:        REG8(CH) = 1; // Unknown
        !          3959: }
        !          3960: 
        !          3961: inline void msdos_int_21h_5ah()
        !          3962: {
        !          3963:        char *path = (char *)(mem + cpustate->sreg[DS].base + REG16(DX));
        !          3964:        int len = strlen(path);
        !          3965:        char tmp[MAX_PATH];
        !          3966:        
        !          3967:        if(GetTempFileName(path, "TMP", 0, tmp)) {
        !          3968:                int fd = _open(tmp, _O_RDWR | _O_BINARY | _O_CREAT | _O_TRUNC, _S_IREAD | _S_IWRITE);
        !          3969:                
        !          3970:                SetFileAttributes(tmp, msdos_file_attribute_create(REG16(CX)) & ~FILE_ATTRIBUTE_READONLY);
        !          3971:                REG16(AX) = fd;
        !          3972:                msdos_file_handler_open(fd, path, _isatty(fd), 2, msdos_drive_number(path), current_psp);
        !          3973:                
        !          3974:                strcpy(path, tmp);
        !          3975:                int dx = REG16(DX) + len;
        !          3976:                int ds = cpustate->sreg[DS].selector;
        !          3977:                while(dx > 0xffff) {
        !          3978:                        dx -= 0x10;
        !          3979:                        ds++;
        !          3980:                }
        !          3981:                REG16(DX) = dx;
        !          3982:                cpustate->sreg[DS].selector = ds;
        !          3983:                i386_load_segment_descriptor(cpustate, DS);
        !          3984:        } else {
        !          3985:                REG16(AX) = (UINT16)GetLastError();
        !          3986:                cpustate->CF = 1;
        !          3987:        }
        !          3988: }
        !          3989: 
        !          3990: inline void msdos_int_21h_5bh()
        !          3991: {
        !          3992:        char *path = msdos_local_file_path((char *)(mem + cpustate->sreg[DS].base + REG16(DX)), 0);
        !          3993:        
        !          3994:        if(_access(path, 0) == 0) {
        !          3995:                // already exists
        !          3996:                REG16(AX) = 0x50;
        !          3997:                cpustate->CF = 1;
        !          3998:        } else {
        !          3999:                int fd = _open(path, _O_RDWR | _O_BINARY | _O_CREAT | _O_TRUNC, _S_IREAD | _S_IWRITE);
        !          4000:                
        !          4001:                if(fd != -1) {
        !          4002:                        SetFileAttributes(path, msdos_file_attribute_create(REG16(CX)) & ~FILE_ATTRIBUTE_READONLY);
        !          4003:                        REG16(AX) = fd;
        !          4004:                        msdos_file_handler_open(fd, path, _isatty(fd), 2, msdos_drive_number(path), current_psp);
        !          4005:                } else {
        !          4006:                        REG16(AX) = errno;
        !          4007:                        cpustate->CF = 1;
        !          4008:                }
        !          4009:        }
        !          4010: }
        !          4011: 
        !          4012: inline void msdos_int_21h_5ch()
        !          4013: {
        !          4014:        process_t *process = msdos_process_info_get(current_psp);
        !          4015:        
        !          4016:        if(REG16(BX) < process->max_files && file_handler[REG16(BX)].valid) {
        !          4017:                if(REG8(AL) == 0 || REG8(AL) == 1) {
        !          4018:                        static int modes[2] = {_LK_LOCK, _LK_UNLCK};
        !          4019:                        UINT32 pos = _tell(REG16(BX));
        !          4020:                        _lseek(REG16(BX), (REG16(CX) << 16) | REG16(DX), SEEK_SET);
        !          4021:                        if(_locking(REG16(BX), modes[REG8(AL)], (REG16(SI) << 16) | REG16(DI))) {
        !          4022:                                REG16(AX) = errno;
        !          4023:                                cpustate->CF = 1;
        !          4024:                        }
        !          4025:                        _lseek(REG16(BX), pos, SEEK_SET);
        !          4026: #ifdef SUPPORT_HARDWARE
        !          4027:                        // some seconds may be passed in _locking()
        !          4028:                        hardware_update();
        !          4029: #endif
        !          4030:                } else {
        !          4031:                        REG16(AX) = 0x01;
        !          4032:                        cpustate->CF = 1;
        !          4033:                }
        !          4034:        } else {
        !          4035:                REG16(AX) = 0x06;
        !          4036:                cpustate->CF = 1;
        !          4037:        }
        !          4038: }
        !          4039: 
        !          4040: inline void msdos_int_21h_60h(int lfn)
        !          4041: {
        !          4042:        if(lfn) {
        !          4043:                char full[MAX_PATH], *name;
        !          4044:                GetFullPathName((char *)(mem + cpustate->sreg[DS].base + REG16(SI)), MAX_PATH, full, &name);
        !          4045:                strcpy((char *)(mem + cpustate->sreg[ES].base + REG16(DI)), full);
        !          4046:        } else {
        !          4047:                strcpy((char *)(mem + cpustate->sreg[ES].base + REG16(DI)), msdos_short_full_path((char *)(mem + cpustate->sreg[DS].base + REG16(SI))));
        !          4048:        }
        !          4049: }
        !          4050: 
        !          4051: inline void msdos_int_21h_61h()
        !          4052: {
        !          4053:        REG8(AL) = 0;
        !          4054: }
        !          4055: 
        !          4056: inline void msdos_int_21h_62h()
        !          4057: {
        !          4058:        REG16(BX) = current_psp;
        !          4059: }
        !          4060: 
        !          4061: inline void msdos_int_21h_63h()
        !          4062: {
        !          4063:        switch(REG8(AL)) {
        !          4064:        case 0x00:
        !          4065:                cpustate->sreg[DS].selector = (DBCS_TABLE >> 4);
        !          4066:                i386_load_segment_descriptor(cpustate, DS);
        !          4067:                REG16(SI) = (DBCS_TABLE & 0x0f);
        !          4068:                REG8(AL) = 0x00;
        !          4069:                break;
        !          4070:        default:
        !          4071:                REG16(AX) = 0x01;
        !          4072:                cpustate->CF = 1;
        !          4073:                break;
        !          4074:        }
        !          4075: }
        !          4076: 
        !          4077: inline void msdos_int_21h_65h()
        !          4078: {
        !          4079:        char tmp[0x10000];
        !          4080:        
        !          4081:        switch(REG8(AL)) {
        !          4082:        case 0x07:
        !          4083:                *(UINT8  *)(mem + cpustate->sreg[ES].base + REG16(DI) + 0) = 0x07;
        !          4084:                *(UINT16 *)(mem + cpustate->sreg[ES].base + REG16(DI) + 1) = (DBCS_TOP & 0x0f);
        !          4085:                *(UINT16 *)(mem + cpustate->sreg[ES].base + REG16(DI) + 3) = (DBCS_TOP >> 4);
        !          4086:                REG16(CX) = 0x05;
        !          4087:                break;
        !          4088:        case 0x20:
        !          4089:                sprintf(tmp, "%c", REG8(DL));
        !          4090:                my_strupr(tmp);
        !          4091:                REG8(DL) = tmp[0];
        !          4092:                break;
        !          4093:        case 0x21:
        !          4094:                memset(tmp, 0, sizeof(tmp));
        !          4095:                memcpy(tmp, mem + cpustate->sreg[DS].base + REG16(DX), REG16(CX));
        !          4096:                my_strupr(tmp);
        !          4097:                memcpy(mem + cpustate->sreg[DS].base + REG16(DX), tmp, REG16(CX));
        !          4098:                break;
        !          4099:        case 0x22:
        !          4100:                my_strupr((char *)(mem + cpustate->sreg[DS].base + REG16(DX)));
        !          4101:                break;
        !          4102:        default:
        !          4103:                REG16(AX) = 0x01;
        !          4104:                cpustate->CF = 1;
        !          4105:                break;
        !          4106:        }
        !          4107: }
        !          4108: 
        !          4109: inline void msdos_int_21h_66h()
        !          4110: {
        !          4111:        switch(REG8(AL)) {
        !          4112:        case 0x01:
        !          4113:                REG16(BX) = active_code_page;
        !          4114:                REG16(DX) = system_code_page;
        !          4115:                break;
        !          4116:        case 0x02:
        !          4117:                if(active_code_page == REG16(BX)) {
        !          4118:                        REG16(AX) = 0xeb41;
        !          4119:                } else if(_setmbcp(REG16(BX)) == 0) {
        !          4120:                        active_code_page = REG16(BX);
        !          4121:                        msdos_dbcs_table_update();
        !          4122:                        REG16(AX) = 0xeb41;
        !          4123:                } else {
        !          4124:                        REG16(AX) = 0x25;
        !          4125:                        cpustate->CF = 1;
        !          4126:                }
        !          4127:                break;
        !          4128:        default:
        !          4129:                REG16(AX) = 0x01;
        !          4130:                cpustate->CF = 1;
        !          4131:                break;
        !          4132:        }
        !          4133: }
        !          4134: 
        !          4135: inline void msdos_int_21h_67h()
        !          4136: {
        !          4137:        process_t *process = msdos_process_info_get(current_psp);
        !          4138:        
        !          4139:        if(REG16(BX) <= MAX_FILES) {
        !          4140:                process->max_files = max(REG16(BX), 20);
        !          4141:        } else {
        !          4142:                REG16(AX) = 0x08;
        !          4143:                cpustate->CF = 1;
        !          4144:        }
        !          4145: }
        !          4146: 
        !          4147: inline void msdos_int_21h_68h()
        !          4148: {
        !          4149:        process_t *process = msdos_process_info_get(current_psp);
        !          4150:        
        !          4151:        if(REG16(BX) < process->max_files && file_handler[REG16(BX)].valid) {
        !          4152:                // fflush(_fdopen(REG16(BX), ""));
        !          4153:        } else {
        !          4154:                REG16(AX) = 0x06;
        !          4155:                cpustate->CF = 1;
        !          4156:        }
        !          4157: }
        !          4158: 
        !          4159: inline void msdos_int_21h_69h()
        !          4160: {
        !          4161:        drive_info_t *info = (drive_info_t *)(mem + cpustate->sreg[DS].base + REG16(DX));
        !          4162:        char path[] = "A:\\";
        !          4163:        char volume_label[MAX_PATH];
        !          4164:        DWORD serial_number = 0;
        !          4165:        char file_system[MAX_PATH];
        !          4166:        
        !          4167:        if(REG8(BL) == 0) {
        !          4168:                path[0] = 'A' + _getdrive() - 1;
        !          4169:        } else {
        !          4170:                path[0] = 'A' + REG8(BL) - 1;
        !          4171:        }
        !          4172:        
        !          4173:        switch(REG8(AL)) {
        !          4174:        case 0x00:
        !          4175:                if(GetVolumeInformation(path, volume_label, MAX_PATH, &serial_number, NULL, NULL, file_system, MAX_PATH)) {
        !          4176:                        info->info_level = 0;
        !          4177:                        info->serial_number = serial_number;
        !          4178:                        memset(info->volume_label, 0x20, 11);
        !          4179:                        memcpy(info->volume_label, volume_label, min(strlen(volume_label), 11));
        !          4180:                        memset(info->file_system, 0x20, 8);
        !          4181:                        memcpy(info->file_system, file_system, min(strlen(file_system), 8));
        !          4182:                } else {
        !          4183:                        REG16(AX) = errno;
        !          4184:                        cpustate->CF = 1;
        !          4185:                }
        !          4186:                break;
        !          4187:        case 0x01:
        !          4188:                REG16(AX) = 0x03;
        !          4189:                cpustate->CF = 1;
        !          4190:        }
        !          4191: }
        !          4192: 
        !          4193: inline void msdos_int_21h_6ah()
        !          4194: {
        !          4195:        REG8(AH) = 0x68;
        !          4196:        msdos_int_21h_68h();
        !          4197: }
        !          4198: 
        !          4199: inline void msdos_int_21h_6bh()
        !          4200: {
        !          4201:        REG8(AL) = 0;
        !          4202: }
        !          4203: 
        !          4204: inline void msdos_int_21h_6ch(int lfn)
        !          4205: {
        !          4206:        char *path = msdos_local_file_path((char *)(mem + cpustate->sreg[DS].base + REG16(SI)), lfn);
        !          4207:        int mode = REG8(BL) & 0x03;
        !          4208:        
        !          4209:        if(mode < 0x03) {
        !          4210:                if(_access(path, 0) == 0) {
        !          4211:                        // file exists
        !          4212:                        if(REG8(DL) & 1) {
        !          4213:                                int fd = _open(path, file_mode[mode].mode);
        !          4214:                                
        !          4215:                                if(fd != -1) {
        !          4216:                                        REG16(AX) = fd;
        !          4217:                                        REG16(CX) = 1;
        !          4218:                                        msdos_file_handler_open(fd, path, _isatty(fd), mode, msdos_drive_number(path), current_psp);
        !          4219:                                } else {
        !          4220:                                        REG16(AX) = errno;
        !          4221:                                        cpustate->CF = 1;
        !          4222:                                }
        !          4223:                        } else if(REG8(DL) & 2) {
        !          4224:                                int attr = GetFileAttributes(path);
        !          4225:                                int fd = -1;
        !          4226:                                
        !          4227:                                if(_stricmp(path, "CON") == 0) {
        !          4228:                                        fd = _open(path, file_mode[mode].mode);
        !          4229:                                } else {
        !          4230:                                        fd = _open(path, _O_RDWR | _O_BINARY | _O_CREAT | _O_TRUNC, _S_IREAD | _S_IWRITE);
        !          4231:                                }
        !          4232:                                if(fd != -1) {
        !          4233:                                        if(attr == -1) {
        !          4234:                                                attr = msdos_file_attribute_create(REG16(CX)) & ~FILE_ATTRIBUTE_READONLY;
        !          4235:                                        }
        !          4236:                                        SetFileAttributes(path, attr);
        !          4237:                                        REG16(AX) = fd;
        !          4238:                                        REG16(CX) = 3;
        !          4239:                                        msdos_file_handler_open(fd, path, _isatty(fd), 2, msdos_drive_number(path), current_psp);
        !          4240:                                } else {
        !          4241:                                        REG16(AX) = errno;
        !          4242:                                        cpustate->CF = 1;
        !          4243:                                }
        !          4244:                        } else {
        !          4245:                                REG16(AX) = 0x50;
        !          4246:                                cpustate->CF = 1;
        !          4247:                        }
        !          4248:                } else {
        !          4249:                        // file not exists
        !          4250:                        if(REG8(DL) & 0x10) {
        !          4251:                                int fd = _open(path, _O_RDWR | _O_BINARY | _O_CREAT | _O_TRUNC, _S_IREAD | _S_IWRITE);
        !          4252:                                
        !          4253:                                if(fd != -1) {
        !          4254:                                        SetFileAttributes(path, msdos_file_attribute_create(REG16(CX)) & ~FILE_ATTRIBUTE_READONLY);
        !          4255:                                        REG16(AX) = fd;
        !          4256:                                        REG16(CX) = 2;
        !          4257:                                        msdos_file_handler_open(fd, path, _isatty(fd), 2, msdos_drive_number(path), current_psp);
        !          4258:                                } else {
        !          4259:                                        REG16(AX) = errno;
        !          4260:                                        cpustate->CF = 1;
        !          4261:                                }
        !          4262:                        } else {
        !          4263:                                REG16(AX) = 0x02;
        !          4264:                                cpustate->CF = 1;
        !          4265:                        }
        !          4266:                }
        !          4267:        } else {
        !          4268:                REG16(AX) = 0x0c;
        !          4269:                cpustate->CF = 1;
        !          4270:        }
        !          4271: }
        !          4272: 
        !          4273: inline void msdos_int_21h_710dh()
        !          4274: {
        !          4275:        // reset drive
        !          4276: }
        !          4277: 
        !          4278: inline void msdos_int_21h_714eh()
        !          4279: {
        !          4280:        process_t *process = msdos_process_info_get(current_psp);
        !          4281:        find_lfn_t *find = (find_lfn_t *)(mem + cpustate->sreg[ES].base + REG16(DI));
        !          4282:        char *path = (char *)(mem + cpustate->sreg[DS].base + REG16(DX));
        !          4283:        WIN32_FIND_DATA fd;
        !          4284:        
        !          4285:        if(process->find_handle != INVALID_HANDLE_VALUE) {
        !          4286:                FindClose(process->find_handle);
        !          4287:                process->find_handle = INVALID_HANDLE_VALUE;
        !          4288:        }
        !          4289:        strcpy(process->volume_label, msdos_volume_label(path));
        !          4290:        process->allowable_mask = REG8(CL);
        !          4291:        process->required_mask = REG8(CH);
        !          4292:        
        !          4293:        if((process->allowable_mask & 8) && !msdos_match_volume_label(path, process->volume_label) && !msdos_match_volume_label(path, msdos_short_volume_label(process->volume_label))) {
        !          4294:                process->allowable_mask &= ~8;
        !          4295:        }
        !          4296:        if((process->find_handle = FindFirstFile(path, &fd)) != INVALID_HANDLE_VALUE) {
        !          4297:                while(!msdos_find_file_check_attribute(fd.dwFileAttributes, process->allowable_mask, process->required_mask)) {
        !          4298:                        if(!FindNextFile(process->find_handle, &fd)) {
        !          4299:                                FindClose(process->find_handle);
        !          4300:                                process->find_handle = INVALID_HANDLE_VALUE;
        !          4301:                                break;
        !          4302:                        }
        !          4303:                }
        !          4304:        }
        !          4305:        if(process->find_handle != INVALID_HANDLE_VALUE) {
        !          4306:                find->attrib = fd.dwFileAttributes;
        !          4307:                msdos_find_file_conv_local_time(&fd);
        !          4308:                if(REG16(SI) == 0) {
        !          4309:                        find->ctime_lo.dw = fd.ftCreationTime.dwLowDateTime;
        !          4310:                        find->ctime_hi.dw = fd.ftCreationTime.dwHighDateTime;
        !          4311:                        find->atime_lo.dw = fd.ftLastAccessTime.dwLowDateTime;
        !          4312:                        find->atime_hi.dw = fd.ftLastAccessTime.dwHighDateTime;
        !          4313:                        find->mtime_lo.dw = fd.ftLastWriteTime.dwLowDateTime;
        !          4314:                        find->mtime_hi.dw = fd.ftLastWriteTime.dwHighDateTime;
        !          4315:                } else {
        !          4316:                        FileTimeToDosDateTime(&fd.ftCreationTime, &find->ctime_lo.w.h, &find->ctime_lo.w.l);
        !          4317:                        FileTimeToDosDateTime(&fd.ftLastAccessTime, &find->atime_lo.w.h, &find->atime_lo.w.l);
        !          4318:                        FileTimeToDosDateTime(&fd.ftLastWriteTime, &find->mtime_lo.w.h, &find->mtime_lo.w.l);
        !          4319:                }
        !          4320:                find->size_hi = fd.nFileSizeHigh;
        !          4321:                find->size_lo = fd.nFileSizeLow;
        !          4322:                strcpy(find->full_name, fd.cFileName);
        !          4323:                strcpy(find->short_name, msdos_short_path(fd.cFileName));
        !          4324:        } else if(process->allowable_mask & 8) {
        !          4325:                // volume label
        !          4326:                find->attrib = 8;
        !          4327:                find->size_hi = find->size_lo = 0;
        !          4328:                strcpy(find->full_name, process->volume_label);
        !          4329:                strcpy(find->short_name, msdos_short_volume_label(process->volume_label));
        !          4330:                process->allowable_mask &= ~8;
        !          4331:        } else {
        !          4332:                REG16(AX) = 0x12;       // NOTE: return 0x02 if file path is invalid
        !          4333:                cpustate->CF = 1;
        !          4334:        }
        !          4335: }
        !          4336: 
        !          4337: inline void msdos_int_21h_714fh()
        !          4338: {
        !          4339:        process_t *process = msdos_process_info_get(current_psp);
        !          4340:        find_lfn_t *find = (find_lfn_t *)(mem + cpustate->sreg[ES].base + REG16(DI));
        !          4341:        WIN32_FIND_DATA fd;
        !          4342:        
        !          4343:        if(process->find_handle != INVALID_HANDLE_VALUE) {
        !          4344:                if(FindNextFile(process->find_handle, &fd)) {
        !          4345:                        while(!msdos_find_file_check_attribute(fd.dwFileAttributes, process->allowable_mask, process->required_mask)) {
        !          4346:                                if(!FindNextFile(process->find_handle, &fd)) {
        !          4347:                                        FindClose(process->find_handle);
        !          4348:                                        process->find_handle = INVALID_HANDLE_VALUE;
        !          4349:                                        break;
        !          4350:                                }
        !          4351:                        }
        !          4352:                } else {
        !          4353:                        FindClose(process->find_handle);
        !          4354:                        process->find_handle = INVALID_HANDLE_VALUE;
        !          4355:                }
        !          4356:        }
        !          4357:        if(process->find_handle != INVALID_HANDLE_VALUE) {
        !          4358:                find->attrib = fd.dwFileAttributes;
        !          4359:                msdos_find_file_conv_local_time(&fd);
        !          4360:                if(REG16(SI) == 0) {
        !          4361:                        find->ctime_lo.dw = fd.ftCreationTime.dwLowDateTime;
        !          4362:                        find->ctime_hi.dw = fd.ftCreationTime.dwHighDateTime;
        !          4363:                        find->atime_lo.dw = fd.ftLastAccessTime.dwLowDateTime;
        !          4364:                        find->atime_hi.dw = fd.ftLastAccessTime.dwHighDateTime;
        !          4365:                        find->mtime_lo.dw = fd.ftLastWriteTime.dwLowDateTime;
        !          4366:                        find->mtime_hi.dw = fd.ftLastWriteTime.dwHighDateTime;
        !          4367:                } else {
        !          4368:                        FileTimeToDosDateTime(&fd.ftCreationTime, &find->ctime_lo.w.h, &find->ctime_lo.w.l);
        !          4369:                        FileTimeToDosDateTime(&fd.ftLastAccessTime, &find->atime_lo.w.h, &find->atime_lo.w.l);
        !          4370:                        FileTimeToDosDateTime(&fd.ftLastWriteTime, &find->mtime_lo.w.h, &find->mtime_lo.w.l);
        !          4371:                }
        !          4372:                find->size_hi = fd.nFileSizeHigh;
        !          4373:                find->size_lo = fd.nFileSizeLow;
        !          4374:                strcpy(find->full_name, fd.cFileName);
        !          4375:                strcpy(find->short_name, msdos_short_path(fd.cFileName));
        !          4376:        } else if(process->allowable_mask & 8) {
        !          4377:                // volume label
        !          4378:                find->attrib = 8;
        !          4379:                find->size_hi = find->size_lo = 0;
        !          4380:                strcpy(find->full_name, process->volume_label);
        !          4381:                strcpy(find->short_name, msdos_short_volume_label(process->volume_label));
        !          4382:                process->allowable_mask &= ~8;
        !          4383:        } else {
        !          4384:                REG16(AX) = 0x12;
        !          4385:                cpustate->CF = 1;
        !          4386:        }
        !          4387: }
        !          4388: 
        !          4389: inline void msdos_int_21h_71a0h()
        !          4390: {
        !          4391:        DWORD max_component_len, file_sys_flag;
        !          4392:        
        !          4393:        if(GetVolumeInformation((char *)(mem + cpustate->sreg[DS].base + REG16(DX)), NULL, 0, NULL, &max_component_len, &file_sys_flag, (char *)(mem + cpustate->sreg[ES].base + REG16(DI)), REG16(CX))) {
        !          4394:                REG16(BX) = (UINT16)file_sys_flag;
        !          4395:                REG16(CX) = (UINT16)max_component_len;          // 255
        !          4396:                REG16(DX) = (UINT16)max_component_len + 5;      // 260
        !          4397:        } else {
        !          4398:                REG16(AX) = (UINT16)GetLastError();
        !          4399:                cpustate->CF = 1;
        !          4400:        }
        !          4401: }
        !          4402: 
        !          4403: inline void msdos_int_21h_71a1h()
        !          4404: {
        !          4405:        process_t *process = msdos_process_info_get(current_psp);
        !          4406:        find_t *find = (find_t *)(mem + (process->dta.w.h << 4) + process->dta.w.l);
        !          4407:        
        !          4408:        if(process->find_handle != INVALID_HANDLE_VALUE) {
        !          4409:                FindClose(process->find_handle);
        !          4410:                process->find_handle = INVALID_HANDLE_VALUE;
        !          4411:        }
        !          4412: }
        !          4413: 
        !          4414: inline void msdos_int_21h_71a6h()
        !          4415: {
        !          4416:        process_t *process = msdos_process_info_get(current_psp);
        !          4417:        UINT8 *buffer = (UINT8 *)(mem + cpustate->sreg[DS].base + REG16(DX));
        !          4418:        struct _stat64 status;
        !          4419:        DWORD serial_number = 0;
        !          4420:        
        !          4421:        if(REG16(BX) < process->max_files && file_handler[REG16(BX)].valid) {
        !          4422:                if(_fstat64(REG16(BX), &status) == 0) {
        !          4423:                        if(file_handler[REG16(BX)].path[1] == ':') {
        !          4424:                                // NOTE: we need to consider the network file path "\\host\share\"
        !          4425:                                char volume[] = "A:\\";
        !          4426:                                volume[0] = file_handler[REG16(BX)].path[1];
        !          4427:                                GetVolumeInformation(volume, NULL, 0, &serial_number, NULL, NULL, NULL, 0);
        !          4428:                        }
        !          4429:                        *(UINT32 *)(buffer + 0x00) = GetFileAttributes(file_handler[REG16(BX)].path);
        !          4430:                        *(UINT32 *)(buffer + 0x04) = (UINT32)(status.st_ctime & 0xffffffff);
        !          4431:                        *(UINT32 *)(buffer + 0x08) = (UINT32)((status.st_ctime >> 32) & 0xffffffff);
        !          4432:                        *(UINT32 *)(buffer + 0x0c) = (UINT32)(status.st_atime & 0xffffffff);
        !          4433:                        *(UINT32 *)(buffer + 0x10) = (UINT32)((status.st_atime >> 32) & 0xffffffff);
        !          4434:                        *(UINT32 *)(buffer + 0x14) = (UINT32)(status.st_mtime & 0xffffffff);
        !          4435:                        *(UINT32 *)(buffer + 0x18) = (UINT32)((status.st_mtime >> 32) & 0xffffffff);
        !          4436:                        *(UINT32 *)(buffer + 0x1c) = serial_number;
        !          4437:                        *(UINT32 *)(buffer + 0x20) = (UINT32)((status.st_size >> 32) & 0xffffffff);
        !          4438:                        *(UINT32 *)(buffer + 0x24) = (UINT32)(status.st_size & 0xffffffff);
        !          4439:                        *(UINT32 *)(buffer + 0x28) = status.st_nlink;
        !          4440:                        // this is dummy id and it will be changed when it is reopend...
        !          4441:                        *(UINT32 *)(buffer + 0x2c) = 0;
        !          4442:                        *(UINT32 *)(buffer + 0x30) = file_handler[REG16(BX)].id;
        !          4443:                } else {
        !          4444:                        REG16(AX) = errno;
        !          4445:                        cpustate->CF = 1;
        !          4446:                }
        !          4447:        } else {
        !          4448:                REG16(AX) = 0x06;
        !          4449:                cpustate->CF = 1;
        !          4450:        }
        !          4451: }
        !          4452: 
        !          4453: inline void msdos_int_21h_71a7h()
        !          4454: {
        !          4455:        switch(REG8(BL)) {
        !          4456:        case 0x00:
        !          4457:                if(!FileTimeToDosDateTime((FILETIME *)(mem + cpustate->sreg[DS].base + REG16(SI)), &REG16(DX), &REG16(CX))) {
        !          4458:                        REG16(AX) = (UINT16)GetLastError();
        !          4459:                        cpustate->CF = 1;
        !          4460:                }
        !          4461:                break;
        !          4462:        case 0x01:
        !          4463:                // NOTE: we need to check BH that shows 10-millisecond untils past time in CX
        !          4464:                if(!DosDateTimeToFileTime(REG16(DX), REG16(CX), (FILETIME *)(mem + cpustate->sreg[ES].base + REG16(DI)))) {
        !          4465:                        REG16(AX) = (UINT16)GetLastError();
        !          4466:                        cpustate->CF = 1;
        !          4467:                }
        !          4468:                break;
        !          4469:        default:
        !          4470:                REG16(AX) = 0x01;
        !          4471:                cpustate->CF = 1;
        !          4472:                break;
        !          4473:        }
        !          4474: }
        !          4475: 
        !          4476: inline void msdos_int_21h_71a8h()
        !          4477: {
        !          4478:        if(REG8(DH) == 0) {
        !          4479:                char tmp[MAX_PATH], fcb[MAX_PATH];
        !          4480:                strcpy(tmp, msdos_short_path((char *)(mem + cpustate->sreg[DS].base + REG16(SI))));
        !          4481:                memset(fcb, 0x20, sizeof(fcb));
        !          4482:                int len = strlen(tmp);
        !          4483:                int pos = 0;
        !          4484:                for(int i = 0; i < len; i++) {
        !          4485:                        if(tmp[i] == '.') {
        !          4486:                                pos = 8;
        !          4487:                        } else {
        !          4488:                                if(msdos_lead_byte_check(tmp[i])) {
        !          4489:                                        fcb[pos++] = tmp[i++];
        !          4490:                                }
        !          4491:                                fcb[pos++] = tmp[i];
        !          4492:                        }
        !          4493:                }
        !          4494:                memcpy((char *)(mem + cpustate->sreg[ES].base + REG16(DI)), fcb, 11);
        !          4495:        } else {
        !          4496:                strcpy((char *)(mem + cpustate->sreg[ES].base + REG16(DI)), msdos_short_path((char *)(mem + cpustate->sreg[DS].base + REG16(SI))));
        !          4497:        }
        !          4498: }
        !          4499: 
        !          4500: inline void msdos_int_21h_7303h()
        !          4501: {
        !          4502:        char *path = (char *)(mem + cpustate->sreg[DS].base + REG16(DX));
        !          4503:        ext_space_info_t *info = (ext_space_info_t *)(mem + cpustate->sreg[ES].base + REG16(DI));
        !          4504:        DWORD sectors_per_cluster, bytes_per_sector, free_clusters, total_clusters;
        !          4505:        
        !          4506:        if(GetDiskFreeSpace(path, &sectors_per_cluster, &bytes_per_sector, &free_clusters, &total_clusters)) {
        !          4507:                info->size_of_structure = sizeof(ext_space_info_t);
        !          4508:                info->structure_version = 0;
        !          4509:                info->sectors_per_cluster = sectors_per_cluster;
        !          4510:                info->bytes_per_sector = bytes_per_sector;
        !          4511:                info->available_clusters_on_drive = free_clusters;
        !          4512:                info->total_clusters_on_drive = total_clusters;
        !          4513:                info->available_sectors_on_drive = sectors_per_cluster * free_clusters;
        !          4514:                info->total_sectors_on_drive = sectors_per_cluster * total_clusters;
        !          4515:                info->available_allocation_units = free_clusters;       // ???
        !          4516:                info->total_allocation_units = total_clusters;          // ???
        !          4517:        } else {
        !          4518:                REG16(AX) = errno;
        !          4519:                cpustate->CF = 1;
        !          4520:        }
        !          4521: }
        !          4522: 
        !          4523: inline void msdos_int_25h()
        !          4524: {
        !          4525:        UINT16 seg, ofs;
        !          4526:        DWORD dwSize;
        !          4527:        
        !          4528:        I386OP(pushf)(cpustate);
        !          4529:        
        !          4530:        if(!(REG8(AL) < 26)) {
        !          4531:                REG8(AL) = 0x01; // unit unknown
        !          4532:                cpustate->CF = 1;
        !          4533:        } else if(!msdos_drive_param_block_update(REG8(AL), &seg, &ofs, 0)) {
        !          4534:                REG8(AL) = 0x02; // drive not ready
        !          4535:                cpustate->CF = 1;
        !          4536:        } else {
        !          4537:                dpb_t *dpb = (dpb_t *)(mem + (seg << 4) + ofs);
        !          4538:                char dev[64];
        !          4539:                sprintf(dev, "\\\\.\\%c:", 'A' + REG8(AL));
        !          4540:                
        !          4541:                HANDLE hFile = CreateFile(dev, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING, NULL);
        !          4542:                if(hFile == INVALID_HANDLE_VALUE) {
        !          4543:                        REG8(AL) = 0x02; // drive not ready
        !          4544:                        cpustate->CF = 1;
        !          4545:                } else {
        !          4546:                        if(DeviceIoControl(hFile, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &dwSize, NULL) == 0) {
        !          4547:                                REG8(AL) = 0x02; // drive not ready
        !          4548:                                cpustate->CF = 1;
        !          4549:                        } else if(SetFilePointer(hFile, REG16(DX) * dpb->bytes_per_sector, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
        !          4550:                                REG8(AL) = 0x08; // sector not found
        !          4551:                                cpustate->CF = 1;
        !          4552:                        } else if(ReadFile(hFile, mem + cpustate->sreg[DS].base + REG16(BX), REG16(CX) * dpb->bytes_per_sector, &dwSize, NULL) == 0) {
        !          4553:                                REG8(AL) = 0x0b; // read error
        !          4554:                                cpustate->CF = 1;
        !          4555:                        }
        !          4556:                        CloseHandle(hFile);
        !          4557:                }
        !          4558:        }
        !          4559: }
        !          4560: 
        !          4561: inline void msdos_int_26h()
        !          4562: {
        !          4563:        // this operation may cause serious damage for drives, so always returns error...
        !          4564:        UINT16 seg, ofs;
        !          4565:        DWORD dwSize;
        !          4566:        
        !          4567:        I386OP(pushf)(cpustate);
        !          4568:        
        !          4569:        if(!(REG8(AL) < 26)) {
        !          4570:                REG8(AL) = 0x01; // unit unknown
        !          4571:                cpustate->CF = 1;
        !          4572:        } else if(!msdos_drive_param_block_update(REG8(AL), &seg, &ofs, 0)) {
        !          4573:                REG8(AL) = 0x02; // drive not ready
        !          4574:                cpustate->CF = 1;
        !          4575:        } else {
        !          4576:                dpb_t *dpb = (dpb_t *)(mem + (seg << 4) + ofs);
        !          4577:                char dev[64];
        !          4578:                sprintf(dev, "\\\\.\\%c:", 'A' + REG8(AL));
        !          4579:                
        !          4580:                if(dpb->media_type == 0xf8) {
        !          4581:                        // this drive is not a floppy
        !          4582:                        REG8(AL) = 0x02; // drive not ready
        !          4583:                        cpustate->CF = 1;
        !          4584:                } else {
        !          4585:                        HANDLE hFile = CreateFile(dev, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING, NULL);
        !          4586:                        if(hFile == INVALID_HANDLE_VALUE) {
        !          4587:                                REG8(AL) = 0x02; // drive not ready
        !          4588:                                cpustate->CF = 1;
        !          4589:                        } else {
        !          4590:                                if(DeviceIoControl(hFile, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &dwSize, NULL) == 0) {
        !          4591:                                        REG8(AL) = 0x02; // drive not ready
        !          4592:                                        cpustate->CF = 1;
        !          4593:                                } else if(SetFilePointer(hFile, REG16(DX) * dpb->bytes_per_sector, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
        !          4594:                                        REG8(AL) = 0x08; // sector not found
        !          4595:                                        cpustate->CF = 1;
        !          4596:                                } else if(WriteFile(hFile, mem + cpustate->sreg[DS].base + REG16(BX), REG16(CX) * dpb->bytes_per_sector, &dwSize, NULL) == 0) {
        !          4597:                                        REG8(AL) = 0x0a; // write error
        !          4598:                                        cpustate->CF = 1;
        !          4599:                                }
        !          4600:                                CloseHandle(hFile);
        !          4601:                        }
        !          4602:                }
        !          4603:        }
        !          4604: }
        !          4605: 
        !          4606: inline void msdos_int_27h()
        !          4607: {
        !          4608:        int mcb_seg = cpustate->sreg[CS].selector - 1;
        !          4609:        mcb_t *mcb = (mcb_t *)(mem + (mcb_seg << 4));
        !          4610:        
        !          4611:        mcb->paragraphs = (REG16(DX) >> 4);
        !          4612:        mcb_seg += mcb->paragraphs + 1;
        !          4613:        msdos_mcb_create(mcb_seg, 'Z', 0, (MEMORY_END >> 4) - mcb_seg - 1);
        !          4614:        
        !          4615:        msdos_process_terminate(cpustate->sreg[CS].selector, retval | 0x300, 0);
        !          4616: }
        !          4617: 
        !          4618: inline void msdos_int_29h()
        !          4619: {
        !          4620:        msdos_putch(REG8(AL));
        !          4621: }
        !          4622: 
        !          4623: inline void msdos_int_2eh()
        !          4624: {
        !          4625:        char tmp[MAX_PATH], command[MAX_PATH], opt[MAX_PATH];
        !          4626:        memset(tmp, 0, sizeof(tmp));
        !          4627:        strcpy(tmp, (char *)(mem + cpustate->sreg[DS].base + REG16(SI)));
        !          4628:        char *token = my_strtok(tmp, " ");
        !          4629:        strcpy(command, token);
        !          4630:        strcpy(opt, token + strlen(token) + 1);
        !          4631:        
        !          4632:        param_block_t *param = (param_block_t *)(mem + WORK_TOP);
        !          4633:        param->env_seg = 0;
        !          4634:        param->cmd_line.w.l = 44;
        !          4635:        param->cmd_line.w.h = (WORK_TOP >> 4);
        !          4636:        param->fcb1.w.l = 24;
        !          4637:        param->fcb1.w.h = (WORK_TOP >> 4);
        !          4638:        param->fcb2.w.l = 24;
        !          4639:        param->fcb2.w.h = (WORK_TOP >> 4);
        !          4640:        
        !          4641:        memset(mem + WORK_TOP + 24, 0x20, 20);
        !          4642:        
        !          4643:        cmd_line_t *cmd_line = (cmd_line_t *)(mem + WORK_TOP + 44);
        !          4644:        cmd_line->len = strlen(opt);
        !          4645:        strcpy(cmd_line->cmd, opt);
        !          4646:        cmd_line->cmd[cmd_line->len] = 0x0d;
        !          4647:        
        !          4648:        msdos_process_exec(command, param, 0);
        !          4649:        REG8(AL) = 0;
        !          4650: }
        !          4651: 
        !          4652: inline void msdos_int_2fh_16h()
        !          4653: {
        !          4654:        switch(REG8(AL)) {
        !          4655:        case 0x00:
        !          4656:                {
        !          4657:                        OSVERSIONINFO osvi;
        !          4658:                        ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
        !          4659:                        osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
        !          4660:                        GetVersionEx(&osvi);
        !          4661:                        REG8(AL) = osvi.dwMajorVersion;
        !          4662:                        REG8(AH) = osvi.dwMinorVersion;
        !          4663:                }
        !          4664:                break;
        !          4665:        default:
        !          4666:                REG16(AX) = 0x01;
        !          4667:                cpustate->CF = 1;
        !          4668:                break;
        !          4669:        }
        !          4670: }
        !          4671: 
        !          4672: inline void msdos_int_2fh_1ah()
        !          4673: {
        !          4674:        switch(REG8(AL)) {
        !          4675:        case 0x00:
        !          4676:                // ansi.sys is installed
        !          4677:                REG8(AL) = 0xff;
        !          4678:                break;
        !          4679:        default:
        !          4680:                REG16(AX) = 0x01;
        !          4681:                cpustate->CF = 1;
        !          4682:                break;
        !          4683:        }
        !          4684: }
        !          4685: 
        !          4686: inline void msdos_int_2fh_43h()
        !          4687: {
        !          4688:        switch(REG8(AL)) {
        !          4689:        case 0x00:
        !          4690:                // xms is not installed
        !          4691:                REG8(AL) = 0;
        !          4692:                break;
        !          4693:        default:
        !          4694:                REG16(AX) = 0x01;
        !          4695:                cpustate->CF = 1;
        !          4696:                break;
        !          4697:        }
        !          4698: }
        !          4699: 
        !          4700: inline void msdos_int_2fh_4ah()
        !          4701: {
        !          4702:        switch(REG8(AL)) {
        !          4703:        case 0x01:
        !          4704:        case 0x02:
        !          4705:                // hma is not installed
        !          4706:                REG16(BX) = 0;
        !          4707:                cpustate->sreg[ES].selector = 0xffff;
        !          4708:                i386_load_segment_descriptor(cpustate, ES);
        !          4709:                REG16(DI) = 0xffff;
        !          4710:                break;
        !          4711:        default:
        !          4712:                REG16(AX) = 0x01;
        !          4713:                cpustate->CF = 1;
        !          4714:                break;
        !          4715:        }
        !          4716: }
        !          4717: 
        !          4718: inline void msdos_int_2fh_4fh()
        !          4719: {
        !          4720:        switch(REG8(AL)) {
        !          4721:        case 0x00:
        !          4722:                REG16(AX) = 0;
        !          4723:                REG8(DL) = 1;   // major version
        !          4724:                REG8(DH) = 0;   // minor version
        !          4725:                break;
        !          4726:        case 0x01:
        !          4727:                REG16(AX) = 0;
        !          4728:                REG16(BX) = active_code_page;
        !          4729:                break;
        !          4730:        default:
        !          4731:                REG16(AX) = 0x01;
        !          4732:                cpustate->CF = 1;
        !          4733:                break;
        !          4734:        }
        !          4735: }
        !          4736: 
        !          4737: inline void msdos_int_2fh_aeh()
        !          4738: {
        !          4739:        switch(REG8(AL)) {
        !          4740:        case 0x00:
        !          4741:                REG8(AL) = 0;
        !          4742:                break;
        !          4743:        case 0x01:
        !          4744:                {
        !          4745:                        char command[MAX_PATH];
        !          4746:                        memset(command, 0, sizeof(command));
        !          4747:                        memcpy(command, mem + cpustate->sreg[DS].base + REG16(SI) + 1, mem[cpustate->sreg[DS].base + REG16(SI)]);
        !          4748:                        
        !          4749:                        param_block_t *param = (param_block_t *)(mem + WORK_TOP);
        !          4750:                        param->env_seg = 0;
        !          4751:                        param->cmd_line.w.l = 44;
        !          4752:                        param->cmd_line.w.h = (WORK_TOP >> 4);
        !          4753:                        param->fcb1.w.l = 24;
        !          4754:                        param->fcb1.w.h = (WORK_TOP >> 4);
        !          4755:                        param->fcb2.w.l = 24;
        !          4756:                        param->fcb2.w.h = (WORK_TOP >> 4);
        !          4757:                        
        !          4758:                        memset(mem + WORK_TOP + 24, 0x20, 20);
        !          4759:                        
        !          4760:                        cmd_line_t *cmd_line = (cmd_line_t *)(mem + WORK_TOP + 44);
        !          4761:                        cmd_line->len = mem[cpustate->sreg[DS].base + REG16(BX) + 1];
        !          4762:                        memcpy(cmd_line->cmd, mem + cpustate->sreg[DS].base + REG16(BX) + 2, cmd_line->len);
        !          4763:                        cmd_line->cmd[cmd_line->len] = 0x0d;
        !          4764:                        
        !          4765:                        if(msdos_process_exec(command, param, 0)) {
        !          4766:                                REG16(AX) = 0x02;
        !          4767:                                cpustate->CF = 1;
        !          4768:                        }
        !          4769:                }
        !          4770:                break;
        !          4771:        default:
        !          4772:                REG16(AX) = 0x01;
        !          4773:                cpustate->CF = 1;
        !          4774:                break;
        !          4775:        }
        !          4776: }
        !          4777: 
        !          4778: inline void msdos_int_2fh_b7h()
        !          4779: {
        !          4780:        switch(REG8(AL)) {
        !          4781:        case 0x00:
        !          4782:                // append is not installed
        !          4783:                REG8(AL) = 0;
        !          4784:                break;
        !          4785:        default:
        !          4786:                REG16(AX) = 0x01;
        !          4787:                cpustate->CF = 1;
        !          4788:                break;
        !          4789:        }
        !          4790: }
        !          4791: 
        !          4792: void msdos_syscall(unsigned num)
        !          4793: {
        !          4794:        switch(num) {
        !          4795:        case 0x00:
        !          4796:                error("division by zero\n");
        !          4797:                msdos_process_terminate(current_psp, (retval & 0xff) | 0x200, 1);
        !          4798:                break;
        !          4799:        case 0x04:
        !          4800:                error("overflow\n");
        !          4801:                msdos_process_terminate(current_psp, (retval & 0xff) | 0x200, 1);
        !          4802:                break;
        !          4803:        case 0x06:
        !          4804:                // NOTE: ish.com has illegal instruction...
        !          4805: //             error("illegal instruction\n");
        !          4806: //             msdos_process_terminate(current_psp, (retval & 0xff) | 0x200, 1);
        !          4807:                break;
        !          4808:        case 0x10:
        !          4809:                // PC BIOS - Video
        !          4810:                if(scr_width != 80 || scr_height != 25) {
        !          4811:                        CONSOLE_SCREEN_BUFFER_INFO csbi;
        !          4812:                        SMALL_RECT rect;
        !          4813:                        COORD co;
        !          4814:                        
        !          4815:                        GetConsoleScreenBufferInfo(hStdout, &csbi);
        !          4816:                        if(csbi.dwCursorPosition.Y > 24) {
        !          4817:                                SET_RECT(rect, 0, 0, scr_width - 1, scr_height - 1);
        !          4818:                                ReadConsoleOutput(hStdout, &scr_buf[0][0], scr_buf_size, scr_buf_pos, &rect);
        !          4819:                                for(int y = 0, y2 = csbi.dwCursorPosition.Y - 24; y < 25; y++, y2++) {
        !          4820:                                        for(int x = 0; x < 80; x++) {
        !          4821:                                                scr_buf[y][x] = scr_buf[y2][x];
        !          4822:                                        }
        !          4823:                                }
        !          4824:                                WriteConsoleOutput(hStdout, &scr_buf[0][0], scr_buf_size, scr_buf_pos, &rect);
        !          4825:                                
        !          4826:                                co.X = csbi.dwCursorPosition.X;
        !          4827:                                co.Y = 24;
        !          4828:                                SetConsoleCursorPosition(hStdout, co);
        !          4829:                                cursor_moved = true;
        !          4830:                        }
        !          4831:                        SET_RECT(rect, 0, 0, 79, 24);
        !          4832:                        co.X = 80;
        !          4833:                        co.Y = 25;
        !          4834:                        SetConsoleWindowInfo(hStdout, TRUE, &rect);
        !          4835:                        SetConsoleScreenBufferSize(hStdout, co);
        !          4836:                        scr_width = 80;
        !          4837:                        scr_height = 25;
        !          4838:                }
        !          4839:                cpustate->CF = 0;
        !          4840:                switch(REG8(AH)) {
        !          4841:                case 0x00: pcbios_int_10h_00h(); break;
        !          4842:                case 0x01: pcbios_int_10h_01h(); break;
        !          4843:                case 0x02: pcbios_int_10h_02h(); break;
        !          4844:                case 0x03: pcbios_int_10h_03h(); break;
        !          4845:                case 0x05: pcbios_int_10h_05h(); break;
        !          4846:                case 0x06: pcbios_int_10h_06h(); break;
        !          4847:                case 0x07: pcbios_int_10h_07h(); break;
        !          4848:                case 0x08: pcbios_int_10h_08h(); break;
        !          4849:                case 0x09: pcbios_int_10h_09h(); break;
        !          4850:                case 0x0a: pcbios_int_10h_0ah(); break;
        !          4851:                case 0x0b: break;
        !          4852:                case 0x0c: break;
        !          4853:                case 0x0d: break;
        !          4854:                case 0x0e: pcbios_int_10h_0eh(); break;
        !          4855:                case 0x0f: pcbios_int_10h_0fh(); break;
        !          4856:                case 0x10: break;
        !          4857:                case 0x11: break;
        !          4858:                case 0x12: REG8(AL) = 0x00; break;
        !          4859:                case 0x13: pcbios_int_10h_13h(); break;
        !          4860:                case 0x18: REG8(AL) = 0x86; break;
        !          4861:                case 0x1a: REG8(AL) = 0x00; break;
        !          4862:                case 0x1c: REG8(AL) = 0x00; break;
        !          4863:                case 0x1d: pcbios_int_10h_1dh(); break;
        !          4864:                case 0x82: pcbios_int_10h_82h(); break;
        !          4865:                case 0xfe: pcbios_int_10h_feh(); break;
        !          4866:                case 0xff: pcbios_int_10h_ffh(); break;
        !          4867:                default:
        !          4868:                        fatalerror("int %02xh (ax=%04xh bx=%04xh cx=%04xh dx=%04x)\n", num, REG16(AX), REG16(BX), REG16(CX), REG16(DX));
        !          4869:                        break;
        !          4870:                }
        !          4871:                break;
        !          4872:        case 0x11:
        !          4873:                // PC BIOS - Get Equipment List
        !          4874:                REG16(AX) = 0x20;
        !          4875:                break;
        !          4876:        case 0x12:
        !          4877:                // PC BIOS - Get Memory Size
        !          4878:                REG16(AX) = MEMORY_END / 1024;
        !          4879:                break;
        !          4880:        case 0x13:
        !          4881:                // PC BIOS - Disk
        !          4882: //             fatalerror("int %02xh (ax=%04xh bx=%04xh cx=%04xh dx=%04x)\n", num, REG16(AX), REG16(BX), REG16(CX), REG16(DX));
        !          4883:                REG8(AH) = 0xff;
        !          4884:                cpustate->CF = 1;
        !          4885:                break;
        !          4886:        case 0x14:
        !          4887:                // PC BIOS - Serial I/O
        !          4888: //             fatalerror("int %02xh (ax=%04xh bx=%04xh cx=%04xh dx=%04x)\n", num, REG16(AX), REG16(BX), REG16(CX), REG16(DX));
        !          4889:                REG8(AH) = 0xff;
        !          4890:                cpustate->CF = 1;
        !          4891:                break;
        !          4892:        case 0x15:
        !          4893:                // PC BIOS
        !          4894:                cpustate->CF = 0;
        !          4895:                switch(REG8(AH)) {
        !          4896:                case 0x23: pcbios_int_15h_23h(); break;
        !          4897:                case 0x24: pcbios_int_15h_24h(); break;
        !          4898:                case 0x49: pcbios_int_15h_49h(); break;
        !          4899:                case 0x86: pcbios_int_15h_86h(); break;
        !          4900:                case 0x87: pcbios_int_15h_87h(); break;
        !          4901:                case 0x88: pcbios_int_15h_88h(); break;
        !          4902:                case 0x89: pcbios_int_15h_89h(); break;
        !          4903:                case 0xc9: pcbios_int_15h_c9h(); break;
        !          4904:                case 0xca: pcbios_int_15h_cah(); break;
        !          4905:                default:
        !          4906: //                     fatalerror("int %02xh (ax=%04xh bx=%04xh cx=%04xh dx=%04x)\n", num, REG16(AX), REG16(BX), REG16(CX), REG16(DX));
        !          4907:                        REG8(AH)=0x86;
        !          4908:                        cpustate->CF = 1;
        !          4909:                        break;
        !          4910:                }
        !          4911:                break;
        !          4912:        case 0x16:
        !          4913:                // PC BIOS - Keyboard
        !          4914:                cpustate->CF = 0;
        !          4915:                switch(REG8(AH)) {
        !          4916:                case 0x00: pcbios_int_16h_00h(); break;
        !          4917:                case 0x01: pcbios_int_16h_01h(); break;
        !          4918:                case 0x02: pcbios_int_16h_02h(); break;
        !          4919:                case 0x03: pcbios_int_16h_03h(); break;
        !          4920:                case 0x05: pcbios_int_16h_05h(); break;
        !          4921:                case 0x10: pcbios_int_16h_00h(); break;
        !          4922:                case 0x11: pcbios_int_16h_01h(); break;
        !          4923:                case 0x12: pcbios_int_16h_12h(); break;
        !          4924:                case 0x13: pcbios_int_16h_13h(); break;
        !          4925:                case 0x14: pcbios_int_16h_14h(); break;
        !          4926:                default:
        !          4927:                        fatalerror("int %02xh (ax=%04xh bx=%04xh cx=%04xh dx=%04x)\n", num, REG16(AX), REG16(BX), REG16(CX), REG16(DX));
        !          4928:                        break;
        !          4929:                }
        !          4930:                break;
        !          4931:        case 0x17:
        !          4932:                // PC BIOS - Printer
        !          4933:                fatalerror("int %02xh (ax=%04xh bx=%04xh cx=%04xh dx=%04x)\n", num, REG16(AX), REG16(BX), REG16(CX), REG16(DX));
        !          4934:                break;
        !          4935:        case 0x1a:
        !          4936:                // PC BIOS - Timer
        !          4937:                cpustate->CF = 0;
        !          4938:                switch(REG8(AH)) {
        !          4939:                case 0x00: pcbios_int_1ah_00h(); break;
        !          4940:                case 0x01: break;
        !          4941:                case 0x02: pcbios_int_1ah_02h(); break;
        !          4942:                case 0x03: break;
        !          4943:                case 0x04: pcbios_int_1ah_04h(); break;
        !          4944:                case 0x05: break;
        !          4945:                case 0x0a: pcbios_int_1ah_0ah(); break;
        !          4946:                case 0x0b: break;
        !          4947:                default:
        !          4948:                        fatalerror("int %02xh (ax=%04xh bx=%04xh cx=%04xh dx=%04x)\n", num, REG16(AX), REG16(BX), REG16(CX), REG16(DX));
        !          4949:                        break;
        !          4950:                }
        !          4951:                break;
        !          4952:        case 0x20:
        !          4953:                msdos_process_terminate(cpustate->sreg[CS].selector, retval, 1);
        !          4954:                break;
        !          4955:        case 0x21:
        !          4956:                // MS-DOS System Call
        !          4957:                cpustate->CF = 0;
        !          4958:                switch(REG8(AH)) {
        !          4959:                case 0x00: msdos_int_21h_00h(); break;
        !          4960:                case 0x01: msdos_int_21h_01h(); break;
        !          4961:                case 0x02: msdos_int_21h_02h(); break;
        !          4962:                case 0x03: msdos_int_21h_03h(); break;
        !          4963:                case 0x04: msdos_int_21h_04h(); break;
        !          4964:                case 0x05: msdos_int_21h_05h(); break;
        !          4965:                case 0x06: msdos_int_21h_06h(); break;
        !          4966:                case 0x07: msdos_int_21h_07h(); break;
        !          4967:                case 0x08: msdos_int_21h_08h(); break;
        !          4968:                case 0x09: msdos_int_21h_09h(); break;
        !          4969:                case 0x0a: msdos_int_21h_0ah(); break;
        !          4970:                case 0x0b: msdos_int_21h_0bh(); break;
        !          4971:                case 0x0c: msdos_int_21h_0ch(); break;
        !          4972:                case 0x0d: msdos_int_21h_0dh(); break;
        !          4973:                case 0x0e: msdos_int_21h_0eh(); break;
        !          4974:                // 0x0f: open file with fcb
        !          4975:                // 0x10: close file with fcb
        !          4976:                case 0x11: msdos_int_21h_11h(); break;
        !          4977:                case 0x12: msdos_int_21h_12h(); break;
        !          4978:                case 0x13: msdos_int_21h_13h(); break;
        !          4979:                // 0x14: sequential read with fcb
        !          4980:                // 0x15: sequential write with fcb
        !          4981:                // 0x16: create new file with fcb
        !          4982:                // 0x17: rename file with fcb
        !          4983:                case 0x18: msdos_int_21h_18h(); break;
        !          4984:                case 0x19: msdos_int_21h_19h(); break;
        !          4985:                case 0x1a: msdos_int_21h_1ah(); break;
        !          4986:                case 0x1b: msdos_int_21h_1bh(); break;
        !          4987:                case 0x1c: msdos_int_21h_1ch(); break;
        !          4988:                case 0x1d: msdos_int_21h_1dh(); break;
        !          4989:                case 0x1e: msdos_int_21h_1eh(); break;
        !          4990:                case 0x1f: msdos_int_21h_1fh(); break;
        !          4991:                case 0x20: msdos_int_21h_20h(); break;
        !          4992:                // 0x21: random read with fcb
        !          4993:                // 0x22: randome write with fcb
        !          4994:                // 0x23: get file size with fcb
        !          4995:                // 0x24: set relative record field with fcb
        !          4996:                case 0x25: msdos_int_21h_25h(); break;
        !          4997:                case 0x26: msdos_int_21h_26h(); break;
        !          4998:                // 0x27: random block read with fcb
        !          4999:                // 0x28: random block write with fcb
        !          5000:                case 0x29: msdos_int_21h_29h(); break;
        !          5001:                case 0x2a: msdos_int_21h_2ah(); break;
        !          5002:                case 0x2b: msdos_int_21h_2bh(); break;
        !          5003:                case 0x2c: msdos_int_21h_2ch(); break;
        !          5004:                case 0x2d: msdos_int_21h_2dh(); break;
        !          5005:                case 0x2e: msdos_int_21h_2eh(); break;
        !          5006:                case 0x2f: msdos_int_21h_2fh(); break;
        !          5007:                case 0x30: msdos_int_21h_30h(); break;
        !          5008:                case 0x31: msdos_int_21h_31h(); break;
        !          5009:                case 0x32: msdos_int_21h_32h(); break;
        !          5010:                case 0x33: msdos_int_21h_33h(); break;
        !          5011:                // 0x34: get address of indos flag
        !          5012:                case 0x35: msdos_int_21h_35h(); break;
        !          5013:                case 0x36: msdos_int_21h_36h(); break;
        !          5014:                case 0x37: msdos_int_21h_37h(); break;
        !          5015:                // 0x38: get country-specific information
        !          5016:                case 0x39: msdos_int_21h_39h(0); break;
        !          5017:                case 0x3a: msdos_int_21h_3ah(0); break;
        !          5018:                case 0x3b: msdos_int_21h_3bh(0); break;
        !          5019:                case 0x3c: msdos_int_21h_3ch(); break;
        !          5020:                case 0x3d: msdos_int_21h_3dh(); break;
        !          5021:                case 0x3e: msdos_int_21h_3eh(); break;
        !          5022:                case 0x3f: msdos_int_21h_3fh(); break;
        !          5023:                case 0x40: msdos_int_21h_40h(); break;
        !          5024:                case 0x41: msdos_int_21h_41h(0); break;
        !          5025:                case 0x42: msdos_int_21h_42h(); break;
        !          5026:                case 0x43: msdos_int_21h_43h(0); break;
        !          5027:                case 0x44: msdos_int_21h_44h(); break;
        !          5028:                case 0x45: msdos_int_21h_45h(); break;
        !          5029:                case 0x46: msdos_int_21h_46h(); break;
        !          5030:                case 0x47: msdos_int_21h_47h(0); break;
        !          5031:                case 0x48: msdos_int_21h_48h(); break;
        !          5032:                case 0x49: msdos_int_21h_49h(); break;
        !          5033:                case 0x4a: msdos_int_21h_4ah(); break;
        !          5034:                case 0x4b: msdos_int_21h_4bh(); break;
        !          5035:                case 0x4c: msdos_int_21h_4ch(); break;
        !          5036:                case 0x4d: msdos_int_21h_4dh(); break;
        !          5037:                case 0x4e: msdos_int_21h_4eh(); break;
        !          5038:                case 0x4f: msdos_int_21h_4fh(); break;
        !          5039:                case 0x50: msdos_int_21h_50h(); break;
        !          5040:                case 0x51: msdos_int_21h_51h(); break;
        !          5041:                case 0x52: msdos_int_21h_52h(); break;
        !          5042:                // 0x53: translate bios parameter block to drive param bock
        !          5043:                case 0x54: msdos_int_21h_54h(); break;
        !          5044:                case 0x55: msdos_int_21h_55h(); break;
        !          5045:                case 0x56: msdos_int_21h_56h(0); break;
        !          5046:                case 0x57: msdos_int_21h_57h(); break;
        !          5047:                case 0x58: msdos_int_21h_58h(); break;
        !          5048:                case 0x59: msdos_int_21h_59h(); break;
        !          5049:                case 0x5a: msdos_int_21h_5ah(); break;
        !          5050:                case 0x5b: msdos_int_21h_5bh(); break;
        !          5051:                case 0x5c: msdos_int_21h_5ch(); break;
        !          5052:                // 0x5e: ms-network
        !          5053:                // 0x5f: ms-network
        !          5054:                case 0x60: msdos_int_21h_60h(0); break;
        !          5055:                case 0x61: msdos_int_21h_61h(); break;
        !          5056:                case 0x62: msdos_int_21h_62h(); break;
        !          5057:                case 0x63: msdos_int_21h_63h(); break;
        !          5058:                // 0x64: set device driver lockahead flag
        !          5059:                case 0x65: msdos_int_21h_65h(); break;
        !          5060:                case 0x66: msdos_int_21h_66h(); break;
        !          5061:                case 0x67: msdos_int_21h_67h(); break;
        !          5062:                case 0x68: msdos_int_21h_68h(); break;
        !          5063:                case 0x69: msdos_int_21h_69h(); break;
        !          5064:                case 0x6a: msdos_int_21h_6ah(); break;
        !          5065:                case 0x6b: msdos_int_21h_6bh(); break;
        !          5066:                case 0x6c: msdos_int_21h_6ch(0); break;
        !          5067:                // 0x6d: find first rom program
        !          5068:                // 0x6e: find next rom program
        !          5069:                // 0x6f: get/set rom scan start address
        !          5070:                // 0x70: windows95 get/set internationalization information
        !          5071:                case 0x71:
        !          5072:                        // windows95 long filename functions
        !          5073:                        switch(REG8(AL)) {
        !          5074:                        case 0x0d: msdos_int_21h_710dh(); break;
        !          5075:                        case 0x39: msdos_int_21h_39h(1); break;
        !          5076:                        case 0x3a: msdos_int_21h_3ah(1); break;
        !          5077:                        case 0x3b: msdos_int_21h_3bh(1); break;
        !          5078:                        case 0x41: msdos_int_21h_41h(1); break;
        !          5079:                        case 0x43: msdos_int_21h_43h(1); break;
        !          5080:                        case 0x47: msdos_int_21h_47h(1); break;
        !          5081:                        case 0x4e: msdos_int_21h_714eh(); break;
        !          5082:                        case 0x4f: msdos_int_21h_714fh(); break;
        !          5083:                        case 0x56: msdos_int_21h_56h(1); break;
        !          5084:                        case 0x60: msdos_int_21h_60h(1); break;
        !          5085:                        case 0x6c: msdos_int_21h_6ch(1); break;
        !          5086:                        case 0xa0: msdos_int_21h_71a0h(); break;
        !          5087:                        case 0xa1: msdos_int_21h_71a1h(); break;
        !          5088:                        case 0xa6: msdos_int_21h_71a6h(); break;
        !          5089:                        case 0xa7: msdos_int_21h_71a7h(); break;
        !          5090:                        case 0xa8: msdos_int_21h_71a8h(); break;
        !          5091:                        // 0xa9: server create/open file
        !          5092:                        // 0xaa: create/terminate SUBST
        !          5093:                        default:
        !          5094: //                             fatalerror("int %02xh (ax=%04xh bx=%04xh cx=%04xh dx=%04x)\n", num, REG16(AX), REG16(BX), REG16(CX), REG16(DX));
        !          5095:                                REG16(AX) = 0x7100;
        !          5096:                                cpustate->CF = 1;
        !          5097:                                break;
        !          5098:                        }
        !          5099:                        break;
        !          5100:                // 0x72: Windows95 beta - LFN FindClose
        !          5101:                case 0x73:
        !          5102:                        // windows95 fat32 functions
        !          5103:                        switch(REG8(AL)) {
        !          5104:                        // 0x00: drive locking ???
        !          5105:                        // 0x01: drive locking ???
        !          5106:                        // 0x02: get extended dpb
        !          5107:                        case 0x03: msdos_int_21h_7303h(); break;
        !          5108:                        // 0x04: set dpb to use for formatting
        !          5109:                        default:
        !          5110: //                             fatalerror("int %02xh (ax=%04xh bx=%04xh cx=%04xh dx=%04x)\n", num, REG16(AX), REG16(BX), REG16(CX), REG16(DX));
        !          5111:                                REG16(AX) = 0x7300;
        !          5112:                                cpustate->CF = 1;
        !          5113:                                break;
        !          5114:                        }
        !          5115:                        break;
        !          5116:                default:
        !          5117: //                     fatalerror("int %02xh (ax=%04xh bx=%04xh cx=%04xh dx=%04x)\n", num, REG16(AX), REG16(BX), REG16(CX), REG16(DX));
        !          5118:                        REG16(AX) = 0x01;
        !          5119:                        cpustate->CF = 1;
        !          5120:                        break;
        !          5121:                }
        !          5122:                if(cpustate->CF) {
        !          5123:                        error_code = REG16(AX);
        !          5124:                }
        !          5125:                break;
        !          5126:        case 0x22:
        !          5127:                fatalerror("int 22h (terminate address)\n");
        !          5128:        case 0x23:
        !          5129:                msdos_process_terminate(current_psp, (retval & 0xff) | 0x100, 1);
        !          5130:                break;
        !          5131:        case 0x24:
        !          5132:                msdos_process_terminate(current_psp, (retval & 0xff) | 0x200, 1);
        !          5133:                break;
        !          5134:        case 0x25:
        !          5135:                msdos_int_25h();
        !          5136:                break;
        !          5137:        case 0x26:
        !          5138:                msdos_int_26h();
        !          5139:                break;
        !          5140:        case 0x27:
        !          5141:                msdos_int_27h();
        !          5142:                break;
        !          5143:        case 0x28:
        !          5144:                Sleep(10);
        !          5145:                break;
        !          5146:        case 0x29:
        !          5147:                msdos_int_29h();
        !          5148:                break;
        !          5149:        case 0x2e:
        !          5150:                msdos_int_2eh();
        !          5151:                break;
        !          5152:        case 0x2f:
        !          5153:                // multiplex interrupt
        !          5154:                cpustate->CF = 0;
        !          5155:                switch(REG8(AH)) {
        !          5156:                case 0x16: msdos_int_2fh_16h(); break;
        !          5157:                case 0x1a: msdos_int_2fh_1ah(); break;
        !          5158:                case 0x43: msdos_int_2fh_43h(); break;
        !          5159:                case 0x4a: msdos_int_2fh_4ah(); break;
        !          5160:                case 0x4f: msdos_int_2fh_4fh(); break;
        !          5161:                case 0xae: msdos_int_2fh_aeh(); break;
        !          5162:                case 0xb7: msdos_int_2fh_b7h(); break;
        !          5163:                default:
        !          5164: //                     fatalerror("int %02xh (ax=%04xh bx=%04xh cx=%04xh dx=%04x)\n", num, REG16(AX), REG16(BX), REG16(CX), REG16(DX));
        !          5165:                        REG16(AX) = 0x01; // ???
        !          5166:                        cpustate->CF = 1;
        !          5167:                        break;
        !          5168:                }
        !          5169:                if(cpustate->CF) {
        !          5170:                        error_code = REG16(AX);
        !          5171:                }
        !          5172:                break;
        !          5173:        default:
        !          5174: //             fatalerror("int %02xh (ax=%04xh bx=%04xh cx=%04xh dx=%04x)\n", num, REG16(AX), REG16(BX), REG16(CX), REG16(DX));
        !          5175:                break;
        !          5176:        }
        !          5177:        
        !          5178:        // update cursor position
        !          5179:        if(cursor_moved) {
        !          5180:                CONSOLE_SCREEN_BUFFER_INFO csbi;
        !          5181:                GetConsoleScreenBufferInfo(hStdout, &csbi);
        !          5182:                mem[0x450 + mem[0x462] * 2] = csbi.dwCursorPosition.X;
        !          5183:                mem[0x451 + mem[0x462] * 2] = csbi.dwCursorPosition.Y;
        !          5184:                cursor_moved = false;
        !          5185:        }
        !          5186: }
        !          5187: 
        !          5188: // init
        !          5189: 
        !          5190: int msdos_init(int argc, char *argv[], char *envp[], int standard_env)
        !          5191: {
        !          5192:        // init file handler
        !          5193:        memset(file_handler, 0, sizeof(file_handler));
        !          5194:        msdos_file_handler_open(0, "STDIN", _isatty(0), 0, 0x80d3, 0);
        !          5195:        msdos_file_handler_open(1, "STDOUT", _isatty(1), 1, 0x80d3, 0);
        !          5196:        msdos_file_handler_open(2, "STDERR", _isatty(2), 1, 0x80d3, 0);
        !          5197: #ifdef SUPPORT_AUX_PRN
        !          5198:        if(_open("stdaux.txt", _O_RDWR | _O_BINARY | _O_CREAT | _O_TRUNC, _S_IREAD | _S_IWRITE) == 3) {
        !          5199:                msdos_file_handler_open(3, 0, 2, 0x80c0, 0);
        !          5200:        }
        !          5201:        if(_open("stdprn.txt", _O_WRONLY | _O_BINARY | _O_CREAT | _O_TRUNC, _S_IREAD | _S_IWRITE) == 4) {
        !          5202:                msdos_file_handler_open(4, 0, 1, 0xa8c0, 0);
        !          5203:        }
        !          5204: #endif
        !          5205:        _dup2(0, DUP_STDIN);
        !          5206:        _dup2(1, DUP_STDOUT);
        !          5207:        _dup2(2, DUP_STDERR);
        !          5208:        
        !          5209:        // init process
        !          5210:        memset(process, 0, sizeof(process));
        !          5211:        
        !          5212:        // init memory
        !          5213:        memset(mem, 0, sizeof(mem));
        !          5214:        for(int i = 0; i < 0x100; i++) {
        !          5215:                *(UINT16 *)(mem + 4 * i + 0) = i;
        !          5216:                *(UINT16 *)(mem + 4 * i + 2) = (IRET_TOP >> 4);
        !          5217:        }
        !          5218:        *(UINT16 *)(mem + 4 * 0x22 + 0) = 0xfff0;
        !          5219:        *(UINT16 *)(mem + 4 * 0x22 + 2) = 0xf000;
        !          5220:        memset(mem + IRET_TOP, 0xcf, IRET_SIZE);
        !          5221:        
        !          5222:        // bios data area
        !          5223:        CONSOLE_SCREEN_BUFFER_INFO csbi;
        !          5224:        GetConsoleScreenBufferInfo(hStdout, &csbi);
        !          5225:        
        !          5226:        *(UINT8  *)(mem + 0x411) = 0x20;
        !          5227:        *(UINT16 *)(mem + 0x410) = 0x20;
        !          5228:        *(UINT16 *)(mem + 0x413) = MEMORY_END / 1024;
        !          5229:        *(UINT8  *)(mem + 0x449) = 0x03;//0x73;
        !          5230:        *(UINT16 *)(mem + 0x44a) = 80;
        !          5231:        *(UINT16 *)(mem + 0x44c) = 4096;
        !          5232:        *(UINT16 *)(mem + 0x44e) = 0;
        !          5233:        *(UINT8  *)(mem + 0x450) = csbi.dwCursorPosition.X;
        !          5234:        *(UINT8  *)(mem + 0x451) = csbi.dwCursorPosition.Y;
        !          5235:        *(UINT8  *)(mem + 0x460) = 7;
        !          5236:        *(UINT8  *)(mem + 0x461) = 7;
        !          5237:        *(UINT8  *)(mem + 0x462) = 0;
        !          5238:        *(UINT16 *)(mem + 0x463) = 0x3d4;
        !          5239:        *(UINT8  *)(mem + 0x484) = 24;
        !          5240:        *(UINT8  *)(mem + 0x485) = 19;
        !          5241:        *(UINT8  *)(mem + 0x487) = 0;   // is this okay?
        !          5242:        
        !          5243:        // dos info
        !          5244:        dos_info_t *dos_info = (dos_info_t *)(mem + DOS_INFO_TOP);
        !          5245:        dos_info->first_mcb = MEMORY_TOP >> 4;
        !          5246:        dos_info->first_dpb.w.l = 0;
        !          5247:        dos_info->first_dpb.w.h = DPB_TOP >> 4;
        !          5248:        dos_info->first_sft.w.l = 0;
        !          5249:        dos_info->first_sft.w.h = FILE_TABLE_TOP >> 4;
        !          5250:        dos_info->cds.w.l = 0;
        !          5251:        dos_info->cds.w.h = CDS_TOP >> 4;
        !          5252:        dos_info->fcb_table.w.l = 0;
        !          5253:        dos_info->fcb_table.w.h = FCB_TABLE_TOP >> 4;
        !          5254:        dos_info->last_drive = 'Z' - 'A' + 1;
        !          5255:        dos_info->buffers_x = 20;
        !          5256:        dos_info->buffers_y = 0;
        !          5257:        char *env;
        !          5258:        if((env = getenv("LASTDRIVE")) != NULL) {
        !          5259:                if(env[0] >= 'A' && env[0] <= 'Z') {
        !          5260:                        dos_info->last_drive = env[0] - 'A' + 1;
        !          5261:                } else if(env[0] >= 'a' && env[0] <= 'z') {
        !          5262:                        dos_info->last_drive = env[0] - 'a' + 1;
        !          5263:                }
        !          5264:        }
        !          5265:        if((env = getenv("windir")) != NULL) {
        !          5266:                if(env[0] >= 'A' && env[0] <= 'Z') {
        !          5267:                        dos_info->boot_drive = env[0] - 'A' + 1;
        !          5268:                } else if(env[0] >= 'a' && env[0] <= 'z') {
        !          5269:                        dos_info->boot_drive = env[0] - 'a' + 1;
        !          5270:                }
        !          5271:        }
        !          5272:        dos_info->i386_or_later = 1;
        !          5273:        dos_info->ext_mem_size = (MAX_MEM - 0x100000) >> 10;
        !          5274:        
        !          5275:        // environment
        !          5276:        int seg = MEMORY_TOP >> 4;
        !          5277:        msdos_mcb_create(seg++, 'M', -1, ENV_SIZE >> 4);
        !          5278:        int env_seg = seg;
        !          5279:        int ofs = 0;
        !          5280:        
        !          5281:        for(char **p = envp; p != NULL && *p != NULL; p++) {
        !          5282:                // lower to upper
        !          5283:                char tmp[ENV_SIZE], name[ENV_SIZE], value[ENV_SIZE];
        !          5284:                int value_pos = 0;
        !          5285:                strcpy(tmp, *p);
        !          5286:                for(int i = 0;; i++) {
        !          5287:                        if(tmp[i] == '=') {
        !          5288:                                tmp[i] = '\0';
        !          5289:                                sprintf(name, ";%s;", tmp);
        !          5290:                                strcpy(value, tmp + (value_pos = i + 1));
        !          5291:                                tmp[i] = '=';
        !          5292:                                break;
        !          5293:                        } else if(tmp[i] >= 'a' && tmp[i] <= 'z') {
        !          5294:                                tmp[i] = tmp[i] - 'a' + 'A';
        !          5295:                        }
        !          5296:                }
        !          5297:                if(!(standard_env && strstr(";COMSPEC;INCLUDE;LIB;PATH;PROMPT;TEMP;TMP;TZ;", name) == NULL)) {
        !          5298:                        if(strncmp(tmp, "COMSPEC=", 8) == 0) {
        !          5299:                                char full_path[MAX_PATH], short_path[MAX_PATH], *file_name;
        !          5300:                                GetFullPathName(argv[0], MAX_PATH, full_path, &file_name);
        !          5301:                                if(_stricmp(file_name, "COMMAND.COM") == 0 || _stricmp(file_name, "COMMAND") == 0) {
        !          5302:                                        sprintf(file_name, "COMMAND.COM");
        !          5303:                                        if(_access(full_path, 0) == 0) {
        !          5304:                                                GetShortPathName(full_path, short_path, MAX_PATH);
        !          5305:                                                my_strupr(short_path);
        !          5306:                                                sprintf(tmp, "COMSPEC=%s", short_path);
        !          5307:                                        }
        !          5308:                                }
        !          5309:                        } else if(strncmp(tmp, "PATH=", 5) == 0 || strncmp(tmp, "TEMP=", 5) == 0 || strncmp(tmp, "TMP=", 4) == 0) {
        !          5310:                                tmp[value_pos] = '\0';
        !          5311:                                char *token = my_strtok(value, ";");
        !          5312:                                while(token != NULL) {
        !          5313:                                        if(strlen(token) != 0) {
        !          5314:                                                char path[MAX_PATH], short_path[MAX_PATH];
        !          5315:                                                if(token[0] == '"' && token[strlen(token) - 1] == '"') {
        !          5316:                                                        memset(path, 0, sizeof(path));
        !          5317:                                                        memcpy(path, token + 1, strlen(token) - 2);
        !          5318:                                                } else {
        !          5319:                                                        sprintf(path, token);
        !          5320:                                                }
        !          5321:                                                GetShortPathName(path, short_path, MAX_PATH);
        !          5322:                                                strcat(tmp, short_path);
        !          5323:                                                strcat(tmp, ";");
        !          5324:                                        }
        !          5325:                                        token = my_strtok(NULL, ";");
        !          5326:                                }
        !          5327:                                tmp[strlen(tmp) - 1] = '\0';
        !          5328:                                my_strupr(tmp);
        !          5329:                        }
        !          5330:                        int len = strlen(tmp);
        !          5331:                        if (ofs + len + 1 + (2 + (8 + 1 + 3)) + 2 > ENV_SIZE) {
        !          5332:                                fatalerror("too many environments\n");
        !          5333:                        }
        !          5334:                        memcpy(mem + (seg << 4) + ofs, tmp, len);
        !          5335:                        ofs += len + 1;
        !          5336:                }
        !          5337:        }
        !          5338:        seg += (ENV_SIZE >> 4);
        !          5339:        
        !          5340:        // psp
        !          5341:        msdos_mcb_create(seg++, 'M', -1, PSP_SIZE >> 4);
        !          5342:        current_psp = seg;
        !          5343:        psp_t *psp = msdos_psp_create(seg, seg + (PSP_SIZE >> 4), -1, env_seg);
        !          5344:        seg += (PSP_SIZE >> 4);
        !          5345:        
        !          5346:        // first mcb
        !          5347:        msdos_mcb_create(seg, 'Z', 0, (MEMORY_END >> 4) - seg - 1);
        !          5348:        
        !          5349:        // boot
        !          5350:        mem[0xffff0] = 0xf4;    // halt
        !          5351:        mem[0xffff1] = 0xcd;    // int 21h
        !          5352:        mem[0xffff2] = 0x21;
        !          5353:        mem[0xffff3] = 0xcb;    // retf
        !          5354:        
        !          5355:        // param block
        !          5356:        // + 0: param block (22bytes)
        !          5357:        // +24: fcb1/2 (20bytes)
        !          5358:        // +44: command tail (128bytes)
        !          5359:        param_block_t *param = (param_block_t *)(mem + WORK_TOP);
        !          5360:        param->env_seg = 0;
        !          5361:        param->cmd_line.w.l = 44;
        !          5362:        param->cmd_line.w.h = (WORK_TOP >> 4);
        !          5363:        param->fcb1.w.l = 24;
        !          5364:        param->fcb1.w.h = (WORK_TOP >> 4);
        !          5365:        param->fcb2.w.l = 24;
        !          5366:        param->fcb2.w.h = (WORK_TOP >> 4);
        !          5367:        
        !          5368:        memset(mem + WORK_TOP + 24, 0x20, 20);
        !          5369:        
        !          5370:        cmd_line_t *cmd_line = (cmd_line_t *)(mem + WORK_TOP + 44);
        !          5371:        if(argc > 1) {
        !          5372:                sprintf(cmd_line->cmd, " %s", argv[1]);
        !          5373:                for(int i = 2; i < argc; i++) {
        !          5374:                        char tmp[128];
        !          5375:                        sprintf(tmp, "%s %s", cmd_line->cmd, argv[i]);
        !          5376:                        strcpy(cmd_line->cmd, tmp);
        !          5377:                }
        !          5378:                cmd_line->len = (UINT8)strlen(cmd_line->cmd);
        !          5379:        } else {
        !          5380:                cmd_line->len = 0;
        !          5381:        }
        !          5382:        cmd_line->cmd[cmd_line->len] = 0x0d;
        !          5383:        
        !          5384:        // system file table
        !          5385:        *(UINT16 *)(mem + FILE_TABLE_TOP +  0) = 6;
        !          5386:        *(UINT16 *)(mem + FILE_TABLE_TOP +  2) = FILE_TABLE_TOP >> 4;
        !          5387:        *(UINT16 *)(mem + FILE_TABLE_TOP +  4) = 100;
        !          5388:        *(UINT32 *)(mem + FILE_TABLE_TOP +  6) = 0xffffffff;
        !          5389:        *(UINT16 *)(mem + FILE_TABLE_TOP + 10) = 100;
        !          5390:        
        !          5391:        // current directory structure
        !          5392:        msdos_cds_update(_getdrive() - 1);
        !          5393:        
        !          5394:        // fcb table
        !          5395:        *(UINT32 *)(mem + FCB_TABLE_TOP + 0) = 0xffffffff;
        !          5396:        *(UINT16 *)(mem + FCB_TABLE_TOP + 4) = 100;
        !          5397:        
        !          5398:        // dbcs table
        !          5399:        msdos_dbcs_table_init();
        !          5400:        
        !          5401:        // execute command
        !          5402:        if(msdos_process_exec(argv[0], param, 0)) {
        !          5403:                fatalerror("'%s' not found\n", argv[0]);
        !          5404:        }
        !          5405:        retval = 0;
        !          5406:        return(0);
        !          5407: }
        !          5408: 
        !          5409: #define remove_std_file(path) { \
        !          5410:        int fd = _open(path, _O_RDONLY | _O_BINARY); \
        !          5411:        if(fd != -1) { \
        !          5412:                _lseek(fd, 0, SEEK_END); \
        !          5413:                int size = _tell(fd); \
        !          5414:                _close(fd); \
        !          5415:                if(size == 0) { \
        !          5416:                        remove(path); \
        !          5417:                } \
        !          5418:        } \
        !          5419: }
        !          5420: 
        !          5421: void msdos_finish()
        !          5422: {
        !          5423:        for(int i = 0; i < MAX_FILES; i++) {
        !          5424:                if(file_handler[i].valid) {
        !          5425:                        _close(i);
        !          5426:                }
        !          5427:        }
        !          5428: #ifdef SUPPORT_AUX_PRN
        !          5429:        remove_std_file("stdaux.txt");
        !          5430:        remove_std_file("stdprn.txt");
        !          5431: #endif
        !          5432:        msdos_dbcs_table_finish();
        !          5433: }
        !          5434: 
        !          5435: /* ----------------------------------------------------------------------------
        !          5436:        PC/AT hardware emulation
        !          5437: ---------------------------------------------------------------------------- */
        !          5438: 
        !          5439: void hardware_init()
        !          5440: {
        !          5441:        cpustate = (i386_state *)CPU_INIT_CALL(CPU_MODEL);
        !          5442:        CPU_RESET_CALL(CPU_MODEL);
        !          5443:        cpu_type = (REG32(EDX) >> 8) & 0x0f;
        !          5444:        cpu_step = (REG32(EDX) >> 0) & 0x0f;
        !          5445:        i386_set_a20_line(cpustate, 0);
        !          5446: #ifdef SUPPORT_HARDWARE
        !          5447:        pic_init();
        !          5448:        //pit_init();
        !          5449:        pit_active = 0;
        !          5450: #endif
        !          5451: }
        !          5452: 
        !          5453: void hardware_finish()
        !          5454: {
        !          5455:        free(cpustate);
        !          5456: }
        !          5457: 
        !          5458: void hardware_run()
        !          5459: {
        !          5460:        int ops = 0;
        !          5461:        
        !          5462:        while(!cpustate->halted) {
        !          5463: #ifdef SUPPORT_DISASSEMBLER
        !          5464:                if(dasm) {
        !          5465:                        char buffer[256];
        !          5466:                        UINT64 eip = cpustate->eip;
        !          5467:                        UINT8 *oprom = mem + cpustate->sreg[CS].base + cpustate->eip;
        !          5468:                        
        !          5469:                        if(cpustate->operand_size) {
        !          5470:                                CPU_DISASSEMBLE_CALL(x86_32);
        !          5471:                        } else {
        !          5472:                                CPU_DISASSEMBLE_CALL(x86_16);
        !          5473:                        }
        !          5474:                        fprintf(stderr, "%04x:%04x\t%s\n", cpustate->sreg[CS].selector, cpustate->eip, buffer);
        !          5475:                }
        !          5476: #endif
        !          5477:                cpustate->cycles = 1;
        !          5478:                CPU_EXECUTE_CALL(i386);
        !          5479: #ifdef SUPPORT_HARDWARE
        !          5480:                if(++ops == 1024) {
        !          5481:                        hardware_update();
        !          5482:                        ops = 0;
        !          5483:                }
        !          5484: #endif
        !          5485:        }
        !          5486: }
        !          5487: 
        !          5488: #ifdef SUPPORT_HARDWARE
        !          5489: void hardware_update()
        !          5490: {
        !          5491:        if(pit_active) {
        !          5492:                pit_run();
        !          5493:        }
        !          5494: }
        !          5495: #endif
        !          5496: 
        !          5497: // pic
        !          5498: 
        !          5499: void pic_init()
        !          5500: {
        !          5501:        for(int c = 0; c < 2; c++) {
        !          5502:                pic[c].imr = 0xff;
        !          5503:                pic[c].irr = pic[c].isr = pic[c].prio = 0;
        !          5504:                pic[c].icw1 = pic[c].icw2 = pic[c].icw3 = pic[c].icw4 = 0;
        !          5505:                pic[c].ocw3 = 0;
        !          5506:                pic[c].icw2_r = pic[c].icw3_r = pic[c].icw4_r = 0;
        !          5507:        }
        !          5508:        
        !          5509:        // from bochs bios
        !          5510:        pic_write(0, 0, 0x11);  // icw1 = 11h
        !          5511:        pic_write(0, 1, 0x08);  // icw2 = 08h
        !          5512:        pic_write(0, 1, 0x04);  // icw3 = 04h
        !          5513:        pic_write(0, 1, 0x01);  // icw4 = 01h
        !          5514:        pic_write(0, 1, 0xb8);  // ocw1 = b8h
        !          5515:        pic_write(1, 0, 0x11);  // icw1 = 11h
        !          5516:        pic_write(1, 1, 0x70);  // icw2 = 70h
        !          5517:        pic_write(1, 1, 0x02);  // icw3 = 02h
        !          5518:        pic_write(1, 1, 0x01);  // icw4 = 01h
        !          5519: }
        !          5520: 
        !          5521: void pic_write(int c, UINT32 addr, UINT8 data)
        !          5522: {
        !          5523:        if(addr & 1) {
        !          5524:                if(pic[c].icw2_r) {
        !          5525:                        // icw2
        !          5526:                        pic[c].icw2 = data;
        !          5527:                        pic[c].icw2_r = 0;
        !          5528:                } else if(pic[c].icw3_r) {
        !          5529:                        // icw3
        !          5530:                        pic[c].icw3 = data;
        !          5531:                        pic[c].icw3_r = 0;
        !          5532:                } else if(pic[c].icw4_r) {
        !          5533:                        // icw4
        !          5534:                        pic[c].icw4 = data;
        !          5535:                        pic[c].icw4_r = 0;
        !          5536:                } else {
        !          5537:                        // ocw1
        !          5538:                        pic[c].imr = data;
        !          5539:                }
        !          5540:        } else {
        !          5541:                if(data & 0x10) {
        !          5542:                        // icw1
        !          5543:                        pic[c].icw1 = data;
        !          5544:                        pic[c].icw2_r = 1;
        !          5545:                        pic[c].icw3_r = (data & 2) ? 0 : 1;
        !          5546:                        pic[c].icw4_r = data & 1;
        !          5547:                        
        !          5548:                        pic[c].irr = 0;
        !          5549:                        pic[c].isr = 0;
        !          5550:                        pic[c].imr = 0;
        !          5551:                        pic[c].prio = 0;
        !          5552:                        if(!(pic[c].icw1 & 1)) {
        !          5553:                                pic[c].icw4 = 0;
        !          5554:                        }
        !          5555:                        pic[c].ocw3 = 0;
        !          5556:                } else if(data & 8) {
        !          5557:                        // ocw3
        !          5558:                        if(!(data & 2)) {
        !          5559:                                data = (data & ~1) | (pic[c].ocw3 & 1);
        !          5560:                        }
        !          5561:                        if(!(data & 0x40)) {
        !          5562:                                data = (data & ~0x20) | (pic[c].ocw3 & 0x20);
        !          5563:                        }
        !          5564:                        pic[c].ocw3 = data;
        !          5565:                } else {
        !          5566:                        // ocw2
        !          5567:                        int level = 0;
        !          5568:                        if(data & 0x40) {
        !          5569:                                level = data & 7;
        !          5570:                        } else {
        !          5571:                                if(!pic[c].isr) {
        !          5572:                                        return;
        !          5573:                                }
        !          5574:                                level = pic[c].prio;
        !          5575:                                while(!(pic[c].isr & (1 << level))) {
        !          5576:                                        level = (level + 1) & 7;
        !          5577:                                }
        !          5578:                        }
        !          5579:                        if(data & 0x80) {
        !          5580:                                pic[c].prio = (level + 1) & 7;
        !          5581:                        }
        !          5582:                        if(data & 0x20) {
        !          5583:                                pic[c].isr &= ~(1 << level);
        !          5584:                        }
        !          5585:                }
        !          5586:        }
        !          5587:        pic_update();
        !          5588: }
        !          5589: 
        !          5590: UINT8 pic_read(int c, UINT32 addr)
        !          5591: {
        !          5592:        if(addr & 1) {
        !          5593:                return(pic[c].imr);
        !          5594:        } else {
        !          5595:                // polling mode is not supported...
        !          5596:                //if(pic[c].ocw3 & 4) {
        !          5597:                //      return ???;
        !          5598:                //}
        !          5599:                if(pic[c].ocw3 & 1) {
        !          5600:                        return(pic[c].isr);
        !          5601:                } else {
        !          5602:                        return(pic[c].irr);
        !          5603:                }
        !          5604:        }
        !          5605: }
        !          5606: 
        !          5607: void pic_req(int c, int level, int signal)
        !          5608: {
        !          5609:        if(signal) {
        !          5610:                pic[c].irr |= (1 << level);
        !          5611:        } else {
        !          5612:                pic[c].irr &= ~(1 << level);
        !          5613:        }
        !          5614:        pic_update();
        !          5615: }
        !          5616: 
        !          5617: int pic_ack()
        !          5618: {
        !          5619:        // ack (INTA=L)
        !          5620:        pic[pic_req_chip].isr |= pic_req_bit;
        !          5621:        pic[pic_req_chip].irr &= ~pic_req_bit;
        !          5622:        if(pic_req_chip > 0) {
        !          5623:                // update isr and irr of master
        !          5624:                UINT8 slave = 1 << (pic[pic_req_chip].icw3 & 7);
        !          5625:                pic[pic_req_chip - 1].isr |= slave;
        !          5626:                pic[pic_req_chip - 1].irr &= ~slave;
        !          5627:        }
        !          5628:        //if(pic[pic_req_chip].icw4 & 1) {
        !          5629:                // 8086 mode
        !          5630:                int vector = (pic[pic_req_chip].icw2 & 0xf8) | pic_req_level;
        !          5631:        //} else {
        !          5632:        //      // 8080 mode
        !          5633:        //      UINT16 addr = (UINT16)pic[pic_req_chip].icw2 << 8;
        !          5634:        //      if(pic[pic_req_chip].icw1 & 4) {
        !          5635:        //              addr |= (pic[pic_req_chip].icw1 & 0xe0) | (pic_req_level << 2);
        !          5636:        //      } else {
        !          5637:        //              addr |= (pic[pic_req_chip].icw1 & 0xc0) | (pic_req_level << 3);
        !          5638:        //      }
        !          5639:        //      vector = 0xcd | (addr << 8);
        !          5640:        //}
        !          5641:        if(pic[pic_req_chip].icw4 & 2) {
        !          5642:                // auto eoi
        !          5643:                pic[pic_req_chip].isr &= ~pic_req_bit;
        !          5644:        }
        !          5645:        return(vector);
        !          5646: }
        !          5647: 
        !          5648: void pic_update()
        !          5649: {
        !          5650:        for(int c = 0; c < 2; c++) {
        !          5651:                UINT8 irr = pic[c].irr;
        !          5652:                if(c + 1 < 2) {
        !          5653:                        // this is master
        !          5654:                        if(pic[c + 1].irr & (~pic[c + 1].imr)) {
        !          5655:                                // request from slave
        !          5656:                                irr |= 1 << (pic[c + 1].icw3 & 7);
        !          5657:                        }
        !          5658:                }
        !          5659:                irr &= (~pic[c].imr);
        !          5660:                if(!irr) {
        !          5661:                        break;
        !          5662:                }
        !          5663:                if(!(pic[c].ocw3 & 0x20)) {
        !          5664:                        irr |= pic[c].isr;
        !          5665:                }
        !          5666:                int level = pic[c].prio;
        !          5667:                UINT8 bit = 1 << level;
        !          5668:                while(!(irr & bit)) {
        !          5669:                        level = (level + 1) & 7;
        !          5670:                        bit = 1 << level;
        !          5671:                }
        !          5672:                if((c + 1 < 2) && (pic[c].icw3 & bit)) {
        !          5673:                        // check slave
        !          5674:                        continue;
        !          5675:                }
        !          5676:                if(pic[c].isr & bit) {
        !          5677:                        break;
        !          5678:                }
        !          5679:                // interrupt request
        !          5680:                pic_req_chip = c;
        !          5681:                pic_req_level = level;
        !          5682:                pic_req_bit = bit;
        !          5683:                i386_set_irq_line(cpustate, INPUT_LINE_IRQ, HOLD_LINE);
        !          5684:                return;
        !          5685:        }
        !          5686:        i386_set_irq_line(cpustate, INPUT_LINE_IRQ, CLEAR_LINE);
        !          5687: };
        !          5688: 
        !          5689: // pit
        !          5690: 
        !          5691: #define PIT_FREQ 1193182
        !          5692: #define PIT_COUNT_VALUE(n) ((pit[n].count_reg == 0) ? 0x10000 : (pit[n].mode == 3 && pit[n].count_reg == 1) ? 0x10001 : pit[n].count_reg)
        !          5693: 
        !          5694: void pit_init()
        !          5695: {
        !          5696:        for(int ch = 0; ch < 3; ch++) {
        !          5697:                pit[ch].prev_out = 1;
        !          5698:                //pit[ch].gate = 1;
        !          5699:                pit[ch].count = 0x10000;
        !          5700:                pit[ch].count_reg = 0;
        !          5701:                pit[ch].ctrl_reg = 0x34;
        !          5702:                pit[ch].mode = 3;
        !          5703:                pit[ch].count_latched = 0;
        !          5704:                pit[ch].low_read = pit[ch].high_read = 0;
        !          5705:                pit[ch].low_write = pit[ch].high_write = 0;
        !          5706:                pit[ch].delay = 0;
        !          5707:                pit[ch].start = 0;
        !          5708:                pit[ch].null_count = 1;
        !          5709:                pit[ch].status_latched = 0;
        !          5710:        }
        !          5711:        
        !          5712:        // from bochs bios
        !          5713:        pit_write(3, 0x34);
        !          5714:        pit_write(0, 0x00);
        !          5715:        pit_write(0, 0x00);
        !          5716: }
        !          5717: 
        !          5718: void pit_write(int ch, UINT8 val)
        !          5719: {
        !          5720:        if(!pit_active) {
        !          5721:                pit_active = 1;
        !          5722:                pit_init();
        !          5723:        }
        !          5724:        
        !          5725:        switch(ch) {
        !          5726:        case 0:
        !          5727:        case 1:
        !          5728:        case 2:
        !          5729:                // write count register
        !          5730:                if(!pit[ch].low_write && !pit[ch].high_write) {
        !          5731:                        if(pit[ch].ctrl_reg & 0x10) {
        !          5732:                                pit[ch].low_write = 1;
        !          5733:                        }
        !          5734:                        if(pit[ch].ctrl_reg & 0x20) {
        !          5735:                                pit[ch].high_write = 1;
        !          5736:                        }
        !          5737:                }
        !          5738:                if(pit[ch].low_write) {
        !          5739:                        pit[ch].count_reg = val;
        !          5740:                        pit[ch].low_write = 0;
        !          5741:                } else if(pit[ch].high_write) {
        !          5742:                        if((pit[ch].ctrl_reg & 0x30) == 0x20) {
        !          5743:                                pit[ch].count_reg = val << 8;
        !          5744:                        } else {
        !          5745:                                pit[ch].count_reg |= val << 8;
        !          5746:                        }
        !          5747:                        pit[ch].high_write = 0;
        !          5748:                }
        !          5749:                pit[ch].null_count = 1;
        !          5750:                // set signal
        !          5751:                if(pit[ch].mode == 0) {
        !          5752:                        pit_set_signal(ch, 0);
        !          5753:                } else {
        !          5754:                        pit_set_signal(ch, 1);
        !          5755:                }
        !          5756:                // start count
        !          5757:                if(pit[ch].mode == 0 || pit[ch].mode == 4) {
        !          5758:                        // restart with new count
        !          5759:                        pit_stop_count(ch);
        !          5760:                        pit[ch].delay = 1;
        !          5761:                        pit_start_count(ch);
        !          5762:                } else if(pit[ch].mode == 2 || pit[ch].mode == 3) {
        !          5763:                        // start with new pit after the current count is finished
        !          5764:                        if(!pit[ch].start) {
        !          5765:                                pit[ch].delay = 1;
        !          5766:                                pit_start_count(ch);
        !          5767:                        }
        !          5768:                }
        !          5769:                break;
        !          5770:        case 3: // ctrl reg
        !          5771:                if((val & 0xc0) == 0xc0) {
        !          5772:                        // i8254 read-back command
        !          5773:                        for(ch = 0; ch < 3; ch++) {
        !          5774:                                UINT8 bit = 2 << ch;
        !          5775:                                if(!(val & 0x10) && !pit[ch].status_latched) {
        !          5776:                                        pit[ch].status = pit[ch].ctrl_reg & 0x3f;
        !          5777:                                        if(pit[ch].prev_out) {
        !          5778:                                                pit[ch].status |= 0x80;
        !          5779:                                        }
        !          5780:                                        if(pit[ch].null_count) {
        !          5781:                                                pit[ch].status |= 0x40;
        !          5782:                                        }
        !          5783:                                        pit[ch].status_latched = 1;
        !          5784:                                }
        !          5785:                                if(!(val & 0x20) && !pit[ch].count_latched) {
        !          5786:                                        pit_latch_count(ch);
        !          5787:                                }
        !          5788:                        }
        !          5789:                        break;
        !          5790:                }
        !          5791:                ch = (val >> 6) & 3;
        !          5792:                if(val & 0x30) {
        !          5793:                        static int modes[8] = {0, 1, 2, 3, 4, 5, 2, 3};
        !          5794:                        pit[ch].mode = modes[(val >> 1) & 7];
        !          5795:                        pit[ch].count_latched = 0;
        !          5796:                        pit[ch].low_read = pit[ch].high_read = 0;
        !          5797:                        pit[ch].low_write = pit[ch].high_write = 0;
        !          5798:                        pit[ch].ctrl_reg = val;
        !          5799:                        // set signal
        !          5800:                        if(pit[ch].mode == 0) {
        !          5801:                                pit_set_signal(ch, 0);
        !          5802:                        } else {
        !          5803:                                pit_set_signal(ch, 1);
        !          5804:                        }
        !          5805:                        // stop count
        !          5806:                        pit_stop_count(ch);
        !          5807:                        pit[ch].count_reg = 0;
        !          5808:                        pit[ch].null_count = 1;
        !          5809:                } else if(!pit[ch].count_latched) {
        !          5810:                        pit_latch_count(ch);
        !          5811:                }
        !          5812:                break;
        !          5813:        }
        !          5814: }
        !          5815: 
        !          5816: UINT8 pit_read(int ch)
        !          5817: {
        !          5818:        if(!pit_active) {
        !          5819:                pit_active = 1;
        !          5820:                pit_init();
        !          5821:        }
        !          5822:        
        !          5823:        switch(ch) {
        !          5824:        case 0:
        !          5825:        case 1:
        !          5826:        case 2:
        !          5827:                if(pit[ch].status_latched) {
        !          5828:                        pit[ch].status_latched = 0;
        !          5829:                        return(pit[ch].status);
        !          5830:                }
        !          5831:                // if not latched, through current count
        !          5832:                if(!pit[ch].count_latched) {
        !          5833:                        if(!pit[ch].low_read && !pit[ch].high_read) {
        !          5834:                                pit_latch_count(ch);
        !          5835:                        }
        !          5836:                }
        !          5837:                // return latched count
        !          5838:                if(pit[ch].low_read) {
        !          5839:                        pit[ch].low_read = 0;
        !          5840:                        if(!pit[ch].high_read) {
        !          5841:                                pit[ch].count_latched = 0;
        !          5842:                        }
        !          5843:                        return(pit[ch].latch & 0xff);
        !          5844:                } else if(pit[ch].high_read) {
        !          5845:                        pit[ch].high_read = 0;
        !          5846:                        pit[ch].count_latched = 0;
        !          5847:                        return((pit[ch].latch >> 8) & 0xff);
        !          5848:                }
        !          5849:        }
        !          5850:        return(0xff);
        !          5851: }
        !          5852: 
        !          5853: void pit_run()
        !          5854: {
        !          5855:        static UINT32 prev_time = 0;
        !          5856:        UINT32 cur_time = timeGetTime();
        !          5857:        
        !          5858:        if(prev_time == cur_time) {
        !          5859:                return;
        !          5860:        }
        !          5861:        prev_time = cur_time;
        !          5862:        
        !          5863:        for(int ch = 0; ch < 3; ch++) {
        !          5864:                if(pit[ch].start && cur_time >= pit[ch].expired_time) {
        !          5865:                        pit_input_clock(ch, pit[ch].input_clk);
        !          5866:                        
        !          5867:                        // next expired time
        !          5868:                        if(pit[ch].start) {
        !          5869:                                pit[ch].input_clk = pit[ch].delay ? 1 : pit_get_next_count(ch);
        !          5870:                                pit[ch].prev_time = pit[ch].expired_time;
        !          5871:                                pit[ch].expired_time += pit_get_expired_time(pit[ch].input_clk);
        !          5872:                                if(pit[ch].expired_time <= cur_time) {
        !          5873:                                        pit[ch].prev_time = cur_time;
        !          5874:                                        pit[ch].expired_time = cur_time + pit_get_expired_time(pit[ch].input_clk);
        !          5875:                                }
        !          5876:                        }
        !          5877:                }
        !          5878:        }
        !          5879: }
        !          5880: 
        !          5881: void pit_input_clock(int ch, int clock)
        !          5882: {
        !          5883:        if(!(pit[ch].start && clock)) {
        !          5884:                return;
        !          5885:        }
        !          5886:        if(pit[ch].delay) {
        !          5887:                clock -= 1;
        !          5888:                pit[ch].delay = 0;
        !          5889:                pit[ch].count = PIT_COUNT_VALUE(ch);
        !          5890:                pit[ch].null_count = 0;
        !          5891:        }
        !          5892:        
        !          5893:        // update pit
        !          5894:        pit[ch].count -= clock;
        !          5895:        INT32 tmp = PIT_COUNT_VALUE(ch);
        !          5896: loop:
        !          5897:        if(pit[ch].mode == 3) {
        !          5898:                INT32 half = tmp >> 1;
        !          5899:                if(pit[ch].count > half) {
        !          5900:                        pit_set_signal(ch, 1);
        !          5901:                } else {
        !          5902:                        pit_set_signal(ch, 0);
        !          5903:                }
        !          5904:        } else {
        !          5905:                if(pit[ch].count <= 1) {
        !          5906:                        if(pit[ch].mode == 2 || pit[ch].mode == 4 || pit[ch].mode == 5) {
        !          5907:                                pit_set_signal(ch, 0);
        !          5908:                        }
        !          5909:                }
        !          5910:                if(pit[ch].count <= 0) {
        !          5911:                        pit_set_signal(ch, 1);
        !          5912:                }
        !          5913:        }
        !          5914:        if(pit[ch].count <= 0) {
        !          5915:                if(pit[ch].mode == 0 || pit[ch].mode == 2 || pit[ch].mode == 3) {
        !          5916:                        pit[ch].count += tmp;
        !          5917:                        pit[ch].null_count = 0;
        !          5918:                        goto loop;
        !          5919:                } else {
        !          5920:                        pit[ch].start = 0;
        !          5921:                        pit[ch].count = 0x10000;
        !          5922:                }
        !          5923:        }
        !          5924: }
        !          5925: 
        !          5926: void pit_start_count(int ch)
        !          5927: {
        !          5928:        if(pit[ch].low_write || pit[ch].high_write) {
        !          5929:                return;
        !          5930:        }
        !          5931:        //if(!pit[ch].gate) {
        !          5932:        //      return;
        !          5933:        //}
        !          5934:        pit[ch].start = 1;
        !          5935:        
        !          5936:        // next expired time
        !          5937:        pit[ch].input_clk = pit[ch].delay ? 1 : pit_get_next_count(ch);
        !          5938:        UINT32 cur_time = timeGetTime();
        !          5939:        pit[ch].prev_time = cur_time;
        !          5940:        pit[ch].expired_time = cur_time + pit_get_expired_time(pit[ch].input_clk);
        !          5941: }
        !          5942: 
        !          5943: void pit_stop_count(int ch)
        !          5944: {
        !          5945:        pit[ch].start = 0;
        !          5946: }
        !          5947: 
        !          5948: void pit_latch_count(int ch)
        !          5949: {
        !          5950:        if(pit[ch].start) {
        !          5951:                // update pit
        !          5952:                UINT32 cur_time = timeGetTime();
        !          5953:                if(cur_time > pit[ch].prev_time) {
        !          5954:                        UINT32 input = PIT_FREQ * (cur_time - pit[ch].prev_time) / 1000;
        !          5955:                        pit_input_clock(ch, input);
        !          5956:                        
        !          5957:                        if(pit[ch].input_clk <= input) {
        !          5958:                                // next expired time
        !          5959:                                if(pit[ch].start) {
        !          5960:                                        pit[ch].input_clk = pit[ch].delay ? 1 : pit_get_next_count(ch);
        !          5961:                                        pit[ch].prev_time = pit[ch].expired_time;
        !          5962:                                        pit[ch].expired_time += pit_get_expired_time(pit[ch].input_clk);
        !          5963:                                        if(pit[ch].expired_time <= cur_time) {
        !          5964:                                                pit[ch].prev_time = cur_time;
        !          5965:                                                pit[ch].expired_time = cur_time + pit_get_expired_time(pit[ch].input_clk);
        !          5966:                                        }
        !          5967:                                }
        !          5968:                        } else {
        !          5969:                                pit[ch].input_clk -= input;
        !          5970:                                pit[ch].prev_time = cur_time;
        !          5971:                        }
        !          5972:                }
        !          5973:        }
        !          5974:        // latch pit
        !          5975:        pit[ch].latch = (UINT16)pit[ch].count;
        !          5976:        pit[ch].count_latched = 1;
        !          5977:        if((pit[ch].ctrl_reg & 0x30) == 0x10) {
        !          5978:                // lower byte
        !          5979:                pit[ch].low_read = 1;
        !          5980:                pit[ch].high_read = 0;
        !          5981:        } else if((pit[ch].ctrl_reg & 0x30) == 0x20) {
        !          5982:                // upper byte
        !          5983:                pit[ch].low_read = 0;
        !          5984:                pit[ch].high_read = 1;
        !          5985:        } else {
        !          5986:                // lower -> upper
        !          5987:                pit[ch].low_read = pit[ch].low_read = 1;
        !          5988:        }
        !          5989: }
        !          5990: 
        !          5991: void pit_set_signal(int ch, int signal)
        !          5992: {
        !          5993:        int prev = pit[ch].prev_out;
        !          5994:        pit[ch].prev_out = signal;
        !          5995:        
        !          5996:        if(prev && !signal) {
        !          5997:                // H->L
        !          5998:                if(ch == 0) {
        !          5999:                        pic_req(0, 0, 0);
        !          6000:                }
        !          6001:        } else if(!prev && signal) {
        !          6002:                // L->H
        !          6003:                if(ch == 0) {
        !          6004:                        pic_req(0, 0, 1);
        !          6005:                }
        !          6006:        }
        !          6007: }
        !          6008: 
        !          6009: int pit_get_next_count(int ch)
        !          6010: {
        !          6011:        if(pit[ch].mode == 2 || pit[ch].mode == 4 || pit[ch].mode == 5) {
        !          6012:                if(pit[ch].count > 1) {
        !          6013:                        return(pit[ch].count - 1);
        !          6014:                } else {
        !          6015:                        return(1);
        !          6016:                }
        !          6017:        }
        !          6018:        if(pit[ch].mode == 3) {
        !          6019:                INT32 half = PIT_COUNT_VALUE(ch) >> 1;
        !          6020:                if(pit[ch].count > half) {
        !          6021:                        return(pit[ch].count - half);
        !          6022:                } else {
        !          6023:                        return(pit[ch].count);
        !          6024:                }
        !          6025:        }
        !          6026:        return(pit[ch].count);
        !          6027: }
        !          6028: 
        !          6029: int pit_get_expired_time(int clock)
        !          6030: {
        !          6031:        UINT32 val = 1000 * clock / PIT_FREQ;
        !          6032:        
        !          6033:        if(val > 0) {
        !          6034:                return(val);
        !          6035:        } else {
        !          6036:                return(1);
        !          6037:        }
        !          6038: }
        !          6039: 
        !          6040: // i/o bus
        !          6041: 
        !          6042: UINT8 read_io_byte(offs_t addr)
        !          6043: {
        !          6044:        switch(addr) {
        !          6045: #ifdef SUPPORT_HARDWARE
        !          6046:        case 0x20:
        !          6047:        case 0x21:
        !          6048:                return(pic_read(0, addr));
        !          6049:        case 0x40:
        !          6050:        case 0x41:
        !          6051:        case 0x42:
        !          6052:        case 0x43:
        !          6053:                return(pit_read(addr & 0x03));
        !          6054: #endif
        !          6055:        case 0x71:
        !          6056:                return(cmos[cmos_addr & 0x7f]);
        !          6057:        case 0x92:
        !          6058:                return((cpustate->a20_mask >> 19) & 2);
        !          6059: #ifdef SUPPORT_HARDWARE
        !          6060:        case 0xa0:
        !          6061:        case 0xa1:
        !          6062:                return(pic_read(1, addr));
        !          6063: #endif
        !          6064:        default:
        !          6065: //             error("inb %4x\n", addr);
        !          6066:                break;
        !          6067:        }
        !          6068:        return(0xff);
        !          6069: }
        !          6070: 
        !          6071: UINT16 read_io_word(offs_t addr)
        !          6072: {
        !          6073:        return(read_io_byte(addr) | (read_io_byte(addr + 1) << 8));
        !          6074: }
        !          6075: 
        !          6076: UINT32 read_io_dword(offs_t addr)
        !          6077: {
        !          6078:        return(read_io_byte(addr) | (read_io_byte(addr + 1) << 8) | (read_io_byte(addr + 2) << 16) | (read_io_byte(addr + 3) << 24));
        !          6079: }
        !          6080: 
        !          6081: void write_io_byte(offs_t addr, UINT8 val)
        !          6082: {
        !          6083:        switch(addr) {
        !          6084: #ifdef SUPPORT_HARDWARE
        !          6085:        case 0x20:
        !          6086:        case 0x21:
        !          6087:                pic_write(0, addr, val);
        !          6088:                break;
        !          6089:        case 0x40:
        !          6090:        case 0x41:
        !          6091:        case 0x42:
        !          6092:        case 0x43:
        !          6093:                pit_write(addr & 0x03, val);
        !          6094:                break;
        !          6095: #endif
        !          6096:        case 0x64:
        !          6097:                if(val == 0xfe) {
        !          6098:                        if(cmos[0x0f] == 5) {
        !          6099:                                // reset pic
        !          6100:                                pic_init();
        !          6101:                                pic[0].irr = pic[1].irr = 0x00;
        !          6102:                                pic[0].imr = pic[1].imr = 0xff;
        !          6103:                        }
        !          6104:                        CPU_RESET_CALL(CPU_MODEL);
        !          6105:                        i386_jmp_far(0x40, 0x67);
        !          6106:                }
        !          6107:                break;
        !          6108:        case 0x70:
        !          6109:                cmos_addr = val;
        !          6110:                break;
        !          6111:        case 0x71:
        !          6112:                cmos[cmos_addr & 7] = val;
        !          6113:                break;
        !          6114:        case 0x92:
        !          6115:                i386_set_a20_line(cpustate, val & 2);
        !          6116:                break;
        !          6117: #ifdef SUPPORT_HARDWARE
        !          6118:        case 0xa0:
        !          6119:        case 0xa1:
        !          6120:                pic_write(1, addr, val);
        !          6121:                break;
        !          6122: #endif
        !          6123:        default:
        !          6124: //             error("outb %4x,%2x\n", addr, val);
        !          6125:                break;
        !          6126:        }
        !          6127: }
        !          6128: 
        !          6129: void write_io_word(offs_t addr, UINT16 val)
        !          6130: {
        !          6131:        write_io_byte(addr + 0, (val >> 0) & 0xff);
        !          6132:        write_io_byte(addr + 1, (val >> 8) & 0xff);
        !          6133: }
        !          6134: 
        !          6135: void write_io_dword(offs_t addr, UINT32 val)
        !          6136: {
        !          6137:        write_io_byte(addr + 0, (val >>  0) & 0xff);
        !          6138:        write_io_byte(addr + 1, (val >>  8) & 0xff);
        !          6139:        write_io_byte(addr + 2, (val >> 16) & 0xff);
        !          6140:        write_io_byte(addr + 3, (val >> 24) & 0xff);
        !          6141: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.