floppy.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. "use strict";
  2. /**
  3. * @constructor
  4. *
  5. * @param {CPU} cpu
  6. */
  7. function FloppyController(cpu, fda_image, fdb_image)
  8. {
  9. /** @const @type {IO|undefined} */
  10. this.io = cpu.io;
  11. /** @const @type {CPU} */
  12. this.cpu = cpu;
  13. /** @const @type {DMA} */
  14. this.dma = cpu.devices.dma;
  15. this.bytes_expecting = 0;
  16. this.receiving_command = new Uint8Array(10);
  17. this.receiving_index = 0;
  18. this.next_command = null;
  19. this.response_data = new Uint8Array(10);
  20. this.response_index = 0;
  21. this.response_length = 0;
  22. this.floppy_size = 0;
  23. /* const */
  24. this.fda_image = fda_image;
  25. /* const */
  26. this.fdb_image = fdb_image;
  27. this.status_reg0 = 0;
  28. this.status_reg1 = 0;
  29. this.status_reg2 = 0;
  30. this.drive = 0;
  31. this.last_cylinder = 0;
  32. this.last_head = 0;
  33. this.last_sector = 1;
  34. // this should actually be write-only ... but people read it anyway
  35. this.dor = 0;
  36. if(!fda_image)
  37. {
  38. // Needed for CD emulation provided by seabios
  39. cpu.devices.rtc.cmos_write(CMOS_FLOPPY_DRIVE_TYPE, 4 << 4);
  40. this.sectors_per_track = 0;
  41. this.number_of_heads = 0;
  42. this.number_of_cylinders = 0;
  43. this.floppy_size = 0;
  44. }
  45. else
  46. {
  47. this.floppy_size = fda_image.byteLength;
  48. var floppy_types = {
  49. 160 : { type: 1, tracks: 40, sectors: 8 , heads: 1 },
  50. 180 : { type: 1, tracks: 40, sectors: 9 , heads: 1 },
  51. 200 : { type: 1, tracks: 40, sectors: 10, heads: 1 },
  52. 320 : { type: 1, tracks: 40, sectors: 8 , heads: 2 },
  53. 360 : { type: 1, tracks: 40, sectors: 9 , heads: 2 },
  54. 400 : { type: 1, tracks: 40, sectors: 10, heads: 2 },
  55. 720 : { type: 3, tracks: 80, sectors: 9 , heads: 2 },
  56. 1200 : { type: 2, tracks: 80, sectors: 15, heads: 2 },
  57. 1440 : { type: 4, tracks: 80, sectors: 18, heads: 2 },
  58. 1722 : { type: 5, tracks: 82, sectors: 21, heads: 2 },
  59. 2880 : { type: 5, tracks: 80, sectors: 36, heads: 2 },
  60. };
  61. var number_of_cylinders,
  62. sectors_per_track,
  63. number_of_heads,
  64. floppy_type = floppy_types[this.floppy_size >> 10];
  65. if(floppy_type && (this.floppy_size & 0x3FF) === 0)
  66. {
  67. cpu.devices.rtc.cmos_write(CMOS_FLOPPY_DRIVE_TYPE, floppy_type.type << 4);
  68. sectors_per_track = floppy_type.sectors;
  69. number_of_heads = floppy_type.heads;
  70. number_of_cylinders = floppy_type.tracks;
  71. }
  72. else
  73. {
  74. throw "Unknown floppy size: " + h(fda_image.byteLength);
  75. }
  76. this.sectors_per_track = sectors_per_track;
  77. this.number_of_heads = number_of_heads;
  78. this.number_of_cylinders = number_of_cylinders;
  79. }
  80. this.io.register_read(0x3F0, this, this.port3F0_read);
  81. this.io.register_read(0x3F2, this, this.port3F2_read);
  82. this.io.register_read(0x3F4, this, this.port3F4_read);
  83. this.io.register_read(0x3F5, this, this.port3F5_read);
  84. this.io.register_read(0x3F7, this, this.port3F7_read);
  85. this.io.register_write(0x3F2, this, this.port3F2_write);
  86. this.io.register_write(0x3F5, this, this.port3F5_write);
  87. }
  88. FloppyController.prototype.get_state = function()
  89. {
  90. var state = [];
  91. state[0] = this.bytes_expecting;
  92. state[1] = this.receiving_command;
  93. state[2] = this.receiving_index;
  94. //state[3] = this.next_command;
  95. state[4] = this.response_data;
  96. state[5] = this.response_index;
  97. state[6] = this.response_length;
  98. state[7] = this.floppy_size;
  99. state[8] = this.status_reg0;
  100. state[9] = this.status_reg1;
  101. state[10] = this.status_reg2;
  102. state[11] = this.drive;
  103. state[12] = this.last_cylinder;
  104. state[13] = this.last_head;
  105. state[14] = this.last_sector;
  106. state[15] = this.dor;
  107. state[16] = this.sectors_per_track;
  108. state[17] = this.number_of_heads;
  109. state[18] = this.number_of_cylinders;
  110. return state;
  111. };
  112. FloppyController.prototype.set_state = function(state)
  113. {
  114. this.bytes_expecting = state[0];
  115. this.receiving_command = state[1];
  116. this.receiving_index = state[2];
  117. this.next_command = state[3];
  118. this.response_data = state[4];
  119. this.response_index = state[5];
  120. this.response_length = state[6];
  121. this.floppy_size = state[7];
  122. this.status_reg0 = state[8];
  123. this.status_reg1 = state[9];
  124. this.status_reg2 = state[10];
  125. this.drive = state[11];
  126. this.last_cylinder = state[12];
  127. this.last_head = state[13];
  128. this.last_sector = state[14];
  129. this.dor = state[15];
  130. this.sectors_per_track = state[16];
  131. this.number_of_heads = state[17];
  132. this.number_of_cylinders = state[18];
  133. };
  134. FloppyController.prototype.port3F0_read = function()
  135. {
  136. dbg_log("3F0 read", LOG_FLOPPY);
  137. return 0;
  138. };
  139. FloppyController.prototype.port3F4_read = function()
  140. {
  141. dbg_log("3F4 read", LOG_FLOPPY);
  142. var return_byte = 0x80;
  143. if(this.response_index < this.response_length)
  144. {
  145. return_byte |= 0x40 | 0x10;
  146. }
  147. if((this.dor & 8) === 0)
  148. {
  149. return_byte |= 0x20;
  150. }
  151. return return_byte;
  152. };
  153. FloppyController.prototype.port3F7_read = function()
  154. {
  155. dbg_log("3F7 read", LOG_FLOPPY);
  156. return 0x00;
  157. };
  158. FloppyController.prototype.port3F5_read = function()
  159. {
  160. if(this.response_index < this.response_length)
  161. {
  162. dbg_log("3F5 read: " + this.response_data[this.response_index], LOG_FLOPPY);
  163. this.cpu.device_lower_irq(6);
  164. return this.response_data[this.response_index++];
  165. }
  166. else
  167. {
  168. dbg_log("3F5 read, empty", LOG_FLOPPY);
  169. return 0xFF;
  170. }
  171. };
  172. FloppyController.prototype.port3F5_write = function(reg_byte)
  173. {
  174. if(!this.fda_image) return;
  175. dbg_log("3F5 write " + h(reg_byte), LOG_FLOPPY);
  176. if(this.bytes_expecting > 0)
  177. {
  178. this.receiving_command[this.receiving_index++] = reg_byte;
  179. this.bytes_expecting--;
  180. if(this.bytes_expecting === 0)
  181. {
  182. if(DEBUG)
  183. {
  184. var log = "3F5 command received: ";
  185. for(var i = 0; i < this.receiving_index; i++)
  186. log += h(this.receiving_command[i]) + " ";
  187. dbg_log(log, LOG_FLOPPY);
  188. }
  189. this.next_command.call(this, this.receiving_command);
  190. }
  191. }
  192. else
  193. {
  194. switch(reg_byte)
  195. {
  196. // TODO
  197. //case 2:
  198. //this.next_command = read_complete_track;
  199. //this.bytes_expecting = 8;
  200. //break;
  201. case 0x03:
  202. this.next_command = this.fix_drive_data;
  203. this.bytes_expecting = 2;
  204. break;
  205. case 0x04:
  206. this.next_command = this.check_drive_status;
  207. this.bytes_expecting = 1;
  208. break;
  209. case 0x05:
  210. case 0xC5:
  211. this.next_command = function(args) { this.do_sector(true, args); };
  212. this.bytes_expecting = 8;
  213. break;
  214. case 0xE6:
  215. this.next_command = function(args) { this.do_sector(false, args); };
  216. this.bytes_expecting = 8;
  217. break;
  218. case 0x07:
  219. this.next_command = this.calibrate;
  220. this.bytes_expecting = 1;
  221. break;
  222. case 0x08:
  223. this.check_interrupt_status();
  224. break;
  225. case 0x4A:
  226. this.next_command = this.read_sector_id;
  227. this.bytes_expecting = 1;
  228. break;
  229. case 0x0F:
  230. this.bytes_expecting = 2;
  231. this.next_command = this.seek;
  232. break;
  233. case 0x0E:
  234. // dump regs
  235. dbg_log("dump registers", LOG_FLOPPY);
  236. this.response_data[0] = 0x80;
  237. this.response_index = 0;
  238. this.response_length = 1;
  239. this.bytes_expecting = 0;
  240. break;
  241. default:
  242. dbg_assert(false, "Unimplemented floppy command call " + h(reg_byte));
  243. }
  244. this.receiving_index = 0;
  245. }
  246. };
  247. FloppyController.prototype.port3F2_read = function()
  248. {
  249. dbg_log("read 3F2: DOR", LOG_FLOPPY);
  250. return this.dor;
  251. };
  252. FloppyController.prototype.port3F2_write = function(value)
  253. {
  254. if((value & 4) === 4 && (this.dor & 4) === 0)
  255. {
  256. // reset
  257. this.cpu.device_raise_irq(6);
  258. }
  259. dbg_log("start motors: " + h(value >> 4), LOG_FLOPPY);
  260. dbg_log("enable dma: " + !!(value & 8), LOG_FLOPPY);
  261. dbg_log("reset fdc: " + !!(value & 4), LOG_FLOPPY);
  262. dbg_log("drive select: " + (value & 3), LOG_FLOPPY);
  263. dbg_log("DOR = " + h(value), LOG_FLOPPY);
  264. this.dor = value;
  265. };
  266. FloppyController.prototype.check_drive_status = function(args)
  267. {
  268. dbg_log("check drive status", LOG_FLOPPY);
  269. this.response_index = 0;
  270. this.response_length = 1;
  271. this.response_data[0] = 1 << 5;
  272. };
  273. FloppyController.prototype.seek = function(args)
  274. {
  275. dbg_log("seek", LOG_FLOPPY);
  276. dbg_assert((args[0] & 3) === 0, "Unhandled seek drive");
  277. this.last_cylinder = args[1];
  278. this.last_head = args[0] >> 2 & 1;
  279. this.raise_irq();
  280. };
  281. FloppyController.prototype.calibrate = function(args)
  282. {
  283. dbg_log("floppy calibrate", LOG_FLOPPY);
  284. this.raise_irq();
  285. };
  286. FloppyController.prototype.check_interrupt_status = function()
  287. {
  288. // do not trigger an interrupt here
  289. dbg_log("floppy check interrupt status", LOG_FLOPPY);
  290. this.response_index = 0;
  291. this.response_length = 2;
  292. this.response_data[0] = 1 << 5;
  293. this.response_data[1] = this.last_cylinder;
  294. };
  295. FloppyController.prototype.do_sector = function(is_write, args)
  296. {
  297. var head = args[2],
  298. cylinder = args[1],
  299. sector = args[3],
  300. sector_size = 128 << args[4],
  301. read_count = args[5] - args[3] + 1,
  302. read_offset = ((head + this.number_of_heads * cylinder) * this.sectors_per_track + sector - 1) * sector_size;
  303. dbg_log("Floppy " + (is_write ? "Write" : "Read"), LOG_FLOPPY);
  304. dbg_log("from " + h(read_offset) + " length " + h(read_count * sector_size), LOG_FLOPPY);
  305. dbg_log(cylinder + " / " + head + " / " + sector, LOG_FLOPPY);
  306. if(!args[4])
  307. {
  308. dbg_log("FDC: sector count is zero, use data length instead", LOG_FLOPPY);
  309. }
  310. if(!this.fda_image)
  311. {
  312. return;
  313. }
  314. if(is_write)
  315. {
  316. this.dma.do_write(this.fda_image, read_offset, read_count * sector_size, 2, this.done.bind(this, args, cylinder, head, sector));
  317. }
  318. else
  319. {
  320. this.dma.do_read(this.fda_image, read_offset, read_count * sector_size, 2, this.done.bind(this, args, cylinder, head, sector));
  321. }
  322. };
  323. FloppyController.prototype.done = function(args, cylinder, head, sector, error)
  324. {
  325. if(error)
  326. {
  327. // TODO: Set appropriate bits
  328. return;
  329. }
  330. sector++;
  331. if(sector > this.sectors_per_track)
  332. {
  333. sector = 1;
  334. head++;
  335. if(head >= this.number_of_heads)
  336. {
  337. head = 0;
  338. cylinder++;
  339. }
  340. }
  341. this.last_cylinder = cylinder;
  342. this.last_head = head;
  343. this.last_sector = sector;
  344. this.response_index = 0;
  345. this.response_length = 7;
  346. this.response_data[0] = head << 2 | 0x20;
  347. this.response_data[1] = 0;
  348. this.response_data[2] = 0;
  349. this.response_data[3] = cylinder;
  350. this.response_data[4] = head;
  351. this.response_data[5] = sector;
  352. this.response_data[6] = args[4];
  353. this.raise_irq();
  354. };
  355. FloppyController.prototype.fix_drive_data = function(args)
  356. {
  357. dbg_log("floppy fix drive data " + args, LOG_FLOPPY);
  358. };
  359. FloppyController.prototype.read_sector_id = function(args)
  360. {
  361. dbg_log("floppy read sector id " + args, LOG_FLOPPY);
  362. this.response_index = 0;
  363. this.response_length = 7;
  364. this.response_data[0] = 0;
  365. this.response_data[1] = 0;
  366. this.response_data[2] = 0;
  367. this.response_data[3] = 0;
  368. this.response_data[4] = 0;
  369. this.response_data[5] = 0;
  370. this.response_data[6] = 0;
  371. this.raise_irq();
  372. };
  373. FloppyController.prototype.raise_irq = function()
  374. {
  375. if(this.dor & 8)
  376. {
  377. this.cpu.device_raise_irq(6);
  378. }
  379. };