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(®_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,®_cx,®_al,®_dx)) reg_al=0xff;
314 break;
315 case 0x1c: /* Get allocation info for specific drive */
316 if (!DOS_GetAllocationInfo(reg_dl,®_cx,®_al,®_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(),®_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,§ors,&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,®_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,®_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,®_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,®_cx,®_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,®_ax,®_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,¶)) 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