BIOS.Oberon.Diskettes.Mod 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640
  1. (* Aos, Copyright 2001, Pieter Muller, ETH Zurich *)
  2. MODULE Diskettes IN Oberon; (** non-portable *)
  3. (** AUTHOR "pjm"; PURPOSE "Diskette device driver"; *)
  4. (* based on Native Oberon. *)
  5. IMPORT SYSTEM, Machine IN A2, AosKernel := Kernel IN A2, Modules IN A2, Kernel, Plugins IN A2, Disks IN A2;
  6. CONST
  7. MaxDevices = 2;
  8. BS = 512;
  9. Read = Disks.Read; Write = Disks.Write; Format = 2; Verify = 3; (* operations *)
  10. Ready = 0; Reset = 1; Recal = 2; (* states *)
  11. T0 = 0; T720 = 1; T1440 = 2; T2880 = 3; (* drive/media types *)
  12. Ok = Disks.Ok;
  13. TYPE
  14. Device* = OBJECT (Disks.Device)
  15. VAR
  16. drive: LONGINT;
  17. locked: BOOLEAN; (* must be locked before access is allowed *)
  18. type, media: SHORTINT; (* drive type & current media *)
  19. (* current parameters *)
  20. size, sectors, heads, tracks: LONGINT;
  21. gap, rate, spec1, spec2, fgap: CHAR;
  22. PROCEDURE Transfer*(op, start, num: LONGINT; VAR buf: ARRAY OF CHAR; ofs: LONGINT; VAR res: WORD);
  23. BEGIN
  24. Transfer1(SELF, op, start, num, buf, ofs, res)
  25. END Transfer;
  26. PROCEDURE GetSize*(VAR size, res: LONGINT);
  27. BEGIN
  28. GetSize1(SELF, size, res)
  29. END GetSize;
  30. PROCEDURE Handle*(VAR msg: Disks.Message; VAR res: WORD);
  31. BEGIN
  32. Handle1(SELF, msg, res)
  33. END Handle;
  34. END Device;
  35. VAR
  36. device: ARRAY MaxDevices OF Device;
  37. curdrive: LONGINT;
  38. curtrack: LONGINT;
  39. state: SHORTINT;
  40. result: ARRAY 7 OF SET;
  41. errors: ARRAY 3 OF SET;
  42. dmabufvirt, dmabufphys, dmabufsize: LONGINT;
  43. motor, interrupt, installed: BOOLEAN;
  44. trace: SHORTINT;
  45. (* Device driver *)
  46. (* Error - Report an error *)
  47. PROCEDURE Error(msg: ARRAY OF CHAR);
  48. VAR error, reason: ARRAY 32 OF CHAR; i: SHORTINT; r0, r1, r2: SET;
  49. BEGIN
  50. COPY(msg, error); r0 := errors[0]; r1 := errors[1]; r2 := errors[2];
  51. IF (0 IN r1) OR (0 IN r2) THEN reason := "Missing address mark"
  52. ELSIF 1 IN r1 THEN reason := "Write protected"
  53. ELSIF 2 IN r1 THEN reason := "Sector not found"
  54. ELSIF 4 IN r1 THEN reason := "Over- or Underrun"
  55. ELSIF (5 IN r1) OR (5 IN r2) THEN reason := "CRC error"
  56. ELSIF 7 IN r1 THEN reason := "Sector past end"
  57. ELSIF (1 IN r2) OR (4 IN r2) THEN reason := "Bad track"
  58. ELSIF 6 IN r2 THEN reason := "Bad mark"
  59. ELSIF r0 * {6,7} = {6} THEN reason := "Command not completed"
  60. ELSIF r0 * {6,7} = {7} THEN reason := "Invalid command"
  61. ELSE reason := ""
  62. END;
  63. Kernel.WriteLn; Kernel.WriteString("Diskette: "); Kernel.WriteString(error);
  64. Kernel.WriteString(". "); Kernel.WriteString(reason); Kernel.WriteLn;
  65. IF trace > 0 THEN
  66. FOR i := 0 TO 2 DO Kernel.WriteHex(SYSTEM.VAL(LONGINT, result[i]), 9) END;
  67. Kernel.WriteLn;
  68. FOR i := 0 TO 2 DO Kernel.WriteHex(SYSTEM.VAL(LONGINT, errors[i]), 9) END;
  69. Kernel.WriteLn
  70. END;
  71. FOR i := 0 TO 6 DO result[i] := {} END;
  72. FOR i := 0 TO 2 DO errors[i] := {} END;
  73. state := Reset
  74. END Error;
  75. (* SetupDMA - Start a DMA operation *)
  76. PROCEDURE SetupDMA(read: BOOLEAN; chan, len: LONGINT);
  77. VAR adr, page, mode: LONGINT;
  78. BEGIN
  79. adr := dmabufphys;
  80. ASSERT(len <= dmabufsize);
  81. IF read THEN
  82. mode := 44H (* IO->memory, no autoinit, increment, single mode *)
  83. ELSE
  84. mode := 48H (* memory->IO, no autoinit, increment, single mode *)
  85. END;
  86. DEC(len);
  87. ASSERT((adr > 0) & (adr+len <= 1000000H));
  88. ASSERT(adr DIV 65536 = (adr+len-1) DIV 65536); (* same 64KB region *)
  89. CASE chan OF
  90. 0: page := 87H
  91. |1: page := 83H
  92. |2: page := 81H
  93. |3: page := 82H
  94. END; (* CASE *)
  95. Machine.Portout8(0AH, CHR(chan + 4)); (* disable DMA *)
  96. Machine.Portout8(0CH, 0X); (* clear flip-flop *)
  97. Machine.Portout8(0BH, CHR(chan + mode)); (* set mode *)
  98. Machine.Portout8(page, CHR(ASH(adr, -16))); (* set page register *)
  99. Machine.Portout8(chan*2, CHR(adr)); (* set address *)
  100. Machine.Portout8(chan*2, CHR(ASH(adr, -8)));
  101. Machine.Portout8(chan*2+1, CHR(len)); (* set length *)
  102. Machine.Portout8(chan*2+1, CHR(ASH(len, -8)));
  103. Machine.Portout8(0AH, CHR(chan)) (* enable DMA *)
  104. END SetupDMA;
  105. (* PutByte - Send byte to controller *)
  106. PROCEDURE PutByte(b: CHAR);
  107. VAR t: AosKernel.MilliTimer; s: SET;
  108. BEGIN
  109. IF state # Reset THEN
  110. AosKernel.SetTimer(t, 500); (* 0.5s *)
  111. REPEAT
  112. Machine.Portin8(3F4H, SYSTEM.VAL(CHAR, s));
  113. IF s * {6,7} = {7} THEN (* ready for write *)
  114. Machine.Portout8(3F5H, b);
  115. RETURN (* done *)
  116. END
  117. UNTIL AosKernel.Expired(t);
  118. state := Reset; IF trace > 0 THEN Kernel.WriteString("~response ") END
  119. END
  120. END PutByte;
  121. (* GetResults - Get results from controller, returns length of result *)
  122. PROCEDURE GetResults(): INTEGER;
  123. VAR t: AosKernel.MilliTimer; s: SET; i: SHORTINT;
  124. BEGIN
  125. IF state # Reset THEN
  126. i := 0; s := {};
  127. AosKernel.SetTimer(t, 500); (* 0.5s *)
  128. REPEAT
  129. Machine.Portin8(3F4H, SYSTEM.VAL(CHAR, s));
  130. IF s * {4,6,7} = {7} THEN (* ready for write (end) *)
  131. IF trace > 0 THEN Kernel.WriteChar("="); Kernel.WriteInt(i, 1) END;
  132. RETURN i
  133. ELSIF s * {6,7} = {6,7} THEN (* ready for read *)
  134. Machine.Portin8(3F5H, SYSTEM.VAL(CHAR, s)); result[i] := s;
  135. IF i < 3 THEN errors[i] := errors[i] + result[i] END;
  136. INC(i)
  137. ELSE (* skip *)
  138. END
  139. UNTIL AosKernel.Expired(t);
  140. state := Reset; IF trace > 0 THEN Kernel.WriteString("~response ") END
  141. END;
  142. RETURN -1
  143. END GetResults;
  144. (* InterruptHandler - Handle floppy interrupt *)
  145. PROCEDURE InterruptHandler(VAR state: Machine.State);
  146. BEGIN
  147. Machine.Sti(); interrupt := TRUE
  148. END InterruptHandler;
  149. (* WaitInterrupt - Wait for an interrupt *)
  150. PROCEDURE WaitInterrupt;
  151. VAR t: AosKernel.MilliTimer;
  152. BEGIN
  153. IF state # Reset THEN
  154. AosKernel.SetTimer(t, 2000); (* 2s *)
  155. REPEAT UNTIL interrupt OR AosKernel.Expired(t);
  156. IF ~interrupt THEN IF trace > 0 THEN Kernel.WriteString("~interrupt ") END; state := Reset END;
  157. interrupt := FALSE
  158. END
  159. END WaitInterrupt;
  160. (* SetParams - Set parameters depending on drive type and media *)
  161. PROCEDURE SetParams(p: Device);
  162. BEGIN
  163. CASE p.media OF
  164. T720:
  165. IF trace > 0 THEN Kernel.WriteString("720k ") END;
  166. p.sectors := 9; p.heads := 2; p.tracks := 80;
  167. p.gap := 1BX; p.rate := 2X; (* transfer rate 250k/s *)
  168. p.spec1 := 0E1X; (* step rate 4ms, head unload 32ms *)
  169. p.spec2 := 6X; (* head load 12ms, DMA mode *)
  170. p.fgap := 50X (* format gap size *)
  171. |T1440:
  172. IF trace > 0 THEN Kernel.WriteString("1.44M ") END;
  173. p.sectors := 18; p.heads := 2; p.tracks := 80;
  174. p.gap := 1BX; p.rate := 0X; (* transfer rate 500k/s *)
  175. p.spec1 := 0C1X; (* step rate 4ms, head unload 16ms *)
  176. p.spec2 := 6X; (* head load 6ms, DMA mode *)
  177. p.fgap := 6CX (* format gap size *)
  178. END;
  179. p.size := p.sectors * p.heads * p.tracks;
  180. state := Reset
  181. END SetParams;
  182. (* CycleMedia - Skip to next media for a drive *)
  183. PROCEDURE CycleMedia(VAR p: Device);
  184. BEGIN
  185. CASE p.type OF
  186. T0: HALT(99) (* no such drive *)
  187. |T720: (* 720k drive can only handle 720k media *)
  188. CASE p.media OF
  189. T0: p.media := T720
  190. |T720: p.media := T0
  191. END
  192. |T1440: (* 1.44M drive first tries 1.44M & then 720k *)
  193. CASE p.media OF
  194. T0: p.media := T1440
  195. |T1440: p.media := T720
  196. |T720: p.media := T0
  197. END
  198. |T2880: (* 2.88M drive first tries 1.44M & then 720k (2.88M not handled yet) *)
  199. CASE p.media OF
  200. T0: p.media := T1440
  201. |T1440: p.media := T720
  202. |T720: p.media := T0
  203. END
  204. END; (* CASE *)
  205. IF p.media # T0 THEN SetParams(p) END (* now set params according to media *)
  206. END CycleMedia;
  207. (* Do - Perform a floppy operation *)
  208. PROCEDURE Do(dev: Device; op, sector, head, track, num: LONGINT; VAR buf: ARRAY OF SYSTEM.BYTE): LONGINT;
  209. CONST MaxLoops = 18; MaxTries = 3;
  210. VAR s: SET; i, loops, try: LONGINT; t: AosKernel.MilliTimer; ok: BOOLEAN; media: SHORTINT;
  211. BEGIN
  212. FOR i := 0 TO 2 DO errors[i] := {} END;
  213. IF (num < 1) OR (num > 126) THEN Error("Bad number of sectors"); RETURN 1003 END;
  214. IF (track < 0) OR (track >= dev.tracks) THEN Error("Invalid track"); RETURN 1004 END;
  215. IF (head < 0) OR (head >= dev.heads) THEN Error("Invalid head"); RETURN 1005 END;
  216. IF curdrive # dev.drive THEN state := Reset; curdrive := dev.drive END;
  217. loops := 0; try := 0; media := dev.media;
  218. LOOP (* two EXIT's at end of CASE state = Ready *)
  219. IF trace > 0 THEN
  220. CASE state OF
  221. Ready: Kernel.WriteString("Ready ")
  222. |Reset: Kernel.WriteString("Reset ")
  223. |Recal: Kernel.WriteString("Recal ")
  224. ELSE Kernel.WriteString("Unknown ")
  225. END
  226. END;
  227. (* select the drive & send power to the motor *)
  228. s := {2,3,dev.drive+4} + SYSTEM.VAL(SET, dev.drive);
  229. Machine.Portout8(3F2H, SYSTEM.VAL(CHAR, s));
  230. IF (op IN {Write, Format}) & ~motor THEN (* motor was not running, wait for it to spin up *)
  231. AosKernel.SetTimer(t, 500); (* 0.5s *)
  232. REPEAT UNTIL AosKernel.Expired(t)
  233. END;
  234. motor := TRUE; ok := TRUE;
  235. CASE state OF
  236. Ready:
  237. IF trace > 0 THEN
  238. Kernel.WriteLn;
  239. CASE op OF
  240. Read: Kernel.WriteString("Read(")
  241. |Write: Kernel.WriteString("Write(")
  242. |Format: Kernel.WriteString("Format(")
  243. |Verify: Kernel.WriteString("Verify(")
  244. END;
  245. Kernel.WriteInt(track, 1); Kernel.WriteChar(",");
  246. Kernel.WriteInt(head, 1); Kernel.WriteChar(",");
  247. Kernel.WriteInt(sector, 1); Kernel.WriteChar(",");
  248. Kernel.WriteInt(num, 1); Kernel.WriteString(") ")
  249. END;
  250. IF curtrack # track THEN (* seek to right track *)
  251. PutByte(0FX); PutByte(CHR(ASH(head, 2) + dev.drive)); PutByte(CHR(track)); (* seek *)
  252. WaitInterrupt;
  253. PutByte(8X); i := GetResults(); (* sense *)
  254. IF (i < 1) OR (result[0] * {3..7} # {5}) THEN
  255. IF trace > 0 THEN Kernel.WriteString("~seek ") END; state := Reset
  256. ELSE
  257. curtrack := track
  258. END
  259. END;
  260. IF state # Reset THEN
  261. CASE op OF
  262. Read, Verify:
  263. SetupDMA(TRUE, 2, num*512);
  264. PutByte(0E6X)
  265. |Write:
  266. SYSTEM.MOVE(ADDRESSOF(buf[0]), dmabufvirt, num*512);
  267. SetupDMA(FALSE, 2, num*512);
  268. PutByte(0C5X)
  269. |Format:
  270. FOR i := 0 TO num-1 DO
  271. SYSTEM.PUT(dmabufvirt+i*4+0, CHR(track));
  272. SYSTEM.PUT(dmabufvirt+i*4+1, CHR(head));
  273. SYSTEM.PUT(dmabufvirt+i*4+2, CHR(i+1));
  274. SYSTEM.PUT(dmabufvirt+i*4+3, CHR(2))
  275. END;
  276. SetupDMA(FALSE, 2, num*4);
  277. PutByte(4DX); PutByte(CHR(ASH(head, 2) + dev.drive));
  278. PutByte(2X); PutByte(CHR(num));
  279. PutByte(dev.fgap); PutByte(0F6X)
  280. END;
  281. IF op IN {Read, Write, Verify} THEN (* standard parameters *)
  282. PutByte(CHR(ASH(head, 2) + dev.drive)); PutByte(CHR(track)); (* drive, head, track *)
  283. PutByte(CHR(head)); PutByte(CHR(sector)); (* head, sector *)
  284. PutByte(2X); (* 512 byte sector *)
  285. PutByte(CHR(dev.sectors)); (* last sector *)
  286. PutByte(dev.gap); (* gap length *)
  287. PutByte(0FFX) (* sector size (unused) *)
  288. END;
  289. WaitInterrupt;
  290. IF (GetResults() < 7) OR (result[0] * {6,7} # {}) THEN
  291. IF trace > 0 THEN Kernel.WriteString("~op ") END; state := Reset
  292. END
  293. END;
  294. IF state = Reset THEN
  295. INC(try); IF trace > 0 THEN Kernel.WriteInt(try, 1); Kernel.WriteString("-try ") END;
  296. IF try = MaxTries THEN
  297. IF op IN {Read, Write} THEN
  298. try := 0; CycleMedia(dev); (* advance to next media type *)
  299. IF dev.media # T0 THEN
  300. EXIT (* EXIT: media type changed *)
  301. END
  302. END;
  303. IF op IN {Read, Verify} THEN Error("Read failed"); RETURN 1006
  304. ELSE Error("Write failed"); RETURN 1007
  305. END
  306. END
  307. ELSE
  308. IF op = Read THEN
  309. SYSTEM.MOVE(dmabufvirt, ADDRESSOF(buf[0]), num*512)
  310. END;
  311. EXIT (* EXIT: operation successful *)
  312. END
  313. |Reset:
  314. curtrack := -1; interrupt := FALSE; (* reset possible late interrupt *)
  315. Machine.Portin8(3F2H, SYSTEM.VAL(CHAR, s)); EXCL(s, 2);
  316. Machine.Portout8(3F2H, SYSTEM.VAL(CHAR, s));
  317. AosKernel.SetTimer(t, 1); REPEAT UNTIL AosKernel.Expired(t); (* > 50us *)
  318. INCL(s, 2); Machine.Portout8(3F2H, SYSTEM.VAL(CHAR, s));
  319. state := Recal; WaitInterrupt;
  320. PutByte(8X); (* sense *)
  321. IF GetResults() < 1 THEN Error("Reset failed"); RETURN 1008 END;
  322. PutByte(3X); (* specify (step rate, head load/unload) *)
  323. PutByte(dev.spec1); PutByte(dev.spec2);
  324. IF state = Reset THEN Error("Specify failed"); RETURN 1009 END;
  325. Machine.Portout8(3F7H, dev.rate); (* data rate *)
  326. |Recal:
  327. PutByte(7X); PutByte(CHR(dev.drive)); (* recalibrate *)
  328. WaitInterrupt;
  329. PutByte(8X); i := GetResults(); (* sense *)
  330. IF (i < 1) OR (result[0] * {6..7} # {}) THEN
  331. (*Error("Recalibrate failed")*)
  332. ELSE
  333. state := Ready; curtrack := 0
  334. END
  335. END; (* CASE *)
  336. INC(loops); IF loops = MaxLoops THEN Error("Too many retries"); RETURN 1010 END;
  337. IF dev.media # media THEN RETURN Disks.MediaChanged END (* trying new media type *)
  338. END;
  339. IF dev.media = media THEN RETURN Ok ELSE RETURN Disks.MediaChanged END
  340. END Do;
  341. PROCEDURE Transfer0(d: Disks.Device; op, start, num: LONGINT; VAR buf: ARRAY OF CHAR; ofs: LONGINT; VAR res: WORD);
  342. VAR dev: Device; sector, head, track, s, ofs0, n, max, start0, num0: LONGINT;
  343. BEGIN
  344. dev := d(Device);
  345. IF dev.locked THEN
  346. ASSERT((op = Read) OR (op = Write));
  347. IF dev.type = T0 THEN Error("Invalid drive"); HALT(99) END;
  348. IF dev.media = T0 THEN CycleMedia(dev) END;
  349. start0 := start; num0 := num; ofs0 := ofs;
  350. REPEAT
  351. s := start; sector := (s MOD dev.sectors) + 1;
  352. s := s DIV dev.sectors; head := s MOD dev.heads;
  353. track := s DIV dev.heads;
  354. max := dev.sectors - sector + 1; (* sectors left on track *)
  355. IF (head = 0) & (dev.heads > 1) THEN
  356. INC(max, dev.sectors) (* multi-track *)
  357. END;
  358. IF max > dmabufsize DIV BS THEN max := dmabufsize DIV BS END;
  359. IF num > max THEN n := max ELSE n := num END;
  360. res := Do(dev, op, sector, head, track, n, buf[ofs]);
  361. IF res = Ok THEN
  362. DEC(num, n); INC(start, n); INC(ofs, n*512)
  363. ELSIF res = Disks.MediaChanged THEN (* media type changed, start over *)
  364. start := start0; num := num0; ofs := ofs0; res := Ok
  365. ELSE
  366. (* skip *)
  367. END
  368. UNTIL (num = 0) OR (res # Ok)
  369. ELSE
  370. res := Disks.MediaMissing (* must be locked for transfer *)
  371. END
  372. END Transfer0;
  373. PROCEDURE Transfer1(d: Disks.Device; op, start, num: LONGINT; VAR buf: ARRAY OF CHAR; ofs: LONGINT; VAR res: WORD);
  374. BEGIN {EXCLUSIVE}
  375. Transfer0(d, op, start, num, buf, ofs, res)
  376. END Transfer1;
  377. PROCEDURE GetSize1(d: Disks.Device; VAR size, res: LONGINT);
  378. VAR dev: Device; buf: ARRAY BS OF CHAR;
  379. BEGIN {EXCLUSIVE}
  380. dev := d(Device);
  381. Transfer0(dev, Read, 0, 1, buf, 0, res);
  382. IF res = Disks.Ok THEN size := dev.size ELSE size := 0 END
  383. END GetSize1;
  384. PROCEDURE Handle1(d: Disks.Device; VAR msg: Disks.Message; VAR res: WORD);
  385. VAR dev: Device; buf: ARRAY BS OF CHAR;
  386. BEGIN {EXCLUSIVE}
  387. dev := d(Device);
  388. IF msg IS Disks.GetGeometryMsg THEN
  389. Transfer0(dev, Read, 0, 1, buf, 0, res);
  390. IF res = Disks.Ok THEN
  391. WITH msg: Disks.GetGeometryMsg DO
  392. msg.cyls := dev.tracks; msg.hds := dev.heads; msg.spt := dev.sectors
  393. END
  394. END
  395. ELSIF msg IS Disks.LockMsg THEN
  396. IF ~dev.locked THEN
  397. dev.locked := TRUE; res := Disks.Ok
  398. ELSE
  399. res := 1001 (* already locked *)
  400. END
  401. ELSIF msg IS Disks.UnlockMsg THEN
  402. IF dev.locked THEN
  403. dev.locked := FALSE; res := Disks.Ok;
  404. StopMotor(dev.drive)
  405. ELSE
  406. res := 1002 (* was not locked *)
  407. END
  408. ELSE
  409. res := Disks.Unsupported
  410. END
  411. END Handle1;
  412. (** FormatDisk - Low-level format a diskette. fmt="H" for high density (1.44M), "D" for double (720k) *)
  413. PROCEDURE FormatDisk*(drive: LONGINT; fmt: CHAR);
  414. VAR
  415. error: ARRAY 32 OF CHAR; head, track, i, div: LONGINT; phys: BOOLEAN; buf: ARRAY 512 OF CHAR;
  416. dev: Device;
  417. BEGIN {EXCLUSIVE}
  418. dev := device[drive];
  419. error := "Format not supported";
  420. CASE fmt OF
  421. "H", "h": (* 1.44M *)
  422. IF dev.type < T1440 THEN HALT(99) END;
  423. dev.media := T1440;
  424. div := 1
  425. |"D", "d": (* 720k *)
  426. IF dev.type < T720 THEN HALT(99) END;
  427. dev.media := T720;
  428. div := 2
  429. END; (* CASE *)
  430. phys := (CAP(fmt) = fmt);
  431. (* format & verify *)
  432. error := "Format or verify error";
  433. SetParams(dev);
  434. FOR track := 0 TO dev.tracks-1 DO
  435. FOR head := 0 TO dev.heads-1 DO
  436. IF phys & (Do(dev, Format, 0, head, track, dev.sectors, buf) # Ok) THEN HALT(99) END;
  437. IF Do(dev, Verify, 1, head, track, dev.sectors, buf) # Ok THEN HALT(99) END
  438. END
  439. END;
  440. (* init boot sector *)
  441. FOR i := 0 TO 511 DO buf[i] := 0X END;
  442. buf[0CH] := 2X; (* 512 bytes per sector *)
  443. buf[0DH] := 1X; (* sectors per cluster *)
  444. buf[0EH] := 1X; (* reserved sectors *)
  445. buf[10H] := 2X; (* number of FAT copies *)
  446. buf[11H] := CHR(224 DIV div); (* number of root dir entries *)
  447. buf[13H] := CHR(dev.size MOD 100H);
  448. buf[14H] := CHR(dev.size DIV 100H);
  449. IF div = 2 THEN buf[15H] := 0F9X ELSE buf[15H] := 0F0X END;
  450. IF div = 2 THEN buf[16H] := 3X ELSE buf[16H] := 9X END;
  451. buf[18H] := CHR(dev.sectors);
  452. buf[1AH] := CHR(dev.heads);
  453. (* write boot sector *)
  454. IF Do(device[drive], Write, 1, 0, 0, 1, buf) # Ok THEN HALT(99) END
  455. END FormatDisk;
  456. (* StopMotor - Switch off diskette motor *)
  457. PROCEDURE StopMotor(drive: LONGINT);
  458. BEGIN
  459. device[drive].media := T0; (* reset media type *)
  460. Machine.Portout8(3F2H, 0CX); (* all motors off *)
  461. motor := FALSE
  462. END StopMotor;
  463. PROCEDURE StrToInt(s: ARRAY OF CHAR): LONGINT;
  464. VAR i: SHORTINT; v: LONGINT;
  465. BEGIN
  466. v := 0; i := 0;
  467. WHILE s[i] # 0X DO v := v*10+(ORD(s[i])-48); INC(i) END;
  468. RETURN v
  469. END StrToInt;
  470. PROCEDURE Init;
  471. VAR s: ARRAY 12 OF CHAR; b10, b14: INTEGER;
  472. BEGIN
  473. Kernel.GetConfig("TraceDiskette", s);
  474. IF s[0] # 0X THEN trace := SHORT(ORD(s[0])-ORD("0")) ELSE trace := 0 END;
  475. curdrive := -1; curtrack := -1; motor := FALSE; interrupt := FALSE; state := Reset;
  476. Kernel.GetConfig("Diskette", s);
  477. IF s = "" THEN
  478. b10 := ORD(Machine.GetNVByte(10H));
  479. b14 := ORD(Machine.GetNVByte(14H))
  480. ELSE
  481. b10 := SHORT(StrToInt(s) MOD 100H);
  482. b14 := INTEGER(ASH(StrToInt(s), -8))
  483. END;
  484. IF trace > 0 THEN
  485. Kernel.WriteString("Diskette config:"); Kernel.WriteHex(b10, -3);
  486. Kernel.WriteHex(b14, -3); Kernel.WriteLn
  487. END;
  488. (* look at drive 0 setup *)
  489. NEW(device[0]); device[0].drive := 0;
  490. CASE ASH(b10, -4) OF
  491. 3: device[0].type := T720
  492. |4: device[0].type := T1440
  493. |5: device[0].type := T2880
  494. ELSE device[0].type := T0
  495. END;
  496. device[0].media := T0;
  497. (* look at drive 1 setup, if present *)
  498. IF ODD(ASH(b14, -6)) THEN
  499. NEW(device[1]); device[1].drive := 1;
  500. CASE b10 MOD 16 OF
  501. 3: device[1].type := T720
  502. |4: device[1].type := T1440
  503. |5: device[1].type := T2880
  504. ELSE device[1].type := T0
  505. END;
  506. device[1].media := T0
  507. (*ELSE device[1].type := T0*)
  508. END
  509. END Init;
  510. PROCEDURE Register;
  511. VAR i, res: LONGINT; dev: Device; name: Plugins.Name;
  512. BEGIN
  513. FOR i := 0 TO MaxDevices-1 DO
  514. dev := device[i];
  515. IF dev # NIL THEN
  516. name := "Diskette0"; name[8] := CHR(48 + i);
  517. dev.SetName(name); dev.desc := "Standard Diskette";
  518. dev.blockSize := BS; dev.flags := {Disks.Removable};
  519. Disks.registry.Add(dev, res);
  520. ASSERT(res = Plugins.Ok)
  521. END
  522. END
  523. END Register;
  524. (** Install the diskette devices. Automatically executed when the module is loaded. *)
  525. PROCEDURE Install*;
  526. END Install;
  527. PROCEDURE DoInstall;
  528. BEGIN
  529. IF ~installed & (dmabufphys # 0) THEN
  530. Init;
  531. Machine.Portout8(3F2H, 0CX); (* motors off, select drive 0, clear reset *)
  532. Machine.InstallHandler(InterruptHandler, Machine.IRQ0+6);
  533. Register;
  534. installed := TRUE
  535. END
  536. END DoInstall;
  537. (** Remove the diskette devices. Automatically executed when the module is unloaded. *)
  538. PROCEDURE Remove*;
  539. VAR i: LONGINT;
  540. BEGIN {EXCLUSIVE}
  541. IF installed & (Modules.shutdown = Modules.None) THEN
  542. FOR i := 0 TO MaxDevices-1 DO
  543. IF device[i] # NIL THEN
  544. Disks.registry.Remove(device[i]);
  545. StopMotor(device[i].drive);
  546. device[i] := NIL
  547. END
  548. END;
  549. Machine.RemoveHandler(InterruptHandler, Machine.IRQ0+6);
  550. installed := FALSE
  551. END
  552. END Remove;
  553. BEGIN
  554. dmabufsize := Machine.dmaSize;
  555. IF dmabufsize > 0 THEN
  556. dmabufphys := Machine.lowTop;
  557. Machine.MapPhysical(dmabufphys, dmabufsize, SYSTEM.VAL(ADDRESS,dmabufvirt))
  558. ELSE
  559. dmabufphys := 0
  560. END;
  561. Modules.InstallTermHandler(Remove);
  562. installed := FALSE; DoInstall
  563. END Diskettes.
  564. (*
  565. Results
  566. -5 Disks.MediaMissing, transfer attempted on unlocked device
  567. 0 Disks.Ok, no error
  568. 1001 already locked
  569. 1002 was not locked
  570. 1003 bad number of sectors
  571. 1004 invalid track
  572. 1005 invalid head
  573. 1006 read failed
  574. 1007 write failed
  575. 1008 reset failed
  576. 1009 specify failed
  577. 1010 too many retries
  578. Diskettes.Install
  579. Diskettes.Remove
  580. System.Free Diskettes ~
  581. Partitions.Show
  582. to do:
  583. o should not import Kernel
  584. o name should be Diskettes
  585. o clean up Format
  586. *)