Oberon/ETH Oberon/ATADisks.Mod

(* ETH Oberon, Copyright 1990-2005 Computer Systems Institute, ETH Zurich, CH-8092 Zurich. Refer to the license ftp&#58;//ftp.ethoberon.ethz.ch/ETHOberon/license.txt . *) MODULE ATADisks; (** non-portable *)	(* cp/pjm/prk *) (* Native Oberon ATA disk driver with Intel 82371SB (PIIX3) bus-mastering support (e.g. 440FX chipset). Reference&#58; T13/1153D Rev. 17, AT Attachment with Packet Interface Extension (ATA/ATAPI-4). 30.07.1998	cp	first version working 04.12.1998	pjm	added bus-mastering and restructured 23.03.1999	pjm	fixed chs parameters 24.03.1999	pjm	non-polling versions of PIO transfers 26.03.1999	pjm	LBA mode implemented 19.04.1999	pjm	Fixed for Compaq laptop 23.08.1999	prk	Adding more ATAPI support 31.08.1999	pjm	Modified for Disks module 22.11.2005	ple	CompactFlash recognition per J. Sedlacek and AosATADisks.Mod &#42;) IMPORT SYSTEM, Kernel, Disks, PCI; CONST &#9;MaxControllers &#61; 2;	(* &#60;&#61; 10 *) &#9;MaxDevices &#61; 2*MaxControllers; &#9;MaxTries &#61; 5; &#9; &#9;BMEnabled &#61; TRUE;	(* check for bus-master chipset *) &#9;OverrideBIOS &#61; TRUE;	(* enable bus-master even if BIOS did not *) &#9;PrimaryEnabled &#61; TRUE;	(* disable for testing *) &#9;SecondaryEnabled &#61; TRUE;	(* disable for testing *) &#9;UseConfigParams &#61; TRUE;	(* disable for testing *) &#9;TraceStatus &#61; TRUE;	(* show error information *) &#9;TraceIdentify &#61; FALSE;	(* show identify information *) &#9;Experimental &#61; TRUE;	(* media handling code *) &#9;AllowEject &#61; TRUE;	(* allow ejection of removable media *) &#9;TraceCalls &#61; FALSE;	(* show procedures called *) &#9;SelectWait &#61; FALSE;	(* wait before select *) &#9; EarlyBug &#61; TRUE;	(* temporary workaround for early interrupts *) &#9;SelectTimeout &#61; 5000;	(* ms *) &#9;IOTimeout &#61; 10000;	(* ms *) &#9;IdentifyTimeout &#61; 500;	(* ms *) &#9;ResetTimeout &#61; 1000;	(* ms *) &#9; &#9;MaxTransfer &#61; 256;	(* &#60;&#61; 256 *) &#9;MaxPRD &#61; 3;	(* &#60;&#61; 4 *) &#9;BS &#61; 512; &#9;CDRomBS &#61; 2048; &#9;MinLBA &#61; 8000000;	(* (&#62; 0) if reported size larger than this, use LBA mode *) &#9; &#9;(* According "CF+ and CompactFlash Specification Version 1.4" by CompactFlash Association*) &#9;CompactFlashSignature &#61; 848AH; (*CF*) &#9; &#9;AtapiBit &#61; 0; RemovableBit &#61; 1;  DMABit &#61; 2;  LBABit &#61; 3; &#9; Packet16Bit &#61; 4;	(*packets are 16 bytes long instead of 12 - Atapi only *) &#9;RMSNBit &#61; 5;	(*Media Status Notification supported*) &#9;LockBit &#61; 6;	(*locking supported - RMSN only *) &#9;EjectBit &#61; 7;	(*power eject supported - RMSN only *) &#9;CDRom &#61; 8;	(*CDRom device - atapi only *) &#9;CompactFlash &#61; 9; (*CF*) &#9;ERR &#61; 0; DRQ &#61; 3;  DRDY &#61; 6;  BSY &#61; 7; &#9; &#9;(*GetMediaStatus Flags*) &#9;NM &#61; 1; MCR &#61; 3;  MC &#61; 5;  WP &#61; 6; &#9; TYPE &#9;CHS &#61; RECORD &#9;&#9;cyls, hds, spt&#58; LONGINT &#9;END; &#9; &#9;ID &#61; RECORD &#9;&#9;type&#58; SET; &#9;&#9;ver&#58; LONGINT; &#9;&#9;model&#58; ARRAY 44 OF CHAR &#9;END; &#9; &#9;Packet16 &#61; ARRAY 16 OF CHAR; &#9;PRDT &#61; POINTER TO RECORD &#9;&#9;prd&#58; ARRAY MaxPRD OF RECORD	(* aligned on 32-byte boundary, see Intel 290550-002 sec. 2.7.3 *) &#9;&#9;&#9;adr, count&#58; LONGINT &#9;&#9;END &#9;END; &#9; &#9;Controller &#61; POINTER TO RECORD &#9;&#9;port, port2, irq, bmbase&#58; LONGINT; &#9;&#9;interrupts, mark&#58; LONGINT; &#9;&#9;prdt&#58; PRDT; &#9;&#9;busy&#58; BOOLEAN &#9;END; &#9; &#9;Device &#61; POINTER TO RECORD (Disks.Device) &#9;&#9;controller&#58; Controller; &#9;&#9;dev&#58; LONGINT;	(* 0 or 1 *) &#9;&#9;size&#58; LONGINT;	(* total size *) &#9;&#9;chs&#58; CHS;	(* for controller *) &#9;&#9;getpar&#58; CHS;	(* for GetParams *) &#9;&#9;id&#58; ID; &#9;&#9;init&#58; BOOLEAN;	(* initialized? *) &#9;&#9;sense&#58; LONGINT;	(*last device sense, atapi only*) &#9;END; &#9; VAR &#9;controller&#58; ARRAY MaxControllers OF Controller; &#9;device&#58; ARRAY MaxDevices OF Device; &#9;irqs&#58; SET; &#9;Nunexpected, Nunknown&#58; LONGINT;	(* number of unknown/unexpected interrupts *) PROCEDURE WriteStatus(msg&#58; ARRAY OF CHAR; port&#58; LONGINT); VAR ch&#58; CHAR; t&#58; LONGINT; BEGIN &#9;IF TraceStatus THEN &#9;&#9;Kernel.WriteString(msg); Kernel.WriteHex(port, 9); &#9;&#9;SYSTEM.PORTIN(port+6, ch); &#9;&#9;Kernel.WriteChar(" "); Kernel.WriteInt(ORD(ch) DIV 10H MOD 2, 1); &#9;&#9;SYSTEM.PORTIN(port+5, ch); t &#58;&#61; ORD(ch); &#9;&#9;SYSTEM.PORTIN(port+4, ch); t &#58;&#61; ASH(t, 8) + ORD(ch); &#9;&#9;Kernel.WriteChar(" "); Kernel.WriteInt(t, 1); &#9;&#9;SYSTEM.PORTIN(port+6, ch); &#9;&#9;Kernel.WriteChar(" "); Kernel.WriteInt(ORD(ch) MOD 10H, 1); &#9;&#9;SYSTEM.PORTIN(port+3, ch); &#9;&#9;Kernel.WriteChar(" "); Kernel.WriteInt(ORD(ch), 1); &#9;&#9;SYSTEM.PORTIN(port+1, ch); &#9;&#9;Kernel.WriteHex(ORD(ch), -3); &#9;&#9;SYSTEM.PORTIN(port+7, ch); &#9;&#9;Kernel.WriteHex(ORD(ch), -3) &#9;END END WriteStatus; (* Wait for specified value of controller status bits. *) PROCEDURE WaitStatus(port&#58; LONGINT; mask, expect, bad&#58; SET;  ms&#58; LONGINT)&#58; BOOLEAN; VAR t&#58; Kernel.MilliTimer; s&#58; SET;  ch&#58; CHAR; BEGIN &#9;Kernel.SetTimer(t, ms); &#9;REPEAT &#9;&#9;SYSTEM.PORTIN(port+7, ch); s &#58;&#61; SYSTEM.VAL(SET, LONG(ORD(ch))) &#9;UNTIL (s * mask &#61; expect) OR (s * bad # &#123;&#125;) OR Kernel.Expired(t); &#9;IF TraceStatus &#38; ((s * bad # &#123;&#125;) OR (s * mask # expect)) THEN &#9;&#9;WriteStatus("Status", port); &#9;&#9;Kernel.WriteHex(SYSTEM.VAL(LONGINT, s), -3); &#9;&#9;Kernel.WriteHex(SYSTEM.VAL(LONGINT, mask), -3); &#9;&#9;Kernel.WriteHex(SYSTEM.VAL(LONGINT, expect), -3); &#9;&#9;Kernel.WriteHex(SYSTEM.VAL(LONGINT, bad), -3); &#9;&#9;Kernel.WriteChar(" "); Kernel.WriteInt(ms, 1); &#9;&#9;Kernel.WriteLn &#9;END; &#9;RETURN (s * mask &#61; expect) &#38; (s * bad &#61; &#123;&#125;) END WaitStatus; (* Wait for specified value of controller status bits using the alternate status bits (don&#39;t clear interrupts). *) PROCEDURE WaitAltStatus(port, port2&#58; LONGINT; mask, expect, bad&#58; SET;  ms&#58; LONGINT)&#58; BOOLEAN; VAR t&#58; Kernel.MilliTimer; s&#58; SET;  ch&#58; CHAR; BEGIN &#9;Kernel.SetTimer(t, ms); &#9;REPEAT &#9;&#9;SYSTEM.PORTIN(port2, ch); s &#58;&#61; SYSTEM.VAL(SET, LONG(ORD(ch))); &#9;UNTIL (s * mask &#61; expect) OR (s * bad # &#123;&#125;) OR Kernel.Expired(t); &#9;IF TraceStatus &#38; ((s * bad # &#123;&#125;) OR (s * mask # expect)) THEN &#9;&#9;Kernel.WriteString("ATADisks&#58; s"); Kernel.WriteHex(SYSTEM.VAL(LONGINT, s), -3); &#9;&#9;Kernel.WriteString(", mask"); Kernel.WriteHex(SYSTEM.VAL(LONGINT, mask), -3); &#9;&#9;Kernel.WriteString(", exp"); Kernel.WriteHex(SYSTEM.VAL(LONGINT, expect), -3); &#9;&#9;Kernel.WriteString(", bad"); Kernel.WriteHex(SYSTEM.VAL(LONGINT, bad), -3); &#9;&#9;Kernel.WriteString(", time "); Kernel.WriteInt(ms, 1); &#9;&#9;Kernel.WriteLn &#9;END; &#9;RETURN (s * mask &#61; expect) &#38; (s * bad &#61; &#123;&#125;) END WaitAltStatus; (* NanoDelay - Delay at least ns nanoseconds. *) PROCEDURE NanoDelay(ns&#58; LONGINT); BEGIN &#9;ns &#58;&#61; ns DIV 2;	(* 400MHz clock &#61;&#62; 2.5ns cycle *) &#9;WHILE ns &#62; 0 DO DEC(ns) END END NanoDelay; (* Select device for next command (0 or 1). Section 9.6. *) PROCEDURE SelectDevice(port, d, timeout&#58; LONGINT)&#58; LONGINT; VAR res&#58; LONGINT; BEGIN &#9;IF &#126;SelectWait OR WaitStatus(port, &#123;DRQ,BSY&#125;, &#123;&#125;, &#123;&#125;, timeout) THEN &#9;&#9;SYSTEM.PORTOUT(port+6, CHR(ASH(d, 4))); &#9;&#9;NanoDelay(400); &#9;&#9;IF WaitStatus(port, &#123;DRQ,BSY&#125;, &#123;&#125;, &#123;&#125;, timeout) THEN res &#58;&#61; 0 ELSE res &#58;&#61; 2 END &#9;ELSE &#9;&#9;res &#58;&#61; 1 &#9;END; &#9;RETURN res END SelectDevice; (* Issue a command to the controller. *) PROCEDURE Command(d&#58; Device; cmd&#58; CHAR;  lba, num&#58; LONGINT); VAR port, sector, cylinder, head, x&#58; LONGINT; BEGIN &#9;port &#58;&#61; d.controller.port; &#9;ASSERT((num &#62; 0) &#38; (num &#60;&#61; 100H)); &#9;SYSTEM.PORTOUT(port+2, CHR(num MOD 100H));	(* 0 means 256 *) &#9;IF LBABit IN d.id.type THEN &#9;&#9;ASSERT((lba &#62;&#61; 0) &#38; (lba &#60; 10000000H)); &#9;&#9;SYSTEM.PORTOUT(port+3, CHR(lba MOD 100H)); &#9;&#9;SYSTEM.PORTOUT(port+4, CHR(ASH(lba, -8) MOD 100H)); &#9;&#9;SYSTEM.PORTOUT(port+5, CHR(ASH(lba, -16) MOD 100H)); &#9;&#9;SYSTEM.PORTOUT(port+6, CHR((40H + ASH(d.dev, 4) + ASH(lba, -24) MOD 10H))) &#9;ELSE &#9;&#9;sector &#58;&#61; lba MOD d.chs.spt + 1; x &#58;&#61; lba DIV d.chs.spt; &#9;&#9;head &#58;&#61; x MOD d.chs.hds; cylinder &#58;&#61; x DIV d.chs.hds; &#9;&#9;ASSERT((sector &#60; 100H) &#38; (cylinder &#60; 10000H) &#38; (head &#60; 10H)); &#9;&#9;SYSTEM.PORTOUT(port+3, CHR(sector)); &#9;&#9;SYSTEM.PORTOUT(port+4, CHR(cylinder MOD 100H)); &#9;&#9;SYSTEM.PORTOUT(port+5, CHR(cylinder DIV 100H)); &#9;&#9;SYSTEM.PORTOUT(port+6, CHR((ASH(d.dev, 4) + head))) &#9;END; &#9;SYSTEM.PORTOUT(port+7, cmd) END Command; (* Interrupt handler (needed for DMA operation). *) PROCEDURE InterruptHandler; VAR i, u, int&#58; LONGINT; ctrl&#58; Controller; BEGIN &#9;SYSTEM.STI; &#9;SYSTEM.GETREG(SYSTEM.EBP, int); SYSTEM.GET(int+40, int); &#9;DEC(int, Kernel.IRQ); u &#58;&#61; 1; &#9;FOR i &#58;&#61; 0 TO MaxControllers-1 DO &#9;&#9;ctrl &#58;&#61; controller&#91;i&#93;; &#9;&#9;IF (ctrl # NIL) &#38; (ctrl.irq &#61; int) THEN u &#58;&#61; 0; INC(ctrl.interrupts) END &#9;END; &#9;SYSTEM.CLI; &#9;INC(Nunknown, u) END InterruptHandler; (* Wait for an interrupt to occur. *) PROCEDURE WaitInterrupt(c&#58; Controller; ms&#58; LONGINT)&#58; BOOLEAN; VAR t&#58; Kernel.MilliTimer; ok&#58; BOOLEAN; BEGIN &#9;Kernel.SetTimer(t, ms); &#9;REPEAT ok &#58;&#61; c.interrupts # c.mark UNTIL ok OR Kernel.Expired(t); &#9;c.mark &#58;&#61; c.interrupts; &#9;RETURN ok END WaitInterrupt; (* Start interrupt waiting period. *) PROCEDURE StartInterrupt(c&#58; Controller); BEGIN &#9;INC(Nunexpected, c.interrupts - c.mark); &#9;c.mark &#58;&#61; c.interrupts END StartInterrupt; (* Block port input instruction. *) PROCEDURE -RepInWord(port, bufAdr, len&#58; LONGINT); CODE &#123;SYSTEM.i386&#125; &#9;POP ECX &#9;POP EDI &#9;POP EDX &#9;CLD &#9;REP INSW END RepInWord; (* Block port out instruction. *) PROCEDURE -RepOutWord(port, bufAdr, len&#58; LONGINT); CODE &#123;SYSTEM.i386&#125; &#9;POP ECX &#9;POP ESI &#9;POP EDX &#9;CLD &#9;REP OUTSW END RepOutWord; PROCEDURE WaitNotBusy(port2&#58; LONGINT); VAR x&#58; CHAR; BEGIN &#9;SYSTEM.PORTIN(port2, x); &#9;IF 7 IN SYSTEM.VAL(SET, LONG(ORD(x))) THEN &#9;&#9;Kernel.WriteString("ATA&#58; early interrupt"); Kernel.WriteHex(ORD(x), -3);  Kernel.WriteLn; &#9;&#9;(*Kernel.WriteChar(8X);*) &#9;&#9;REPEAT &#9;&#9;&#9;SYSTEM.PORTIN(port2, x) &#9;&#9;UNTIL &#126;(7 IN SYSTEM.VAL(SET, LONG(ORD(x)))) &#9;END END WaitNotBusy; (* Read blocks using PIO protocol. *) PROCEDURE ReadPIO(d&#58; Device; lba, num, bufAdr&#58; LONGINT)&#58; LONGINT; VAR port, port2, res&#58; LONGINT; x&#58; CHAR;  s&#58; SET;  c&#58; Controller; BEGIN &#9;res &#58;&#61; 0; c &#58;&#61; d.controller;  port &#58;&#61; c.port;  port2 &#58;&#61; c.port2; &#9;StartInterrupt(c); &#9;Command(d, 20X, lba, num);	(* read sectors *) &#9;REPEAT &#9;&#9;IF WaitInterrupt(c, IOTimeout) THEN &#9;&#9;&#9;IF EarlyBug THEN WaitNotBusy(c.port2) END; &#9;&#9;&#9;SYSTEM.PORTIN(port+7, x); s &#58;&#61; SYSTEM.VAL(SET, LONG(ORD(x))); &#9;&#9;&#9;IF &#126;(ERR IN s) THEN &#9;&#9;&#9;&#9;RepInWord(port, bufAdr, BS DIV 2); &#9;&#9;&#9;&#9;INC(bufAdr, BS); DEC(num) &#9;&#9;&#9;ELSE &#9;&#9;&#9;&#9;res &#58;&#61; 20 &#9;&#9;&#9;END &#9;&#9;ELSE &#9;&#9;&#9;res &#58;&#61; 19 &#9;&#9;END &#9;UNTIL (num &#61; 0) OR (res # 0); &#9;SYSTEM.PORTIN(port2, x);	(* according to spec *) &#9;SYSTEM.PORTIN(port+7, x); s &#58;&#61; SYSTEM.VAL(SET, LONG(ORD(x))); &#9;IF (res &#61; 0) &#38; (ERR IN s) THEN res &#58;&#61; 21 END; &#9;IF TraceStatus &#38; (res # 0) THEN WriteStatus("ReadPIO", port); Kernel.WriteLn END; &#9;RETURN res END ReadPIO; (* Write blocks using PIO protocol. *) PROCEDURE WritePIO(d&#58; Device; lba, num, bufAdr&#58; LONGINT)&#58; LONGINT; VAR port, port2, res&#58; LONGINT; x&#58; CHAR;  s&#58; SET;  c&#58; Controller; BEGIN &#9;res &#58;&#61; 0; c &#58;&#61; d.controller;  port &#58;&#61; c.port;  port2 &#58;&#61; c.port2; &#9;StartInterrupt(c); &#9;Command(d, 30X, lba, num);	(* write sectors *) &#9;IF WaitAltStatus(port, port2, &#123;DRQ,BSY&#125;, &#123;DRQ&#125;, &#123;ERR&#125;, IOTimeout) THEN &#9;&#9;REPEAT &#9;&#9;&#9;RepOutWord(port, bufAdr, BS DIV 2); &#9;&#9;&#9;INC(bufAdr, BS); DEC(num); &#9;&#9;&#9;IF WaitInterrupt(c, IOTimeout) THEN &#9;&#9; &#9;&#9;IF EarlyBug THEN WaitNotBusy(c.port2) END; &#9;&#9;&#9;&#9;SYSTEM.PORTIN(port+7, x); s &#58;&#61; SYSTEM.VAL(SET, LONG(ORD(x))); &#9;&#9;&#9;&#9;IF ERR IN s THEN res &#58;&#61; 24 END &#9;&#9;&#9;ELSE &#9;&#9;&#9;&#9;res &#58;&#61; 23 &#9;&#9;&#9;END &#9;&#9;UNTIL (num &#61; 0) OR (res # 0) &#9;ELSE &#9;&#9;res &#58;&#61; 22 &#9;END; &#9;IF res # 0 THEN &#9;&#9;IF TraceStatus THEN WriteStatus("WritePIO", port); Kernel.WriteLn END; &#9;&#9;SYSTEM.PORTIN(port+7, x)	(* clear interrupt *) &#9;END; &#9;RETURN res END WritePIO; (* Send packet command, atapi only *) PROCEDURE CommandPacket(d&#58; Device; VAR pkt&#58; Packet16)&#58; LONGINT; VAR res, port&#58; LONGINT; x&#58; CHAR;  c&#58; Controller; BEGIN &#9;c &#58;&#61; d.controller; port &#58;&#61; c.port; &#9;SYSTEM.PORTOUT(port+1, 0X);	(* no OVL, no DMA *) &#9;SYSTEM.PORTOUT(port+2, 0X);	(* tag 0 *) &#9;SYSTEM.PORTOUT(port+4, 0FEX);	(* byte count limit *) &#9;SYSTEM.PORTOUT(port+5, 0FFX); &#9;SYSTEM.PORTOUT(port+6, CHR(ASH(d.dev, 4)));	(*device*) &#9;SYSTEM.PORTOUT(port+7, 0A0X);	(* packet *) &#9;NanoDelay(400); &#9;IF WaitStatus(port, &#123;DRQ,BSY&#125;, &#123;DRQ&#125;, &#123;&#125;, SelectTimeout) THEN &#9;&#9;IF Packet16Bit IN d.id.type THEN &#9;&#9;&#9;RepOutWord(port, SYSTEM.ADR(pkt&#91;0&#93;), 8) &#9;&#9;ELSE &#9;&#9;&#9;RepOutWord(port, SYSTEM.ADR(pkt&#91;0&#93;), 6) &#9;&#9;END; &#9;&#9;SYSTEM.PORTIN(c.port2, x);	(* ensure status is valid *) &#9;&#9;res &#58;&#61; 0 &#9;ELSE &#9;&#9;res &#58;&#61; 31 &#9;END; &#9;RETURN res END CommandPacket; (* Compose packet command, atapi only *) PROCEDURE ComposePacket(VAR pkt&#58; Packet16; cmd&#58; CHAR;  lba, num&#58; LONGINT); BEGIN &#9;pkt&#91;0&#93; &#58;&#61; cmd; &#9;pkt&#91;1&#93; &#58;&#61; 0X; &#9;pkt&#91;2&#93; &#58;&#61; CHR(ASH(lba, -24) MOD 100H); &#9;pkt&#91;3&#93; &#58;&#61; CHR(ASH(lba, -16) MOD 100H); &#9;pkt&#91;4&#93; &#58;&#61; CHR(ASH(lba, -8) MOD 100H); &#9;pkt&#91;5&#93; &#58;&#61; CHR(lba MOD 100H); &#9;pkt&#91;6&#93; &#58;&#61; 0X; &#9;pkt&#91;7&#93; &#58;&#61; CHR(ASH(num, -8) MOD 100H); &#9;pkt&#91;8&#93; &#58;&#61; CHR(num MOD 100H); &#9;pkt&#91;9&#93; &#58;&#61; 0X; pkt&#91;10&#93; &#58;&#61; 0X;  pkt&#91;11&#93; &#58;&#61; 0X; &#9;pkt&#91;12&#93; &#58;&#61; 0X; pkt&#91;13&#93; &#58;&#61; 0X;  pkt&#91;14&#93; &#58;&#61; 0X;  pkt&#91;15&#93; &#58;&#61; 0X; END ComposePacket; (* Transfer a packet and read/write data, atapi only *) PROCEDURE TransferPacket(d&#58; Device; pkt&#58; Packet16;  read&#58; BOOLEAN;  bufAdr, size&#58; LONGINT)&#58; LONGINT; VAR res, port, avail, key, code&#58; LONGINT; x&#58; CHAR; BEGIN &#9;port &#58;&#61; d.controller.port; &#9;res &#58;&#61; CommandPacket(d, pkt); &#9;WHILE (res &#61; 0) &#38; (size &#62; 0) DO &#9;&#9;IF WaitStatus(port, &#123;DRQ,BSY&#125;, &#123;DRQ&#125;, &#123;ERR&#125;, IOTimeout) THEN &#9;&#9;&#9;SYSTEM.PORTIN(port+4, x); avail &#58;&#61; LONG(ORD(x)); &#9;&#9;&#9;SYSTEM.PORTIN(port+5, x); avail &#58;&#61; avail + LONG(ORD(x))*100H; &#9;&#9;&#9;IF avail &#62; size THEN avail &#58;&#61; size END; &#9;&#9;&#9; (*Kernel.WriteString("Avail &#61; "); Kernel.WriteInt(avail, 0); Kernel.WriteChar("/"); Kernel.WriteInt(size, 0); Kernel.WriteLn;*) &#9;&#9;&#9;IF read THEN &#9;&#9;&#9;&#9;RepInWord(port, bufAdr, avail DIV 2) &#9;&#9;&#9;ELSE &#9;&#9;&#9;&#9;RepOutWord(port, bufAdr, avail DIV 2) &#9;&#9;&#9;END; &#9;&#9;&#9;INC(bufAdr, avail); DEC(size, avail) &#9;&#9;ELSE &#9;&#9;&#9;res &#58;&#61; 33; &#9;&#9;&#9;SYSTEM.PORTIN(port+1, x); key &#58;&#61; ORD(x) DIV 10H; &#9;&#9;&#9;IF key # 0 THEN &#9;&#9;&#9;&#9;res &#58;&#61; 34; &#9;&#9;&#9;&#9;code &#58;&#61; SensePacketDevice(d); d.sense &#58;&#61; code; &#9;&#9;&#9;&#9;(*Kernel.WriteString("Transfer -&#62; Sense "); Kernel.WriteHex(code, 0); Kernel.WriteLn;*) &#9;&#9;&#9;&#9;IF (key &#61; 6) &#38; ((code DIV 100H) &#61; 29H) THEN	(*attention + power on, ignore*) &#9;&#9;&#9;&#9;&#9;res &#58;&#61; CommandPacket(d, pkt)	(*resubmit command*) &#9;&#9;&#9;&#9;END &#9;&#9;&#9;END &#9;&#9;END &#9;END; &#9;IF (res &#61; 0) &#38; &#126;WaitStatus(port, &#123;BSY, DRDY&#125;, &#123;DRDY&#125;, &#123;ERR&#125;, IOTimeout) THEN res &#58;&#61; 32 END; &#9;RETURN res END TransferPacket; (* Sense device, packet only *) PROCEDURE SensePacketDevice(d&#58; Device)&#58; LONGINT; VAR pkt&#58; Packet16; sense, res, port&#58; LONGINT; buf&#58; ARRAY 18 OF CHAR; BEGIN &#9;port &#58;&#61; d.controller.port; &#9;ComposePacket(pkt, 03X, 0, 0);	(* Request Sense *) &#9;pkt&#91;4&#93; &#58;&#61; 12X;	(* buffer size *) &#9;res &#58;&#61; TransferPacket(d, pkt, TRUE, SYSTEM.ADR(buf&#91;0&#93;), LEN(buf)); &#9;IF res &#61; 0 THEN &#9;&#9;sense &#58;&#61; ORD(buf&#91;12&#93;)*100H + ORD(buf&#91;13&#93;); &#9;ELSE &#9;&#9;sense &#58;&#61; ORD(buf&#91;12&#93;)*100H + ORD(buf&#91;13&#93;); &#9;&#9;Kernel.WriteString("sense failed &#61; "); Kernel.WriteHex(sense, -5); Kernel.WriteLn; &#9;&#9;sense &#58;&#61; -1 &#9;END; &#9;RETURN sense END SensePacketDevice ; (* Read device blocksize with ModeSense command, packet only *) PROCEDURE GetModeInfo(dev&#58; Device; VAR blockSize&#58; LONGINT)&#58; LONGINT; VAR pkt&#58; Packet16; buf&#58; ARRAY 12 OF CHAR;  res, port(*, size*)&#58; LONGINT; BEGIN &#9;port &#58;&#61; dev.controller.port; &#9;ComposePacket(pkt, 1AX, 0, 0);	(*ModeSense*) &#9;pkt&#91;2&#93; &#58;&#61; 3FX;	(*mode page&#58; all pages*) &#9;pkt&#91;4&#93; &#58;&#61; 0CX;	(*buffer size*) (* &#9;ComposePacket(pkt, 5AX(* 1AX *), 0, 0);	(*ModeSense 10 *) &#9;pkt&#91;2&#93; &#58;&#61; 0BFX(* 3FX *) ;	(* default values + mode page&#58; all pages*) &#9;pkt&#91; 8(* 4 *) &#93; &#58;&#61; 0CX;	(*buffer size*) &#42;) &#9;res &#58;&#61; TransferPacket(dev, pkt, TRUE, SYSTEM.ADR(buf&#91;0&#93;), LEN(buf)); &#9;IF (res &#61; 34) &#38; (dev.sense &#61; 2800H) THEN	(*failed because of media change, repeat*) &#9;&#9;res &#58;&#61; TransferPacket(dev, pkt, TRUE, SYSTEM.ADR(buf&#91;0&#93;), LEN(buf)) &#9;END; &#9;IF (res &#61; 0) &#38; (ORD(buf&#91;3&#93;) &#61; 8) THEN	(*if one descriptor returned*) &#9;&#9;blockSize &#58;&#61; ORD(buf&#91;4+7&#93;); &#9;&#9;blockSize &#58;&#61; blockSize + SYSTEM.LSH(ORD(buf&#91;4+5&#93;), 16); &#9;&#9;blockSize &#58;&#61; blockSize + SYSTEM.LSH(ORD(buf&#91;4+6&#93;), 8); &#9;&#9;(*size &#58;&#61; ORD(buf&#91;4+3&#93;); &#9;&#9;size &#58;&#61; size + SYSTEM.LSH(ORD(buf&#91;4+2&#93;), 16); &#9;&#9;size &#58;&#61; size + SYSTEM.LSH(ORD(buf&#91;4+1&#93;), 8);*) &#9;ELSE &#9;&#9;blockSize &#58;&#61; 0; &#9;&#9;(*size &#58;&#61; 0;*) &#9;END; &#9;IF blockSize &#61; 0 THEN &#9;&#9;Kernel.WriteString("GetModeInfo "); Kernel.WriteString(dev.name); &#9;&#9;Kernel.WriteChar(" "); Kernel.WriteInt(blockSize, 1); Kernel.WriteLn; &#9;&#9;blockSize &#58;&#61; BS &#9;END; &#9;RETURN res END GetModeInfo; (* Read medium capacity, packet only *) PROCEDURE ReadCapacity(d&#58; Device; VAR blkSize, size&#58; LONGINT)&#58; LONGINT; VAR pkt&#58; Packet16 ; buf&#58; ARRAY 8 OF CHAR;  res, port&#58; LONGINT; BEGIN &#9;IF TraceCalls THEN Kernel.WriteString("ReadCapacity");  Kernel.WriteLn  END; &#9;port &#58;&#61; d.controller.port; &#9;ComposePacket(pkt, 25X, 0, 0);	(* read capacity *) &#9;res &#58;&#61; TransferPacket(d, pkt, TRUE, SYSTEM.ADR(buf&#91;0&#93;), LEN(buf)); &#9;IF (res &#61; 34) &#38; ( d.sense &#61; 0401H) THEN	(*failed because getting ready, repeat*) &#9;&#9;NanoDelay(1000);	(*wait a little*) &#9;&#9;REPEAT res &#58;&#61; TransferPacket(d, pkt, TRUE, SYSTEM.ADR(buf&#91;0&#93;), LEN(buf))  UNTIL (res &#61; 0) OR (d.sense # 0401H) &#9;END; &#9;IF (res &#61; 34) &#38; (d.sense &#61; 2800H) THEN	(*failed because of media change, repeat*) &#9;&#9;&#9;res &#58;&#61; TransferPacket(d, pkt, TRUE, SYSTEM.ADR(buf&#91;0&#93;), LEN(buf)) &#9;END;	(*yes, error 2800H may follow 0401H*) &#9;IF res &#61; 0 THEN &#9;&#9; size &#58;&#61; 1 + ASH(LONG(ORD(buf&#91;0&#93;)), 24) + ASH(LONG(ORD(buf&#91;1&#93;)), 16); &#9;&#9; size &#58;&#61; size + ASH(LONG(ORD(buf&#91;2&#93;)), 8) + LONG(ORD(buf&#91;3&#93;)); &#9;&#9;blkSize &#58;&#61; ASH(LONG(ORD(buf&#91;4&#93;)), 24) + ASH(LONG(ORD(buf&#91;5&#93;)), 16); &#9;&#9;blkSize &#58;&#61; blkSize + ASH(LONG(ORD(buf&#91;6&#93;)), 8) + LONG(ORD(buf&#91;7&#93;)); &#9;&#9;IF (CDRom IN d.id.type) &#38; (blkSize # 2048) THEN &#9;&#9;&#9;Kernel.WriteString(d.name); Kernel.WriteString(" block size set to 2048, was "); &#9;&#9;&#9;Kernel.WriteInt(blkSize, 0); Kernel.WriteLn; &#9;&#9;&#9;blkSize &#58;&#61; 2048 &#9;&#9;END; &#9;&#9;IF TraceIdentify THEN &#9;&#9;&#9;Kernel.WriteString("ATADisks&#58; read capacity "); &#9;&#9;&#9;Kernel.WriteInt(size, 4); Kernel.WriteString(" * "); &#9;&#9;&#9;Kernel.WriteInt(blkSize, 4); Kernel.WriteString(" &#61; "); &#9;&#9;&#9;Kernel.WriteInt(size * (blkSize DIV 512) DIV 2048, 4); Kernel.WriteString(" MB"); &#9;&#9;&#9;Kernel.WriteLn &#9;&#9;END &#9;END; &#9;RETURN res END ReadCapacity; (* test unit ready, packet only *) PROCEDURE TestUnitReady(d&#58; Device)&#58; LONGINT; VAR port, res&#58; LONGINT; pkt&#58; Packet16 ; BEGIN &#9;IF TraceCalls THEN Kernel.WriteString("TestUnitReady");  Kernel.WriteLn  END; &#9;port &#58;&#61; d.controller.port; &#9;ComposePacket(pkt, 0X, 0, 0);	(* test unit ready command *) &#9;res &#58;&#61; TransferPacket(d, pkt, FALSE, 0, 0); &#9;RETURN res END TestUnitReady; (* set CDRom Speed *) (* PROCEDURE SetCDSpeed(dev&#58; Device; read, write&#58; LONGINT); VAR res, port&#58; LONGINT;  pkt&#58; Packet16; BEGIN &#9;port &#58;&#61; dev.controller.port; &#9;ComposePacket(pkt, 0BBX, 0, 0);	(* set cd-rom speed command *) &#9;pkt&#91;2&#93; &#58;&#61; CHR(ASH(read, -8) MOD 100H); &#9;pkt&#91;3&#93; &#58;&#61; CHR(read MOD 100H); &#9;pkt&#91;4&#93; &#58;&#61; CHR(ASH(write, -8) MOD 100H); &#9;pkt&#91;5&#93; &#58;&#61; CHR(write MOD 100H); &#9;res &#58;&#61; TransferPacket(dev, pkt, FALSE, 0, 0); &#9;IF res # 0 THEN &#9;&#9;Kernel.WriteString("SetSpeed failed ");  Kernel.WriteHex(SensePacketDevice(dev), 0);  Kernel.WriteLn &#9;END; END SetCDSpeed; &#42;) (* lock/unlock media *) PROCEDURE LockMedium(d&#58; Device; lock&#58; BOOLEAN)&#58; LONGINT; VAR port, res&#58; LONGINT; pkt&#58; Packet16; BEGIN &#9;IF Experimental THEN &#9;&#9; port &#58;&#61; d.controller.port; &#9; &#9;IF AtapiBit IN d.id.type THEN &#9;&#9; &#9;ComposePacket(pkt, 1EX, 0, 0);	(* allow medium removal *) &#9;&#9; &#9;IF lock THEN pkt&#91;4&#93; &#58;&#61; 1X END; &#9;&#9; &#9;res &#58;&#61; TransferPacket(d, pkt, FALSE, 0, 0); &#9;&#9; &#9;RETURN res &#9;&#9;ELSIF CompactFlash IN d.id.type THEN (*CF*) &#9;&#9;&#9;RETURN Disks.Ok &#9; &#9;ELSE &#9; &#9;&#9;res &#58;&#61; SelectDevice(port, d.dev, SelectTimeout); &#9; &#9;&#9;IF res &#61; 0 THEN &#9; &#9;&#9;&#9;IF lock THEN &#9; &#9;&#9;&#9;&#9;SYSTEM.PORTOUT(port+7, 0DEX)	(* media lock *) &#9; &#9;&#9;&#9;ELSE &#9; &#9;&#9;&#9;&#9;SYSTEM.PORTOUT(port+7, 0DFX)	(* media unlock *) &#9; &#9;&#9;&#9;END; &#9; &#9;&#9;&#9;NanoDelay(400); &#9; &#9;&#9;&#9;IF &#126;WaitStatus(port, &#123;BSY&#125;, &#123;&#125;, &#123;ERR&#125;, ResetTimeout) THEN res &#58;&#61; 29  END &#9;&#9;&#9;END; &#9;&#9;&#9;RETURN res (*CF*) &#9; &#9;END &#9;ELSE &#9;&#9;RETURN Disks.Unsupported &#9;END END LockMedium; (* eject media *) PROCEDURE RemoveMedium(d&#58; Device)&#58; LONGINT; VAR port, res&#58; LONGINT; pkt&#58; Packet16; BEGIN &#9;port &#58;&#61; d.controller.port; &#9; IF AtapiBit IN d.id.type THEN &#9;&#9;ComposePacket(pkt, 01BX, 0, 0);	(* StartStopUnit *) &#9;&#9;pkt&#91;4&#93; &#58;&#61; 2X;	(*eject*) &#9;&#9;res &#58;&#61; TransferPacket(d, pkt, FALSE, 0, 0); &#9;ELSE &#9;&#9;SYSTEM.PORTOUT(port+6, CHR((ASH(d.dev, 4)))); &#9;&#9;SYSTEM.PORTOUT(port+7, 0EDX);	(* media eject *) &#9;&#9;NanoDelay(400); &#9;&#9;IF &#126;WaitStatus(port, &#123;BSY&#125;, &#123;&#125;, &#123;ERR&#125;, ResetTimeout) THEN res &#58;&#61; 39  END &#9;END; &#9; RETURN res END RemoveMedium ; (* get the media status, RMSN only *) PROCEDURE GetMediaStatus( dev&#58; Device ; VAR status&#58; SET)&#58; LONGINT; VAR port, res&#58; LONGINT; ch&#58; CHAR; BEGIN &#9;port &#58;&#61; dev.controller.port; &#9;SYSTEM.PORTOUT(port+6, CHR((ASH( dev. dev, 4)))); &#9;SYSTEM.PORTOUT(port+7, 0DAX);	(* get media status *) &#9;IF WaitStatus(port, &#123;BSY&#125;, &#123;&#125;, &#123;&#125;, ResetTimeout) THEN &#9;&#9;res &#58;&#61; 0; &#9;&#9;SYSTEM.PORTIN(port+1, ch); status &#58;&#61; SYSTEM.VAL(SET, LONG(ORD(ch))) &#9;ELSE &#9;&#9;res &#58;&#61; 38; &#9;&#9;ch &#58;&#61; 0X; status &#58;&#61; &#123;&#125; &#9;END; &#9;RETURN res END GetMediaStatus; (* switch on/off RMSN, RMSN only *) PROCEDURE SetRMSN(dev&#58; Device; on&#58; BOOLEAN)&#58; LONGINT; VAR port, res&#58; LONGINT;  ch&#58; CHAR; BEGIN &#9;IF Experimental THEN &#9; &#9;port &#58;&#61; dev.controller.port; &#9; &#9;(*WriteStatus("SetRMSN", port); Kernel.WriteLn;*) &#9; &#9;res &#58;&#61; SelectDevice(port, dev.dev, IdentifyTimeout); &#9; &#9;IF res &#61; 0 THEN &#9; &#9;&#9;IF on THEN &#9; &#9; &#9; &#9;SYSTEM.PORTOUT(port+1, 95X)	(* enable media status notification *) &#9; &#9;&#9;ELSE &#9; &#9;&#9;&#9;SYSTEM.PORTOUT(port+1, 31X)	(* disable media status notification *) &#9; &#9;&#9;END; &#9;&#9; &#9;SYSTEM.PORTOUT(port+4, 0X); &#9;&#9; &#9;SYSTEM.PORTOUT(port+5, 0X); &#9;&#9; &#9;SYSTEM.PORTOUT(port+7, 0EFX);	(* set features *) &#9;&#9; &#9;NanoDelay(400); &#9;&#9; &#9;IF WaitStatus(port, &#123;BSY&#125;, &#123;&#125;, &#123;ERR&#125;, ResetTimeout) THEN &#9;&#9; &#9;&#9;res &#58;&#61; 0; &#9; &#9;&#9;&#9;Kernel.WriteString(dev.name); Kernel.WriteString("&#58; enable RMSN with&#58;"); &#9;&#9; &#9;&#9;SYSTEM.PORTIN(port+5, ch); &#9;&#9; &#9;&#9;IF 0 IN SYSTEM.VAL(SET, ch) THEN Kernel.WriteString(" PENA") END; &#9;&#9; &#9;&#9;IF 1 IN SYSTEM.VAL(SET, ch) THEN INCL(dev.id.type, LockBit) ;  Kernel.WriteString(" LOCK")  END; &#9;&#9; &#9;&#9;IF 2 IN SYSTEM.VAL(SET, ch) THEN INCL(dev.id.type, EjectBit) ;  Kernel.WriteString(" PEJ")  END; &#9; &#9;&#9;&#9;Kernel.WriteLn &#9;&#9; &#9;ELSE &#9;&#9; &#9;&#9;res &#58;&#61; 35; &#9; &#9;&#9;&#9;Kernel.WriteString(dev.name); Kernel.WriteString("&#58; device doesn&#39;t support RMSN"); &#9; &#9;&#9;&#9;SYSTEM.PORTIN(port+5, ch); Kernel.WriteHex(ORD(ch), -3); &#9; &#9;&#9;&#9;Kernel.WriteLn &#9; &#9;&#9;END &#9;&#9; END &#9;ELSE &#9;&#9;res &#58;&#61; 0 &#9;END; &#9;RETURN res END SetRMSN; (* Check if media has changed and update it, removable only *) PROCEDURE CheckRemovable(dev&#58; Device; read&#58; BOOLEAN;  lba, num&#58; LONGINT)&#58; LONGINT; VAR port, res, code, blockSize&#58; LONGINT; status&#58; SET;  changed&#58; BOOLEAN; BEGIN &#9;port &#58;&#61; dev.controller.port; changed &#58;&#61; FALSE; &#9;IF RMSNBit IN dev.id.type THEN &#9;&#9;res &#58;&#61; GetMediaStatus(dev, status); &#9;&#9;IF res &#61; 0 THEN &#9;&#9;&#9;IF NM IN status THEN &#9;&#9;&#9;&#9;res &#58;&#61; Disks.MediaMissing &#9;&#9;&#9;ELSIF AllowEject &#38; (MCR IN status) THEN	(*remove request*) &#9;&#9;&#9;&#9;res &#58;&#61; RemoveMedium(dev); &#9;&#9;&#9;&#9;IF res &#61; 0 THEN &#9;&#9;&#9;&#9;&#9;REPEAT res &#58;&#61; GetMediaStatus(dev, status)  UNTIL  (res # 0) OR (NM IN status); &#9;&#9;&#9;&#9;&#9;dev.size &#58;&#61; 0; res &#58;&#61; Disks.MediaMissing; &#9;&#9;&#9;&#9;&#9;EXCL(dev.flags, Disks.ReadOnly) &#9;&#9;&#9;&#9;END &#9;&#9;&#9;ELSIF (MC IN status) OR (dev.size &#61; 0) THEN &#9;&#9;&#9;&#9;res &#58;&#61; ReadCapacity(dev, blockSize, dev.size); &#9;&#9;&#9;&#9;ASSERT((res # 0) OR (blockSize &#61; dev.blockSize)); &#9;&#9;&#9;&#9;IF WP IN status THEN INCL(dev.flags, Disks.ReadOnly) ELSE EXCL(dev.flags, Disks.ReadOnly) END; &#9;&#9;&#9;&#9;changed &#58;&#61; TRUE; &#9;&#9;&#9;END &#9;&#9;END &#9;ELSIF AtapiBit IN dev.id.type THEN &#9;&#9;res &#58;&#61; TestUnitReady(dev); &#9;&#9;IF res &#61; 34 THEN &#9;&#9;&#9;code &#58;&#61; dev.sense &#9;&#9;ELSIF (res &#61; 0) OR (res &#61; 32) THEN &#9;&#9;&#9;code &#58;&#61; SensePacketDevice(dev) &#9;&#9;ELSE &#9;&#9;&#9;code &#58;&#61; 0; &#9;&#9;END; &#9;&#9;IF code &#61; 0 THEN &#9;&#9;&#9;IF dev.size &#61; 0 THEN &#9;&#9;&#9;&#9;res &#58;&#61; ReadCapacity(dev, dev.blockSize, dev.size); changed &#58;&#61; TRUE; &#9;&#9;&#9;&#9;IF dev.blockSize &#61; 0 THEN dev.blockSize &#58;&#61; blockSize END &#9;&#9;&#9;END	(*first time*) &#9;&#9;ELSIF code &#61; 2800H THEN	(* medium may have changed *) &#9;&#9;&#9;res &#58;&#61; ReadCapacity(dev, blockSize, dev.size); changed &#58;&#61; TRUE; &#9;&#9;&#9;IF dev.blockSize &#61; 0 THEN dev.blockSize &#58;&#61; blockSize END &#9;&#9;ELSIF code &#61; 3A00H THEN	(* medium not present *) &#9;&#9;&#9;res &#58;&#61; Disks.MediaMissing; &#9;&#9;ELSIF code &#61; -1 THEN &#9;&#9;&#9;res &#58;&#61; Disks.MediaMissing &#9;&#9;ELSE &#9;&#9;&#9;res &#58;&#61; Disks.Unsupported; &#9;&#9;&#9;Kernel.WriteString("Check/Unknown Sense "); Kernel.WriteHex(code, 0); Kernel.WriteLn; &#9;&#9;END &#9;ELSIF CompactFlash IN dev.id.type THEN (*CF*) &#9;&#9;res &#58;&#61; Disks.Ok (*? CompactFlash treated as non removable - to be completed *) &#9;ELSE &#9;&#9;res &#58;&#61; Disks.Unsupported &#9;END; &#9; &#9;IF (res # 0) OR (num &#61; 0) THEN &#9;&#9;(*skip*) &#9;ELSIF (lba &#62;&#61; dev.size) OR (lba+num &#62; dev.size) THEN	(* out of range *) &#9;&#9;res &#58;&#61; 226 &#9;ELSIF &#126;read &#38; (WP IN status) THEN	(* write protected *) &#9;&#9;res &#58;&#61; 28 &#9;ELSIF changed THEN &#9;&#9;res &#58;&#61; Disks.MediaChanged &#9;END; &#9; &#9;RETURN res END CheckRemovable; (* Transfer blocks using DMA. *) PROCEDURE TransferDMA(d&#58; Device; read&#58; BOOLEAN;  lba, num, bufAdr&#58; LONGINT)&#58; LONGINT; VAR port, res, bmbase, i, size, left&#58; LONGINT; x&#58; CHAR;  s&#58; SET;  c&#58; Controller; BEGIN &#9;ASSERT(&#126;ODD(bufAdr));	(* transfer must be word-aligned *) &#9;ASSERT((num &#62; 0) &#38; (num &#60;&#61; MaxTransfer)); &#9;res &#58;&#61; 0; c &#58;&#61; d.controller; &#9;port &#58;&#61; c.port; bmbase &#58;&#61; c.bmbase;  ASSERT(bmbase # 0); &#9;&#9;(* init prdt *) &#9;i &#58;&#61; 0; size &#58;&#61; num*BS; &#9;REPEAT &#9;&#9;left &#58;&#61; 10000H - bufAdr MOD 10000H;	(* bytes left until next 64k boundary *) &#9;&#9;IF left &#62; size THEN left &#58;&#61; size END; &#9;&#9;c.prdt.prd&#91;i&#93;.adr &#58;&#61; bufAdr; c.prdt.prd&#91;i&#93;.count &#58;&#61; left; &#9;&#9;INC(bufAdr, left); DEC(size, left);  INC(i) &#9;UNTIL size &#61; 0; &#9;INCL(SYSTEM.VAL(SET, c.prdt.prd&#91;i-1&#93;.count), 31);	(* end marker *) &#9;&#9;(* init dma *) &#9;SYSTEM.PORTOUT(bmbase+4, SYSTEM.ADR(c.prdt.prd&#91;0&#93;)); &#9;IF read THEN s &#58;&#61; &#123;3&#125; ELSE s &#58;&#61; &#123;&#125; END; &#9;SYSTEM.PORTOUT(bmbase, SYSTEM.VAL(CHAR, s));	(* set direction *) &#9;SYSTEM.PORTIN(bmbase+2, x); &#9;s &#58;&#61; SYSTEM.VAL(SET, LONG(ORD(x))); &#9;SYSTEM.PORTOUT(bmbase+2, CHR(SYSTEM.VAL(LONGINT, s + &#123;1,2&#125;)));	(* clear error, interrupt by writing *) &#9;&#9;(* init transfer *) &#9;StartInterrupt(c); &#9;IF read THEN x &#58;&#61; 0C8X ELSE x &#58;&#61; 0CAX END;	(* read dma or write dma *) &#9;Command(d, x, lba, num); &#9;SYSTEM.PORTIN(bmbase, x); &#9;s &#58;&#61; SYSTEM.VAL(SET, LONG(ORD(x))); &#9;SYSTEM.PORTOUT(bmbase, CHR(SYSTEM.VAL(LONGINT, s + &#123;0&#125;)));	(* start bm *) &#9;IF WaitInterrupt(c, IOTimeout) THEN &#9;&#9;IF EarlyBug THEN WaitNotBusy(c.port2) END; &#9;&#9;SYSTEM.PORTIN(bmbase+2, x);	(* check dma status *) &#9;&#9;s &#58;&#61; SYSTEM.VAL(SET, LONG(ORD(x))); &#9;&#9;IF 1 IN s THEN res &#58;&#61; 8 END &#9;ELSE &#9;&#9;res &#58;&#61; 7 &#9;END; &#9;SYSTEM.PORTIN(bmbase, x); &#9;s &#58;&#61; SYSTEM.VAL(SET, LONG(ORD(x))); &#9;SYSTEM.PORTOUT(bmbase, CHR(SYSTEM.VAL(LONGINT, s - &#123;0&#125;)));	(* stop bm *) &#9;SYSTEM.PORTIN(port+7, x);	(* clear interrupt *) &#9;s &#58;&#61; SYSTEM.VAL(SET, LONG(ORD(x))); &#9;IF (res &#61; 0) &#38; (ERR IN s) THEN res &#58;&#61; 9 END; &#9;RETURN res END TransferDMA; (* Initialize/Reset the selected device. *) PROCEDURE InitDevice(port, port2, dev&#58; LONGINT; type&#58; SET;  chs&#58; CHS)&#58; LONGINT; VAR x&#58; CHAR; sector, head, res&#58; LONGINT; BEGIN &#9;IF AtapiBit IN type THEN &#9;&#9;res &#58;&#61; 0 &#9;ELSE &#9;&#9;sector &#58;&#61; chs.spt; &#9;&#9;head &#58;&#61; chs.hds; &#9;&#9;SYSTEM.PORTOUT(port+2, CHR(sector)); &#9;&#9;SYSTEM.PORTOUT(port+3, 0X); &#9;&#9;SYSTEM.PORTOUT(port+4, 0X); &#9;&#9;SYSTEM.PORTOUT(port+5, 0X); &#9;&#9;SYSTEM.PORTOUT(port+6, CHR((ASH(dev, 4) + (head-1) MOD 10H))); &#9;&#9;SYSTEM.PORTOUT(port+7, 091X);	(* initialize device parameters *) &#9;&#9;SYSTEM.PORTIN(port2, x); &#9;&#9;IF &#126;WaitStatus(port, &#123;BSY&#125;, &#123;&#125;, &#123;ERR&#125;, ResetTimeout) THEN &#9;&#9;&#9;IF LBABit IN type THEN &#9;&#9;&#9;&#9;Kernel.WriteString("ATADisks&#58; using LBA mode, init dev param ignored"); &#9;&#9;&#9;&#9;Kernel.WriteLn &#9;&#9;&#9;ELSE &#9;&#9;&#9;&#9;res &#58;&#61; 17 &#9;&#9;&#9;END &#9;&#9;END &#9;END; &#9;RETURN res END InitDevice; (* Convert a string to an integer. *) PROCEDURE StrToInt(VAR i&#58; LONGINT; VAR s&#58; ARRAY OF CHAR)&#58; LONGINT; VAR vd, vh, sgn, d&#58; LONGINT; hex&#58; BOOLEAN; BEGIN &#9;vd &#58;&#61; 0; vh &#58;&#61; 0;  hex &#58;&#61; FALSE; &#9;IF s&#91;i&#93; &#61; "-" THEN sgn &#58;&#61; -1; INC(i) ELSE sgn &#58;&#61; 1 END; &#9;LOOP &#9;&#9;IF (s&#91;i&#93; &#62;&#61; "0") &#38; (s&#91;i&#93; &#60;&#61; "9") THEN d &#58;&#61; ORD(s&#91;i&#93;)-ORD("0") &#9;&#9;ELSIF (CAP(s&#91;i&#93;) &#62;&#61; "A") &#38; (CAP(s&#91;i&#93;) &#60;&#61; "F") THEN d &#58;&#61; ORD(CAP(s&#91;i&#93;))-ORD("A")+10; hex &#58;&#61; TRUE &#9;&#9;ELSE EXIT &#9;&#9;END; &#9;&#9;vd &#58;&#61; 10*vd + d; vh &#58;&#61; 16*vh + d; &#9;&#9;INC(i) &#9;END; &#9;IF CAP(s&#91;i&#93;) &#61; "H" THEN hex &#58;&#61; TRUE; INC(i) END;	(* optional H *) &#9;IF hex THEN vd &#58;&#61; vh END; &#9;RETURN sgn * vd END StrToInt; (* Initialize controller parameters. *) PROCEDURE ParseControllerConfig(num, dirq, dc&#58; LONGINT); VAR s&#58; ARRAY 32 OF CHAR; id&#58; ARRAY 8 OF CHAR;  p, c, r, irq&#58; LONGINT; BEGIN &#9;id &#58;&#61; "IDE#"; id&#91;3&#93; &#58;&#61; CHR(ORD("0") + num); &#9;Kernel.GetConfig(id, s); &#9;IF (num &#61; 0) &#38; (s &#61; "") THEN Kernel.GetConfig("IDE", s) END; &#9;IF UseConfigParams &#38; (s # "") THEN &#9;&#9;p &#58;&#61; 0; c &#58;&#61; 0;  r &#58;&#61; 0; &#9;&#9;irq &#58;&#61; StrToInt(p, s); &#9;&#9;IF (irq # 0) &#38; (s&#91;p&#93; &#61; ",") THEN &#9;&#9;&#9;INC(p); c &#58;&#61; StrToInt(p, s); &#9;&#9;&#9;IF s&#91;p&#93; &#61; "," THEN &#9;&#9;&#9;&#9;INC(p); r &#58;&#61; StrToInt(p, s) &#9;&#9;&#9;END; &#9;&#9;&#9;IF c &#61; 0 THEN c &#58;&#61; dc END; &#9;&#9;&#9;IF (r &#61; 0) &#38; (c &#62; 0) THEN r &#58;&#61; c + 206H END &#9;&#9;END &#9;ELSE &#9;&#9;irq &#58;&#61; dirq; c &#58;&#61; dc;  r &#58;&#61; c + 206H &#9;END; &#9;IF c &#62; 0 THEN &#9;&#9;NEW(controller&#91;num&#93;); &#9;&#9;controller&#91;num&#93;.port &#58;&#61; c; controller&#91;num&#93;.port2 &#58;&#61; r;  controller&#91;num&#93;.irq &#58;&#61; irq; &#9;&#9;controller&#91;num&#93;.bmbase &#58;&#61; 0	(* BM disabled *) &#9;END END ParseControllerConfig; (* Get hard disk parameters. *) PROCEDURE GetPar(p&#58; LONGINT; ofs&#58; LONGINT)&#58; LONGINT; VAR x&#58; CHAR; BEGIN &#9;SYSTEM.GET(p+12+ofs, x); &#9;RETURN ORD(x) END GetPar; (* Parse hard disk parameters of first two drives from BIOS. *) PROCEDURE GetKernelCHS(d&#58; LONGINT; VAR chs1&#58; CHS); VAR p, t, i &#58; LONGINT; chs&#58; CHS; BEGIN &#9;p &#58;&#61; Kernel.bt; i &#58;&#61; 0; &#9;LOOP &#9;&#9;SYSTEM.GET(p, t); &#9;&#9;IF t &#61; -1 THEN EXIT	(* end *) &#9;&#9;ELSIF t &#61; 5 THEN	(* HD params *) &#9;&#9;&#9;IF i &#61; d THEN &#9;&#9;&#9;&#9;chs.cyls &#58;&#61; GetPar(p, 0) + 100H*GetPar(p, 1); &#9;&#9;&#9;&#9;chs.hds &#58;&#61; GetPar(p, 2); chs.spt &#58;&#61; GetPar(p, 14); &#9;&#9;&#9;&#9;IF chs.cyls * chs.hds * chs.spt &#62; 0 THEN chs1 &#58;&#61; chs END &#9;&#9;&#9;END; &#9;&#9;&#9;INC(i) &#9;&#9;END; &#9;&#9;SYSTEM.GET(p+4, t); INC(p, t) &#9;END END GetKernelCHS; (* Convert an ATA identify string to a readable format. *) PROCEDURE GetATAString(VAR buf&#58; ARRAY OF INTEGER; from, to&#58; LONGINT;  VAR s&#58; ARRAY OF CHAR); VAR i, j&#58; LONGINT; BEGIN &#9;FOR i &#58;&#61; from TO to DO &#9;&#9;s&#91;2*(i-from)&#93; &#58;&#61; CHR(buf&#91;i&#93; DIV 100H MOD 100H); &#9;&#9;s&#91;2*(i-from)+1&#93; &#58;&#61; CHR(buf&#91;i&#93; MOD 100H) &#9;END; &#9;s&#91;2*(to-from+1)&#93; &#58;&#61; 0X; &#9;i &#58;&#61; 0; j &#58;&#61; 0; &#9;WHILE s&#91;i&#93; # 0X DO &#9;&#9;IF (s&#91;i&#93; &#62;&#61; 20X) &#38; (s&#91;i&#93; &#60;&#61; 7EX) THEN s&#91;j&#93; &#58;&#61; s&#91;i&#93;; INC(j) END; &#9;&#9;INC(i); &#9;&#9;IF (j # 0) &#38; (s&#91;j-1&#93; &#61; 20X) THEN &#9;&#9;&#9;WHILE s&#91;i&#93; &#61; 20X DO INC(i) END &#9;&#9;END &#9;END; &#9;IF (j # 0) &#38; (s&#91;j-1&#93; &#61; 20X) THEN DEC(j) END; &#9;s&#91;j&#93; &#58;&#61; 0X END GetATAString; (* Identify an Atapi packet device. *) PROCEDURE IdentifyAtapi(port, port2&#58; LONGINT; VAR id&#58; ID)&#58; LONGINT; VAR res&#58; LONGINT; x&#58; CHAR;  buf&#58; ARRAY BS DIV 2 OF INTEGER; BEGIN &#9;SYSTEM.PORTOUT(port+7, 0A1X);	(* identify ATAPI device *) &#9;SYSTEM.PORTIN(port2, x);	(* ensure status ok *) &#9;IF WaitStatus(port, &#123;BSY&#125;, &#123;&#125;, &#123;&#125;, IdentifyTimeout) THEN &#9;&#9;INCL(id.type, AtapiBit); &#9;&#9;RepInWord(port, SYSTEM.ADR(buf&#91;0&#93;), BS DIV 2); &#9;&#9;IF 0 IN SYSTEM.VAL(SET, LONG(buf&#91;0&#93;)) THEN INCL(id.type, Packet16Bit) END; &#9;&#9;IF 7 IN SYSTEM.VAL(SET, LONG(buf&#91;0&#93;)) THEN INCL(id.type, RemovableBit) END; &#9;&#9;IF SYSTEM.VAL(SET, LONG(buf&#91;0&#93;)) * &#123;8..12&#125; &#61; &#123;8, 10&#125; THEN INCL(id.type, CDRom) END; &#9;&#9;IF 8 IN SYSTEM.VAL(SET, LONG(buf&#91;49&#93;)) THEN INCL(id.type, DMABit) END; &#9;&#9;IF SYSTEM.VAL(SET, LONG(buf&#91;127&#93;)) * &#123;0,1&#125; &#61; &#123;0&#125; THEN INCL(id.type, RMSNBit) END; &#9;&#9;IF LONG(buf&#91;0&#93;) MOD 10000H &#61; CompactFlashSignature THEN INCL(id.type, CompactFlash) END; (*CF*) &#9;&#9;GetATAString(buf, 27, 46, id.model); &#9;&#9;IF (buf&#91;80&#93; # -1) &#38; (buf&#91;81&#93; # -1) THEN &#9;&#9;&#9;id.ver &#58;&#61; ASH(LONG(buf&#91;80&#93;) MOD 10000H, 16) + LONG(buf&#91;81&#93;) MOD 10000H &#9;&#9;END; &#9;&#9;res &#58;&#61; 0 &#9;ELSE &#9;&#9;res &#58;&#61; 12 &#9;END; &#9;RETURN res END IdentifyAtapi; (* Identify an ATA device. *) PROCEDURE IdentifyATA(port&#58; LONGINT; VAR chs&#58; CHS;  VAR size&#58; LONGINT;  VAR id&#58; ID)&#58; LONGINT; VAR res, size1&#58; LONGINT; buf&#58; ARRAY BS DIV 2 OF INTEGER; BEGIN &#9;RepInWord(port, SYSTEM.ADR(buf&#91;0&#93;), BS DIV 2); &#9;(* Kernel.WriteMemory(SYSTEM.ADR(buf&#91;0&#93;), BS); *) &#9;chs.cyls &#58;&#61; LONG(buf&#91;1&#93;) MOD 10000H; &#9;chs.hds &#58;&#61; LONG(buf&#91;3&#93;) MOD 10000H; &#9;chs.spt &#58;&#61; LONG(buf&#91;6&#93;) MOD 10000H; &#9;size &#58;&#61; ASH(LONG(buf&#91;61&#93;) MOD 10000H, 16) + LONG(buf&#91;60&#93;) MOD 10000H; &#9;IF size &#62;&#61; MinLBA THEN INCL(id.type, LBABit) END; &#9;size1 &#58;&#61; chs.cyls * chs.hds * chs.spt; &#9;IF size &#60; size1 THEN size &#58;&#61; size1 END; &#9;IF SYSTEM.VAL(SET, LONG(buf&#91;0&#93;)) * &#123;6,7&#125; &#61; &#123;7&#125; THEN INCL(id.type, RemovableBit) END; &#9;IF SYSTEM.VAL(SET, LONG(buf&#91;127&#93;)) * &#123;0,1&#125; &#61; &#123;0&#125; THEN INCL(id.type, RMSNBit) END; &#9;IF LONG(buf&#91;0&#93;) MOD 10000H &#61; CompactFlashSignature THEN INCL(id.type, CompactFlash) END; (*CF*) &#9;INCL(id.type, DMABit);	(* DMA support mandatory in ATA/ATAPI-4 *) &#9;GetATAString(buf, 27, 46, id.model); &#9;IF (buf&#91;80&#93; # -1) &#38; (buf&#91;81&#93; # -1) THEN &#9;&#9;id.ver &#58;&#61; ASH(LONG(buf&#91;80&#93;) MOD 10000H, 16) + LONG(buf&#91;81&#93;) MOD 10000H &#9;END; &#9;IF (chs.hds &#60;&#61; 16) &#38; (chs.spt &#60;&#61; 255) THEN res &#58;&#61; 0 ELSE res &#58;&#61; 25 END; &#9;RETURN res END IdentifyATA; (* Identify a device. *) PROCEDURE IdentifyDevice(controller&#58; Controller; dev&#58; LONGINT;  VAR chs&#58; CHS;  VAR size&#58; LONGINT;  VAR id&#58; ID)&#58; LONGINT; VAR res, port, port2&#58; LONGINT; x&#58; CHAR;  t&#58; Kernel.MilliTimer;  zip&#58; ARRAY 12 OF CHAR; BEGIN &#9;id.type &#58;&#61; &#123;&#125;; id.ver &#58;&#61; 0;  chs.cyls &#58;&#61; 0;  chs.hds &#58;&#61; 0;  chs.spt &#58;&#61; 0;  size &#58;&#61; 0; &#9;port &#58;&#61; controller.port; port2 &#58;&#61; controller.port2; &#9;IF port # 0 THEN &#9;&#9;Kernel.SetTimer(t, 50); REPEAT UNTIL Kernel.Expired(t); &#9;&#9;res &#58;&#61; SelectDevice(port, dev, IdentifyTimeout); &#9;&#9;IF res &#61; 0 THEN &#9;&#9;&#9;StartInterrupt(controller); &#9;&#9;&#9;SYSTEM.PORTOUT(port+7, 0ECX);	(* identify device *) &#9;&#9;&#9;IF WaitInterrupt(controller, IdentifyTimeout) THEN &#9;&#9;&#9;&#9;IF EarlyBug THEN WaitNotBusy(controller.port2) END; &#9;&#9;&#9;&#9;SYSTEM.PORTIN(port+5, x);	(* signature byte *) &#9;&#9;&#9;&#9;IF x &#61; 0EBX THEN &#9;&#9;&#9;&#9;&#9;res &#58;&#61; IdentifyAtapi(port, port2, id); &#9;&#9;&#9;&#9;&#9;&#9;(* hack to make bootable zips (should write code to detect geometry) *) &#9;&#9;&#9;&#9;&#9;COPY(id.model, zip); zip&#91;10&#93; &#58;&#61; 0X; &#9;&#9;&#9;&#9;&#9;IF zip &#61; "IOMEGA ZIP" THEN &#9;&#9;&#9;&#9;&#9;&#9;chs.hds &#58;&#61; 40H; chs.spt &#58;&#61; 20H; &#9;&#9;&#9;&#9;&#9;&#9;IF id.model&#91;11&#93; &#61; "2" THEN chs.cyls &#58;&#61; 239 ELSE chs.cyls &#58;&#61; 96 END; &#9;&#9;&#9;&#9;&#9;END &#9;&#9;&#9;&#9;ELSE &#9;&#9;&#9;&#9;&#9;res &#58;&#61; IdentifyATA(port, chs, size, id); &#9;&#9;&#9;&#9;&#9;IF (res &#61; 0) &#38; (size &#61; 0) THEN res &#58;&#61; 13 END &#9;&#9;&#9;&#9;END &#9;&#9;&#9;ELSE &#9;&#9;&#9;&#9;res &#58;&#61; 14 &#9;&#9;&#9;END &#9;&#9;END; &#9;&#9;Kernel.SetTimer(t, 50); REPEAT UNTIL Kernel.Expired(t); &#9;&#9;SYSTEM.PORTOUT(port+6, 0X);	(* select device 0 again *) &#9;&#9;NanoDelay(400) &#9;ELSE &#9;&#9;res &#58;&#61; 15 &#9;END; &#9;RETURN res END IdentifyDevice; (* Clean up unloaded module. *) PROCEDURE Cleanup; VAR i&#58; LONGINT; d&#58; Device; BEGIN &#9;IF Kernel.shutdown &#61; 0 THEN &#9;&#9;FOR i &#58;&#61; 0 TO MaxDevices-1 DO &#9;&#9;&#9;d &#58;&#61; device&#91;i&#93;; &#9;&#9;&#9;IF d # NIL THEN Disks.Unregister(d) END &#9;&#9;END; &#9;&#9;FOR i &#58;&#61; 0 TO 15 DO &#9;&#9;&#9;IF i IN irqs THEN &#9;&#9;&#9;&#9;EXCL(irqs, i); &#9;&#9;&#9;&#9;Kernel.RemoveIP(InterruptHandler, SHORT(Kernel.IRQ + i)) &#9;&#9;&#9;END &#9;&#9;END &#9;END END Cleanup; (* Initialization. *) PROCEDURE IdentifyControllers; VAR i&#58; LONGINT; BEGIN &#9;FOR i &#58;&#61; 0 TO MaxControllers-1 DO controller&#91;i&#93; &#58;&#61; NIL END; &#9;IF PrimaryEnabled THEN ParseControllerConfig(0, 14, 01F0H) END; &#9;IF SecondaryEnabled THEN ParseControllerConfig(1, 15, 0170H) END; &#9;IF MaxControllers &#62; 2 THEN	(* compile-time expression *) &#9;&#9;FOR i &#58;&#61; 2 TO MaxControllers-1 DO ParseControllerConfig(i, 0, 0) END &#9;END END IdentifyControllers; PROCEDURE ResetController(ctrl&#58; Controller)&#58; BOOLEAN; VAR t&#58; Kernel.MilliTimer; BEGIN &#9;SYSTEM.PORTOUT(ctrl.port2, 4X);	(* reset controller *) &#9;Kernel.SetTimer(t, 1); REPEAT UNTIL Kernel.Expired(t);	(* wait &#62; 4.8us *) &#9;SYSTEM.PORTOUT(ctrl.port2, 8X); &#9;RETURN WaitStatus(ctrl.port, &#123;BSY&#125;, &#123;&#125;, &#123;&#125;, ResetTimeout) END ResetController; PROCEDURE IdentifyDevices; VAR &#9;p, q, a, n, res, size, tmp&#58; LONGINT; ctrl&#58; Controller;  dev&#58; Device;  chs&#58; CHS;  id&#58; ID;  x&#58; CHAR; &#9;name&#58; Disks.Name; s&#58; ARRAY 16 OF CHAR;  useBIOS&#58; BOOLEAN; BEGIN &#9;Kernel.GetConfig("ATABIOS", s); &#9;useBIOS &#58;&#61; s&#91;0&#93; &#61; "1"; &#9;IF useBIOS THEN &#9;&#9;Kernel.WriteString("Using BIOS geometry"); Kernel.WriteLn &#9;END; &#9;&#9;(* identify all devices *) &#9;a &#58;&#61; 0; n &#58;&#61; 0; &#9;FOR p &#58;&#61; 0 TO MaxDevices-1 DO &#9;&#9;ctrl &#58;&#61; controller&#91;p DIV 2&#93;; &#9;&#9;IF ctrl # NIL THEN &#9;&#9;&#9;name &#58;&#61; "IDEx"; name&#91;3&#93; &#58;&#61; CHR(48 + p); &#9;&#9;&#9;res &#58;&#61; IdentifyDevice(ctrl, p MOD 2, chs, size, id); &#9;&#9;&#9;IF res &#61; 0 THEN &#9;&#9;&#9;&#9;NEW(dev); Disks.InitDevice(dev, name); &#9;&#9;&#9;&#9;dev.controller &#58;&#61; ctrl; dev.dev &#58;&#61; p MOD 2;  dev.init &#58;&#61; FALSE; &#9;&#9;&#9;&#9;dev.id &#58;&#61; id; dev.size &#58;&#61; size;  dev.chs &#58;&#61; chs;  dev.getpar &#58;&#61; dev.chs; &#9;&#9;&#9;&#9;COPY(id.model, dev.desc); &#9;&#9;&#9;&#9;IF AtapiBit IN id.type THEN &#9;&#9;&#9;&#9;&#9;SYSTEM.PORTOUT(ctrl.port+6, CHR(ASH(dev.dev, 4))); &#9;&#9;&#9;&#9;&#9;SYSTEM.PORTOUT(ctrl.port+7, 8X);	(* reset packet device *) &#9;&#9;&#9;&#9;&#9;SYSTEM.PORTIN(ctrl.port2, x); &#9;&#9;&#9;&#9;&#9;IF &#126;WaitStatus(ctrl.port, &#123;BSY&#125;, &#123;&#125;, &#123;&#125;, ResetTimeout) THEN res &#58;&#61; 16 END; &#9;&#9;&#9;&#9;&#9;res &#58;&#61; TestUnitReady(dev); &#9;&#9;&#9;&#9;&#9;IF res # 0 THEN tmp &#58;&#61; SensePacketDevice(dev); res &#58;&#61; 0 END; &#9;&#9;&#9;&#9;&#9;IF CDRom IN id.type THEN &#9;&#9;&#9;&#9;&#9;&#9;(*SetCDSpeed(dev, 0FFFFH, 0FFFFH);*)	(* use the maximal speed *) &#9;&#9;&#9;&#9;&#9;&#9;dev.blockSize &#58;&#61; CDRomBS	(* ATAPI/CDRom shall all use this size *) &#9;&#9;&#9;&#9;&#9;ELSE res &#58;&#61; GetModeInfo(dev, dev.blockSize)	(* detect block size *) &#9;&#9;&#9;&#9;&#9;END; &#9;&#9;&#9;&#9;&#9;IF (res &#61; 0) &#38; &#126;(RemovableBit IN dev.id.type) THEN res &#58;&#61; ReadCapacity(dev, tmp, dev.size)  END &#9;&#9;&#9;&#9;ELSE &#9;&#9;&#9;&#9;&#9;dev.blockSize &#58;&#61; BS; &#9;&#9;&#9;&#9;&#9;IF (p &#60; 2) &#38; useBIOS THEN GetKernelCHS(p, dev.getpar) END;	(* override CHS *) &#9;&#9;&#9;&#9;&#9;IF dev.size &#62; 16383*16*63 THEN &#9;&#9;&#9;&#9;&#9;&#9;dev.getpar.cyls &#58;&#61; dev.size DIV (dev.getpar.hds * dev.getpar.spt) &#9;&#9;&#9;&#9;&#9;END &#9;&#9;&#9;&#9;END; &#9;&#9;&#9;&#9;device&#91;q&#93; &#58;&#61; dev; INC(q); &#9;&#9;&#9;&#9;IF (res &#61; 0) &#38; (RMSNBit IN id.type) THEN &#9;&#9;&#9;&#9;&#9;res &#58;&#61; SetRMSN(dev, TRUE); &#9;&#9;&#9;&#9;&#9;(*IF res # 0 THEN EXCL(dev.id.type, RMSNBit) END*) &#9;&#9;&#9;&#9;END &#9;&#9;&#9;ELSE (* skip *) &#9;&#9;&#9;END; &#9;&#9;&#9;IF TraceIdentify THEN &#9;&#9;&#9;&#9;Kernel.WriteString(name); Kernel.WriteString("&#58; Identify &#61; "); &#9;&#9;&#9;&#9;Kernel.WriteInt(res, 1); Kernel.WriteLn &#9;&#9;&#9;END; &#9;&#9;&#9;IF &#126;ResetController(ctrl) THEN &#9;&#9;&#9;&#9;Kernel.WriteString(name); Kernel.WriteString("&#58; reset failed");  Kernel.WriteLn &#9;&#9;&#9;ELSIF TraceIdentify THEN &#9;&#9;&#9;&#9;Kernel.WriteString(name); Kernel.WriteString("&#58; reset ok");  Kernel.WriteLn &#9;&#9;&#9;END &#9;&#9;END &#9;END; &#9;WHILE q # MaxDevices DO device&#91;q&#93; &#58;&#61; NIL; INC(q) END END IdentifyDevices; PROCEDURE ShowCHS(chs&#58; CHS); BEGIN &#9;Kernel.WriteInt(chs.cyls, 1); &#9;Kernel.WriteChar("*"); &#9;Kernel.WriteInt(chs.hds, 1); &#9;Kernel.WriteChar("*"); &#9;Kernel.WriteInt(chs.spt, 1) END ShowCHS; PROCEDURE ShowDevices; VAR i, j&#58; LONGINT; dev&#58; Device; BEGIN &#9;FOR i &#58;&#61; 0 TO MaxDevices-1 DO &#9;&#9;dev &#58;&#61; device&#91;i&#93;; &#9;&#9;IF dev # NIL THEN &#9;&#9;&#9;j &#58;&#61; 0; WHILE controller&#91;j&#93; # dev.controller DO INC(j) END; &#9;&#9;&#9;Kernel.WriteString("IDE"); Kernel.WriteInt(j*2 + dev.dev, 1); &#9;&#9;&#9;IF AtapiBit IN dev.id.type THEN &#9;&#9;&#9;&#9;Kernel.WriteString(", packet"); &#9;&#9;&#9;&#9;IF Packet16Bit IN dev.id.type THEN Kernel.WriteString("16") ELSE Kernel.WriteString("12") END; &#9;&#9;&#9;ELSE &#9;&#9;&#9;&#9;Kernel.WriteString(", "); Kernel.WriteInt(dev.size DIV 2048, 1);  Kernel.WriteString("MB"); &#9;&#9;&#9;&#9;Kernel.WriteString(", "); ShowCHS(dev.chs); &#9;&#9;&#9;&#9;IF (dev.getpar.cyls # dev.chs.cyls) OR (dev.getpar.hds # dev.chs.hds) OR (dev.getpar.spt # dev.chs.spt) THEN &#9;&#9;&#9;&#9;&#9;Kernel.WriteString(", ("); ShowCHS(dev.getpar);  Kernel.WriteChar(")") &#9;&#9;&#9;&#9;END &#9;&#9;&#9;END; &#9;&#9;&#9;IF RemovableBit IN dev.id.type THEN Kernel.WriteString(", removable") END; &#9;&#9;&#9;IF CompactFlash IN dev.id.type THEN Kernel.WriteString(", CompactFlash") END; (*CF*) &#9;&#9;&#9;IF RMSNBit IN dev.id.type THEN Kernel.WriteString(", RMSN") END; &#9;&#9;&#9;IF &#126;(DMABit IN dev.id.type) THEN Kernel.WriteString(", no DMA") END; &#9;&#9;&#9;IF LBABit IN dev.id.type THEN Kernel.WriteString(", LBA") END; &#9;&#9;&#9;Kernel.WriteString(", "); Kernel.WriteString(dev.id.model); &#9;&#9;&#9;IF dev.id.ver # 0 THEN &#9;&#9;&#9;&#9;Kernel.WriteString(", ver "); &#9;&#9;&#9;&#9;j &#58;&#61; 30; WHILE (j # 16) &#38; &#126;ODD(ASH(dev.id.ver, -j)) DO DEC(j) END; &#9;&#9;&#9;&#9;Kernel.WriteInt(j-16, 1); Kernel.WriteChar("."); &#9;&#9;&#9;&#9;Kernel.WriteInt(dev.id.ver MOD 10000H, 1) &#9;&#9;&#9;END; &#9;&#9;&#9;Kernel.WriteLn &#9;&#9;END &#9;END END ShowDevices; PROCEDURE InitControllers; VAR i&#58; LONGINT; ctrl&#58; Controller; BEGIN &#9;FOR i &#58;&#61; 0 TO MaxControllers-1 DO &#9;&#9;ctrl &#58;&#61; controller&#91;i&#93;; &#9;&#9;Kernel.WriteString("IDE"); Kernel.WriteInt(i*2, 1); &#9;&#9;Kernel.WriteString(".."); Kernel.WriteInt(i*2+1, 1);  Kernel.WriteString("&#58; "); &#9;&#9;IF ctrl # NIL THEN &#9;&#9;&#9;Kernel.WriteInt(ctrl.irq, 1); Kernel.WriteHex(ctrl.port, 9);  Kernel.WriteHex(ctrl.port2, 9); &#9;&#9;&#9;Kernel.WriteChar(" "); &#9;&#9;&#9;IF ResetController(ctrl) THEN &#9;&#9;&#9;&#9;Kernel.WriteString("reset ok"); Kernel.WriteLn; &#9;&#9;&#9;&#9;IF &#126;(ctrl.irq IN irqs) THEN &#9;&#9;&#9;&#9;&#9;Kernel.InstallIP(InterruptHandler, SHORT(Kernel.IRQ + ctrl.irq)); &#9;&#9;&#9;&#9;&#9;INCL(irqs, ctrl.irq) &#9;&#9;&#9;&#9;END &#9;&#9;&#9;ELSE &#9;&#9;&#9;&#9;controller&#91;i&#93; &#58;&#61; NIL; &#9;&#9;&#9;&#9;IF &#126;TraceStatus THEN &#9;&#9;&#9;&#9;&#9;Kernel.WriteString("reset failed"); Kernel.WriteLn &#9;&#9;&#9;&#9;END &#9;&#9;&#9;END &#9;&#9;ELSE &#9;&#9;&#9;Kernel.WriteString("disabled"); Kernel.WriteLn &#9;&#9;END &#9;END END InitControllers; PROCEDURE InitBusMaster; VAR bus, dev, fkt, i, bmbase, iobase&#58; LONGINT; s&#58; SET;  str&#58; ARRAY 8 OF CHAR; &#9;PROCEDURE FindDevice(id, vendor&#58; LONGINT)&#58; BOOLEAN;	(* set bus, dev, fkt as side-effect *) &#9;VAR res&#58; LONGINT; &#9;BEGIN &#9;&#9;res &#58;&#61; PCI.FindPCIDevice(id, vendor, 0, bus, dev, fkt); &#9;&#9;RETURN res &#61; PCI.Done &#9;END FindDevice; &#9; &#9;PROCEDURE ReadDWord(adr&#58; LONGINT; VAR s&#58; SET); &#9;VAR res&#58; LONGINT; &#9;BEGIN &#9;&#9;res &#58;&#61; PCI.ReadConfigDword(bus, dev, fkt, adr, SYSTEM.VAL(LONGINT, s)); &#9;&#9;IF res # PCI.Done THEN s &#58;&#61; &#123;&#125; END &#9;END ReadDWord; &#9;PROCEDURE WriteByte(adr&#58; LONGINT; s&#58; SET); &#9;VAR res&#58; LONGINT; &#9;BEGIN &#9;&#9;res &#58;&#61; PCI.WriteConfigByte(bus, dev, fkt, adr, SYSTEM.VAL(LONGINT, s)); &#9;&#9;(* ignore res *) &#9;END WriteByte; BEGIN &#9;IF BMEnabled THEN &#9;&#9;Kernel.GetConfig("ATABM", str); &#9;&#9;IF str&#91;0&#93; # "0" THEN &#9;&#9;&#9;IF FindDevice(7010H, 8086H) OR FindDevice(7111H, 8086H) OR &#9;&#9;&#9;&#9;FindDevice(2411H, 8086H) OR FindDevice(2421H, 8086H) THEN &#9;&#9;&#9;&#9;ReadDWord(PCI.CmdReg, s); &#9;&#9;&#9;&#9;IF OverrideBIOS &#38; (s * &#123;0,2&#125; &#61; &#123;0&#125;) THEN	(* override BIOS-disabled BM setting *) &#9;&#9;&#9;&#9;&#9;Kernel.WriteString("Overriding BIOS bus-master setting"); Kernel.WriteLn; &#9;&#9;&#9;&#9;&#9;WriteByte(PCI.CmdReg, s + &#123;2&#125;); &#9;&#9;&#9;&#9;&#9;ReadDWord(PCI.CmdReg, s) &#9;&#9;&#9;&#9;END; &#9;&#9;&#9;&#9;IF s * &#123;0,2&#125; &#61; &#123;0,2&#125; THEN	(* ports &#38; BM enabled *) &#9;&#9;&#9;&#9;&#9;ReadDWord(20H, s);	(* BMIBA *) &#9;&#9;&#9;&#9;&#9;bmbase &#58;&#61; SYSTEM.VAL(LONGINT, s * &#123;4..15&#125;); &#9;&#9;&#9;&#9;&#9;IF bmbase # 0 THEN &#9;&#9;&#9;&#9;&#9;&#9;ReadDWord(40H, s);	(* IDETIM *) &#9;&#9;&#9;&#9;&#9;&#9;FOR i &#58;&#61; 0 TO 1 DO	(* primary and secondary controller *) &#9;&#9;&#9;&#9;&#9;&#9;&#9;IF i &#61; 0 THEN iobase &#58;&#61; 1F0H ELSE iobase &#58;&#61; 170H END;	(* standard ports *) &#9;&#9;&#9;&#9;&#9;&#9;&#9;IF (15 IN s) &#38; (controller&#91;i&#93; # NIL) &#38; (controller&#91;i&#93;.port &#61; iobase) THEN &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;Kernel.WriteString("IDE"); Kernel.WriteInt(i*2, 1); &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;Kernel.WriteString(".."); Kernel.WriteInt(i*2+1, 1); &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;Kernel.WriteString("&#58; Bus-master enabled"); Kernel.WriteLn; &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;controller&#91;i&#93;.bmbase &#58;&#61; bmbase + 8*i; &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;NEW(controller&#91;i&#93;.prdt) &#9;&#9;&#9;&#9;&#9;&#9;&#9;END; &#9;&#9;&#9;&#9;&#9;&#9;&#9;s &#58;&#61; SYSTEM.LSH(s, -16) &#9;&#9;&#9;&#9;&#9;&#9;END &#9;&#9;&#9;&#9;&#9;ELSE &#9;&#9;&#9;&#9;&#9;&#9;Kernel.WriteString("Bus-master ports disabled (BIOS)"); Kernel.WriteLn &#9;&#9;&#9;&#9;&#9;END &#9;&#9;&#9;&#9;ELSE &#9;&#9;&#9;&#9;&#9;Kernel.WriteString("Bus-master disabled"); Kernel.WriteLn &#9;&#9;&#9;&#9;END &#9;&#9;&#9;ELSE &#9;&#9;&#9;&#9; (* Kernel.WriteString("Bus-master chipset not detected"); Kernel.WriteLn *) &#9;&#9;&#9;END &#9;&#9;END &#9;END END InitBusMaster; (* Main transfer procedure. *) PROCEDURE Transfer(d&#58; Disks.Device; op, lba, num&#58; LONGINT;  VAR data&#58; ARRAY OF CHAR;  ofs&#58; LONGINT;  VAR res&#58; LONGINT); VAR dev&#58; Device; ctrl&#58; Controller;  num1, try, bufAdr&#58; LONGINT;  pkt&#58; Packet16;  read&#58; BOOLEAN; BEGIN &#9;IF TraceCalls THEN Kernel.WriteString(d.name); Kernel.WriteString(" Transfer"); Kernel.WriteLn END; &#9;IF (op &#61; Disks.Read) OR (op &#61; Disks.Write) THEN &#9;&#9;read &#58;&#61; op &#61; Disks.Read; &#9;&#9;dev &#58;&#61; d(Device); bufAdr &#58;&#61; SYSTEM.ADR(data&#91;ofs&#93;); &#9;&#9;IF dev # NIL THEN &#9;&#9;&#9;IF (lba &#62;&#61; 0) &#38; (num &#62;&#61; 0) &#38; ((lba &#60; dev.size) &#38; (lba+num &#60;&#61; dev.size) OR (RemovableBit IN dev.id.type) ) THEN &#9;&#9;&#9;&#9;ASSERT(num*dev.blockSize &#60;&#61; LEN(data)-ofs);	(* range check *) &#9;&#9;&#9;&#9;ctrl &#58;&#61; dev.controller; &#9;&#9;&#9;&#9;SYSTEM.CLI; &#9;&#9;&#9;&#9;IF ctrl.busy THEN SYSTEM.STI; SYSTEM.HALT(17) END; &#9;&#9;&#9;&#9;ctrl.busy &#58;&#61; TRUE; &#9;&#9;&#9;&#9;SYSTEM.STI; &#9;&#9;&#9;&#9;res &#58;&#61; SelectDevice(ctrl.port, dev.dev, SelectTimeout); &#9;&#9;&#9;&#9;IF (res &#61; 0) &#38; &#126;dev.init THEN	(* initialize *) &#9;&#9;&#9;&#9;&#9;res &#58;&#61; InitDevice(ctrl.port, ctrl.port2, dev.dev, dev.id.type, dev.chs); &#9;&#9;&#9;&#9;&#9;dev.init &#58;&#61; res &#61; 0 &#9;&#9;&#9;&#9;END; &#9; &#9;&#9;&#9;IF (res &#61; 0) &#38; (RemovableBit IN dev.id.type) THEN res &#58;&#61; CheckRemovable (dev, read, lba, num) END; &#9;&#9;&#9;&#9;WHILE (res &#61; 0) &#38; (num &#62; 0) DO &#9;&#9;&#9;&#9;&#9;try &#58;&#61; MaxTries; num1 &#58;&#61; MaxTransfer; &#9;&#9;&#9;&#9;&#9;IF num1 &#62; num THEN num1 &#58;&#61; num END; &#9;&#9;&#9;&#9;&#9;REPEAT &#9;&#9;&#9;&#9;&#9;&#9;IF AtapiBit IN dev.id.type THEN &#9;&#9;&#9;&#9;&#9;&#9;&#9;IF read THEN ComposePacket(pkt, 28X, lba, num) &#9;&#9;&#9;&#9;&#9;&#9;&#9;ELSE ComposePacket(pkt, 2AX, lba, num) &#9;&#9;&#9;&#9;&#9;&#9;&#9;END; &#9; &#9;&#9;&#9;&#9;&#9;&#9;res &#58;&#61; TransferPacket(dev, pkt, read, bufAdr, num* dev.blockSize ) &#9;&#9;&#9;&#9;&#9;&#9;ELSE &#9;&#9;&#9;&#9;&#9;&#9;&#9;IF (ctrl.bmbase # 0) &#38; &#126;ODD(bufAdr) THEN &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;res &#58;&#61; TransferDMA(dev, read, lba, num1, bufAdr) &#9;&#9;&#9;&#9;&#9;&#9;&#9;ELSE &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;IF read THEN res &#58;&#61; ReadPIO(dev, lba, num1, bufAdr) &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;ELSE res &#58;&#61; WritePIO(dev, lba, num1, bufAdr) &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;END &#9;&#9;&#9;&#9;&#9;&#9;&#9;END &#9;&#9;&#9;&#9;&#9;&#9;END; &#9;&#9;&#9;&#9;&#9;&#9;DEC(try) &#9;&#9;&#9;&#9;&#9;UNTIL (res &#61; 0) OR (try &#61; 0); &#9;&#9;&#9;&#9;&#9;INC(lba, num1); DEC(num, num1);  INC(bufAdr, BS*num1) &#9;&#9;&#9;&#9;END; &#9;&#9;&#9;&#9;ctrl.busy &#58;&#61; FALSE &#9;&#9;&#9;ELSE &#9;&#9;&#9;&#9;res &#58;&#61; 26 &#9;&#9;&#9;END &#9;&#9;ELSE &#9;&#9;&#9;res &#58;&#61; 11 &#9;&#9;END &#9;ELSE &#9;&#9;res &#58;&#61; Disks.Unsupported &#9;END END Transfer; &#9; PROCEDURE GetSize(d&#58; Disks.Device; VAR size, res&#58; LONGINT); VAR dev&#58; Device; BEGIN &#9;dev &#58;&#61; d(Device); res &#58;&#61; 0 ; &#9;IF TraceCalls THEN Kernel.WriteString(d.name); Kernel.WriteString(" GetSize"); Kernel.WriteLn END; &#9;IF RemovableBit IN dev.id.type THEN &#9;&#9;res &#58;&#61; CheckRemovable(dev, FALSE, 0, 0); &#9;&#9;IF res &#61; Disks.MediaChanged THEN res &#58;&#61; 0 END &#9;END; &#9;IF res # 0 THEN &#9;&#9; size &#58;&#61; 0 &#9;ELSIF AtapiBit IN dev.id.type THEN &#9;&#9;size &#58;&#61; dev.size; &#9;&#9;res &#58;&#61; 0 &#9;ELSE &#9;&#9;size &#58;&#61; dev.getpar.cyls*dev.getpar.hds*dev.getpar.spt; &#9;&#9;res &#58;&#61; 0 &#9;END END GetSize; PROCEDURE Handle(d&#58; Disks.Device; VAR msg&#58; Disks.Message;  VAR res&#58; LONGINT); VAR dev&#58; Device; BEGIN &#9;dev &#58;&#61; d(Device); &#9;IF msg IS Disks.GetGeometryMsg THEN &#9; &#9;IF TraceCalls THEN Kernel.WriteString(d.name); Kernel.WriteString(" Handle/GetGeometry"); Kernel.WriteLn END; &#9;&#9;IF dev.getpar.spt # 0 THEN &#9;&#9;&#9;WITH msg&#58; Disks.GetGeometryMsg DO &#9;&#9;&#9;&#9;msg.cyls &#58;&#61; dev.getpar.cyls; msg.hds &#58;&#61; dev.getpar.hds;  msg.spt &#58;&#61; dev.getpar.spt; &#9;&#9;&#9;&#9;res &#58;&#61; Disks.Ok &#9;&#9;&#9;END &#9;&#9;ELSE &#9;&#9;&#9;res &#58;&#61; Disks.Unsupported &#9;&#9;END &#9;ELSIF msg IS Disks.LockMsg THEN &#9;&#9;IF TraceCalls THEN Kernel.WriteString(d.name); Kernel.WriteString(" Handle/Lock"); Kernel.WriteLn END; &#9;&#9;IF (RemovableBit IN dev.id.type) THEN &#9;&#9;&#9;res &#58;&#61; LockMedium(dev, TRUE) &#9;&#9;ELSE &#9;&#9;&#9;res &#58;&#61; Disks.Unsupported &#9;&#9;END &#9;ELSIF msg IS Disks.UnlockMsg THEN &#9;&#9;IF TraceCalls THEN Kernel.WriteString(d.name); Kernel.WriteString(" Handle/Unlock"); Kernel.WriteLn END; &#9;&#9;IF (RemovableBit IN dev.id.type) THEN &#9;&#9;&#9;res &#58;&#61; LockMedium(dev, FALSE) &#9;&#9;ELSE &#9;&#9;&#9;res &#58;&#61; Disks.Unsupported &#9;&#9;END &#9;ELSIF msg IS Disks.EjectMsg THEN &#9;&#9;IF TraceCalls THEN Kernel.WriteString(d.name); Kernel.WriteString(" Handle/Eject"); Kernel.WriteLn END; &#9;&#9;IF (RemovableBit IN dev.id.type) THEN &#9;&#9;&#9;res &#58;&#61; RemoveMedium(dev) &#9;&#9;ELSE &#9;&#9;&#9;res &#58;&#61; Disks.Unsupported &#9;&#9;END &#9;ELSE &#9;&#9;IF TraceCalls THEN Kernel.WriteString(d.name); Kernel.WriteString(" Handle/others"); Kernel.WriteLn END; &#9;&#9;res &#58;&#61; Disks.Unsupported &#9;END END Handle; PROCEDURE Register; VAR i&#58; LONGINT; dev&#58; Device; BEGIN &#9;FOR i &#58;&#61; 0 TO MaxDevices-1 DO &#9;&#9;dev &#58;&#61; device&#91;i&#93;; &#9;&#9;IF dev # NIL THEN &#9;&#9;&#9;dev.flags &#58;&#61; &#123;&#125;; &#9;&#9;&#9;IF RemovableBit IN dev.id.type THEN INCL(dev.flags, Disks.Removable) END; &#9;&#9;&#9;dev.transfer &#58;&#61; Transfer; dev.getSize &#58;&#61; GetSize;  dev.handle &#58;&#61; Handle; &#9;&#9;&#9;Disks.Register(dev) &#9;&#9;END &#9;END END Register; (** The install command has no effect, as all ATA devices are installed when the module is loaded. *) PROCEDURE Install*; END Install; BEGIN &#9;Kernel.WriteString("ATADisks 08.12.2000 by cp, pjm, prk"); Kernel.WriteLn; &#9;irqs &#58;&#61; &#123;&#125;; Kernel.InstallTermHandler(Cleanup); &#9;IdentifyControllers; &#9;InitControllers; &#9;IdentifyDevices; &#9;ShowDevices; &#9; (*InitControllers*) ;	(* re-initialize controllers after identify *) &#9; InitBusMaster; &#9;Register END ATADisks. Error codes&#58; 1	device select failed before issueing 2	device select failed after issueing 3	pio read failed 4	pio read error 5	pio write failed 6	pio write error 7	dma transfer timeout 8	dma transfer failed 9	dma transfer error 10	controller re-entered 11	device does not exist 12	identify atapi failed 13	device size is 0 14	identify failed 15	bad controller port 16	atapi reset failed 17	ata set parameters failed 18	 19	pio read timeout 20	pio read error 21	pio read error 22	pio write error 23	pio write timeout 24	pio write error 25	identify ata geometry bad 26	transfer out of range 27	no media 28	media write protected 29	media unlock failed 30	sync cache failed 31	packet command failed 32	transfer packet error (did not complete) 33	transfer failed (no sense data available) 34	transfer failed (sense data available) 35	rmsn failed (not supported) 36	read capacity packet failed 37	unit attention failed 38	get status failed 39	remove ata medium failed ATADisks.Install	System.Free ATADisks &#126; ATAErrors.Text to do&#58; o check selected device o atapi identify with interrupt o set ReadOnly bit in device.flags depending on removable media status for non RMSN devices