PageRenderTime 27ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/DOSBox/src/ints/bios_disk.cpp

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