floppy.js 12 KB

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