blob: aac849abb9b4065d4aa7bafdb28856489981a360 [file] [log] [blame]
(* IOLink.mod implement the ISO IOLink specification.
Copyright (C) 2008-2025 Free Software Foundation, Inc.
Contributed by Gaius Mulley <gaius.mulley@southwales.ac.uk>.
This file is part of GNU Modula-2.
GNU Modula-2 is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GNU Modula-2 is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
Under Section 7 of GPL version 3, you are granted additional
permissions described in the GCC Runtime Library Exception, version
3.1, as published by the Free Software Foundation.
You should have received a copy of the GNU General Public License and
a copy of the GCC Runtime Library Exception along with this program;
see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
<http://www.gnu.org/licenses/>. *)
IMPLEMENTATION MODULE IOLink ;
IMPORT RTio, RTentity, EXCEPTIONS, M2RTS, ASCII ;
FROM Storage IMPORT ALLOCATE, DEALLOCATE ;
(*
Values of this type are used to identify new device modules,
and are normally obtained by them during their initialization.
*)
TYPE
DeviceId = POINTER TO RECORD
cids: RTentity.Group ;
END ;
resourceState = (allocated, deallocated) ;
VAR
dids : RTentity.Group ;
iolink: EXCEPTIONS.ExceptionSource ;
(*
checkValidDevice - checks to see that the, did, is
known to exist.
*)
PROCEDURE checkValidDevice (did: DeviceId) ;
BEGIN
IF NOT RTentity.IsIn(dids, did)
THEN
EXCEPTIONS.RAISE(iolink, ORD(IOChan.wrongDevice),
'IOLink: device id specified does not exist')
END
END checkValidDevice ;
(*
Allocates a unique value of type DeviceId, and assigns this
value to did.
*)
PROCEDURE AllocateDeviceId (VAR did: DeviceId) ;
BEGIN
NEW(did) ;
IF did=NIL
THEN
EXCEPTIONS.RAISE(iolink, ORD(IOChan.hardDeviceError),
'IOLink.AllocateDeviceId: out of memory error')
ELSE
RTentity.PutKey(dids, did, ORD(allocated)) ;
WITH did^ DO
cids := RTentity.InitGroup()
END
END
END AllocateDeviceId ;
PROCEDURE defaultLook (d: DeviceTablePtr;
VAR ch: CHAR;
VAR r : IOConsts.ReadResults) ;
BEGIN
EXCEPTIONS.RAISE(iolink, ORD(IOChan.notAvailable),
'IOLink:Look operation on device is not available')
END defaultLook ;
PROCEDURE defaultSkip (d: DeviceTablePtr) ;
BEGIN
EXCEPTIONS.RAISE(iolink, ORD(IOChan.notAvailable),
'IOLink:Skip operation on device is not available')
END defaultSkip ;
PROCEDURE defaultSkipLook (d: DeviceTablePtr;
VAR ch: CHAR;
VAR r : IOConsts.ReadResults) ;
BEGIN
EXCEPTIONS.RAISE(iolink, ORD(IOChan.notAvailable),
'IOLink:SkipLook operation on device is not available')
END defaultSkipLook ;
PROCEDURE defaultWriteLn (d: DeviceTablePtr) ;
BEGIN
EXCEPTIONS.RAISE(iolink, ORD(IOChan.notAvailable),
'IOLink:WriteLn operation on device is not available')
END defaultWriteLn ;
PROCEDURE defaultTextRead (d: DeviceTablePtr; a: SYSTEM.ADDRESS; n: CARDINAL; VAR r: CARDINAL) ;
BEGIN
EXCEPTIONS.RAISE(iolink, ORD(IOChan.notAvailable),
'IOLink:TextRead operation on device is not available')
END defaultTextRead ;
PROCEDURE defaultTextWrite (d: DeviceTablePtr; a: SYSTEM.ADDRESS; n: CARDINAL) ;
BEGIN
EXCEPTIONS.RAISE(iolink, ORD(IOChan.notAvailable),
'IOLink:TextWrite operation on device is not available')
END defaultTextWrite ;
PROCEDURE defaultRawRead (d: DeviceTablePtr; a: SYSTEM.ADDRESS; n: CARDINAL; VAR r: CARDINAL) ;
BEGIN
EXCEPTIONS.RAISE(iolink, ORD(IOChan.notAvailable),
'IOLink:TextRawRead operation on device is not available')
END defaultRawRead ;
PROCEDURE defaultRawWrite (d: DeviceTablePtr; a: SYSTEM.ADDRESS; n: CARDINAL) ;
BEGIN
EXCEPTIONS.RAISE(iolink, ORD(IOChan.notAvailable),
'IOLink:TextRawWrite operation on device is not available')
END defaultRawWrite ;
PROCEDURE defaultGetName (d: DeviceTablePtr; VAR a: ARRAY OF CHAR) ;
BEGIN
a[0] := ASCII.nul
END defaultGetName ;
PROCEDURE defaultReset (d: DeviceTablePtr) ;
BEGIN
(* do nothing *)
END defaultReset ;
PROCEDURE defaultFlush (d: DeviceTablePtr) ;
BEGIN
(* do nothing *)
END defaultFlush ;
PROCEDURE defaultFree (d: DeviceTablePtr) ;
BEGIN
(* do nothing *)
END defaultFree ;
(*
InitDtp - creates a new DeviceTablePtr and initializes the
fields to their defaults.
*)
PROCEDURE InitDtp (d: DeviceId; c: IOChan.ChanId) : DeviceTablePtr ;
VAR
p: DeviceTablePtr ;
BEGIN
NEW(p) ;
WITH p^ DO
cd := NIL ;
did := d ;
cid := c ;
result := IOConsts.notKnown ;
errNum := 0 ;
flags := ChanConsts.FlagSet{} ;
doLook := defaultLook ;
doSkip := defaultSkip ;
doSkipLook := defaultSkipLook ;
doLnWrite := defaultWriteLn ;
doTextRead := defaultTextRead ;
doTextWrite := defaultTextWrite ;
doRawRead := defaultRawRead ;
doRawWrite := defaultRawWrite ;
doGetName := defaultGetName ;
doReset := defaultReset ;
doFlush := defaultFlush ;
doFree := defaultFree ;
END ;
RETURN( p )
END InitDtp ;
(*
KillDtp - deallocate, p, and any associated resource.
*)
PROCEDURE KillDtp (p: DeviceTablePtr) : DeviceTablePtr ;
BEGIN
WITH p^ DO
doFlush(p) ;
doFree(p)
END ;
DISPOSE(p) ;
RETURN( NIL )
END KillDtp ;
(*
Attempts to make a new channel for the device module identified
by did. If no more channels can be made, the identity of
the invalid channel is assigned to cid. Otherwise, the identity
of a new channel is assigned to cid.
*)
PROCEDURE MakeChan (did: DeviceId; VAR cid: IOChan.ChanId) ;
BEGIN
checkValidDevice(did) ;
cid := IOChan.ChanId(RTio.InitChanId()) ;
IF cid=NIL
THEN
cid := IOChan.InvalidChan()
ELSE
WITH did^ DO
RTentity.PutKey(cids, cid, ORD(allocated))
END ;
RTio.SetDeviceId(cid, did) ;
RTio.SetDevicePtr(cid, InitDtp(did, cid))
END
END MakeChan ;
(*
If the device module identified by did is not the module that
made the channel identified by cid, the exception wrongDevice is
raised; otherwise the channel is deallocated, and the value
identifying the invalid channel is assigned to cid.
*)
PROCEDURE UnMakeChan (did: DeviceId; VAR cid: IOChan.ChanId) ;
BEGIN
checkValidDevice(did) ;
WITH did^ DO
IF RTentity.IsIn(cids, cid)
THEN
RTio.SetDevicePtr(cid, KillDtp(RTio.GetDevicePtr(cid))) ;
RTentity.DelKey(cids, cid) ;
cid := IOChan.ChanId(RTio.KillChanId(cid)) ;
cid := IOChan.InvalidChan()
ELSE
EXCEPTIONS.RAISE(iolink, ORD(IOChan.wrongDevice),
'IOLink.UnMakeChan: channel does not belong to device')
END
END
END UnMakeChan ;
(*
The pointer to the device table for a channel is obtained using the
following procedure:
If the device module identified by did is not the module that made
the channel identified by cid, the exception wrongDevice is raised.
*)
PROCEDURE DeviceTablePtrValue (cid: IOChan.ChanId;
did: DeviceId) : DeviceTablePtr ;
BEGIN
checkValidDevice(did) ;
WITH did^ DO
IF RTentity.IsIn(cids, cid)
THEN
RETURN( RTio.GetDevicePtr(cid) )
ELSE
EXCEPTIONS.RAISE(iolink, ORD(IOChan.wrongDevice),
'IOLink.DeviceTablePtrValue: channel does belong to device') ;
RETURN NIL
END
END
END DeviceTablePtrValue ;
PROCEDURE IsDevice (cid: IOChan.ChanId; did: DeviceId) : BOOLEAN ;
(* Tests if the device module identified by did is the module
that made the channel identified by cid.
*)
BEGIN
IF RTentity.IsIn(dids, did)
THEN
WITH did^ DO
RETURN( RTentity.IsIn(cids, cid) )
END
END ;
RETURN( FALSE )
END IsDevice ;
PROCEDURE RAISEdevException (cid: IOChan.ChanId; did: DeviceId;
x: DevExceptionRange; s: ARRAY OF CHAR) ;
(* If the device module identified by did is not the module
that made the channel identified by cid, the exception
wrongDevice is raised; otherwise the given exception
is raised, and the string value in s is included in the
exception message.
*)
BEGIN
checkValidDevice(did) ;
WITH did^ DO
IF RTentity.IsIn(cids, cid)
THEN
EXCEPTIONS.RAISE(iolink, ORD(x), s)
ELSE
EXCEPTIONS.RAISE(iolink, ORD(IOChan.wrongDevice),
'IOLink.RAISEdevException: channel does not belong to device')
END
END
END RAISEdevException ;
PROCEDURE IsIOException () : BOOLEAN ;
(* Returns TRUE if the current coroutine is in the exceptional
execution state because of the raising af an exception from
ChanExceptions; otherwise FALSE.
*)
BEGIN
RETURN( EXCEPTIONS.IsExceptionalExecution() AND
EXCEPTIONS.IsCurrentSource(iolink) )
END IsIOException ;
PROCEDURE IOException () : IOChan.ChanExceptions ;
(* If the current coroutine is in the exceptional execution state
because of the raising of an exception from ChanExceptions,
returns the corresponding enumeration value, and otherwise
raises an exception.
*)
BEGIN
IF IsIOException()
THEN
RETURN( VAL(IOChan.ChanExceptions,
EXCEPTIONS.CurrentNumber(iolink)) )
ELSE
M2RTS.NoException(SYSTEM.ADR(__FILE__), __LINE__,
__COLUMN__, SYSTEM.ADR(__FUNCTION__),
SYSTEM.ADR ("not in the exceptional execution state"))
END
END IOException ;
(*
Init - initialise global variables.
*)
PROCEDURE Init ;
BEGIN
EXCEPTIONS.AllocateSource(iolink) ;
dids := RTentity.InitGroup()
END Init ;
BEGIN
Init
END IOLink.