|
|
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:
1.1.1.2 ! root 3215: // MS-DOS version (7.10)
1.1 root 3216: REG8(BL) = 7;
1.1.1.2 ! root 3217: REG8(BH) = 10;
1.1 root 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, ®16(DX), ®16(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)), ®16(DX), ®16(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, §ors_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);
1.1.1.2 ! root 5687: }
1.1 root 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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.