1 /*
2 * Copyright (C) 2002-2015 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
20 #include "dosbox.h"
21 #include "callback.h"
22 #include "bios.h"
23 #include "bios_disk.h"
24 #include "regs.h"
25 #include "mem.h"
26 #include "dos_inc.h" /* for Drives[] */
27 #include "../dos/drives.h"
28 #include "mapper.h"
29
30 #define MAX_DISK_IMAGES 4
31
32 diskGeo DiskGeometryList[] = {
33 { 160, 8, 1, 40, 0},
34 { 180, 9, 1, 40, 0},
35 { 200, 10, 1, 40, 0},
36 { 320, 8, 2, 40, 1},
37 { 360, 9, 2, 40, 1},
38 { 400, 10, 2, 40, 1},
39 { 720, 9, 2, 80, 3},
40 {1200, 15, 2, 80, 2},
41 {1440, 18, 2, 80, 4},
42 {2880, 36, 2, 80, 6},
43 {0, 0, 0, 0, 0}
44 };
45
46 Bitu call_int13;
47 Bitu diskparm0, diskparm1;
48 static Bit8u last_status;
49 static Bit8u last_drive;
50 Bit16u imgDTASeg;
51 RealPt imgDTAPtr;
52 DOS_DTA *imgDTA;
53 bool killRead;
54 static bool swapping_requested;
55
56 void CMOS_SetRegister(Bitu regNr, Bit8u val); //For setting equipment word
57
58 /* 2 floppys and 2 harddrives, max */
59 imageDisk *imageDiskList[MAX_DISK_IMAGES];
60 imageDisk *diskSwap[MAX_SWAPPABLE_DISKS];
61 Bit32s swapPosition;
62
updateDPT(void)63 void updateDPT(void) {
64 Bit32u tmpheads, tmpcyl, tmpsect, tmpsize;
65 if(imageDiskList[2] != NULL) {
66 PhysPt dp0physaddr=CALLBACK_PhysPointer(diskparm0);
67 imageDiskList[2]->Get_Geometry(&tmpheads, &tmpcyl, &tmpsect, &tmpsize);
68 phys_writew(dp0physaddr,(Bit16u)tmpcyl);
69 phys_writeb(dp0physaddr+0x2,(Bit8u)tmpheads);
70 phys_writew(dp0physaddr+0x3,0);
71 phys_writew(dp0physaddr+0x5,(Bit16u)-1);
72 phys_writeb(dp0physaddr+0x7,0);
73 phys_writeb(dp0physaddr+0x8,(0xc0 | (((imageDiskList[2]->heads) > 8) << 3)));
74 phys_writeb(dp0physaddr+0x9,0);
75 phys_writeb(dp0physaddr+0xa,0);
76 phys_writeb(dp0physaddr+0xb,0);
77 phys_writew(dp0physaddr+0xc,(Bit16u)tmpcyl);
78 phys_writeb(dp0physaddr+0xe,(Bit8u)tmpsect);
79 }
80 if(imageDiskList[3] != NULL) {
81 PhysPt dp1physaddr=CALLBACK_PhysPointer(diskparm1);
82 imageDiskList[3]->Get_Geometry(&tmpheads, &tmpcyl, &tmpsect, &tmpsize);
83 phys_writew(dp1physaddr,(Bit16u)tmpcyl);
84 phys_writeb(dp1physaddr+0x2,(Bit8u)tmpheads);
85 phys_writeb(dp1physaddr+0xe,(Bit8u)tmpsect);
86 }
87 }
88
incrementFDD(void)89 void incrementFDD(void) {
90 Bit16u equipment=mem_readw(BIOS_CONFIGURATION);
91 if(equipment&1) {
92 Bitu numofdisks = (equipment>>6)&3;
93 numofdisks++;
94 if(numofdisks > 1) numofdisks=1;//max 2 floppies at the moment
95 equipment&=~0x00C0;
96 equipment|=(numofdisks<<6);
97 } else equipment|=1;
98 mem_writew(BIOS_CONFIGURATION,equipment);
99 CMOS_SetRegister(0x14, (Bit8u)(equipment&0xff));
100 }
101
swapInDisks(void)102 void swapInDisks(void) {
103 bool allNull = true;
104 Bit32s diskcount = 0;
105 Bit32s swapPos = swapPosition;
106 Bit32s i;
107
108 /* Check to make sure that there is at least one setup image */
109 for(i=0;i<MAX_SWAPPABLE_DISKS;i++) {
110 if(diskSwap[i]!=NULL) {
111 allNull = false;
112 break;
113 }
114 }
115
116 /* No disks setup... fail */
117 if (allNull) return;
118
119 /* If only one disk is loaded, this loop will load the same disk in dive A and drive B */
120 while(diskcount<2) {
121 if(diskSwap[swapPos] != NULL) {
122 LOG_MSG("Loaded disk %d from swaplist position %d - \"%s\"", diskcount, swapPos, diskSwap[swapPos]->diskname);
123 imageDiskList[diskcount] = diskSwap[swapPos];
124 diskcount++;
125 }
126 swapPos++;
127 if(swapPos>=MAX_SWAPPABLE_DISKS) swapPos=0;
128 }
129 }
130
getSwapRequest(void)131 bool getSwapRequest(void) {
132 bool sreq=swapping_requested;
133 swapping_requested = false;
134 return sreq;
135 }
136
swapInNextDisk(bool pressed)137 void swapInNextDisk(bool pressed) {
138 if (!pressed)
139 return;
140 DriveManager::CycleAllDisks();
141 /* Hack/feature: rescan all disks as well */
142 LOG_MSG("Diskcaching reset for normal mounted drives.");
143 for(Bitu i=0;i<DOS_DRIVES;i++) {
144 if (Drives[i]) Drives[i]->EmptyCache();
145 }
146 swapPosition++;
147 if(diskSwap[swapPosition] == NULL) swapPosition = 0;
148 swapInDisks();
149 swapping_requested = true;
150 }
151
152
Read_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,void * data)153 Bit8u imageDisk::Read_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,void * data) {
154 Bit32u sectnum;
155
156 sectnum = ( (cylinder * heads + head) * sectors ) + sector - 1L;
157
158 return Read_AbsoluteSector(sectnum, data);
159 }
160
Read_AbsoluteSector(Bit32u sectnum,void * data)161 Bit8u imageDisk::Read_AbsoluteSector(Bit32u sectnum, void * data) {
162 Bit32u bytenum;
163
164 bytenum = sectnum * sector_size;
165
166 if (last_action==WRITE || bytenum!=current_fpos) fseek(diskimg,bytenum,SEEK_SET);
167 size_t ret=fread(data, 1, sector_size, diskimg);
168 current_fpos=bytenum+ret;
169 last_action=READ;
170
171 return 0x00;
172 }
173
Write_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,void * data)174 Bit8u imageDisk::Write_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,void * data) {
175 Bit32u sectnum;
176
177 sectnum = ( (cylinder * heads + head) * sectors ) + sector - 1L;
178
179 return Write_AbsoluteSector(sectnum, data);
180 }
181
182
Write_AbsoluteSector(Bit32u sectnum,void * data)183 Bit8u imageDisk::Write_AbsoluteSector(Bit32u sectnum, void *data) {
184 Bit32u bytenum;
185
186 bytenum = sectnum * sector_size;
187
188 //LOG_MSG("Writing sectors to %ld at bytenum %d", sectnum, bytenum);
189
190 if (last_action==READ || bytenum!=current_fpos) fseek(diskimg,bytenum,SEEK_SET);
191 size_t ret=fwrite(data, 1, sector_size, diskimg);
192 current_fpos=bytenum+ret;
193 last_action=WRITE;
194
195 return ((ret>0)?0x00:0x05);
196
197 }
198
imageDisk(FILE * imgFile,Bit8u * imgName,Bit32u imgSizeK,bool isHardDisk)199 imageDisk::imageDisk(FILE *imgFile, Bit8u *imgName, Bit32u imgSizeK, bool isHardDisk) {
200 heads = 0;
201 cylinders = 0;
202 sectors = 0;
203 sector_size = 512;
204 current_fpos = 0;
205 last_action = NONE;
206 diskimg = imgFile;
207 fseek(diskimg,0,SEEK_SET);
208
209 memset(diskname,0,512);
210 if(strlen((const char *)imgName) > 511) {
211 memcpy(diskname, imgName, 511);
212 } else {
213 strcpy((char *)diskname, (const char *)imgName);
214 }
215
216 active = false;
217 hardDrive = isHardDisk;
218 if(!isHardDisk) {
219 Bit8u i=0;
220 bool founddisk = false;
221 while (DiskGeometryList[i].ksize!=0x0) {
222 if ((DiskGeometryList[i].ksize==imgSizeK) ||
223 (DiskGeometryList[i].ksize+1==imgSizeK)) {
224 if (DiskGeometryList[i].ksize!=imgSizeK)
225 LOG_MSG("ImageLoader: image file with additional data, might not load!");
226 founddisk = true;
227 active = true;
228 floppytype = i;
229 heads = DiskGeometryList[i].headscyl;
230 cylinders = DiskGeometryList[i].cylcount;
231 sectors = DiskGeometryList[i].secttrack;
232 break;
233 }
234 i++;
235 }
236 if(!founddisk) {
237 active = false;
238 } else {
239 incrementFDD();
240 }
241 }
242 }
243
Set_Geometry(Bit32u setHeads,Bit32u setCyl,Bit32u setSect,Bit32u setSectSize)244 void imageDisk::Set_Geometry(Bit32u setHeads, Bit32u setCyl, Bit32u setSect, Bit32u setSectSize) {
245 heads = setHeads;
246 cylinders = setCyl;
247 sectors = setSect;
248 sector_size = setSectSize;
249 active = true;
250 }
251
Get_Geometry(Bit32u * getHeads,Bit32u * getCyl,Bit32u * getSect,Bit32u * getSectSize)252 void imageDisk::Get_Geometry(Bit32u * getHeads, Bit32u *getCyl, Bit32u *getSect, Bit32u *getSectSize) {
253 *getHeads = heads;
254 *getCyl = cylinders;
255 *getSect = sectors;
256 *getSectSize = sector_size;
257 }
258
GetBiosType(void)259 Bit8u imageDisk::GetBiosType(void) {
260 if(!hardDrive) {
261 return (Bit8u)DiskGeometryList[floppytype].biosval;
262 } else return 0;
263 }
264
getSectSize(void)265 Bit32u imageDisk::getSectSize(void) {
266 return sector_size;
267 }
268
GetDosDriveNumber(Bit8u biosNum)269 static Bit8u GetDosDriveNumber(Bit8u biosNum) {
270 switch(biosNum) {
271 case 0x0:
272 return 0x0;
273 case 0x1:
274 return 0x1;
275 case 0x80:
276 return 0x2;
277 case 0x81:
278 return 0x3;
279 case 0x82:
280 return 0x4;
281 case 0x83:
282 return 0x5;
283 default:
284 return 0x7f;
285 }
286 }
287
driveInactive(Bit8u driveNum)288 static bool driveInactive(Bit8u driveNum) {
289 if(driveNum>=(2 + MAX_HDD_IMAGES)) {
290 LOG(LOG_BIOS,LOG_ERROR)("Disk %d non-existant", driveNum);
291 last_status = 0x01;
292 CALLBACK_SCF(true);
293 return true;
294 }
295 if(imageDiskList[driveNum] == NULL) {
296 LOG(LOG_BIOS,LOG_ERROR)("Disk %d not active", driveNum);
297 last_status = 0x01;
298 CALLBACK_SCF(true);
299 return true;
300 }
301 if(!imageDiskList[driveNum]->active) {
302 LOG(LOG_BIOS,LOG_ERROR)("Disk %d not active", driveNum);
303 last_status = 0x01;
304 CALLBACK_SCF(true);
305 return true;
306 }
307 return false;
308 }
309
310
INT13_DiskHandler(void)311 static Bitu INT13_DiskHandler(void) {
312 Bit16u segat, bufptr;
313 Bit8u sectbuf[512];
314 Bit8u drivenum;
315 Bitu i,t;
316 last_drive = reg_dl;
317 drivenum = GetDosDriveNumber(reg_dl);
318 bool any_images = false;
319 for(i = 0;i < MAX_DISK_IMAGES;i++) {
320 if(imageDiskList[i]) any_images=true;
321 }
322
323 // unconditionally enable the interrupt flag
324 CALLBACK_SIF(true);
325
326 //drivenum = 0;
327 //LOG_MSG("INT13: Function %x called on drive %x (dos drive %d)", reg_ah, reg_dl, drivenum);
328 switch(reg_ah) {
329 case 0x0: /* Reset disk */
330 {
331 /* if there aren't any diskimages (so only localdrives and virtual drives)
332 * always succeed on reset disk. If there are diskimages then and only then
333 * do real checks
334 */
335 if (any_images && driveInactive(drivenum)) {
336 /* driveInactive sets carry flag if the specified drive is not available */
337 if ((machine==MCH_CGA) || (machine==MCH_PCJR)) {
338 /* those bioses call floppy drive reset for invalid drive values */
339 if (((imageDiskList[0]) && (imageDiskList[0]->active)) || ((imageDiskList[1]) && (imageDiskList[1]->active))) {
340 if (machine!=MCH_PCJR && reg_dl<0x80) reg_ip++;
341 last_status = 0x00;
342 CALLBACK_SCF(false);
343 }
344 }
345 return CBRET_NONE;
346 }
347 if (machine!=MCH_PCJR && reg_dl<0x80) reg_ip++;
348 last_status = 0x00;
349 CALLBACK_SCF(false);
350 }
351 break;
352 case 0x1: /* Get status of last operation */
353
354 if(last_status != 0x00) {
355 reg_ah = last_status;
356 CALLBACK_SCF(true);
357 } else {
358 reg_ah = 0x00;
359 CALLBACK_SCF(false);
360 }
361 break;
362 case 0x2: /* Read sectors */
363 if (reg_al==0) {
364 reg_ah = 0x01;
365 CALLBACK_SCF(true);
366 return CBRET_NONE;
367 }
368 if (!any_images) {
369 // Inherit the Earth cdrom (uses it as disk test)
370 if (((reg_dl&0x80)==0x80) && (reg_dh==0) && ((reg_cl&0x3f)==1)) {
371 reg_ah = 0;
372 CALLBACK_SCF(false);
373 return CBRET_NONE;
374 }
375 }
376 if (driveInactive(drivenum)) {
377 reg_ah = 0xff;
378 CALLBACK_SCF(true);
379 return CBRET_NONE;
380 }
381
382 segat = SegValue(es);
383 bufptr = reg_bx;
384 for(i=0;i<reg_al;i++) {
385 last_status = imageDiskList[drivenum]->Read_Sector((Bit32u)reg_dh, (Bit32u)(reg_ch | ((reg_cl & 0xc0)<< 2)), (Bit32u)((reg_cl & 63)+i), sectbuf);
386 if((last_status != 0x00) || (killRead)) {
387 LOG_MSG("Error in disk read");
388 killRead = false;
389 reg_ah = 0x04;
390 CALLBACK_SCF(true);
391 return CBRET_NONE;
392 }
393 for(t=0;t<512;t++) {
394 real_writeb(segat,bufptr,sectbuf[t]);
395 bufptr++;
396 }
397 }
398 reg_ah = 0x00;
399 CALLBACK_SCF(false);
400 break;
401 case 0x3: /* Write sectors */
402
403 if(driveInactive(drivenum)) {
404 reg_ah = 0xff;
405 CALLBACK_SCF(true);
406 return CBRET_NONE;
407 }
408
409
410 bufptr = reg_bx;
411 for(i=0;i<reg_al;i++) {
412 for(t=0;t<imageDiskList[drivenum]->getSectSize();t++) {
413 sectbuf[t] = real_readb(SegValue(es),bufptr);
414 bufptr++;
415 }
416
417 last_status = imageDiskList[drivenum]->Write_Sector((Bit32u)reg_dh, (Bit32u)(reg_ch | ((reg_cl & 0xc0) << 2)), (Bit32u)((reg_cl & 63) + i), §buf[0]);
418 if(last_status != 0x00) {
419 CALLBACK_SCF(true);
420 return CBRET_NONE;
421 }
422 }
423 reg_ah = 0x00;
424 CALLBACK_SCF(false);
425 break;
426 case 0x04: /* Verify sectors */
427 if (reg_al==0) {
428 reg_ah = 0x01;
429 CALLBACK_SCF(true);
430 return CBRET_NONE;
431 }
432 if(driveInactive(drivenum)) return CBRET_NONE;
433
434 /* TODO: Finish coding this section */
435 /*
436 segat = SegValue(es);
437 bufptr = reg_bx;
438 for(i=0;i<reg_al;i++) {
439 last_status = imageDiskList[drivenum]->Read_Sector((Bit32u)reg_dh, (Bit32u)(reg_ch | ((reg_cl & 0xc0)<< 2)), (Bit32u)((reg_cl & 63)+i), sectbuf);
440 if(last_status != 0x00) {
441 LOG_MSG("Error in disk read");
442 CALLBACK_SCF(true);
443 return CBRET_NONE;
444 }
445 for(t=0;t<512;t++) {
446 real_writeb(segat,bufptr,sectbuf[t]);
447 bufptr++;
448 }
449 }*/
450 reg_ah = 0x00;
451 //Qbix: The following codes don't match my specs. al should be number of sector verified
452 //reg_al = 0x10; /* CRC verify failed */
453 //reg_al = 0x00; /* CRC verify succeeded */
454 CALLBACK_SCF(false);
455
456 break;
457 case 0x05: /* Format track */
458 if (driveInactive(drivenum)) {
459 reg_ah = 0xff;
460 CALLBACK_SCF(true);
461 return CBRET_NONE;
462 }
463 reg_ah = 0x00;
464 CALLBACK_SCF(false);
465 break;
466 case 0x08: /* Get drive parameters */
467 if(driveInactive(drivenum)) {
468 last_status = 0x07;
469 reg_ah = last_status;
470 CALLBACK_SCF(true);
471 return CBRET_NONE;
472 }
473 reg_ax = 0x00;
474 reg_bl = imageDiskList[drivenum]->GetBiosType();
475 Bit32u tmpheads, tmpcyl, tmpsect, tmpsize;
476 imageDiskList[drivenum]->Get_Geometry(&tmpheads, &tmpcyl, &tmpsect, &tmpsize);
477 if (tmpcyl==0) LOG(LOG_BIOS,LOG_ERROR)("INT13 DrivParm: cylinder count zero!");
478 else tmpcyl--; // cylinder count -> max cylinder
479 if (tmpheads==0) LOG(LOG_BIOS,LOG_ERROR)("INT13 DrivParm: head count zero!");
480 else tmpheads--; // head count -> max head
481 reg_ch = (Bit8u)(tmpcyl & 0xff);
482 reg_cl = (Bit8u)(((tmpcyl >> 2) & 0xc0) | (tmpsect & 0x3f));
483 reg_dh = (Bit8u)tmpheads;
484 last_status = 0x00;
485 if (reg_dl&0x80) { // harddisks
486 reg_dl = 0;
487 if(imageDiskList[2] != NULL) reg_dl++;
488 if(imageDiskList[3] != NULL) reg_dl++;
489 } else { // floppy disks
490 reg_dl = 0;
491 if(imageDiskList[0] != NULL) reg_dl++;
492 if(imageDiskList[1] != NULL) reg_dl++;
493 }
494 CALLBACK_SCF(false);
495 break;
496 case 0x11: /* Recalibrate drive */
497 reg_ah = 0x00;
498 CALLBACK_SCF(false);
499 break;
500 case 0x17: /* Set disk type for format */
501 /* Pirates! needs this to load */
502 killRead = true;
503 reg_ah = 0x00;
504 CALLBACK_SCF(false);
505 break;
506 default:
507 LOG(LOG_BIOS,LOG_ERROR)("INT13: Function %x called on drive %x (dos drive %d)", reg_ah, reg_dl, drivenum);
508 reg_ah=0xff;
509 CALLBACK_SCF(true);
510 }
511 return CBRET_NONE;
512 }
513
514
BIOS_SetupDisks(void)515 void BIOS_SetupDisks(void) {
516 /* TODO Start the time correctly */
517 call_int13=CALLBACK_Allocate();
518 CALLBACK_Setup(call_int13,&INT13_DiskHandler,CB_INT13,"Int 13 Bios disk");
519 RealSetVec(0x13,CALLBACK_RealPointer(call_int13));
520 int i;
521 for(i=0;i<4;i++) {
522 imageDiskList[i] = NULL;
523 }
524
525 for(i=0;i<MAX_SWAPPABLE_DISKS;i++) {
526 diskSwap[i] = NULL;
527 }
528
529 diskparm0 = CALLBACK_Allocate();
530 diskparm1 = CALLBACK_Allocate();
531 swapPosition = 0;
532
533 RealSetVec(0x41,CALLBACK_RealPointer(diskparm0));
534 RealSetVec(0x46,CALLBACK_RealPointer(diskparm1));
535
536 PhysPt dp0physaddr=CALLBACK_PhysPointer(diskparm0);
537 PhysPt dp1physaddr=CALLBACK_PhysPointer(diskparm1);
538 for(i=0;i<16;i++) {
539 phys_writeb(dp0physaddr+i,0);
540 phys_writeb(dp1physaddr+i,0);
541 }
542
543 imgDTASeg = 0;
544
545 /* Setup the Bios Area */
546 mem_writeb(BIOS_HARDDISK_COUNT,2);
547
548 MAPPER_AddHandler(swapInNextDisk,MK_f4,MMOD1,"swapimg","Swap Image");
549 killRead = false;
550 swapping_requested = false;
551 }
552