Oberon/A2/BIOS.V24.Mod

MODULE V24; (** AUTHOR "AFI"; PURPOSE "V24/RS-232 driver" *) (** Supports a maximum of 8 COM serial ports at speeds up to 115&#39;200 BPS. &#9;No longer compatible with ETH Native Oberon. &#9;The I/O base address and the IRQ corresponding to each COM port must be &#9;declared in Aos.Par, except that COM1 and COM2 are declared by default &#9;with their standard values &#9;&#9;COM1&#61;"3F8H,4" &#9;&#9;COM2&#61;"2F8H,3" &#9;and must be specified if these values do not apply to a particular machine. &#9;Bluebottle operates in 32-bit addressing mode and cannot interrogate &#9;the base address by accessing the port directly in BIOS. &#9;The ports are numbered in the order of appeareance in Aos.Par, starting from 0 &#9;and are named logically starting from COM1. &#9;Includes a facility to determine the UART type and a facility to trace the data. &#9;References&#58; &#9;&#9;&#9;Serial and UART Tutorial by Frank Durda &#9;&#9;&#9;"http&#58;//freebsd.org/doc/en_US.ISO8859-1/articles/serial-uart" &#9;&#9;&#9;"http&#58;//www.lammertbies.nl/comm/info/RS-232_uart.html" &#42; &#42;&#32;History&#58; &#42; &#42;&#9;14.06.2006	Adapted to changes in Serials.Mod (staubesv) &#42;&#9;26.06.2006	ClearMC, SetMC &#38; GetMC procedure bodies made exclusive, performance counters implemented (staubesv) &#42;) IMPORT SYSTEM, Objects, Machine, Streams, Commands, KernelLog, Serials; CONST &#9;MaxPortNo &#61; 8;	(* Up to 8 serial ports supported *) &#9;BufSize &#61; 1024; &#9;(* Port registers *) &#9;(* RBR &#61; 0;	 Select with DLAB &#61; 0 - Receive Buffer Register - read only &#9;&#9;&#9;&#9;&#9;&#9;&#9;Select with DLAB &#61; 1 - Baud Rate Divisor LSB *) &#9;IER &#61; 1;	(* Select with DLAB &#61; 0 - Interrupt Enable Register - R/W &#9;&#9;&#9;&#9;&#9;&#9;&#32;Select with DLAB &#61; 1 - Baud Rate Divisor MSB *) &#9;IIR &#61; 2;	(* Interrupt Identification Register - read only *) &#9;FCR &#61; 2;	(* 16550 FIFO Control Register write only *) &#9;LCR &#61; 3;	(* Line Control Register - R/W *) &#9;MCR &#61; 4;	(* Modem Control Register -  R/W *) &#9;LSR &#61; 5;	(* Line Status Register -  read only*) &#9;MSR &#61; 6;	(* Modem Status Register - R/W *) &#9;SCR &#61; 7;	(* Scratch Register - R/W *) &#9;(** Modem control lines *) &#9;DTR* &#61; 0;  RTS* &#61; 1;	(** output *) &#9;Break* &#61; 2;	(** input/output - Bit 6 in LCR *) &#9;DSR* &#61; 3;  CTS* &#61; 4;  RI* &#61; 5;  DCD* &#61; 6;	(** input *) &#9;ModuleName &#61; "V24"; &#9;Verbose &#61; TRUE; TYPE &#9;RS232Port &#61; OBJECT (Serials.Port); &#9;&#9;VAR &#9;&#9;&#9;baseaddr, irq, maxbps&#58; SIGNED32; &#9;&#9;&#9;buf&#58; ARRAY BufSize OF CHAR; &#9;&#9;&#9;head, tail&#58; SIGNED32; &#9;&#9;&#9;open, ox16&#58; BOOLEAN; &#9;&#9;&#9;diagnostic&#58; SIGNED32; &#9;&#9;PROCEDURE &#38;Init*(basespec, irqspec &#58; SIGNED32); &#9;&#9;BEGIN &#9;&#9;&#9;baseaddr &#58;&#61; basespec; &#9;&#9;&#9;irq &#58;&#61; irqspec; &#9;&#9;&#9;open &#58;&#61; FALSE; ox16 &#58;&#61; CheckOX16PCI954(basespec); &#9;&#9;&#9;IF ox16 THEN &#9;&#9;&#9;&#9;maxbps &#58;&#61; 460800 &#9;&#9;&#9;ELSE &#9;&#9;&#9;&#9;maxbps &#58;&#61; 115200 &#9;&#9;&#9;END &#9;&#9;END Init; &#9;&#9;PROCEDURE Open*(bps, data, parity, stop &#58; SIGNED32; VAR res&#58; INTEGER); &#9;&#9;BEGIN &#123;EXCLUSIVE&#125; &#9;&#9;&#9;IF open THEN &#9;&#9;&#9;&#9;IF Verbose THEN KernelLog.String(ModuleName); KernelLog.String("&#58; "); KernelLog.String(name); KernelLog.String(" already open"); KernelLog.Ln; END; &#9;&#9;&#9;&#9;res &#58;&#61; Serials.PortInUse; &#9;&#9;&#9;&#9;RETURN &#9;&#9;&#9;END; &#9;&#9;&#9;SetPortState(bps, data, parity, stop, res); &#9;&#9;&#9;IF res &#61; Serials.Ok THEN &#9;&#9;&#9;&#9;open &#58;&#61; TRUE; &#9;&#9;&#9;&#9;head &#58;&#61; 0; tail&#58;&#61; 0; &#9;&#9;&#9;&#9;charactersSent &#58;&#61; 0; charactersReceived &#58;&#61; 0; &#9;&#9;&#9;&#9;(* install interrupt handler *) &#9;&#9;&#9;&#9;Objects.InstallHandler(HandleInterrupt, Machine.IRQ0 + irq); &#9;&#9;&#9;&#9;Machine.Portout8((baseaddr) + IER, 01X);	(* Enable receive interrupts *) &#9;&#9;&#9;&#9;IF Verbose THEN KernelLog.String(ModuleName); KernelLog.String("&#58; "); KernelLog.String(name); KernelLog.String(" opened"); KernelLog.Ln END; &#9;&#9;&#9;END &#9;&#9;END Open; &#9;&#9;(** Send a single character to the UART. *) &#9;&#9;PROCEDURE SendChar*(ch&#58; CHAR; VAR res &#58; INTEGER); &#9;&#9;VAR s&#58; SET; &#9;&#9;BEGIN &#123;EXCLUSIVE&#125; &#9;&#9;&#9;IF &#126;open THEN res &#58;&#61; Serials.Closed; RETURN; END; &#9;&#9;&#9;res &#58;&#61; Serials.Ok; &#9;&#9;&#9;REPEAT	(* wait for room in Transmitter Holding Register *) &#9;&#9;&#9;&#9;Machine.Portin8((baseaddr) + LSR, SYSTEM.VAL(CHAR, s))	(* now send that character *) &#9;&#9;&#9;UNTIL 5 IN s; &#9;&#9;&#9;Machine.Portout8((baseaddr), ch); &#9;&#9;&#9;INC(charactersSent); &#9;&#9;END SendChar; &#9;&#9;(** Wait for the next character is received in the input buffer. The buffer is fed by HandleInterrupt *) &#9;&#9;PROCEDURE ReceiveChar*(VAR ch&#58; CHAR; VAR res&#58; INTEGER); &#9;&#9;BEGIN &#123;EXCLUSIVE&#125; &#9;&#9;&#9;IF &#126;open THEN res &#58;&#61; Serials.Closed; RETURN END; &#9;&#9;&#9;AWAIT(tail # head); &#9;&#9;&#9;IF tail &#61; -1 THEN &#9;&#9;&#9;&#9;res &#58;&#61; Serials.Closed; &#9;&#9;&#9;ELSE &#9;&#9;&#9;&#9;ch &#58;&#61; buf&#91;head&#93;; head &#58;&#61; (head+1) MOD BufSize; &#9;&#9;&#9;&#9;res &#58;&#61; diagnostic; &#9;&#9;&#9;END &#9;&#9;END ReceiveChar; &#9;&#9;(** On detecting an interupt request, transfer the characters from the UART buffer to the input buffer *) &#9;&#9;PROCEDURE HandleInterrupt; &#9;&#9;VAR n&#58; SIGNED32; ch&#58; CHAR; s&#58; SET; &#9;&#9;BEGIN &#123;EXCLUSIVE&#125; &#9;&#9;&#9;LOOP	(* transfer all the data available in the UART buffer to buf *) &#9;&#9;&#9;&#9;Machine.Portin8((baseaddr) + IIR, ch); &#9;&#9;&#9;&#9;IF ODD(ORD(ch)) THEN EXIT END;	(* nothing pending *) &#9;&#9;&#9;&#9;diagnostic &#58;&#61; 0; &#9;&#9;&#9;&#9;Machine.Portin8((baseaddr) + LSR, SYSTEM.VAL(CHAR, s));	(* Inspect if error *) &#9;&#9;&#9;&#9;IF (7 IN s) OR (1 IN s) THEN	(* Establish a diagnostic of the error *) &#9;&#9;&#9;&#9;&#9;IF (1 IN s) THEN diagnostic &#58;&#61; Serials.OverrunError; &#9;&#9;&#9;&#9;&#9;ELSIF (2 IN s) THEN diagnostic &#58;&#61; Serials.ParityError &#9;&#9;&#9;&#9;&#9;ELSIF (3 IN s) THEN diagnostic &#58;&#61; Serials.FramingError &#9;&#9;&#9;&#9;&#9;ELSIF (4 IN s) THEN diagnostic &#58;&#61; Serials.BreakInterrupt &#9;&#9;&#9;&#9;&#9;END; &#9;&#9;&#9;&#9;END; &#9;&#9;&#9;&#9;Machine.Portin8((baseaddr), ch);	(* Receive a character from the UART - baseaddr points to RBR *) &#9;&#9;&#9;&#9;n &#58;&#61; (tail+1) MOD BufSize; &#9;&#9;&#9;&#9;IF n # head THEN buf&#91;tail&#93; &#58;&#61; ch; tail &#58;&#61; n END; &#9;&#9;&#9;&#9;INC(charactersReceived); &#9;&#9;&#9;END; &#9;&#9;END HandleInterrupt; &#9;&#9;PROCEDURE Available*&#58; SIZE; &#9;&#9;BEGIN &#123;EXCLUSIVE&#125; &#9;&#9;&#9;RETURN (tail - head) MOD BufSize &#9;&#9;END Available; &#9;&#9;(* Set the port state&#58; speed in bps, no. of data bits, parity, stop bit length. *) &#9;&#9;PROCEDURE SetPortState(bps, data, parity, stop &#58; SIGNED32; VAR res&#58; INTEGER); &#9;&#9;CONST TCR &#61; 2; &#9;&#9;VAR s&#58; SET; tcr&#58; SIGNED32; &#9;&#9;BEGIN &#9;&#9;&#9;IF (bps &#62; 0) &#38; (maxbps MOD bps &#61; 0) THEN &#9;&#9;&#9;&#9;IF (data &#62;&#61; 5) &#38; (data &#60;&#61; 8) &#38; (parity &#62;&#61; Serials.ParNo) &#38; (parity &#60;&#61; Serials.ParSpace) &#38; &#9;&#9;&#9;&#9;&#9;&#9;(stop &#62;&#61; Serials.Stop1) &#38; (stop &#60;&#61; Serials.Stop1dot5) THEN &#9;&#9;&#9;&#9;&#9;IF ox16 THEN &#9;&#9;&#9;&#9;&#9;&#9;IF bps &#60;&#61; 115200 THEN &#9;&#9;&#9;&#9;&#9;&#9;&#9;tcr &#58;&#61; 0 &#9;&#9;&#9;&#9;&#9;&#9;ELSE &#9;&#9;&#9;&#9;&#9;&#9;&#9;tcr &#58;&#61; 115200*16 DIV bps; &#9;&#9;&#9;&#9;&#9;&#9;&#9;ASSERT((tcr &#62;&#61; 4) &#38; (tcr &#60; 16)); &#9;&#9;&#9;&#9;&#9;&#9;&#9;bps &#58;&#61; 115200 &#9;&#9;&#9;&#9;&#9;&#9;END; &#9;&#9;&#9;&#9;&#9;&#9;IF ReadICR(baseaddr, TCR) # CHR(tcr) THEN &#9;&#9;&#9;&#9;&#9;&#9;&#9;WriteICR(baseaddr, TCR, CHR(tcr)) &#9;&#9;&#9;&#9;&#9;&#9;END &#9;&#9;&#9;&#9;&#9;END; &#9;&#9;&#9;&#9;&#9;bps &#58;&#61; 115200 DIV bps; &#9;&#9;&#9;&#9;&#9;(* disable interrupts *) &#9;&#9;&#9;&#9;&#9;Machine.Portout8((baseaddr)+LCR, 0X);	(* clear DLAB *) &#9;&#9;&#9;&#9;&#9;Machine.Portout8((baseaddr)+IER, 0X);	(* Disable all interrupts *) &#9;&#9;&#9;&#9;&#9;(* clear latches *) &#9;&#9;&#9;&#9;&#9;Machine.Portin8((baseaddr)+LSR, SYSTEM.VAL(CHAR, s)); &#9;&#9;&#9;&#9;&#9;Machine.Portin8((baseaddr)+IIR, SYSTEM.VAL(CHAR, s)); &#9;&#9;&#9;&#9;&#9;Machine.Portin8((baseaddr)+MSR, SYSTEM.VAL(CHAR, s)); &#9;&#9;&#9;&#9;&#9;Machine.Portout8((baseaddr)+FCR, 0C1X);	(* See if one can activate the FIFO *) &#9;&#9;&#9;&#9;&#9;Machine.Portin8((baseaddr)+IIR, SYSTEM.VAL(CHAR, s));	(* Read how the chip responded in bits 6 &#38; 7 of IIR *) &#9;&#9;&#9;&#9;&#9;IF s * &#123;6,7&#125; &#61; &#123;6,7&#125; THEN	(* FIFO enabled on 16550 chip and later ones *) &#9;&#9;&#9;&#9;&#9;&#9;Machine.Portout8((baseaddr) + FCR, 47X)	(* 16550 setup&#58; EnableFifo, CLRRX, CLRTX, SIZE4 *) &#9;&#9;&#9;&#9;&#9;ELSIF s * &#123;6,7&#125; &#61; &#123;&#125; THEN	(* Bits 6 and 7 are always zero on 8250 / 16450 chip *) &#9;&#9;&#9;&#9;&#9;&#9;Machine.Portout8((baseaddr) + FCR, 0X) &#9;&#9;&#9;&#9;&#9;ELSE KernelLog.String("Not prepared to deal with this COM port situation");	(* This case should not exist *) &#9;&#9;&#9;&#9;&#9;END; &#9;&#9;&#9;&#9;&#9;(* set parameters *) &#9;&#9;&#9;&#9;&#9;Machine.Portout8((baseaddr) + LCR, 80X);	(* Set the Divisor Latch Bit - DLAB &#61; 1 *) &#9;&#9;&#9;&#9;&#9;Machine.Portout8((baseaddr), CHR(bps));	(* Set the Divisor Latch LSB *) &#9;&#9;&#9;&#9;&#9;Machine.Portout8((baseaddr)+1, CHR(bps DIV 100H));	(* Set the Divisor Latch MSB *) &#9;&#9;&#9;&#9;&#9;(* Prepare parameters destined to LCR data, stop, parity *) &#9;&#9;&#9;&#9;&#9;CASE data OF	(* word length *) &#9;&#9;&#9;&#9;&#9;&#9;&#32;&#32;&#32;5&#58; s &#58;&#61; &#123;&#125; &#9;&#9;&#9;&#9;&#9;&#9;&#124; 6&#58; s &#58;&#61; &#123;0&#125; &#9;&#9;&#9;&#9;&#9;&#9;&#124; 7&#58; s &#58;&#61; &#123;1&#125; &#9;&#9;&#9;&#9;&#9;&#9;&#124; 8&#58; s &#58;&#61; &#123;0,1&#125; &#9;&#9;&#9;&#9;&#9;END; &#9;&#9;&#9;&#9;&#9;IF stop # Serials.Stop1 THEN INCL(s, 2) END; &#9;&#9;&#9;&#9;&#9;CASE parity OF &#9;&#9;&#9;&#9;&#9;&#9;&#32;&#32;&#32;Serials.ParNo&#58; &#9;&#9;&#9;&#9;&#9;&#9;&#124; Serials.ParOdd&#58; INCL(s, 3) &#9;&#9;&#9;&#9;&#9;&#9;&#124; Serials.ParEven&#58; s &#58;&#61; s + &#123;3,4&#125; &#9;&#9;&#9;&#9;&#9;&#9;&#124; Serials.ParMark&#58; s &#58;&#61; s + &#123;3,5&#125; &#9;&#9;&#9;&#9;&#9;&#9;&#124; Serials.ParSpace&#58; s &#58;&#61; s + &#123;3..5&#125; &#9;&#9;&#9;&#9;&#9;END; &#9;&#9;&#9;&#9;&#9;(* Finalize the LCR *) &#9;&#9;&#9;&#9;&#9;Machine.Portout8((baseaddr)+LCR, SYSTEM.VAL(CHAR, s));	(* DLAB is set &#61; 0 at the same time *) &#9;&#9;&#9;&#9;&#9;(* Set DTR, RTS, OUT2 in the MCR *) &#9;&#9;&#9;&#9;&#9;Machine.Portout8((baseaddr)+MCR, SYSTEM.VAL(CHAR, &#123;DTR,RTS,3&#125;)); (*					Machine.Portout8((baseaddr)+IER, 01X);	*) &#9;&#9;&#9;&#9;&#9;res &#58;&#61; Serials.Ok &#9;&#9;&#9;&#9;ELSE res &#58;&#61; Serials.WrongData (* bad data/parity/stop *) &#9;&#9;&#9;&#9;END &#9;&#9;&#9;ELSE res &#58;&#61; Serials.WrongBPS (* bad BPS *) &#9;&#9;&#9;END &#9;&#9;END SetPortState; &#9;&#9;(** Get the port state&#58; state (open/closed), speed in bps, no. of data bits, parity, top bit length. *) &#9;&#9;PROCEDURE GetPortState*(VAR openstat &#58; BOOLEAN; VAR bps, data, parity, stop &#58; SIGNED32); &#9;&#9;CONST TCR &#61; 2; &#9;&#9;VAR savset, set&#58; SET; ch&#58; CHAR; &#9;&#9;BEGIN &#123;EXCLUSIVE&#125; &#9;&#9;&#9;(* get parameters *) &#9;&#9;&#9;openstat &#58;&#61; open; &#9;&#9;&#9;Machine.Portin8((baseaddr) + LCR, SYSTEM.VAL(CHAR, savset)); &#9;&#9;&#9;set &#58;&#61; savset + &#123;7&#125;; &#9;&#9;&#9;Machine.Portout8((baseaddr) + LCR, SYSTEM.VAL(CHAR, set));	(* INCL the Divisor Latch Bit - DLAB &#61; 1 *) &#9;&#9;&#9;Machine.Portin8((baseaddr)+1, ch); &#9;&#9;&#9;bps &#58;&#61; ORD(ch); &#9;&#9;&#9;Machine.Portin8((baseaddr), ch); &#9;&#9;&#9;IF (bps &#61; 0 ) &#38; (ch &#61; 0X) THEN &#9;&#9;&#9;ELSE &#9;&#9;&#9;&#9;bps &#58;&#61; 115200 DIV (100H*bps + ORD(ch)) &#9;&#9;&#9;END; &#9;&#9;&#9;IF ox16 THEN &#9;&#9;&#9;&#9;ch &#58;&#61; ReadICR(baseaddr, TCR); &#9;&#9;&#9;&#9;IF (ch &#62;&#61; 04X) &#38; (ch &#60; 16X) THEN &#9;&#9;&#9;&#9;&#9;bps &#58;&#61; bps*16 DIV ORD(ch) &#9;&#9;&#9;&#9;END &#9;&#9;&#9;END; &#9;&#9;&#9;Machine.Portout8((baseaddr)+LCR, SYSTEM.VAL(CHAR, savset));	(* Reset the Divisor Latch Bit - DLAB &#61; 0 *) &#9;&#9;&#9;Machine.Portin8((baseaddr)+LCR, SYSTEM.VAL(CHAR, set)); &#9;&#9;&#9;IF set * &#123;0, 1&#125; &#61; &#123;0, 1&#125; THEN data &#58;&#61; 8 &#9;&#9;&#9;ELSIF set * &#123;0, 1&#125; &#61; &#123;1&#125; THEN data &#58;&#61; 7 &#9;&#9;&#9;ELSIF set * &#123;0, 1&#125; &#61; &#123;0&#125; THEN data &#58;&#61; 6 &#9;&#9;&#9;ELSE data &#58;&#61; 5 &#9;&#9;&#9;END; &#9;&#9;&#9;IF 2 IN set THEN &#9;&#9;&#9;&#9;IF set * &#123;0, 1&#125; &#61; &#123;&#125; THEN stop &#58;&#61; 3 &#9;&#9;&#9;&#9;ELSE stop &#58;&#61; 2 &#9;&#9;&#9;&#9;END; &#9;&#9;&#9;ELSE stop &#58;&#61; 1 &#9;&#9;&#9;END; &#9;&#9;&#9;IF set * &#123;3..5&#125; &#61; &#123;3..5&#125; THEN parity &#58;&#61; 4 &#9;&#9;&#9;ELSIF set * &#123;3,5&#125; &#61; &#123;3,5&#125; THEN parity &#58;&#61; 3 &#9;&#9;&#9;ELSIF set * &#123;3,4&#125; &#61; &#123;3,4&#125; THEN parity &#58;&#61; 2 &#9;&#9;&#9;ELSIF set * &#123;3&#125; &#61; &#123;3&#125; THEN parity &#58;&#61; 1 &#9;&#9;&#9;ELSE parity &#58;&#61; 0 &#9;&#9;&#9;END; &#9;&#9;END GetPortState; &#9;&#9;(** Clear the specified modem control lines. s may contain DTR, RTS &#38; Break. *) &#9;&#9;PROCEDURE ClearMC*(s&#58; SET); &#9;&#9;VAR t&#58; SET; &#9;&#9;BEGIN &#123;EXCLUSIVE&#125; &#9;&#9;&#9;IF s * &#123;DTR, RTS&#125; # &#123;&#125; THEN &#9;&#9;&#9;&#9;Machine.Portin8((baseaddr) + MCR, SYSTEM.VAL(CHAR, t)); &#9;&#9;&#9;&#9;t &#58;&#61; t - (s * &#123;DTR, RTS&#125;);	(* modify only bits 0 &#38; 1 *) &#9;&#9;&#9;&#9;Machine.Portout8((baseaddr) + MCR, SYSTEM.VAL(CHAR, t)) &#9;&#9;&#9;END; &#9;&#9;&#9;IF Break IN s THEN &#9;&#9;&#9;&#9;Machine.Portin8((baseaddr) + LCR, SYSTEM.VAL(CHAR, t)); &#9;&#9;&#9;&#9;EXCL(t, 6);	(* break off *) &#9;&#9;&#9;&#9;Machine.Portout8((baseaddr) + LCR, SYSTEM.VAL(CHAR, t)) &#9;&#9;&#9;END &#9;&#9;END ClearMC; &#9;&#9;(** Set the specified modem control lines. s may contain DTR, RTS &#38; Break. *) &#9;&#9;PROCEDURE SetMC*(s&#58; SET); &#9;&#9;VAR t&#58; SET; &#9;&#9;BEGIN &#123;EXCLUSIVE&#125; &#9;&#9;&#9;IF s * &#123;DTR, RTS&#125; # &#123;&#125; THEN &#9;&#9;&#9;&#9;Machine.Portin8((baseaddr) + MCR, SYSTEM.VAL(CHAR, t)); &#9;&#9;&#9;&#9;t &#58;&#61; t + (s * &#123;DTR, RTS&#125;);	(* modify only bits 0 &#38; 1 *) &#9;&#9;&#9;&#9;Machine.Portout8((baseaddr) + MCR, SYSTEM.VAL(CHAR, t)) &#9;&#9;&#9;END; &#9;&#9;&#9;IF Break IN s THEN &#9;&#9;&#9;&#9;Machine.Portin8((baseaddr) + LCR, SYSTEM.VAL(CHAR, t)); &#9;&#9;&#9;&#9;INCL(t, 6);	(* break on *) &#9;&#9;&#9;&#9;Machine.Portout8((baseaddr) + LCR, SYSTEM.VAL(CHAR, t)) &#9;&#9;&#9;END &#9;&#9;END SetMC; &#9;&#9;(** Return the state of the specified modem control lines. s contains the current state of DSR, CTS, RI, DCD &#38; Break Interrupt. *) &#9;&#9;PROCEDURE GetMC*(VAR s&#58; SET); &#9;&#9;VAR t&#58; SET; &#9;&#9;BEGIN &#123;EXCLUSIVE&#125; &#9;&#9;&#9;s &#58;&#61; &#123;&#125;; &#9;&#9;&#9;Machine.Portin8((baseaddr) + MSR, SYSTEM.VAL(CHAR, t));	(* note&#58; this clears bits 0-3 *) &#9;&#9;&#9;IF 4 IN t THEN INCL(s, CTS) END; &#9;&#9;&#9;IF 5 IN t THEN INCL(s, DSR) END; &#9;&#9;&#9;IF 6 IN t THEN INCL(s, RI) END; &#9;&#9;&#9;IF 7 IN t THEN INCL(s, DCD) END; &#9;&#9;&#9;Machine.Portin8((baseaddr) + LSR, SYSTEM.VAL(CHAR, t));	(* note&#58; this clears bits 1-4 *) &#9;&#9;&#9;IF 4 IN t THEN INCL(s, Break) END &#9;&#9;END GetMC; &#9;&#9;PROCEDURE Close*; &#9;&#9;VAR s&#58; SET; &#9;&#9;BEGIN &#123;EXCLUSIVE&#125; &#9;&#9;&#9;IF &#126;open THEN &#9;&#9;&#9;&#9;IF Verbose THEN KernelLog.String(ModuleName); KernelLog.String("&#58; "); KernelLog.String(name); KernelLog.String(" not open"); KernelLog.Ln; END; &#9;&#9;&#9;&#9;RETURN &#9;&#9;&#9;END; &#9;&#9;&#9;REPEAT	(* wait for last byte to leave *) &#9;&#9;&#9;&#9;Machine.Portin8((baseaddr)+LSR, SYSTEM.VAL(CHAR, s)) &#9;&#9;&#9;UNTIL 6 IN s;	(* No remaining word in the FIFO or transmit shift register *) &#9;&#9;&#9;tail &#58;&#61; -1;	(* Force a pending Receive to terminate in error. *) &#9;&#9;&#9;(* disable interrupts *) &#9;&#9;&#9;Machine.Portout8((baseaddr) + IER, 0X); &#9;&#9;&#9;(* remove interrupt handler *) &#9;&#9;&#9;Objects.RemoveHandler(HandleInterrupt, Machine.IRQ0 + irq); &#9;&#9;&#9;open &#58;&#61; FALSE; &#9;&#9;&#9;IF Verbose THEN KernelLog.String(ModuleName); KernelLog.String("&#58; "); KernelLog.String(name); KernelLog.String(" closed"); KernelLog.Ln; END; &#9;&#9;END Close; &#9;END RS232Port; PROCEDURE ReadICR(baseaddr, index&#58; SIGNED32)&#58; CHAR; &#9;CONST SPR &#61; 7; ICR &#61; 5; ICREnable &#61; 6; &#9;VAR ch&#58; CHAR; BEGIN &#9;Machine.Portout8((baseaddr) + SPR, 0X); &#9;Machine.Portout8((baseaddr) + ICR, SYSTEM.VAL(CHAR, &#123;ICREnable&#125;)); &#9;Machine.Portout8((baseaddr) + SPR, CHR(index)); &#9;Machine.Portin8((baseaddr) + ICR, ch); &#9;Machine.Portout8((baseaddr) + SPR, 0X); &#9;Machine.Portout8((baseaddr) + ICR, 0X); &#9;RETURN ch END ReadICR; PROCEDURE WriteICR(baseaddr, index&#58; SIGNED32; ch&#58; CHAR); &#9;CONST SPR &#61; 7; ICR &#61; 5; BEGIN &#9;Machine.Portout8((baseaddr) + SPR, CHR(index)); &#9;Machine.Portout8((baseaddr) + ICR, ch) END WriteICR; PROCEDURE CheckOX16PCI954(baseaddr&#58; SIGNED32)&#58; BOOLEAN; &#9;CONST ID1 &#61; 8; ID2 &#61; 9; ID3 &#61; 10; REV &#61; 11; BEGIN &#9;RETURN (baseaddr &#62;&#61; 1000H) &#38; (ReadICR(baseaddr, ID1) &#61; 016X) &#38; (ReadICR(baseaddr, ID2) &#61; 0C9X) &#38; &#9;&#9;(ReadICR(baseaddr, ID3) &#61; 050X) &#38; (ReadICR(baseaddr, REV) &#61; 001X) END CheckOX16PCI954; PROCEDURE ShowModule(out &#58; Streams.Writer); BEGIN &#9;out.String(ModuleName); out.String("&#58; "); END ShowModule; (** Scan the installed serial ports and determine the chip type used *) PROCEDURE Scan*(context &#58; Commands.Context); VAR i&#58; SIGNED32; port&#58; RS232Port; serialPort &#58; Serials.Port; portstatus&#58; SET; found &#58; BOOLEAN; &#9;PROCEDURE DetectChip(baseaddr&#58; SIGNED32); &#9;VAR ch&#58; CHAR; &#9;BEGIN &#9;&#9;context.out.String(" Detected UART  "); &#9;&#9;Machine.Portout8((baseaddr) + FCR, 0C1X);	(* See if one can activate the FIFO *) &#9;&#9;Machine.Portin8((baseaddr) + IIR, ch);	(* Read how the chip responded in the 2 most significant bits of IIR *) &#9;&#9;Machine.Portout8((baseaddr) + FCR, 00X);	(* Deactivate the FIFO *) &#9;&#9;CASE ASH(ORD(ch), -6) OF &#9;&#9;&#32;&#32;&#32;0&#58; Machine.Portout8((baseaddr) + SCR, 0FAX);	(* See if one can write in the SCR *) &#9;&#9;&#9;&#9;Machine.Portin8((baseaddr) + SCR, ch); &#9;&#9;&#9;&#9;IF ch &#61; 0FAX THEN &#9;&#9;&#9;&#9;&#9;Machine.Portout8((baseaddr) + SCR, 0AFX); &#9;&#9;&#9;&#9;&#9;Machine.Portin8((baseaddr) + SCR, ch); &#9;&#9;&#9;&#9;&#9;IF ch &#61; 0AFX THEN &#9;&#9;&#9;&#9;&#9;&#9;context.out.String("16450, 8250A") &#9;&#9;&#9;&#9;&#9;ELSE &#9;&#9;&#9;&#9;&#9;&#9;context.out.String("8250, 8250-B, (has flaws)") &#9;&#9;&#9;&#9;&#9;END &#9;&#9;&#9;&#9;ELSE	(* No SCR present *) &#9;&#9;&#9;&#9;&#9;context.out.String("8250, 8250-B, (has flaws)") &#9;&#9;&#9;&#9;END &#9;&#9;&#124; 1&#58; context.out.String("Unknown chip") &#9;&#9;&#124; 2&#58; context.out.String("16550, non-buffered (has flaws)") &#9;&#9;&#124; 3&#58; IF CheckOX16PCI954(baseaddr) THEN &#9;&#9;&#9;&#9;&#9;context.out.String("OX16PCI954") &#9;&#9;&#9;&#9;ELSE &#9;&#9;&#9;&#9;&#9;context.out.String("16550A, buffer operational") &#9;&#9;&#9;&#9;END &#9;&#9;END &#9;END DetectChip; BEGIN &#9;ShowModule(context.out); context.out.String("Serial port detection and inspection&#58;"); context.out.Ln; &#9;found &#58;&#61; FALSE; &#9;FOR i &#58;&#61; 1 TO Serials.MaxPorts DO &#9;&#9;serialPort &#58;&#61; Serials.GetPort(i); &#9;&#9;IF (serialPort # NIL) &#38; (serialPort IS RS232Port) THEN &#9;&#9;&#9;port &#58;&#61; serialPort (RS232Port); found &#58;&#61; TRUE; &#9;&#9;&#9;IF port.baseaddr # 0 THEN (* Port has a valid base address *) &#9;&#9;&#9;&#9;context.out.String(port.name); context.out.String("&#58; "); context.out.Hex(port.baseaddr, 10); context.out.Char("H"); context.out.Int(port.irq, 4); &#9;&#9;&#9;&#9;DetectChip(port.baseaddr); &#9;&#9;&#9;&#9;port.GetMC(portstatus); &#9;&#9;&#9;&#9;IF CTS IN portstatus THEN context.out.String(" - CTS signals the presence of a DCE / Modem") END; &#9;&#9;&#9;&#9;context.out.Ln &#9;&#9;&#9;END &#9;&#9;END; &#9;END; &#9;IF &#126;found THEN context.out.String("No COM port found."); context.out.Ln; END; END Scan; (** Set the essential port operating parameters as specified in Aos.Par &#9;&#9;If omitted, default standard values are assigned to COM1 and COM2 *) PROCEDURE Install*(context &#58; Commands.Context); VAR i&#58; SIGNED32; p &#58; SIZE; name, s&#58; ARRAY 16 OF CHAR; BASE, IRQ&#58; SIGNED32; port &#58; RS232Port; BEGIN &#9;FOR i &#58;&#61; 0 TO MaxPortNo-1 DO &#9;&#9;COPY("COM ", name); &#9;&#9;name&#91;3&#93; &#58;&#61; CHR(ORD("1") + i); &#9;&#9;Machine.GetConfig(name, s); &#9;&#9;p &#58;&#61; 0; &#9;&#9;BASE &#58;&#61; Machine.StrToInt(p, s); &#9;&#9;IF s&#91;p&#93; &#61; "," THEN &#9;&#9;&#9;INC(p); IRQ &#58;&#61; Machine.StrToInt(p, s) &#9;&#9;END; &#9;&#9;IF (i &#61; 0) &#38; (BASE &#61; 0) THEN BASE &#58;&#61; 3F8H; IRQ &#58;&#61; 4 END;	(* COM1 port default values *) &#9;&#9;IF (i &#61; 1) &#38; (BASE &#61; 0) THEN BASE &#58;&#61; 2F8H; IRQ &#58;&#61; 3 END;	(* COM2 port default values *) &#9;&#9;IF BASE # 0 THEN &#9;&#9;&#9;NEW(port, BASE, IRQ); &#9;&#9;&#9;(* Check the presence of a UART at the specified base address *) &#9;&#9;&#9;Machine.Portin8((port.baseaddr) + MCR, s&#91;0&#93;); &#9;&#9;&#9;IF ORD(s&#91;0&#93;) &#60; 32 THEN	(* Bits 7..5 of the MCR are always 0 when a UART is present *) &#9;&#9;&#9;&#9;(* Register this RS232Port with an identical index in Serials.registeredSerials array *) &#9;&#9;&#9;&#9;Serials.RegisterOnboardPort (i+1, port, name, "Onboard UART"); &#9;&#9;&#9;&#9;IF context # NIL THEN &#9;&#9;&#9;&#9;&#9;ShowModule(context.out); context.out.String("Port "); context.out.String(name); context.out.String(" installed."); context.out.Ln; &#9;&#9;&#9;&#9;END; &#9;&#9;&#9;ELSE &#9;&#9;&#9;&#9;IF context # NIL THEN &#9;&#9;&#9;&#9;&#9;ShowModule(context.out); context.out.String("No UART present at address specified for "); &#9;&#9;&#9;&#9;&#9;context.out.String(name); &#9;&#9;&#9;&#9;&#9;context.out.Ln &#9;&#9;&#9;&#9;END; &#9;&#9;&#9;END &#9;&#9;END &#9;END; END Install; PROCEDURE Init*; (* compatibility with windows ... *) BEGIN END Init; END V24. V24.Install &#126;		System.Free V24 &#126; V24.Scan &#126; Example Aos.Par information (typical values usually assigned to the 4 first serial ports) &#32;&#32;COM1&#61;"3F8H,4" &#32;&#32;COM2&#61;"2F8H,3" &#32;&#32;COM3&#61;"3E8H,6" &#32;&#32;COM4&#61;"2E8H,9" &#126; In Bluebottle, the generalization of the serial port support lead to the following adjustments&#58; New low-level module V24.Mod -&#62; V24.Obx is completely new. &#9;A new object-oriented driver supporting up to 8 serial ports (COM1 .. COM8) at speeds up to &#9;115&#39;200 BPS. No longer compatible with ETH Native Oberon. &#9;The I/O base address and the IRQ corresponding to each COM port must be declared in Aos.Par, &#9;which contains configuration data, except that COM1 and COM2 are declared by default &#9;with their standard values, as used on most machines &#9;&#9;COM1&#61;"3F8H,4" &#9;&#9;COM2&#61;"2F8H,3" &#9;These two ports must be declared only in the case that the indicated standard do not apply. &#9;Bluebottle operates in 32-bit addressing mode and it is not possible to interrogate the base address &#9;by accessing the port directly in BIOS. &#9;The port information is registered in the order of appearance in Aos.Par and the ports are&#58; &#9;- named from the user&#39;s viewpoint starting from COM1 by name and 1 by number and &#9;- numbered internally starting from 0 &#9;The module includes the facilities &#9;- to verify that the ports declared in Aos.Par exist effectively &#9;- to determine the UART chip type used by the ports &#9;- to detect the presence of a modem &#9;- to trace the data stream (in the next update round) &#9;Error detection and handling during the reception have been improved, but the reception is &#9;not error prone anyway. Very low-level module using a serial port KernelLog.Mod -&#62; KernelLog.Obx &#9;Offers the possibility of tracing the boot process on another machine connected via a serial port &#9;without the assistance of any other V24 support mentioned in this context. &#9;Like V24.Mod, it collects the base address of the available serial ports from Aos.Par &#9;and the port is selected from this list by reading the TracePort value in Aos.Par &#9;In the original version the port base address was hard-coded in the module. &#9;The module produces only an outgoing data stream. Modified low-level module Aos.V24.Mod -&#62; V24.Obx &#9;In the earlier Bluebottle versions, this module offered the low-level serial port support. &#9;It is now an application module exploiting V24.Obx. Consequently, it is much simpler &#9;although it offers all the functionality of its predecessor. &#9;Backward compatibility with the original version is thus provided for client modules. &#9;New developments should avoid using it and make use of the enhanced V24.Obx.