1 /*
2  *  Copyright (C) 2002-2010  The DOSBox Team
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18 
19 /* $Id: dos.cpp,v 1.121 2009-10-28 21:45:12 qbix79 Exp $ */
20 
21 #include <stdlib.h>
22 #include <string.h>
23 #include <ctype.h>
24 #include <time.h>
25 #include "dosbox.h"
26 #include "bios.h"
27 #include "mem.h"
28 #include "callback.h"
29 #include "regs.h"
30 #include "dos_inc.h"
31 #include "setup.h"
32 #include "support.h"
33 #include "serialport.h"
34 
35 DOS_Block dos;
36 DOS_InfoBlock dos_infoblock;
37 
38 #define DOS_COPYBUFSIZE 0x10000
39 Bit8u dos_copybuf[DOS_COPYBUFSIZE];
40 
DOS_SetError(Bit16u code)41 void DOS_SetError(Bit16u code) {
42 	dos.errorcode=code;
43 }
44 
45 #define DATA_TRANSFERS_TAKE_CYCLES 1
46 #ifdef DATA_TRANSFERS_TAKE_CYCLES
47 
48 #ifndef DOSBOX_CPU_H
49 #include "cpu.h"
50 #endif
modify_cycles(Bits value)51 static inline void modify_cycles(Bits value) {
52 	if((4*value+5) < CPU_Cycles) {
53 		CPU_Cycles -= 4*value;
54 		CPU_IODelayRemoved += 4*value;
55 	} else {
56 		CPU_IODelayRemoved += CPU_Cycles/*-5*/; //don't want to mess with negative
57 		CPU_Cycles = 5;
58 	}
59 }
60 #else
modify_cycles(Bits)61 static inline void modify_cycles(Bits /* value */) {
62 	return;
63 }
64 #endif
65 #define DOS_OVERHEAD 1
66 #ifdef DOS_OVERHEAD
67 #ifndef DOSBOX_CPU_H
68 #include "cpu.h"
69 #endif
70 
overhead()71 static inline void overhead() {
72 	reg_ip += 2;
73 }
74 #else
overhead()75 static inline void overhead() {
76 	return;
77 }
78 #endif
79 
80 #define DOSNAMEBUF 256
DOS_21Handler(void)81 static Bitu DOS_21Handler(void) {
82 	if (((reg_ah != 0x50) && (reg_ah != 0x51) && (reg_ah != 0x62) && (reg_ah != 0x64)) && (reg_ah<0x6c)) {
83 		DOS_PSP psp(dos.psp());
84 		psp.SetStack(RealMake(SegValue(ss),reg_sp-18));
85 	}
86 
87 	char name1[DOSNAMEBUF+2+DOS_NAMELENGTH_ASCII];
88 	char name2[DOSNAMEBUF+2+DOS_NAMELENGTH_ASCII];
89 
90 	switch (reg_ah) {
91 	case 0x00:		/* Terminate Program */
92 		DOS_Terminate(mem_readw(SegPhys(ss)+reg_sp+2),false,0);
93 		break;
94 	case 0x01:		/* Read character from STDIN, with echo */
95 		{
96 			Bit8u c;Bit16u n=1;
97 			dos.echo=true;
98 			DOS_ReadFile(STDIN,&c,&n);
99 			reg_al=c;
100 			dos.echo=false;
101 		}
102 		break;
103 	case 0x02:		/* Write character to STDOUT */
104 		{
105 			Bit8u c=reg_dl;Bit16u n=1;
106 			DOS_WriteFile(STDOUT,&c,&n);
107 			//Not in the official specs, but happens nonetheless. (last written character)
108 			reg_al = c;// reg_al=(c==9)?0x20:c; //Officially: tab to spaces
109 		}
110 		break;
111 	case 0x03:		/* Read character from STDAUX */
112 		{
113 			Bit16u port = real_readw(0x40,0);
114 			if(port!=0 && serialports[0]) {
115 				Bit8u status;
116 				// RTS/DTR on
117 				IO_WriteB(port+4,0x3);
118 				serialports[0]->Getchar(&reg_al, &status, true, 0xFFFFFFFF);
119 			}
120 		}
121 		break;
122 	case 0x04:		/* Write Character to STDAUX */
123 		{
124 			Bit16u port = real_readw(0x40,0);
125 			if(port!=0 && serialports[0]) {
126 				// RTS/DTR on
127 				IO_WriteB(port+4,0x3);
128 				serialports[0]->Putchar(reg_dl,true,true, 0xFFFFFFFF);
129 				// RTS off
130 				IO_WriteB(port+4,0x1);
131 			}
132 		}
133 		break;
134 	case 0x05:		/* Write Character to PRINTER */
135 		E_Exit("DOS:Unhandled call %02X",reg_ah);
136 		break;
137 	case 0x06:		/* Direct Console Output / Input */
138 		switch (reg_dl) {
139 		case 0xFF:	/* Input */
140 			{
141 				//Simulate DOS overhead for timing sensitive games
142 				//MM1
143 				overhead();
144 				//TODO Make this better according to standards
145 				if (!DOS_GetSTDINStatus()) {
146 					reg_al=0;
147 					CALLBACK_SZF(true);
148 					break;
149 				}
150 				Bit8u c;Bit16u n=1;
151 				DOS_ReadFile(STDIN,&c,&n);
152 				reg_al=c;
153 				CALLBACK_SZF(false);
154 				break;
155 			}
156 		default:
157 			{
158 				Bit8u c = reg_dl;Bit16u n = 1;
159 				DOS_WriteFile(STDOUT,&c,&n);
160 				reg_al = reg_dl;
161 			}
162 			break;
163 		};
164 		break;
165 	case 0x07:		/* Character Input, without echo */
166 		{
167 				Bit8u c;Bit16u n=1;
168 				DOS_ReadFile (STDIN,&c,&n);
169 				reg_al=c;
170 				break;
171 		};
172 	case 0x08:		/* Direct Character Input, without echo (checks for breaks officially :)*/
173 		{
174 				Bit8u c;Bit16u n=1;
175 				DOS_ReadFile (STDIN,&c,&n);
176 				reg_al=c;
177 				break;
178 		};
179 	case 0x09:		/* Write string to STDOUT */
180 		{
181 			Bit8u c;Bit16u n=1;
182 			PhysPt buf=SegPhys(ds)+reg_dx;
183 			while ((c=mem_readb(buf++))!='$') {
184 				DOS_WriteFile(STDOUT,&c,&n);
185 			}
186 		}
187 		break;
188 	case 0x0a:		/* Buffered Input */
189 		{
190 			//TODO ADD Break checkin in STDIN but can't care that much for it
191 			PhysPt data=SegPhys(ds)+reg_dx;
192 			Bit8u free=mem_readb(data);
193 			Bit8u read=0;Bit8u c;Bit16u n=1;
194 			if (!free) break;
195 			for(;;) {
196 				DOS_ReadFile(STDIN,&c,&n);
197 				if (c == 8) {			// Backspace
198 					if (read) {	//Something to backspace.
199 						// STDOUT treats backspace as non-destructive.
200 						         DOS_WriteFile(STDOUT,&c,&n);
201 						c = ' '; DOS_WriteFile(STDOUT,&c,&n);
202 						c = 8;   DOS_WriteFile(STDOUT,&c,&n);
203 						--read;
204 					}
205 					continue;
206 				}
207 				if (read >= free) {		// Keyboard buffer full
208 					Bit8u bell = 7;
209 					DOS_WriteFile(STDOUT, &bell, &n);
210 					continue;
211 				}
212 				DOS_WriteFile(STDOUT,&c,&n);
213 				mem_writeb(data+read+2,c);
214 				if (c==13)
215 					break;
216 				read++;
217 			};
218 			mem_writeb(data+1,read);
219 			break;
220 		};
221 	case 0x0b:		/* Get STDIN Status */
222 		if (!DOS_GetSTDINStatus()) {reg_al=0x00;}
223 		else {reg_al=0xFF;}
224 		//Simulate some overhead for timing issues
225 		//Tankwar menu (needs maybe even more)
226 		overhead();
227 		break;
228 	case 0x0c:		/* Flush Buffer and read STDIN call */
229 		{
230 			/* flush STDIN-buffer */
231 			Bit8u c;Bit16u n;
232 			while (DOS_GetSTDINStatus()) {
233 				n=1;	DOS_ReadFile(STDIN,&c,&n);
234 			}
235 			switch (reg_al) {
236 			case 0x1:
237 			case 0x6:
238 			case 0x7:
239 			case 0x8:
240 			case 0xa:
241 				{
242 					Bit8u oldah=reg_ah;
243 					reg_ah=reg_al;
244 					DOS_21Handler();
245 					reg_ah=oldah;
246 				}
247 				break;
248 			default:
249 //				LOG_ERROR("DOS:0C:Illegal Flush STDIN Buffer call %d",reg_al);
250 				reg_al=0;
251 				break;
252 			}
253 		}
254 		break;
255 //TODO Find out the values for when reg_al!=0
256 //TODO Hope this doesn't do anything special
257 	case 0x0d:		/* Disk Reset */
258 //Sure let's reset a virtual disk
259 		break;
260 	case 0x0e:		/* Select Default Drive */
261 		DOS_SetDefaultDrive(reg_dl);
262 		reg_al=DOS_DRIVES;
263 		break;
264 	case 0x0f:		/* Open File using FCB */
265 		if(DOS_FCBOpen(SegValue(ds),reg_dx)){
266 			reg_al=0;
267 		}else{
268 			reg_al=0xff;
269 		}
270 		LOG(LOG_FCB,LOG_NORMAL)("DOS:0x0f FCB-fileopen used, result:al=%d",reg_al);
271 		break;
272 	case 0x10:		/* Close File using FCB */
273 		if(DOS_FCBClose(SegValue(ds),reg_dx)){
274 			reg_al=0;
275 		}else{
276 			reg_al=0xff;
277 		}
278 		LOG(LOG_FCB,LOG_NORMAL)("DOS:0x10 FCB-fileclose used, result:al=%d",reg_al);
279 		break;
280 	case 0x11:		/* Find First Matching File using FCB */
281 		if(DOS_FCBFindFirst(SegValue(ds),reg_dx)) reg_al = 0x00;
282 		else reg_al = 0xFF;
283 		LOG(LOG_FCB,LOG_NORMAL)("DOS:0x11 FCB-FindFirst used, result:al=%d",reg_al);
284 		break;
285 	case 0x12:		/* Find Next Matching File using FCB */
286 		if(DOS_FCBFindNext(SegValue(ds),reg_dx)) reg_al = 0x00;
287 		else reg_al = 0xFF;
288 		LOG(LOG_FCB,LOG_NORMAL)("DOS:0x12 FCB-FindNext used, result:al=%d",reg_al);
289 		break;
290 	case 0x13:		/* Delete File using FCB */
291 		if (DOS_FCBDeleteFile(SegValue(ds),reg_dx)) reg_al = 0x00;
292 		else reg_al = 0xFF;
293 		LOG(LOG_FCB,LOG_NORMAL)("DOS:0x16 FCB-Delete used, result:al=%d",reg_al);
294 		break;
295 	case 0x14:		/* Sequential read from FCB */
296 		reg_al = DOS_FCBRead(SegValue(ds),reg_dx,0);
297 		LOG(LOG_FCB,LOG_NORMAL)("DOS:0x14 FCB-Read used, result:al=%d",reg_al);
298 		break;
299 	case 0x15:		/* Sequential write to FCB */
300 		reg_al=DOS_FCBWrite(SegValue(ds),reg_dx,0);
301 		LOG(LOG_FCB,LOG_NORMAL)("DOS:0x15 FCB-Write used, result:al=%d",reg_al);
302 		break;
303 	case 0x16:		/* Create or truncate file using FCB */
304 		if (DOS_FCBCreate(SegValue(ds),reg_dx)) reg_al = 0x00;
305 		else reg_al = 0xFF;
306 		LOG(LOG_FCB,LOG_NORMAL)("DOS:0x16 FCB-Create used, result:al=%d",reg_al);
307 		break;
308 	case 0x17:		/* Rename file using FCB */
309 		if (DOS_FCBRenameFile(SegValue(ds),reg_dx)) reg_al = 0x00;
310 		else reg_al = 0xFF;
311 		break;
312 	case 0x1b:		/* Get allocation info for default drive */
313 		if (!DOS_GetAllocationInfo(0,&reg_cx,&reg_al,&reg_dx)) reg_al=0xff;
314 		break;
315 	case 0x1c:		/* Get allocation info for specific drive */
316 		if (!DOS_GetAllocationInfo(reg_dl,&reg_cx,&reg_al,&reg_dx)) reg_al=0xff;
317 		break;
318 	case 0x21:		/* Read random record from FCB */
319 		reg_al = DOS_FCBRandomRead(SegValue(ds),reg_dx,1,true);
320 		LOG(LOG_FCB,LOG_NORMAL)("DOS:0x21 FCB-Random read used, result:al=%d",reg_al);
321 		break;
322 	case 0x22:		/* Write random record to FCB */
323 		reg_al=DOS_FCBRandomWrite(SegValue(ds),reg_dx,1,true);
324 		LOG(LOG_FCB,LOG_NORMAL)("DOS:0x22 FCB-Random write used, result:al=%d",reg_al);
325 		break;
326 	case 0x23:		/* Get file size for FCB */
327 		if (DOS_FCBGetFileSize(SegValue(ds),reg_dx)) reg_al = 0x00;
328 		else reg_al = 0xFF;
329 		break;
330 	case 0x24:		/* Set Random Record number for FCB */
331 		DOS_FCBSetRandomRecord(SegValue(ds),reg_dx);
332 		break;
333 	case 0x27:		/* Random block read from FCB */
334 		reg_al = DOS_FCBRandomRead(SegValue(ds),reg_dx,reg_cx,false);
335 		LOG(LOG_FCB,LOG_NORMAL)("DOS:0x27 FCB-Random(block) read used, result:al=%d",reg_al);
336 		break;
337 	case 0x28:		/* Random Block write to FCB */
338 		reg_al=DOS_FCBRandomWrite(SegValue(ds),reg_dx,reg_cx,false);
339 		LOG(LOG_FCB,LOG_NORMAL)("DOS:0x28 FCB-Random(block) write used, result:al=%d",reg_al);
340 		break;
341 	case 0x29:		/* Parse filename into FCB */
342 		{
343 			Bit8u difference;
344 			char string[1024];
345 			MEM_StrCopy(SegPhys(ds)+reg_si,string,1023); // 1024 toasts the stack
346 			reg_al=FCB_Parsename(SegValue(es),reg_di,reg_al ,string, &difference);
347 			reg_si+=difference;
348 		}
349 		LOG(LOG_FCB,LOG_NORMAL)("DOS:29:FCB Parse Filename, result:al=%d",reg_al);
350 		break;
351 	case 0x19:		/* Get current default drive */
352 		reg_al=DOS_GetDefaultDrive();
353 		break;
354 	case 0x1a:		/* Set Disk Transfer Area Address */
355 		dos.dta(RealMakeSeg(ds,reg_dx));
356 		break;
357 	case 0x25:		/* Set Interrupt Vector */
358 		RealSetVec(reg_al,RealMakeSeg(ds,reg_dx));
359 		break;
360 	case 0x26:		/* Create new PSP */
361 		DOS_NewPSP(reg_dx,DOS_PSP(dos.psp()).GetSize());
362 		break;
363 	case 0x2a:		/* Get System Date */
364 		{
365 			int a = (14 - dos.date.month)/12;
366 			int y = dos.date.year - a;
367 			int m = dos.date.month + 12*a - 2;
368 			reg_al=(dos.date.day+y+(y/4)-(y/100)+(y/400)+(31*m)/12) % 7;
369 			reg_cx=dos.date.year;
370 			reg_dh=dos.date.month;
371 			reg_dl=dos.date.day;
372 		}
373 		break;
374 	case 0x2b:		/* Set System Date */
375 		if (reg_cx<1980) { reg_al=0xff;break;}
376 		if ((reg_dh>12) || (reg_dh==0))	{ reg_al=0xff;break;}
377 		if ((reg_dl>31) || (reg_dl==0))	{ reg_al=0xff;break;}
378 		dos.date.year=reg_cx;
379 		dos.date.month=reg_dh;
380 		dos.date.day=reg_dl;
381 		reg_al=0;
382 		break;
383 	case 0x2c:		/* Get System Time */
384 //TODO Get time through bios calls date is fixed
385 		{
386 /*	Calculate how many miliseconds have passed */
387 			Bitu ticks=5*mem_readd(BIOS_TIMER);
388 			ticks = ((ticks / 59659u) << 16) + ((ticks % 59659u) << 16) / 59659u;
389 			Bitu seconds=(ticks/100);
390 			reg_ch=(Bit8u)(seconds/3600);
391 			reg_cl=(Bit8u)((seconds % 3600)/60);
392 			reg_dh=(Bit8u)(seconds % 60);
393 			reg_dl=(Bit8u)(ticks % 100);
394 		}
395 		//Simulate DOS overhead for timing-sensitive games
396         	//Robomaze 2
397 		overhead();
398 		break;
399 	case 0x2d:		/* Set System Time */
400 		LOG(LOG_DOSMISC,LOG_ERROR)("DOS:Set System Time not supported");
401 		//Check input parameters nonetheless
402 		if( reg_ch > 23 || reg_cl > 59 || reg_dh > 59 || reg_dl > 99 )
403 			reg_al = 0xff;
404 		else reg_al = 0;
405 		break;
406 	case 0x2e:		/* Set Verify flag */
407 		dos.verify=(reg_al==1);
408 		break;
409 	case 0x2f:		/* Get Disk Transfer Area */
410 		SegSet16(es,RealSeg(dos.dta()));
411 		reg_bx=RealOff(dos.dta());
412 		break;
413 	case 0x30:		/* Get DOS Version */
414 		if (reg_al==0) reg_bh=0xFF;		/* Fake Microsoft DOS */
415 		if (reg_al==1) reg_bh=0x10;		/* DOS is in HMA */
416 		reg_al=dos.version.major;
417 		reg_ah=dos.version.minor;
418 		/* Serialnumber */
419 		reg_bl=0x00;
420 		reg_cx=0x0000;
421 		break;
422 	case 0x31:		/* Terminate and stay resident */
423 		// Important: This service does not set the carry flag!
424 		DOS_ResizeMemory(dos.psp(),&reg_dx);
425 		DOS_Terminate(dos.psp(),true,reg_al);
426 		break;
427 	case 0x1f: /* Get drive parameter block for default drive */
428 	case 0x32: /* Get drive parameter block for specific drive */
429 		{	/* Officially a dpb should be returned as well. The disk detection part is implemented */
430 			Bit8u drive=reg_dl;
431 			if (!drive || reg_ah==0x1f) drive = DOS_GetDefaultDrive();
432 			else drive--;
433 			if (Drives[drive]) {
434 				reg_al = 0x00;
435 				SegSet16(ds,dos.tables.dpb);
436 				reg_bx = drive;//Faking only the first entry (that is the driveletter)
437 				LOG(LOG_DOSMISC,LOG_ERROR)("Get drive parameter block.");
438 			} else {
439 				reg_al=0xff;
440 			}
441 		}
442 		break;
443 	case 0x33:		/* Extended Break Checking */
444 		switch (reg_al) {
445 			case 0:reg_dl=dos.breakcheck;break;			/* Get the breakcheck flag */
446 			case 1:dos.breakcheck=(reg_dl>0);break;		/* Set the breakcheck flag */
447 			case 2:{bool old=dos.breakcheck;dos.breakcheck=(reg_dl>0);reg_dl=old;}break;
448 			case 3: /* Get cpsw */
449 				/* Fallthrough */
450 			case 4: /* Set cpsw */
451 				LOG(LOG_DOSMISC,LOG_ERROR)("Someone playing with cpsw %x",reg_ax);
452 				break;
453 			case 5:reg_dl=3;break;//TODO should be z						/* Always boot from c: :) */
454 			case 6:											/* Get true version number */
455 				reg_bl=dos.version.major;
456 				reg_bh=dos.version.minor;
457 				reg_dl=dos.version.revision;
458 				reg_dh=0x10;								/* Dos in HMA */
459 				break;
460 			default:
461 				E_Exit("DOS:Illegal 0x33 Call %2X",reg_al);
462 		}
463 		break;
464 	case 0x34:		/* Get INDos Flag */
465 		SegSet16(es,DOS_SDA_SEG);
466 		reg_bx=DOS_SDA_OFS + 0x01;
467 		break;
468 	case 0x35:		/* Get interrupt vector */
469 		reg_bx=real_readw(0,((Bit16u)reg_al)*4);
470 		SegSet16(es,real_readw(0,((Bit16u)reg_al)*4+2));
471 		break;
472 	case 0x36:		/* Get Free Disk Space */
473 		{
474 			Bit16u bytes,clusters,free;
475 			Bit8u sectors;
476 			if (DOS_GetFreeDiskSpace(reg_dl,&bytes,&sectors,&clusters,&free)) {
477 				reg_ax=sectors;
478 				reg_bx=free;
479 				reg_cx=bytes;
480 				reg_dx=clusters;
481 			} else {
482 				Bit8u drive=reg_dl;
483 				if (drive==0) drive=DOS_GetDefaultDrive();
484 				else drive--;
485 				if (drive<2) {
486 					// floppy drive, non-present drivesdisks issue floppy check through int24
487 					// (critical error handler); needed for Mixed up Mother Goose (hook)
488 //					CALLBACK_RunRealInt(0x24);
489 				}
490 				reg_ax=0xffff;	// invalid drive specified
491 			}
492 		}
493 		break;
494 	case 0x37:		/* Get/Set Switch char Get/Set Availdev thing */
495 //TODO	Give errors for these functions to see if anyone actually uses this shit-
496 		switch (reg_al) {
497 		case 0:
498 			 reg_al=0;reg_dl=0x2f;break;  /* always return '/' like dos 5.0+ */
499 		case 1:
500 			 reg_al=0;break;
501 		case 2:
502 			 reg_al=0;reg_dl=0x2f;break;
503 		case 3:
504 			 reg_al=0;break;
505 		};
506 		LOG(LOG_MISC,LOG_ERROR)("DOS:0x37:Call for not supported switchchar");
507 		break;
508 	case 0x38:					/* Set Country Code */
509 		if (reg_al==0) {		/* Get country specidic information */
510 			PhysPt dest = SegPhys(ds)+reg_dx;
511 			MEM_BlockWrite(dest,dos.tables.country,0x18);
512 			reg_ax = reg_bx = 0x01;
513 			CALLBACK_SCF(false);
514 			break;
515 		} else {				/* Set country code */
516 			LOG(LOG_MISC,LOG_ERROR)("DOS:Setting country code not supported");
517 		}
518 		CALLBACK_SCF(true);
519 		break;
520 	case 0x39:		/* MKDIR Create directory */
521 		MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF);
522 		if (DOS_MakeDir(name1)) {
523 			CALLBACK_SCF(false);
524 		} else {
525 			reg_ax=dos.errorcode;
526 			CALLBACK_SCF(true);
527 		}
528 		break;
529 	case 0x3a:		/* RMDIR Remove directory */
530 		MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF);
531 		if  (DOS_RemoveDir(name1)) {
532 			CALLBACK_SCF(false);
533 		} else {
534 			reg_ax=dos.errorcode;
535 			CALLBACK_SCF(true);
536 			LOG(LOG_MISC,LOG_NORMAL)("Remove dir failed on %s with error %X",name1,dos.errorcode);
537 		}
538 		break;
539 	case 0x3b:		/* CHDIR Set current directory */
540 		MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF);
541 		if  (DOS_ChangeDir(name1)) {
542 			CALLBACK_SCF(false);
543 		} else {
544 			reg_ax=dos.errorcode;
545 			CALLBACK_SCF(true);
546 		}
547 		break;
548 	case 0x3c:		/* CREATE Create of truncate file */
549 		MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF);
550 		if (DOS_CreateFile(name1,reg_cx,&reg_ax)) {
551 			CALLBACK_SCF(false);
552 		} else {
553 			reg_ax=dos.errorcode;
554 			CALLBACK_SCF(true);
555 		}
556 		break;
557 	case 0x3d:		/* OPEN Open existing file */
558 		MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF);
559 		if (DOS_OpenFile(name1,reg_al,&reg_ax)) {
560 			CALLBACK_SCF(false);
561 		} else {
562 			reg_ax=dos.errorcode;
563 			CALLBACK_SCF(true);
564 		}
565 		break;
566 	case 0x3e:		/* CLOSE Close file */
567 		if (DOS_CloseFile(reg_bx)) {
568 			CALLBACK_SCF(false);
569 		} else {
570 			reg_ax=dos.errorcode;
571 			CALLBACK_SCF(true);
572 		}
573 		break;
574 	case 0x3f:		/* READ Read from file or device */
575 		{
576 			Bit16u toread=reg_cx;
577 			dos.echo=true;
578 			if (DOS_ReadFile(reg_bx,dos_copybuf,&toread)) {
579 				MEM_BlockWrite(SegPhys(ds)+reg_dx,dos_copybuf,toread);
580 				reg_ax=toread;
581 				CALLBACK_SCF(false);
582 			} else {
583 				reg_ax=dos.errorcode;
584 				CALLBACK_SCF(true);
585 			}
586 			modify_cycles(reg_ax);
587 			dos.echo=false;
588 			break;
589 		}
590 	case 0x40:					/* WRITE Write to file or device */
591 		{
592 			Bit16u towrite=reg_cx;
593 			MEM_BlockRead(SegPhys(ds)+reg_dx,dos_copybuf,towrite);
594 			if (DOS_WriteFile(reg_bx,dos_copybuf,&towrite)) {
595 				reg_ax=towrite;
596 	   			CALLBACK_SCF(false);
597 			} else {
598 				reg_ax=dos.errorcode;
599 				CALLBACK_SCF(true);
600 			}
601 			modify_cycles(reg_ax);
602 			break;
603 		};
604 	case 0x41:					/* UNLINK Delete file */
605 		MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF);
606 		if (DOS_UnlinkFile(name1)) {
607 			CALLBACK_SCF(false);
608 		} else {
609 			reg_ax=dos.errorcode;
610 			CALLBACK_SCF(true);
611 		}
612 		break;
613 	case 0x42:					/* LSEEK Set current file position */
614 		{
615 			Bit32u pos=(reg_cx<<16) + reg_dx;
616 			if (DOS_SeekFile(reg_bx,&pos,reg_al)) {
617 				reg_dx=(Bit16u)(pos >> 16);
618 				reg_ax=(Bit16u)(pos & 0xFFFF);
619 				CALLBACK_SCF(false);
620 			} else {
621 				reg_ax=dos.errorcode;
622 				CALLBACK_SCF(true);
623 			}
624 			break;
625 		}
626 	case 0x43:					/* Get/Set file attributes */
627 		MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF);
628 		switch (reg_al) {
629 		case 0x00:				/* Get */
630 			{
631 				Bit16u attr_val=reg_cx;
632 				if (DOS_GetFileAttr(name1,&attr_val)) {
633 					reg_cx=attr_val;
634 					reg_ax=attr_val; /* Undocumented */
635 					CALLBACK_SCF(false);
636 				} else {
637 					CALLBACK_SCF(true);
638 					reg_ax=dos.errorcode;
639 				}
640 				break;
641 			};
642 		case 0x01:				/* Set */
643 			LOG(LOG_MISC,LOG_ERROR)("DOS:Set File Attributes for %s not supported",name1);
644 			if (DOS_SetFileAttr(name1,reg_cx)) {
645 				reg_ax=0x202;	/* ax destroyed */
646 				CALLBACK_SCF(false);
647 			} else {
648 				CALLBACK_SCF(true);
649 				reg_ax=dos.errorcode;
650 			}
651 			break;
652 		default:
653 			LOG(LOG_MISC,LOG_ERROR)("DOS:0x43:Illegal subfunction %2X",reg_al);
654 			reg_ax=1;
655 			CALLBACK_SCF(true);
656 			break;
657 		}
658 		break;
659 	case 0x44:					/* IOCTL Functions */
660 		if (DOS_IOCTL()) {
661 			CALLBACK_SCF(false);
662 		} else {
663 			reg_ax=dos.errorcode;
664 			CALLBACK_SCF(true);
665 		}
666 		break;
667 	case 0x45:					/* DUP Duplicate file handle */
668 		if (DOS_DuplicateEntry(reg_bx,&reg_ax)) {
669 			CALLBACK_SCF(false);
670 		} else {
671 			reg_ax=dos.errorcode;
672 			CALLBACK_SCF(true);
673 		}
674 		break;
675 	case 0x46:					/* DUP2,FORCEDUP Force duplicate file handle */
676 		if (DOS_ForceDuplicateEntry(reg_bx,reg_cx)) {
677 			reg_ax=reg_cx; //Not all sources agree on it.
678 			CALLBACK_SCF(false);
679 		} else {
680 			reg_ax=dos.errorcode;
681 			CALLBACK_SCF(true);
682 		}
683 		break;
684 	case 0x47:					/* CWD Get current directory */
685 		if (DOS_GetCurrentDir(reg_dl,name1)) {
686 			MEM_BlockWrite(SegPhys(ds)+reg_si,name1,(Bitu)(strlen(name1)+1));
687 			reg_ax=0x0100;
688 			CALLBACK_SCF(false);
689 		} else {
690 			reg_ax=dos.errorcode;
691 			CALLBACK_SCF(true);
692 		}
693 		break;
694 	case 0x48:					/* Allocate memory */
695 		{
696 			Bit16u size=reg_bx;Bit16u seg;
697 			if (DOS_AllocateMemory(&seg,&size)) {
698 				reg_ax=seg;
699 				CALLBACK_SCF(false);
700 			} else {
701 				reg_ax=dos.errorcode;
702 				reg_bx=size;
703 				CALLBACK_SCF(true);
704 			}
705 			break;
706 		}
707 	case 0x49:					/* Free memory */
708 		if (DOS_FreeMemory(SegValue(es))) {
709 			CALLBACK_SCF(false);
710 		} else {
711 			reg_ax=dos.errorcode;
712 			CALLBACK_SCF(true);
713 		}
714 		break;
715 	case 0x4a:					/* Resize memory block */
716 		{
717 			Bit16u size=reg_bx;
718 			if (DOS_ResizeMemory(SegValue(es),&size)) {
719 				reg_ax=SegValue(es);
720 				CALLBACK_SCF(false);
721 			} else {
722 				reg_ax=dos.errorcode;
723 				reg_bx=size;
724 				CALLBACK_SCF(true);
725 			}
726 			break;
727 		}
728 	case 0x4b:					/* EXEC Load and/or execute program */
729 		{
730 			MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF);
731 			LOG(LOG_EXEC,LOG_ERROR)("Execute %s %d",name1,reg_al);
732 			if (!DOS_Execute(name1,SegPhys(es)+reg_bx,reg_al)) {
733 				reg_ax=dos.errorcode;
734 				CALLBACK_SCF(true);
735 			}
736 		}
737 		break;
738 //TODO Check for use of execution state AL=5
739 	case 0x4c:					/* EXIT Terminate with return code */
740 		DOS_Terminate(dos.psp(),false,reg_al);
741 		break;
742 	case 0x4d:					/* Get Return code */
743 		reg_al=dos.return_code;/* Officially read from SDA and clear when read */
744 		reg_ah=dos.return_mode;
745 		break;
746 	case 0x4e:					/* FINDFIRST Find first matching file */
747 		MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF);
748 		if (DOS_FindFirst(name1,reg_cx)) {
749 			CALLBACK_SCF(false);
750 			reg_ax=0;			/* Undocumented */
751 		} else {
752 			reg_ax=dos.errorcode;
753 			CALLBACK_SCF(true);
754 		};
755 		break;
756 	case 0x4f:					/* FINDNEXT Find next matching file */
757 		if (DOS_FindNext()) {
758 			CALLBACK_SCF(false);
759 			/* reg_ax=0xffff;*/			/* Undocumented */
760 			reg_ax=0;				/* Undocumented:Qbix Willy beamish */
761 		} else {
762 			reg_ax=dos.errorcode;
763 			CALLBACK_SCF(true);
764 		};
765 		break;
766 	case 0x50:					/* Set current PSP */
767 		dos.psp(reg_bx);
768 		break;
769 	case 0x51:					/* Get current PSP */
770 		reg_bx=dos.psp();
771 		break;
772 	case 0x52: {				/* Get list of lists */
773 		RealPt addr=dos_infoblock.GetPointer();
774 		SegSet16(es,RealSeg(addr));
775 		reg_bx=RealOff(addr);
776 		LOG(LOG_DOSMISC,LOG_NORMAL)("Call is made for list of lists - let's hope for the best");
777 		break; }
778 //TODO Think hard how shit this is gonna be
779 //And will any game ever use this :)
780 	case 0x53:					/* Translate BIOS parameter block to drive parameter block */
781 		E_Exit("Unhandled Dos 21 call %02X",reg_ah);
782 		break;
783 	case 0x54:					/* Get verify flag */
784 		reg_al=dos.verify?1:0;
785 		break;
786 	case 0x55:					/* Create Child PSP*/
787 		DOS_ChildPSP(reg_dx,reg_si);
788 		dos.psp(reg_dx);
789 		break;
790 	case 0x56:					/* RENAME Rename file */
791 		MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF);
792 		MEM_StrCopy(SegPhys(es)+reg_di,name2,DOSNAMEBUF);
793 		if (DOS_Rename(name1,name2)) {
794 			CALLBACK_SCF(false);
795 		} else {
796 			reg_ax=dos.errorcode;
797 			CALLBACK_SCF(true);
798 		}
799 		break;
800 	case 0x57:					/* Get/Set File's Date and Time */
801 		if (reg_al==0x00) {
802 			if (DOS_GetFileDate(reg_bx,&reg_cx,&reg_dx)) {
803 				CALLBACK_SCF(false);
804 			} else {
805 				CALLBACK_SCF(true);
806 			}
807 		} else if (reg_al==0x01) {
808 			LOG(LOG_DOSMISC,LOG_ERROR)("DOS:57:Set File Date Time Faked");
809 			CALLBACK_SCF(false);
810 		} else {
811 			LOG(LOG_DOSMISC,LOG_ERROR)("DOS:57:Unsupported subtion %X",reg_al);
812 		}
813 		break;
814 	case 0x58:					/* Get/Set Memory allocation strategy */
815 		switch (reg_al) {
816 		case 0:					/* Get Strategy */
817 			reg_ax=DOS_GetMemAllocStrategy();
818 			break;
819 		case 1:					/* Set Strategy */
820 			if (DOS_SetMemAllocStrategy(reg_bx)) CALLBACK_SCF(false);
821 			else {
822 				reg_ax=1;
823 				CALLBACK_SCF(true);
824 			}
825 			break;
826 		case 2:					/* Get UMB Link Status */
827 			reg_al=dos_infoblock.GetUMBChainState()&1;
828 			CALLBACK_SCF(false);
829 			break;
830 		case 3:					/* Set UMB Link Status */
831 			if (DOS_LinkUMBsToMemChain(reg_bx)) CALLBACK_SCF(false);
832 			else {
833 				reg_ax=1;
834 				CALLBACK_SCF(true);
835 			}
836 			break;
837 		default:
838 			LOG(LOG_DOSMISC,LOG_ERROR)("DOS:58:Not Supported Set//Get memory allocation call %X",reg_al);
839 			reg_ax=1;
840 			CALLBACK_SCF(true);
841 		}
842 		break;
843 	case 0x59:					/* Get Extended error information */
844 		reg_ax=dos.errorcode;
845 		if (dos.errorcode==DOSERR_FILE_NOT_FOUND || dos.errorcode==DOSERR_PATH_NOT_FOUND) {
846 			reg_bh=8;	//Not Found error class (Road Hog)
847 		} else {
848 			reg_bh=0;	//Unspecified error class
849 		}
850 		reg_bl=1;	//Retry retry retry
851 		reg_ch=0;	//Unkown error locus
852 		break;
853 	case 0x5a:					/* Create temporary file */
854 		{
855 			Bit16u handle;
856 			MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF);
857 			if (DOS_CreateTempFile(name1,&handle)) {
858 				reg_ax=handle;
859 				MEM_BlockWrite(SegPhys(ds)+reg_dx,name1,(Bitu)(strlen(name1)+1));
860 				CALLBACK_SCF(false);
861 			} else {
862 				reg_ax=dos.errorcode;
863 				CALLBACK_SCF(true);
864 			}
865 		}
866 		break;
867 	case 0x5b:					/* Create new file */
868 		{
869 			MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF);
870 			Bit16u handle;
871 			if (DOS_OpenFile(name1,0,&handle)) {
872 				DOS_CloseFile(handle);
873 				DOS_SetError(DOSERR_FILE_ALREADY_EXISTS);
874 				reg_ax=dos.errorcode;
875 				CALLBACK_SCF(true);
876 				break;
877 			}
878 			if (DOS_CreateFile(name1,reg_cx,&handle)) {
879 				reg_ax=handle;
880 				CALLBACK_SCF(false);
881 			} else {
882 				reg_ax=dos.errorcode;
883 				CALLBACK_SCF(true);
884 			}
885 			break;
886 		}
887 	case 0x5c:			/* FLOCK File region locking */
888 		DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
889 		reg_ax = dos.errorcode;
890 		CALLBACK_SCF(true);
891 		break;
892 	case 0x5d:					/* Network Functions */
893 		if(reg_al == 0x06) {
894 			SegSet16(ds,DOS_SDA_SEG);
895 			reg_si = DOS_SDA_OFS;
896 			reg_cx = 0x80;  // swap if in dos
897 			reg_dx = 0x1a;  // swap always
898 			LOG(LOG_DOSMISC,LOG_ERROR)("Get SDA, Let's hope for the best!");
899 		}
900 		break;
901 	case 0x5f:					/* Network redirection */
902 		reg_ax=0x0001;		//Failing it
903 		CALLBACK_SCF(true);
904 		break;
905 	case 0x60:					/* Canonicalize filename or path */
906 		MEM_StrCopy(SegPhys(ds)+reg_si,name1,DOSNAMEBUF);
907 		if (DOS_Canonicalize(name1,name2)) {
908 				MEM_BlockWrite(SegPhys(es)+reg_di,name2,(Bitu)(strlen(name2)+1));
909 				CALLBACK_SCF(false);
910 			} else {
911 				reg_ax=dos.errorcode;
912 				CALLBACK_SCF(true);
913 			}
914 			break;
915 	case 0x62:					/* Get Current PSP Address */
916 		reg_bx=dos.psp();
917 		break;
918 	case 0x63:					/* DOUBLE BYTE CHARACTER SET */
919 		if(reg_al == 0) {
920 			SegSet16(ds,RealSeg(dos.tables.dbcs));
921 			reg_si=RealOff(dos.tables.dbcs);
922 			reg_al = 0;
923 			CALLBACK_SCF(false); //undocumented
924 		} else reg_al = 0xff; //Doesn't officially touch carry flag
925 		break;
926 	case 0x64:					/* Set device driver lookahead flag */
927 		LOG(LOG_DOSMISC,LOG_NORMAL)("set driver look ahead flag");
928 		break;
929 	case 0x65:					/* Get extented country information and a lot of other useless shit*/
930 		{ /* Todo maybe fully support this for now we set it standard for USA */
931 			LOG(LOG_DOSMISC,LOG_ERROR)("DOS:65:Extended country information call %X",reg_ax);
932 			if((reg_al <=  0x07) && (reg_cx < 0x05)) {
933 				DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
934 				CALLBACK_SCF(true);
935 				break;
936 			}
937 			Bitu len = 0; /* For 0x21 and 0x22 */
938 			PhysPt data=SegPhys(es)+reg_di;
939 			switch (reg_al) {
940 			case 0x01:
941 				mem_writeb(data + 0x00,reg_al);
942 				mem_writew(data + 0x01,0x26);
943 				mem_writew(data + 0x03,1);
944 				if(reg_cx > 0x06 ) mem_writew(data+0x05,dos.loaded_codepage);
945 				if(reg_cx > 0x08 ) {
946 					Bitu amount = (reg_cx>=0x29)?0x22:(reg_cx-7);
947 					MEM_BlockWrite(data + 0x07,dos.tables.country,amount);
948 					reg_cx=(reg_cx>=0x29)?0x29:reg_cx;
949 				}
950 				CALLBACK_SCF(false);
951 				break;
952 			case 0x05: // Get pointer to filename terminator table
953 				mem_writeb(data + 0x00, reg_al);
954 				mem_writed(data + 0x01, dos.tables.filenamechar);
955 				reg_cx = 5;
956 				CALLBACK_SCF(false);
957 				break;
958 			case 0x02: // Get pointer to uppercase table
959 				mem_writeb(data + 0x00, reg_al);
960 				mem_writed(data + 0x01, dos.tables.upcase);
961 				reg_cx = 5;
962 				CALLBACK_SCF(false);
963 				break;
964 			case 0x06: // Get pointer to collating sequence table
965 				mem_writeb(data + 0x00, reg_al);
966 				mem_writed(data + 0x01, dos.tables.collatingseq);
967 				reg_cx = 5;
968 				CALLBACK_SCF(false);
969 				break;
970 			case 0x03: // Get pointer to lowercase table
971 			case 0x04: // Get pointer to filename uppercase table
972 			case 0x07: // Get pointer to double byte char set table
973 				mem_writeb(data + 0x00, reg_al);
974 				mem_writed(data + 0x01, dos.tables.dbcs); //used to be 0
975 				reg_cx = 5;
976 				CALLBACK_SCF(false);
977 				break;
978 			case 0x20: /* Capitalize Character */
979 				{
980 					int in  = reg_dl;
981 					int out = toupper(in);
982 					reg_dl  = (Bit8u)out;
983 				}
984 				CALLBACK_SCF(false);
985 				break;
986 			case 0x21: /* Capitalize String (cx=length) */
987 			case 0x22: /* Capatilize ASCIZ string */
988 				data = SegPhys(ds) + reg_dx;
989 				if(reg_al == 0x21) len = reg_cx;
990 				else len = mem_strlen(data); /* Is limited to 1024 */
991 
992 				if(len > DOS_COPYBUFSIZE - 1) E_Exit("DOS:0x65 Buffer overflow");
993 				if(len) {
994 					MEM_BlockRead(data,dos_copybuf,len);
995 					dos_copybuf[len] = 0;
996 					//No upcase as String(0x21) might be multiple asciz strings
997 					for (Bitu count = 0; count < len;count++)
998 						dos_copybuf[count] = (Bit8u)toupper(*reinterpret_cast<unsigned char*>(dos_copybuf+count));
999 					MEM_BlockWrite(data,dos_copybuf,len);
1000 				}
1001 				CALLBACK_SCF(false);
1002 				break;
1003 			default:
1004 				E_Exit("DOS:0x65:Unhandled country information call %2X",reg_al);
1005 			};
1006 			break;
1007 		}
1008 	case 0x66:					/* Get/Set global code page table  */
1009 		if (reg_al==1) {
1010 			LOG(LOG_DOSMISC,LOG_ERROR)("Getting global code page table");
1011 			reg_bx=reg_dx=dos.loaded_codepage;
1012 			CALLBACK_SCF(false);
1013 			break;
1014 		}
1015 		LOG(LOG_DOSMISC,LOG_NORMAL)("DOS:Setting code page table is not supported");
1016 		break;
1017 	case 0x67:					/* Set handle count */
1018 		/* Weird call to increase amount of file handles needs to allocate memory if >20 */
1019 		{
1020 			DOS_PSP psp(dos.psp());
1021 			psp.SetNumFiles(reg_bx);
1022 			CALLBACK_SCF(false);
1023 			break;
1024 		};
1025 	case 0x68:                  /* FFLUSH Commit file */
1026 		if(DOS_FlushFile(reg_bl)) {
1027 			CALLBACK_SCF(false);
1028 		} else {
1029 			reg_ax = dos.errorcode;
1030 			CALLBACK_SCF(true);
1031 		}
1032 		break;
1033 	case 0x69:					/* Get/Set disk serial number */
1034 		{
1035 			switch(reg_al)		{
1036 			case 0x00:				/* Get */
1037 				LOG(LOG_DOSMISC,LOG_ERROR)("DOS:Get Disk serial number");
1038 				CALLBACK_SCF(true);
1039 				break;
1040 			case 0x01:
1041 				LOG(LOG_DOSMISC,LOG_ERROR)("DOS:Set Disk serial number");
1042 			default:
1043 				E_Exit("DOS:Illegal Get Serial Number call %2X",reg_al);
1044 			}
1045 			break;
1046 		}
1047 	case 0x6c:					/* Extended Open/Create */
1048 		MEM_StrCopy(SegPhys(ds)+reg_si,name1,DOSNAMEBUF);
1049 		if (DOS_OpenFileExtended(name1,reg_bx,reg_cx,reg_dx,&reg_ax,&reg_cx)) {
1050 			CALLBACK_SCF(false);
1051 		} else {
1052 			reg_ax=dos.errorcode;
1053 			CALLBACK_SCF(true);
1054 		}
1055 		break;
1056 
1057 	case 0x71:					/* Unknown probably 4dos detection */
1058 		reg_ax=0x7100;
1059 		CALLBACK_SCF(true);
1060 		LOG(LOG_DOSMISC,LOG_NORMAL)("DOS:Windows long file name support call %2X",reg_al);
1061 		break;
1062 
1063 	case 0xE0:
1064 	case 0x18:	            	/* NULL Function for CP/M compatibility or Extended rename FCB */
1065 	case 0x1d:	            	/* NULL Function for CP/M compatibility or Extended rename FCB */
1066 	case 0x1e:	            	/* NULL Function for CP/M compatibility or Extended rename FCB */
1067 	case 0x20:	            	/* NULL Function for CP/M compatibility or Extended rename FCB */
1068 	case 0x6b:		            /* NULL Function */
1069 	case 0x61:		            /* UNUSED */
1070 	case 0xEF:                  /* Used in Ancient Art Of War CGA */
1071 	case 0x5e:					/* More Network Functions */
1072 	default:
1073 		LOG(LOG_DOSMISC,LOG_ERROR)("DOS:Unhandled call %02X al=%02X. Set al to default of 0",reg_ah,reg_al);
1074 		reg_al=0x00; /* default value */
1075 		break;
1076 	};
1077 	return CBRET_NONE;
1078 }
1079 
1080 
DOS_20Handler(void)1081 static Bitu DOS_20Handler(void) {
1082 	reg_ah=0x00;
1083 	DOS_21Handler();
1084 	return CBRET_NONE;
1085 }
1086 
DOS_27Handler(void)1087 static Bitu DOS_27Handler(void) {
1088 	// Terminate & stay resident
1089 	Bit16u para = (reg_dx/16)+((reg_dx % 16)>0);
1090 	Bit16u psp = dos.psp(); //mem_readw(SegPhys(ss)+reg_sp+2);
1091 	if (DOS_ResizeMemory(psp,&para)) DOS_Terminate(psp,true,0);
1092 	return CBRET_NONE;
1093 }
1094 
DOS_25Handler(void)1095 static Bitu DOS_25Handler(void) {
1096 	if(Drives[reg_al]==0){
1097 		reg_ax=0x8002;
1098 		SETFLAGBIT(CF,true);
1099 	}else{
1100 		SETFLAGBIT(CF,false);
1101 		if((reg_cx != 1) ||(reg_dx != 1))
1102 			LOG(LOG_DOSMISC,LOG_NORMAL)("int 25 called but not as diskdetection drive %X",reg_al);
1103 
1104 	   reg_ax=0;
1105 	}
1106     return CBRET_NONE;
1107 }
DOS_26Handler(void)1108 static Bitu DOS_26Handler(void) {
1109 	LOG(LOG_DOSMISC,LOG_NORMAL)("int 26 called: hope for the best!");
1110 	if(Drives[reg_al]==0){
1111 		reg_ax=0x8002;
1112 		SETFLAGBIT(CF,true);
1113 	}else{
1114 		SETFLAGBIT(CF,false);
1115 		reg_ax=0;
1116 	}
1117     return CBRET_NONE;
1118 }
1119 
1120 
1121 class DOS:public Module_base{
1122 private:
1123 	CALLBACK_HandlerObject callback[7];
1124 public:
DOS(Section * configuration)1125 	DOS(Section* configuration):Module_base(configuration){
1126 		callback[0].Install(DOS_20Handler,CB_IRET,"DOS Int 20");
1127 		callback[0].Set_RealVec(0x20);
1128 
1129 		callback[1].Install(DOS_21Handler,CB_INT21,"DOS Int 21");
1130 		callback[1].Set_RealVec(0x21);
1131 	//Pseudo code for int 21
1132 	// sti
1133 	// callback
1134 	// iret
1135 	// retf  <- int 21 4c jumps here to mimic a retf Cyber
1136 
1137 		callback[2].Install(DOS_25Handler,CB_RETF,"DOS Int 25");
1138 		callback[2].Set_RealVec(0x25);
1139 
1140 		callback[3].Install(DOS_26Handler,CB_RETF,"DOS Int 26");
1141 		callback[3].Set_RealVec(0x26);
1142 
1143 		callback[4].Install(DOS_27Handler,CB_IRET,"DOS Int 27");
1144 		callback[4].Set_RealVec(0x27);
1145 
1146 		callback[5].Install(NULL,CB_IRET,"DOS Int 28");
1147 		callback[5].Set_RealVec(0x28);
1148 
1149 		callback[6].Install(NULL,CB_INT29,"CON Output Int 29");
1150 		callback[6].Set_RealVec(0x29);
1151 		// pseudocode for CB_INT29:
1152 		//	push ax
1153 		//	mov ah, 0x0e
1154 		//	int 0x10
1155 		//	pop ax
1156 		//	iret
1157 
1158 		DOS_SetupFiles();								/* Setup system File tables */
1159 		DOS_SetupDevices();							/* Setup dos devices */
1160 		DOS_SetupTables();
1161 		DOS_SetupMemory();								/* Setup first MCB */
1162 		DOS_SetupPrograms();
1163 		DOS_SetupMisc();							/* Some additional dos interrupts */
1164 		DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).SetDrive(25); /* Else the next call gives a warning. */
1165 		DOS_SetDefaultDrive(25);
1166 
1167 		dos.version.major=5;
1168 		dos.version.minor=0;
1169 
1170 		/* Setup time and date */
1171 		time_t curtime;struct tm *loctime;
1172 		curtime = time (NULL);loctime = localtime (&curtime);
1173 
1174 		dos.date.day=(Bit8u)loctime->tm_mday;
1175 		dos.date.month=(Bit8u)loctime->tm_mon+1;
1176 		dos.date.year=(Bit16u)loctime->tm_year+1900;
1177 		Bit32u ticks=(Bit32u)((loctime->tm_hour*3600+loctime->tm_min*60+loctime->tm_sec)*(float)PIT_TICK_RATE/65536.0);
1178 		mem_writed(BIOS_TIMER,ticks);
1179 	}
~DOS()1180 	~DOS(){
1181 		for (Bit16u i=0;i<DOS_DRIVES;i++) delete Drives[i];
1182 	}
1183 };
1184 
1185 static DOS* test;
1186 
DOS_ShutDown(Section *)1187 void DOS_ShutDown(Section* /*sec*/) {
1188 	delete test;
1189 }
1190 
DOS_Init(Section * sec)1191 void DOS_Init(Section* sec) {
1192 	test = new DOS(sec);
1193 	/* shutdown function */
1194 	sec->AddDestroyFunction(&DOS_ShutDown,false);
1195 }
1196