floppy.js 12 KB

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