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), §buf[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