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), &sectbuf[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