1 /*
2  *  Copyright (C) 2002-2021  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 along
15  *  with this program; if not, write to the Free Software Foundation, Inc.,
16  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18 
19 #include "bios_disk.h"
20 
21 #include <algorithm>
22 #include <cassert>
23 #include <utility>
24 
25 #include "callback.h"
26 #include "regs.h"
27 #include "mem.h"
28 #include "dos_inc.h" /* for Drives[] */
29 #include "drives.h"
30 #include "mapper.h"
31 #include "string_utils.h"
32 
33 diskGeo DiskGeometryList[] = {
34 	{ 160,  8, 1, 40, 0},	// SS/DD 5.25"
35 	{ 180,  9, 1, 40, 0},	// SS/DD 5.25"
36 	{ 200, 10, 1, 40, 0},	// SS/DD 5.25" (booters)
37 	{ 320,  8, 2, 40, 1},	// DS/DD 5.25"
38 	{ 360,  9, 2, 40, 1},	// DS/DD 5.25"
39 	{ 400, 10, 2, 40, 1},	// DS/DD 5.25" (booters)
40 	{ 720,  9, 2, 80, 3},	// DS/DD 3.5"
41 	{1200, 15, 2, 80, 2},	// DS/HD 5.25"
42 	{1440, 18, 2, 80, 4},	// DS/HD 3.5"
43 	{1680, 21, 2, 80, 4},	// DS/HD 3.5"  (DMF)
44 	{2880, 36, 2, 80, 6},	// DS/ED 3.5"
45 	{0, 0, 0, 0, 0}
46 };
47 
48 Bitu call_int13;
49 Bitu diskparm0, diskparm1;
50 static Bit8u last_status;
51 static Bit8u last_drive;
52 Bit16u imgDTASeg;
53 RealPt imgDTAPtr;
54 DOS_DTA *imgDTA;
55 bool killRead;
56 static bool swapping_requested;
57 
58 void BIOS_SetEquipment(Bit16u equipment);
59 
60 /* 2 floppys and 2 harddrives, max */
61 std::array<std::shared_ptr<imageDisk>, MAX_DISK_IMAGES> imageDiskList;
62 std::array<std::shared_ptr<imageDisk>, MAX_SWAPPABLE_DISKS> diskSwap;
63 
64 unsigned int swapPosition;
65 
updateDPT(void)66 void updateDPT(void) {
67 	Bit32u tmpheads, tmpcyl, tmpsect, tmpsize;
68 	if(imageDiskList[2]) {
69 		PhysPt dp0physaddr=CALLBACK_PhysPointer(diskparm0);
70 		imageDiskList[2]->Get_Geometry(&tmpheads, &tmpcyl, &tmpsect, &tmpsize);
71 		phys_writew(dp0physaddr,(Bit16u)tmpcyl);
72 		phys_writeb(dp0physaddr+0x2,(Bit8u)tmpheads);
73 		phys_writew(dp0physaddr+0x3,0);
74 		phys_writew(dp0physaddr+0x5,(Bit16u)-1);
75 		phys_writeb(dp0physaddr+0x7,0);
76 		phys_writeb(dp0physaddr+0x8,(0xc0 | (((imageDiskList[2]->heads) > 8) << 3)));
77 		phys_writeb(dp0physaddr+0x9,0);
78 		phys_writeb(dp0physaddr+0xa,0);
79 		phys_writeb(dp0physaddr+0xb,0);
80 		phys_writew(dp0physaddr+0xc,(Bit16u)tmpcyl);
81 		phys_writeb(dp0physaddr+0xe,(Bit8u)tmpsect);
82 	}
83 	if(imageDiskList[3]) {
84 		PhysPt dp1physaddr=CALLBACK_PhysPointer(diskparm1);
85 		imageDiskList[3]->Get_Geometry(&tmpheads, &tmpcyl, &tmpsect, &tmpsize);
86 		phys_writew(dp1physaddr,(Bit16u)tmpcyl);
87 		phys_writeb(dp1physaddr+0x2,(Bit8u)tmpheads);
88 		phys_writeb(dp1physaddr+0xe,(Bit8u)tmpsect);
89 	}
90 }
91 
incrementFDD(void)92 void incrementFDD(void) {
93 	Bit16u equipment=mem_readw(BIOS_CONFIGURATION);
94 	if(equipment&1) {
95 		Bitu numofdisks = (equipment>>6)&3;
96 		numofdisks++;
97 		if(numofdisks > 1) numofdisks=1;//max 2 floppies at the moment
98 		equipment&=~0x00C0;
99 		equipment|=(numofdisks<<6);
100 	} else equipment|=1;
101 	BIOS_SetEquipment(equipment);
102 }
103 
104 template<typename T, size_t N>
disk_array_prefix_size(const std::array<T,N> & images)105 static size_t disk_array_prefix_size(const std::array<T, N> &images) {
106 	size_t num = 0;
107 	for (const auto &disk : images) {
108 		if (!disk)
109 			break;
110 		num++;
111 	}
112 	return num;
113 }
114 
swapInDisks(unsigned int swap_position)115 void swapInDisks(unsigned int swap_position) {
116 	const size_t boot_disks_num = disk_array_prefix_size(diskSwap);
117 	if (boot_disks_num == 0)
118 		return;
119 
120 	assert(swap_position < boot_disks_num);
121 	const unsigned int pos_1 = swap_position;
122 	const unsigned int pos_2 = (swap_position + 1) % boot_disks_num;
123 
124 	imageDiskList[0] = diskSwap[pos_1];
125 	LOG_MSG("Loaded disk A from swaplist position %u - \"%s\"",
126 	        pos_1, diskSwap[pos_1]->diskname);
127 
128 	imageDiskList[1] = diskSwap[pos_2];
129 	LOG_MSG("Loaded disk B from swaplist position %u - \"%s\"",
130 	        pos_2, diskSwap[pos_2]->diskname);
131 }
132 
getSwapRequest(void)133 bool getSwapRequest(void) {
134 	bool sreq=swapping_requested;
135 	swapping_requested = false;
136 	return sreq;
137 }
138 
swapInNextDisk(bool pressed)139 void swapInNextDisk(bool pressed) {
140 	if (!pressed)
141 		return;
142 	DriveManager::CycleAllDisks();
143 	/* Hack/feature: rescan all disks as well */
144 	LOG_MSG("Diskcaching reset for normal mounted drives.");
145 	for(Bitu i=0;i<DOS_DRIVES;i++) {
146 		if (Drives[i]) Drives[i]->EmptyCache();
147 	}
148 	swapPosition++;
149 	if (!diskSwap[swapPosition]) {
150 		swapPosition = 0;
151 	}
152 	swapInDisks(swapPosition);
153 	swapping_requested = true;
154 }
155 
156 
Read_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,void * data)157 Bit8u imageDisk::Read_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,void * data) {
158 	Bit32u sectnum;
159 
160 	sectnum = ( (cylinder * heads + head) * sectors ) + sector - 1L;
161 
162 	return Read_AbsoluteSector(sectnum, data);
163 }
164 
Read_AbsoluteSector(Bit32u sectnum,void * data)165 Bit8u imageDisk::Read_AbsoluteSector(Bit32u sectnum, void * data) {
166 	Bit32u bytenum;
167 
168 	bytenum = sectnum * sector_size;
169 
170 	if (last_action==WRITE || bytenum!=current_fpos) fseek(diskimg,bytenum,SEEK_SET);
171 	size_t ret=fread(data, 1, sector_size, diskimg);
172 	current_fpos=bytenum+ret;
173 	last_action=READ;
174 
175 	return 0x00;
176 }
177 
Write_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,void * data)178 Bit8u imageDisk::Write_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,void * data) {
179 	Bit32u sectnum;
180 
181 	sectnum = ( (cylinder * heads + head) * sectors ) + sector - 1L;
182 
183 	return Write_AbsoluteSector(sectnum, data);
184 }
185 
186 
Write_AbsoluteSector(Bit32u sectnum,void * data)187 Bit8u imageDisk::Write_AbsoluteSector(Bit32u sectnum, void *data) {
188 	Bit32u bytenum;
189 
190 	bytenum = sectnum * sector_size;
191 
192 	//LOG_MSG("Writing sectors to %ld at bytenum %d", sectnum, bytenum);
193 
194 	if (last_action==READ || bytenum!=current_fpos) fseek(diskimg,bytenum,SEEK_SET);
195 	size_t ret=fwrite(data, 1, sector_size, diskimg);
196 	current_fpos=bytenum+ret;
197 	last_action=WRITE;
198 
199 	return ((ret>0)?0x00:0x05);
200 
201 }
202 
imageDisk(FILE * img_file,const char * img_name,uint32_t img_size_k,bool is_hdd)203 imageDisk::imageDisk(FILE *img_file, const char *img_name, uint32_t img_size_k, bool is_hdd)
204         : hardDrive(is_hdd),
205           active(false),
206           diskimg(img_file),
207           floppytype(0),
208           sector_size(512),
209           heads(0),
210           cylinders(0),
211           sectors(0),
212           current_fpos(0),
213           last_action(NONE)
214 {
215 	fseek(diskimg,0,SEEK_SET);
216 	memset(diskname,0,512);
217 	safe_strcpy(diskname, img_name);
218 	if (!is_hdd) {
219 		Bit8u i=0;
220 		bool founddisk = false;
221 		while (DiskGeometryList[i].ksize!=0x0) {
222 			if ((DiskGeometryList[i].ksize == img_size_k) ||
223 			    (DiskGeometryList[i].ksize + 1 == img_size_k)) {
224 				if (DiskGeometryList[i].ksize != img_size_k)
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-existent", driveNum);
291 		last_status = 0x01;
292 		CALLBACK_SCF(true);
293 		return true;
294 	}
295 	if(!imageDiskList[driveNum]) {
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 template<typename T, size_t N>
has_image(const std::array<T,N> & arr)311 static bool has_image(const std::array<T, N> &arr) {
312 	auto to_bool = [](const T &x) { return bool(x); };
313 	return std::any_of(std::begin(arr), std::end(arr), to_bool);
314 }
315 
INT13_DiskHandler(void)316 static Bitu INT13_DiskHandler(void) {
317 	Bit16u segat, bufptr;
318 	Bit8u sectbuf[512];
319 	Bit8u  drivenum;
320 	Bitu t;
321 	last_drive = reg_dl;
322 	drivenum = GetDosDriveNumber(reg_dl);
323 	const bool any_images = has_image(imageDiskList);
324 
325 	// unconditionally enable the interrupt flag
326 	CALLBACK_SIF(true);
327 
328 	//drivenum = 0;
329 	//LOG_MSG("INT13: Function %x called on drive %x (dos drive %d)", reg_ah,  reg_dl, drivenum);
330 
331 	// NOTE: the 0xff error code returned in some cases is questionable; 0x01 seems more correct
332 	switch(reg_ah) {
333 	case 0x0: /* Reset disk */
334 		{
335 			/* if there aren't any diskimages (so only localdrives and virtual drives)
336 			 * always succeed on reset disk. If there are diskimages then and only then
337 			 * do real checks
338 			 */
339 			if (any_images && driveInactive(drivenum)) {
340 				/* driveInactive sets carry flag if the specified drive is not available */
341 				if ((machine==MCH_CGA) || (machine==MCH_PCJR)) {
342 					/* those bioses call floppy drive reset for invalid drive values */
343 					if (((imageDiskList[0]) && (imageDiskList[0]->active)) || ((imageDiskList[1]) && (imageDiskList[1]->active))) {
344 						if (machine!=MCH_PCJR && reg_dl<0x80) reg_ip++;
345 						last_status = 0x00;
346 						CALLBACK_SCF(false);
347 					}
348 				}
349 				return CBRET_NONE;
350 			}
351 			if (machine!=MCH_PCJR && reg_dl<0x80) reg_ip++;
352 			last_status = 0x00;
353 			CALLBACK_SCF(false);
354 		}
355         break;
356 	case 0x1: /* Get status of last operation */
357 
358 		if(last_status != 0x00) {
359 			reg_ah = last_status;
360 			CALLBACK_SCF(true);
361 		} else {
362 			reg_ah = 0x00;
363 			CALLBACK_SCF(false);
364 		}
365 		break;
366 	case 0x2: /* Read sectors */
367 		if (reg_al==0) {
368 			reg_ah = 0x01;
369 			CALLBACK_SCF(true);
370 			return CBRET_NONE;
371 		}
372 		if (!any_images) {
373 			if (drivenum >= DOS_DRIVES || !Drives[drivenum] || Drives[drivenum]->isRemovable()) {
374 				reg_ah = 0x01;
375 				CALLBACK_SCF(true);
376 				return CBRET_NONE;
377 			}
378 			// Inherit the Earth cdrom and Amberstar use it as a disk test
379 			if (((reg_dl&0x80)==0x80) && (reg_dh==0) && ((reg_cl&0x3f)==1)) {
380 				if (reg_ch==0) {
381 					PhysPt ptr = PhysMake(SegValue(es),reg_bx);
382 					// write some MBR data into buffer for Amberstar installer
383 					mem_writeb(ptr+0x1be,0x80); // first partition is active
384 					mem_writeb(ptr+0x1c2,0x06); // first partition is FAT16B
385 				}
386 				reg_ah = 0;
387 				CALLBACK_SCF(false);
388 				return CBRET_NONE;
389 			}
390 		}
391 		if (driveInactive(drivenum)) {
392 			reg_ah = 0xff;
393 			CALLBACK_SCF(true);
394 			return CBRET_NONE;
395 		}
396 
397 		segat = SegValue(es);
398 		bufptr = reg_bx;
399 		for (Bitu i = 0; i < reg_al; i++) {
400 			last_status = imageDiskList[drivenum]->Read_Sector((Bit32u)reg_dh, (Bit32u)(reg_ch | ((reg_cl & 0xc0)<< 2)), (Bit32u)((reg_cl & 63)+i), sectbuf);
401 			if((last_status != 0x00) || (killRead)) {
402 				LOG_MSG("Error in disk read");
403 				killRead = false;
404 				reg_ah = 0x04;
405 				CALLBACK_SCF(true);
406 				return CBRET_NONE;
407 			}
408 			for(t=0;t<512;t++) {
409 				real_writeb(segat,bufptr,sectbuf[t]);
410 				bufptr++;
411 			}
412 		}
413 		reg_ah = 0x00;
414 		CALLBACK_SCF(false);
415 		break;
416 	case 0x3: /* Write sectors */
417 		if(driveInactive(drivenum)) {
418 			reg_ah = 0xff;
419 			CALLBACK_SCF(true);
420 			return CBRET_NONE;
421 		}
422 		bufptr = reg_bx;
423 		for (Bitu i = 0; i < reg_al; i++) {
424 			for(t=0;t<imageDiskList[drivenum]->getSectSize();t++) {
425 				sectbuf[t] = real_readb(SegValue(es),bufptr);
426 				bufptr++;
427 			}
428 			last_status = imageDiskList[drivenum]->Write_Sector((Bit32u)reg_dh, (Bit32u)(reg_ch | ((reg_cl & 0xc0) << 2)), (Bit32u)((reg_cl & 63) + i), &sectbuf[0]);
429 			if(last_status != 0x00) {
430 				CALLBACK_SCF(true);
431 				return CBRET_NONE;
432 			}
433 		}
434 		reg_ah = 0x00;
435 		CALLBACK_SCF(false);
436 		break;
437 	case 0x04: /* Verify sectors */
438 		if (reg_al==0) {
439 			reg_ah = 0x01;
440 			CALLBACK_SCF(true);
441 			return CBRET_NONE;
442 		}
443 		if(driveInactive(drivenum)) {
444 			reg_ah = last_status;
445 			return CBRET_NONE;
446 		}
447 
448 		/* TODO: Finish coding this section */
449 		/*
450 		segat = SegValue(es);
451 		bufptr = reg_bx;
452 		for(i=0;i<reg_al;i++) {
453 			last_status = imageDiskList[drivenum]->Read_Sector((Bit32u)reg_dh, (Bit32u)(reg_ch | ((reg_cl & 0xc0)<< 2)), (Bit32u)((reg_cl & 63)+i), sectbuf);
454 			if(last_status != 0x00) {
455 				LOG_MSG("Error in disk read");
456 				CALLBACK_SCF(true);
457 				return CBRET_NONE;
458 			}
459 			for(t=0;t<512;t++) {
460 				real_writeb(segat,bufptr,sectbuf[t]);
461 				bufptr++;
462 			}
463 		}*/
464 		reg_ah = 0x00;
465 		//Qbix: The following codes don't match my specs. al should be number of sector verified
466 		//reg_al = 0x10; /* CRC verify failed */
467 		//reg_al = 0x00; /* CRC verify succeeded */
468 		CALLBACK_SCF(false);
469 
470 		break;
471 	case 0x05: /* Format track */
472 		if (driveInactive(drivenum)) {
473 			reg_ah = 0xff;
474 			CALLBACK_SCF(true);
475 			return CBRET_NONE;
476 		}
477 		reg_ah = 0x00;
478 		CALLBACK_SCF(false);
479 		break;
480 	case 0x08: /* Get drive parameters */
481 		if(driveInactive(drivenum)) {
482 			last_status = 0x07;
483 			reg_ah = last_status;
484 			CALLBACK_SCF(true);
485 			return CBRET_NONE;
486 		}
487 		reg_ax = 0x00;
488 		reg_bl = imageDiskList[drivenum]->GetBiosType();
489 		Bit32u tmpheads, tmpcyl, tmpsect, tmpsize;
490 		imageDiskList[drivenum]->Get_Geometry(&tmpheads, &tmpcyl, &tmpsect, &tmpsize);
491 		if (tmpcyl==0) LOG(LOG_BIOS,LOG_ERROR)("INT13 DrivParm: cylinder count zero!");
492 		else tmpcyl--;		// cylinder count -> max cylinder
493 		if (tmpheads==0) LOG(LOG_BIOS,LOG_ERROR)("INT13 DrivParm: head count zero!");
494 		else tmpheads--;	// head count -> max head
495 		reg_ch = (Bit8u)(tmpcyl & 0xff);
496 		reg_cl = (Bit8u)(((tmpcyl >> 2) & 0xc0) | (tmpsect & 0x3f));
497 		reg_dh = (Bit8u)tmpheads;
498 		last_status = 0x00;
499 		if (reg_dl&0x80) {	// harddisks
500 			reg_dl = 0;
501 			if(imageDiskList[2]) reg_dl++;
502 			if(imageDiskList[3]) reg_dl++;
503 		} else {		// floppy disks
504 			reg_dl = 0;
505 			if(imageDiskList[0]) reg_dl++;
506 			if(imageDiskList[1]) reg_dl++;
507 		}
508 		CALLBACK_SCF(false);
509 		break;
510 	case 0x11: /* Recalibrate drive */
511 		reg_ah = 0x00;
512 		CALLBACK_SCF(false);
513 		break;
514 	case 0x15: /* Get disk type */
515 		/* Korean Powerdolls uses this to detect harddrives */
516 		LOG(LOG_BIOS,LOG_WARN)("INT13: Get disktype used!");
517 		if (any_images) {
518 			if(driveInactive(drivenum)) {
519 				last_status = 0x07;
520 				reg_ah = last_status;
521 				CALLBACK_SCF(true);
522 				return CBRET_NONE;
523 			}
524 
525 			uint32_t tmpheads, tmpcyl, tmpsect, tmpsize;
526 			imageDiskList[drivenum]->Get_Geometry(&tmpheads, &tmpcyl,
527 			                                      &tmpsect, &tmpsize);
528 			// Store intermediate calculations in 64-bit to avoid
529 			// accidental integer overflow on temporary value:
530 			uint64_t largesize = tmpheads;
531 			largesize *= tmpcyl;
532 			largesize *= tmpsect;
533 			largesize *= tmpsize;
534 			const uint32_t ts = static_cast<uint32_t>(largesize / 512);
535 
536 			reg_ah = (drivenum <2)?1:3; //With 2 for floppy MSDOS starts calling int 13 ah 16
537 			if(reg_ah == 3) {
538 				reg_cx = static_cast<Bit16u>(ts >>16);
539 				reg_dx = static_cast<Bit16u>(ts & 0xffff);
540 			}
541 			CALLBACK_SCF(false);
542 		} else {
543 			if (drivenum <DOS_DRIVES && (Drives[drivenum] != 0 || drivenum <2)) {
544 				if (drivenum <2) {
545 					//TODO use actual size (using 1.44 for now).
546 					reg_ah = 0x1; // type
547 //					reg_cx = 0;
548 //					reg_dx = 2880; //Only set size for harddrives.
549 				} else {
550 					//TODO use actual size (using 105 mb for now).
551 					reg_ah = 0x3; // type
552 					reg_cx = 3;
553 					reg_dx = 0x4800;
554 				}
555 				CALLBACK_SCF(false);
556 			} else {
557 				LOG(LOG_BIOS,LOG_WARN)("INT13: no images, but invalid drive for call 15");
558 				reg_ah=0xff;
559 				CALLBACK_SCF(true);
560 			}
561 		}
562 		break;
563 	case 0x17: /* Set disk type for format */
564 		/* Pirates! needs this to load */
565 		killRead = true;
566 		reg_ah = 0x00;
567 		CALLBACK_SCF(false);
568 		break;
569 	default:
570 		LOG(LOG_BIOS,LOG_ERROR)("INT13: Function %x called on drive %x (dos drive %d)", reg_ah,  reg_dl, drivenum);
571 		reg_ah=0xff;
572 		CALLBACK_SCF(true);
573 	}
574 	return CBRET_NONE;
575 }
576 
577 
BIOS_SetupDisks(void)578 void BIOS_SetupDisks(void) {
579 /* TODO Start the time correctly */
580 	call_int13=CALLBACK_Allocate();
581 	CALLBACK_Setup(call_int13,&INT13_DiskHandler,CB_INT13,"Int 13 Bios disk");
582 	RealSetVec(0x13,CALLBACK_RealPointer(call_int13));
583 	for (auto &disk : imageDiskList)
584 		disk.reset();
585 	for (auto &disk : diskSwap)
586 		disk.reset();
587 	diskparm0 = CALLBACK_Allocate();
588 	diskparm1 = CALLBACK_Allocate();
589 	swapPosition = 0;
590 
591 	RealSetVec(0x41,CALLBACK_RealPointer(diskparm0));
592 	RealSetVec(0x46,CALLBACK_RealPointer(diskparm1));
593 
594 	PhysPt dp0physaddr=CALLBACK_PhysPointer(diskparm0);
595 	PhysPt dp1physaddr=CALLBACK_PhysPointer(diskparm1);
596 	for (int i = 0; i < 16; i++) {
597 		phys_writeb(dp0physaddr+i,0);
598 		phys_writeb(dp1physaddr+i,0);
599 	}
600 
601 	imgDTASeg = 0;
602 
603 /* Setup the Bios Area */
604 	mem_writeb(BIOS_HARDDISK_COUNT,2);
605 
606 	MAPPER_AddHandler(swapInNextDisk, SDL_SCANCODE_F4, PRIMARY_MOD,
607 	                  "swapimg", "Swap Image");
608 	killRead = false;
609 	swapping_requested = false;
610 }
611